refactor(ags): remove unneeded agsV1 stuff
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-11-12 16:55:57 -05:00
parent c20ec51142
commit ab8bb48feb
137 changed files with 184 additions and 8950 deletions

View file

@ -1,12 +1,12 @@
# My NixOS configs # My NixOS configs
## Ags ## AGS
You might find it weird that most of my config is written in TypeScript. You might find it weird that most of my config is written in TypeScript.
That's because all my desktops run That's because all my desktops run
[AGS](https://github.com/Aylur/ags) [AGSv2](https://github.com/Aylur/ags/tree/v2)
for UI. Click on for UI. Click on
[this](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/nixosModules/ags) [this](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/nixosModules/ags-v2)
to see my configuration. to see my configuration.
I'm also a victim of Stockholm syndrome at this point and make my scripts I'm also a victim of Stockholm syndrome at this point and make my scripts

View file

@ -35,6 +35,5 @@ in {
sudo.text = mkDefault (mkBefore grosshackConf); sudo.text = mkDefault (mkBefore grosshackConf);
login.text = mkDefault (mkBefore grosshackConf); login.text = mkDefault (mkBefore grosshackConf);
polkit-1.text = mkDefault (mkBefore grosshackConf); polkit-1.text = mkDefault (mkBefore grosshackConf);
ags.text = mkDefault (mkBefore grosshackConf);
}; };
} }

Binary file not shown.

BIN
flake.nix

Binary file not shown.

View file

@ -156,11 +156,6 @@ let
}; };
agsInputs = { agsInputs = {
ags = mkDep {
owner = "Aylur";
repo = "ags";
};
agsV2 = mkDep { agsV2 = mkDep {
owner = "Aylur"; owner = "Aylur";
repo = "ags"; repo = "ags";

View file

@ -27,7 +27,7 @@
}) {}; }) {};
in "${pkg}/lib/node_modules/${pkg.pname}/node_modules"; in "${pkg}/lib/node_modules/${pkg.pname}/node_modules";
buildNodeTypes = { buildGirTypes = {
configPath, configPath,
packages, packages,
pname, pname,

View file

@ -6,44 +6,55 @@
osConfig, osConfig,
pkgs, pkgs,
... ...
}: { }: let
options.programs.ags-v2.lockPkg = lib.mkOption { # Inputs
type = with lib.types; nullOr package; inherit (self.inputs) agsV2 gtk-session-lock;
default = null;
# Libs
inherit (lib) attrValues boolToString optionals removeAttrs;
# Cfg info
inherit (osConfig.networking) hostName;
cfgDesktop = osConfig.roles.desktop;
fullConfPath = "/home/${cfgDesktop.user}/${agsConfigDir}";
# Astal libraries
gtkSessionLock = gtk-session-lock.packages.${pkgs.system}.default;
agsV2Packages = agsV2.packages.${pkgs.system};
astalLibs = attrValues (removeAttrs agsV2.inputs.astal.packages.${pkgs.system} ["docs" "gjs"]) ++ [gtkSessionLock];
# Final ags package
agsFull = agsV2Packages.ags.override {extraPackages = astalLibs;};
in {
options.programs.ags-v2 = {
package = lib.mkOption {
type = with lib.types; nullOr package;
default = null;
};
lockPkg = lib.mkOption {
type = with lib.types; nullOr package;
default = null;
};
}; };
config = let config = {
# Inputs # Make these accessible outside these files
inherit (self.inputs) agsV2 gtk-session-lock; programs.ags-v2 = {
package = agsFull;
# Libs lockPkg = pkgs.writeShellApplication {
inherit (lib) attrValues boolToString optionals removeAttrs; name = "lock";
runtimeInputs = [agsFull];
text = ''
export CONF="lock"
# Cfg info if [ "$#" == 0 ]; then
inherit (osConfig.networking) hostName; exec ags run ${fullConfPath}
cfgDesktop = osConfig.roles.desktop; else
fullConfPath = "/home/${cfgDesktop.user}/${agsConfigDir}"; exec ags "$@" -i lock
fi
# Astal libraries '';
gtkSessionLock = gtk-session-lock.packages.${pkgs.system}.default; };
agsV2Packages = agsV2.packages.${pkgs.system};
astalLibs = attrValues (removeAttrs agsV2.inputs.astal.packages.${pkgs.system} ["docs" "gjs"]) ++ [gtkSessionLock];
# Final ags package
agsFull = agsV2Packages.ags.override {extraPackages = astalLibs;};
in {
programs.ags-v2.lockPkg = pkgs.writeShellApplication {
name = "lock";
runtimeInputs = [agsFull];
text = ''
export CONF="lock"
if [ "$#" == 0 ]; then
exec ags run ${fullConfPath}
else
exec ags "$@" -i lock
fi
'';
}; };
home = { home = {
@ -89,10 +100,10 @@
inherit inherit
(import "${self}/lib" {inherit pkgs self;}) (import "${self}/lib" {inherit pkgs self;})
buildNodeModules buildNodeModules
buildNodeTypes buildGirTypes
; ;
in ( in (
(buildNodeTypes { (buildGirTypes {
pname = "agsV2"; pname = "agsV2";
configPath = "${agsConfigDir}/@girs"; configPath = "${agsConfigDir}/@girs";
packages = astalLibs; packages = astalLibs;

View file

@ -0,0 +1 @@
# Moving to [AGSv2](https://github.com/Aylur/ags/tree/v2)

View file

@ -0,0 +1,78 @@
// For ./ts/bar/hovers/keyboard.ts
export interface Keyboard {
address: string
name: string
rules: string
model: string
layout: string
variant: string
options: string
active_keymap: string
main: boolean
}
// For ./ts/media-player/gesture.ts
export interface Gesture {
attribute?: object
setup?(self: OverlayGeneric): void
props?: OverlayProps<unknown & Widget, unknown>
}
// For ./ts/media-player/mpris.ts
type PlayerDragProps = unknown & { dragging: boolean };
export type PlayerDrag = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, unknown & PlayerDragProps
>;
interface Colors {
imageAccent: string
buttonAccent: string
buttonText: string
hoverAccent: string
}
// For ./ts/media-player
export interface PlayerBoxProps {
bgStyle: string
player: MprisPlayer
}
export type PlayerBox = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, PlayerBoxProps
>;
export type PlayerOverlay = AgsOverlay<AgsWidget, {
players: Map
setup: boolean
dragging: boolean
includesWidget(playerW: PlayerBox): PlayerBox
showTopOnly(): void
moveToTop(player: PlayerBox): void
}>;
export interface PlayerButtonType {
player: MprisPlayer
colors: Var<Colors>
children: StackProps['children']
onClick: string
prop: string
}
// For ./ts/on-screen-keyboard
export type OskWindow = Window<BoxGeneric, {
startY: null | number
setVisible: (state: boolean) => void
killGestureSigs: () => void
setSlideUp: () => void
setSlideDown: () => void
}>;
// For ./ts/quick-settings
import { BluetoothDevice as BTDev } from 'types/service/bluetooth.ts';
export interface APType {
bssid: string
address: string
lastSeen: number
ssid: string
active: boolean
strength: number
iconName: string
}
export type APBox = AgsBox<unknown & Widget, { ap: Var<APType> }>;
export type DeviceBox = AgsBox<unknown & Widget, { dev: BTDev }>;

View file

@ -1,40 +0,0 @@
# [AGS](https://github.com/Aylur/ags)
## Nix
This directory is the Nix entrypoint to my AGS configration.
On system activation, if this module is imported, it will
generate the `config.js` of the host with the host's name
as the parameter in `transpileTypeScript` to support a different
setup per device.
## Non-Nix
To use this setup without Nix:
```js
/* ~/.config/ags/config.js */
import { transpileTypeScript } from './js/utils.js';
export default (await transpileTypeScript('wim')).default;
```
If you want to try my main config, this is what you need to have
as your `config.js` after copying the contents of `./config` to
`~/.config/ags`
## Dependencies
The main dependencies to try it are as follows:
- **bun** to transpile TS to JS
- **dart-sass** to compile SCSS to CSS
- **[coloryou](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/common/pkgs/coloryou)** for media player colors
- **playerctl** for media player functionality
If you're interested in my 2-1 laptop setup, you'll need:
- **ydotool** for my custom on-screen keyboard
- **lisgd** to have touch screen gestures

View file

@ -1,19 +0,0 @@
{
cliphist,
gawk,
imagemagick,
ripgrep,
writeShellApplication,
...
}:
writeShellApplication {
name = "clipboard-manager";
runtimeInputs = [
cliphist
gawk
imagemagick
ripgrep
];
text = builtins.readFile ./script.sh;
}

View file

@ -1,45 +0,0 @@
set +o errexit
# Modified from https://github.com/sentriz/cliphist/blob/master/contrib/cliphist-wofi-img
# set up thumbnail directory
thumb_dir="/tmp/cliphist/thumbs"
mkdir -p "$thumb_dir"
cliphist_list="$(cliphist list)"
# delete thumbnails in cache but not in cliphist
for thumb in "$thumb_dir"/*; do
clip_id="${thumb##*/}"
clip_id="${clip_id%.*}"
check=$(rg <<< "$cliphist_list" "^$clip_id\s")
if [ -z "$check" ]; then
>&2 rm -v "$thumb"
fi
done
# create thumbnail if image not processed already
read -r -d '' prog <<EOF
/^[0-9]+\s<meta http-equiv=/ { next }
match(\$0, /^([0-9]+)\s(\[\[\s)?binary.*(jpg|jpeg|png|bmp)/, grp) {
image = grp[1]"."grp[3]
system("[ -f $thumb_dir/"image" ] || echo " grp[1] "\\\\\t | cliphist decode | convert - -resize '256x256>' $thumb_dir/"image )
print "img:$thumb_dir/"image
next
}
1
EOF
output=$(gawk <<< "$cliphist_list" "$prog")
# Use a while loop with read to iterate over each line
echo "$output" | while IFS= read -r line; do
if [[ ! $line =~ ^img:/tmp/cliphist/thumbs ]]; then
[[ $line =~ ([0-9]+) ]]
line=${BASH_REMATCH[1]}
fi
echo "$line"
done

View file

@ -1 +0,0 @@
use flake "$FLAKE#node"

View file

@ -1,40 +0,0 @@
import Brightness from './services/brightness.ts';
import GSR from './services/gpu-screen-recorder.ts';
import Pointers from './services/pointers.ts';
import AppLauncher from './ts/applauncher/main.ts';
import Bar from './ts/bar/binto.ts';
import Clipboard from './ts/clipboard/main.ts';
import Calendar from './ts/date/binto.ts';
import { NotifPopups, NotifCenter } from './ts/notifications/binto.ts';
import OSD from './ts/osd/main.ts';
import Powermenu from './ts/powermenu.ts';
import Screenshot from './ts/screenshot/main.ts';
// TODO: add workspace indicator
App.config({
icons: './icons',
onConfigParsed: () => {
Brightness.capsName = 'input30::capslock';
globalThis.Brightness = Brightness;
globalThis.Pointers = Pointers;
setTimeout(() => {
globalThis.GSR = new GSR();
}, 1000);
},
windows: () => [
AppLauncher(),
Calendar(),
Clipboard(),
NotifCenter(),
Powermenu(),
Screenshot(),
Bar(),
NotifPopups(),
OSD(),
],
});

View file

@ -1,452 +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({
// @ts-expect-error this should work
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',
],
},
});

View file

@ -1,177 +0,0 @@
import { Widget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import GObject from 'types/@girs/gobject-2.0/gobject-2.0';
import { Variable as Var } from 'types/variable';
import { Widget as agsWidget } from 'types/widgets/widget';
export type AgsWidget = agsWidget<unknown> & Widget;
// Generic widgets
import AgsBox from 'types/widgets/box.ts';
export type BoxGeneric = AgsBox<unknown & Widget, unknown>;
import AgsCenterBox, { CenterBoxProps } from 'types/widgets/centerbox';
export type CenterBoxGeneric = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, unknown
>;
export type CenterBoxPropsGeneric = CenterBoxProps<
unknown & Widget, unknown & Widget, unknown & Widget, unknown
>;
import AgsEventBox from 'types/widgets/eventbox';
export type EventBoxGeneric = AgsEventBox<unknown & Widget, unknown>;
import AgsIcon, { IconProps } from 'types/widgets/icon';
export type IconGeneric = AgsIcon<unknown>;
export type IconPropsGeneric = IconProps<unknown>;
import AgsLabel from 'types/widgets/label';
export type LabelGeneric = AgsLabel<unknown>;
import AgsOverlay, { OverlayProps } from 'types/widgets/overlay';
export type OverlayGeneric = AgsOverlay<unknown & Widget, unknown>;
import AgsProgressBar from 'types/widgets/progressbar';
export type ProgressBarGeneric = AgsProgressBar<unknown & Widget, unknown>;
import AgsRevealer, { RevealerProps } from 'types/widgets/revealer';
export type RevealerGeneric = AgsRevealer<unknown & Widget, unknown>;
import AgsStack, { StackProps } from 'types/widgets/stack';
export type StackGeneric = AgsStack<Record<string, Widget>, unknown>;
import AgsScrollable from 'types/widgets/scrollable';
export type ScrollableGeneric = AgsScrollable<unkown & Widget, unknown>;
import AgsWindow from 'types/widgets/window';
export type WindowGeneric = AgsWindow<unknown & Widget, unknown>;
// For ./ts/applauncher/main.ts
import { Application } from 'types/service/applications.ts';
import { CursorBoxProps } from 'ts/misc/cursorbox';
export type AgsAppItem = AgsEventBox<Widget, { app: Application }
& CursorBoxProps<Widget, unknown>>;
// For ./ts/bar/hovers/keyboard.ts
export interface Keyboard {
address: string
name: string
rules: string
model: string
layout: string
variant: string
options: string
active_keymap: string
main: boolean
}
// For ./ts/bar/items/workspaces.ts
// TODO: improve type
export type Workspace = AgsRevealer<unknown & Widget, unknown & { id: number }>;
// For ./ts/bar/fullscreen.ts
export type DefaultProps = RevealerProps<CenterBoxGeneric>;
// For ./ts/media-player/gesture.ts
export interface Gesture {
attribute?: object
setup?(self: OverlayGeneric): void
props?: OverlayProps<unknown & Widget, unknown>
}
// For ./ts/media-player/mpris.ts
type PlayerDragProps = unknown & { dragging: boolean };
export type PlayerDrag = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, unknown & PlayerDragProps
>;
interface Colors {
imageAccent: string
buttonAccent: string
buttonText: string
hoverAccent: string
}
// For ./ts/media-player
export interface PlayerBoxProps {
bgStyle: string
player: MprisPlayer
}
export type PlayerBox = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, PlayerBoxProps
>;
export type PlayerOverlay = AgsOverlay<AgsWidget, {
players: Map
setup: boolean
dragging: boolean
includesWidget(playerW: PlayerBox): PlayerBox
showTopOnly(): void
moveToTop(player: PlayerBox): void
}>;
export interface PlayerButtonType {
player: MprisPlayer
colors: Var<Colors>
children: StackProps['children']
onClick: string
prop: string
}
// For ./ts/notifications/gesture.js
interface NotifGestureProps {
dragging: boolean
hovered: boolean
ready: boolean
id: number
slideAway(side: 'Left' | 'Right'): void
}
export type NotifGesture = AgsEventBox<BoxGeneric, NotifGestureProps>;
// For ./ts/osd/ctor.ts
export type OSDStack = AgsStack<unknown & Widget, {
popup(osd: string): void
}>;
export type ConnectFunc = (self?: ProgressBarGeneric) => void;
export interface OSD {
name: string
icon: IconPropsGeneric['icon']
info: {
mod: GObject.Object
signal?: string | string[]
logic?(self: ProgressBarGeneric): void
widget?: AgsWidget
}
}
// For ./ts/on-screen-keyboard
export type OskWindow = Window<BoxGeneric, {
startY: null | number
setVisible: (state: boolean) => void
killGestureSigs: () => void
setSlideUp: () => void
setSlideDown: () => void
}>;
// For CursorBox
import { CursorBox, CursorBoxProps } from 'ts/misc/cursorbox';
export type CursorBox = CursorBox;
export type CursorBoxProps = CursorBoxProps;
// For PopupWindow
export type HyprTransition = 'slide' | 'slide top' | 'slide bottom' | 'slide left' |
'slide right' | 'popin' | 'fade';
export type CloseType = 'none' | 'stay' | 'released' | 'clicked';
import { PopupWindow } from 'ts/misc/popup';
export type PopupWindow = PopupWindow;
// For ./ts/quick-settings
import { BluetoothDevice as BTDev } from 'types/service/bluetooth.ts';
export interface APType {
bssid: string
address: string
lastSeen: number
ssid: string
active: boolean
strength: number
iconName: string
}
export type APBox = AgsBox<unknown & Widget, { ap: Var<APType> }>;
export type DeviceBox = AgsBox<unknown & Widget, { dev: BTDev }>;

View file

@ -1,52 +0,0 @@
const { execAsync, monitorFile } = Utils;
/**
* @param {string} host the name of the machine/user who's running ags
*/
const watchAndCompileSass = (host) => {
const reloadCss = () => {
const scss = `${App.configDir}/scss/${host}.scss`;
const css = `/tmp/ags-${host}/style.css`;
execAsync(`sass ${scss} ${css}`).then(() => {
App.resetCss();
App.applyCss(css);
}).catch(print);
};
monitorFile(
`${App.configDir}/scss`,
reloadCss,
);
reloadCss();
};
/**
* @param {string} host the name of the machine/user who's running ags
* @returns the config
*/
export const transpileTypeScript = async(host) => {
const outPath = `/tmp/ags-${host}/index.js`;
await execAsync([
'bash', '-c',
// Create the dir if it doesn't exist
`mkdir -p /tmp/ags-${host}; ` +
// Let bun see tsconfig.json
`cd ${App.configDir};` +
`bun build ${App.configDir}/${host === 'lockscreen' ? 'ts/lockscreen/main' : host}.ts ` +
'--external resource:///* ' +
'--external gi://* ' +
'--external cairo ' +
// Since bun wants to write in cwd, we just redirect stdin instead
`> ${outPath}`,
]).catch(print);
watchAndCompileSass(host);
return await import(`file://${outPath}`);
};

View file

@ -1,3 +0,0 @@
import { transpileTypeScript } from './js/utils.js';
export default (await transpileTypeScript('lockscreen')).default;

Binary file not shown.

View file

@ -1,18 +0,0 @@
{
"name": "ags-node-modules",
"version": "0.0.0",
"main": "config.js",
"type": "module",
"dependencies": {
"@eslint/js": "9.13.0",
"@stylistic/eslint-plugin": "2.9.0",
"@types/eslint__js": "8.42.3",
"@types/node": "22.7.7",
"eslint": "9.13.0",
"eslint-plugin-jsdoc": "50.4.3",
"fzf": "0.5.2",
"jiti": "2.3.3",
"typescript": "5.6.3",
"typescript-eslint": "8.10.0"
}
}

View file

@ -1,95 +0,0 @@
.applauncher {
all: unset;
border: 2px solid $contrast-bg;
background-color: $bg;
color: #f8f8f2;
padding: 2px;
* {
font-size: 16px;
}
list,
row {
all: unset;
}
.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: 10px;
padding-bottom: 0;
min-width: 900px;
min-height: 650px;
}
.placeholder {
margin-top: 9px;
color: #f8f8f2;
font-size: 1.2em;
}
.app {
all: unset;
transition: 200ms;
border-radius: 9px;
label {
transition: 200ms;
&.title {
margin-top: 20px;
color: #f8f8f2;
}
&.description {
color: rgba(238, 238, 238, 0.7);
}
}
image {
transition: 200ms;
margin: 0 8px;
}
&:active {
background-color: rgba($contrast-bg, 0.5);
box-shadow: inset 0 0 0 3px rgba(238, 238, 238, 0.03);
}
}
*:selected,
.app:hover,
.app:focus {
* {
font-weight: unset;
}
label.title {
color: $contrast-bg;
}
image {
-gtk-icon-shadow: 2px 2px $contrast-bg;
}
}
}

View file

@ -1,41 +0,0 @@
.bar {
.clock,
.notif-panel {
border: 2px solid $contrast-bg;
padding: 4.5px 7px;
background-color: $bgfull;
}
.sys-tray {
border: 2px solid $contrast-bg;
menubar {
background-color: $bgfull;
padding: 2.5px;
}
menuitem {
image {
color: #CBA6F7;
}
padding: 0 2px;
* {
font-size: 25px;
}
}
menuitem:first-child image {
padding-left: 4px;
}
}
.current-window {
border: 2px solid $contrast-bg;
font-size: 18px;
padding-right: 7px;
background-color: $bgfull;
}
}

View file

@ -1,62 +0,0 @@
.clipboard {
all: unset;
border: 2px solid $contrast-bg;
background-color: $bg;
color: #f8f8f2;
padding: 2px;
* {
font-size: 16px;
}
list,
row {
all: unset;
}
.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: 10px;
padding-bottom: 0;
min-width: 900px;
min-height: 750px;
}
.item {
all: unset;
transition: 200ms;
border-radius: 9px;
padding: 10px;
}
*:selected .item,
*:hover .item,
*:focus .item {
background-color: #363449;
}
.down-arrow {
transition: all 500ms;
opacity: 0.5;
}
}

View file

@ -1,74 +0,0 @@
.date {
background-color: $bg;
color: $fg;
border: 2px solid $contrast-bg;
}
.timebox {
margin: 30px 0;
.time-container {
.content {
font-weight: bolder;
font-size: 60px;
}
.divider {
margin: 8px 15px;
padding: 0 1px;
background: linear-gradient($red, $magenta, $blue, $cyan);
}
}
.date-container {
margin-top: 2px;
}
}
.cal-box {
border-radius: 30px;
padding: 0 1rem .2rem;
color: $fg;
background-color: $bgfull;
border-bottom: 2px solid $contrast-bg;
border-top: 2px solid $contrast-bg;
margin: 0 12px 18px;
.cal {
font-size: 20px;
background-color: inherit;
padding: .5rem .10rem 0;
margin-left: 10px;
border-radius: 30px;
&>* {
border: solid 0 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;
}

View file

@ -1,109 +0,0 @@
.notification-center {
border: 2px solid $contrast-bg;
min-height: 700px;
min-width: 524px;
background: $bg;
padding: 0;
* {
font-size: 16px;
}
.header {
padding: 10px;
margin-top: 22px;
margin-bottom: 9px;
label {
font-size: 22px;
}
.clear {
box {
all: unset;
transition: 200ms;
color: #eee;
background-color: #664C90;
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
padding: 4.5px 9px;
}
&:hover box {
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 {
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;
border-top: 2px solid $contrast-bg;
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);
}
}
}

View file

@ -1,207 +0,0 @@
$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;
background-color: $bg;
padding: 16.2px;
border: 2px solid $contrast-bg;
* {
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 {
margin-right: 9px;
}
.icon.img {
border: 1px solid rgba(238, 238, 238, 0.03);
}
.actions {
button {
all: unset;
transition: all 500ms;
background-color: $background-color-3;
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
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;
background-color: $background-color-5;
background-image: none;
box-shadow: none;
margin-left: 9px;
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-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);
}
}
}

View file

@ -1,40 +0,0 @@
.osd {
padding: 12px 20px;
background: rgba(40, 42, 54, 0.8);
border: 2px solid $contrast-bg;
label {
min-width: 170px;
}
progressbar:disabled {
opacity: 0.5;
}
progressbar {
min-height: 6px;
min-width: 170px;
border-radius: 999px;
background: transparent;
border: none;
trough {
background: #363847;
min-height: inherit;
border: none;
}
progress {
background: #79659f;
min-height: inherit;
border: none;
}
}
image {
font-size: 2rem;
color: white;
margin-left: -0.4rem;
margin-right: 0.8rem;
}
}

View file

@ -1,48 +0,0 @@
.powermenu {
background-color: $bg;
color: $fg;
padding: 10px;
font-family: "MesloLGS NF";
font-size: 70px;
border: 2px solid $contrast-bg;
label {
min-width: 140px;
min-height: 130px;
}
.button {
margin: 5px 10px;
min-width: 80px;
transition: all ease .2s;
&:hover {
background-color: $bg-secondary;
}
&:active {
background-color: $bg-secondary;
}
.content {
padding: 0 15px;
}
}
.shutdown {
color: $red;
}
.reboot {
color: $magenta;
}
.logout {
color: $yellow;
}
}
.powermenu-clickhandler {
background-color: black;
}

View file

@ -1,66 +0,0 @@
.screenshot {
background-color: $bg;
color: $fg;
border: 2px solid $contrast-bg;
label {
font-size: 20pt;
}
.header {
border-bottom: 2px solid $contrast-bg;
.header-btn {
transition: 200ms;
&:hover {
background-color: rgba(23, 23, 23, 0.7);
image {
-gtk-icon-shadow: 1px 1px $contrast-bg;
}
}
label {
margin: 10px;
font-weight: bold;
}
image {
font-size: 30px;
}
}
}
scrolledwindow {
min-height: 400px;
min-width: 800px;
padding: 0 10px;
box {
.item-btn {
transition: 200ms;
&:hover {
background-color: rgba(23, 23, 23, 0.7);
}
}
label {
padding: 5px;
}
image {
font-size: 30px;
}
&:first-child label {
padding-top: 10px;
}
&:last-child label {
padding-bottom: 10px;
}
}
}
}

View file

@ -1,20 +0,0 @@
window,
button,
eventbox,
box,
progressbar,
trough,
undershoot {
all: unset;
}
@import './common';
@import './binto-widgets/applauncher';
@import './binto-widgets/bar';
@import './binto-widgets/clipboard';
@import './binto-widgets/date';
@import './binto-widgets/notification';
@import './binto-widgets/notification-center';
@import './binto-widgets/osd';
@import './binto-widgets/powermenu';
@import './binto-widgets/screenshot';

View file

@ -1,65 +0,0 @@
$darkbg: #0b0d16;
$bg: rgba(40, 42, 54, 0.8); // rgba(69, 71, 90, 0.3); #0d0f18;
$bgfull: rgb(40, 42, 54);
$contrast-bg: rgba(189, 147, 249, 0.8);
$contrast-bg-full: rgba(189, 147, 249, 1);
$bg-secondary: rgba(#382c4a, 0.8);
$bg-secondary-alt: #a5b6cf;
$fg: #a5b6cf;
$fg-dim: #a5b6cf;
$watermelon: #dd6777;
// Aliases
$background: $bg;
$background-secondary: $bg-secondary;
$background-secondary-alt: $bg-secondary-alt;
$foreground: $fg;
$foreground-dim: $fg-dim;
$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;
$javacafe-magenta: #c296eb;
$javacafe-blue: #86aaec;
scrolledwindow {
scrollbar,
scrollbar * {
all: unset;
}
scrollbar.vertical {
transition: 200ms;
background-color: rgba(23, 23, 23, 0.3);
margin: 20px 0;
&: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;
}
}
}

View file

@ -1,10 +0,0 @@
@import './common';
window {
background-color: transparent;
}
.clock {
font-size: 80pt;
font-family: 'Ubuntu Mono';
}

View file

@ -1,95 +0,0 @@
.applauncher {
all: unset;
border: 2px solid $contrast-bg;
border-radius: 25px;
background-color: $bg;
color: #f8f8f2;
padding: 2px;
* {
font-size: 16px;
}
list,
row {
all: unset;
}
.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: 10px;
padding-bottom: 0;
min-width: 700px;
min-height: 450px;
}
.placeholder {
margin-top: 9px;
color: #f8f8f2;
font-size: 1.2em;
}
.app {
all: unset;
transition: 200ms;
border-radius: 9px;
label {
transition: 200ms;
&.title {
color: #f8f8f2;
}
&.description {
color: rgba(238, 238, 238, 0.7);
}
}
image {
transition: 200ms;
margin: 0 8px;
}
&:active {
background-color: rgba($contrast-bg, 0.5);
box-shadow: inset 0 0 0 3px rgba(238, 238, 238, 0.03);
}
}
*:selected,
.app:hover,
.app:focus {
* {
font-weight: unset;
}
label.title {
color: $contrast-bg;
}
image {
-gtk-icon-shadow: 2px 2px $contrast-bg;
}
}
}

View file

@ -1,63 +0,0 @@
.clipboard {
all: unset;
border: 2px solid $contrast-bg;
border-radius: 25px;
background-color: $bg;
color: #f8f8f2;
padding: 2px;
* {
font-size: 16px;
}
list,
row {
all: unset;
}
.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: 10px;
padding-bottom: 0;
min-width: 900px;
min-height: 750px;
}
.item {
all: unset;
transition: 200ms;
border-radius: 9px;
padding: 10px;
}
*:selected .item,
*:hover .item,
*:focus .item {
background-color: #363449;
}
.down-arrow {
transition: all 500ms;
opacity: 0.5;
}
}

View file

@ -1,75 +0,0 @@
.date {
background-color: $bg;
color: $fg;
border-radius: 30px;
border: 2px solid $contrast-bg;
}
.timebox {
margin: 30px 0;
.time-container {
.content {
font-weight: bolder;
font-size: 60px;
}
.divider {
margin: 8px 15px;
padding: 0 1px;
background: linear-gradient($red, $magenta, $blue, $cyan);
}
}
.date-container {
margin-top: 2px;
}
}
.cal-box {
border-radius: 30px;
padding: 0 1rem .2rem;
color: $fg;
background-color: $bgfull;
border-bottom: 2px solid $contrast-bg;
border-top: 2px solid $contrast-bg;
margin: 0 12px 18px;
.cal {
font-size: 20px;
background-color: inherit;
padding: .5rem .10rem 0;
margin-left: 10px;
border-radius: 30px;
&>* {
border: solid 0 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;
}

View file

@ -1,113 +0,0 @@
.notification-center {
min-height: 700px;
min-width: 524px;
background: $bg;
border-radius: 30px;
border-top-right-radius: 0;
border: 2px solid $contrast-bg;
padding: 0;
* {
font-size: 16px;
}
.header {
padding: 10px;
margin-top: 22px;
margin-bottom: 9px;
label {
font-size: 22px;
}
.clear {
box {
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 {
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 {
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;
border-radius: 30px;
border-top: 2px solid $contrast-bg;
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);
}
}
}

View file

@ -1,210 +0,0 @@
$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;
margin: 9px 9px 0;
border: 2px solid $contrast-bg;
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;
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;
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-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);
}
}
}

View file

@ -1,43 +0,0 @@
.osd {
padding: 12px 20px;
border-radius: 999px;
background: rgba(40, 42, 54, 0.8);
border: 2px solid $contrast-bg;
label {
min-width: 170px;
}
progressbar:disabled {
opacity: 0.5;
}
progressbar {
min-height: 6px;
min-width: 170px;
border-radius: 999px;
background: transparent;
border: none;
trough {
background: #363847;
min-height: inherit;
border-radius: inherit;
border: none;
}
progress {
background: #79659f;
min-height: inherit;
border-radius: inherit;
border: none;
}
}
image {
font-size: 2rem;
color: white;
margin-left: -0.4rem;
margin-right: 0.8rem;
}
}

View file

@ -1,52 +0,0 @@
.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 $contrast-bg;
label {
min-width: 140px;
min-height: 130px;
}
.button {
margin: 5px 10px;
border-radius: 12px;
min-width: 80px;
transition: all ease .2s;
&:hover {
background-color: $bg-secondary;
}
&:active {
background-color: $bg-secondary;
}
.content {
border-radius: 4px;
padding: 0 15px;
}
}
.shutdown {
color: $red;
}
.reboot {
color: $magenta;
}
.logout {
color: $yellow;
}
}
.powermenu-clickhandler {
background-color: black;
}

View file

@ -1,76 +0,0 @@
.screenshot {
background-color: $bg;
color: $fg;
border: 2px solid $contrast-bg;
border-radius: 25px;
label {
font-size: 20pt;
}
.header {
border-bottom: 2px solid $contrast-bg;
.header-btn {
transition: 200ms;
&:hover,
&.active {
background-color: rgba(23, 23, 23, 0.7);
image {
-gtk-icon-shadow: 1px 1px $contrast-bg;
}
}
label {
margin: 10px;
font-weight: bold;
}
image {
font-size: 30px;
}
&:first-child {
border-top-left-radius: 25px;
}
&:last-child {
border-top-right-radius: 25px;
}
}
}
scrolledwindow {
min-height: 400px;
min-width: 800px;
padding: 0 10px;
box {
.item-btn {
transition: 200ms;
&:hover {
background-color: rgba(23, 23, 23, 0.7);
}
}
label {
padding: 5px;
}
image {
font-size: 30px;
}
&:first-child label {
padding-top: 10px;
}
&:last-child label {
padding-bottom: 10px;
}
}
}
}

View file

@ -1,33 +0,0 @@
.sys-tray {
padding: 5px;
background-color: $bg;
border-radius: 80px;
border: 2px solid $bg-secondary;
transition: background-color 0.5s ease-in-out,
border 0.5s ease-in-out;
menuitem {
image {
color: #CBA6F7;
}
background-color: transparent;
padding: 0 2px;
border-radius: 100%;
transition: all 0.5s ease-in-out;
&:hover {
border-radius: 100%;
transition: all 0.5s ease-in-out;
}
* {
font-size: 25px;
border-radius: 10px;
}
}
menubar {
background-color: transparent;
}
}

View file

@ -1,123 +0,0 @@
.bar {
margin: 5px;
}
.current-window {
color: #CBA6F7;
font-size: 18px;
}
.osk-toggle,
.tablet-toggle,
.heart-toggle {
font-size: 28px;
min-height: 40px;
min-width: 53px;
}
.heart-toggle {
font-size: 28px;
min-height: 40px;
color: #CBA6F7;
}
.notif-panel {
font-size: 20px;
min-height: 37px;
min-width: 105px;
}
.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 $bg-secondary;
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 $contrast-bg;
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: 0 15px;
}
.audio,
.bluetooth,
.brightness,
.keyboard {
padding: 0 10px;
font-size: 20px;
margin-right: -10px;
}
.network {
padding: 0 10px;
font-size: 20px;
}
.bg-text {
color: $bg;
font-weight: bold;
}
.battery {
padding: 0 10px;
font-size: 20px;
.battery-indicator {
&.charging {
color: green;
}
&.charged {
// TODO: charged battery style
}
&.low {
color: red;
}
}
icon {
.charging {
// TODO: charging battery style
}
.discharging {
// TODO: discharging battery style
}
}
label {
font-size: 20px;
}
}
tooltip {
background: rgba(0, 0, 0, 0.6);
border-radius: 5px;
}

View file

@ -1,34 +0,0 @@
.bar {
.workspaces {
background-color: $bg;
border-radius: 80px;
border: 2px solid $bg-secondary;
padding: 3px 12px;
}
.button {
margin: 0 2.5px;
min-height: 22px;
min-width: 22px;
border-radius: 100%;
border: 2px solid transparent;
}
.occupied {
border: 2px solid $bg;
background: $contrast-bg;
transition: background-color 0.6s ease-in-out;
}
.urgent {
border: 2px solid $bg;
background: red;
transition: background-color 0.6s ease-in-out;
}
.active {
border: 2px solid #50fa7b;
transition: margin-left 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
}
}

View file

@ -1,25 +0,0 @@
window,
button,
eventbox,
box,
progressbar,
trough,
undershoot {
all: unset;
}
@import './common';
@import './wim-widgets/powermenu';
@import './wim-widgets/traybuttons';
@import './wim-widgets/workspaces';
@import './wim-widgets/systray';
@import './wim-widgets/notification-center';
@import './wim-widgets/notification';
@import './wim-widgets/date';
@import './wim-widgets/quick-settings';
@import './wim-widgets/player';
@import './wim-widgets/applauncher';
@import './wim-widgets/osd';
@import './wim-widgets/osk';
@import './wim-widgets/clipboard';
@import './wim-widgets/screenshot';

View file

@ -1,151 +0,0 @@
const { exec, execAsync } = Utils;
const KBD = 'tpacpi::kbd_backlight';
const INTERVAL = 500;
const SCREEN_ICONS = {
90: 'display-brightness-high-symbolic',
70: 'display-brightness-medium-symbolic',
20: 'display-brightness-low-symbolic',
5: 'display-brightness-off-symbolic',
};
class Brightness extends Service {
static {
Service.register(this, {
screen: ['float'],
kbd: ['float'],
caps: ['int'],
}, {
'screen-icon': ['string', 'rw'],
'caps-icon': ['string', 'rw'],
});
}
#kbd = 0;
#kbdMax = 0;
#screen = 0;
#screenIcon = 'display-brightness-symbolic';
#capsName = 'input0::capslock';
#caps = 0;
#capsIcon = 'caps-lock-symbolic';
get capsName() {
return this.#capsName;
}
set capsName(value: string) {
this.#capsName = value;
}
get kbd() {
return this.#kbd;
}
set kbd(value) {
if (value < 0 || value > this.#kbdMax) {
return;
}
execAsync(`brightnessctl -d ${KBD} s ${value} -q`)
.then(() => {
this.#kbd = value;
this.emit('kbd', this.#kbd);
})
.catch(console.error);
}
get screen() {
return this.#screen;
}
set screen(percent) {
if (percent < 0) {
percent = 0;
}
if (percent > 1) {
percent = 1;
}
execAsync(`brightnessctl s ${percent * 100}% -q`)
.then(() => {
this.#screen = percent;
this.#getScreenIcon();
this.emit('screen', this.#screen);
})
.catch(console.error);
}
get screenIcon() {
return this.#screenIcon;
}
get caps() {
return this.#caps;
}
get capsIcon() {
return this.#capsIcon;
}
constructor() {
super();
try {
this.#monitorKbdState();
this.#kbdMax = Number(exec(`brightnessctl -d ${KBD} m`));
this.#caps = Number(exec(`bash -c brightnessctl -d ${this.#capsName} g`));
this.#screen = Number(exec('brightnessctl g')) / Number(exec('brightnessctl m'));
}
catch (_e) {
console.error('missing dependancy: brightnessctl');
}
}
#getScreenIcon() {
const brightness = this.#screen * 100;
// eslint-disable-next-line
for (const threshold of [4, 19, 69, 89]) {
if (brightness > threshold + 1) {
this.#screenIcon = SCREEN_ICONS[threshold + 1];
this.notify('screen-icon');
}
}
}
fetchCapsState() {
execAsync(`brightnessctl -d ${this.#capsName} g`)
.then((out) => {
this.#caps = Number(out);
this.#capsIcon = this.#caps ?
'caps-lock-symbolic' :
'capslock-disabled-symbolic';
this.notify('caps-icon');
this.emit('caps', this.#caps);
})
.catch(logError);
}
#monitorKbdState() {
const interval = setInterval(() => {
execAsync(`brightnessctl -d ${KBD} g`).then(
(out) => {
if (parseInt(out) !== this.#kbd) {
this.#kbd = parseInt(out);
this.emit('kbd', this.#kbd);
return this.#kbd;
}
},
).catch(() => {
// @ts-expect-error this works in ags
interval.destroy();
});
}, INTERVAL);
}
}
const brightnessService = new Brightness();
export default brightnessService;

View file

@ -1,147 +0,0 @@
const { execAsync, subprocess } = Utils;
class Clipboard extends Service {
static {
Service.register(this, {
'clip-added': ['jsobject'],
'history-searched': [],
}, {
clips: ['jsobject'],
});
}
private static _MAX_CLIPS = 100;
// Class Attributes
private _clips_left = 0;
private _clips = new Map<number, { clip: string, isImage: boolean }>();
get clips() {
return this._clips;
}
constructor() {
super();
this._getHistory();
this._watchClipboard();
}
// Public Class Methods
public copyOldItem(key: string | number): void {
execAsync([
'bash', '-c', `cliphist list | grep '^${key}' | cliphist decode | wl-copy`,
]);
}
// Private Class Methods
private _decrementClipsLeft() {
if (--this._clips_left === 0) {
this.emit('history-searched');
// this is necessary when not putting a cap on clip amount
// exec(`prlimit --pid ${exec('pgrep ags')} --nofile=1024:`);
}
}
private async _decodeItem(index: string): Promise<string | null> {
try {
const decodedItem = await execAsync([
'bash', '-c', `cliphist list | grep -a ${index} | cliphist decode`,
]);
this._decrementClipsLeft();
return decodedItem;
}
catch (error) {
console.error(error);
this._decrementClipsLeft();
return null;
}
}
private _addClip(newClip: [number, { clip: string, isImage: boolean }]) {
if (
![...this.clips.values()]
.map((c) => c.clip)
.includes(newClip[1].clip)
) {
this._clips.set(...newClip);
this.emit('clip-added', newClip);
}
else {
const oldClip = [...this.clips.entries()]
.find(([_, { clip }]) => clip === newClip[1].clip);
if (oldClip && oldClip[0] < newClip[0]) {
this.clips.delete(oldClip[0]);
this._clips.set(...newClip);
this.emit('clip-added', newClip);
}
}
}
private _getHistory(n = Clipboard._MAX_CLIPS) {
// this is necessary when not putting a cap on clip amount
// exec(`prlimit --pid ${exec('pgrep ags')} --nofile=10024:`);
// This command comes from '../../clipboard/script.sh'
execAsync('clipboard-manager').then((out) => {
const rawClips = out.split('\n');
this._clips_left = Math.min(rawClips.length - 1, n);
rawClips.forEach(async(clip, i) => {
if (i > n) {
return;
}
if (clip.includes('img')) {
this._decrementClipsLeft();
const newClip: [number, { clip: string, isImage: boolean }] = [
parseInt((clip.match('[0-9]+') ?? [''])[0]),
{
clip,
isImage: true,
},
];
this._addClip(newClip);
}
else {
const decodedClip = await this._decodeItem(clip);
if (decodedClip) {
const newClip: [number, { clip: string, isImage: boolean }] = [
parseInt(clip),
{
clip: decodedClip,
isImage: false,
},
];
this._addClip(newClip);
}
}
});
}).catch((error) => console.error(error));
}
private _watchClipboard() {
subprocess(
['wl-paste', '--watch', 'echo'],
() => {
this._getHistory(1);
},
() => { /**/ },
);
}
}
const clipboard = new Clipboard();
export default clipboard;

View file

@ -1,80 +0,0 @@
const { execAsync, subprocess } = Utils;
const Notifications = await Service.import('notifications');
const APP_NAME = 'gpu-screen-recorder';
const START_APP_ID = 2345;
const ICON_NAME = 'nvidia';
class GSR extends Service {
static {
Service.register(this, {}, {});
}
#appID = START_APP_ID;
constructor() {
super();
subprocess(
['gsr-start'],
(path) => {
Notifications.getNotification(this.#appID)?.close();
const notifId = Notifications.Notify(
APP_NAME,
++this.#appID,
ICON_NAME,
'Replay Saved',
`Saved to ${path}`,
['folder', 'Open Folder', 'video', 'Open Video', 'kdenlive', 'Edit in kdenlive'],
{},
Notifications.popupTimeout,
);
Notifications.getNotification(notifId)?.connect(
'invoked',
(_, actionId: string) => {
if (actionId === 'folder') {
execAsync([
'xdg-open',
path.substring(0, path.lastIndexOf('/')),
]).catch(print);
}
else if (actionId === 'video') {
execAsync(['xdg-open', path]).catch(print);
}
else if (actionId === 'kdenlive') {
execAsync([
'bash',
'-c',
`kdenlive -i ${path}`,
]).catch(print);
}
},
);
},
() => { /**/ },
);
}
saveReplay() {
execAsync(['gpu-save-replay']).then(() => {
Notifications.Notify(
APP_NAME,
this.#appID,
ICON_NAME,
'Saving Replay',
'Last 20 minutes',
[],
{},
Notifications.popupTimeout,
);
}).catch(console.error);
}
}
export default GSR;

View file

@ -1,228 +0,0 @@
const Hyprland = await Service.import('hyprland');
const { subprocess } = Utils;
const ON_RELEASE_TRIGGERS = [
'released',
'TOUCH_UP',
'HOLD_END',
];
const ON_CLICK_TRIGGERS = [
'pressed',
'TOUCH_DOWN',
];
// Types
import { PopupWindow } from 'global-types';
import { Subprocess } from 'types/@girs/gio-2.0/gio-2.0.cjs';
interface Layer {
address: string
x: number
y: number
w: number
h: number
namespace: string
}
interface Levels {
0?: Layer[] | null
1?: Layer[] | null
2?: Layer[] | null
3?: Layer[] | null
}
interface Layers {
levels: Levels
}
interface CursorPos {
x: number
y: number
}
class Pointers extends Service {
static {
Service.register(this, {
'proc-started': ['boolean'],
'proc-destroyed': ['boolean'],
'device-fetched': ['boolean'],
'new-line': ['string'],
'released': ['string'],
'clicked': ['string'],
});
}
#process = null as Subprocess | null;
#lastLine = '';
#pointers = [] as string[];
get process() {
return this.#process;
}
get lastLine() {
return this.#lastLine;
}
get pointers() {
return this.#pointers;
}
constructor() {
super();
this.#initAppConnection();
}
startProc() {
if (this.#process) {
return;
}
this.#process = subprocess(
['libinput', 'debug-events'],
(output) => {
if (output.includes('cancelled')) {
return;
}
if (ON_RELEASE_TRIGGERS.some((p) => output.includes(p))) {
this.#lastLine = output;
Pointers.detectClickedOutside('released');
this.emit('released', output);
this.emit('new-line', output);
}
if (ON_CLICK_TRIGGERS.some((p) => output.includes(p))) {
this.#lastLine = output;
Pointers.detectClickedOutside('clicked');
this.emit('clicked', output);
this.emit('new-line', output);
}
},
);
this.emit('proc-started', true);
}
killProc() {
if (this.#process) {
this.#process.force_exit();
this.#process = null;
this.emit('proc-destroyed', true);
}
}
#initAppConnection() {
App.connect('window-toggled', () => {
const anyVisibleAndClosable =
(App.windows as PopupWindow[]).some((w) => {
const closable = w.close_on_unfocus &&
!(
w.close_on_unfocus === 'none' ||
w.close_on_unfocus === 'stay'
);
return w.visible && closable;
});
if (anyVisibleAndClosable) {
this.startProc();
}
else {
this.killProc();
}
});
}
static detectClickedOutside(clickStage: string) {
const toClose = ((App.windows as PopupWindow[])).some((w) => {
const closable = (
w.close_on_unfocus &&
w.close_on_unfocus === clickStage
);
return w.visible && closable;
});
if (!toClose) {
return;
}
Hyprland.messageAsync('j/layers').then((response) => {
const layers = JSON.parse(response) as { Layers: Layers };
Hyprland.messageAsync('j/cursorpos').then((res) => {
const pos = JSON.parse(res) as CursorPos;
Object.values(layers).forEach((key) => {
const overlayLayer = key.levels['3'];
if (overlayLayer) {
const noCloseWidgetsNames = [
'bar-',
'osk',
];
const getNoCloseWidgets = (names: string[]) => {
const arr = [] as Layer[];
names.forEach((name) => {
arr.push(
overlayLayer.find(
(n) => n.namespace.startsWith(name),
) ||
// Return an empty Layer if widget doesn't exist
{
address: '',
x: 0,
y: 0,
w: 0,
h: 0,
namespace: '',
},
);
});
return arr;
};
const clickIsOnWidget = (w: Layer) => {
return pos.x > w.x && pos.x < w.x + w.w &&
pos.y > w.y && pos.y < w.y + w.h;
};
const noCloseWidgets =
getNoCloseWidgets(noCloseWidgetsNames);
const widgets = overlayLayer.filter((n) => {
let window = null as null | PopupWindow;
if (App.windows.some((win) =>
win.name === n.namespace)) {
window = (App
.getWindow(n.namespace) as PopupWindow);
}
return window &&
window.close_on_unfocus &&
window.close_on_unfocus ===
clickStage;
});
if (noCloseWidgets.some(clickIsOnWidget)) {
// Don't handle clicks when on certain widgets
}
else {
widgets.forEach(
(w) => {
if (!(pos.x > w.x && pos.x < w.x + w.w &&
pos.y > w.y && pos.y < w.y + w.h)) {
App.closeWindow(w.namespace);
}
},
);
}
}
});
}).catch(print);
}).catch(print);
}
}
export default new Pointers();

View file

@ -1,143 +0,0 @@
const { subprocess } = Utils;
const SCREEN = '/dev/input/by-path/platform-AMDI0010\:00-event';
const GESTURE_VERIF = [
'LR', // Left to Right
'RL', // Right to Left
'DU', // Down to Up
'UD', // Up to Down
'DLUR', // Down to Left to Up to Right (clockwise motion from Down)
'DRUL', // Down to Right to Up to Left (counter-clockwise from Down)
'URDL', // Up to Right to Down to Left (clockwise motion from Up)
'ULDR', // Up to Left to Down to Right (counter-clockwise from Up)
];
const EDGE_VERIF = [
'*', // Any
'N', // None
'L', // Left
'R', // Right
'T', // Top
'B', // Bottom
'TL', // Top left
'TR', // Top right
'BL', // Bottom left
'BR', // Bottom right
];
const DISTANCE_VERIF = [
'*', // Any
'S', // Short
'M', // Medium
'L', // Large
];
// Types
import { Subprocess } from 'types/@girs/gio-2.0/gio-2.0.cjs';
// TODO: add actmode param
// TODO: support multiple daemons for different thresholds
class TouchGestures extends Service {
static {
Service.register(this, {
'daemon-started': ['boolean'],
'daemon-destroyed': ['boolean'],
});
}
#gestures = new Map();
#gestureDaemon = null as Subprocess | null;
get gestures() {
return this.#gestures;
}
get gestureDaemon() {
return this.#gestureDaemon;
}
addGesture({
name,
nFingers = '1',
gesture,
edge = '*',
distance = '*',
command,
}) {
gesture = String(gesture).toUpperCase();
if (!GESTURE_VERIF.includes(gesture)) {
logError('Wrong gesture id');
return;
}
edge = String(edge).toUpperCase();
if (!EDGE_VERIF.includes(edge)) {
logError('Wrong edge id');
return;
}
distance = String(distance).toUpperCase();
if (!DISTANCE_VERIF.includes(distance)) {
logError('Wrong distance id');
return;
}
if (typeof command !== 'string') {
globalThis[name] = command;
command = `ags -r "${name}()"`;
}
this.#gestures.set(name, [
'-g',
`${nFingers},${gesture},${edge},${distance},${command}`,
]);
if (this.#gestureDaemon) {
this.killDaemon();
}
this.startDaemon();
}
startDaemon() {
if (this.#gestureDaemon) {
return;
}
let command = [
'lisgd', '-d', SCREEN,
// Orientation
'-o', '0',
// Threshold of gesture recognized
'-t', '125',
// Leniency of gesture angle
'-r', '25',
// Timeout time
'-m', '3200',
];
this.#gestures.forEach((gesture) => {
command = command.concat(gesture);
});
this.#gestureDaemon = subprocess(
command,
() => { /**/ },
);
this.emit('daemon-started', true);
}
killDaemon() {
if (this.#gestureDaemon) {
this.#gestureDaemon.force_exit();
this.#gestureDaemon = null;
this.emit('daemon-destroyed', true);
}
}
}
const touchGesturesService = new TouchGestures();
export default touchGesturesService;

View file

@ -1,63 +0,0 @@
const { Box, Icon, Label } = Widget;
const { lookUpIcon } = Utils;
import CursorBox from '../misc/cursorbox.ts';
/* Types */
import { Application } from 'types/service/applications.ts';
export default (app: Application) => {
const icon = Icon({ size: 42 });
const iconString = app.app.get_string('Icon');
if (app.icon_name) {
if (lookUpIcon(app.icon_name)) {
icon.icon = app.icon_name;
}
else if (iconString && iconString !== 'nix-snowflake') {
icon.icon = iconString;
}
else {
icon.icon = '';
}
}
const textBox = Box({
vertical: true,
vpack: 'start',
children: [
Label({
class_name: 'title',
label: app.name,
xalign: 0,
truncate: 'end',
}),
Label({
class_name: 'description',
label: app.description || '',
wrap: true,
xalign: 0,
justification: 'left',
}),
Label(),
],
});
return CursorBox({
hexpand: true,
class_name: 'app',
attribute: { app },
child: Box({
children: [
icon,
textBox,
],
}),
});
};

View file

@ -1,26 +0,0 @@
/* Types */
import { Application } from 'types/service/applications.ts';
const bash = async(strings: TemplateStringsArray | string, ...values: unknown[]) => {
const cmd = typeof strings === 'string' ?
strings :
strings.flatMap((str, i) => `${str}${values[i] ?? ''}`)
.join('');
return Utils.execAsync(['bash', '-c', cmd]).catch((err) => {
console.error(cmd, err);
return '';
});
};
export const launchApp = (app: Application) => {
const exe = app.executable
.split(/\s+/)
.filter((str) => !str.startsWith('%') && !str.startsWith('@'))
.join(' ');
bash(`${exe} &`);
app.frequency += 1;
};

View file

@ -1,76 +0,0 @@
const Applications = await Service.import('applications');
import { Fzf, FzfResultItem } from 'fzf';
import SortedList from '../misc/sorted-list.ts';
import AppItem from './app-item.ts';
import { launchApp } from './launch.ts';
/* Types */
import { Application } from 'types/service/applications.ts';
import { AgsAppItem } from 'global-types';
export default () => {
let fzfResults = [] as FzfResultItem<Application>[];
return SortedList({
name: 'applauncher',
class_name: 'applauncher',
transition: 'slide top',
on_select: (r) => {
App.closeWindow('win-applauncher');
launchApp((r.get_child() as AgsAppItem).attribute.app);
},
setup_list: (list, entry) => {
Applications.query('')
.flatMap((app) => AppItem(app))
.forEach((ch) => {
list.add(ch);
});
list.show_all();
list.set_sort_func((a, b) => {
const row1 = (a.get_children()[0] as AgsAppItem).attribute.app;
const row2 = (b.get_children()[0] as AgsAppItem).attribute.app;
if (entry.text === '' || entry.text === '-') {
a.set_visible(true);
b.set_visible(true);
return row2.frequency - row1.frequency;
}
else {
const s1 = fzfResults.find((r) => r.item.name === row1.name)?.score ?? 0;
const s2 = fzfResults.find((r) => r.item.name === row2.name)?.score ?? 0;
a.set_visible(s1 !== 0);
b.set_visible(s2 !== 0);
return s2 - s1;
}
});
},
on_text_change: (text, list, placeholder) => {
const fzf = new Fzf(Applications.list, {
selector: (app) => app.name + app.executable,
tiebreakers: [
(a, b) => b.item.frequency - a.item.frequency,
],
});
fzfResults = fzf.find(text);
list.invalidate_sort();
const visibleApps = list.get_children().filter((row) => row.visible).length;
placeholder.reveal_child = visibleApps <= 0;
},
});
};

View file

@ -1,57 +0,0 @@
const { Box, CenterBox } = Widget;
import Separator from '../misc/separator.ts';
import { get_gdkmonitor_from_desc } from '../lib.ts';
import BarRevealer from './fullscreen.ts';
import Clock from './items/cal-opener.ts';
import CurrentWindow from './items/current-window';
import NotifButton from './items/notif-button.ts';
import SysTray from './items/systray.ts';
const PADDING = 20;
export default () => BarRevealer({
gdkmonitor: get_gdkmonitor_from_desc('desc:Acer Technologies Acer K212HQL T3EAA0014201'),
exclusivity: 'exclusive',
anchor: ['bottom', 'left', 'right'],
bar: Box({
vertical: true,
children: [
CenterBox({
class_name: 'bar',
hexpand: true,
start_widget: Box({
hpack: 'start',
children: [
Separator(PADDING),
SysTray(),
],
}),
center_widget: Box({
children: [
CurrentWindow(),
Separator(PADDING / 2),
],
}),
end_widget: Box({
hpack: 'end',
children: [
NotifButton(),
Separator(PADDING / 2),
Clock(),
Separator(PADDING),
],
}),
}),
Separator(PADDING, { vertical: true }),
],
}),
});

View file

@ -1,156 +0,0 @@
const Hyprland = await Service.import('hyprland');
const { Box, EventBox, Revealer, Window } = Widget;
import Gdk from 'gi://Gdk?version=3.0';
import {
get_hyprland_monitor_desc,
get_monitor_desc_from_id,
} from '../lib.ts';
const FullscreenState = Variable({
monitors: [] as string[],
clientAddrs: new Map() as Map<string, string>,
});
Hyprland.connect('event', (hyprObj) => {
const arrayEquals = (a1: unknown[], a2: unknown[]) =>
a1.sort().toString() === a2.sort().toString();
const mapEquals = (m1: Map<string, string>, m2: Map<string, string>) =>
m1.size === m2.size &&
Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));
const fs = FullscreenState.value;
const fsClients = hyprObj.clients.filter((c) => {
const mon = Hyprland.getMonitor(c.monitor);
return c.fullscreen &&
c.workspace.id === mon?.activeWorkspace.id;
});
const monitors = fsClients.map((c) =>
get_monitor_desc_from_id(c.monitor));
const clientAddrs = new Map(fsClients.map((c) => [
get_monitor_desc_from_id(c.monitor),
c.address,
]));
const hasChanged =
!arrayEquals(monitors, fs.monitors) ||
!mapEquals(clientAddrs, fs.clientAddrs);
if (hasChanged) {
FullscreenState.setValue({
monitors,
clientAddrs,
});
}
});
export default ({
anchor,
bar,
gdkmonitor = Gdk.Display.get_default()?.get_monitor(0) as Gdk.Monitor,
...rest
}) => {
const monitor = get_hyprland_monitor_desc(gdkmonitor);
const BarVisible = Variable(true);
FullscreenState.connect('changed', (v) => {
BarVisible.setValue(!v.value.monitors.includes(monitor));
});
const barCloser = Window({
name: `bar-${monitor}-closer`,
visible: false,
gdkmonitor,
anchor: ['top', 'bottom', 'left', 'right'],
layer: 'overlay',
child: EventBox({
on_hover: () => {
barCloser.set_visible(false);
BarVisible.setValue(false);
},
child: Box({
css: 'padding: 1px;',
}),
}),
});
// Hide bar instantly when out of focus
Hyprland.active.workspace.connect('changed', () => {
const addr = FullscreenState.value.clientAddrs.get(monitor);
if (addr) {
const client = Hyprland.getClient(addr);
if (client!.workspace.id !== Hyprland.active.workspace.id) {
BarVisible.setValue(false);
barCloser.visible = false;
}
}
});
const buffer = Box({
css: 'min-height: 10px',
visible: BarVisible.bind().as((v) => !v),
});
const vertical = anchor.includes('left') && anchor.includes('right');
const isBottomOrLeft = (
anchor.includes('left') && anchor.includes('right') && anchor.includes('bottom')
) || (
anchor.includes('top') && anchor.includes('bottom') && anchor.includes('left')
);
let transition: 'slide_up' | 'slide_down' | 'slide_left' | 'slide_right';
if (vertical) {
transition = isBottomOrLeft ? 'slide_up' : 'slide_down';
}
else {
transition = isBottomOrLeft ? 'slide_right' : 'slide_left';
}
const barWrap = Revealer({
reveal_child: BarVisible.bind(),
transition,
child: bar,
});
return Window({
name: `bar-${monitor}`,
layer: 'overlay',
gdkmonitor,
margins: [-1, -1, -1, -1],
anchor,
...rest,
attribute: {
barCloser,
},
child: EventBox({
child: Box({
css: 'min-height: 1px; padding: 1px;',
hexpand: true,
hpack: 'fill',
vertical,
children: isBottomOrLeft ?
[buffer, barWrap] :
[barWrap, buffer],
}),
}).on('enter-notify-event', () => {
if (!BarVisible.value) {
barCloser.visible = true;
BarVisible.setValue(true);
}
}),
});
};

View file

@ -1,21 +0,0 @@
const Audio = await Service.import('audio');
const { Label, Icon } = Widget;
import { SpeakerIcon } from '../../misc/audio-icons.ts';
import HoverRevealer from './hover-revealer.ts';
export default () => HoverRevealer({
class_name: 'audio',
icon: Icon({
icon: SpeakerIcon.bind(),
}),
label: Label().hook(Audio, (self) => {
if (Audio.speaker.volume) {
self.label =
`${Math.round(Audio.speaker.volume * 100)}%`;
}
}, 'speaker-changed'),
});

View file

@ -1,26 +0,0 @@
const Bluetooth = await Service.import('bluetooth');
const { Label, Icon } = Widget;
import HoverRevealer from './hover-revealer.ts';
export default () => HoverRevealer({
class_name: 'bluetooth',
icon: Icon().hook(Bluetooth, (self) => {
if (Bluetooth.enabled) {
self.icon = Bluetooth.connected_devices[0] ?
Bluetooth.connected_devices[0].icon_name :
'bluetooth-active-symbolic';
}
else {
self.icon = 'bluetooth-disabled-symbolic';
}
}),
label: Label().hook(Bluetooth, (self) => {
self.label = Bluetooth.connected_devices[0] ?
`${Bluetooth.connected_devices[0]}` :
'Disconnected';
}, 'notify::connected-devices'),
});

View file

@ -1,17 +0,0 @@
const { Icon, Label } = Widget;
import Brightness from '../../../services/brightness.ts';
import HoverRevealer from './hover-revealer.ts';
export default () => HoverRevealer({
class_name: 'brightness',
icon: Icon({
icon: Brightness.bind('screenIcon'),
}),
label: Label().hook(Brightness, (self) => {
self.label = `${Math.round(Brightness.screen * 100)}%`;
}, 'screen'),
});

View file

@ -1,48 +0,0 @@
const { Box, Revealer } = Widget;
import Gtk from 'gi://Gtk?version=3.0';
import Separator from '../../misc/separator.ts';
import CursorBox from '../../misc/cursorbox.ts';
export default ({
class_name,
icon,
label,
spacing = 5,
}) => {
const hoverRevLabel = Revealer({
transition: 'slide_right',
attribute: {
var: Variable(Box()),
},
child: Box({
children: [
Separator(spacing),
label,
],
}),
});
const widget = CursorBox({
on_hover: () => {
hoverRevLabel.reveal_child = true;
hoverRevLabel.attribute.var.value.set_state_flags(Gtk.StateFlags.PRELIGHT, false);
},
child: Box({
class_name,
children: [
icon,
hoverRevLabel,
],
}),
});
return widget;
};

View file

@ -1,37 +0,0 @@
const Network = await Service.import('network');
const { Label, Icon } = Widget;
import HoverRevealer from './hover-revealer.ts';
export default () => HoverRevealer({
class_name: 'network',
icon: Icon().hook(Network, (self) => {
if (Network.wifi.internet === 'connected' ||
Network.wifi.internet === 'connecting') {
self.icon = Network.wifi.icon_name;
}
else if (Network.wired.internet === 'connected' ||
Network.wired.internet === 'connecting') {
self.icon = Network.wired.icon_name;
}
else {
self.icon = Network.wifi.icon_name;
}
}),
label: Label().hook(Network, (self) => {
if (Network.wifi.internet === 'connected' ||
Network.wifi.internet === 'connecting') {
self.label = Network.wifi.ssid || 'Unknown';
}
else if (Network.wired.internet === 'connected' ||
Network.wired.internet === 'connecting') {
self.label = 'Connected';
}
else {
self.label = 'Disconnected';
}
}),
});

View file

@ -1,30 +0,0 @@
const Battery = await Service.import('battery');
const { Label, Icon, Box } = Widget;
import Separator from '../../misc/separator.ts';
const LOW_BATT = 20;
const SPACING = 5;
export default () => Box({
class_name: 'toggle-off battery',
children: [
Icon({
class_name: 'battery-indicator',
icon: Battery.bind('icon_name'),
}).hook(Battery, (self) => {
self.toggleClassName('charging', Battery.charging);
self.toggleClassName('charged', Battery.charged);
self.toggleClassName('low', Battery.percent < LOW_BATT);
}),
Separator(SPACING),
Label({
label: Battery.bind('percent')
.transform((v) => `${v}%`),
}),
],
});

View file

@ -1,19 +0,0 @@
import CursorBox from '../../misc/cursorbox.ts';
import Clock from './clock';
export default () => CursorBox({
class_name: 'toggle-off',
on_primary_click_release: () => App.toggleWindow('win-calendar'),
setup: (self) => {
self.hook(App, (_, windowName, visible) => {
if (windowName === 'win-calendar') {
self.toggleClassName('toggle-on', visible);
}
});
},
child: Clock(),
});

View file

@ -1,23 +0,0 @@
const { Label } = Widget;
const { new_now_local } = imports.gi.GLib.DateTime;
export default () => Label({ class_name: 'clock' })
.poll(1000, (self) => {
const time = new_now_local();
const dayName = time.format('%a. ');
const dayNum = time.get_day_of_month();
const date = time.format(' %b. ');
const hour = (new Date().toLocaleString([], {
hour: 'numeric',
minute: 'numeric',
hour12: true,
}) ?? '')
.replace('a.m.', 'AM')
.replace('p.m.', 'PM');
if (dayNum && dayName && date) {
self.label = dayName + dayNum + date + hour;
}
});

View file

@ -1,33 +0,0 @@
const Applications = await Service.import('applications');
const Hyprland = await Service.import('hyprland');
const { Box, Icon, Label } = Widget;
import Separator from '../../misc/separator.ts';
const SPACING = 8;
export default () => Box({
class_name: 'current-window',
children: [
Separator(SPACING / 2),
Icon({ size: 30 })
.hook(Hyprland.active.client, (self) => {
const app = Applications
.query(Hyprland.active.client.class)[0];
self.icon = app?.icon_name || '';
}),
Separator(SPACING),
Label({
truncate: 'end',
label: Hyprland.active.client.bind('title'),
}),
],
}).hook(Hyprland.active.client, (self) => {
self.visible = Hyprland.active.client.title !== '';
});

View file

@ -1,60 +0,0 @@
const Notifications = await Service.import('notifications');
const { Box, CenterBox, Icon, Label } = Widget;
import CursorBox from '../../misc/cursorbox.ts';
import Separator from '../../misc/separator.ts';
const SPACING = 4;
// Types
import { PopupWindow } from 'global-types';
export default () => CursorBox({
class_name: 'toggle-off',
on_primary_click_release: (self) => {
(App.getWindow('win-notification-center') as PopupWindow)
.set_x_pos(
self.get_allocation(),
'right',
);
App.toggleWindow('win-notification-center');
},
setup: (self) => {
self.hook(App, (_, windowName, visible) => {
if (windowName === 'win-notification-center') {
self.toggleClassName('toggle-on', visible);
}
});
},
child: CenterBox({
class_name: 'notif-panel',
center_widget: Box({
children: [
Icon().hook(Notifications, (self) => {
if (Notifications.dnd) {
self.icon = 'notification-disabled-symbolic';
}
else if (Notifications.notifications.length > 0) {
self.icon = 'notification-new-symbolic';
}
else {
self.icon = 'notification-symbolic';
}
}),
Separator(SPACING),
Label({
label: Notifications.bind('notifications')
.transform((n) => String(n.length)),
}),
],
}),
}),
});

View file

@ -1,23 +0,0 @@
const { Label } = Widget;
import Tablet from '../../../services/tablet.ts';
import CursorBox from '../../misc/cursorbox.ts';
export default () => CursorBox({
class_name: 'toggle-off',
on_primary_click_release: () => Tablet.toggleOsk(),
setup: (self) => {
self.hook(Tablet, () => {
self.toggleClassName('toggle-on', Tablet.oskState);
}, 'osk-toggled');
},
child: Label({
class_name: 'osk-toggle',
xalign: 0.6,
label: '󰌌 ',
}),
});

View file

@ -1,85 +0,0 @@
const { Box, Icon } = Widget;
import Audio from '../hovers/audio.ts';
import Bluetooth from '../hovers/bluetooth.ts';
import Brightness from '../hovers/brightness.ts';
import KeyboardLayout from '../hovers/keyboard-layout.ts';
import Network from '../hovers/network.ts';
import CursorBox from '../../misc/cursorbox.ts';
import Separator from '../../misc/separator.ts';
const SPACING = 4;
// Types
import { PopupWindow } from 'global-types';
export default () => {
const hoverRevealers = [
KeyboardLayout(),
Brightness(),
Audio(),
Bluetooth(),
Network(),
];
const widget = CursorBox({
class_name: 'toggle-off',
on_primary_click_release: (self) => {
(App.getWindow('win-quick-settings') as PopupWindow)
.set_x_pos(
self.get_allocation(),
'right',
);
App.toggleWindow('win-quick-settings');
},
setup: (self) => {
self.hook(App, (_, windowName, visible) => {
if (windowName === 'win-quick-settings') {
self.toggleClassName('toggle-on', visible);
}
});
},
attribute: {
hoverRevealers: hoverRevealers.map((rev) => {
const box = rev.child;
return box.children[1];
}),
},
on_hover_lost: (self) => {
self.attribute.hoverRevealers.forEach((rev) => {
rev.reveal_child = false;
});
},
child: Box({
class_name: 'quick-settings-toggle',
vertical: false,
children: [
Separator(SPACING),
...hoverRevealers,
Icon('nixos-logo-symbolic'),
Separator(SPACING),
],
}),
});
widget.attribute.hoverRevealers.forEach((hv) => {
hv.attribute.var.setValue(widget);
});
return widget;
};

View file

@ -1,100 +0,0 @@
import AstalTray from 'gi://AstalTray?version=0.1';
const SystemTray = AstalTray.Tray.get_default();
const { timeout } = Utils;
const { Box, Icon, MenuItem, MenuBar, Revealer } = Widget;
import Separator from '../../misc/separator.ts';
const REVEAL_DURATION = 500;
const SPACING = 12;
const SysTrayItem = (item: AstalTray.TrayItem) => {
if (item.id === 'spotify-client') {
return;
}
return MenuItem({
submenu: item.create_menu(),
child: Revealer({
transition: 'slide_right',
transition_duration: REVEAL_DURATION,
child: Icon({
size: 24,
icon: Utils.watch(
item.iconPixbuf || item.iconName || 'image-missing',
item,
() => item.iconPixbuf || item.iconName || 'image-missing',
),
}),
}),
}).bind('tooltip_markup', item, 'tooltipMarkup');
};
const SysTray = () => MenuBar({
attribute: { items: new Map() },
setup: (self) => {
self
.hook(SystemTray, (_, id) => {
if (!id) {
return;
}
const item = SystemTray.get_item(id);
if (self.attribute.items.has(id) || !item) {
return;
}
const w = SysTrayItem(item);
// Early return if item is in blocklist
if (!w) {
return;
}
self.attribute.items.set(id, w);
self.add(w);
self.show_all();
w.child.reveal_child = true;
}, 'item_added')
.hook(SystemTray, (_, id) => {
if (!id || !self.attribute.items.has(id)) {
return;
}
self.attribute.items.get(id).child.reveal_child = false;
timeout(REVEAL_DURATION, () => {
self.attribute.items.get(id).destroy();
self.attribute.items.delete(id);
});
}, 'item_removed');
},
});
export default () => {
const systray = SysTray();
return Revealer({
transition: 'slide_right',
child: Box({
children: [
Box({
class_name: 'sys-tray',
children: [systray],
}),
Separator(SPACING),
],
}),
}).hook(SystemTray, (self) => {
self.reveal_child = systray.get_children().length > 0;
}, 'item_added');
};

View file

@ -1,24 +0,0 @@
const { Box, Label } = Widget;
import Tablet from '../../../services/tablet.ts';
import CursorBox from '../../misc/cursorbox.ts';
export default () => CursorBox({
class_name: 'toggle-off',
on_primary_click_release: () => Tablet.toggleMode(),
setup: (self) => {
self.hook(Tablet, () => {
self.toggleClassName('toggle-on', Tablet.tabletMode);
}, 'mode-toggled');
},
child: Box({
class_name: 'tablet-toggle',
vertical: false,
children: [Label(' 󰦧 ')],
}),
});

View file

@ -1,179 +0,0 @@
const Hyprland = await Service.import('hyprland');
const { timeout } = Utils;
const { Box, Overlay, Revealer } = Widget;
import CursorBox from '../../misc/cursorbox.ts';
const URGENT_DURATION = 1000;
// Types
import {
BoxGeneric,
EventBoxGeneric,
OverlayGeneric,
RevealerGeneric,
Workspace,
} from 'global-types';
const Workspace = ({ id }: { id: number }) => {
return Revealer({
transition: 'slide_right',
attribute: { id },
child: CursorBox({
tooltip_text: `${id}`,
on_primary_click_release: () => {
Hyprland.messageAsync(`dispatch workspace ${id}`);
},
child: Box({
vpack: 'center',
class_name: 'button',
setup: (self) => {
const update = (
_: BoxGeneric,
addr: string | undefined,
) => {
const workspace = Hyprland.getWorkspace(id);
const occupied = workspace && workspace.windows > 0;
self.toggleClassName('occupied', occupied);
if (!addr) {
return;
}
// Deal with urgent windows
const client = Hyprland.getClient(addr);
const isThisUrgent = client &&
client.workspace.id === id;
if (isThisUrgent) {
self.toggleClassName('urgent', true);
// Only show for a sec when urgent is current workspace
if (Hyprland.active.workspace.id === id) {
timeout(URGENT_DURATION, () => {
self.toggleClassName('urgent', false);
});
}
}
};
self
.hook(Hyprland, update)
// Deal with urgent windows
.hook(Hyprland, update, 'urgent-window')
.hook(Hyprland.active.workspace, () => {
if (Hyprland.active.workspace.id === id) {
self.toggleClassName('urgent', false);
}
});
},
}),
}),
});
};
export default () => {
const L_PADDING = 16;
const WS_WIDTH = 30;
const updateHighlight = (self: BoxGeneric) => {
const currentId = Hyprland.active.workspace.id;
const indicators = (((self.get_parent() as OverlayGeneric)
.child as EventBoxGeneric)
.child as BoxGeneric)
.children as Workspace[];
const currentIndex = indicators
.findIndex((w) => w.attribute.id === currentId);
if (currentIndex < 0) {
return;
}
self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`);
};
const highlight = Box({
vpack: 'center',
hpack: 'start',
class_name: 'button active',
}).hook(Hyprland.active.workspace, updateHighlight);
const widget = Overlay({
pass_through: true,
overlays: [highlight],
child: CursorBox({
child: Box({
class_name: 'workspaces',
attribute: { workspaces: [] as Workspace[] },
setup: (self) => {
const refresh = () => {
(self.children as RevealerGeneric[])
.forEach((rev) => {
rev.reveal_child = false;
});
self.attribute.workspaces
.forEach((ws) => {
ws.reveal_child = true;
});
};
const updateWorkspaces = () => {
Hyprland.workspaces.forEach((ws) => {
const currentWs =
(self.children as Workspace[])
.find((ch) => ch.attribute.id === ws.id);
if (!currentWs && ws.id > 0) {
self.add(Workspace({ id: ws.id }));
}
});
self.show_all();
// Make sure the order is correct
self.attribute.workspaces.forEach((workspace, i) => {
(workspace.get_parent() as BoxGeneric)
.reorder_child(workspace, i);
});
};
self.hook(Hyprland, () => {
self.attribute.workspaces =
(self.children as Workspace[])
.filter((ch) => {
return Hyprland.workspaces.find((ws) => {
return ws.id === ch.attribute.id;
});
})
.sort((a, b) =>
a.attribute.id - b.attribute.id);
updateWorkspaces();
refresh();
// Make sure the highlight doesn't go too far
const TEMP_TIMEOUT = 10;
timeout(TEMP_TIMEOUT, () => updateHighlight(highlight));
});
},
}),
}),
});
return widget;
};

View file

@ -1,80 +0,0 @@
const { CenterBox, Box } = Widget;
import Separator from '../misc/separator.ts';
import Battery from './items/battery.ts';
import Clock from './items/cal-opener.ts';
import CurrentWindow from './items/current-window.ts';
import Heart from './items/heart.ts';
import NotifButton from './items/notif-button.ts';
import OskToggle from './items/osk-toggle.ts';
import QsToggle from './items/quick-settings.ts';
import SysTray from './items/systray.ts';
import TabletToggle from './items/tablet-toggle.ts';
import Workspaces from './items/workspaces.ts';
import BarRevealer from './fullscreen.ts';
const SPACING = 12;
export default () => BarRevealer({
anchor: ['top', 'left', 'right'],
exclusivity: 'exclusive',
bar: CenterBox({
css: 'margin: 5px 5px 5px 5px',
class_name: 'bar',
start_widget: Box({
hpack: 'start',
children: [
OskToggle(),
Separator(SPACING),
TabletToggle(),
Separator(SPACING),
SysTray(),
Workspaces(),
Separator(SPACING),
CurrentWindow(),
],
}),
center_widget: Box({
children: [
Separator(SPACING),
Clock(),
Separator(SPACING),
],
}),
end_widget: Box({
hpack: 'end',
children: [
Heart(),
Separator(SPACING),
Battery(),
Separator(SPACING),
NotifButton(),
Separator(SPACING),
QsToggle(),
],
}),
}),
});

View file

@ -1,80 +0,0 @@
const { Box, Button, Icon, Label, Revealer } = Widget;
const ImageClip = (key: number, val: string) => Box({
class_name: 'item',
name: key.toString(),
child: Icon({
icon: val.replace('img:', ''),
size: 100 * 2,
}),
});
const TextClip = (key: number, val: string) => {
const lines = val.split('\n');
if (lines.length <= 5) {
return Box({
class_name: 'item',
name: key.toString(),
child: Label({
label: val,
truncate: 'end',
max_width_chars: 100,
}),
});
}
else {
const revText = Revealer({
hpack: 'start',
child: Label({
label: lines.slice(2, lines.length).join('\n'),
truncate: 'end',
max_width_chars: 100,
}),
});
return Box({
class_name: 'item',
name: key.toString(),
vertical: true,
children: [
Label({
label: lines.slice(0, 2).join('\n'),
truncate: 'end',
max_width_chars: 100,
hpack: 'start',
}),
revText,
Button({
child: Icon({
class_name: 'down-arrow',
icon: 'down-large-symbolic',
size: 24,
}),
on_primary_click_release: (self) => {
const state = !revText.reveal_child;
revText.reveal_child = state;
self.child.setCss(`
-gtk-icon-transform: rotate(${state ? '180' : '0'}deg);
`);
},
}),
],
});
}
};
export default ({
key = 0,
isImage = false,
val = '',
}) => isImage ? ImageClip(key, val) : TextClip(key, val);

View file

@ -1,85 +0,0 @@
import { Fzf, FzfResultItem } from 'fzf';
import Gtk from 'gi://Gtk?version=3.0';
import Clipboard from '../../services/clipboard.ts';
import SortedList from '../misc/sorted-list.ts';
import ClipWidget from './clip.ts';
const getKey = (r: Gtk.ListBoxRow): number => parseInt(r.get_child()?.name ?? '0');
export default () => {
let fzfResults = [] as FzfResultItem<[number, { clip: string, isImage: boolean }]>[];
return SortedList({
name: 'clipboard',
class_name: 'clipboard',
transition: 'slide top',
on_select: (r) => {
Clipboard.copyOldItem(getKey(r));
App.closeWindow('win-clipboard');
},
setup_list: (list, entry) => {
list.cursor = 'pointer';
const CONNECT_ID = Clipboard.connect('history-searched', () => {
// Do every clip that existed before this widget
Clipboard.clips.forEach((clip, key) => {
const widget = ClipWidget({ key, isImage: clip.isImage, val: clip.clip });
list.add(widget);
widget.show_all();
});
// Setup connection for new clips
Clipboard.connect('clip-added', (_, [key, clip]) => {
const widget = ClipWidget({ key, isImage: clip.isImage, val: clip.clip });
list.add(widget);
widget.show_all();
});
list.set_sort_func((a, b) => {
if (entry.text === '' || entry.text === '-') {
a.set_visible(true);
b.set_visible(true);
return getKey(b) - getKey(a);
}
else {
const ROW_1 = fzfResults.find((f) => f.item[0] === getKey(a))?.score ?? 0;
const ROW_2 = fzfResults.find((f) => f.item[0] === getKey(b))?.score ?? 0;
a.set_visible(ROW_1 !== 0);
b.set_visible(ROW_2 !== 0);
return ROW_2 - ROW_1;
}
});
// Trigger on_text_change after init
entry.text = '-';
Clipboard.disconnect(CONNECT_ID);
});
},
on_text_change: (text, list, placeholder) => {
const fzf = new Fzf([...Clipboard.clips.entries()], {
selector: ([_key, { clip }]) => clip,
tiebreakers: [(a, b) => b[0] - a[0]],
});
fzfResults = fzf.find(text);
list.invalidate_sort();
const visibleApps = list.get_children().filter((row) => row.visible).length;
placeholder.reveal_child = visibleApps <= 0;
},
});
};

View file

@ -1,52 +0,0 @@
const { Window } = Widget;
import RoundedCorner from './screen-corners.ts';
const TopLeft = () => Window({
name: 'cornertl',
layer: 'overlay',
exclusivity: 'ignore',
anchor: ['top', 'left'],
visible: true,
click_through: true,
child: RoundedCorner('topleft'),
});
const TopRight = () => Window({
name: 'cornertr',
layer: 'overlay',
exclusivity: 'ignore',
anchor: ['top', 'right'],
visible: true,
click_through: true,
child: RoundedCorner('topright'),
});
const BottomLeft = () => Window({
name: 'cornerbl',
layer: 'overlay',
exclusivity: 'ignore',
anchor: ['bottom', 'left'],
visible: true,
click_through: true,
child: RoundedCorner('bottomleft'),
});
const BottomRight = () => Window({
name: 'cornerbr',
layer: 'overlay',
exclusivity: 'ignore',
anchor: ['bottom', 'right'],
visible: true,
click_through: true,
child: RoundedCorner('bottomright'),
});
export default () => [
TopLeft(),
TopRight(),
BottomLeft(),
BottomRight(),
];

View file

@ -1,79 +0,0 @@
const { Box, DrawingArea } = Widget;
const { Gtk } = imports.gi;
export default (
place = 'top left',
css = 'background-color: black;',
) => Box({
hpack: place.includes('left') ? 'start' : 'end',
vpack: place.includes('top') ? 'start' : 'end',
css: `
padding: 1px; margin:
${place.includes('top') ? '-1px' : '0'}
${place.includes('right') ? '-1px' : '0'}
${place.includes('bottom') ? '-1px' : '0'}
${place.includes('left') ? '-1px' : '0'};
`,
child: DrawingArea({
css: `
border-radius: 18px;
border-width: 0.068rem;
${css}
`,
setup: (widget) => {
let r = widget.get_style_context()
.get_property('border-radius', Gtk.StateFlags.NORMAL);
widget.set_size_request(r, r);
widget.connect('draw', (_, cr) => {
const c = widget.get_style_context()
.get_property('background-color', Gtk.StateFlags.NORMAL);
r = widget.get_style_context()
.get_property('border-radius', Gtk.StateFlags.NORMAL);
const borderColor = widget.get_style_context()
.get_property('color', Gtk.StateFlags.NORMAL);
// You're going to write border-width: something anyway
const borderWidth = widget.get_style_context()
.get_border(Gtk.StateFlags.NORMAL).left;
widget.set_size_request(r, r);
switch (place) {
case 'topleft':
cr.arc(r, r, r, Math.PI, 3 * Math.PI / 2);
cr.lineTo(0, 0);
break;
case 'topright':
cr.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI);
cr.lineTo(r, 0);
break;
case 'bottomleft':
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
cr.lineTo(0, r);
break;
case 'bottomright':
cr.arc(0, 0, r, 0, Math.PI / 2);
cr.lineTo(r, r);
break;
}
cr.closePath();
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
cr.fill();
cr.setLineWidth(borderWidth);
cr.setSourceRGBA(borderColor.red,
borderColor.green,
borderColor.blue,
borderColor.alpha);
cr.stroke();
});
},
}),
});

View file

@ -1,16 +0,0 @@
import PopupWindow from '../misc/popup.ts';
import CalendarWidget from './main.ts';
import { get_gdkmonitor_from_desc } from '../lib.ts';
const RIGHT_MARGIN = 20;
export default () => PopupWindow({
name: 'calendar',
anchor: ['bottom', 'right'],
margins: [0, RIGHT_MARGIN, 0, 0],
transition: 'slide bottom',
gdkmonitor: get_gdkmonitor_from_desc('desc:Acer Technologies Acer K212HQL T3EAA0014201'),
child: CalendarWidget(),
});

View file

@ -1,92 +0,0 @@
const { Box, Calendar, Label } = Widget;
const { new_now_local } = imports.gi.GLib.DateTime;
const Divider = () => Box({
class_name: 'divider',
vertical: true,
});
const Time = () => Box({
class_name: 'timebox',
vertical: true,
children: [
Box({
class_name: 'time-container',
hpack: 'center',
vpack: 'center',
children: [
Label({
class_name: 'content',
label: 'hour',
setup: (self) => {
self.poll(1000, () => {
self.label = new_now_local().format('%H') || '';
});
},
}),
Divider(),
Label({
class_name: 'content',
label: 'minute',
setup: (self) => {
self.poll(1000, () => {
self.label = new_now_local().format('%M') || '';
});
},
}),
],
}),
Box({
class_name: 'date-container',
hpack: 'center',
child: Label({
css: 'font-size: 20px',
label: 'complete date',
setup: (self) => {
self.poll(1000, () => {
const time = new_now_local();
const dayNameMonth = time.format('%A, %B ');
const dayNum = time.get_day_of_month();
const date = time.format(', %Y');
if (dayNum && dayNameMonth && date) {
self.label = dayNameMonth + dayNum + date;
}
});
},
}),
}),
],
});
const CalendarWidget = () => Box({
class_name: 'cal-box',
child: Calendar({
show_day_names: true,
show_heading: true,
class_name: 'cal',
}),
});
export default () => Box({
class_name: 'date',
vertical: true,
children: [
Time(),
CalendarWidget(),
],
});

View file

@ -1,12 +0,0 @@
import PopupWindow from '../misc/popup.ts';
import CalendarWidget from './main.ts';
const TOP_MARGIN = 6;
export default () => PopupWindow({
name: 'calendar',
anchor: ['top'],
margins: [TOP_MARGIN, 0, 0, 0],
child: CalendarWidget(),
});

View file

@ -1,43 +0,0 @@
const Hyprland = await Service.import('hyprland');
import Gdk from 'gi://Gdk?version=3.0';
/* Types */
import { Monitor } from 'types/service/hyprland';
export const get_hyprland_monitor = (monitor: Gdk.Monitor): Monitor | undefined => {
const manufacturer = monitor.manufacturer?.replace(',', '');
const model = monitor.model?.replace(',', '');
const start = `${manufacturer} ${model}`;
return Hyprland.monitors.find((m) => m.description.startsWith(start));
};
export const get_hyprland_monitor_desc = (monitor: Gdk.Monitor): string => {
const manufacturer = monitor.manufacturer?.replace(',', '');
const model = monitor.model?.replace(',', '');
const start = `${manufacturer} ${model}`;
return `desc:${Hyprland.monitors.find((m) => m.description.startsWith(start))?.description}`;
};
export const get_gdkmonitor_from_desc = (desc: string): Gdk.Monitor => {
const display = Gdk.Display.get_default();
for (let m = 0; m < (display?.get_n_monitors() ?? 0); m++) {
const monitor = display?.get_monitor(m);
if (monitor && desc === get_hyprland_monitor_desc(monitor)) {
return monitor;
}
}
throw Error(`Monitor ${desc} not found`);
};
export const get_monitor_desc_from_id = (id: number): string => {
const monitor = Hyprland.monitors.find((m) => m.id === id);
return `desc:${monitor?.description}`;
};

View file

@ -1,211 +0,0 @@
const { Box, Entry, Label, Window } = Widget;
import Gdk from 'gi://Gdk?version=3.0';
import Gtk from 'gi://Gtk?version=3.0';
import Lock from 'gi://GtkSessionLock?version=0.1';
// This file is generated by Nix
import Vars from './vars.ts';
import Separator from '../misc/separator.ts';
import { get_hyprland_monitor_desc } from '../lib.ts';
/* Types */
import { Box as AgsBox } from 'types/widgets/box';
const lock = Lock.prepare_lock();
const windows = new Map<Gdk.Monitor, Gtk.Window>();
const blurBGs: AgsBox<Gtk.Widget, { geometry: { w: number, h: number } }>[] = [];
const transition_duration = 1000;
const WINDOW_MARGINS = -2;
const ENTRY_SPACING = 20;
const CLOCK_SPACING = 60;
const bgCSS = ({ w = 1, h = 1 } = {}) => `
border: 2px solid rgba(189, 147, 249, 0.8);
background: rgba(0, 0, 0, 0.2);
min-height: ${h}px;
min-width: ${w}px;
transition: min-height ${transition_duration / 2}ms,
min-width ${transition_duration / 2}ms;
`;
const unlock = () => {
blurBGs.forEach((b) => {
b.css = bgCSS({
w: b.attribute.geometry.w,
h: 1,
});
Utils.timeout(transition_duration / 2, () => {
b.css = bgCSS({
w: 1,
h: 1,
});
});
});
Utils.timeout(transition_duration, () => {
lock.unlock_and_destroy();
Gdk.Display.get_default()?.sync();
App.quit();
});
};
const Clock = () => Label({ class_name: 'clock' })
.poll(1000, (self) => {
self.label = (new Date().toLocaleString([], {
hour: 'numeric',
minute: 'numeric',
hour12: true,
}) ?? '')
.replace('a.m.', 'AM')
.replace('p.m.', 'PM');
});
const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => {
const rev = Box({
css: bgCSS(),
attribute: {
geometry: {} as { w: number, h: number },
},
setup: (self) => Utils.idle(() => {
self.attribute.geometry = {
w: monitor.geometry.width,
h: monitor.geometry.height,
};
self.css = bgCSS({
w: self.attribute.geometry.w,
h: 1,
});
Utils.timeout(transition_duration / 2, () => {
self.css = bgCSS({
w: self.attribute.geometry.w,
h: self.attribute.geometry.h,
});
});
}),
});
blurBGs.push(rev);
Window({
name: `blur-bg-${monitor.get_model()}`,
gdkmonitor: monitor,
layer: 'overlay',
anchor: ['top', 'bottom', 'right', 'left'],
margins: [WINDOW_MARGINS],
exclusivity: 'ignore',
child: Box({
hexpand: false,
vexpand: false,
hpack: 'center',
vpack: 'center',
child: rev,
}),
});
const label = Label('Enter password:');
return new Gtk.Window({
child: visible ?
Box({
vertical: true,
vpack: 'center',
hpack: 'center',
spacing: 16,
children: [
Clock(),
Separator(CLOCK_SPACING, { vertical: true }),
Box({
hpack: 'center',
class_name: 'avatar',
}),
Box({
class_name: 'entry-box',
vertical: true,
children: [
label,
Separator(ENTRY_SPACING, { vertical: true }),
Entry({
hpack: 'center',
xalign: 0.5,
visibility: false,
placeholder_text: 'password',
on_accept: (self) => {
self.sensitive = false;
Utils.authenticate(self.text ?? '')
.then(() => unlock())
.catch((e) => {
self.text = '';
label.label = e.message;
self.sensitive = true;
});
},
}).on('realize', (entry) => entry.grab_focus()),
],
}),
],
}) :
Box(),
});
};
const createWindow = (monitor: Gdk.Monitor) => {
const hyprDesc = get_hyprland_monitor_desc(monitor);
const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen;
const win = PasswordPrompt(monitor, entryVisible);
windows.set(monitor, win);
};
const lock_screen = () => {
const display = Gdk.Display.get_default();
for (let m = 0; m < (display?.get_n_monitors() ?? 0); m++) {
const monitor = display?.get_monitor(m);
if (monitor) {
createWindow(monitor);
}
}
display?.connect('monitor-added', (_, monitor) => {
createWindow(monitor);
});
lock.lock_lock();
windows.forEach((win, monitor) => {
lock.new_surface(win, monitor);
win.show();
});
};
const on_finished = () => {
lock.destroy();
Gdk.Display.get_default()?.sync();
App.quit();
};
lock.connect('finished', on_finished);
if (Vars.hasFprintd) {
globalThis.authFinger = () => Utils.authenticate('')
.then(() => unlock())
.catch(logError);
globalThis.authFinger();
}
export default () => lock_screen();

View file

@ -1,4 +0,0 @@
import LaunchLockscreen from './lock.ts';
LaunchLockscreen();

View file

@ -1,57 +0,0 @@
const Audio = await Service.import('audio');
const speakerIcons = {
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 micIcons = {
67: 'audio-input-microphone-high-symbolic',
34: 'audio-input-microphone-medium-symbolic',
1: 'audio-input-microphone-low-symbolic',
0: 'audio-input-microphone-muted-symbolic',
};
export const SpeakerIcon = Variable('');
Audio.connect('speaker-changed', () => {
if (!Audio.speaker) {
return;
}
if (Audio.speaker.stream?.is_muted) {
SpeakerIcon.setValue(speakerIcons[0]);
}
else {
const vol = Audio.speaker.volume * 100;
for (const threshold of [-1, 0, 33, 66, 100]) {
if (vol > threshold + 1) {
SpeakerIcon.setValue(speakerIcons[threshold + 1]);
}
}
}
});
export const MicIcon = Variable('');
Audio.connect('microphone-changed', () => {
if (!Audio.microphone) {
return;
}
if (Audio.microphone.stream?.is_muted) {
MicIcon.setValue(micIcons[0]);
}
else {
const vol = Audio.microphone.volume * 100;
for (const threshold of [-1, 0, 33, 66]) {
if (vol > threshold + 1) {
MicIcon.setValue(micIcons[threshold + 1]);
}
}
}
});

View file

@ -1,15 +0,0 @@
const { Window } = Widget;
export default () => Window({
name: 'bg-gradient',
layer: 'background',
exclusivity: 'ignore',
anchor: ['top', 'bottom', 'left', 'right'],
css: `
background-image: -gtk-gradient (linear,
left top, left bottom,
from(rgba(0, 0, 0, 0.5)),
to(rgba(0, 0, 0, 0)));
`,
});

View file

@ -1,13 +0,0 @@
// Types
import { PopupWindow } from 'global-types';
export default () => {
(App.windows as PopupWindow[])
.filter((w) => w &&
w.close_on_unfocus &&
w.close_on_unfocus !== 'stay')
.forEach((w) => {
App.closeWindow(w.name);
});
};

View file

@ -1,270 +0,0 @@
import Gtk from 'gi://Gtk?version=3.0';
import Gdk from 'gi://Gdk?version=3.0';
// Types
import { BaseProps, Widget as AgsWidget } from 'types/widgets/widget';
type EventHandler<Self> = (self: Self, event: Gdk.Event) => boolean | unknown;
// eslint-disable-next-line
export interface CursorBox<Child, Attr> extends AgsWidget<Attr> { }
export type CursorBoxProps<
Child extends Gtk.Widget,
Attr = unknown,
Self = CursorBox<Child, Attr>,
> = BaseProps<Self, Gtk.EventBox.ConstructorProperties & {
child?: Child
on_hover?: EventHandler<Self>
on_hover_lost?: EventHandler<Self>
on_scroll_up?: EventHandler<Self>
on_scroll_down?: EventHandler<Self>
on_primary_click?: EventHandler<Self>
on_middle_click?: EventHandler<Self>
on_secondary_click?: EventHandler<Self>
on_primary_click_release?: EventHandler<Self>
on_middle_click_release?: EventHandler<Self>
on_secondary_click_release?: EventHandler<Self>
}, Attr>;
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export class CursorBox<Child extends Gtk.Widget, Attr> extends Gtk.EventBox {
static {
Widget.register(this, {
properties: {
'on-clicked': ['jsobject', 'rw'],
'on-hover': ['jsobject', 'rw'],
'on-hover-lost': ['jsobject', 'rw'],
'on-scroll-up': ['jsobject', 'rw'],
'on-scroll-down': ['jsobject', 'rw'],
'on-primary-click': ['jsobject', 'rw'],
'on-secondary-click': ['jsobject', 'rw'],
'on-middle-click': ['jsobject', 'rw'],
'on-primary-click-release': ['jsobject', 'rw'],
'on-secondary-click-release': ['jsobject', 'rw'],
'on-middle-click-release': ['jsobject', 'rw'],
},
});
}
constructor(props: CursorBoxProps<Child, Attr> = {}) {
super(props as Gtk.EventBox.ConstructorProperties);
this.add_events(Gdk.EventMask.SCROLL_MASK);
this.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK);
// Gesture stuff
const gesture = Gtk.GestureMultiPress.new(this);
this.hook(gesture, (_, _n, x, y) => {
if (!x || !y) {
return;
}
this.#canRun.setValue(!(
x > this.get_allocated_width() ||
x <= 0 ||
y > this.get_allocated_height() ||
y <= 0
));
}, 'released');
this.connect('enter-notify-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.PRELIGHT, false);
if (!this.#display) {
return;
}
this.window.set_cursor(Gdk.Cursor.new_from_name(
this.#display,
this.#disabled.value ?
'not-allowed' :
'pointer',
));
return this.on_hover?.(this, event);
});
this.connect('leave-notify-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.PRELIGHT);
this.window.set_cursor(null);
return this.on_hover_lost?.(this, event);
});
this.connect('button-press-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.ACTIVE, false);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
return this.on_primary_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click?.(this, event);
}
});
this.connect('button-release-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.ACTIVE);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = this.#canRun.connect('changed', () => {
if (this.#canRun.value) {
this.on_primary_click_release?.(this, event);
}
this.#canRun.disconnect(id);
});
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click_release?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click_release?.(this, event);
}
});
this.connect('scroll-event', (_, event: Gdk.Event) => {
if (event.get_scroll_deltas()[2] < 0) {
return this.on_scroll_up?.(this, event);
}
else if (event.get_scroll_deltas()[2] > 0) {
return this.on_scroll_down?.(this, event);
}
});
this.hook(this.#disabled, () => {
this.toggleClassName('disabled', this.#disabled.value);
});
}
#display = Gdk.Display.get_default();
// Make this variable to know if the function should
// be executed depending on where the click is released
#canRun = Variable(true);
#disabled = Variable(false);
get disabled() {
return this.#disabled.value;
}
set disabled(value: boolean) {
this.#disabled.setValue(value);
}
get child() {
return super.child as Child;
}
set child(child: Child) {
super.child = child;
}
get on_hover() {
return this._get('on-hover');
}
set on_hover(callback: EventHandler<this>) {
this._set('on-hover', callback);
}
get on_hover_lost() {
return this._get('on-hover-lost');
}
set on_hover_lost(callback: EventHandler<this>) {
this._set('on-hover-lost', callback);
}
get on_scroll_up() {
return this._get('on-scroll-up');
}
set on_scroll_up(callback: EventHandler<this>) {
this._set('on-scroll-up', callback);
}
get on_scroll_down() {
return this._get('on-scroll-down');
}
set on_scroll_down(callback: EventHandler<this>) {
this._set('on-scroll-down', callback);
}
get on_primary_click() {
return this._get('on-primary-click');
}
set on_primary_click(callback: EventHandler<this>) {
this._set('on-primary-click', callback);
}
get on_middle_click() {
return this._get('on-middle-click');
}
set on_middle_click(callback: EventHandler<this>) {
this._set('on-middle-click', callback);
}
get on_secondary_click() {
return this._get('on-secondary-click');
}
set on_secondary_click(callback: EventHandler<this>) {
this._set('on-secondary-click', callback);
}
get on_primary_click_release() {
return this._get('on-primary-click-release');
}
set on_primary_click_release(callback: EventHandler<this>) {
this._set('on-primary-click-release', callback);
}
get on_middle_click_release() {
return this._get('on-middle-click-release');
}
set on_middle_click_release(callback: EventHandler<this>) {
this._set('on-middle-click-release', callback);
}
get on_secondary_click_release() {
return this._get('on-secondary-click-release');
}
set on_secondary_click_release(callback: EventHandler<this>) {
this._set('on-secondary-click-release', callback);
}
}
export default <Child extends Gtk.Widget, Attr>(
props?: CursorBoxProps<Child, Attr>,
) => new CursorBox(props ?? {});

View file

@ -1,54 +0,0 @@
const { execAsync, readFileAsync, timeout } = Utils;
const { get_home_dir } = imports.gi.GLib;
import GObject from 'types/@girs/gobject-2.0/gobject-2.0';
interface Persist {
name: string
gobject: GObject.Object
prop: string
condition?: boolean | string // If string, compare following props to this
whenTrue?: boolean | string
whenFalse?: boolean | string
signal?: string
}
export default ({
name,
gobject,
prop,
condition = true,
whenTrue = condition,
whenFalse = false,
signal = 'changed',
}: Persist) => {
const cacheFile = `${get_home_dir()}/.cache/ags/.${name}`;
const stateCmd = () => ['bash', '-c',
`echo ${gobject[prop] === condition} > ${cacheFile}`];
const monitorState = () => {
gobject.connect(signal, () => {
execAsync(stateCmd()).catch(print);
});
};
readFileAsync(cacheFile)
.then((content) => {
// JSON.parse was the only way I found to reliably
// convert a string of 'true' or 'false' into a bool
gobject[prop] = JSON.parse(content) ? whenTrue : whenFalse;
timeout(1000, () => {
monitorState();
});
})
.catch(() => {
execAsync(stateCmd())
.then(() => {
monitorState();
})
.catch(print);
});
};

Some files were not shown because too many files have changed in this diff Show more