Compare commits
No commits in common. "master" and "no-flakes" have entirely different histories.
591 changed files with 8180 additions and 32849 deletions
|
@ -1,19 +0,0 @@
|
||||||
name: Discord
|
|
||||||
|
|
||||||
on:
|
|
||||||
- workflow_dispatch
|
|
||||||
- push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
discord_commits:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: discord commits
|
|
||||||
if: contains(github.event.head_commit.message, '(servers)')
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Discommit
|
|
||||||
uses: https://github.com/matt1432/discommit@v0.0.2
|
|
||||||
with:
|
|
||||||
discord_webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
|
||||||
api_url: 'https://git.nelim.org/api/v1/repos/$OWNER/$REPO/git/commits/$REF'
|
|
||||||
title: 'New commit containing changes to server configs:'
|
|
5
.gitattributes
vendored
5
.gitattributes
vendored
|
@ -1,5 +0,0 @@
|
||||||
flake.lock -diff
|
|
||||||
flake.nix -diff
|
|
||||||
**/non-declarative-conf -diff
|
|
||||||
**/package-lock.json -diff
|
|
||||||
**/HomeAssistantGenerated -diff
|
|
21
.gitignore
vendored
21
.gitignore
vendored
|
@ -1,23 +1,2 @@
|
||||||
# Python
|
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
|
||||||
# NPM
|
|
||||||
*node_modules
|
|
||||||
*build/
|
|
||||||
|
|
||||||
# Direnv
|
|
||||||
*.direnv/
|
|
||||||
|
|
||||||
# Generated by nix
|
|
||||||
result*
|
|
||||||
!results/
|
|
||||||
.nixd.json
|
|
||||||
|
|
||||||
## AGS
|
|
||||||
**/vars.ts
|
|
||||||
**/config.js
|
|
||||||
*icons
|
|
||||||
**/types
|
|
||||||
|
|
||||||
# Other
|
|
||||||
*.temp
|
*.temp
|
||||||
|
|
16
LICENSE.md
16
LICENSE.md
|
@ -1,16 +0,0 @@
|
||||||
MIT No Attribution
|
|
||||||
|
|
||||||
Copyright 2024 Mathis H.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
without restriction, including without limitation the rights to use, copy, modify,
|
|
||||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
124
README.md
124
README.md
|
@ -1,120 +1,18 @@
|
||||||
# My NixOS configs
|
# My NixOS configs
|
||||||
|
|
||||||
TODO: add directory structure info and enforce it
|
what is currently not working:
|
||||||
- every root folder in the repo represents a flake output except inputs
|
|
||||||
- every root folder only has a `default.nix` and subfolders for each
|
|
||||||
of its attrs
|
|
||||||
- in a subfolder, there should always be a `default.nix`
|
|
||||||
- if there is non nix code, it will be in a `config` folder
|
|
||||||
- redo docs
|
|
||||||
- every module should not do anything if imported
|
|
||||||
|
|
||||||
## AGS
|
- plymouth theme has no login prompt
|
||||||
|
- sddm theme flashes white
|
||||||
|
- autosign in to keyring
|
||||||
|
|
||||||
You might find it weird that most of my config is written in TypeScript.
|
what i want to do:
|
||||||
That's because all my desktops run
|
|
||||||
[AGS](https://github.com/Aylur/ags)
|
|
||||||
for UI. Click on
|
|
||||||
[this](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/modules/ags)
|
|
||||||
to see my configuration.
|
|
||||||
|
|
||||||
I'm also a victim of Stockholm syndrome at this point and make my scripts
|
- learn flakes
|
||||||
in TypeScript because it's the scripting language I am most comfortable with.
|
- add auto-rotate widget in ags control center
|
||||||
|
- when multiple widgets open, clicking on a background one puts it forward
|
||||||
|
|
||||||
## About
|
## Docs
|
||||||
|
|
||||||
### General
|
Since I use my laptop with one user, I symlinked the configs to my home
|
||||||
|
directory following the tutorial [here](https://nixos.wiki/wiki/NixOS_configuration_editors)
|
||||||
This repo is the complete configuration of machines I own,
|
|
||||||
running NixOS or Nix. Its structure is based on a flake's
|
|
||||||
[outputs](https://wiki.nixos.org/wiki/Flakes#Output_schema).
|
|
||||||
|
|
||||||
### Flake Location
|
|
||||||
|
|
||||||
This git repo will always be located at `$FLAKE` (`config.environment.variables.FLAKE`)
|
|
||||||
and symlinked to `/etc/nixos` to have everything where NixOS tools
|
|
||||||
expect things to be.
|
|
||||||
|
|
||||||
ie.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo rm -r /etc/nixos
|
|
||||||
|
|
||||||
echo "$FLAKE" # /home/matt/.nix
|
|
||||||
|
|
||||||
sudo ln -sf /home/matt/.nix /etc/nixos
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flake Outputs
|
|
||||||
|
|
||||||
| Output | Description |
|
|
||||||
| ---------------------------------- | ----------- |
|
|
||||||
| `nixosConfigurations` | [devices](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/devices)' + ISO's configurations |
|
|
||||||
| `nixOnDroidConfigurations.default` | [Nix-On-Droid](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/devices/android)'s configuration |
|
|
||||||
| `packages` | Some custom [packages](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/packages) not available in nixpkgs or modified from it |
|
|
||||||
| `scopedPackages` | Some custom [package scopes](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/scopedPackages) not available in nixpkgs or modified from it |
|
|
||||||
| `apps` | Scripts ran from the flake defined [here](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/apps) |
|
|
||||||
| `homeManagerModules` | [Modules](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/homeManagerModules) made for home-manager |
|
|
||||||
| `homeManagerModules` | [Modules](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/modules) made for NixOS systems |
|
|
||||||
| `formatter` | I format nix code with [alejandra](https://github.com/kamadorueda/alejandra) |
|
|
||||||
| `devShells.default` | A dev shell to build an ISO from the live-image nixosConfiguration |
|
|
||||||
| `devShells.ags` | A dev shell to have a NodeJS env when I enter my AGS's config directory |
|
|
||||||
|
|
||||||
### Flake Inputs
|
|
||||||
|
|
||||||
To allow use of the full nix language for my inputs, I use [genflake](https://github.com/jorsn/flakegen).
|
|
||||||
Therefore, the flake I edit is located at `./outputs.nix`.
|
|
||||||
|
|
||||||
I also prefer using a more descriptive format for my inputs like so:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
nixpkgs = {
|
|
||||||
type = "github";
|
|
||||||
owner = "NixOS";
|
|
||||||
repo = "nixpkgs";
|
|
||||||
|
|
||||||
# Branch name
|
|
||||||
ref = "nixos-unstable";
|
|
||||||
|
|
||||||
# Pin this input to a specific commit
|
|
||||||
rev = "842d9d80cfd4560648c785f8a4e6f3b096790e19";
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
to make it more clear what is what in the flake URI
|
|
||||||
|
|
||||||
### Secrets
|
|
||||||
|
|
||||||
All my secrets are in a private git repo that makes use of
|
|
||||||
[sops-nix](https://github.com/Mic92/sops-nix).
|
|
||||||
I generate `.sops.yaml` from `.sops.nix`:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
let
|
|
||||||
wim = "somekey";
|
|
||||||
binto = "somekey2";
|
|
||||||
in {
|
|
||||||
creation_rules = [
|
|
||||||
{
|
|
||||||
path_regex = "secrets/[^/]+\\.(yaml|json|env|ini)$";
|
|
||||||
key_groups = [
|
|
||||||
{
|
|
||||||
age = [wim binto];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
which is then converted to `.sops.yaml` using
|
|
||||||
[remarshal](https://github.com/remarshal-project/remarshal)
|
|
||||||
and this shell command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix eval --json --file ./.sops.nix | remarshal --if json --of yaml > .sops.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
TLDR: I
|
|
||||||
**[hate](https://ruudvanasseldonk.com/2023/01/11/the-yaml-document-from-hell)**
|
|
||||||
YAML
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
use flake $FLAKE#node
|
|
||||||
npm ci
|
|
|
@ -1,451 +0,0 @@
|
||||||
import eslint from '@eslint/js';
|
|
||||||
import jsdoc from 'eslint-plugin-jsdoc';
|
|
||||||
import stylistic from '@stylistic/eslint-plugin';
|
|
||||||
import tseslint from 'typescript-eslint';
|
|
||||||
|
|
||||||
|
|
||||||
export default tseslint.config({
|
|
||||||
files: ['**/*.js', '**/*.ts'],
|
|
||||||
ignores: ['node_modules/**', 'types/**'],
|
|
||||||
|
|
||||||
extends: [
|
|
||||||
eslint.configs.recommended,
|
|
||||||
jsdoc.configs['flat/recommended-typescript'],
|
|
||||||
stylistic.configs['recommended-flat'],
|
|
||||||
...tseslint.configs.recommended,
|
|
||||||
...tseslint.configs.stylistic,
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
// JSDoc settings
|
|
||||||
'jsdoc/tag-lines': ['warn', 'any', { startLines: 1 }],
|
|
||||||
'jsdoc/check-line-alignment': ['warn', 'always', {
|
|
||||||
tags: ['param', 'arg', 'argument', 'property', 'prop'],
|
|
||||||
}],
|
|
||||||
'jsdoc/no-types': 'off',
|
|
||||||
|
|
||||||
// Newer settings
|
|
||||||
'@typescript-eslint/no-extraneous-class': ['off'],
|
|
||||||
'@typescript-eslint/no-implied-eval': ['off'],
|
|
||||||
'class-methods-use-this': 'off',
|
|
||||||
'@stylistic/no-multiple-empty-lines': 'off',
|
|
||||||
|
|
||||||
// Pre-flat config
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
args: 'all',
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
caughtErrors: 'all',
|
|
||||||
caughtErrorsIgnorePattern: '^_',
|
|
||||||
destructuredArrayIgnorePattern: '^_',
|
|
||||||
varsIgnorePattern: '^_',
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'array-callback-return': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allowImplicit: true,
|
|
||||||
checkForEach: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'no-constructor-return': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-unreachable-loop': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
ignore: [
|
|
||||||
'ForInStatement',
|
|
||||||
'ForOfStatement',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'no-use-before-define': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
functions: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'block-scoped-var': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'curly': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'default-case-last': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'default-param-last': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'eqeqeq': [
|
|
||||||
'error',
|
|
||||||
'smart',
|
|
||||||
],
|
|
||||||
'func-names': [
|
|
||||||
'warn',
|
|
||||||
'never',
|
|
||||||
],
|
|
||||||
'func-style': [
|
|
||||||
'warn',
|
|
||||||
'expression',
|
|
||||||
],
|
|
||||||
'logical-assignment-operators': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'no-array-constructor': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-empty-function': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-empty-static-block': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-extend-native': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-extra-bind': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-implicit-coercion': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-iterator': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-labels': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-lone-blocks': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-lonely-if': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-loop-func': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-magic-numbers': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
ignore: [
|
|
||||||
-1,
|
|
||||||
0.1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
10,
|
|
||||||
12,
|
|
||||||
33,
|
|
||||||
66,
|
|
||||||
100,
|
|
||||||
255,
|
|
||||||
360,
|
|
||||||
450,
|
|
||||||
500,
|
|
||||||
1000,
|
|
||||||
],
|
|
||||||
ignoreDefaultValues: true,
|
|
||||||
ignoreClassFieldInitialValues: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'no-multi-assign': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-new-wrappers': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-object-constructor': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-proto': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-return-assign': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-sequences': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-shadow': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
builtinGlobals: true,
|
|
||||||
allow: [
|
|
||||||
'Window',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'no-undef-init': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-undefined': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-useless-constructor': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-useless-escape': [
|
|
||||||
'off',
|
|
||||||
],
|
|
||||||
'no-useless-return': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-var': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'no-void': [
|
|
||||||
'off',
|
|
||||||
],
|
|
||||||
'no-with': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'object-shorthand': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'one-var': [
|
|
||||||
'error',
|
|
||||||
'never',
|
|
||||||
],
|
|
||||||
'operator-assignment': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'prefer-arrow-callback': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'prefer-const': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'prefer-object-has-own': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'prefer-regex-literals': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'prefer-template': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'no-prototype-builtins': 'off',
|
|
||||||
'@typescript-eslint/no-var-requires': [
|
|
||||||
'off',
|
|
||||||
],
|
|
||||||
'@stylistic/array-bracket-newline': [
|
|
||||||
'warn',
|
|
||||||
'consistent',
|
|
||||||
],
|
|
||||||
'@stylistic/array-bracket-spacing': [
|
|
||||||
'warn',
|
|
||||||
'never',
|
|
||||||
],
|
|
||||||
'@stylistic/arrow-parens': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'@stylistic/brace-style': [
|
|
||||||
'warn',
|
|
||||||
'stroustrup',
|
|
||||||
],
|
|
||||||
'@stylistic/comma-dangle': [
|
|
||||||
'warn',
|
|
||||||
'always-multiline',
|
|
||||||
],
|
|
||||||
'@stylistic/comma-spacing': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
before: false,
|
|
||||||
after: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/comma-style': [
|
|
||||||
'error',
|
|
||||||
'last',
|
|
||||||
],
|
|
||||||
'@stylistic/dot-location': [
|
|
||||||
'error',
|
|
||||||
'property',
|
|
||||||
],
|
|
||||||
'@stylistic/function-call-argument-newline': [
|
|
||||||
'warn',
|
|
||||||
'consistent',
|
|
||||||
],
|
|
||||||
'@stylistic/function-paren-newline': [
|
|
||||||
'warn',
|
|
||||||
'consistent',
|
|
||||||
],
|
|
||||||
'@stylistic/indent': [
|
|
||||||
'warn',
|
|
||||||
4,
|
|
||||||
{
|
|
||||||
SwitchCase: 1,
|
|
||||||
ignoreComments: true,
|
|
||||||
ignoredNodes: ['TemplateLiteral > *'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/key-spacing': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
beforeColon: false,
|
|
||||||
afterColon: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/keyword-spacing': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
before: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/linebreak-style': [
|
|
||||||
'error',
|
|
||||||
'unix',
|
|
||||||
],
|
|
||||||
'@stylistic/lines-between-class-members': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
{
|
|
||||||
exceptAfterSingleLine: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/max-len': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
code: 105,
|
|
||||||
ignoreComments: true,
|
|
||||||
ignoreTrailingComments: true,
|
|
||||||
ignoreUrls: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/multiline-ternary': [
|
|
||||||
'warn',
|
|
||||||
'always-multiline',
|
|
||||||
],
|
|
||||||
'@stylistic/new-parens': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'@stylistic/no-mixed-operators': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/no-mixed-spaces-and-tabs': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'@stylistic/no-multi-spaces': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'@stylistic/no-tabs': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'@stylistic/no-trailing-spaces': [
|
|
||||||
'error',
|
|
||||||
],
|
|
||||||
'@stylistic/no-whitespace-before-property': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/nonblock-statement-body-position': [
|
|
||||||
'error',
|
|
||||||
'below',
|
|
||||||
],
|
|
||||||
'@stylistic/object-curly-newline': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
consistent: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/object-curly-spacing': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'@stylistic/operator-linebreak': [
|
|
||||||
'warn',
|
|
||||||
'after',
|
|
||||||
],
|
|
||||||
'@stylistic/padded-blocks': [
|
|
||||||
'error',
|
|
||||||
'never',
|
|
||||||
],
|
|
||||||
'@stylistic/padding-line-between-statements': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
blankLine: 'always',
|
|
||||||
prev: '*',
|
|
||||||
next: 'return',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
blankLine: 'always',
|
|
||||||
prev: [
|
|
||||||
'const',
|
|
||||||
'let',
|
|
||||||
'var',
|
|
||||||
],
|
|
||||||
next: '*',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
blankLine: 'any',
|
|
||||||
prev: [
|
|
||||||
'const',
|
|
||||||
'let',
|
|
||||||
'var',
|
|
||||||
],
|
|
||||||
next: [
|
|
||||||
'const',
|
|
||||||
'let',
|
|
||||||
'var',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
blankLine: 'always',
|
|
||||||
prev: [
|
|
||||||
'case',
|
|
||||||
'default',
|
|
||||||
],
|
|
||||||
next: '*',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/quote-props': [
|
|
||||||
'error',
|
|
||||||
'consistent-as-needed',
|
|
||||||
],
|
|
||||||
'@stylistic/quotes': [
|
|
||||||
'error',
|
|
||||||
'single',
|
|
||||||
{
|
|
||||||
avoidEscape: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@stylistic/semi': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'@stylistic/semi-spacing': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/space-before-blocks': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/space-before-function-paren': [
|
|
||||||
'warn',
|
|
||||||
'never',
|
|
||||||
],
|
|
||||||
'@stylistic/space-infix-ops': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/spaced-comment': [
|
|
||||||
'warn',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'@stylistic/switch-colon-spacing': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
'@stylistic/wrap-regex': [
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import eslintConf from './eslint.config';
|
|
||||||
|
|
||||||
export default eslintConf;
|
|
BIN
apps/config/package-lock.json
generated
BIN
apps/config/package-lock.json
generated
Binary file not shown.
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"name": "eslint-conf",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"exports": "./index.ts",
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "9.17.0",
|
|
||||||
"@stylistic/eslint-plugin": "2.12.1",
|
|
||||||
"eslint": "9.17.0",
|
|
||||||
"eslint-plugin-jsdoc": "50.6.1",
|
|
||||||
"jiti": "2.4.2",
|
|
||||||
"pkg-types": "1.2.1",
|
|
||||||
"typescript": "5.7.2",
|
|
||||||
"typescript-eslint": "8.18.1"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
// Env
|
|
||||||
"target": "ESNext",
|
|
||||||
"lib": [
|
|
||||||
"ESNext"
|
|
||||||
],
|
|
||||||
// Module
|
|
||||||
"module": "nodenext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"baseUrl": ".",
|
|
||||||
// Emit
|
|
||||||
"noEmit": true,
|
|
||||||
"newLine": "LF",
|
|
||||||
// Interop
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
// Type Checking
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitAny": false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"extends": "./tsconfig.base.json",
|
|
||||||
"includes": [
|
|
||||||
"*.ts",
|
|
||||||
"**/*.ts",
|
|
||||||
"*.js",
|
|
||||||
"**/*.js"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
inputs,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
inherit (pkgs.lib) getExe listToAttrs nameValuePair;
|
|
||||||
|
|
||||||
buildApp = attrs: (pkgs.callPackage ./nix/buildApp.nix ({} // inputs // attrs));
|
|
||||||
|
|
||||||
mkApp = file: {
|
|
||||||
program = getExe (pkgs.callPackage file ({inherit buildApp;} // inputs));
|
|
||||||
type = "app";
|
|
||||||
};
|
|
||||||
|
|
||||||
mkApps = apps: listToAttrs (map (x: nameValuePair x (mkApp ./${x})) apps);
|
|
||||||
in
|
|
||||||
mkApps [
|
|
||||||
"extract-subs"
|
|
||||||
"update-sources"
|
|
||||||
]
|
|
|
@ -1,2 +0,0 @@
|
||||||
use flake $FLAKE#subtitles-dev
|
|
||||||
npm ci
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
buildApp,
|
|
||||||
ffmpeg-full,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
buildApp {
|
|
||||||
src = ./.;
|
|
||||||
npmDepsHash = "sha256-ytaO1mqoORNB5rP/8X/WpIoHkxmi1+Pc6Nep02+fTZ8=";
|
|
||||||
|
|
||||||
runtimeInputs = [
|
|
||||||
ffmpeg-full
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import eslintConf from 'eslint-conf';
|
|
||||||
|
|
||||||
export default eslintConf;
|
|
BIN
apps/extract-subs/package-lock.json
generated
BIN
apps/extract-subs/package-lock.json
generated
Binary file not shown.
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"name": "extract-subs",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"bin": "out/bin/app.cjs",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/fluent-ffmpeg": "2.1.27",
|
|
||||||
"fluent-ffmpeg": "2.1.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint-conf": "file:../config",
|
|
||||||
"@types/node": "22.10.2",
|
|
||||||
"esbuild": "0.24.2",
|
|
||||||
"eslint": "9.17.0",
|
|
||||||
"jiti": "2.4.2",
|
|
||||||
"typescript": "5.7.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
import { spawnSync as spawn } from 'child_process';
|
|
||||||
|
|
||||||
import ffprobe from './ffprobe';
|
|
||||||
import { ISO6393To1 } from './lang-codes';
|
|
||||||
|
|
||||||
/* Types */
|
|
||||||
import { FfprobeStream } from 'fluent-ffmpeg';
|
|
||||||
|
|
||||||
|
|
||||||
const SPAWN_OPTS = {
|
|
||||||
stdio: [process.stdin, process.stdout, process.stderr],
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These are the cli arguments
|
|
||||||
*
|
|
||||||
* @param videoPath the directory in which we want to sync the subtitles
|
|
||||||
* @param languages a comma-separated list of languages (3 letters) to sync the subtitles
|
|
||||||
*/
|
|
||||||
const video = process.argv[2];
|
|
||||||
const languages = process.argv[3]?.split(',');
|
|
||||||
|
|
||||||
|
|
||||||
// Global Vars
|
|
||||||
const subIndexes: number[] = [];
|
|
||||||
let videoPath: string;
|
|
||||||
let baseName: string;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the relative path to the subtitle file of a ffmpeg stream.
|
|
||||||
*
|
|
||||||
* @param sub the stream of the subtitles to extract
|
|
||||||
* @returns the path of the subtitle file
|
|
||||||
*/
|
|
||||||
const getSubPath = (sub: FfprobeStream): string => {
|
|
||||||
const language = ISO6393To1.get(sub.tags.language);
|
|
||||||
|
|
||||||
const forced = sub.disposition?.forced === 0 ?
|
|
||||||
'' :
|
|
||||||
'.forced';
|
|
||||||
|
|
||||||
const hearingImpaired = sub.disposition?.hearing_impaired === 0 ?
|
|
||||||
'' :
|
|
||||||
'.sdh';
|
|
||||||
|
|
||||||
return `${baseName}${forced}.${language}${hearingImpaired}.srt`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all subtitles streams from the video file.
|
|
||||||
*/
|
|
||||||
const removeContainerSubs = (): void => {
|
|
||||||
spawn('mv', [
|
|
||||||
videoPath,
|
|
||||||
`${videoPath}.bak`,
|
|
||||||
], SPAWN_OPTS);
|
|
||||||
|
|
||||||
spawn('ffmpeg', [
|
|
||||||
'-i', `${videoPath}.bak`,
|
|
||||||
'-map', '0',
|
|
||||||
...subIndexes.map((i) => ['-map', `-0:${i}`]).flat(),
|
|
||||||
'-c', 'copy', videoPath,
|
|
||||||
], SPAWN_OPTS);
|
|
||||||
|
|
||||||
spawn('rm', [
|
|
||||||
`${videoPath}.bak`,
|
|
||||||
], SPAWN_OPTS);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts a sub of a video file to a subtitle file.
|
|
||||||
*
|
|
||||||
* @param sub the stream of the subtitles to extract
|
|
||||||
*/
|
|
||||||
const extractSub = (sub: FfprobeStream): void => {
|
|
||||||
const subFile = getSubPath(sub);
|
|
||||||
|
|
||||||
spawn('ffmpeg', [
|
|
||||||
'-i', videoPath,
|
|
||||||
'-map', `0:${sub.index}`, subFile,
|
|
||||||
], SPAWN_OPTS);
|
|
||||||
|
|
||||||
subIndexes.push(sub.index);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorts the list of streams to only keep subtitles
|
|
||||||
* that can be extracted.
|
|
||||||
*
|
|
||||||
* @param lang the language of the subtitles
|
|
||||||
* @param streams the streams
|
|
||||||
* @returns the streams that represent subtitles
|
|
||||||
*/
|
|
||||||
const findSubs = (
|
|
||||||
lang: string,
|
|
||||||
streams: FfprobeStream[],
|
|
||||||
): FfprobeStream[] => {
|
|
||||||
const subs = streams.filter((s) => s.tags?.language &&
|
|
||||||
s.tags.language === lang &&
|
|
||||||
s.codec_type === 'subtitle');
|
|
||||||
|
|
||||||
const pgs = subs.filter((s) => s.codec_name === 'hdmv_pgs_subtitle');
|
|
||||||
|
|
||||||
// If we only have PGS subs, warn user
|
|
||||||
if (pgs.length === subs.length) {
|
|
||||||
console.warn(`No SRT subtitle tracks were found for ${lang}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove PGS streams from subs
|
|
||||||
return subs.filter((s) => s.codec_name !== 'hdmv_pgs_subtitle');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where the magic happens.
|
|
||||||
*/
|
|
||||||
const main = async(): Promise<void> => {
|
|
||||||
// Get rid of video extension
|
|
||||||
baseName = videoPath.split('/').at(-1)!.replace(/\.[^.]*$/, '');
|
|
||||||
|
|
||||||
// ffprobe the video file to see available sub tracks
|
|
||||||
const data = await ffprobe(videoPath);
|
|
||||||
|
|
||||||
if (!data?.streams) {
|
|
||||||
console.error('Couldn\'t find streams in video file');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for languages wanted
|
|
||||||
languages.forEach((lang) => {
|
|
||||||
const subs = findSubs(lang, data.streams);
|
|
||||||
|
|
||||||
if (subs.length === 0) {
|
|
||||||
console.warn(`No subtitle tracks were found for ${lang}`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract all subs
|
|
||||||
subs.forEach((sub) => { extractSub(sub); });
|
|
||||||
});
|
|
||||||
|
|
||||||
removeContainerSubs();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Check if there are 2 params
|
|
||||||
if (video && languages) {
|
|
||||||
videoPath = video;
|
|
||||||
main();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error('Error: no argument passed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import Ffmpeg from 'fluent-ffmpeg';
|
|
||||||
|
|
||||||
|
|
||||||
export default (videoPath: string) => new Promise<Ffmpeg.FfprobeData>((resolve) => {
|
|
||||||
Ffmpeg.ffprobe(videoPath, (_e, data) => {
|
|
||||||
resolve(data);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,373 +0,0 @@
|
||||||
export const ISO6391To3 = new Map([
|
|
||||||
['aa', 'aar'],
|
|
||||||
['ab', 'abk'],
|
|
||||||
['af', 'afr'],
|
|
||||||
['ak', 'aka'],
|
|
||||||
['am', 'amh'],
|
|
||||||
['ar', 'ara'],
|
|
||||||
['an', 'arg'],
|
|
||||||
['as', 'asm'],
|
|
||||||
['av', 'ava'],
|
|
||||||
['ae', 'ave'],
|
|
||||||
['ay', 'aym'],
|
|
||||||
['az', 'aze'],
|
|
||||||
['ba', 'bak'],
|
|
||||||
['bm', 'bam'],
|
|
||||||
['be', 'bel'],
|
|
||||||
['bn', 'ben'],
|
|
||||||
['bi', 'bis'],
|
|
||||||
['bo', 'bod'],
|
|
||||||
['bs', 'bos'],
|
|
||||||
['br', 'bre'],
|
|
||||||
['bg', 'bul'],
|
|
||||||
['ca', 'cat'],
|
|
||||||
['cs', 'ces'],
|
|
||||||
['ch', 'cha'],
|
|
||||||
['ce', 'che'],
|
|
||||||
['cu', 'chu'],
|
|
||||||
['cv', 'chv'],
|
|
||||||
['kw', 'cor'],
|
|
||||||
['co', 'cos'],
|
|
||||||
['cr', 'cre'],
|
|
||||||
['cy', 'cym'],
|
|
||||||
['da', 'dan'],
|
|
||||||
['de', 'deu'],
|
|
||||||
['dv', 'div'],
|
|
||||||
['dz', 'dzo'],
|
|
||||||
['el', 'ell'],
|
|
||||||
['en', 'eng'],
|
|
||||||
['eo', 'epo'],
|
|
||||||
['et', 'est'],
|
|
||||||
['eu', 'eus'],
|
|
||||||
['ee', 'ewe'],
|
|
||||||
['fo', 'fao'],
|
|
||||||
['fa', 'fas'],
|
|
||||||
['fj', 'fij'],
|
|
||||||
['fi', 'fin'],
|
|
||||||
['fr', 'fre'],
|
|
||||||
['fy', 'fry'],
|
|
||||||
['ff', 'ful'],
|
|
||||||
['gd', 'gla'],
|
|
||||||
['ga', 'gle'],
|
|
||||||
['gl', 'glg'],
|
|
||||||
['gv', 'glv'],
|
|
||||||
['gn', 'grn'],
|
|
||||||
['gu', 'guj'],
|
|
||||||
['ht', 'hat'],
|
|
||||||
['ha', 'hau'],
|
|
||||||
['sh', 'hbs'],
|
|
||||||
['he', 'heb'],
|
|
||||||
['hz', 'her'],
|
|
||||||
['hi', 'hin'],
|
|
||||||
['ho', 'hmo'],
|
|
||||||
['hr', 'hrv'],
|
|
||||||
['hu', 'hun'],
|
|
||||||
['hy', 'hye'],
|
|
||||||
['ig', 'ibo'],
|
|
||||||
['io', 'ido'],
|
|
||||||
['ii', 'iii'],
|
|
||||||
['iu', 'iku'],
|
|
||||||
['ie', 'ile'],
|
|
||||||
['ia', 'ina'],
|
|
||||||
['id', 'ind'],
|
|
||||||
['ik', 'ipk'],
|
|
||||||
['is', 'isl'],
|
|
||||||
['it', 'ita'],
|
|
||||||
['jv', 'jav'],
|
|
||||||
['ja', 'jpn'],
|
|
||||||
['kl', 'kal'],
|
|
||||||
['kn', 'kan'],
|
|
||||||
['ks', 'kas'],
|
|
||||||
['ka', 'kat'],
|
|
||||||
['kr', 'kau'],
|
|
||||||
['kk', 'kaz'],
|
|
||||||
['km', 'khm'],
|
|
||||||
['ki', 'kik'],
|
|
||||||
['rw', 'kin'],
|
|
||||||
['ky', 'kir'],
|
|
||||||
['kv', 'kom'],
|
|
||||||
['kg', 'kon'],
|
|
||||||
['ko', 'kor'],
|
|
||||||
['kj', 'kua'],
|
|
||||||
['ku', 'kur'],
|
|
||||||
['lo', 'lao'],
|
|
||||||
['la', 'lat'],
|
|
||||||
['lv', 'lav'],
|
|
||||||
['li', 'lim'],
|
|
||||||
['ln', 'lin'],
|
|
||||||
['lt', 'lit'],
|
|
||||||
['lb', 'ltz'],
|
|
||||||
['lu', 'lub'],
|
|
||||||
['lg', 'lug'],
|
|
||||||
['mh', 'mah'],
|
|
||||||
['ml', 'mal'],
|
|
||||||
['mr', 'mar'],
|
|
||||||
['mk', 'mkd'],
|
|
||||||
['mg', 'mlg'],
|
|
||||||
['mt', 'mlt'],
|
|
||||||
['mn', 'mon'],
|
|
||||||
['mi', 'mri'],
|
|
||||||
['ms', 'msa'],
|
|
||||||
['my', 'mya'],
|
|
||||||
['na', 'nau'],
|
|
||||||
['nv', 'nav'],
|
|
||||||
['nr', 'nbl'],
|
|
||||||
['nd', 'nde'],
|
|
||||||
['ng', 'ndo'],
|
|
||||||
['ne', 'nep'],
|
|
||||||
['nl', 'nld'],
|
|
||||||
['nn', 'nno'],
|
|
||||||
['nb', 'nob'],
|
|
||||||
['no', 'nor'],
|
|
||||||
['ny', 'nya'],
|
|
||||||
['oc', 'oci'],
|
|
||||||
['oj', 'oji'],
|
|
||||||
['or', 'ori'],
|
|
||||||
['om', 'orm'],
|
|
||||||
['os', 'oss'],
|
|
||||||
['pa', 'pan'],
|
|
||||||
['pi', 'pli'],
|
|
||||||
['pl', 'pol'],
|
|
||||||
['pt', 'por'],
|
|
||||||
['ps', 'pus'],
|
|
||||||
['qu', 'que'],
|
|
||||||
['rm', 'roh'],
|
|
||||||
['ro', 'ron'],
|
|
||||||
['rn', 'run'],
|
|
||||||
['ru', 'rus'],
|
|
||||||
['sg', 'sag'],
|
|
||||||
['sa', 'san'],
|
|
||||||
['si', 'sin'],
|
|
||||||
['sk', 'slk'],
|
|
||||||
['sl', 'slv'],
|
|
||||||
['se', 'sme'],
|
|
||||||
['sm', 'smo'],
|
|
||||||
['sn', 'sna'],
|
|
||||||
['sd', 'snd'],
|
|
||||||
['so', 'som'],
|
|
||||||
['st', 'sot'],
|
|
||||||
['es', 'spa'],
|
|
||||||
['sq', 'sqi'],
|
|
||||||
['sc', 'srd'],
|
|
||||||
['sr', 'srp'],
|
|
||||||
['ss', 'ssw'],
|
|
||||||
['su', 'sun'],
|
|
||||||
['sw', 'swa'],
|
|
||||||
['sv', 'swe'],
|
|
||||||
['ty', 'tah'],
|
|
||||||
['ta', 'tam'],
|
|
||||||
['tt', 'tat'],
|
|
||||||
['te', 'tel'],
|
|
||||||
['tg', 'tgk'],
|
|
||||||
['tl', 'tgl'],
|
|
||||||
['th', 'tha'],
|
|
||||||
['ti', 'tir'],
|
|
||||||
['to', 'ton'],
|
|
||||||
['tn', 'tsn'],
|
|
||||||
['ts', 'tso'],
|
|
||||||
['tk', 'tuk'],
|
|
||||||
['tr', 'tur'],
|
|
||||||
['tw', 'twi'],
|
|
||||||
['ug', 'uig'],
|
|
||||||
['uk', 'ukr'],
|
|
||||||
['ur', 'urd'],
|
|
||||||
['uz', 'uzb'],
|
|
||||||
['ve', 'ven'],
|
|
||||||
['vi', 'vie'],
|
|
||||||
['vo', 'vol'],
|
|
||||||
['wa', 'wln'],
|
|
||||||
['wo', 'wol'],
|
|
||||||
['xh', 'xho'],
|
|
||||||
['yi', 'yid'],
|
|
||||||
['yo', 'yor'],
|
|
||||||
['za', 'zha'],
|
|
||||||
['zh', 'zho'],
|
|
||||||
['zu', 'zul'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const ISO6393To1 = new Map([
|
|
||||||
['aar', 'aa'],
|
|
||||||
['abk', 'ab'],
|
|
||||||
['afr', 'af'],
|
|
||||||
['aka', 'ak'],
|
|
||||||
['amh', 'am'],
|
|
||||||
['ara', 'ar'],
|
|
||||||
['arg', 'an'],
|
|
||||||
['asm', 'as'],
|
|
||||||
['ava', 'av'],
|
|
||||||
['ave', 'ae'],
|
|
||||||
['aym', 'ay'],
|
|
||||||
['aze', 'az'],
|
|
||||||
['bak', 'ba'],
|
|
||||||
['bam', 'bm'],
|
|
||||||
['bel', 'be'],
|
|
||||||
['ben', 'bn'],
|
|
||||||
['bis', 'bi'],
|
|
||||||
['bod', 'bo'],
|
|
||||||
['bos', 'bs'],
|
|
||||||
['bre', 'br'],
|
|
||||||
['bul', 'bg'],
|
|
||||||
['cat', 'ca'],
|
|
||||||
['ces', 'cs'],
|
|
||||||
['cha', 'ch'],
|
|
||||||
['che', 'ce'],
|
|
||||||
['chu', 'cu'],
|
|
||||||
['chv', 'cv'],
|
|
||||||
['cor', 'kw'],
|
|
||||||
['cos', 'co'],
|
|
||||||
['cre', 'cr'],
|
|
||||||
['cym', 'cy'],
|
|
||||||
['dan', 'da'],
|
|
||||||
['deu', 'de'],
|
|
||||||
['div', 'dv'],
|
|
||||||
['dzo', 'dz'],
|
|
||||||
['ell', 'el'],
|
|
||||||
['eng', 'en'],
|
|
||||||
['epo', 'eo'],
|
|
||||||
['est', 'et'],
|
|
||||||
['eus', 'eu'],
|
|
||||||
['ewe', 'ee'],
|
|
||||||
['fao', 'fo'],
|
|
||||||
['fas', 'fa'],
|
|
||||||
['fij', 'fj'],
|
|
||||||
['fin', 'fi'],
|
|
||||||
['fre', 'fr'],
|
|
||||||
['fry', 'fy'],
|
|
||||||
['ful', 'ff'],
|
|
||||||
['gla', 'gd'],
|
|
||||||
['gle', 'ga'],
|
|
||||||
['glg', 'gl'],
|
|
||||||
['glv', 'gv'],
|
|
||||||
['grn', 'gn'],
|
|
||||||
['guj', 'gu'],
|
|
||||||
['hat', 'ht'],
|
|
||||||
['hau', 'ha'],
|
|
||||||
['hbs', 'sh'],
|
|
||||||
['heb', 'he'],
|
|
||||||
['her', 'hz'],
|
|
||||||
['hin', 'hi'],
|
|
||||||
['hmo', 'ho'],
|
|
||||||
['hrv', 'hr'],
|
|
||||||
['hun', 'hu'],
|
|
||||||
['hye', 'hy'],
|
|
||||||
['ibo', 'ig'],
|
|
||||||
['ido', 'io'],
|
|
||||||
['iii', 'ii'],
|
|
||||||
['iku', 'iu'],
|
|
||||||
['ile', 'ie'],
|
|
||||||
['ina', 'ia'],
|
|
||||||
['ind', 'id'],
|
|
||||||
['ipk', 'ik'],
|
|
||||||
['isl', 'is'],
|
|
||||||
['ita', 'it'],
|
|
||||||
['jav', 'jv'],
|
|
||||||
['jpn', 'ja'],
|
|
||||||
['kal', 'kl'],
|
|
||||||
['kan', 'kn'],
|
|
||||||
['kas', 'ks'],
|
|
||||||
['kat', 'ka'],
|
|
||||||
['kau', 'kr'],
|
|
||||||
['kaz', 'kk'],
|
|
||||||
['khm', 'km'],
|
|
||||||
['kik', 'ki'],
|
|
||||||
['kin', 'rw'],
|
|
||||||
['kir', 'ky'],
|
|
||||||
['kom', 'kv'],
|
|
||||||
['kon', 'kg'],
|
|
||||||
['kor', 'ko'],
|
|
||||||
['kua', 'kj'],
|
|
||||||
['kur', 'ku'],
|
|
||||||
['lao', 'lo'],
|
|
||||||
['lat', 'la'],
|
|
||||||
['lav', 'lv'],
|
|
||||||
['lim', 'li'],
|
|
||||||
['lin', 'ln'],
|
|
||||||
['lit', 'lt'],
|
|
||||||
['ltz', 'lb'],
|
|
||||||
['lub', 'lu'],
|
|
||||||
['lug', 'lg'],
|
|
||||||
['mah', 'mh'],
|
|
||||||
['mal', 'ml'],
|
|
||||||
['mar', 'mr'],
|
|
||||||
['mkd', 'mk'],
|
|
||||||
['mlg', 'mg'],
|
|
||||||
['mlt', 'mt'],
|
|
||||||
['mon', 'mn'],
|
|
||||||
['mri', 'mi'],
|
|
||||||
['msa', 'ms'],
|
|
||||||
['mya', 'my'],
|
|
||||||
['nau', 'na'],
|
|
||||||
['nav', 'nv'],
|
|
||||||
['nbl', 'nr'],
|
|
||||||
['nde', 'nd'],
|
|
||||||
['ndo', 'ng'],
|
|
||||||
['nep', 'ne'],
|
|
||||||
['nld', 'nl'],
|
|
||||||
['nno', 'nn'],
|
|
||||||
['nob', 'nb'],
|
|
||||||
['nor', 'no'],
|
|
||||||
['nya', 'ny'],
|
|
||||||
['oci', 'oc'],
|
|
||||||
['oji', 'oj'],
|
|
||||||
['ori', 'or'],
|
|
||||||
['orm', 'om'],
|
|
||||||
['oss', 'os'],
|
|
||||||
['pan', 'pa'],
|
|
||||||
['pli', 'pi'],
|
|
||||||
['pol', 'pl'],
|
|
||||||
['por', 'pt'],
|
|
||||||
['pus', 'ps'],
|
|
||||||
['que', 'qu'],
|
|
||||||
['roh', 'rm'],
|
|
||||||
['ron', 'ro'],
|
|
||||||
['run', 'rn'],
|
|
||||||
['rus', 'ru'],
|
|
||||||
['sag', 'sg'],
|
|
||||||
['san', 'sa'],
|
|
||||||
['sin', 'si'],
|
|
||||||
['slk', 'sk'],
|
|
||||||
['slv', 'sl'],
|
|
||||||
['sme', 'se'],
|
|
||||||
['smo', 'sm'],
|
|
||||||
['sna', 'sn'],
|
|
||||||
['snd', 'sd'],
|
|
||||||
['som', 'so'],
|
|
||||||
['sot', 'st'],
|
|
||||||
['spa', 'es'],
|
|
||||||
['sqi', 'sq'],
|
|
||||||
['srd', 'sc'],
|
|
||||||
['srp', 'sr'],
|
|
||||||
['ssw', 'ss'],
|
|
||||||
['sun', 'su'],
|
|
||||||
['swa', 'sw'],
|
|
||||||
['swe', 'sv'],
|
|
||||||
['tah', 'ty'],
|
|
||||||
['tam', 'ta'],
|
|
||||||
['tat', 'tt'],
|
|
||||||
['tel', 'te'],
|
|
||||||
['tgk', 'tg'],
|
|
||||||
['tgl', 'tl'],
|
|
||||||
['tha', 'th'],
|
|
||||||
['tir', 'ti'],
|
|
||||||
['ton', 'to'],
|
|
||||||
['tsn', 'tn'],
|
|
||||||
['tso', 'ts'],
|
|
||||||
['tuk', 'tk'],
|
|
||||||
['tur', 'tr'],
|
|
||||||
['twi', 'tw'],
|
|
||||||
['uig', 'ug'],
|
|
||||||
['ukr', 'uk'],
|
|
||||||
['urd', 'ur'],
|
|
||||||
['uzb', 'uz'],
|
|
||||||
['ven', 've'],
|
|
||||||
['vie', 'vi'],
|
|
||||||
['vol', 'vo'],
|
|
||||||
['wln', 'wa'],
|
|
||||||
['wol', 'wo'],
|
|
||||||
['xho', 'xh'],
|
|
||||||
['yid', 'yi'],
|
|
||||||
['yor', 'yo'],
|
|
||||||
['zha', 'za'],
|
|
||||||
['zho', 'zh'],
|
|
||||||
['zul', 'zu'],
|
|
||||||
]);
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"extends": "../config/tsconfig.base.json",
|
|
||||||
"includes": [
|
|
||||||
"*.ts",
|
|
||||||
"**/*.ts",
|
|
||||||
"*.js",
|
|
||||||
"**/*.js"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
runtimeInputs,
|
|
||||||
npmDepsHash,
|
|
||||||
src,
|
|
||||||
lib,
|
|
||||||
buildNpmPackage,
|
|
||||||
makeWrapper,
|
|
||||||
nodejs_latest,
|
|
||||||
jq,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
inherit (lib) concatMapStringsSep;
|
|
||||||
inherit (builtins) fromJSON readFile;
|
|
||||||
|
|
||||||
packageJSON = fromJSON (readFile "${src}/package.json");
|
|
||||||
in
|
|
||||||
buildNpmPackage rec {
|
|
||||||
pname = packageJSON.name;
|
|
||||||
inherit (packageJSON) version;
|
|
||||||
|
|
||||||
inherit src runtimeInputs npmDepsHash;
|
|
||||||
|
|
||||||
prePatch = ''
|
|
||||||
mv ./tsconfig.json ./project.json
|
|
||||||
sed 's/^ *\/\/.*//' ${../config/tsconfig.json} > ./base.json
|
|
||||||
${jq}/bin/jq -sr '.[0] * .[1] | del(.extends)' ./project.json ./base.json > ./tsconfig.json
|
|
||||||
rm base.json project.json
|
|
||||||
'';
|
|
||||||
|
|
||||||
nativeBuildInputs = [makeWrapper];
|
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
wrapProgram $out/bin/${pname} \
|
|
||||||
--prefix PATH : ${concatMapStringsSep ":" (p: p + "/bin") runtimeInputs}
|
|
||||||
'';
|
|
||||||
|
|
||||||
nodejs = nodejs_latest;
|
|
||||||
meta.mainProgram = pname;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
self,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
inherit (pkgs.lib) mapAttrs removeSuffix;
|
|
||||||
in
|
|
||||||
mapAttrs (
|
|
||||||
name: app: (pkgs.symlinkJoin {
|
|
||||||
name = "app-${name}";
|
|
||||||
paths = [(removeSuffix "/bin/${name}" (toString app.program))];
|
|
||||||
})
|
|
||||||
)
|
|
||||||
(removeAttrs self.apps.${pkgs.system} ["genflake"])
|
|
|
@ -1,2 +0,0 @@
|
||||||
use flake $FLAKE#node
|
|
||||||
npm ci
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
buildApp,
|
|
||||||
callPackage,
|
|
||||||
nix-update,
|
|
||||||
nodejs_latest,
|
|
||||||
prefetch-npm-deps,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
buildApp {
|
|
||||||
src = ./.;
|
|
||||||
npmDepsHash = "sha256-vIhR/+Pgj35sv/ZX1iL+xWheu6w7vQeJy4OEfa8tXFw=";
|
|
||||||
|
|
||||||
runtimeInputs = [
|
|
||||||
nix-update
|
|
||||||
nodejs_latest
|
|
||||||
prefetch-npm-deps
|
|
||||||
(callPackage ../../modules/docker/updateImage.nix {})
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import eslintConf from 'eslint-conf';
|
|
||||||
|
|
||||||
export default eslintConf;
|
|
BIN
apps/update-sources/package-lock.json
generated
BIN
apps/update-sources/package-lock.json
generated
Binary file not shown.
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"name": "update-sources",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"bin": "out/bin/app.cjs",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint-conf": "file:../config",
|
|
||||||
"@types/node": "22.10.2",
|
|
||||||
"esbuild": "0.24.2",
|
|
||||||
"eslint": "9.17.0",
|
|
||||||
"jiti": "2.4.2",
|
|
||||||
"pkg-types": "1.2.1",
|
|
||||||
"typescript": "5.7.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
import { writeFileSync } from 'node:fs';
|
|
||||||
|
|
||||||
import { parseArgs } from './lib';
|
|
||||||
|
|
||||||
import { updateDocker } from './docker';
|
|
||||||
import { updateFirefoxAddons } from '././firefox';
|
|
||||||
import { updateFlakeInputs } from './flake';
|
|
||||||
import updateNodeModules from './node-modules';
|
|
||||||
|
|
||||||
import {
|
|
||||||
runNixUpdate,
|
|
||||||
updateCustomPackage,
|
|
||||||
updateVuetorrent,
|
|
||||||
} from './misc';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE;
|
|
||||||
|
|
||||||
if (!FLAKE) {
|
|
||||||
console.error('Env var FLAKE not found');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = parseArgs();
|
|
||||||
|
|
||||||
const main = async() => {
|
|
||||||
if (args['d'] || args['docker']) {
|
|
||||||
console.log(updateDocker());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['i'] || args['inputs']) {
|
|
||||||
console.log(updateFlakeInputs());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['f'] || args['firefox']) {
|
|
||||||
console.log(updateFirefoxAddons());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['v'] || args['vuetorrent']) {
|
|
||||||
console.log(updateVuetorrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['c'] || args['custom-sidebar']) {
|
|
||||||
console.log(updateCustomPackage(
|
|
||||||
'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['s'] || args['some-sass-language-server']) {
|
|
||||||
console.log(updateCustomPackage('some-sass-language-server'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['n'] || args['node_modules']) {
|
|
||||||
console.log(await updateNodeModules());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['h'] || args['homepage']) {
|
|
||||||
console.log(runNixUpdate('homepage'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['a'] || args['all']) {
|
|
||||||
// Update this first because of nix run cmd
|
|
||||||
const firefoxOutput = updateFirefoxAddons();
|
|
||||||
|
|
||||||
console.log(firefoxOutput);
|
|
||||||
|
|
||||||
|
|
||||||
const flakeOutput = updateFlakeInputs();
|
|
||||||
|
|
||||||
console.log(flakeOutput);
|
|
||||||
|
|
||||||
|
|
||||||
const dockerOutput = updateDocker();
|
|
||||||
|
|
||||||
console.log(dockerOutput);
|
|
||||||
|
|
||||||
|
|
||||||
const nodeModulesOutput = await updateNodeModules();
|
|
||||||
|
|
||||||
console.log(nodeModulesOutput);
|
|
||||||
|
|
||||||
|
|
||||||
const vuetorrentOutput = updateVuetorrent();
|
|
||||||
|
|
||||||
console.log(vuetorrentOutput);
|
|
||||||
|
|
||||||
|
|
||||||
// This doesn't need to be added to commit msgs
|
|
||||||
console.log(updateCustomPackage(
|
|
||||||
'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar',
|
|
||||||
));
|
|
||||||
console.log(updateCustomPackage('some-sass-language-server'));
|
|
||||||
|
|
||||||
// nix-update executions
|
|
||||||
let nixUpdateOutputs = '';
|
|
||||||
|
|
||||||
const updatePackage = (pkg: string): void => {
|
|
||||||
const execution = runNixUpdate(pkg);
|
|
||||||
|
|
||||||
nixUpdateOutputs += execution.stdout;
|
|
||||||
console.log(execution.stderr);
|
|
||||||
console.log(execution.stdout);
|
|
||||||
};
|
|
||||||
|
|
||||||
updatePackage('homepage');
|
|
||||||
|
|
||||||
|
|
||||||
spawnSync('nixFastBuild', [], {
|
|
||||||
shell: true,
|
|
||||||
stdio: [process.stdin, process.stdout, process.stderr],
|
|
||||||
});
|
|
||||||
|
|
||||||
const indentOutput = (output: string): string => {
|
|
||||||
return ` ${output.split('\n').join('\n ')}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const output = [
|
|
||||||
'chore: update sources\n\n',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (flakeOutput.length > 5) {
|
|
||||||
output.push(`Flake Inputs:\n${indentOutput(flakeOutput)}\n\n`);
|
|
||||||
}
|
|
||||||
if (dockerOutput.length > 5) {
|
|
||||||
output.push(`Docker Images:\n${indentOutput(dockerOutput)}\n`);
|
|
||||||
}
|
|
||||||
if (firefoxOutput.length > 5) {
|
|
||||||
output.push(`Firefox Addons:\n${indentOutput(firefoxOutput)}\n\n`);
|
|
||||||
}
|
|
||||||
if (nodeModulesOutput.length > 5) {
|
|
||||||
output.push(`Node modules:\n${indentOutput(nodeModulesOutput)}\n`);
|
|
||||||
}
|
|
||||||
if (vuetorrentOutput.length > 5) {
|
|
||||||
output.push(`Misc Sources:\n${indentOutput(vuetorrentOutput)}\n\n`);
|
|
||||||
}
|
|
||||||
if (nixUpdateOutputs.length > 5) {
|
|
||||||
output.push(`nix-update executions:\n${indentOutput(nixUpdateOutputs)}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args['f']) {
|
|
||||||
writeFileSync(args['f'] as string, output.join(''));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log(output.join(''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spawnSync('alejandra', ['-q', FLAKE], { shell: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
main();
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { readdirSync } from 'node:fs';
|
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE;
|
|
||||||
|
|
||||||
const updateImages = (imagePath: string): string | undefined => {
|
|
||||||
console.log(`Updating ${imagePath.split('/').at(-1)} images`);
|
|
||||||
|
|
||||||
const out = spawnSync('updateImages', [imagePath], { shell: true }).stdout.toString();
|
|
||||||
|
|
||||||
if (!out.startsWith('# Locked')) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateDocker = () => {
|
|
||||||
let updates = '';
|
|
||||||
|
|
||||||
updates += updateImages(`${FLAKE}/configurations/nos/modules/jellyfin`) ?? '';
|
|
||||||
updates += updateImages(`${FLAKE}/configurations/homie/modules/home-assistant/netdaemon`) ?? '';
|
|
||||||
|
|
||||||
const DIR = `${FLAKE}/configurations/nos/modules/docker`;
|
|
||||||
|
|
||||||
readdirSync(DIR, { withFileTypes: true, recursive: true }).forEach((path) => {
|
|
||||||
if (path.name === 'compose.nix') {
|
|
||||||
updates += updateImages(path.parentPath) ?? '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return updates;
|
|
||||||
};
|
|
|
@ -1,61 +0,0 @@
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
import { readFileSync } from 'node:fs';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE;
|
|
||||||
|
|
||||||
export const updateFirefoxAddons = () => {
|
|
||||||
console.log('Updating firefox addons using mozilla-addons-to-nix');
|
|
||||||
|
|
||||||
const DIR = `${FLAKE}/scopedPackages/firefox-addons`;
|
|
||||||
const GENERATED_FILE = `${DIR}/generated-firefox-addons.nix`;
|
|
||||||
const SLUGS = `${DIR}/addons.json`;
|
|
||||||
|
|
||||||
const nameMap = Object.fromEntries([...JSON.parse(readFileSync(SLUGS, 'utf-8'))]
|
|
||||||
.map((addon) => [addon.slug, addon.pname || addon.slug]));
|
|
||||||
|
|
||||||
const nixExpr = `'
|
|
||||||
x: let
|
|
||||||
inherit (builtins) attrValues filter hasAttr isAttrs map;
|
|
||||||
in
|
|
||||||
map (d: d.name) (filter (y:
|
|
||||||
isAttrs y &&
|
|
||||||
hasAttr "type" y &&
|
|
||||||
y.type == "derivation") (attrValues x))
|
|
||||||
'`;
|
|
||||||
|
|
||||||
const OLD_VERS = Object.fromEntries([...JSON.parse(spawnSync('nix', [
|
|
||||||
'eval',
|
|
||||||
'.#scopedPackages.x86_64-linux.firefoxAddons',
|
|
||||||
'--apply',
|
|
||||||
nixExpr,
|
|
||||||
'--json',
|
|
||||||
], { shell: true }).stdout.toString())]
|
|
||||||
.map((p) => {
|
|
||||||
const pname = p.replace(/-[0-9].*$/, '');
|
|
||||||
|
|
||||||
return [pname, p.replace(`${pname}-`, '')];
|
|
||||||
})
|
|
||||||
.filter((pinfo) => pinfo[0] !== 'frankerfacez'));
|
|
||||||
|
|
||||||
const NEW_VERS = Object.fromEntries(spawnSync(
|
|
||||||
'nix',
|
|
||||||
['run', 'sourcehut:~rycee/mozilla-addons-to-nix',
|
|
||||||
SLUGS, GENERATED_FILE],
|
|
||||||
{ shell: true },
|
|
||||||
).stdout
|
|
||||||
.toString()
|
|
||||||
.split('\n')
|
|
||||||
.map((p) => {
|
|
||||||
const pinfo = p.replace('Fetched ', '').split(' ');
|
|
||||||
|
|
||||||
return [nameMap[pinfo[0]], pinfo[2]];
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Object.keys(OLD_VERS)
|
|
||||||
.sort()
|
|
||||||
.filter((pname) => OLD_VERS[pname] !== NEW_VERS[pname])
|
|
||||||
.map((pname) => `${pname}: ${OLD_VERS[pname]} -> ${NEW_VERS[pname]}`)
|
|
||||||
.join('\n');
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE;
|
|
||||||
|
|
||||||
export const updateFlakeInputs = () => {
|
|
||||||
const output = spawnSync(
|
|
||||||
`git restore flake.lock &> /dev/null; nix flake update --flake ${FLAKE}` +
|
|
||||||
' |& grep -v "warning: updating lock file" |& grep -v "unpacking"',
|
|
||||||
[],
|
|
||||||
{ shell: true },
|
|
||||||
).stdout
|
|
||||||
.toString()
|
|
||||||
// Add an extra blank line between inputs
|
|
||||||
.split('\n•')
|
|
||||||
// Filter out some inputs
|
|
||||||
.filter((input) => ![
|
|
||||||
'systems',
|
|
||||||
'flake-utils',
|
|
||||||
'flake-parts',
|
|
||||||
'treefmt-nix',
|
|
||||||
'lib-aggregate',
|
|
||||||
'lib-aggregate/nixpkgs-lib',
|
|
||||||
'sops-nix/nixpkgs-stable',
|
|
||||||
'discord-overlay/Vencord-src',
|
|
||||||
'nix-gaming/umu',
|
|
||||||
].some((inputName) => input.startsWith(` Updated input '${inputName}'`)))
|
|
||||||
.join('\n\n•')
|
|
||||||
// help readability of git revs
|
|
||||||
.split('\n')
|
|
||||||
.map((l) => l
|
|
||||||
.replace(
|
|
||||||
/\/(.{40})\?narHash=sha256[^']*(.*)/,
|
|
||||||
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
|
|
||||||
)
|
|
||||||
.replace(
|
|
||||||
/\?ref.*&rev=(.{40})[^'&]*(.*)/,
|
|
||||||
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
|
|
||||||
))
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
import { readFileSync, writeFileSync } from 'node:fs';
|
|
||||||
|
|
||||||
|
|
||||||
export const parseArgs = () => {
|
|
||||||
const args = {} as Record<string, unknown>;
|
|
||||||
let lastFlag: string | null = null;
|
|
||||||
|
|
||||||
for (let i = 2; i < process.argv.length; ++i) {
|
|
||||||
const arg = process.argv[i];
|
|
||||||
|
|
||||||
if (arg.toString().startsWith('-')) {
|
|
||||||
lastFlag = arg.toString().replace(/^-{1,2}/, '');
|
|
||||||
args[lastFlag] = true;
|
|
||||||
}
|
|
||||||
else if (lastFlag) {
|
|
||||||
args[lastFlag] = arg;
|
|
||||||
lastFlag = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(`Could not parse args: ${arg.toString()}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseFetchurl = (url: string) => JSON.parse(spawnSync(
|
|
||||||
'nix', ['store', 'prefetch-file', '--refresh', '--json',
|
|
||||||
'--hash-type', 'sha256', url, '--name', '"escaped"'], { shell: true },
|
|
||||||
).stdout.toString()).hash;
|
|
||||||
|
|
||||||
export const replaceInFile = (replace: RegExp, replacement: string, file: string) => {
|
|
||||||
const fileContents = readFileSync(file);
|
|
||||||
|
|
||||||
const replaced = fileContents.toString().replace(replace, replacement);
|
|
||||||
|
|
||||||
writeFileSync(file, replaced);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const npmRun = (args: string[], workspaceDir: string) => spawnSync(
|
|
||||||
'npm', args, { cwd: workspaceDir },
|
|
||||||
).stdout.toString();
|
|
|
@ -1,73 +0,0 @@
|
||||||
import { writeFileSync } from 'node:fs';
|
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
|
|
||||||
import { parseFetchurl } from './lib';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE;
|
|
||||||
|
|
||||||
const genVueText = (
|
|
||||||
version: string,
|
|
||||||
hash: string,
|
|
||||||
url: string,
|
|
||||||
) => `# This file was autogenerated. DO NOT EDIT!
|
|
||||||
{
|
|
||||||
version = "${version}";
|
|
||||||
url = "${url}";
|
|
||||||
hash = "${hash}";
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const updateVuetorrent = () => {
|
|
||||||
const FILE = `${FLAKE}/configurations/nos/modules/qbittorrent/vuetorrent.nix`;
|
|
||||||
|
|
||||||
const OLD_VERSION = JSON.parse(spawnSync('nix',
|
|
||||||
['eval', '-f', FILE, '--json'],
|
|
||||||
{ shell: true }).stdout.toString()).version;
|
|
||||||
|
|
||||||
const VERSION = JSON.parse(spawnSync('curl',
|
|
||||||
['-s', 'https://api.github.com/repos/VueTorrent/VueTorrent/releases/latest'],
|
|
||||||
{ shell: true }).stdout.toString()).tag_name.replace('v', '');
|
|
||||||
|
|
||||||
const URL = `https://github.com/VueTorrent/VueTorrent/releases/download/v${VERSION}/vuetorrent.zip`;
|
|
||||||
const HASH = parseFetchurl(URL);
|
|
||||||
|
|
||||||
const fileText = genVueText(VERSION, HASH, URL);
|
|
||||||
|
|
||||||
writeFileSync(FILE, fileText);
|
|
||||||
|
|
||||||
return OLD_VERSION !== VERSION ? `Vuetorrent: ${OLD_VERSION} -> ${VERSION}` : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export const updateCustomPackage = (pkg: string) => spawnSync(
|
|
||||||
`nix run ${FLAKE}#${pkg}.update`,
|
|
||||||
[],
|
|
||||||
{ shell: true },
|
|
||||||
).stderr.toString();
|
|
||||||
|
|
||||||
|
|
||||||
const getAttrVersion = (attr: string): string => spawnSync('nix',
|
|
||||||
['eval', '--raw', `${FLAKE}#${attr}.version`],
|
|
||||||
{ shell: true }).stdout.toString();
|
|
||||||
|
|
||||||
export const runNixUpdate = (
|
|
||||||
attr: string,
|
|
||||||
options: string[] = [],
|
|
||||||
): { stdout: string, stderr: string } => {
|
|
||||||
const OLD_VERSION = getAttrVersion(attr);
|
|
||||||
|
|
||||||
const execution = spawnSync(
|
|
||||||
`nix-update --flake ${attr} --write-commit-message >(head -n 1 -) > /dev/null`,
|
|
||||||
options,
|
|
||||||
{ shell: true, cwd: FLAKE },
|
|
||||||
);
|
|
||||||
|
|
||||||
const NEW_VERSION = getAttrVersion(attr);
|
|
||||||
|
|
||||||
return {
|
|
||||||
stdout: OLD_VERSION !== NEW_VERSION ? execution.stdout.toString() : '',
|
|
||||||
stderr: execution.stderr.toString(),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,82 +0,0 @@
|
||||||
import { readPackageJSON, writePackageJSON } from 'pkg-types';
|
|
||||||
import { readdirSync } from 'node:fs';
|
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
|
|
||||||
import { replaceInFile, npmRun } from './lib';
|
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
const FLAKE = process.env.FLAKE as string;
|
|
||||||
|
|
||||||
|
|
||||||
const updatePackageJson = async(workspaceDir: string, updates: object) => {
|
|
||||||
const currentPackageJson = await readPackageJSON(`${workspaceDir}/package.json`);
|
|
||||||
|
|
||||||
const outdated = JSON.parse(npmRun(['outdated', '--json'], workspaceDir));
|
|
||||||
|
|
||||||
const updateDeps = (deps: string) => {
|
|
||||||
Object.keys(currentPackageJson[deps]).forEach((dep) => {
|
|
||||||
const versions = outdated[dep];
|
|
||||||
const current = versions?.wanted || versions?.current;
|
|
||||||
|
|
||||||
if (!current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current !== versions.latest) {
|
|
||||||
updates[dep] = `${current} -> ${versions.latest}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPackageJson[deps][dep] = versions.latest;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (currentPackageJson.dependencies) {
|
|
||||||
updateDeps('dependencies');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPackageJson.devDependencies) {
|
|
||||||
updateDeps('devDependencies');
|
|
||||||
}
|
|
||||||
|
|
||||||
await writePackageJSON(`${workspaceDir}/package.json`, currentPackageJson);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const prefetchNpmDeps = (workspaceDir: string): string => {
|
|
||||||
npmRun(['install', '--package-lock-only'], workspaceDir);
|
|
||||||
|
|
||||||
return spawnSync(
|
|
||||||
'prefetch-npm-deps',
|
|
||||||
[`${workspaceDir}/package-lock.json`],
|
|
||||||
).stdout.toString().replace('\n', '');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export default async() => {
|
|
||||||
const updates = {};
|
|
||||||
|
|
||||||
const packages = readdirSync(FLAKE, { withFileTypes: true, recursive: true });
|
|
||||||
|
|
||||||
for (const path of packages) {
|
|
||||||
if (
|
|
||||||
path.name === 'package.json' &&
|
|
||||||
!path.parentPath.includes('node_modules') &&
|
|
||||||
!path.parentPath.includes('apps/config')
|
|
||||||
) {
|
|
||||||
await updatePackageJson(path.parentPath, updates);
|
|
||||||
|
|
||||||
const hash = prefetchNpmDeps(path.parentPath);
|
|
||||||
|
|
||||||
replaceInFile(
|
|
||||||
/npmDepsHash = ".*";/,
|
|
||||||
`npmDepsHash = "${hash}";`,
|
|
||||||
`${path.parentPath}/default.nix`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.entries(updates)
|
|
||||||
.map(([key, dep]) => `${key}: ${dep}`)
|
|
||||||
.join('\n');
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"extends": "../config/tsconfig.base.json",
|
|
||||||
"includes": [
|
|
||||||
"*.ts",
|
|
||||||
"**/*.ts",
|
|
||||||
"*.js",
|
|
||||||
"**/*.js"
|
|
||||||
]
|
|
||||||
}
|
|
13
config/ags/bin/heart.sh
Executable file
13
config/ags/bin/heart.sh
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
FILE="$HOME/.config/.heart"
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
if grep -q "$FILE"; then
|
||||||
|
echo > "$FILE"
|
||||||
|
else
|
||||||
|
echo >> "$FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ "$1" == "toggle" ]] && toggle
|
15
config/ags/bin/launch-app.sh
Executable file
15
config/ags/bin/launch-app.sh
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
APP="$1"
|
||||||
|
EXEC="$2"
|
||||||
|
|
||||||
|
if [[ "$APP" == "thunderbird" ]]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace thunder
|
||||||
|
elif [[ "$APP" == "Spotify" ]]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace spot
|
||||||
|
elif [[ $(hyprctl clients | grep "$APP") != "" ]]; then
|
||||||
|
hyprctl dispatch focuswindow "^($APP)$"
|
||||||
|
else
|
||||||
|
hyprctl dispatch workspace empty
|
||||||
|
hyprctl dispatch exec "$EXEC"
|
||||||
|
fi
|
30
config/ags/bin/osk-toggle.sh
Executable file
30
config/ags/bin/osk-toggle.sh
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
state () {
|
||||||
|
if [[ $(busctl get-property --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 Visible) == "b true" ]]; then
|
||||||
|
echo "Running"
|
||||||
|
else
|
||||||
|
echo "Stopped"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle () {
|
||||||
|
if [[ $(busctl get-property --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 Visible) == "b true" ]]; then
|
||||||
|
echo "Running"
|
||||||
|
busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
|
||||||
|
else
|
||||||
|
echo "Stopped"
|
||||||
|
busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $1 == "getState" ]]; then
|
||||||
|
while true; do
|
||||||
|
sleep 0.2
|
||||||
|
state
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $1 == "toggle" ]];then
|
||||||
|
toggle
|
||||||
|
fi
|
33
config/ags/bin/qs-toggles.sh
Executable file
33
config/ags/bin/qs-toggles.sh
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
radio_status () {
|
||||||
|
radio_status=$(nmcli radio wifi)
|
||||||
|
if [[ $radio_status == "enabled" ]]; then
|
||||||
|
echo "on"
|
||||||
|
else
|
||||||
|
echo "off"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $1 == "toggle-radio" ]]; then
|
||||||
|
stat=$(radio_status)
|
||||||
|
if [[ $stat == "on" ]]; then
|
||||||
|
nmcli radio wifi off
|
||||||
|
else
|
||||||
|
nmcli radio wifi on
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILE='/home/matt/.config/.bluetooth'
|
||||||
|
get_state() {
|
||||||
|
if [[ "$(rfkill list | grep -A 1 hci0 | grep -o no)" == "no" ]]; then
|
||||||
|
echo " " > "$FILE"
|
||||||
|
else
|
||||||
|
echo " " > "$FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$1" == "blue-toggle" ]]; then
|
||||||
|
rfkill toggle bluetooth
|
||||||
|
get_state
|
||||||
|
fi
|
10
config/ags/bin/startup.sh
Executable file
10
config/ags/bin/startup.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
## Make bluetooth status persistent between reboots
|
||||||
|
if [[ ! -f "$HOME/.config/.bluetooth" ]]; then
|
||||||
|
echo > "$FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "$HOME/.config/.bluetooth"; then
|
||||||
|
rfkill block bluetooth
|
||||||
|
fi
|
38
config/ags/bin/tablet-toggle.sh
Executable file
38
config/ags/bin/tablet-toggle.sh
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
tablet() {
|
||||||
|
gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true
|
||||||
|
|
||||||
|
brightnessctl -d tpacpi::kbd_backlight s 0
|
||||||
|
|
||||||
|
"$HYPR_PATH"/autorotate.sh &
|
||||||
|
|
||||||
|
evtest --grab "/dev/input/by-path/platform-i8042-serio-0-event-kbd" &
|
||||||
|
evtest --grab "/dev/input/by-path/platform-i8042-serio-1-event-mouse" &
|
||||||
|
evtest --grab "/dev/input/by-path/platform-AMDI0010:02-event-mouse" &
|
||||||
|
evtest --grab "/dev/input/by-path/platform-thinkpad_acpi-event" &
|
||||||
|
evtest --grab "/dev/video-bus" &
|
||||||
|
}
|
||||||
|
|
||||||
|
laptop() {
|
||||||
|
gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false
|
||||||
|
|
||||||
|
brightnessctl -d tpacpi::kbd_backlight s 2
|
||||||
|
|
||||||
|
killall -r autorotate.sh
|
||||||
|
killall -r evtest
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle () {
|
||||||
|
if [[ "$(gsettings get org.gnome.desktop.a11y.applications screen-keyboard-enabled)" == "false" ]]; then
|
||||||
|
echo "Tablet"
|
||||||
|
tablet > /dev/null
|
||||||
|
else
|
||||||
|
echo "Laptop"
|
||||||
|
laptop > /dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $1 == "toggle" ]] && toggle
|
||||||
|
[[ $1 == "laptop" ]] && laptop
|
||||||
|
[[ $1 == "tablet" ]] && tablet
|
44
config/ags/config.js
Normal file
44
config/ags/config.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { exec, execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
|
import { Powermenu } from './js/powermenu.js';
|
||||||
|
import { Bar } from './js/bar/main.js';
|
||||||
|
import { NotificationCenter } from './js/notifications/center.js';
|
||||||
|
import { NotificationsPopupList } from './js/notifications/popup.js'
|
||||||
|
import { Calendar } from './js/date.js';
|
||||||
|
import { QuickSettings } from './js/quick-settings/main.js';
|
||||||
|
import Overview from './js/overview/main.js';
|
||||||
|
import AppLauncher from './js/applauncher/main.js';
|
||||||
|
|
||||||
|
import { Closer, closeAll } from './js/misc/closer.js';
|
||||||
|
ags.App.closeAll = () => closeAll();
|
||||||
|
|
||||||
|
const scss = ags.App.configDir + '/scss/main.scss';
|
||||||
|
const css = ags.App.configDir + '/style.css';
|
||||||
|
|
||||||
|
exec(`sassc ${scss} ${css}`);
|
||||||
|
|
||||||
|
execAsync(['bash', '-c', '$AGS_PATH/startup.sh']).catch(print);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
style: css,
|
||||||
|
notificationPopupTimeout: 5000,
|
||||||
|
cacheNotificationActions: true,
|
||||||
|
closeWindowDelay: {
|
||||||
|
'quick-settings': 500,
|
||||||
|
'notification-center': 500,
|
||||||
|
'calendar': 500,
|
||||||
|
'powermenu': 500,
|
||||||
|
'overview': 500,
|
||||||
|
'applauncher': 500,
|
||||||
|
},
|
||||||
|
windows: [
|
||||||
|
Powermenu,
|
||||||
|
Bar,
|
||||||
|
Closer,
|
||||||
|
NotificationCenter,
|
||||||
|
NotificationsPopupList,
|
||||||
|
Calendar,
|
||||||
|
QuickSettings,
|
||||||
|
Overview,
|
||||||
|
AppLauncher,
|
||||||
|
],
|
||||||
|
};
|
134
config/ags/js/applauncher/main.js
Normal file
134
config/ags/js/applauncher/main.js
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
const { App } = ags;
|
||||||
|
const { Applications } = ags.Service;
|
||||||
|
const { Label, Box, Icon, Button, Scrollable, Entry, Window, EventBox } = ags.Widget;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
import { PopUp } from '../misc/popup.js';
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
apps: {
|
||||||
|
apps: 'view-app-grid-symbolic',
|
||||||
|
search: 'preferences-system-search-symbolic',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppItem = (app, window) => {
|
||||||
|
if (app.app.get_string('Icon') == 'Nextcloud')
|
||||||
|
return;
|
||||||
|
|
||||||
|
return Button({
|
||||||
|
className: 'app',
|
||||||
|
connections: [['clicked', () => {
|
||||||
|
App.closeWindow(window);
|
||||||
|
app.launch();
|
||||||
|
}]],
|
||||||
|
child: Box({
|
||||||
|
children: [
|
||||||
|
Icon({
|
||||||
|
icon: app.app.get_string('Icon'),
|
||||||
|
size: 42,
|
||||||
|
}),
|
||||||
|
Box({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Label({
|
||||||
|
className: 'title',
|
||||||
|
label: app.name,
|
||||||
|
xalign: 0,
|
||||||
|
valign: 'center',
|
||||||
|
ellipsize: 3,
|
||||||
|
}),
|
||||||
|
Label({
|
||||||
|
className: 'description',
|
||||||
|
label: app.description || '',
|
||||||
|
wrap: true,
|
||||||
|
xalign: 0,
|
||||||
|
justification: 'left',
|
||||||
|
valign: 'center',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const Applauncher = ({ windowName = 'applauncher' } = {}) => {
|
||||||
|
const list = Box({ vertical: true });
|
||||||
|
const placeholder = Label({
|
||||||
|
label: " Couldn't find a match",
|
||||||
|
className: 'placeholder',
|
||||||
|
});
|
||||||
|
const entry = Entry({
|
||||||
|
hexpand: true,
|
||||||
|
placeholderText: 'Search',
|
||||||
|
onAccept: ({ text }) => {
|
||||||
|
const list = Applications.query(text);
|
||||||
|
if (list[0]) {
|
||||||
|
App.toggleWindow(windowName);
|
||||||
|
list[0].launch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange: ({ text }) => {
|
||||||
|
list.children = Applications.query(text).map(app => [
|
||||||
|
Separator(4),
|
||||||
|
AppItem(app, windowName),
|
||||||
|
]).flat();
|
||||||
|
list.add(Separator(4));
|
||||||
|
list.show_all();
|
||||||
|
|
||||||
|
placeholder.visible = list.children.length === 1;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Box({
|
||||||
|
className: 'applauncher',
|
||||||
|
properties: [['list', list]],
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Box({
|
||||||
|
className: 'header',
|
||||||
|
children: [
|
||||||
|
Icon(icons.apps.search),
|
||||||
|
entry,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Scrollable({
|
||||||
|
hscroll: 'never',
|
||||||
|
child: Box({
|
||||||
|
vertical: true,
|
||||||
|
children: [list, placeholder],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
connections: [[App, (_b, name, visible) => {
|
||||||
|
if (name !== windowName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry.set_text('-'); // force onChange
|
||||||
|
entry.set_text('');
|
||||||
|
if (visible)
|
||||||
|
entry.grab_focus();
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: make it so I don't have to click to trigger onHoverLost
|
||||||
|
export default Window({
|
||||||
|
name: 'applauncher',
|
||||||
|
popup: true,
|
||||||
|
focusable: true,
|
||||||
|
layer: 'overlay',
|
||||||
|
child: EventBox({
|
||||||
|
onHover: box => {
|
||||||
|
box.get_parent().focusable = true
|
||||||
|
},
|
||||||
|
onHoverLost: box => {
|
||||||
|
box.get_parent().focusable = false
|
||||||
|
},
|
||||||
|
child: PopUp({
|
||||||
|
name: 'applauncher',
|
||||||
|
child: Applauncher(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
55
config/ags/js/bar/audio.js
Normal file
55
config/ags/js/bar/audio.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
const { Audio } = ags.Service;
|
||||||
|
const { Label, Box, Icon, Stack, Button, Slider } = ags.Widget;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
const items = {
|
||||||
|
101: 'audio-volume-overamplified-symbolic',
|
||||||
|
67: 'audio-volume-high-symbolic',
|
||||||
|
34: 'audio-volume-medium-symbolic',
|
||||||
|
1: 'audio-volume-low-symbolic',
|
||||||
|
0: 'audio-volume-muted-symbolic',
|
||||||
|
};
|
||||||
|
|
||||||
|
const SpeakerIndicator = params => Icon({
|
||||||
|
...params,
|
||||||
|
icon: '',
|
||||||
|
connections: [[Audio, icon => {
|
||||||
|
if (Audio.speaker) {
|
||||||
|
if (Audio.speaker.isMuted) {
|
||||||
|
icon.icon = items[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const vol = Audio.speaker.volume * 100;
|
||||||
|
for (const threshold of [-1, 0, 33, 66, 100]) {
|
||||||
|
if (vol > threshold + 1) {
|
||||||
|
icon.icon = items[threshold + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'speaker-changed']],
|
||||||
|
});
|
||||||
|
|
||||||
|
const SpeakerPercentLabel = params => Label({
|
||||||
|
...params,
|
||||||
|
connections: [[Audio, label => {
|
||||||
|
if (Audio.speaker) {
|
||||||
|
label.label = Math.round(Audio.speaker.volume * 100) + '%';
|
||||||
|
}
|
||||||
|
}, 'speaker-changed']],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AudioIndicator = EventBox({
|
||||||
|
onPrimaryClickRelease: 'pavucontrol',
|
||||||
|
className: 'toggle-off',
|
||||||
|
child: Box({
|
||||||
|
className: 'audio',
|
||||||
|
children: [
|
||||||
|
SpeakerIndicator(),
|
||||||
|
Separator(5),
|
||||||
|
SpeakerPercentLabel(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
58
config/ags/js/bar/battery.js
Normal file
58
config/ags/js/bar/battery.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
const { Battery } = ags.Service;
|
||||||
|
const { Label, Icon, Stack, Box } = ags.Widget;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
|
||||||
|
const icons = charging => ([
|
||||||
|
...Array.from({ length: 10 }, (_, i) => i * 10).map(i => ([
|
||||||
|
`${i}`, Icon({
|
||||||
|
className: `${i} ${charging ? 'charging' : 'discharging'}`,
|
||||||
|
icon: `battery-level-${i}${charging ? '-charging' : ''}-symbolic`,
|
||||||
|
}),
|
||||||
|
])),
|
||||||
|
['100', Icon({
|
||||||
|
className: `100 ${charging ? 'charging' : 'discharging'}`,
|
||||||
|
icon: `battery-level-100${charging ? '-charged' : ''}-symbolic`,
|
||||||
|
})],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const Indicators = charging => Stack({
|
||||||
|
items: icons(charging),
|
||||||
|
connections: [[Battery, stack => {
|
||||||
|
stack.shown = `${Math.floor(Battery.percent / 10) * 10}`;
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Indicator = ({
|
||||||
|
charging = Indicators(true),
|
||||||
|
discharging = Indicators(false),
|
||||||
|
...params
|
||||||
|
} = {}) => Stack({
|
||||||
|
...params,
|
||||||
|
className: 'battery-indicator',
|
||||||
|
items: [
|
||||||
|
['true', charging],
|
||||||
|
['false', discharging],
|
||||||
|
],
|
||||||
|
connections: [[Battery, stack => {
|
||||||
|
stack.shown = `${Battery.charging || Battery.charged}`;
|
||||||
|
stack.toggleClassName('charging', Battery.charging);
|
||||||
|
stack.toggleClassName('charged', Battery.charged);
|
||||||
|
stack.toggleClassName('low', Battery.percent < 20);
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
|
||||||
|
const LevelLabel = params => Label({
|
||||||
|
...params,
|
||||||
|
className: 'label',
|
||||||
|
connections: [[Battery, label => label.label = `${Battery.percent}%`]],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const BatteryIndicator = Box({
|
||||||
|
className: 'toggle-off battery',
|
||||||
|
children: [
|
||||||
|
Indicator(),
|
||||||
|
Separator(5),
|
||||||
|
LevelLabel(),
|
||||||
|
],
|
||||||
|
});
|
35
config/ags/js/bar/brightness.js
Normal file
35
config/ags/js/bar/brightness.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const { ProgressBar, Overlay, Box } = ags.Widget;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
import { Heart } from './heart.js';
|
||||||
|
|
||||||
|
export const Brightness = Overlay({
|
||||||
|
setup: widget => {
|
||||||
|
widget.set_tooltip_text('Brightness');
|
||||||
|
},
|
||||||
|
child: ProgressBar({
|
||||||
|
className: 'toggle-off brightness',
|
||||||
|
connections: [
|
||||||
|
[200, progress => {
|
||||||
|
execAsync('brightnessctl get').then(out => {
|
||||||
|
let br = out / 255;
|
||||||
|
if (br > 0.33) {
|
||||||
|
progress.value = br;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
progress.value = 0.33;
|
||||||
|
}
|
||||||
|
}).catch(print);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
overlays: [
|
||||||
|
Box({
|
||||||
|
style: 'color: #CBA6F7;',
|
||||||
|
children: [
|
||||||
|
Separator(25),
|
||||||
|
Heart,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
34
config/ags/js/bar/clock.js
Normal file
34
config/ags/js/bar/clock.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const { Box, Label } = ags.Widget;
|
||||||
|
const { toggleWindow } = ags.App;
|
||||||
|
const { DateTime } = imports.gi.GLib;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
const ClockModule = ({
|
||||||
|
interval = 1000,
|
||||||
|
...params
|
||||||
|
}) => Label({
|
||||||
|
...params,
|
||||||
|
className: 'clock',
|
||||||
|
connections: [
|
||||||
|
[interval, label => {
|
||||||
|
var time = DateTime.new_now_local();
|
||||||
|
label.label = time.format('%a. ') + time.get_day_of_month() + time.format(' %b. %H:%M');
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Clock = EventBox({
|
||||||
|
className: 'toggle-off',
|
||||||
|
onPrimaryClickRelease: () => toggleWindow('calendar'),
|
||||||
|
connections: [
|
||||||
|
[ags.App, (box, windowName, visible) => {
|
||||||
|
if (windowName == 'calendar') {
|
||||||
|
box.toggleClassName('toggle-on', visible);
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
child: Box({
|
||||||
|
child: ClockModule({}),
|
||||||
|
}),
|
||||||
|
});
|
13
config/ags/js/bar/current-window.js
Normal file
13
config/ags/js/bar/current-window.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const { Hyprland } = ags.Service;
|
||||||
|
const { Label } = ags.Widget;
|
||||||
|
const { Gtk } = imports.gi;
|
||||||
|
|
||||||
|
export const CurrentWindow = Label({
|
||||||
|
style: 'color: #CBA6F7; font-size: 18px',
|
||||||
|
truncate: 'end',
|
||||||
|
connections: [
|
||||||
|
[Hyprland, label => {
|
||||||
|
label.label = Hyprland.active.client.title;
|
||||||
|
}, 'changed'],
|
||||||
|
],
|
||||||
|
});
|
32
config/ags/js/bar/gesture.js
Normal file
32
config/ags/js/bar/gesture.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
const { Window, CenterBox, EventBox, Button } = ags.Widget;
|
||||||
|
const { openWindow } = ags.App;
|
||||||
|
const { Gtk, Gdk } = imports.gi;
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
export const Gesture = ({
|
||||||
|
child,
|
||||||
|
...params
|
||||||
|
}) => {
|
||||||
|
let w = EventBox({
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
|
let gesture = Gtk.GestureSwipe.new(w);
|
||||||
|
|
||||||
|
w.child = CenterBox({
|
||||||
|
children: [
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
|
||||||
|
[gesture, box => {
|
||||||
|
const velocity = gesture.get_velocity()[1];
|
||||||
|
if (velocity < -100)
|
||||||
|
openWindow('quick-settings');
|
||||||
|
}, 'update'],
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return w;
|
||||||
|
};
|
26
config/ags/js/bar/heart.js
Normal file
26
config/ags/js/bar/heart.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
const { Box, Label } = ags.Widget;
|
||||||
|
const { subprocess, execAsync } = ags.Utils;
|
||||||
|
const deflisten = subprocess;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
deflisten(
|
||||||
|
['bash', '-c', 'tail -f /home/matt/.config/.heart'],
|
||||||
|
(output) => {
|
||||||
|
Heart.child.children[0].label = ' ' + output;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
export const Heart = EventBox({
|
||||||
|
halign: 'center',
|
||||||
|
onPrimaryClickRelease: () => {
|
||||||
|
execAsync(['bash', '-c', '$AGS_PATH/heart.sh toggle']).catch(print);
|
||||||
|
},
|
||||||
|
child: Box({
|
||||||
|
className: 'heart-toggle',
|
||||||
|
vertical: false,
|
||||||
|
|
||||||
|
child: Label({
|
||||||
|
label: '',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
79
config/ags/js/bar/main.js
Normal file
79
config/ags/js/bar/main.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
const { Window, CenterBox, Box } = ags.Widget;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
import { CurrentWindow } from './current-window.js';
|
||||||
|
import { Workspaces } from './workspaces.js';
|
||||||
|
import { OskToggle } from './osk-toggle.js';
|
||||||
|
import { TabletToggle } from './tablet-toggle.js';
|
||||||
|
import { QsToggle } from './quick-settings.js';
|
||||||
|
import { NotifButton } from './notif-button.js';
|
||||||
|
import { Clock } from './clock.js';
|
||||||
|
import { SysTray } from './systray.js';
|
||||||
|
import { BatteryIndicator } from './battery.js';
|
||||||
|
import { Brightness } from './brightness.js';
|
||||||
|
import { AudioIndicator } from './audio.js';
|
||||||
|
import { Gesture } from './gesture.js';
|
||||||
|
|
||||||
|
export const Bar = Window({
|
||||||
|
name: 'bar',
|
||||||
|
layer: 'overlay',
|
||||||
|
anchor: 'top left right',
|
||||||
|
exclusive: true,
|
||||||
|
|
||||||
|
child: Gesture({
|
||||||
|
child: CenterBox({
|
||||||
|
className: 'transparent',
|
||||||
|
halign: 'fill',
|
||||||
|
style: 'margin: 5px',
|
||||||
|
vertical: false,
|
||||||
|
|
||||||
|
startWidget: Box({
|
||||||
|
halign: 'start',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
OskToggle,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
TabletToggle,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
SysTray,
|
||||||
|
|
||||||
|
AudioIndicator,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
Brightness,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
Workspaces,
|
||||||
|
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
centerWidget: CurrentWindow,
|
||||||
|
|
||||||
|
endWidget: Box({
|
||||||
|
halign: 'end',
|
||||||
|
children: [
|
||||||
|
BatteryIndicator,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
Clock,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
NotifButton,
|
||||||
|
|
||||||
|
Separator(12),
|
||||||
|
|
||||||
|
QsToggle,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
54
config/ags/js/bar/notif-button.js
Normal file
54
config/ags/js/bar/notif-button.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
const { Box, Label, Icon } = ags.Widget;
|
||||||
|
const { toggleWindow } = ags.App;
|
||||||
|
const { Notifications } = ags.Service;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
export const NotifButton = EventBox({
|
||||||
|
className: 'toggle-off',
|
||||||
|
onPrimaryClickRelease: () => toggleWindow('notification-center'),
|
||||||
|
connections: [
|
||||||
|
[ags.App, (box, windowName, visible) => {
|
||||||
|
if (windowName == 'notification-center') {
|
||||||
|
box.toggleClassName('toggle-on', visible);
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
child: Box({
|
||||||
|
className: 'notif-panel',
|
||||||
|
vertical: false,
|
||||||
|
children: [
|
||||||
|
Separator(28),
|
||||||
|
|
||||||
|
Icon({
|
||||||
|
connections: [
|
||||||
|
[Notifications, icon => {
|
||||||
|
if (Notifications.dnd) {
|
||||||
|
icon.icon = 'notification-disabled-symbolic'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Notifications.notifications.length > 0) {
|
||||||
|
icon.icon = 'notification-new-symbolic'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.icon = 'notification-symbolic'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Separator(8),
|
||||||
|
|
||||||
|
Label({
|
||||||
|
connections: [
|
||||||
|
[Notifications, label => {
|
||||||
|
label.label = String(Notifications.notifications.length);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
41
config/ags/js/bar/osk-toggle.js
Normal file
41
config/ags/js/bar/osk-toggle.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const { Box, Label } = ags.Widget;
|
||||||
|
const { subprocess } = ags.Utils;
|
||||||
|
const deflisten = subprocess;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
deflisten(
|
||||||
|
['bash', '-c', '$AGS_PATH/osk-toggle.sh getState'],
|
||||||
|
(output) => {
|
||||||
|
if (output == 'Running') {
|
||||||
|
OskToggle.toggleClassName('toggle-on', true);
|
||||||
|
} else {
|
||||||
|
OskToggle.toggleClassName('toggle-on', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
export const OskToggle = EventBox({
|
||||||
|
className: 'toggle-off',
|
||||||
|
onPrimaryClickRelease: function() {
|
||||||
|
subprocess(
|
||||||
|
['bash', '-c', '$AGS_PATH/osk-toggle.sh toggle'],
|
||||||
|
(output) => {
|
||||||
|
if (output == 'Running') {
|
||||||
|
OskToggle.toggleClassName('toggle-on', false);
|
||||||
|
} else {
|
||||||
|
OskToggle.toggleClassName('toggle-on', true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Box({
|
||||||
|
className: 'osk-toggle',
|
||||||
|
vertical: false,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
Label({
|
||||||
|
label: " ",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
23
config/ags/js/bar/quick-settings.js
Normal file
23
config/ags/js/bar/quick-settings.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
const { Box, Label } = ags.Widget;
|
||||||
|
const { toggleWindow } = ags.App;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
export const QsToggle = EventBox({
|
||||||
|
className: 'toggle-off',
|
||||||
|
onPrimaryClickRelease: () => toggleWindow('quick-settings'),
|
||||||
|
connections: [
|
||||||
|
[ags.App, (box, windowName, visible) => {
|
||||||
|
if (windowName == 'quick-settings') {
|
||||||
|
box.toggleClassName('toggle-on', visible);
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
child: Box({
|
||||||
|
className: 'quick-settings-toggle',
|
||||||
|
vertical: false,
|
||||||
|
child: Label({
|
||||||
|
label: " ",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
57
config/ags/js/bar/systray.js
Normal file
57
config/ags/js/bar/systray.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
const { SystemTray } = ags.Service;
|
||||||
|
const { Box, Revealer, Icon, MenuItem } = ags.Widget;
|
||||||
|
const { Gtk } = imports.gi;
|
||||||
|
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
|
||||||
|
const SysTrayItem = item => MenuItem({
|
||||||
|
className: 'tray-item',
|
||||||
|
child: Icon({
|
||||||
|
size: 24,
|
||||||
|
}),
|
||||||
|
submenu: item.menu,
|
||||||
|
connections: [[item, btn => {
|
||||||
|
btn.child.icon = item.icon;
|
||||||
|
btn.tooltipMarkup = item.tooltipMarkup;
|
||||||
|
}]]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SysTray = Revealer({
|
||||||
|
transition: 'slide_right',
|
||||||
|
connections: [[SystemTray, rev => {
|
||||||
|
rev.revealChild = rev.child.children[0].get_children().length > 0;
|
||||||
|
}]],
|
||||||
|
child: Box({
|
||||||
|
children: [
|
||||||
|
ags.Widget({
|
||||||
|
type: Gtk.MenuBar,
|
||||||
|
className: 'sys-tray',
|
||||||
|
properties: [
|
||||||
|
['items', new Map()],
|
||||||
|
['onAdded', (box, id) => {
|
||||||
|
const item = SystemTray.getItem(id);
|
||||||
|
if (box._items.has(id) || !item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const widget = SysTrayItem(item);
|
||||||
|
box._items.set(id, widget);
|
||||||
|
box.add(widget);
|
||||||
|
box.show_all();
|
||||||
|
}],
|
||||||
|
['onRemoved', (box, id) => {
|
||||||
|
if (!box._items.has(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
box._items.get(id).destroy();
|
||||||
|
box._items.delete(id);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
[SystemTray, (box, id) => box._onAdded(box, id), 'added'],
|
||||||
|
[SystemTray, (box, id) => box._onRemoved(box, id), 'removed'],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Separator(12),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
28
config/ags/js/bar/tablet-toggle.js
Normal file
28
config/ags/js/bar/tablet-toggle.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
const { Box, Label } = ags.Widget;
|
||||||
|
const { subprocess } = ags.Utils;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
export const TabletToggle = EventBox({
|
||||||
|
className: 'toggle-off',
|
||||||
|
onPrimaryClickRelease: function() {
|
||||||
|
subprocess(
|
||||||
|
['bash', '-c', '$AGS_PATH/tablet-toggle.sh toggle'],
|
||||||
|
(output) => {
|
||||||
|
print(output)
|
||||||
|
if (output == 'Tablet') {
|
||||||
|
TabletToggle.toggleClassName('toggle-on', true);
|
||||||
|
} else {
|
||||||
|
TabletToggle.toggleClassName('toggle-on', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Box({
|
||||||
|
className: 'tablet-toggle',
|
||||||
|
vertical: false,
|
||||||
|
child: Label({
|
||||||
|
label: " ",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
70
config/ags/js/bar/workspaces.js
Normal file
70
config/ags/js/bar/workspaces.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
const { Hyprland, Applications } = ags.Service;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
const { Box, Button, Label, Revealer } = ags.Widget;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
const Workspace = ({ i } = {}) =>
|
||||||
|
Revealer({
|
||||||
|
transition: "slide_right",
|
||||||
|
properties: [
|
||||||
|
['id', i],
|
||||||
|
],
|
||||||
|
child: EventBox({
|
||||||
|
tooltipText: `${i}`,
|
||||||
|
onPrimaryClickRelease: () => execAsync(`hyprctl dispatch workspace ${i}`).catch(print),
|
||||||
|
child: Box({
|
||||||
|
className: 'button',
|
||||||
|
child: Label(`${i}`),
|
||||||
|
connections: [
|
||||||
|
[Hyprland, btn => {
|
||||||
|
const occupied = Hyprland.getWorkspace(i)?.windows > 0;
|
||||||
|
btn.toggleClassName('active', Hyprland.active.workspace.id === i);
|
||||||
|
btn.toggleClassName('occupied', occupied);
|
||||||
|
btn.toggleClassName('empty', !occupied);
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Workspaces = Box({
|
||||||
|
className: 'workspaces',
|
||||||
|
children: [EventBox({
|
||||||
|
child: Box({
|
||||||
|
properties: [
|
||||||
|
['workspaces'],
|
||||||
|
|
||||||
|
['refresh', box => {
|
||||||
|
box.children.forEach(rev => rev.reveal_child = false);
|
||||||
|
box._workspaces.forEach(ws => {
|
||||||
|
ws.revealChild = true;
|
||||||
|
});
|
||||||
|
}],
|
||||||
|
|
||||||
|
['updateWs', box => {
|
||||||
|
Hyprland.workspaces.forEach(ws => {
|
||||||
|
let currentWs = box.children.find(ch => ch._id == ws.id);
|
||||||
|
if (!currentWs && ws.id > 0) {
|
||||||
|
box.add(Workspace({ i: ws.id}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
box.show_all();
|
||||||
|
|
||||||
|
// Make sure the order is correct
|
||||||
|
box._workspaces.forEach((workspace, i) => {
|
||||||
|
workspace.get_parent().reorder_child(workspace, i);
|
||||||
|
});
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
connections: [[Hyprland, box => {
|
||||||
|
box._workspaces = box.children.filter(ch => {
|
||||||
|
return Hyprland.workspaces.find(ws => ws.id == ch._id)
|
||||||
|
}).sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
|
box._updateWs(box);
|
||||||
|
box._refresh(box);
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
})],
|
||||||
|
});
|
88
config/ags/js/date.js
Normal file
88
config/ags/js/date.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
const { Box, Label, Window } = ags.Widget;
|
||||||
|
const { Gtk } = imports.gi;
|
||||||
|
const { DateTime } = imports.gi.GLib;
|
||||||
|
|
||||||
|
import { PopUp } from './misc/popup.js';
|
||||||
|
|
||||||
|
const Divider = () => Box({
|
||||||
|
className: 'divider',
|
||||||
|
vertical: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Time = () => Box({
|
||||||
|
className: 'timebox',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'time-container',
|
||||||
|
halign: 'center',
|
||||||
|
valign: 'center',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Label({
|
||||||
|
className: 'content',
|
||||||
|
label: 'hour',
|
||||||
|
connections: [[1000, label => {
|
||||||
|
label.label = DateTime.new_now_local().format('%H');
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Divider(),
|
||||||
|
|
||||||
|
Label({
|
||||||
|
className: 'content',
|
||||||
|
label: 'minute',
|
||||||
|
connections: [[1000, label => {
|
||||||
|
label.label = DateTime.new_now_local().format('%M');
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'date-container',
|
||||||
|
halign: 'center',
|
||||||
|
child: Label({
|
||||||
|
style: 'font-size: 20px',
|
||||||
|
label: 'complete date',
|
||||||
|
connections: [[1000, label => {
|
||||||
|
var time = DateTime.new_now_local();
|
||||||
|
label.label = time.format("%A, %B ") + time.get_day_of_month() + time.format(", %Y");
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const CalendarWidget = () => Box({
|
||||||
|
className: 'cal-box',
|
||||||
|
child: ags.Widget({
|
||||||
|
type: Gtk.Calendar,
|
||||||
|
showDayNames: true,
|
||||||
|
showHeading: true,
|
||||||
|
className: 'cal',
|
||||||
|
connections: [/* */]
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Calendar = Window({
|
||||||
|
name: 'calendar',
|
||||||
|
layer: 'overlay',
|
||||||
|
popup: true,
|
||||||
|
anchor: 'top right',
|
||||||
|
margin: [ 8, 182, 0, 0],
|
||||||
|
child: PopUp({
|
||||||
|
name: 'calendar',
|
||||||
|
child: Box({
|
||||||
|
className: 'date',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Time(),
|
||||||
|
CalendarWidget(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
82
config/ags/js/media-player/gesture.js
Normal file
82
config/ags/js/media-player/gesture.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
const { Box, Overlay, EventBox } = ags.Widget;
|
||||||
|
const { Gtk } = imports.gi;
|
||||||
|
|
||||||
|
const MAX_OFFSET = 200;
|
||||||
|
const OFFSCREEN = 500;
|
||||||
|
const TRANSITION = 'transition: margin 0.5s ease, opacity 3s ease;';
|
||||||
|
|
||||||
|
export default ({ properties, connections, params } = {}) => {
|
||||||
|
let widget = EventBox();
|
||||||
|
|
||||||
|
let gesture = Gtk.GestureDrag.new(widget)
|
||||||
|
|
||||||
|
widget.child = Overlay({
|
||||||
|
...params,
|
||||||
|
properties: [
|
||||||
|
...properties,
|
||||||
|
['dragging', false],
|
||||||
|
],
|
||||||
|
child: Box({className: 'player'}),
|
||||||
|
connections: [
|
||||||
|
...connections,
|
||||||
|
|
||||||
|
[gesture, overlay => {
|
||||||
|
if (overlay.overlays.length <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
overlay._dragging = true;
|
||||||
|
const offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
|
let playerBox = overlay.get_children().at(-1);
|
||||||
|
if (offset >= 0) {
|
||||||
|
playerBox.setStyle(`margin-left: ${offset}px;
|
||||||
|
margin-right: -${offset}px;
|
||||||
|
${playerBox._bgStyle}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let newOffset = Math.abs(offset);
|
||||||
|
playerBox.setStyle(`margin-left: -${newOffset}px;
|
||||||
|
margin-right: ${newOffset}px;
|
||||||
|
${playerBox._bgStyle}`);
|
||||||
|
}
|
||||||
|
overlay._selected = playerBox;
|
||||||
|
}, 'drag-update'],
|
||||||
|
|
||||||
|
[gesture, overlay => {
|
||||||
|
if (overlay.overlays.length <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
overlay._dragging = false;
|
||||||
|
const offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
|
let playerBox = overlay.get_children().at(-1);
|
||||||
|
|
||||||
|
if (Math.abs(offset) > MAX_OFFSET) {
|
||||||
|
if (offset >= 0) {
|
||||||
|
playerBox.setStyle(`${TRANSITION}
|
||||||
|
margin-left: ${OFFSCREEN}px;
|
||||||
|
margin-right: -${OFFSCREEN}px;
|
||||||
|
opacity: 0;
|
||||||
|
${playerBox._bgStyle}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
playerBox.setStyle(`${TRANSITION}
|
||||||
|
margin-left: -${OFFSCREEN}px;
|
||||||
|
margin-right: ${OFFSCREEN}px;
|
||||||
|
opacity: 0;
|
||||||
|
${playerBox._bgStyle}`);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
overlay.reorder_overlay(playerBox, 0);
|
||||||
|
playerBox.setStyle(playerBox._bgStyle);
|
||||||
|
overlay._selected = overlay.get_children().at(-1);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
playerBox.setStyle(`${TRANSITION} ${playerBox._bgStyle}`);
|
||||||
|
|
||||||
|
}, 'drag-end'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return widget;
|
||||||
|
};
|
345
config/ags/js/media-player/mpris.js
Normal file
345
config/ags/js/media-player/mpris.js
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
const { execAsync, lookUpIcon } = ags.Utils;
|
||||||
|
const { Mpris } = ags.Service;
|
||||||
|
const { Button, Icon, Label, Stack, Slider, CenterBox, Box } = ags.Widget;
|
||||||
|
const { Gdk } = imports.gi;
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
mpris: {
|
||||||
|
fallback: 'audio-x-generic-symbolic',
|
||||||
|
shuffle: {
|
||||||
|
enabled: '',
|
||||||
|
disabled: '',
|
||||||
|
},
|
||||||
|
loop: {
|
||||||
|
none: '',
|
||||||
|
track: '',
|
||||||
|
playlist: '',
|
||||||
|
},
|
||||||
|
playing: ' ',
|
||||||
|
paused: ' ',
|
||||||
|
stopped: ' ',
|
||||||
|
prev: '',
|
||||||
|
next: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CoverArt = (player, params) => CenterBox({
|
||||||
|
...params,
|
||||||
|
vertical: true,
|
||||||
|
properties: [['bgStyle', '']],
|
||||||
|
connections: [
|
||||||
|
[player, box => {
|
||||||
|
execAsync(['bash', '-c', `[[ -f "${player.coverPath}" ]] && coloryou "${player.coverPath}"`])
|
||||||
|
.then(out => {
|
||||||
|
if (!Mpris.players.find(p => player === p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
player.colors.value = JSON.parse(out);
|
||||||
|
|
||||||
|
box._bgStyle = `background: radial-gradient(circle,
|
||||||
|
rgba(0, 0, 0, 0.4) 30%,
|
||||||
|
${player.colors.value.imageAccent}),
|
||||||
|
url("${player.coverPath}");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;`;
|
||||||
|
if (!box.get_parent()._dragging)
|
||||||
|
box.setStyle(box._bgStyle);
|
||||||
|
}).catch(err => { if (err !== "") print(err) });
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TitleLabel = (player, params) => Label({
|
||||||
|
...params,
|
||||||
|
xalign: 0,
|
||||||
|
maxWidthChars: 40,
|
||||||
|
truncate: 'end',
|
||||||
|
justification: 'left',
|
||||||
|
className: 'title',
|
||||||
|
binds: [['label', player, 'trackTitle']],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ArtistLabel = (player, params) => Label({
|
||||||
|
...params,
|
||||||
|
xalign: 0,
|
||||||
|
maxWidthChars: 40,
|
||||||
|
truncate: 'end',
|
||||||
|
justification: 'left',
|
||||||
|
className: 'artist',
|
||||||
|
connections: [[player, label => {
|
||||||
|
label.label = player.trackArtists.join(', ') || '';
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PlayerIcon = (player, { symbolic = true, ...params } = {}) => {
|
||||||
|
let MainIcon = Icon({
|
||||||
|
...params,
|
||||||
|
className: 'player-icon',
|
||||||
|
size: 32,
|
||||||
|
tooltipText: player.identity || '',
|
||||||
|
connections: [
|
||||||
|
[player, icon => {
|
||||||
|
const name = `${player.entry}${symbolic ? '-symbolic' : ''}`;
|
||||||
|
lookUpIcon(name) ? icon.icon = name
|
||||||
|
: icon.icon = icons.mpris.fallback;
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return Box({
|
||||||
|
connections: [
|
||||||
|
[Mpris, box => {
|
||||||
|
let overlays = box.get_parent().get_parent().get_parent().overlays;
|
||||||
|
let player = overlays.find(overlay => overlay === box.get_parent().get_parent());
|
||||||
|
let index = overlays.indexOf(player)
|
||||||
|
|
||||||
|
let children = [];
|
||||||
|
for (let i = 0; i < overlays.length; ++i) {
|
||||||
|
if (i === index) {
|
||||||
|
children.push(MainIcon);
|
||||||
|
children.push(Separator(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
children.push(Box({ className: 'position-indicator' }));
|
||||||
|
children.push(Separator(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
box.children = children;
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PositionSlider = (player, params) => EventBox({
|
||||||
|
child: Slider({
|
||||||
|
...params,
|
||||||
|
className: 'position-slider',
|
||||||
|
hexpand: true,
|
||||||
|
drawValue: false,
|
||||||
|
onChange: ({ value }) => {
|
||||||
|
player.position = player.length * value;
|
||||||
|
},
|
||||||
|
properties: [
|
||||||
|
['update', slider => {
|
||||||
|
if (slider.dragging) {
|
||||||
|
slider.get_parent().window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (slider.get_parent() && slider.get_parent().window) {
|
||||||
|
slider.get_parent().window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
|
||||||
|
}
|
||||||
|
|
||||||
|
slider.sensitive = player.length > 0;
|
||||||
|
if (player.length > 0) {
|
||||||
|
slider.value = player.position / player.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
[player, s => s._update(s), 'position'],
|
||||||
|
[1000, s => s._update(s)],
|
||||||
|
[player.colors, s => {
|
||||||
|
if (player.colors.value)
|
||||||
|
s.setCss(`highlight { background-color: ${player.colors.value.buttonAccent}; }
|
||||||
|
slider { background-color: ${player.colors.value.buttonAccent}; }
|
||||||
|
slider:hover { background-color: ${player.colors.value.hoverAccent}; }
|
||||||
|
trough { background-color: ${player.colors.value.buttonText}; }`);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function lengthStr(length) {
|
||||||
|
const min = Math.floor(length / 60);
|
||||||
|
const sec0 = Math.floor(length % 60) < 10 ? '0' : '';
|
||||||
|
const sec = Math.floor(length % 60);
|
||||||
|
return `${min}:${sec0}${sec}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PositionLabel = player => Label({
|
||||||
|
properties: [['update', label => {
|
||||||
|
player.length > 0 ? label.label = lengthStr(player.position)
|
||||||
|
: label.visible = !!player;
|
||||||
|
}]],
|
||||||
|
connections: [
|
||||||
|
[player, l => l._update(l), 'position'],
|
||||||
|
[1000, l => l._update(l)],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const LengthLabel = player => Label({
|
||||||
|
connections: [[player, label => {
|
||||||
|
player.length > 0 ? label.label = lengthStr(player.length)
|
||||||
|
: label.visible = !!player;
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Slash = player => Label({
|
||||||
|
label: '/',
|
||||||
|
connections: [[player, label => {
|
||||||
|
label.visible = player.length > 0;
|
||||||
|
}]],
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: use label instead of stack to fix UI issues
|
||||||
|
const PlayerButton = ({ player, items, onClick, prop }) => Button({
|
||||||
|
child: Stack({ items }),
|
||||||
|
onPrimaryClickRelease: () => player[onClick](),
|
||||||
|
properties: [['hovered', false]],
|
||||||
|
onHover: box => {
|
||||||
|
box._hovered = true;
|
||||||
|
if (! box.child.sensitive || ! box.sensitive) {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop == 'playBackStatus') {
|
||||||
|
items.forEach(item => {
|
||||||
|
item[1].setStyle(`background-color: ${player.colors.value.hoverAccent};
|
||||||
|
color: ${player.colors.value.buttonText};
|
||||||
|
min-height: 40px; min-width: 36px;
|
||||||
|
margin-bottom: 1px; margin-right: 1px;`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHoverLost: box => {
|
||||||
|
box._hovered = false;
|
||||||
|
box.window.set_cursor(null);
|
||||||
|
if (prop == 'playBackStatus') {
|
||||||
|
items.forEach(item => {
|
||||||
|
item[1].setStyle(`background-color: ${player.colors.value.buttonAccent};
|
||||||
|
color: ${player.colors.value.buttonText};
|
||||||
|
min-height: 42px; min-width: 38px;`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connections: [
|
||||||
|
[player, button => {
|
||||||
|
button.child.shown = `${player[prop]}`;
|
||||||
|
}],
|
||||||
|
|
||||||
|
[player.colors, button => {
|
||||||
|
if (!Mpris.players.find(p => player === p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (player.colors.value) {
|
||||||
|
if (prop == 'playBackStatus') {
|
||||||
|
if (button._hovered) {
|
||||||
|
items.forEach(item => {
|
||||||
|
item[1].setStyle(`background-color: ${player.colors.value.hoverAccent};
|
||||||
|
color: ${player.colors.value.buttonText};
|
||||||
|
min-height: 40px; min-width: 36px;
|
||||||
|
margin-bottom: 1px; margin-right: 1px;`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
items.forEach(item => {
|
||||||
|
item[1].setStyle(`background-color: ${player.colors.value.buttonAccent};
|
||||||
|
color: ${player.colors.value.buttonText};
|
||||||
|
min-height: 42px; min-width: 38px;`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
button.setCss(`* { color: ${player.colors.value.buttonAccent}; }
|
||||||
|
*:hover { color: ${player.colors.value.hoverAccent}; }`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ShuffleButton = player => PlayerButton({
|
||||||
|
player,
|
||||||
|
items: [
|
||||||
|
['true', Label({
|
||||||
|
className: 'shuffle enabled',
|
||||||
|
label: icons.mpris.shuffle.enabled,
|
||||||
|
})],
|
||||||
|
['false', Label({
|
||||||
|
className: 'shuffle disabled',
|
||||||
|
label: icons.mpris.shuffle.disabled,
|
||||||
|
})],
|
||||||
|
],
|
||||||
|
onClick: 'shuffle',
|
||||||
|
prop: 'shuffleStatus',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const LoopButton = player => PlayerButton({
|
||||||
|
player,
|
||||||
|
items: [
|
||||||
|
['None', Label({
|
||||||
|
className: 'loop none',
|
||||||
|
label: icons.mpris.loop.none,
|
||||||
|
})],
|
||||||
|
['Track', Label({
|
||||||
|
className: 'loop track',
|
||||||
|
label: icons.mpris.loop.track,
|
||||||
|
})],
|
||||||
|
['Playlist', Label({
|
||||||
|
className: 'loop playlist',
|
||||||
|
label: icons.mpris.loop.playlist,
|
||||||
|
})],
|
||||||
|
],
|
||||||
|
onClick: 'loop',
|
||||||
|
prop: 'loopStatus',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PlayPauseButton = player => PlayerButton({
|
||||||
|
player,
|
||||||
|
items: [
|
||||||
|
['Playing', Label({
|
||||||
|
className: 'pausebutton playing',
|
||||||
|
label: icons.mpris.playing,
|
||||||
|
})],
|
||||||
|
['Paused', Label({
|
||||||
|
className: 'pausebutton paused',
|
||||||
|
label: icons.mpris.paused,
|
||||||
|
})],
|
||||||
|
['Stopped', Label({
|
||||||
|
className: 'pausebutton stopped paused',
|
||||||
|
label: icons.mpris.stopped,
|
||||||
|
})],
|
||||||
|
],
|
||||||
|
onClick: 'playPause',
|
||||||
|
prop: 'playBackStatus',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PreviousButton = player => PlayerButton({
|
||||||
|
player,
|
||||||
|
items: [
|
||||||
|
['true', Label({
|
||||||
|
className: 'previous',
|
||||||
|
label: icons.mpris.prev,
|
||||||
|
})],
|
||||||
|
['false', Label({
|
||||||
|
className: 'previous',
|
||||||
|
label: icons.mpris.prev,
|
||||||
|
})],
|
||||||
|
],
|
||||||
|
onClick: 'previous',
|
||||||
|
prop: 'canGoPrev',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NextButton = player => PlayerButton({
|
||||||
|
player,
|
||||||
|
items: [
|
||||||
|
['true', Label({
|
||||||
|
className: 'next',
|
||||||
|
label: icons.mpris.next,
|
||||||
|
})],
|
||||||
|
['false', Label({
|
||||||
|
className: 'next',
|
||||||
|
label: icons.mpris.next,
|
||||||
|
})],
|
||||||
|
],
|
||||||
|
onClick: 'next',
|
||||||
|
prop: 'canGoNext',
|
||||||
|
});
|
139
config/ags/js/media-player/player.js
Normal file
139
config/ags/js/media-player/player.js
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
const { Mpris } = ags.Service;
|
||||||
|
const { Box, CenterBox, Label } = ags.Widget;
|
||||||
|
|
||||||
|
import * as mpris from './mpris.js';
|
||||||
|
import PlayerGesture from './gesture.js';
|
||||||
|
import { Separator } from '../misc/separator.js';
|
||||||
|
|
||||||
|
const Top = player => Box({
|
||||||
|
className: 'top',
|
||||||
|
halign: 'start',
|
||||||
|
valign: 'start',
|
||||||
|
children: [
|
||||||
|
mpris.PlayerIcon(player, {
|
||||||
|
symbolic: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Center = player => Box({
|
||||||
|
className: 'center',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
CenterBox({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'metadata',
|
||||||
|
vertical: true,
|
||||||
|
halign: 'start',
|
||||||
|
valign: 'center',
|
||||||
|
hexpand: true,
|
||||||
|
children: [
|
||||||
|
mpris.TitleLabel(player),
|
||||||
|
mpris.ArtistLabel(player),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Label(),
|
||||||
|
Label(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
CenterBox({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Label(),
|
||||||
|
mpris.PlayPauseButton(player),
|
||||||
|
Label(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Bottom = player => Box({
|
||||||
|
className: 'bottom',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
mpris.PreviousButton(player, {
|
||||||
|
valign: 'end',
|
||||||
|
halign: 'start',
|
||||||
|
}),
|
||||||
|
Separator(8),
|
||||||
|
mpris.PositionSlider(player),
|
||||||
|
Separator(8),
|
||||||
|
mpris.NextButton(player),
|
||||||
|
Separator(8),
|
||||||
|
mpris.ShuffleButton(player),
|
||||||
|
Separator(8),
|
||||||
|
mpris.LoopButton(player),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const PlayerBox = player => mpris.CoverArt(player, {
|
||||||
|
className: `player ${player.name}`,
|
||||||
|
hexpand: true,
|
||||||
|
children: [
|
||||||
|
Top(player),
|
||||||
|
Center(player),
|
||||||
|
Bottom(player),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => Box({
|
||||||
|
className: 'media',
|
||||||
|
child: PlayerGesture({
|
||||||
|
properties: [
|
||||||
|
['players', new Map()],
|
||||||
|
['setup', false],
|
||||||
|
['selected'],
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
[Mpris, (overlay, busName) => {
|
||||||
|
if (!busName || overlay._players.has(busName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const player = Mpris.getPlayer(busName);
|
||||||
|
player.colors = ags.Variable();
|
||||||
|
overlay._players.set(busName, PlayerBox(player));
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
overlay._players.forEach(widget => {
|
||||||
|
result.push(widget);
|
||||||
|
});
|
||||||
|
|
||||||
|
overlay.overlays = result;
|
||||||
|
|
||||||
|
// Favor spotify
|
||||||
|
if (!overlay._setup) {
|
||||||
|
if (overlay._players.has('org.mpris.MediaPlayer2.spotify')) {
|
||||||
|
overlay._selected = overlay._players.get('org.mpris.MediaPlayer2.spotify');
|
||||||
|
}
|
||||||
|
overlay._setup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overlay._selected)
|
||||||
|
overlay.reorder_overlay(overlay._selected, -1);
|
||||||
|
}, 'player-added'],
|
||||||
|
|
||||||
|
[Mpris, (overlay, busName) => {
|
||||||
|
if (!busName || !overlay._players.has(busName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
overlay._players.delete(busName);
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
overlay._players.forEach(widget => {
|
||||||
|
result.push(widget);
|
||||||
|
});
|
||||||
|
|
||||||
|
overlay.overlays = result;
|
||||||
|
if (overlay._selected)
|
||||||
|
overlay.reorder_overlay(overlay._selected, -1);
|
||||||
|
}, 'player-closed'],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
34
config/ags/js/misc/closer.js
Normal file
34
config/ags/js/misc/closer.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const { Window, EventBox } = ags.Widget;
|
||||||
|
const { closeWindow } = ags.App;
|
||||||
|
|
||||||
|
const ALWAYS_OPEN = [
|
||||||
|
'closer',
|
||||||
|
'bar',
|
||||||
|
'notifications',
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: close on scroll event too?
|
||||||
|
export const closeAll = () => {
|
||||||
|
ags.App.windows.forEach(w => {
|
||||||
|
if (!ALWAYS_OPEN.some(window => window === w.name))
|
||||||
|
ags.App.closeWindow(w.name)
|
||||||
|
});
|
||||||
|
closeWindow('closer');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Closer = Window({
|
||||||
|
name: 'closer',
|
||||||
|
popup: true,
|
||||||
|
layer: 'top',
|
||||||
|
anchor: 'top bottom left right',
|
||||||
|
|
||||||
|
child: EventBox({
|
||||||
|
onPrimaryClickRelease: () => closeAll(),
|
||||||
|
connections: [[ags.App, (_b, _w, _v) => {
|
||||||
|
if (!Array.from(ags.App.windows).some(w => w[1].visible &&
|
||||||
|
!ALWAYS_OPEN.some(window => window === w[0]))) {
|
||||||
|
closeWindow('closer');
|
||||||
|
}
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
});
|
34
config/ags/js/misc/cursorbox.js
Normal file
34
config/ags/js/misc/cursorbox.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import Gdk from 'gi://Gdk';
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
export const EventBox = ({ reset = true, ...params }) => ags.Widget.EventBox({
|
||||||
|
...params,
|
||||||
|
onHover: box => {
|
||||||
|
if (! box.child.sensitive || ! box.sensitive) {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHoverLost: box => {
|
||||||
|
if (reset)
|
||||||
|
box.window.set_cursor(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Button = ({ reset = true, ...params }) => ags.Widget.Button({
|
||||||
|
...params,
|
||||||
|
onHover: box => {
|
||||||
|
if (! box.child.sensitive || ! box.sensitive) {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHoverLost: box => {
|
||||||
|
if (reset)
|
||||||
|
box.window.set_cursor(null);
|
||||||
|
},
|
||||||
|
});
|
138
config/ags/js/misc/drag.js
Normal file
138
config/ags/js/misc/drag.js
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
const { Box, EventBox } = ags.Widget;
|
||||||
|
const { Gtk, Gdk } = imports.gi;
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
export const Draggable = ({
|
||||||
|
maxOffset = 150,
|
||||||
|
startMargin = 0,
|
||||||
|
endMargin = 300,
|
||||||
|
command = () => {},
|
||||||
|
onHover = w => {},
|
||||||
|
onHoverLost = w => {},
|
||||||
|
child = '',
|
||||||
|
children = [],
|
||||||
|
properties = [[]],
|
||||||
|
...params
|
||||||
|
}) => {
|
||||||
|
let w = EventBox({
|
||||||
|
...params,
|
||||||
|
properties: [
|
||||||
|
['dragging', false],
|
||||||
|
...properties,
|
||||||
|
],
|
||||||
|
onHover: box => {
|
||||||
|
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||||
|
onHover(box);
|
||||||
|
},
|
||||||
|
onHoverLost: box => {
|
||||||
|
box.window.set_cursor(null);
|
||||||
|
onHoverLost(box);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let gesture = Gtk.GestureDrag.new(w);
|
||||||
|
|
||||||
|
let leftAnim1 = `transition: margin 0.5s ease, opacity 0.5s ease;
|
||||||
|
margin-left: -${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-right: ${Number(maxOffset + endMargin)}px;
|
||||||
|
opacity: 0;`;
|
||||||
|
|
||||||
|
let leftAnim2 = `transition: margin 0.5s ease, opacity 0.5s ease;
|
||||||
|
margin-left: -${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-right: ${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-bottom: -70px; margin-top: -70px; opacity: 0;`;
|
||||||
|
|
||||||
|
let rightAnim1 = `transition: margin 0.5s ease, opacity 0.5s ease;
|
||||||
|
margin-left: ${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-right: -${Number(maxOffset + endMargin)}px;
|
||||||
|
opacity: 0;`;
|
||||||
|
|
||||||
|
let rightAnim2 = `transition: margin 0.5s ease, opacity 0.5s ease;
|
||||||
|
margin-left: ${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-right: -${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-bottom: -70px; margin-top: -70px; opacity: 0;`;
|
||||||
|
|
||||||
|
w.child = Box({
|
||||||
|
properties: [
|
||||||
|
['leftAnim1', leftAnim1],
|
||||||
|
['leftAnim2', leftAnim2],
|
||||||
|
['rightAnim1', rightAnim1],
|
||||||
|
['rightAnim2', rightAnim2],
|
||||||
|
['ready', false]
|
||||||
|
],
|
||||||
|
children: [
|
||||||
|
...children,
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
style: leftAnim2,
|
||||||
|
connections: [
|
||||||
|
|
||||||
|
[gesture, box => {
|
||||||
|
var offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
|
if (offset >= 0) {
|
||||||
|
box.setStyle('margin-left: ' + Number(offset + startMargin) + 'px; ' +
|
||||||
|
'margin-right: -' + Number(offset + startMargin) + 'px;');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offset = Math.abs(offset);
|
||||||
|
box.setStyle('margin-right: ' + Number(offset + startMargin) + 'px; ' +
|
||||||
|
'margin-left: -' + Number(offset + startMargin) + 'px;');
|
||||||
|
}
|
||||||
|
|
||||||
|
box.get_parent()._dragging = Math.abs(offset) > 10;
|
||||||
|
|
||||||
|
if (w.window)
|
||||||
|
w.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
||||||
|
}, 'drag-update'],
|
||||||
|
|
||||||
|
[gesture, box => {
|
||||||
|
if (!box._ready) {
|
||||||
|
box.setStyle(`transition: margin 0.5s ease, opacity 0.5s ease;
|
||||||
|
margin-left: -${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-right: ${Number(maxOffset + endMargin)}px;
|
||||||
|
margin-bottom: 0px; margin-top: 0px; opacity: 0;`);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
box.setStyle('transition: margin 0.5s ease, opacity 0.5s ease; ' +
|
||||||
|
'margin-left: ' + startMargin + 'px; ' +
|
||||||
|
'margin-right: ' + startMargin + 'px; ' +
|
||||||
|
'margin-bottom: unset; margin-top: unset; opacity: 1;');
|
||||||
|
}, 500);
|
||||||
|
setTimeout(() => box._ready = true, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
|
if (Math.abs(offset) > maxOffset) {
|
||||||
|
if (offset > 0) {
|
||||||
|
box.setStyle(rightAnim1);
|
||||||
|
setTimeout(() => box.setStyle(rightAnim2), 500);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.setStyle(leftAnim1);
|
||||||
|
setTimeout(() => box.setStyle(leftAnim2), 500);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
command();
|
||||||
|
box.destroy();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.setStyle('transition: margin 0.5s ease, opacity 0.5s ease; ' +
|
||||||
|
'margin-left: ' + startMargin + 'px; ' +
|
||||||
|
'margin-right: ' + startMargin + 'px; ' +
|
||||||
|
'margin-bottom: unset; margin-top: unset; opacity: 1;');
|
||||||
|
if (w.window)
|
||||||
|
w.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||||
|
|
||||||
|
box.get_parent()._dragging = false;
|
||||||
|
}
|
||||||
|
}, 'drag-end'],
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return w;
|
||||||
|
};
|
20
config/ags/js/misc/popup.js
Normal file
20
config/ags/js/misc/popup.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const { Revealer, Box } = ags.Widget;
|
||||||
|
const { openWindow } = ags.App;
|
||||||
|
|
||||||
|
export const PopUp = ({name, child, transition = 'slide_down', ...params}) => Box({
|
||||||
|
style: 'min-height:1px; min-width:1px',
|
||||||
|
child: Revealer({
|
||||||
|
...params,
|
||||||
|
transition,
|
||||||
|
transitionDuration: 500,
|
||||||
|
connections: [[ags.App, (revealer, currentName, visible) => {
|
||||||
|
if (currentName === name) {
|
||||||
|
revealer.reveal_child = visible;
|
||||||
|
|
||||||
|
if (visible && name !== 'overview')
|
||||||
|
openWindow('closer');
|
||||||
|
}
|
||||||
|
}]],
|
||||||
|
child: child,
|
||||||
|
}),
|
||||||
|
});
|
3
config/ags/js/misc/separator.js
Normal file
3
config/ags/js/misc/separator.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export const Separator = width => ags.Widget.Box({
|
||||||
|
style: `min-width: ${width}px;`,
|
||||||
|
});
|
184
config/ags/js/notifications/base.js
Normal file
184
config/ags/js/notifications/base.js
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
const { GLib } = imports.gi;
|
||||||
|
const { Notifications, Applications } = ags.Service;
|
||||||
|
const { lookUpIcon, exec, execAsync } = ags.Utils;
|
||||||
|
const { Box, Icon, Label, Button } = ags.Widget;
|
||||||
|
|
||||||
|
import { Draggable } from '../misc/drag.js';
|
||||||
|
import { EventBox } from '../misc/cursorbox.js'
|
||||||
|
import { closeAll } from '../misc/closer.js';
|
||||||
|
|
||||||
|
const NotificationIcon = ({ appEntry, appIcon, image }) => {
|
||||||
|
let iconCmd = () => {};
|
||||||
|
|
||||||
|
if (Applications.query(appEntry).length > 0) {
|
||||||
|
let app = Applications.query(appEntry)[0];
|
||||||
|
|
||||||
|
if (app.app.get_string('StartupWMClass') != null) {
|
||||||
|
iconCmd = box => {
|
||||||
|
if (!box.get_parent().get_parent().get_parent().get_parent().get_parent()._dragging) {
|
||||||
|
execAsync(['bash', '-c', `$AGS_PATH/launch-app.sh ${app.app.get_string('StartupWMClass')} ${app.app.get_string('Exec')}`]).catch(print);
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (app.app.get_filename().includes('discord')) {
|
||||||
|
iconCmd = box => {
|
||||||
|
if (!box.get_parent().get_parent().get_parent().get_parent().get_parent()._dragging) {
|
||||||
|
execAsync(['bash', '-c', `$AGS_PATH/launch-app.sh discord ${app.app.get_string('Exec')}`])
|
||||||
|
.catch(print);
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
return EventBox({
|
||||||
|
onPrimaryClickRelease: iconCmd,
|
||||||
|
child: Box({
|
||||||
|
valign: 'start',
|
||||||
|
hexpand: false,
|
||||||
|
className: 'icon img',
|
||||||
|
style: `
|
||||||
|
background-image: url("${image}");
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
min-width: 78px;
|
||||||
|
min-height: 78px;
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let icon = 'dialog-information-symbolic';
|
||||||
|
if (lookUpIcon(appIcon)) {
|
||||||
|
icon = appIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lookUpIcon(appEntry)) {
|
||||||
|
icon = appEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventBox({
|
||||||
|
onPrimaryClickRelease: iconCmd,
|
||||||
|
child: Box({
|
||||||
|
valign: 'start',
|
||||||
|
hexpand: false,
|
||||||
|
className: 'icon',
|
||||||
|
style: `
|
||||||
|
min-width: 78px;
|
||||||
|
min-height: 78px;
|
||||||
|
`,
|
||||||
|
children: [Icon({
|
||||||
|
icon, size: 58,
|
||||||
|
halign: 'center',
|
||||||
|
hexpand: true,
|
||||||
|
valign: 'center',
|
||||||
|
vexpand: true,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ({ id, summary, body, actions, urgency, time, command = i => {}, ...icon }) => {
|
||||||
|
const BlockedApps = [
|
||||||
|
'Spotify',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (BlockedApps.find(app => app == Notifications.getNotification(id).appName)) {
|
||||||
|
Notifications.close(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Draggable({
|
||||||
|
maxOffset: 200,
|
||||||
|
command: () => command(id),
|
||||||
|
properties: [
|
||||||
|
['hovered', false],
|
||||||
|
['id', id],
|
||||||
|
],
|
||||||
|
onHover: w => {
|
||||||
|
if (!w._hovered) {
|
||||||
|
w._hovered = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHoverLost: w => {
|
||||||
|
if (w._hovered) {
|
||||||
|
w._hovered = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
child: Box({
|
||||||
|
className: `notification ${urgency}`,
|
||||||
|
vexpand: false,
|
||||||
|
// Notification
|
||||||
|
child: Box({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
// Content
|
||||||
|
Box({
|
||||||
|
children: [
|
||||||
|
NotificationIcon(icon),
|
||||||
|
Box({
|
||||||
|
hexpand: true,
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
// Top of Content
|
||||||
|
Box({
|
||||||
|
children: [
|
||||||
|
Label({
|
||||||
|
className: 'title',
|
||||||
|
xalign: 0,
|
||||||
|
justification: 'left',
|
||||||
|
hexpand: true,
|
||||||
|
maxWidthChars: 24,
|
||||||
|
truncate: 'end',
|
||||||
|
wrap: true,
|
||||||
|
label: summary,
|
||||||
|
useMarkup: summary.startsWith('<'),
|
||||||
|
}),
|
||||||
|
Label({
|
||||||
|
className: 'time',
|
||||||
|
valign: 'start',
|
||||||
|
label: GLib.DateTime.new_from_unix_local(time).format('%H:%M'),
|
||||||
|
}),
|
||||||
|
EventBox({
|
||||||
|
reset: false,
|
||||||
|
child: Button({
|
||||||
|
className: 'close-button',
|
||||||
|
valign: 'start',
|
||||||
|
onClicked: () => Notifications.close(id),
|
||||||
|
child: Icon('window-close-symbolic'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Label({
|
||||||
|
className: 'description',
|
||||||
|
hexpand: true,
|
||||||
|
useMarkup: true,
|
||||||
|
xalign: 0,
|
||||||
|
justification: 'left',
|
||||||
|
label: body,
|
||||||
|
wrap: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
// Actions
|
||||||
|
Box({
|
||||||
|
className: 'actions',
|
||||||
|
children: actions.map(action => Button({
|
||||||
|
className: 'action-button',
|
||||||
|
onClicked: () => Notifications.invoke(id, action.id),
|
||||||
|
hexpand: true,
|
||||||
|
child: Label(action.label),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
145
config/ags/js/notifications/center.js
Normal file
145
config/ags/js/notifications/center.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
const { Notifications } = ags.Service;
|
||||||
|
const { Button, Label, Box, Icon, Scrollable, Window, Revealer } = ags.Widget;
|
||||||
|
const { timeout } = ags.Utils;
|
||||||
|
const { getWindow } = ags.App;
|
||||||
|
|
||||||
|
import Notification from './base.js';
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
import { PopUp } from '../misc/popup.js';
|
||||||
|
|
||||||
|
const ClearButton = () => EventBox({
|
||||||
|
child: Button({
|
||||||
|
onPrimaryClickRelease: button => {
|
||||||
|
button._popups.children.forEach(ch => ch.child.setStyle(ch.child._leftAnim1));
|
||||||
|
button._notifList.children.forEach(ch => {
|
||||||
|
ch.child.setStyle(ch.child._rightAnim1);
|
||||||
|
timeout(500, () => {
|
||||||
|
button._notifList.remove(ch);
|
||||||
|
Notifications.close(ch._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
properties: [['notifList'], ['popups']],
|
||||||
|
connections: [[Notifications, button => {
|
||||||
|
if (!button._notifList)
|
||||||
|
button._notifList = NotificationList;
|
||||||
|
|
||||||
|
if (!button._popups)
|
||||||
|
button._popups = getWindow('notifications').child.children[0].child;
|
||||||
|
|
||||||
|
button.sensitive = Notifications.notifications.length > 0;
|
||||||
|
}]],
|
||||||
|
child: Box({
|
||||||
|
children: [
|
||||||
|
Label('Clear '),
|
||||||
|
Icon({
|
||||||
|
connections: [[Notifications, icon => {
|
||||||
|
icon.icon = Notifications.notifications.length > 0
|
||||||
|
? 'user-trash-full-symbolic' : 'user-trash-symbolic';
|
||||||
|
}]],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const Header = () => Box({
|
||||||
|
className: 'header',
|
||||||
|
children: [
|
||||||
|
Label({ label: 'Notifications', hexpand: true, xalign: 0 }),
|
||||||
|
ClearButton(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const NotificationList = Box({
|
||||||
|
vertical: true,
|
||||||
|
vexpand: true,
|
||||||
|
connections: [
|
||||||
|
[Notifications, (box, id) => {
|
||||||
|
if (box.children.length == 0) {
|
||||||
|
box.children = Notifications.notifications
|
||||||
|
.reverse()
|
||||||
|
.map(n => Notification({ ...n, command: i => Notifications.close(i), }));
|
||||||
|
}
|
||||||
|
else if (id) {
|
||||||
|
const NewNotif = Notification({
|
||||||
|
...Notifications.getNotification(id),
|
||||||
|
command: i => Notifications.close(i),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (NewNotif) {
|
||||||
|
box.add(NewNotif);
|
||||||
|
box.show_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 'notified'],
|
||||||
|
|
||||||
|
[Notifications, (box, id) => {
|
||||||
|
for (const ch of box.children) {
|
||||||
|
if (ch._id == id) {
|
||||||
|
ch.child.setStyle(ch.child._rightAnim1);
|
||||||
|
timeout(500, () => box.remove(ch));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'closed'],
|
||||||
|
|
||||||
|
[Notifications, box => box.visible = Notifications.notifications.length > 0],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Placeholder = () => Revealer({
|
||||||
|
transition: 'crossfade',
|
||||||
|
connections: [[Notifications, box => {
|
||||||
|
box.revealChild = Notifications.notifications.length === 0;
|
||||||
|
}]],
|
||||||
|
child: Box({
|
||||||
|
className: 'placeholder',
|
||||||
|
vertical: true,
|
||||||
|
valign: 'center',
|
||||||
|
halign: 'center',
|
||||||
|
vexpand: true,
|
||||||
|
hexpand: true,
|
||||||
|
children: [
|
||||||
|
Icon('notification-disabled-symbolic'),
|
||||||
|
Label('Your inbox is empty'),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const NotificationCenterWidget = Box({
|
||||||
|
className: 'notification-center',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Header(),
|
||||||
|
Box({
|
||||||
|
className: 'notification-wallpaper-box',
|
||||||
|
children: [Scrollable({
|
||||||
|
className: 'notification-list-box',
|
||||||
|
hscroll: 'never',
|
||||||
|
vscroll: 'automatic',
|
||||||
|
child: Box({
|
||||||
|
className: 'notification-list',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
NotificationList,
|
||||||
|
Placeholder(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NotificationCenter = Window({
|
||||||
|
name: 'notification-center',
|
||||||
|
layer: 'overlay',
|
||||||
|
anchor: 'top right',
|
||||||
|
popup: true,
|
||||||
|
margin: [ 8, 60, 0, 0 ],
|
||||||
|
child: PopUp({
|
||||||
|
name: 'notification-center',
|
||||||
|
child: NotificationCenterWidget,
|
||||||
|
}),
|
||||||
|
});
|
82
config/ags/js/notifications/popup.js
Normal file
82
config/ags/js/notifications/popup.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import Notification from './base.js';
|
||||||
|
const { Notifications } = ags.Service;
|
||||||
|
const { Box, Revealer, Window } = ags.Widget;
|
||||||
|
const { timeout, interval } = ags.Utils;
|
||||||
|
const { source_remove } = imports.gi.GLib;
|
||||||
|
|
||||||
|
const Popups = () => Box({
|
||||||
|
vertical: true,
|
||||||
|
properties: [
|
||||||
|
['map', new Map()],
|
||||||
|
['dismiss', (box, id, force = false) => {
|
||||||
|
if (!id || !box._map.has(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (box._map.get(id)._hovered && !force)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (box._map.size - 1 === 0)
|
||||||
|
box.get_parent().reveal_child = false;
|
||||||
|
|
||||||
|
timeout(200, () => {
|
||||||
|
if (box._map.get(id)?.interval) {
|
||||||
|
source_remove(box._map.get(id).interval);
|
||||||
|
box._map.get(id).interval = undefined;
|
||||||
|
}
|
||||||
|
box._map.get(id)?.destroy();
|
||||||
|
box._map.delete(id);
|
||||||
|
});
|
||||||
|
}],
|
||||||
|
['notify', (box, id) => {
|
||||||
|
if (!id || Notifications.dnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (! Notifications.getNotification(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
box._map.delete(id);
|
||||||
|
|
||||||
|
box._map.set(id, Notification({
|
||||||
|
...Notifications.getNotification(id),
|
||||||
|
command: i => Notifications.dismiss(i),
|
||||||
|
}));
|
||||||
|
|
||||||
|
box.children = Array.from(box._map.values()).reverse();
|
||||||
|
timeout(10, () => {
|
||||||
|
box.get_parent().revealChild = true;
|
||||||
|
});
|
||||||
|
box._map.get(id).interval = interval(4500, () => {
|
||||||
|
if (!box._map.get(id)._hovered) {
|
||||||
|
box._map.get(id).child.setStyle(box._map.get(id).child._leftAnim1);
|
||||||
|
|
||||||
|
if (box._map.get(id).interval) {
|
||||||
|
source_remove(box._map.get(id).interval);
|
||||||
|
box._map.get(id).interval = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
[Notifications, (box, id) => box._notify(box, id), 'notified'],
|
||||||
|
[Notifications, (box, id) => box._dismiss(box, id), 'dismissed'],
|
||||||
|
[Notifications, (box, id) => box._dismiss(box, id, true), 'closed'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const PopupList = ({ transition = 'none' } = {}) => Box({
|
||||||
|
className: 'notifications-popup-list',
|
||||||
|
style: 'padding: 1px',
|
||||||
|
children: [
|
||||||
|
Revealer({
|
||||||
|
transition,
|
||||||
|
child: Popups(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NotificationsPopupList = Window({
|
||||||
|
name: `notifications`,
|
||||||
|
anchor: 'top left',
|
||||||
|
child: PopupList(),
|
||||||
|
});
|
124
config/ags/js/overview/clients.js
Normal file
124
config/ags/js/overview/clients.js
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
const { Icon, Revealer } = ags.Widget;
|
||||||
|
const { closeWindow } = ags.App;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
const { Hyprland } = ags.Service;
|
||||||
|
|
||||||
|
import { WindowButton } from './dragndrop.js';
|
||||||
|
import * as VARS from './variables.js';
|
||||||
|
|
||||||
|
Array.prototype.remove = function (el) { this.splice(this.indexOf(el), 1) };
|
||||||
|
const IconStyle = app => `min-width: ${app.size[0] * VARS.SCALE - VARS.MARGIN}px;
|
||||||
|
min-height: ${app.size[1] * VARS.SCALE - VARS.MARGIN}px;
|
||||||
|
font-size: ${Math.min(app.size[0] * VARS.SCALE - VARS.MARGIN,
|
||||||
|
app.size[1] * VARS.SCALE - VARS.MARGIN) * VARS.ICON_SCALE}px;`;
|
||||||
|
|
||||||
|
const Client = (client, active, clients) => Revealer({
|
||||||
|
transition: 'crossfade',
|
||||||
|
setup: rev => {
|
||||||
|
rev.revealChild = true;
|
||||||
|
},
|
||||||
|
properties: [
|
||||||
|
['address', client.address],
|
||||||
|
['toDestroy', false]
|
||||||
|
],
|
||||||
|
child: WindowButton({
|
||||||
|
address: client.address,
|
||||||
|
onSecondaryClickRelease: () => {
|
||||||
|
execAsync(`hyprctl dispatch closewindow address:${client.address}`)
|
||||||
|
.catch(print)
|
||||||
|
},
|
||||||
|
onPrimaryClickRelease: () => {
|
||||||
|
if (client.workspace.id < 0) {
|
||||||
|
if (client.workspace.name === 'special') {
|
||||||
|
execAsync(`hyprctl dispatch movetoworkspacesilent special:${client.workspace.id},address:${client.address}`)
|
||||||
|
.then(execAsync(`hyprctl dispatch togglespecialworkspace ${client.workspace.id}`)
|
||||||
|
.then(() => closeWindow('overview'))
|
||||||
|
.catch(print))
|
||||||
|
.catch(print);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
execAsync(`hyprctl dispatch togglespecialworkspace ${String(client.workspace.name)
|
||||||
|
.replace('special:', '')}`)
|
||||||
|
.then(() => closeWindow('overview'))
|
||||||
|
.catch(print);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// close special workspace if one is opened
|
||||||
|
let activeAddress = Hyprland.active.client.address;
|
||||||
|
let currentActive = clients.find(c => c.address === activeAddress)
|
||||||
|
|
||||||
|
if (currentActive && currentActive.workspace.id < 0)
|
||||||
|
execAsync(`hyprctl dispatch togglespecialworkspace ${String(currentActive.workspace.name)
|
||||||
|
.replace('special:', '')}`).catch(print);
|
||||||
|
execAsync(`hyprctl dispatch focuswindow address:${client.address}`)
|
||||||
|
.then(() => closeWindow('overview'))
|
||||||
|
.catch(print);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Icon({
|
||||||
|
className: `window ${active}`,
|
||||||
|
style: IconStyle(client) + 'font-size: 10px;',
|
||||||
|
icon: client.class,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function updateClients(box) {
|
||||||
|
ags.Utils.execAsync('hyprctl clients -j')
|
||||||
|
.then(result => {
|
||||||
|
let clients = JSON.parse(result).filter(client => client.class)
|
||||||
|
|
||||||
|
box._workspaces.forEach(workspace => {
|
||||||
|
let fixed = workspace.child.child.overlays[0].children[0];
|
||||||
|
let toRemove = fixed.get_children();
|
||||||
|
|
||||||
|
clients.filter(client => client.workspace.id == workspace._id).forEach(client => {
|
||||||
|
let active = '';
|
||||||
|
if (client.address == Hyprland.active.client.address) {
|
||||||
|
active = 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special workspaces that haven't been opened yet
|
||||||
|
// return a size of 0. We need to set them to default
|
||||||
|
// values to show the workspace properly
|
||||||
|
if (client.size[0] === 0) {
|
||||||
|
client.size[0] = VARS.DEFAULT_SPECIAL.SIZE_X;
|
||||||
|
client.size[1] = VARS.DEFAULT_SPECIAL.SIZE_Y;
|
||||||
|
client.at[0] = VARS.DEFAULT_SPECIAL.POS_X;
|
||||||
|
client.at[1] = VARS.DEFAULT_SPECIAL.POS_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingClient = fixed.get_children().find(ch => ch._address == client.address);
|
||||||
|
toRemove.remove(existingClient);
|
||||||
|
|
||||||
|
if (existingClient) {
|
||||||
|
fixed.move(
|
||||||
|
existingClient,
|
||||||
|
client.at[0] * VARS.SCALE,
|
||||||
|
client.at[1] * VARS.SCALE,
|
||||||
|
);
|
||||||
|
existingClient.child.child.className = `window ${active}`;
|
||||||
|
existingClient.child.child.style = IconStyle(client);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fixed.put(
|
||||||
|
Client(client, active, clients),
|
||||||
|
client.at[0] * VARS.SCALE,
|
||||||
|
client.at[1] * VARS.SCALE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fixed.show_all();
|
||||||
|
toRemove.forEach(ch => {
|
||||||
|
if (ch._toDestroy) {
|
||||||
|
ch.destroy();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ch.revealChild = false;
|
||||||
|
ch._toDestroy = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(print);
|
||||||
|
};
|
69
config/ags/js/overview/dragndrop.js
Normal file
69
config/ags/js/overview/dragndrop.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
const { Gtk, Gdk } = imports.gi;
|
||||||
|
const { EventBox } = ags.Widget;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
const { getWindow } = ags.App;
|
||||||
|
import Cairo from 'cairo';
|
||||||
|
|
||||||
|
import { Button } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
|
||||||
|
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
||||||
|
|
||||||
|
function createSurfaceFromWidget(widget) {
|
||||||
|
const alloc = widget.get_allocation();
|
||||||
|
const surface = new Cairo.ImageSurface(
|
||||||
|
Cairo.Format.ARGB32,
|
||||||
|
alloc.width,
|
||||||
|
alloc.height,
|
||||||
|
);
|
||||||
|
const cr = new Cairo.Context(surface);
|
||||||
|
cr.setSourceRGBA(255, 255, 255, 0);
|
||||||
|
cr.rectangle(0, 0, alloc.width, alloc.height);
|
||||||
|
cr.fill();
|
||||||
|
widget.draw(cr);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
};
|
||||||
|
|
||||||
|
let hidden = 0;
|
||||||
|
export const WorkspaceDrop = params => EventBox({
|
||||||
|
...params,
|
||||||
|
//tooltipText: `Workspace: ${id}`,
|
||||||
|
connections: [['drag-data-received', (eventbox, _c, _x, _y, data) => {
|
||||||
|
let id = eventbox.get_parent()._id;
|
||||||
|
|
||||||
|
if (id < -1) {
|
||||||
|
id = eventbox.get_parent()._name;
|
||||||
|
}
|
||||||
|
else if (id === -1) {
|
||||||
|
id = `special:${++hidden}`;
|
||||||
|
}
|
||||||
|
else if (id === 1000) {
|
||||||
|
id = "empty";
|
||||||
|
}
|
||||||
|
execAsync(`hyprctl dispatch movetoworkspacesilent ${id},address:${data.get_text()}`)
|
||||||
|
.catch(print);
|
||||||
|
}]],
|
||||||
|
setup: eventbox => {
|
||||||
|
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WindowButton = ({address, ...params} = {}) => Button({
|
||||||
|
...params,
|
||||||
|
setup: button => {
|
||||||
|
button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY);
|
||||||
|
button.connect('drag-data-get', (_w, _c, data) => {
|
||||||
|
data.set_text(address, address.length);
|
||||||
|
});
|
||||||
|
button.connect('drag-begin', (_, context) => {
|
||||||
|
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(button));
|
||||||
|
button.get_parent().revealChild = false;
|
||||||
|
});
|
||||||
|
button.connect('drag-end', () => {
|
||||||
|
button.get_parent().destroy();
|
||||||
|
let mainBox = getWindow('overview').child.children[0].child;
|
||||||
|
mainBox._updateClients(mainBox);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
51
config/ags/js/overview/main.js
Normal file
51
config/ags/js/overview/main.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
const { Window, Box } = ags.Widget;
|
||||||
|
const { Hyprland } = ags.Service;
|
||||||
|
|
||||||
|
import { PopUp } from '../misc/popup.js';
|
||||||
|
import { WorkspaceRow, getWorkspaces, updateWorkspaces } from './workspaces.js';
|
||||||
|
import { updateClients } from './clients.js';
|
||||||
|
|
||||||
|
export default Window({
|
||||||
|
name: 'overview',
|
||||||
|
layer: 'overlay',
|
||||||
|
popup: true,
|
||||||
|
|
||||||
|
child: PopUp({
|
||||||
|
name: 'overview',
|
||||||
|
transition: 'crossfade',
|
||||||
|
|
||||||
|
child: Box({
|
||||||
|
className: 'overview',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Box({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
WorkspaceRow('normal', 0),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Box({
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
WorkspaceRow('special', 0),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
connections: [
|
||||||
|
[Hyprland, box => {
|
||||||
|
box._getWorkspaces(box);
|
||||||
|
box._updateWorkspaces(box);
|
||||||
|
box._updateClients(box);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
['workspaces'],
|
||||||
|
|
||||||
|
['getWorkspaces', getWorkspaces],
|
||||||
|
['updateWorkspaces', updateWorkspaces],
|
||||||
|
['updateClients', updateClients],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
}),
|
||||||
|
});
|
14
config/ags/js/overview/variables.js
Normal file
14
config/ags/js/overview/variables.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export const SCALE = 0.11;
|
||||||
|
export const ICON_SCALE = 0.8;
|
||||||
|
export const MARGIN = 8;
|
||||||
|
export const DEFAULT_SPECIAL = {
|
||||||
|
SIZE_X: 1524,
|
||||||
|
SIZE_Y: 908,
|
||||||
|
POS_X: 197,
|
||||||
|
POS_Y: 170,
|
||||||
|
};
|
||||||
|
export const WORKSPACE_PER_ROW = 6;
|
||||||
|
export const SCREEN = {
|
||||||
|
X: 1920,
|
||||||
|
Y: 1200,
|
||||||
|
};
|
187
config/ags/js/overview/workspaces.js
Normal file
187
config/ags/js/overview/workspaces.js
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
const { Revealer, CenterBox, Box, EventBox, Label, Overlay } = ags.Widget;
|
||||||
|
const { Hyprland } = ags.Service;
|
||||||
|
const { Gtk } = imports.gi;
|
||||||
|
|
||||||
|
import { WorkspaceDrop } from './dragndrop.js';
|
||||||
|
import * as VARS from './variables.js';
|
||||||
|
|
||||||
|
const DEFAULT_STYLE = `min-width: ${VARS.SCREEN.X * VARS.SCALE}px;
|
||||||
|
min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;`;
|
||||||
|
|
||||||
|
export function getWorkspaces(box) {
|
||||||
|
let children = [];
|
||||||
|
box.children.forEach(type => {
|
||||||
|
type.children.forEach(row => {
|
||||||
|
row.child.centerWidget.child.children.forEach(ch => {
|
||||||
|
children.push(ch);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
box._workspaces = children.sort((a, b) => a._id - b._id);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceRow = (className, i) => Revealer({
|
||||||
|
transition: 'slide_down',
|
||||||
|
connections: [[Hyprland, rev => {
|
||||||
|
let minId = i * VARS.WORKSPACE_PER_ROW;
|
||||||
|
let activeId = Hyprland.active.workspace.id;
|
||||||
|
|
||||||
|
rev.revealChild = Hyprland.workspaces.some(ws => ws.id > minId &&
|
||||||
|
(ws.windows > 0 ||
|
||||||
|
ws.id === activeId));
|
||||||
|
}]],
|
||||||
|
child: CenterBox({
|
||||||
|
children: [null, EventBox({
|
||||||
|
properties: [['box']],
|
||||||
|
setup: eventbox => eventbox._box = eventbox.child.children[0],
|
||||||
|
connections: [[Hyprland, eventbox => {
|
||||||
|
let maxId = i * VARS.WORKSPACE_PER_ROW + VARS.WORKSPACE_PER_ROW;
|
||||||
|
let activeId = Hyprland.active.workspace.id;
|
||||||
|
|
||||||
|
eventbox._box.revealChild = className === 'special' ||
|
||||||
|
!Hyprland.workspaces.some(ws => ws.id > maxId &&
|
||||||
|
(ws.windows > 0 ||
|
||||||
|
ws.id === activeId));
|
||||||
|
}]],
|
||||||
|
child: Box({
|
||||||
|
className: className,
|
||||||
|
children: [
|
||||||
|
Revealer({
|
||||||
|
transition: 'slide_right',
|
||||||
|
properties: [
|
||||||
|
['id', className === 'special' ? -1 : 1000],
|
||||||
|
['name', className === 'special' ? 'special' : ''],
|
||||||
|
],
|
||||||
|
child: WorkspaceDrop({
|
||||||
|
child: Overlay({
|
||||||
|
child: Box({
|
||||||
|
className: 'workspace',
|
||||||
|
style: DEFAULT_STYLE,
|
||||||
|
}),
|
||||||
|
overlays: [Box({
|
||||||
|
style: DEFAULT_STYLE,
|
||||||
|
children: [
|
||||||
|
ags.Widget({
|
||||||
|
type: Gtk.Fixed,
|
||||||
|
}),
|
||||||
|
Label({
|
||||||
|
label: ' +',
|
||||||
|
style: 'font-size: 40px;',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}), null],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const Workspace = (id, name) => Revealer({
|
||||||
|
transition: 'slide_right',
|
||||||
|
transitionDuration: 500,
|
||||||
|
properties: [
|
||||||
|
['id', id],
|
||||||
|
['name', name],
|
||||||
|
['timeouts', []],
|
||||||
|
['wasActive', false],
|
||||||
|
],
|
||||||
|
connections: [[Hyprland, box => {
|
||||||
|
box._timeouts.forEach(clearTimeout);
|
||||||
|
|
||||||
|
let activeId = Hyprland.active.workspace.id;
|
||||||
|
let active = activeId === box._id;
|
||||||
|
|
||||||
|
let rev = box.child.child.child;
|
||||||
|
let n = activeId > box._id;
|
||||||
|
|
||||||
|
if (Hyprland.getWorkspace(box._id)?.windows > 0 || active) {
|
||||||
|
rev.setStyle(DEFAULT_STYLE);
|
||||||
|
box._timeouts.push(setTimeout(() => {
|
||||||
|
box.revealChild = true;
|
||||||
|
}, 100));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (!Hyprland.getWorkspace(box._id)?.windows > 0) {
|
||||||
|
rev.setStyle(DEFAULT_STYLE);
|
||||||
|
box._timeouts.push(setTimeout(() => {
|
||||||
|
box.revealChild = false;
|
||||||
|
}, 100));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
rev.setStyle(`${DEFAULT_STYLE}
|
||||||
|
transition: margin 0.5s ease-in-out;
|
||||||
|
opacity: 1;`);
|
||||||
|
box._wasActive = true;
|
||||||
|
}
|
||||||
|
else if (box._wasActive) {
|
||||||
|
box._wasActive = false;
|
||||||
|
box._timeouts.push(setTimeout(() => {
|
||||||
|
rev.setStyle(`${DEFAULT_STYLE}
|
||||||
|
transition: margin 0.5s ease-in-out;
|
||||||
|
opacity: 1; margin-left: ${n ? '' : '-'}300px;
|
||||||
|
margin-right: ${n ? '-' : ''}300px;`);
|
||||||
|
}, 120));
|
||||||
|
box._timeouts.push(setTimeout(() => {
|
||||||
|
rev.setStyle(`${DEFAULT_STYLE} opacity: 0;
|
||||||
|
margin-left: ${n ? '' : '-'}300px;
|
||||||
|
margin-right: ${n ? '-' : ''}300px;`);
|
||||||
|
}, 500));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rev.setStyle(`${DEFAULT_STYLE} opacity: 0;
|
||||||
|
margin-left: ${n ? '' : '-'}300px;
|
||||||
|
margin-right: ${n ? '-' : ''}300px;`);
|
||||||
|
}
|
||||||
|
}]],
|
||||||
|
child: WorkspaceDrop({
|
||||||
|
child: Overlay({
|
||||||
|
child: Box({
|
||||||
|
className: 'workspace active',
|
||||||
|
style: `${DEFAULT_STYLE} opacity: 0;`,
|
||||||
|
}),
|
||||||
|
overlays: [Box({
|
||||||
|
className: 'workspace',
|
||||||
|
style: DEFAULT_STYLE,
|
||||||
|
child: ags.Widget({
|
||||||
|
type: Gtk.Fixed,
|
||||||
|
}),
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function updateWorkspaces(box) {
|
||||||
|
Hyprland.workspaces.forEach(ws => {
|
||||||
|
let currentWs = box._workspaces.find(ch => ch._id == ws.id);
|
||||||
|
if (!currentWs) {
|
||||||
|
var type = 0;
|
||||||
|
var rowNo = 0;
|
||||||
|
|
||||||
|
if (ws.id < 0) {
|
||||||
|
// This means it's a special workspace
|
||||||
|
type = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rowNo = Math.floor((ws.id - 1) / VARS.WORKSPACE_PER_ROW);
|
||||||
|
if (rowNo >= box.children[type].children.length) {
|
||||||
|
for (let i = box.children[type].children.length; i <= rowNo; ++i) {
|
||||||
|
box.children[type].add(WorkspaceRow(type ? 'special' : 'normal', i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var row = box.children[type].children[rowNo].child.centerWidget.child;
|
||||||
|
row.add(Workspace(ws.id, type ? ws.name : ''));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
box.show_all();
|
||||||
|
|
||||||
|
// Make sure the order is correct
|
||||||
|
box._workspaces.forEach((workspace, i) => {
|
||||||
|
workspace.get_parent().reorder_child(workspace, i)
|
||||||
|
});
|
||||||
|
}
|
47
config/ags/js/powermenu.js
Normal file
47
config/ags/js/powermenu.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const { Window, CenterBox, Label } = ags.Widget;
|
||||||
|
|
||||||
|
import { PopUp } from './misc/popup.js';
|
||||||
|
import { Button } from './misc/cursorbox.js'
|
||||||
|
|
||||||
|
const PowermenuWidget = CenterBox({
|
||||||
|
className: 'powermenu',
|
||||||
|
vertical: false,
|
||||||
|
|
||||||
|
startWidget: Button({
|
||||||
|
className: 'shutdown',
|
||||||
|
onPrimaryClickRelease: 'systemctl poweroff',
|
||||||
|
|
||||||
|
child: Label({
|
||||||
|
label: '襤',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
centerWidget: Button({
|
||||||
|
className: 'reboot',
|
||||||
|
onPrimaryClickRelease: 'systemctl reboot',
|
||||||
|
|
||||||
|
child: Label({
|
||||||
|
label: '勒',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
endWidget: Button({
|
||||||
|
className: 'logout',
|
||||||
|
onPrimaryClickRelease: 'hyprctl dispatch exit',
|
||||||
|
|
||||||
|
child: Label({
|
||||||
|
label: '',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Powermenu = Window({
|
||||||
|
name: 'powermenu',
|
||||||
|
popup: true,
|
||||||
|
layer: 'overlay',
|
||||||
|
child: PopUp({
|
||||||
|
name: 'powermenu',
|
||||||
|
transition: 'crossfade',
|
||||||
|
child: PowermenuWidget,
|
||||||
|
}),
|
||||||
|
});
|
212
config/ags/js/quick-settings/button-grid.js
Normal file
212
config/ags/js/quick-settings/button-grid.js
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
const { Box, CenterBox, Label, Icon } = ags.Widget;
|
||||||
|
const { Network, Bluetooth, Audio } = ags.Service;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
const { openWindow } = ags.App;
|
||||||
|
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
const GridButton = ({ command = () => {}, secondaryCommand = () => {}, icon } = {}) => Box({
|
||||||
|
className: 'grid-button',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
EventBox({
|
||||||
|
className: 'left-part',
|
||||||
|
onPrimaryClickRelease: () => command(),
|
||||||
|
child: icon,
|
||||||
|
}),
|
||||||
|
|
||||||
|
EventBox({
|
||||||
|
className: 'right-part',
|
||||||
|
onPrimaryClickRelease: () => secondaryCommand(),
|
||||||
|
child: Label({
|
||||||
|
label: " ",
|
||||||
|
className: 'grid-chev',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const FirstRow = Box({
|
||||||
|
className: 'button-row',
|
||||||
|
halign: 'center',
|
||||||
|
style: 'margin-top: 15px; margin-bottom: 7px;',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => Network.toggleWifi(),
|
||||||
|
secondaryCommand: () => execAsync(['bash', '-c', 'nm-connection-editor']).catch(print),
|
||||||
|
icon: Icon({
|
||||||
|
className: 'grid-label',
|
||||||
|
connections: [[Network, icon => {
|
||||||
|
if (Network.wifi.enabled) {
|
||||||
|
icon.icon = 'network-wireless-connected-symbolic';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.icon = 'network-wireless-offline-symbolic';
|
||||||
|
}
|
||||||
|
}, 'changed']],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh blue-toggle']).catch(print),
|
||||||
|
secondaryCommand: () => execAsync(['bash', '-c', 'blueberry']).catch(print),
|
||||||
|
icon: Icon({
|
||||||
|
className: 'grid-label',
|
||||||
|
connections: [[Bluetooth, icon => {
|
||||||
|
if (Bluetooth.enabled) {
|
||||||
|
icon.icon = 'bluetooth-active-symbolic';
|
||||||
|
execAsync(['bash', '-c', 'echo > $HOME/.config/.bluetooth']).catch(print);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.icon = 'bluetooth-disabled-symbolic';
|
||||||
|
execAsync(['bash', '-c', 'echo > $HOME/.config/.bluetooth']).catch(print);
|
||||||
|
}
|
||||||
|
}, 'changed']],
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh toggle-radio']).catch(print),
|
||||||
|
secondaryCommand: () => execAsync(['notify-send', 'set this up moron']).catch(print),
|
||||||
|
icon: Icon({
|
||||||
|
className: 'grid-label',
|
||||||
|
connections: [[Network, icon => {
|
||||||
|
if (Network.wifi.enabled) {
|
||||||
|
icon.icon = 'airplane-mode-disabled-symbolic';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.icon = 'airplane-mode-symbolic';
|
||||||
|
}
|
||||||
|
}, 'changed']],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const SubRow = CenterBox({
|
||||||
|
halign: 'start',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Label({
|
||||||
|
className: 'sub-label',
|
||||||
|
truncate: 'end',
|
||||||
|
maxWidthChars: 12,
|
||||||
|
connections: [[Network, label => {
|
||||||
|
label.label = Network.wifi.ssid;
|
||||||
|
}, 'changed']],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Label({
|
||||||
|
className: 'sub-label',
|
||||||
|
truncate: 'end',
|
||||||
|
maxWidthChars: 12,
|
||||||
|
connections: [[Bluetooth, label => {
|
||||||
|
label.label = Bluetooth.connectedDevices[0] ? String(Bluetooth.connectedDevices[0]) :
|
||||||
|
'Disconnected';
|
||||||
|
}, 'changed']],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Label({
|
||||||
|
className: '',
|
||||||
|
truncate: 'end',
|
||||||
|
maxWidthChars: 12,
|
||||||
|
/*connections: [[Network, label => {
|
||||||
|
label.label = Network.wifi.ssid;
|
||||||
|
}, 'changed']],*/
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = {
|
||||||
|
101: 'audio-volume-overamplified-symbolic',
|
||||||
|
67: 'audio-volume-high-symbolic',
|
||||||
|
34: 'audio-volume-medium-symbolic',
|
||||||
|
1: 'audio-volume-low-symbolic',
|
||||||
|
0: 'audio-volume-muted-symbolic',
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemsMic = {
|
||||||
|
2: 'audio-input-microphone-high-symbolic',
|
||||||
|
1: 'audio-input-microphone-muted-symbolic',
|
||||||
|
0: 'audio-input-microphone-muted-symbolic',
|
||||||
|
};
|
||||||
|
|
||||||
|
const SecondRow = Box({
|
||||||
|
className: 'button-row',
|
||||||
|
halign: 'center',
|
||||||
|
style: 'margin-top: 7px; margin-bottom: 15px;',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => execAsync(['swayosd-client', '--output-volume', 'mute-toggle']).catch(print),
|
||||||
|
secondaryCommand: () => execAsync(['bash', '-c', 'pavucontrol']).catch(print),
|
||||||
|
icon: Icon({
|
||||||
|
className: 'grid-label',
|
||||||
|
connections: [[Audio, icon => {
|
||||||
|
if (Audio.speaker) {
|
||||||
|
if (Audio.speaker.isMuted) {
|
||||||
|
icon.icon = items[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const vol = Audio.speaker.volume * 100;
|
||||||
|
for (const threshold of [-1, 0, 33, 66, 100]) {
|
||||||
|
if (vol > threshold + 1) {
|
||||||
|
icon.icon = items[threshold + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'speaker-changed']],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => execAsync(['swayosd-client', '--input-volume', 'mute-toggle']).catch(print),
|
||||||
|
secondaryCommand: () => execAsync(['bash', '-c', 'pavucontrol']).catch(print),
|
||||||
|
icon: Icon({
|
||||||
|
className: 'grid-label',
|
||||||
|
connections: [[Audio, icon => {
|
||||||
|
if (Audio.microphone) {
|
||||||
|
if (Audio.microphone.isMuted) {
|
||||||
|
icon.icon = itemsMic[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const vol = Audio.microphone.volume * 100;
|
||||||
|
for (const threshold of [-1, 0, 1]) {
|
||||||
|
if (vol > threshold + 1) {
|
||||||
|
icon.icon = itemsMic[threshold + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'microphone-changed']],
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
|
GridButton({
|
||||||
|
command: () => execAsync(['bash', '-c', '$LOCK_PATH/lock.sh']).catch(print),
|
||||||
|
secondaryCommand: () => openWindow('powermenu'),
|
||||||
|
icon: Label({
|
||||||
|
className: 'grid-label',
|
||||||
|
label: " ",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const ButtonGrid = Box({
|
||||||
|
className: 'button-grid',
|
||||||
|
vertical: true,
|
||||||
|
halign: 'center',
|
||||||
|
children: [
|
||||||
|
FirstRow,
|
||||||
|
SubRow,
|
||||||
|
SecondRow,
|
||||||
|
],
|
||||||
|
});
|
80
config/ags/js/quick-settings/main.js
Normal file
80
config/ags/js/quick-settings/main.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
const { Window, Box, Label, Revealer, Icon } = ags.Widget;
|
||||||
|
const { Mpris } = ags.Service;
|
||||||
|
const { ToggleButton } = imports.gi.Gtk;
|
||||||
|
|
||||||
|
import { ButtonGrid } from './button-grid.js';
|
||||||
|
import { SliderBox } from './slider-box.js';
|
||||||
|
import Player from '../media-player/player.js';
|
||||||
|
import { EventBox } from '../misc/cursorbox.js';
|
||||||
|
import { PopUp } from '../misc/popup.js';
|
||||||
|
|
||||||
|
const QuickSettingsWidget = Box({
|
||||||
|
className: 'qs-container',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'quick-settings',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Label({
|
||||||
|
label: 'Control Center',
|
||||||
|
className: 'title',
|
||||||
|
halign: 'start',
|
||||||
|
style: 'margin-left: 20px'
|
||||||
|
}),
|
||||||
|
|
||||||
|
ButtonGrid,
|
||||||
|
|
||||||
|
SliderBox,
|
||||||
|
|
||||||
|
EventBox({
|
||||||
|
child: ags.Widget({
|
||||||
|
type: ToggleButton,
|
||||||
|
setup: btn => {
|
||||||
|
const id = Mpris.instance.connect('changed', () => {
|
||||||
|
btn.set_active(Mpris.players.length > 0);
|
||||||
|
Mpris.instance.disconnect(id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
connections: [['toggled', button => {
|
||||||
|
if (button.get_active()) {
|
||||||
|
button.child.setStyle("-gtk-icon-transform: rotate(0deg);");
|
||||||
|
button.get_parent().get_parent().get_parent().children[1].revealChild = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
button.child.setStyle('-gtk-icon-transform: rotate(180deg);');
|
||||||
|
button.get_parent().get_parent().get_parent().children[1].revealChild = false;
|
||||||
|
}
|
||||||
|
}]],
|
||||||
|
child: Icon({
|
||||||
|
icon: 'folder-download-symbolic',
|
||||||
|
className: 'arrow',
|
||||||
|
style: `-gtk-icon-transform: rotate(180deg);`,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Revealer({
|
||||||
|
transition: 'slide_down',
|
||||||
|
child: Player(),
|
||||||
|
})
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const QuickSettings = Window({
|
||||||
|
name: 'quick-settings',
|
||||||
|
layer: 'overlay',
|
||||||
|
anchor: 'top right',
|
||||||
|
popup: true,
|
||||||
|
margin: [ 8, 5, 0, ],
|
||||||
|
child: PopUp({
|
||||||
|
name: 'quick-settings',
|
||||||
|
child: QuickSettingsWidget,
|
||||||
|
}),
|
||||||
|
});
|
91
config/ags/js/quick-settings/slider-box.js
Normal file
91
config/ags/js/quick-settings/slider-box.js
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
const { Box, Slider, Icon, EventBox } = ags.Widget;
|
||||||
|
const { Audio } = ags.Service;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
|
||||||
|
const items = {
|
||||||
|
101: 'audio-volume-overamplified-symbolic',
|
||||||
|
67: 'audio-volume-high-symbolic',
|
||||||
|
34: 'audio-volume-medium-symbolic',
|
||||||
|
1: 'audio-volume-low-symbolic',
|
||||||
|
0: 'audio-volume-muted-symbolic',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SliderBox = Box({
|
||||||
|
className: 'slider-box',
|
||||||
|
vertical: true,
|
||||||
|
halign: 'center',
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'slider',
|
||||||
|
valign: 'start',
|
||||||
|
halign: 'center',
|
||||||
|
children: [
|
||||||
|
Icon({
|
||||||
|
size: 26,
|
||||||
|
className: 'slider-label',
|
||||||
|
connections: [[Audio, icon => {
|
||||||
|
if (Audio.speaker) {
|
||||||
|
if (Audio.speaker.isMuted) {
|
||||||
|
icon.icon = items[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const vol = Audio.speaker.volume * 100;
|
||||||
|
for (const threshold of [-1, 0, 33, 66, 100]) {
|
||||||
|
if (vol > threshold + 1) {
|
||||||
|
icon.icon = items[threshold + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'speaker-changed']],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Slider({
|
||||||
|
connections: [[Audio, slider => {
|
||||||
|
if (Audio.speaker) {
|
||||||
|
slider.value = Audio.speaker.volume;
|
||||||
|
}
|
||||||
|
}, 'speaker-changed']],
|
||||||
|
onChange: ({ value }) => Audio.speaker.volume = value,
|
||||||
|
max: 0.999,
|
||||||
|
draw_value: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Box({
|
||||||
|
className: 'slider',
|
||||||
|
valign: 'start',
|
||||||
|
halign: 'center',
|
||||||
|
children: [
|
||||||
|
Icon({
|
||||||
|
className: 'slider-label',
|
||||||
|
icon: 'display-brightness-symbolic',
|
||||||
|
}),
|
||||||
|
|
||||||
|
EventBox({
|
||||||
|
onHover: box => box.child._canChange = false,
|
||||||
|
onHoverLost: box => box.child._canChange = true,
|
||||||
|
child: Slider({
|
||||||
|
properties: [
|
||||||
|
['canChange', true],
|
||||||
|
],
|
||||||
|
onChange: ({ value }) => {
|
||||||
|
execAsync(`brightnessctl set ${value}`).catch(print);
|
||||||
|
},
|
||||||
|
connections: [[1000, slider => {
|
||||||
|
if (slider._canChange) {
|
||||||
|
execAsync('brightnessctl get').then(out => slider.value = out).catch(print);
|
||||||
|
}
|
||||||
|
}]],
|
||||||
|
min: 0,
|
||||||
|
max: 255,
|
||||||
|
draw_value: false,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
32
config/ags/scss/common.scss
Normal file
32
config/ags/scss/common.scss
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
$darkbg: #0b0d16;
|
||||||
|
$bg: rgba(40, 42, 54, 0.8); //rgba(69, 71, 90, 0.3); #0d0f18;
|
||||||
|
$bgfull: rgb(40, 42, 54);
|
||||||
|
$contrastbg: rgba(189, 147, 249, 0.8);
|
||||||
|
$bgSecondary: rgba(#382c4a, 0.8);
|
||||||
|
$bgSecondaryAlt: #a5b6cf;
|
||||||
|
$fg: #a5b6cf;
|
||||||
|
$fgDim: #a5b6cf;
|
||||||
|
$watermelon: #dd6777;
|
||||||
|
|
||||||
|
// Aliases
|
||||||
|
$background: $bg;
|
||||||
|
$backgroundSecondary: $bgSecondary;
|
||||||
|
$backgroundSecondaryAlt: $bgSecondaryAlt;
|
||||||
|
$foreground: $fg;
|
||||||
|
$foregroundDim: $fgDim;
|
||||||
|
|
||||||
|
$black: #151720;
|
||||||
|
$dimblack: #1a1c25;
|
||||||
|
$lightblack: #262831;
|
||||||
|
$red: #dd6777;
|
||||||
|
$blue: #86aaec;
|
||||||
|
$cyan: #93cee9;
|
||||||
|
$blue-desaturated: #93cee9;
|
||||||
|
$magenta: #c296eb;
|
||||||
|
$purple: #c296eb;
|
||||||
|
$green: #90ceaa;
|
||||||
|
$aquamarine: #90ceaa;
|
||||||
|
$yellow: #ecd3a0;
|
||||||
|
$accent: $blue;
|
||||||
|
$javacafeMagenta: #c296eb;
|
||||||
|
$javacafeBlue: #86aaec;
|
16
config/ags/scss/main.scss
Normal file
16
config/ags/scss/main.scss
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
* {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "./common.scss";
|
||||||
|
@import "./widgets/powermenu.scss";
|
||||||
|
@import "./widgets/traybuttons.scss";
|
||||||
|
@import "./widgets/workspaces.scss";
|
||||||
|
@import "./widgets/systray.scss";
|
||||||
|
@import "./widgets/notification-center.scss";
|
||||||
|
@import "./widgets/notification.scss";
|
||||||
|
@import "./widgets/date.scss";
|
||||||
|
@import "./widgets/quick-settings.scss";
|
||||||
|
@import "./widgets/player.scss";
|
||||||
|
@import "./widgets/overview.scss";
|
||||||
|
@import "./widgets/applauncher.scss";
|
113
config/ags/scss/widgets/applauncher.scss
Normal file
113
config/ags/scss/widgets/applauncher.scss
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
.applauncher {
|
||||||
|
all: unset;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.6);
|
||||||
|
margin: 9px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
border-radius: 25px;
|
||||||
|
background-color: $bg;
|
||||||
|
color: #f8f8f2;
|
||||||
|
padding: 16.2px;
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin: 16.2px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
image, entry {
|
||||||
|
all: unset;
|
||||||
|
border-radius: 9px;
|
||||||
|
color: #f8f8f2;
|
||||||
|
background-color: rgba(#44475a, 0.6);
|
||||||
|
border: 1px solid #44475a;
|
||||||
|
padding: 4.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
image {
|
||||||
|
margin-right: 9px;
|
||||||
|
-gtk-icon-transform: scale(0.8);
|
||||||
|
font-size: 25.6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrolledwindow {
|
||||||
|
padding: 16.2px;
|
||||||
|
min-width: 700px;
|
||||||
|
min-height: 450px;
|
||||||
|
|
||||||
|
scrollbar, scrollbar * {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
scrollbar.vertical {
|
||||||
|
transition: 200ms;
|
||||||
|
background-color: rgba(23, 23, 23, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(23, 23, 23, 0.7);
|
||||||
|
|
||||||
|
slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.7);
|
||||||
|
min-width: .6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: .4em;
|
||||||
|
min-height: 2em;
|
||||||
|
transition: 200ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
margin-top: 9px;
|
||||||
|
color: #f8f8f2;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
all: unset;
|
||||||
|
transition: 200ms;
|
||||||
|
padding: 9px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgba($contrastbg, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
box-shadow: inset 0 0 0 3px rgba(238, 238, 238, 0.03);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #f8f8f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
.title {
|
||||||
|
color: $contrastbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
image {
|
||||||
|
-gtk-icon-shadow: 2px 2px $contrastbg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
transition: 200ms;
|
||||||
|
|
||||||
|
&.title {
|
||||||
|
color: #f8f8f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.description {
|
||||||
|
color: rgba(238, 238, 238, 0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image {
|
||||||
|
transition: 200ms;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
config/ags/scss/widgets/date.scss
Normal file
76
config/ags/scss/widgets/date.scss
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
.date {
|
||||||
|
background-color: $bg;
|
||||||
|
color: $fg;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timebox {
|
||||||
|
margin: 30px 0px;
|
||||||
|
.time-container {
|
||||||
|
.content {
|
||||||
|
font-family: Product Sans;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 60px;
|
||||||
|
}
|
||||||
|
.divider {
|
||||||
|
margin: 8px 15px;
|
||||||
|
padding: 0px 1px;
|
||||||
|
background: linear-gradient($red, $magenta, $blue, $cyan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.date-container {
|
||||||
|
font-family: Product Sans;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-box {
|
||||||
|
font-family: Product Sans;
|
||||||
|
border-radius: 30px;
|
||||||
|
padding: 0 1rem .2rem;
|
||||||
|
color: $fg;
|
||||||
|
background-color: $bgfull;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
margin: 0px 12px 18px 12px;
|
||||||
|
|
||||||
|
.cal {
|
||||||
|
font-size: 20px;
|
||||||
|
background-color: inherit;
|
||||||
|
padding: .5rem .10rem 0rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
border-radius: 30px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
border: solid 0px transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.highlight {
|
||||||
|
padding: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar:selected {
|
||||||
|
color: $cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar.header {
|
||||||
|
color: $cyan;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar.button {
|
||||||
|
color: $cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar.highlight {
|
||||||
|
color: $green;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar:indeterminate {
|
||||||
|
color: $lightblack;
|
||||||
|
}
|
100
config/ags/scss/widgets/notification-center.scss
Normal file
100
config/ags/scss/widgets/notification-center.scss
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
.notification-center {
|
||||||
|
min-height: 700px;
|
||||||
|
min-width: 524px;
|
||||||
|
background: $bg;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 22px;
|
||||||
|
margin-bottom: 9px;
|
||||||
|
label {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
all: unset;
|
||||||
|
transition: 200ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
color: #eee;
|
||||||
|
background-color: #664C90;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
padding: 4.5px 9px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: rgba(238, 238, 238, 0.154);
|
||||||
|
color: #f1f1f1;
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: rgba(#664C90, 0.3);
|
||||||
|
color: rgba(238, 238, 238, 0.3);
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-list-box {
|
||||||
|
background: $bgfull;
|
||||||
|
padding: 0 12px 0 12px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
scrollbar {
|
||||||
|
all: unset;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
* {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
border-radius: 8px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scrollbar.vertical {
|
||||||
|
transition: 200ms;
|
||||||
|
background-color: rgba(23, 23, 23, 0.3);
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(23, 23, 23, 0.7);
|
||||||
|
slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.7);
|
||||||
|
min-width: .6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: .4em;
|
||||||
|
min-height: 2em;
|
||||||
|
transition: 200ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: white;
|
||||||
|
image {
|
||||||
|
font-size: 7em;
|
||||||
|
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
config/ags/scss/widgets/notification.scss
Normal file
179
config/ags/scss/widgets/notification.scss
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
$background-color_1: rgba(238, 238, 238, 0.154);
|
||||||
|
$background-color_2: rgba(230, 112, 144, 0.5);
|
||||||
|
$background-color_3: rgba(238, 238, 238, 0.06);
|
||||||
|
$background-color_4: #51a4e7;
|
||||||
|
$background-color_5: transparent;
|
||||||
|
$background-color_6: #171717;
|
||||||
|
$background-color_7: rgba(23, 23, 23, 0.3);
|
||||||
|
$background-color_8: rgba(23, 23, 23, 0.7);
|
||||||
|
$background-color_9: rgba(238, 238, 238, 0.7);
|
||||||
|
$background-color_10: rgba(238, 238, 238, 0.5);
|
||||||
|
|
||||||
|
.notification.critical {
|
||||||
|
>box {
|
||||||
|
box-shadow: inset 0 0 0.5em 0 #e67090;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.notification {
|
||||||
|
>box {
|
||||||
|
all: unset;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.4);
|
||||||
|
margin: 9px 9px 0 9px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: $bg;
|
||||||
|
padding: 16.2px;
|
||||||
|
* {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.close-button {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: $background-color_1;
|
||||||
|
background-color: $background-color_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
margin-right: 9px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
font-size: .9em;
|
||||||
|
min-width: 350px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
border-radius: 7.2px;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
.icon.img {
|
||||||
|
border: 1px solid rgba(238, 238, 238, 0.03);
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
button {
|
||||||
|
all: unset;
|
||||||
|
transition: all 500ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
background-color: $background-color_3;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
border-radius: 7.2px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 4.5px 9px;
|
||||||
|
margin: 9px 4.5px 0;
|
||||||
|
* {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
box-shadow: inset 0 0 0 1px #51a4e7;
|
||||||
|
background-color: $background-color_1;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: $background-color_1;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:checked {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $background-color_5;
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.on {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.close-button {
|
||||||
|
all: unset;
|
||||||
|
transition: all 500ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
background-color: $background-color_5;
|
||||||
|
background-image: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 9px;
|
||||||
|
border-radius: 7.2px;
|
||||||
|
min-width: 1.2em;
|
||||||
|
min-height: 1.2em;
|
||||||
|
* {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
box-shadow: inset 0 0 0 1px #51a4e7;
|
||||||
|
background-color: $background-color_1;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: $background-color_1;
|
||||||
|
background-color: $background-color_2;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
background-image: linear-gradient(#e67090, #e67090);
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:checked {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $background-color_5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.close-button.on {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.close-button.active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: $background-color_4;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
config/ags/scss/widgets/overview.scss
Normal file
52
config/ags/scss/widgets/overview.scss
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
.overview {
|
||||||
|
|
||||||
|
.workspace {
|
||||||
|
padding: 4px 15px 4px 0px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: rgba(lighten($color: black, $amount: 15), 0.8);
|
||||||
|
border: 2px solid black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace .window {
|
||||||
|
background-color: $bgfull;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0 10px;
|
||||||
|
transition: min-width 0.2s ease-in-out,
|
||||||
|
min-height 0.2s ease-in-out,
|
||||||
|
border-color 0.2s ease-in-out,
|
||||||
|
font-size 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normal {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
.workspace {
|
||||||
|
|
||||||
|
.window {
|
||||||
|
border: 2px solid #411C6C;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border: 2px solid purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.special {
|
||||||
|
|
||||||
|
.workspace {
|
||||||
|
|
||||||
|
.window {
|
||||||
|
border: 2px solid lighten($color: black, $amount: 20);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border: 2px solid purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
config/ags/scss/widgets/player.scss
Normal file
111
config/ags/scss/widgets/player.scss
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
.arrow {
|
||||||
|
transition: -gtk-icon-transform 0.3s ease-in-out;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player {
|
||||||
|
all: unset;
|
||||||
|
padding: 10px;
|
||||||
|
min-width: 400px;
|
||||||
|
min-height: 200px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
transition: background 250ms;
|
||||||
|
.top {
|
||||||
|
font-size: 23px;
|
||||||
|
}
|
||||||
|
.metadata {
|
||||||
|
.title{
|
||||||
|
font-weight: 500;
|
||||||
|
transition: text 250ms;
|
||||||
|
}
|
||||||
|
.artist{
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 15px;
|
||||||
|
transition: text 250ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bottom {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pausebutton {
|
||||||
|
transition: background-color ease .2s,
|
||||||
|
color ease .2s;
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 4px 4px 4px 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playing {
|
||||||
|
transition: background-color ease .2s,
|
||||||
|
color ease .2s;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stopped,
|
||||||
|
.paused {
|
||||||
|
transition: background-color ease .2s,
|
||||||
|
color ease .2s;
|
||||||
|
border-radius: 26px;
|
||||||
|
padding: 4px 4px 4px 10px;
|
||||||
|
}
|
||||||
|
button label {
|
||||||
|
min-width: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-indicator {
|
||||||
|
min-width: 18px;
|
||||||
|
margin: 7px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
box-shadow: 0 0 5px 0px rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previous,
|
||||||
|
.next,
|
||||||
|
.shuffle,
|
||||||
|
.loop {
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: color 200ms;
|
||||||
|
&:hover {
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: rgba(127, 132, 156, 0.4);
|
||||||
|
transition: color 200ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loop {
|
||||||
|
label {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-slider {
|
||||||
|
highlight {
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
trough {
|
||||||
|
margin: 0 8px;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider {
|
||||||
|
margin: -8px;
|
||||||
|
min-height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
|
||||||
|
transition: background-color 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
slider:hover {
|
||||||
|
transition: background-color 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
36
config/ags/scss/widgets/powermenu.scss
Normal file
36
config/ags/scss/widgets/powermenu.scss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.powermenu {
|
||||||
|
background-color: $bg;
|
||||||
|
color: $fg;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: MesloLGS NF;
|
||||||
|
/*font-family: Iosevka Nerd Font;*/
|
||||||
|
font-size: 70px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
label {
|
||||||
|
min-width: 140px;
|
||||||
|
min-height: 130px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 12px;
|
||||||
|
min-width: 80px;
|
||||||
|
transition: all ease .2s;
|
||||||
|
&:hover { background-color: $bgSecondary; }
|
||||||
|
&:active { background-color: $bgSecondary; }
|
||||||
|
.content {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0px 15px 0px 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shutdown { color: $red; }
|
||||||
|
.reboot { color: $magenta; }
|
||||||
|
.logout { color: $yellow; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.powermenu-clickhandler {
|
||||||
|
background-color: black;
|
||||||
|
}
|
162
config/ags/scss/widgets/quick-settings.scss
Normal file
162
config/ags/scss/widgets/quick-settings.scss
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
.quick-settings {
|
||||||
|
font-size: 30px;
|
||||||
|
min-width: 500px;
|
||||||
|
padding: 0px 0px 0px 0px;
|
||||||
|
background-color: $bg;
|
||||||
|
border-radius: 30px 0 30px 30px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-label {
|
||||||
|
font-size: 30px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-label {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-left: 31px;
|
||||||
|
&:nth-child(1) {
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
border-top-right-radius: 20px;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-bottom-right-radius: 20px;
|
||||||
|
min-width: 106px;
|
||||||
|
background: #1b1b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-chev {
|
||||||
|
margin-left: 0px;
|
||||||
|
|
||||||
|
font-size: 40px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-grid {
|
||||||
|
font-size: 10px;
|
||||||
|
min-height: 160px;
|
||||||
|
min-width: 470px;
|
||||||
|
background-color: $bgfull;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
min-height: 70px;
|
||||||
|
min-width: 450px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-button {
|
||||||
|
min-height: 65px;
|
||||||
|
min-width: 70px;
|
||||||
|
margin: 5px;
|
||||||
|
margin-left: 22px;
|
||||||
|
&:nth-child(1) {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-part {
|
||||||
|
background: #1b1b1b;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-left: 2px solid $contrastbg;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-part {
|
||||||
|
background: #1b1b1b;
|
||||||
|
border-top-right-radius: 30px;
|
||||||
|
border-bottom-right-radius: 30px;
|
||||||
|
border-right: 2px solid $contrastbg;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-part:hover, .right-part:active {
|
||||||
|
color: $contrastbg;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
border-top-left-radius: 7px;
|
||||||
|
border-bottom-left-radius: 7px;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-part:hover, .left-part:active {
|
||||||
|
color: $contrastbg;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
border-top-right-radius: 7px;
|
||||||
|
border-bottom-right-radius: 7px;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player {
|
||||||
|
margin-top: 6px;
|
||||||
|
min-height: 220px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-box {
|
||||||
|
min-height: 100px;
|
||||||
|
min-width: 470px;
|
||||||
|
background-color: $bgfull;
|
||||||
|
border-top: 2px solid $contrastbg;
|
||||||
|
border-bottom: 2px solid $contrastbg;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.slider-label {
|
||||||
|
font-size: 30px;
|
||||||
|
min-width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale {
|
||||||
|
min-height: 55px;
|
||||||
|
min-width: 400px;
|
||||||
|
margin-left: 18px;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
highlight {
|
||||||
|
margin: 0px;
|
||||||
|
background-color: #79659f;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
trough {
|
||||||
|
background-color: #363847;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider {
|
||||||
|
margin: -4px;
|
||||||
|
min-width: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
background: #3e4153;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
|
||||||
|
transition: background-color 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
slider:hover {
|
||||||
|
background-color: #303240;
|
||||||
|
transition: background-color 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
config/ags/scss/widgets/systray.scss
Normal file
54
config/ags/scss/widgets/systray.scss
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
.sys-tray {
|
||||||
|
padding: 5px;
|
||||||
|
background-color: $bg;
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid $bgSecondary;
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
|
||||||
|
.tray-item {
|
||||||
|
all: unset;
|
||||||
|
padding: 0px 2px 0px 2px;
|
||||||
|
font-size: 25px;
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
background: rgba(127, 132, 156, 0.4);
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu {
|
||||||
|
background: $bgfull;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border: 2px solid #1b1b1b;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
check {
|
||||||
|
color: white;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 5px;
|
||||||
|
min-height: 20px;
|
||||||
|
min-width: 20px;
|
||||||
|
-gtk-icon-source: -gtk-icontheme('checkbox-symbolic');
|
||||||
|
|
||||||
|
&:checked {
|
||||||
|
-gtk-icon-source: -gtk-icontheme('checkbox-checked-symbolic');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuitem:not(.tray-item) {
|
||||||
|
padding: 5px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #1b1b1b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
config/ags/scss/widgets/traybuttons.scss
Normal file
107
config/ags/scss/widgets/traybuttons.scss
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
.osk-toggle,
|
||||||
|
.tablet-toggle,
|
||||||
|
.heart-toggle {
|
||||||
|
font-size: 28px;
|
||||||
|
min-height: 40px;
|
||||||
|
min-width: 53px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-panel {
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 80px;
|
||||||
|
min-height: 37px;
|
||||||
|
min-width: 105px;
|
||||||
|
padding: 1px 0px 1px 5px;
|
||||||
|
|
||||||
|
.toggle-on {
|
||||||
|
border-top-left-radius: 22px;
|
||||||
|
border-top-right-radius: 22px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-bottom: 0px solid $bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-settings-toggle {
|
||||||
|
font-size: 24px;
|
||||||
|
min-height: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
padding-right: 4px;
|
||||||
|
margin-left: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-off {
|
||||||
|
background-color: $bg;
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid $bgSecondary;
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-on {
|
||||||
|
background-color: $bg;
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid $contrastbg;
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-on:hover, .toggle-off:hover {
|
||||||
|
background-color: rgba(127, 132, 156, 0.4);
|
||||||
|
transition: background-color 0.5s ease-in-out,
|
||||||
|
border 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock {
|
||||||
|
font-size: 20px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
min-width: 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio {
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.battery {
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
.battery-indicator {
|
||||||
|
&.charging {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
&.charged {}
|
||||||
|
&.low {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
icon {
|
||||||
|
.charging {}
|
||||||
|
.discharging {}
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brightness {
|
||||||
|
trough {
|
||||||
|
margin-right: -50px;
|
||||||
|
progress {
|
||||||
|
margin-right: 50px;
|
||||||
|
margin-top: -30px;
|
||||||
|
border-radius: 80px;
|
||||||
|
min-height: 36px;
|
||||||
|
background: rgba(#5e497c, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip {
|
||||||
|
background: rgba(0,0,0, 0.6);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
30
config/ags/scss/widgets/workspaces.scss
Normal file
30
config/ags/scss/widgets/workspaces.scss
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
.workspaces {
|
||||||
|
background-color: $bg;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid $bgSecondary;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: 2px;
|
||||||
|
min-width: 20px;
|
||||||
|
border-radius: 100%;
|
||||||
|
* {color: transparent;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
border: none;
|
||||||
|
transition: border-color 0.25s linear;
|
||||||
|
}
|
||||||
|
.occupied {
|
||||||
|
border: 2px solid $bg;
|
||||||
|
background: $contrastbg;
|
||||||
|
transition: border-color 0.25s linear;
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
border: 2px solid #50fa7b;
|
||||||
|
transition: border-color 0.25s linear;
|
||||||
|
}
|
||||||
|
}
|
162
config/ags/services_wip/libinput.js
Normal file
162
config/ags/services_wip/libinput.js
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
import Libinput from './libinput.js';
|
||||||
|
Libinput.instance.connect('device-init', () => {
|
||||||
|
let pointers = [];
|
||||||
|
Libinput.devices.forEach(dev => {
|
||||||
|
if (dev.Capabilities.includes('pointer')) {
|
||||||
|
pointers.push(dev);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Libinput.addDebugInstance('pointers', pointers)
|
||||||
|
.connect('new-line', e => print(e.lastLine))
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { Service } = ags;
|
||||||
|
const { execAsync } = ags.Utils;
|
||||||
|
import GLib from 'gi://GLib';
|
||||||
|
import Gio from 'gi://Gio';
|
||||||
|
import GObject from 'gi://GObject';
|
||||||
|
|
||||||
|
class DebugInstance extends GObject.Object {
|
||||||
|
static {
|
||||||
|
GObject.registerClass({
|
||||||
|
Signals: {
|
||||||
|
'changed': {},
|
||||||
|
'closed': {},
|
||||||
|
'new-line': {},
|
||||||
|
},
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
devices = [];
|
||||||
|
name = '';
|
||||||
|
lastLine = '';
|
||||||
|
|
||||||
|
readOutput(stdout, stdin) {
|
||||||
|
stdout.read_line_async(GLib.PRIORITY_LOW, null, (stream, result) => {
|
||||||
|
try {
|
||||||
|
const [line] = stream.read_line_finish_utf8(result);
|
||||||
|
|
||||||
|
if (line !== null) {
|
||||||
|
this.lastLine = line;
|
||||||
|
this.emit('new-line');
|
||||||
|
this.readOutput(stdout, stdin);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutput(devs) {
|
||||||
|
try {
|
||||||
|
let args = [];
|
||||||
|
devs.forEach(dev => {
|
||||||
|
if (dev.Kernel) {
|
||||||
|
args.push('--device');
|
||||||
|
args.push(dev.Kernel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const proc = Gio.Subprocess.new(['libinput', 'debug-events', ...args],
|
||||||
|
Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE);
|
||||||
|
|
||||||
|
// Get the `stdin`and `stdout` pipes, wrapping `stdout` to make it easier to
|
||||||
|
// read lines of text
|
||||||
|
const stdinStream = proc.get_stdin_pipe();
|
||||||
|
const stdoutStream = new Gio.DataInputStream({
|
||||||
|
base_stream: proc.get_stdout_pipe(),
|
||||||
|
close_base_stream: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the loop
|
||||||
|
this.readOutput(stdoutStream, stdinStream);
|
||||||
|
} catch (e) {
|
||||||
|
logError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(name, devs) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.devices = devs;
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
this.getOutput(devs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LibinputService extends Service {
|
||||||
|
static {
|
||||||
|
Service.register(this, {
|
||||||
|
'device-init': ['boolean'],
|
||||||
|
'instance-closed': ['string'],
|
||||||
|
'instance-added': ['string'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
debugInstances = new Map();
|
||||||
|
devices = new Map();
|
||||||
|
|
||||||
|
get devices() { return this._devices; }
|
||||||
|
|
||||||
|
parseOutput(output) {
|
||||||
|
let lines = output.split('\n');
|
||||||
|
let device = null;
|
||||||
|
|
||||||
|
lines.forEach(line => {
|
||||||
|
let parts = line.split(':');
|
||||||
|
|
||||||
|
if (parts[0] === 'Device') {
|
||||||
|
device = {};
|
||||||
|
this.devices.set(parts[1].trim(), device);
|
||||||
|
}
|
||||||
|
else if (device && parts[1]) {
|
||||||
|
let key = parts[0].trim();
|
||||||
|
let value = parts[1].trim();
|
||||||
|
device[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.emit('device-init', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.debugInstances = new Map();
|
||||||
|
execAsync(['libinput', 'list-devices'])
|
||||||
|
.then(out => this.parseOutput(out))
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDebugInstance(name, devs) {
|
||||||
|
if (this.debugInstances.get(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
devs = Array(devs);
|
||||||
|
if (devs.some(dev => dev.Capabilities && dev.Capabilities.includes('pointer'))) {
|
||||||
|
}
|
||||||
|
const debugInst = new DebugInstance(name, devs);
|
||||||
|
|
||||||
|
debugInst.connect('closed', () => {
|
||||||
|
this.debugInstances.delete(name);
|
||||||
|
this.emit('instance-closed', name);
|
||||||
|
this.emit('changed');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.debugInstances.set(name, debugInst);
|
||||||
|
this.emit('instance-added', name);
|
||||||
|
return debugInst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Libinput {
|
||||||
|
static { Service.Libinput = this; }
|
||||||
|
static instance = new LibinputService();
|
||||||
|
|
||||||
|
static get devices() { return Libinput.instance.devices; }
|
||||||
|
static get debugInstances() { return Libinput.instance.debugInstances; }
|
||||||
|
|
||||||
|
static addDebugInstance(name, dev) {
|
||||||
|
return Libinput.instance.addDebugInstance(name, dev);
|
||||||
|
}
|
||||||
|
}
|
770
config/ags/style.css
Normal file
770
config/ags/style.css
Normal file
|
@ -0,0 +1,770 @@
|
||||||
|
* {
|
||||||
|
all: unset; }
|
||||||
|
|
||||||
|
.powermenu {
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #a5b6cf;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: MesloLGS NF;
|
||||||
|
/*font-family: Iosevka Nerd Font;*/
|
||||||
|
font-size: 70px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8); }
|
||||||
|
.powermenu label {
|
||||||
|
min-width: 140px;
|
||||||
|
min-height: 130px; }
|
||||||
|
.powermenu button {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 12px;
|
||||||
|
min-width: 80px;
|
||||||
|
transition: all ease .2s; }
|
||||||
|
.powermenu button:hover {
|
||||||
|
background-color: rgba(56, 44, 74, 0.8); }
|
||||||
|
.powermenu button:active {
|
||||||
|
background-color: rgba(56, 44, 74, 0.8); }
|
||||||
|
.powermenu button .content {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0px 15px 0px 15px; }
|
||||||
|
.powermenu .shutdown {
|
||||||
|
color: #dd6777; }
|
||||||
|
.powermenu .reboot {
|
||||||
|
color: #c296eb; }
|
||||||
|
.powermenu .logout {
|
||||||
|
color: #ecd3a0; }
|
||||||
|
|
||||||
|
.powermenu-clickhandler {
|
||||||
|
background-color: black; }
|
||||||
|
|
||||||
|
.osk-toggle,
|
||||||
|
.tablet-toggle,
|
||||||
|
.heart-toggle {
|
||||||
|
font-size: 28px;
|
||||||
|
min-height: 40px;
|
||||||
|
min-width: 53px; }
|
||||||
|
|
||||||
|
.notif-panel {
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 80px;
|
||||||
|
min-height: 37px;
|
||||||
|
min-width: 105px;
|
||||||
|
padding: 1px 0px 1px 5px; }
|
||||||
|
.notif-panel .toggle-on {
|
||||||
|
border-top-left-radius: 22px;
|
||||||
|
border-top-right-radius: 22px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-bottom: 0px solid rgba(40, 42, 54, 0.8); }
|
||||||
|
|
||||||
|
.quick-settings-toggle {
|
||||||
|
font-size: 24px;
|
||||||
|
min-height: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
padding-right: 4px;
|
||||||
|
margin-left: -3px; }
|
||||||
|
|
||||||
|
.toggle-off {
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid rgba(56, 44, 74, 0.8);
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.toggle-on {
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.toggle-on:hover, .toggle-off:hover {
|
||||||
|
background-color: rgba(127, 132, 156, 0.4);
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.clock {
|
||||||
|
font-size: 20px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
min-width: 230px; }
|
||||||
|
|
||||||
|
.audio {
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
font-size: 20px; }
|
||||||
|
|
||||||
|
.battery {
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
font-size: 20px; }
|
||||||
|
.battery .battery-indicator.charging {
|
||||||
|
color: green; }
|
||||||
|
.battery .battery-indicator.low {
|
||||||
|
color: red; }
|
||||||
|
.battery .label {
|
||||||
|
font-size: 20px; }
|
||||||
|
|
||||||
|
.brightness trough {
|
||||||
|
margin-right: -50px; }
|
||||||
|
.brightness trough progress {
|
||||||
|
margin-right: 50px;
|
||||||
|
margin-top: -30px;
|
||||||
|
border-radius: 80px;
|
||||||
|
min-height: 36px;
|
||||||
|
background: rgba(94, 73, 124, 0.8); }
|
||||||
|
|
||||||
|
tooltip {
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
border-radius: 5px; }
|
||||||
|
|
||||||
|
.workspaces {
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid rgba(56, 44, 74, 0.8);
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px; }
|
||||||
|
.workspaces .button {
|
||||||
|
margin: 2px;
|
||||||
|
min-width: 20px;
|
||||||
|
border-radius: 100%; }
|
||||||
|
.workspaces .button * {
|
||||||
|
color: transparent; }
|
||||||
|
.workspaces .empty {
|
||||||
|
border: none;
|
||||||
|
transition: border-color 0.25s linear; }
|
||||||
|
.workspaces .occupied {
|
||||||
|
border: 2px solid rgba(40, 42, 54, 0.8);
|
||||||
|
background: rgba(189, 147, 249, 0.8);
|
||||||
|
transition: border-color 0.25s linear; }
|
||||||
|
.workspaces .active {
|
||||||
|
border: 2px solid #50fa7b;
|
||||||
|
transition: border-color 0.25s linear; }
|
||||||
|
|
||||||
|
.sys-tray {
|
||||||
|
padding: 5px;
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #CBA6F7;
|
||||||
|
border-radius: 80px;
|
||||||
|
border: 2px solid rgba(56, 44, 74, 0.8);
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
.sys-tray .tray-item {
|
||||||
|
all: unset;
|
||||||
|
padding: 0px 2px 0px 2px;
|
||||||
|
font-size: 25px;
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
.sys-tray .tray-item:hover {
|
||||||
|
background: rgba(127, 132, 156, 0.4);
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: background-color 0.5s ease-in-out, border 0.5s ease-in-out; }
|
||||||
|
.sys-tray menu {
|
||||||
|
background: #282a36;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border: 2px solid #1b1b1b;
|
||||||
|
border-radius: 10px; }
|
||||||
|
.sys-tray menu check {
|
||||||
|
color: white;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 5px;
|
||||||
|
min-height: 20px;
|
||||||
|
min-width: 20px;
|
||||||
|
-gtk-icon-source: -gtk-icontheme("checkbox-symbolic"); }
|
||||||
|
.sys-tray menu check:checked {
|
||||||
|
-gtk-icon-source: -gtk-icontheme("checkbox-checked-symbolic"); }
|
||||||
|
.sys-tray menuitem:not(.tray-item) {
|
||||||
|
padding: 5px;
|
||||||
|
transition: all 0.2s ease-in-out; }
|
||||||
|
.sys-tray menuitem:not(.tray-item):hover {
|
||||||
|
background: #1b1b1b; }
|
||||||
|
|
||||||
|
.notification-center {
|
||||||
|
min-height: 700px;
|
||||||
|
min-width: 524px;
|
||||||
|
background: rgba(40, 42, 54, 0.8);
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.6); }
|
||||||
|
.notification-center * {
|
||||||
|
font-size: 16px; }
|
||||||
|
.notification-center .header {
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 22px;
|
||||||
|
margin-bottom: 9px; }
|
||||||
|
.notification-center .header label {
|
||||||
|
font-size: 22px; }
|
||||||
|
.notification-center .header button {
|
||||||
|
all: unset;
|
||||||
|
transition: 200ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
color: #eee;
|
||||||
|
background-color: #664C90;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
padding: 4.5px 9px; }
|
||||||
|
.notification-center .header button:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: rgba(238, 238, 238, 0.154);
|
||||||
|
color: #f1f1f1; }
|
||||||
|
.notification-center .header button:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: rgba(102, 76, 144, 0.3);
|
||||||
|
color: rgba(238, 238, 238, 0.3); }
|
||||||
|
.notification-center .header button label {
|
||||||
|
font-size: 1.2em; }
|
||||||
|
.notification-center .notification-list-box {
|
||||||
|
background: #282a36;
|
||||||
|
padding: 0 12px 0 12px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5); }
|
||||||
|
.notification-center .notification-list-box scrollbar {
|
||||||
|
all: unset;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0; }
|
||||||
|
.notification-center .notification-list-box scrollbar * {
|
||||||
|
all: unset; }
|
||||||
|
.notification-center .notification-list-box scrollbar:hover {
|
||||||
|
border-radius: 8px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0; }
|
||||||
|
.notification-center .notification-list-box scrollbar.vertical {
|
||||||
|
transition: 200ms;
|
||||||
|
background-color: rgba(23, 23, 23, 0.3); }
|
||||||
|
.notification-center .notification-list-box scrollbar.vertical:hover {
|
||||||
|
background-color: rgba(23, 23, 23, 0.7); }
|
||||||
|
.notification-center .notification-list-box scrollbar.vertical:hover slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.7);
|
||||||
|
min-width: .6em; }
|
||||||
|
.notification-center .notification-list-box scrollbar.vertical slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: .4em;
|
||||||
|
min-height: 2em;
|
||||||
|
transition: 200ms; }
|
||||||
|
.notification-center .placeholder {
|
||||||
|
color: white; }
|
||||||
|
.notification-center .placeholder image {
|
||||||
|
font-size: 7em;
|
||||||
|
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6); }
|
||||||
|
.notification-center .placeholder label {
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
|
||||||
|
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6); }
|
||||||
|
|
||||||
|
.notification.critical > box {
|
||||||
|
box-shadow: inset 0 0 0.5em 0 #e67090; }
|
||||||
|
|
||||||
|
.notification > box {
|
||||||
|
all: unset;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.4);
|
||||||
|
margin: 9px 9px 0 9px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
padding: 16.2px; }
|
||||||
|
.notification > box * {
|
||||||
|
font-size: 16px; }
|
||||||
|
|
||||||
|
.notification:hover .close-button {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: rgba(238, 238, 238, 0.154);
|
||||||
|
background-color: rgba(230, 112, 144, 0.5); }
|
||||||
|
|
||||||
|
.notification .title {
|
||||||
|
margin-right: 9px;
|
||||||
|
font-size: 1.1em; }
|
||||||
|
|
||||||
|
.notification .description {
|
||||||
|
font-size: .9em;
|
||||||
|
min-width: 350px; }
|
||||||
|
|
||||||
|
.notification .icon {
|
||||||
|
border-radius: 7.2px;
|
||||||
|
margin-right: 9px; }
|
||||||
|
|
||||||
|
.notification .icon.img {
|
||||||
|
border: 1px solid rgba(238, 238, 238, 0.03); }
|
||||||
|
|
||||||
|
.notification .actions button {
|
||||||
|
all: unset;
|
||||||
|
transition: all 500ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
background-color: rgba(238, 238, 238, 0.06);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
border-radius: 7.2px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 4.5px 9px;
|
||||||
|
margin: 9px 4.5px 0; }
|
||||||
|
.notification .actions button * {
|
||||||
|
font-size: 16px; }
|
||||||
|
.notification .actions button:focus {
|
||||||
|
box-shadow: inset 0 0 0 1px #51a4e7;
|
||||||
|
background-color: rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification .actions button:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification .actions button:active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification .actions button:active:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification .actions button:checked {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification .actions button:checked:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification .actions button:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent; }
|
||||||
|
.notification .actions button:first-child {
|
||||||
|
margin-left: 0; }
|
||||||
|
.notification .actions button:last-child {
|
||||||
|
margin-right: 0; }
|
||||||
|
|
||||||
|
.notification .actions button.on {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification .actions button.on:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
|
||||||
|
.notification .actions button.active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification .actions button.active:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
|
||||||
|
.notification button.close-button {
|
||||||
|
all: unset;
|
||||||
|
transition: all 500ms;
|
||||||
|
border-radius: 9px;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 9px;
|
||||||
|
border-radius: 7.2px;
|
||||||
|
min-width: 1.2em;
|
||||||
|
min-height: 1.2em; }
|
||||||
|
.notification button.close-button * {
|
||||||
|
font-size: 16px; }
|
||||||
|
.notification button.close-button:focus {
|
||||||
|
box-shadow: inset 0 0 0 1px #51a4e7;
|
||||||
|
background-color: rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification button.close-button:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-color: rgba(238, 238, 238, 0.154);
|
||||||
|
background-color: rgba(230, 112, 144, 0.5); }
|
||||||
|
.notification button.close-button:active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7;
|
||||||
|
background-image: linear-gradient(#e67090, #e67090); }
|
||||||
|
.notification button.close-button:active:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification button.close-button:checked {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification button.close-button:checked:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
.notification button.close-button:disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent; }
|
||||||
|
|
||||||
|
.notification button.close-button.on {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification button.close-button.on:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
|
||||||
|
.notification button.close-button.active {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
|
||||||
|
background-image: linear-gradient(to right, #51a4e7, #6cb2eb);
|
||||||
|
background-color: #51a4e7; }
|
||||||
|
.notification button.close-button.active:hover {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03), inset 0 0 0 99px rgba(238, 238, 238, 0.154); }
|
||||||
|
|
||||||
|
.date {
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #a5b6cf;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8); }
|
||||||
|
|
||||||
|
.timebox {
|
||||||
|
margin: 30px 0px; }
|
||||||
|
.timebox .time-container .content {
|
||||||
|
font-family: Product Sans;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 60px; }
|
||||||
|
.timebox .time-container .divider {
|
||||||
|
margin: 8px 15px;
|
||||||
|
padding: 0px 1px;
|
||||||
|
background: linear-gradient(#dd6777, #c296eb, #86aaec, #93cee9); }
|
||||||
|
.timebox .date-container {
|
||||||
|
font-family: Product Sans;
|
||||||
|
margin-top: 2px; }
|
||||||
|
|
||||||
|
.cal-box {
|
||||||
|
font-family: Product Sans;
|
||||||
|
border-radius: 30px;
|
||||||
|
padding: 0 1rem .2rem;
|
||||||
|
color: #a5b6cf;
|
||||||
|
background-color: #282a36;
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
margin: 0px 12px 18px 12px; }
|
||||||
|
.cal-box .cal {
|
||||||
|
font-size: 20px;
|
||||||
|
background-color: inherit;
|
||||||
|
padding: .5rem .10rem 0rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
border-radius: 30px; }
|
||||||
|
.cal-box .cal > * {
|
||||||
|
border: solid 0px transparent; }
|
||||||
|
.cal-box .cal.highlight {
|
||||||
|
padding: 10rem; }
|
||||||
|
|
||||||
|
calendar:selected {
|
||||||
|
color: #93cee9; }
|
||||||
|
|
||||||
|
calendar.header {
|
||||||
|
color: #93cee9;
|
||||||
|
font-weight: bold; }
|
||||||
|
|
||||||
|
calendar.button {
|
||||||
|
color: #93cee9; }
|
||||||
|
|
||||||
|
calendar.highlight {
|
||||||
|
color: #90ceaa;
|
||||||
|
font-weight: bold; }
|
||||||
|
|
||||||
|
calendar:indeterminate {
|
||||||
|
color: #262831; }
|
||||||
|
|
||||||
|
.quick-settings {
|
||||||
|
font-size: 30px;
|
||||||
|
min-width: 500px;
|
||||||
|
padding: 0px 0px 0px 0px;
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
border-radius: 30px 0 30px 30px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8); }
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: 30px; }
|
||||||
|
|
||||||
|
.grid-label {
|
||||||
|
font-size: 30px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
|
min-width: 50px; }
|
||||||
|
|
||||||
|
.sub-label {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-left: 31px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top-right-radius: 20px;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-bottom-right-radius: 20px;
|
||||||
|
min-width: 106px;
|
||||||
|
background: #1b1b1b; }
|
||||||
|
.sub-label:nth-child(1) {
|
||||||
|
margin-left: 25px; }
|
||||||
|
|
||||||
|
.grid-chev {
|
||||||
|
margin-left: 0px;
|
||||||
|
font-size: 40px;
|
||||||
|
margin-right: 5px; }
|
||||||
|
|
||||||
|
.button-grid {
|
||||||
|
font-size: 10px;
|
||||||
|
min-height: 160px;
|
||||||
|
min-width: 470px;
|
||||||
|
background-color: #282a36;
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 30px; }
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
min-height: 70px;
|
||||||
|
min-width: 450px;
|
||||||
|
margin-left: 20px; }
|
||||||
|
|
||||||
|
.grid-button {
|
||||||
|
min-height: 65px;
|
||||||
|
min-width: 70px;
|
||||||
|
margin: 5px;
|
||||||
|
margin-left: 22px; }
|
||||||
|
.grid-button:nth-child(1) {
|
||||||
|
margin-left: 5px; }
|
||||||
|
|
||||||
|
.left-part {
|
||||||
|
background: #1b1b1b;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-left: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
transition: all 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.right-part {
|
||||||
|
background: #1b1b1b;
|
||||||
|
border-top-right-radius: 30px;
|
||||||
|
border-bottom-right-radius: 30px;
|
||||||
|
border-right: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
transition: all 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.right-part:hover, .right-part:active {
|
||||||
|
color: rgba(189, 147, 249, 0.8);
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top-left-radius: 7px;
|
||||||
|
border-bottom-left-radius: 7px;
|
||||||
|
transition: all 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.left-part:hover, .left-part:active {
|
||||||
|
color: rgba(189, 147, 249, 0.8);
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-top-right-radius: 7px;
|
||||||
|
border-bottom-right-radius: 7px;
|
||||||
|
transition: all 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.player {
|
||||||
|
margin-top: 6px;
|
||||||
|
min-height: 220px;
|
||||||
|
opacity: 0; }
|
||||||
|
|
||||||
|
.slider-box {
|
||||||
|
min-height: 100px;
|
||||||
|
min-width: 470px;
|
||||||
|
background-color: #282a36;
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 20px; }
|
||||||
|
.slider-box .slider-label {
|
||||||
|
font-size: 30px;
|
||||||
|
min-width: 30px; }
|
||||||
|
.slider-box scale {
|
||||||
|
min-height: 55px;
|
||||||
|
min-width: 400px;
|
||||||
|
margin-left: 18px;
|
||||||
|
margin-right: 20px; }
|
||||||
|
.slider-box scale highlight {
|
||||||
|
margin: 0px;
|
||||||
|
background-color: #79659f;
|
||||||
|
border-radius: 2em; }
|
||||||
|
.slider-box scale trough {
|
||||||
|
background-color: #363847;
|
||||||
|
border-radius: 2em; }
|
||||||
|
.slider-box scale slider {
|
||||||
|
margin: -4px;
|
||||||
|
min-width: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
background: #3e4153;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
|
||||||
|
transition: background-color 0.5s ease-in-out; }
|
||||||
|
.slider-box scale slider:hover {
|
||||||
|
background-color: #303240;
|
||||||
|
transition: background-color 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
transition: -gtk-icon-transform 0.3s ease-in-out;
|
||||||
|
margin-bottom: 12px; }
|
||||||
|
|
||||||
|
.media {
|
||||||
|
margin-top: 9px; }
|
||||||
|
|
||||||
|
.player {
|
||||||
|
all: unset;
|
||||||
|
padding: 10px;
|
||||||
|
min-width: 400px;
|
||||||
|
min-height: 200px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-top: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-bottom: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
transition: background 250ms; }
|
||||||
|
.player .top {
|
||||||
|
font-size: 23px; }
|
||||||
|
.player .metadata .title {
|
||||||
|
font-weight: 500;
|
||||||
|
transition: text 250ms; }
|
||||||
|
.player .metadata .artist {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 15px;
|
||||||
|
transition: text 250ms; }
|
||||||
|
.player .bottom {
|
||||||
|
font-size: 30px; }
|
||||||
|
.player .pausebutton {
|
||||||
|
transition: background-color ease .2s, color ease .2s;
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 4px 4px 4px 7px; }
|
||||||
|
.player .playing {
|
||||||
|
transition: background-color ease .2s, color ease .2s;
|
||||||
|
border-radius: 15px; }
|
||||||
|
.player .stopped,
|
||||||
|
.player .paused {
|
||||||
|
transition: background-color ease .2s, color ease .2s;
|
||||||
|
border-radius: 26px;
|
||||||
|
padding: 4px 4px 4px 10px; }
|
||||||
|
.player button label {
|
||||||
|
min-width: 35px; }
|
||||||
|
|
||||||
|
.position-indicator {
|
||||||
|
min-width: 18px;
|
||||||
|
margin: 7px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
box-shadow: 0 0 5px 0px rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 100%; }
|
||||||
|
|
||||||
|
.previous,
|
||||||
|
.next,
|
||||||
|
.shuffle,
|
||||||
|
.loop {
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: color 200ms; }
|
||||||
|
.previous:hover,
|
||||||
|
.next:hover,
|
||||||
|
.shuffle:hover,
|
||||||
|
.loop:hover {
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: rgba(127, 132, 156, 0.4);
|
||||||
|
transition: color 200ms; }
|
||||||
|
|
||||||
|
.loop label {
|
||||||
|
padding-right: 8px; }
|
||||||
|
|
||||||
|
.position-slider highlight {
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 2em; }
|
||||||
|
|
||||||
|
.position-slider trough {
|
||||||
|
margin: 0 8px;
|
||||||
|
border-radius: 2em; }
|
||||||
|
|
||||||
|
.position-slider slider {
|
||||||
|
margin: -8px;
|
||||||
|
min-height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
|
||||||
|
transition: background-color 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.position-slider slider:hover {
|
||||||
|
transition: background-color 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.overview .workspace {
|
||||||
|
padding: 4px 15px 4px 0px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 10px; }
|
||||||
|
.overview .workspace.active {
|
||||||
|
background-color: rgba(38, 38, 38, 0.8);
|
||||||
|
border: 2px solid black; }
|
||||||
|
|
||||||
|
.overview .workspace .window {
|
||||||
|
background-color: #282a36;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0 10px;
|
||||||
|
transition: min-width 0.2s ease-in-out, min-height 0.2s ease-in-out, border-color 0.2s ease-in-out, font-size 0.2s ease-in-out; }
|
||||||
|
|
||||||
|
.overview .normal {
|
||||||
|
margin-bottom: 5px; }
|
||||||
|
.overview .normal .workspace .window {
|
||||||
|
border: 2px solid #411C6C; }
|
||||||
|
.overview .normal .workspace .window.active {
|
||||||
|
border: 2px solid purple; }
|
||||||
|
|
||||||
|
.overview .special .workspace .window {
|
||||||
|
border: 2px solid #333333; }
|
||||||
|
.overview .special .workspace .window.active {
|
||||||
|
border: 2px solid purple; }
|
||||||
|
|
||||||
|
.applauncher {
|
||||||
|
all: unset;
|
||||||
|
box-shadow: 0 0 4.5px 0 rgba(0, 0, 0, 0.6);
|
||||||
|
margin: 9px;
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
border-radius: 25px;
|
||||||
|
background-color: rgba(40, 42, 54, 0.8);
|
||||||
|
color: #f8f8f2;
|
||||||
|
padding: 16.2px; }
|
||||||
|
.applauncher * {
|
||||||
|
font-size: 16px; }
|
||||||
|
.applauncher .header {
|
||||||
|
margin: 16.2px;
|
||||||
|
margin-bottom: 0; }
|
||||||
|
.applauncher .header image, .applauncher .header entry {
|
||||||
|
all: unset;
|
||||||
|
border-radius: 9px;
|
||||||
|
color: #f8f8f2;
|
||||||
|
background-color: rgba(68, 71, 90, 0.6);
|
||||||
|
border: 1px solid #44475a;
|
||||||
|
padding: 4.5px; }
|
||||||
|
.applauncher .header image {
|
||||||
|
margin-right: 9px;
|
||||||
|
-gtk-icon-transform: scale(0.8);
|
||||||
|
font-size: 25.6px; }
|
||||||
|
.applauncher scrolledwindow {
|
||||||
|
padding: 16.2px;
|
||||||
|
min-width: 700px;
|
||||||
|
min-height: 450px; }
|
||||||
|
.applauncher scrolledwindow scrollbar, .applauncher scrolledwindow scrollbar * {
|
||||||
|
all: unset; }
|
||||||
|
.applauncher scrolledwindow scrollbar.vertical {
|
||||||
|
transition: 200ms;
|
||||||
|
background-color: rgba(23, 23, 23, 0.3); }
|
||||||
|
.applauncher scrolledwindow scrollbar.vertical:hover {
|
||||||
|
background-color: rgba(23, 23, 23, 0.7); }
|
||||||
|
.applauncher scrolledwindow scrollbar.vertical:hover slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.7);
|
||||||
|
min-width: .6em; }
|
||||||
|
.applauncher scrolledwindow scrollbar.vertical slider {
|
||||||
|
background-color: rgba(238, 238, 238, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: .4em;
|
||||||
|
min-height: 2em;
|
||||||
|
transition: 200ms; }
|
||||||
|
.applauncher .placeholder {
|
||||||
|
margin-top: 9px;
|
||||||
|
color: #f8f8f2;
|
||||||
|
font-size: 1.2em; }
|
||||||
|
.applauncher .app {
|
||||||
|
all: unset;
|
||||||
|
transition: 200ms;
|
||||||
|
padding: 9px; }
|
||||||
|
.applauncher .app:active {
|
||||||
|
background-color: rgba(189, 147, 249, 0.5);
|
||||||
|
border-radius: 9px;
|
||||||
|
box-shadow: inset 0 0 0 3px rgba(238, 238, 238, 0.03); }
|
||||||
|
.applauncher .app:active .title {
|
||||||
|
color: #f8f8f2; }
|
||||||
|
.applauncher .app:hover .title, .applauncher .app:focus .title {
|
||||||
|
color: rgba(189, 147, 249, 0.8); }
|
||||||
|
.applauncher .app:hover image, .applauncher .app:focus image {
|
||||||
|
-gtk-icon-shadow: 2px 2px rgba(189, 147, 249, 0.8); }
|
||||||
|
.applauncher .app label {
|
||||||
|
transition: 200ms; }
|
||||||
|
.applauncher .app label.title {
|
||||||
|
color: #f8f8f2; }
|
||||||
|
.applauncher .app label.description {
|
||||||
|
color: rgba(238, 238, 238, 0.7); }
|
||||||
|
.applauncher .app image {
|
||||||
|
transition: 200ms;
|
||||||
|
margin-right: 9px; }
|
11
config/discord/settings.json
Normal file
11
config/discord/settings.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"SKIP_HOST_UPDATE": true,
|
||||||
|
"IS_MAXIMIZED": false,
|
||||||
|
"IS_MINIMIZED": false,
|
||||||
|
"WINDOW_BOUNDS": {
|
||||||
|
"x": 7,
|
||||||
|
"y": 57,
|
||||||
|
"width": 1906,
|
||||||
|
"height": 1136
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,6 @@ Places Icons Static Size=22
|
||||||
1920x1200 screen: Height=567
|
1920x1200 screen: Height=567
|
||||||
1920x1200 screen: Width=604
|
1920x1200 screen: Width=604
|
||||||
1920x1200 screen: Window-Maximized=true
|
1920x1200 screen: Window-Maximized=true
|
||||||
3 screens: Window-Maximized=true
|
|
||||||
|
|
||||||
[MainWindow]
|
[MainWindow]
|
||||||
MenuBar=Disabled
|
MenuBar=Disabled
|
4
config/gtklock/config.ini
Normal file
4
config/gtklock/config.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[main]
|
||||||
|
modules=/nix/var/nix/profiles/system/sw/lib/gtklock/powerbar-module.so;/nix/var/nix/profiles/system/sw/lib/gtklock/playerctl-module.so
|
||||||
|
|
||||||
|
#;/nix/var/nix/profiles/system/sw/lib/gtklock/userinfo-module.so
|
5
config/gtklock/scripts/blur.sh
Executable file
5
config/gtklock/scripts/blur.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
grim -t jpeg /tmp/image.jpeg
|
||||||
|
convert /tmp/image.jpeg -blur 0x8 /tmp/image.jpeg
|
||||||
|
|
4
config/gtklock/scripts/lock.sh
Executable file
4
config/gtklock/scripts/lock.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
$AGS_PATH/tablet-toggle.sh laptop &
|
||||||
|
$LOCK_PATH/blur.sh
|
||||||
|
gtklock
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue