mirror of
https://github.com/zoriya/flake.git
synced 2025-12-06 06:36:19 +00:00
Rework the bar
This commit is contained in:
@@ -44,7 +44,6 @@
|
||||
user = "zoriya";
|
||||
|
||||
mkSystem = system: hostname: {
|
||||
nixModules,
|
||||
homeModules,
|
||||
}: let
|
||||
inputs = rawInput // {inherit user;};
|
||||
@@ -55,7 +54,6 @@
|
||||
./modules/misc
|
||||
# ./modules/gnome
|
||||
./modules/dwl
|
||||
nixModules
|
||||
nur.nixosModules.nur
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
@@ -137,7 +135,6 @@
|
||||
git.enable = true;
|
||||
nvim.enable = true;
|
||||
direnv.enable = true;
|
||||
ntfy.enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
13
modules/dwl/ags/.editorconfig
Normal file
13
modules/dwl/ags/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = tab
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,76 +0,0 @@
|
||||
env:
|
||||
es2021: true
|
||||
extends: eslint:recommended
|
||||
overrides: []
|
||||
parserOptions:
|
||||
ecmaVersion: latest
|
||||
sourceType: 'module'
|
||||
rules:
|
||||
arrow-parens:
|
||||
- error
|
||||
- as-needed
|
||||
comma-dangle:
|
||||
- error
|
||||
- always-multiline
|
||||
comma-spacing:
|
||||
- error
|
||||
- before: false
|
||||
after: true
|
||||
comma-style:
|
||||
- error
|
||||
- last
|
||||
curly:
|
||||
- error
|
||||
- multi-or-nest
|
||||
- consistent
|
||||
dot-location:
|
||||
- error
|
||||
- property
|
||||
eol-last: error
|
||||
indent:
|
||||
- error
|
||||
- 4
|
||||
- SwitchCase: 1
|
||||
keyword-spacing:
|
||||
- error
|
||||
- before: true
|
||||
lines-between-class-members:
|
||||
- error
|
||||
- always
|
||||
- exceptAfterSingleLine: true
|
||||
padded-blocks:
|
||||
- error
|
||||
- never
|
||||
- allowSingleLineBlocks: false
|
||||
prefer-const: error
|
||||
quotes:
|
||||
- error
|
||||
- single
|
||||
- avoidEscape: true
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
nonblock-statement-body-position:
|
||||
- error
|
||||
- below
|
||||
no-trailing-spaces:
|
||||
- error
|
||||
array-bracket-spacing:
|
||||
- error
|
||||
- never
|
||||
key-spacing:
|
||||
- error
|
||||
- beforeColon: false
|
||||
afterColon: true
|
||||
object-curly-spacing:
|
||||
- error
|
||||
- always
|
||||
no-useless-escape:
|
||||
- off
|
||||
globals:
|
||||
ags: readonly
|
||||
ARGV: readonly
|
||||
imports: readonly
|
||||
print: readonly
|
||||
console: readonly
|
||||
logError: readonly
|
||||
4
modules/dwl/ags/.gitignore
vendored
4
modules/dwl/ags/.gitignore
vendored
@@ -1,8 +1,4 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
weather_key
|
||||
*.css
|
||||
settings.json
|
||||
scss/generated.scss
|
||||
scss/additional.scss
|
||||
scss/.goutputstream-*
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
extends: stylelint-config-standard-scss
|
||||
ignoreFiles: "**/*.js"
|
||||
rules:
|
||||
selector-type-no-unknown: null
|
||||
declaration-empty-line-before: null
|
||||
no-descending-specificity: null
|
||||
selector-pseudo-class-no-unknown: null
|
||||
color-function-notation: legacy
|
||||
alpha-value-notation: number
|
||||
scss/operator-no-unspaced: null
|
||||
scss/no-global-function-names: null
|
||||
scss/dollar-variable-empty-line-before: null
|
||||
scss/dollar-variable-pattern: ^[a-z]+(_[a-z]+)*$
|
||||
scss/at-mixin-pattern: ^[a-z]+(_[a-z]+)*$
|
||||
@@ -1,5 +1,4 @@
|
||||
import topbar from './layouts/topbar.js';
|
||||
import * as shared from './layouts/shared.js';
|
||||
import topbar from "./layouts/bar.js";
|
||||
|
||||
// TODO: (ags) dwl patch
|
||||
// const monitors = ags.Service.Hyprland.HyprctlGet('monitors')
|
||||
@@ -7,14 +6,15 @@ import * as shared from './layouts/shared.js';
|
||||
const monitors = [0];
|
||||
|
||||
export default {
|
||||
closeWindowDelay: {
|
||||
'quicksettings': 300,
|
||||
'dashboard': 300,
|
||||
},
|
||||
windows: [
|
||||
...topbar(monitors),
|
||||
// shared.ApplauncherPopup(),
|
||||
shared.PowermenuPopup(),
|
||||
// shared.VerificationPopup(),
|
||||
],
|
||||
closeWindowDelay: {
|
||||
quicksettings: 300,
|
||||
dashboard: 300,
|
||||
},
|
||||
style: ags.App.configDir + "/style.css",
|
||||
windows: [
|
||||
...topbar(monitors),
|
||||
// shared.ApplauncherPopup(),
|
||||
// shared.PowermenuPopup(),
|
||||
// shared.VerificationPopup(),
|
||||
],
|
||||
};
|
||||
|
||||
74
modules/dwl/ags/layouts/bar.js
Normal file
74
modules/dwl/ags/layouts/bar.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// import { Separator } from '../modules/misc.js';
|
||||
// import { PanelIndicator as NotificationIndicator } from './widgets/notifications.js';
|
||||
// import { PanelButton as ColorPicker } from '../modules/colorpicker.js';
|
||||
// import { PanelButton as DashBoard } from './widgets/dashboard.js';
|
||||
// import { PanelButton as ScreenRecord } from '../modules/screenrecord.js';
|
||||
// import { PanelButton as QuickSettings } from './widgets/quicksettings.js';
|
||||
|
||||
import { Clock } from "../modules/clock.js";
|
||||
import { BatteryIndicator } from "../modules/battery.js";
|
||||
|
||||
const { App } = ags;
|
||||
const { Window, CenterBox, Box, Button } = ags.Widget;
|
||||
|
||||
const Bar = (monitor) =>
|
||||
Window({
|
||||
name: `bar${monitor}`,
|
||||
className: "transparent",
|
||||
exclusive: true,
|
||||
anchor: "top left right",
|
||||
monitor,
|
||||
child: CenterBox({
|
||||
startWidget: Box({
|
||||
children: [
|
||||
// Workspaces(),
|
||||
// Separator({ valign: "center" }),
|
||||
// Client(),
|
||||
],
|
||||
}),
|
||||
endWidget: Box({
|
||||
halign: "end",
|
||||
children: [
|
||||
// NotificationIndicator({
|
||||
// direction: "right",
|
||||
// hexpand: true,
|
||||
// halign: "start",
|
||||
// }),
|
||||
// ags.Widget.Box({ hexpand: true }),
|
||||
// ScreenRecord(),
|
||||
// ColorPicker(),
|
||||
// Separator({ valign: "center" }),
|
||||
Button({
|
||||
onClicked: () => App.toggleWindow("quicksettings"),
|
||||
connections: [[App, (btn, win, visible) => {
|
||||
btn.toggleClassName( "active", win === "quicksettings" && visible);
|
||||
}]],
|
||||
child: Box({
|
||||
children: [
|
||||
// audio.MicrophoneMuteIndicator({ unmuted: null }),
|
||||
// notifications.DNDIndicator({ noisy: null }),
|
||||
// BluetoothIndicator(),
|
||||
// bluetooth.Indicator({ disabled: null }),
|
||||
// network.Indicator(),
|
||||
// audio.SpeakerIndicator(),
|
||||
BatteryIndicator(),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Clock({ format: "%a %d %b", className: "module bold" }),
|
||||
Clock({ format: "%H:%M", className: "module accent bold" }),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export default (monitors) =>
|
||||
[
|
||||
...monitors.map((mon) => [
|
||||
Bar(mon),
|
||||
// shared.Notifications(mon, 'slide_down', 'top'),
|
||||
// shared.OSDIndicator(mon),
|
||||
]),
|
||||
// shared.Quicksettings({ position: 'top right' }),
|
||||
// shared.Dashboard({ position: 'top' }),
|
||||
].flat(2);
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as shared from './shared.js';
|
||||
import { Separator } from '../modules/misc.js';
|
||||
import { PanelIndicator as NotificationIndicator } from './widgets/notifications.js';
|
||||
import { PanelButton as ColorPicker } from '../modules/colorpicker.js';
|
||||
import { PanelButton as DashBoard } from './widgets/dashboard.js';
|
||||
import { PanelButton as ScreenRecord } from '../modules/screenrecord.js';
|
||||
import { PanelButton as QuickSettings } from './widgets/quicksettings.js';
|
||||
|
||||
const Bar = monitor => shared.Bar({
|
||||
anchor: 'top left right',
|
||||
monitor,
|
||||
start: [
|
||||
// Workspaces(),
|
||||
Separator({ valign: 'center' }),
|
||||
// Client(),
|
||||
],
|
||||
center: [
|
||||
DashBoard(),
|
||||
],
|
||||
end: [
|
||||
NotificationIndicator({ direction: 'right', hexpand: true, halign: 'start' }),
|
||||
ags.Widget.Box({ hexpand: true }),
|
||||
ScreenRecord(),
|
||||
ColorPicker(),
|
||||
Separator({ valign: 'center' }),
|
||||
QuickSettings(),
|
||||
Separator({ valign: 'center' }),
|
||||
],
|
||||
});
|
||||
|
||||
export default monitors => ([
|
||||
...monitors.map(mon => [
|
||||
Bar(mon),
|
||||
shared.Notifications(mon, 'slide_down', 'top'),
|
||||
shared.OSDIndicator(mon),
|
||||
]),
|
||||
shared.Quicksettings({ position: 'top right' }),
|
||||
shared.Dashboard({ position: 'top' }),
|
||||
]).flat(2);
|
||||
@@ -1,73 +1,117 @@
|
||||
const { Battery } = ags.Service;
|
||||
const { Label, Icon, Stack, ProgressBar, Overlay, Box } = ags.Widget;
|
||||
|
||||
const icons = charging => ([
|
||||
...Array.from({ length: 9 }, (_, i) => i * 10).map(i => ([
|
||||
`${i}`, Icon({
|
||||
className: `${i} ${charging ? 'charging' : 'discharging'}`,
|
||||
icon: `battery-level-${i}${charging ? '-charging' : ''}-symbolic`,
|
||||
}),
|
||||
])),
|
||||
['100', Icon({
|
||||
className: `100 ${charging ? 'charging' : 'discharging'}`,
|
||||
icon: `battery-level-100${charging ? '-charged' : ''}-symbolic`,
|
||||
})],
|
||||
]);
|
||||
const icons = (charging) =>
|
||||
Array.from({ length: 10 }, (_, i) => i * 10).map((i) => [
|
||||
`${i}`,
|
||||
Icon({
|
||||
className: `${i} ${charging ? "charging" : "discharging"}`,
|
||||
icon: `battery-level-${i}${charging ? "-charging" : ""}-symbolic`,
|
||||
}),
|
||||
]);
|
||||
|
||||
const Indicators = charging => Stack({
|
||||
items: icons(charging),
|
||||
connections: [[Battery, stack => {
|
||||
stack.shown = `${Math.floor(Battery.percent / 10) * 10}`;
|
||||
}]],
|
||||
});
|
||||
const Indicators = (charging) =>
|
||||
Stack({
|
||||
items: icons(charging),
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(stack) => {
|
||||
stack.shown = `${Math.floor(Battery.percent / 10) * 10}`;
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
export const Indicator = ({
|
||||
charging = Indicators(true),
|
||||
discharging = Indicators(false),
|
||||
...props
|
||||
} = {}) => Stack({
|
||||
...props,
|
||||
className: 'battery',
|
||||
items: [
|
||||
['true', charging],
|
||||
['false', discharging],
|
||||
],
|
||||
connections: [[Battery, stack => {
|
||||
const { charging, charged } = Battery;
|
||||
stack.shown = `${charging || charged}`;
|
||||
stack.toggleClassName('charging', Battery.charging);
|
||||
stack.toggleClassName('charged', Battery.charged);
|
||||
stack.toggleClassName('low', Battery.percent < 30);
|
||||
}]],
|
||||
});
|
||||
const BatteryIconIndicator = ({
|
||||
charging = Indicators(true),
|
||||
discharging = Indicators(false),
|
||||
...props
|
||||
} = {}) =>
|
||||
Stack({
|
||||
...props,
|
||||
className: "battery",
|
||||
items: [
|
||||
["true", charging],
|
||||
["false", discharging],
|
||||
],
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(stack) => {
|
||||
const { charging, charged } = Battery;
|
||||
log("battery:", charging, charged, Battery.available);
|
||||
stack.shown = `${charging || charged}`;
|
||||
stack.toggleClassName("charging", Battery.charging);
|
||||
stack.toggleClassName("charged", Battery.charged);
|
||||
stack.toggleClassName("low", Battery.percent < 30);
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
export const LevelLabel = props => Label({
|
||||
...props,
|
||||
connections: [[Battery, label => label.label = `${Battery.percent}%`]],
|
||||
});
|
||||
const LevelLabel = (props) =>
|
||||
Label({
|
||||
...props,
|
||||
connections: [[Battery, (label) => (label.label = `${Battery.percent}%`)]],
|
||||
});
|
||||
|
||||
export const BatteryProgress = props => Box({
|
||||
...props,
|
||||
className: 'battery-progress',
|
||||
connections: [[Battery, w => {
|
||||
w.toggleClassName('half', Battery.percent < 46);
|
||||
w.toggleClassName('charging', Battery.charging);
|
||||
w.toggleClassName('charged', Battery.charged);
|
||||
w.toggleClassName('low', Battery.percent < 30);
|
||||
}]],
|
||||
children: [Overlay({
|
||||
child: ProgressBar({
|
||||
hexpand: true,
|
||||
connections: [[Battery, progress => {
|
||||
progress.fraction = Battery.percent / 100;
|
||||
}]],
|
||||
}),
|
||||
overlays: [Label({
|
||||
connections: [[Battery, l => {
|
||||
l.label = Battery.charging || Battery.charged
|
||||
? ''
|
||||
: `${Battery.percent}%`;
|
||||
}]],
|
||||
})],
|
||||
})],
|
||||
});
|
||||
export const BatteryIndicator = () =>
|
||||
Box({
|
||||
children: [BatteryIconIndicator(), LevelLabel()],
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(box) => {
|
||||
box.visible = Battery.available;
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
// export const BatteryProgress = (props) =>
|
||||
// Box({
|
||||
// ...props,
|
||||
// className: "battery-progress",
|
||||
// connections: [
|
||||
// [
|
||||
// Battery,
|
||||
// (w) => {
|
||||
// w.toggleClassName("half", Battery.percent < 46);
|
||||
// w.toggleClassName("charging", Battery.charging);
|
||||
// w.toggleClassName("charged", Battery.charged);
|
||||
// w.toggleClassName("low", Battery.percent < 30);
|
||||
// },
|
||||
// ],
|
||||
// ],
|
||||
// children: [
|
||||
// Overlay({
|
||||
// child: ProgressBar({
|
||||
// hexpand: true,
|
||||
// connections: [
|
||||
// [
|
||||
// Battery,
|
||||
// (progress) => {
|
||||
// progress.fraction = Battery.percent / 100;
|
||||
// },
|
||||
// ],
|
||||
// ],
|
||||
// }),
|
||||
// overlays: [
|
||||
// Label({
|
||||
// connections: [
|
||||
// [
|
||||
// Battery,
|
||||
// (l) => {
|
||||
// l.label =
|
||||
// Battery.charging || Battery.charged
|
||||
// ? ""
|
||||
// : `${Battery.percent}%`;
|
||||
// },
|
||||
// ],
|
||||
// ],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// ],
|
||||
// });
|
||||
|
||||
@@ -2,13 +2,16 @@ const { Label } = ags.Widget;
|
||||
const { DateTime } = imports.gi.GLib;
|
||||
|
||||
export const Clock = ({
|
||||
format = '%a %d %b %H:%M ',
|
||||
interval = 1000,
|
||||
...props
|
||||
} = {}) => Label({
|
||||
className: 'clock',
|
||||
...props,
|
||||
connections: [[interval, label =>
|
||||
label.label = DateTime.new_now_local().format(format),
|
||||
]],
|
||||
});
|
||||
format = "%a %d %b %H:%M ",
|
||||
interval = 1000,
|
||||
...props
|
||||
} = {}) =>
|
||||
Label({
|
||||
...props,
|
||||
connections: [
|
||||
[
|
||||
interval,
|
||||
(label) => (label.label = DateTime.new_now_local().format(format)),
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
315
modules/dwl/ags/old/scss/common.scss
Normal file
315
modules/dwl/ags/old/scss/common.scss
Normal file
@@ -0,0 +1,315 @@
|
||||
window {
|
||||
background-color: transparent;
|
||||
}
|
||||
//
|
||||
// @mixin common{
|
||||
// all: unset;
|
||||
//
|
||||
// * {
|
||||
// font-size: $font_size;
|
||||
// font-family: $font, sans-serif;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin widget{
|
||||
// @include common;
|
||||
// border-radius: $radii;
|
||||
// color: $fg_color;
|
||||
// background-color: $widget_bg;
|
||||
// border: $border;
|
||||
// }
|
||||
//
|
||||
// @mixin button_focus() {
|
||||
// box-shadow: inset 0 0 0 $border_width $accent;
|
||||
// background-color: $hover;
|
||||
// color: $hover_fg;
|
||||
// }
|
||||
//
|
||||
// @mixin button_hover() {
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
// background-color: $hover;
|
||||
// color: $hover_fg;
|
||||
// }
|
||||
//
|
||||
// @mixin button_active() {
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
// background-image: $active_gradient;
|
||||
// background-color: $accent;
|
||||
// color: $accent_fg;
|
||||
// }
|
||||
//
|
||||
// @mixin button_disabled() {
|
||||
// box-shadow: none;
|
||||
// background-color: transparent;
|
||||
// color: transparentize($fg_color, 0.7);
|
||||
// }
|
||||
//
|
||||
// @mixin button($flat: false, $reactive: true, $radii: $radii, $focusable: true){
|
||||
// @include common;
|
||||
// transition: $transition;
|
||||
// border-radius: $radii;
|
||||
// color: $fg_color;
|
||||
//
|
||||
// @if $flat{
|
||||
// background-color: transparent;
|
||||
// background-image: none;
|
||||
// box-shadow: none;
|
||||
// } @else{
|
||||
// background-color: $widget_bg;
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
// }
|
||||
//
|
||||
// @if $reactive{
|
||||
// @if $focusable {
|
||||
// &:focus{
|
||||
// @include button_focus;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:hover{
|
||||
// @include button_hover;
|
||||
// }
|
||||
//
|
||||
// &:active, &.on, &.active, &:checked {
|
||||
// @include button_active;
|
||||
//
|
||||
// &:hover {
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color,
|
||||
// inset 0 0 0 99px $hover;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:disabled {
|
||||
// @include button_disabled;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin accs_button($flat: false, $reactive: true){
|
||||
// @include button($flat: true, $reactive: false, $focusable: false);
|
||||
// color: $fg_color;
|
||||
//
|
||||
// > * {
|
||||
// border-radius: $radii;
|
||||
// transition: $transition;
|
||||
//
|
||||
// @if $flat{
|
||||
// background-color: transparent;
|
||||
// box-shadow: none;
|
||||
// } @else{
|
||||
// background-color: $widget_bg;
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @if $reactive{
|
||||
// &:focus > *, &.focused > * {
|
||||
// @include button_focus;
|
||||
// }
|
||||
//
|
||||
// &:hover > * {
|
||||
// @include button_hover;
|
||||
// }
|
||||
//
|
||||
// &:active, &.active, &.on, &:checked {
|
||||
// > * {
|
||||
// @include button_active;
|
||||
// }
|
||||
//
|
||||
// &:hover > * {
|
||||
// box-shadow: inset 0 0 0 $border_width $border_color,
|
||||
// inset 0 0 0 99px $hover;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin floating_widget {
|
||||
// @include common;
|
||||
//
|
||||
// @if $drop_shadow {
|
||||
// box-shadow: 0 0 6px 0 $shadow;
|
||||
// }
|
||||
// margin: max($spacing, 8px);
|
||||
// border: $border_width solid $popover_border_color;
|
||||
// border-radius: $popover_radius;
|
||||
// background-color: $bg_color;
|
||||
// color: $fg_color;
|
||||
// padding: $popover_padding;
|
||||
// }
|
||||
//
|
||||
// @mixin slider($width: .7em, $slider_width: .5em, $gradient: $active_gradient, $slider: true, $focusable: true, $radii: $radii){
|
||||
// @include common;
|
||||
// * { all:unset; }
|
||||
//
|
||||
// trough{
|
||||
// transition: $transition;
|
||||
// border-radius: $radii;
|
||||
// border: $border;
|
||||
// background-color: $widget_bg;
|
||||
// min-height: $width;
|
||||
// min-width: $width;
|
||||
//
|
||||
// highlight, progress{
|
||||
// border-radius: max($radii - $border_width, 0);
|
||||
// background-image: $gradient;
|
||||
// min-height: $width;
|
||||
// min-width: $width;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// slider {
|
||||
// box-shadow: none;
|
||||
// background-color: transparent;
|
||||
// border: $border_width solid transparent;
|
||||
// transition: $transition;
|
||||
// border-radius: $radii;
|
||||
// min-height: $width;
|
||||
// min-width: $width;
|
||||
// margin: -$slider_width;
|
||||
// }
|
||||
//
|
||||
// &:hover {
|
||||
// trough {
|
||||
// background-color: $hover;
|
||||
// }
|
||||
//
|
||||
// slider {
|
||||
// @if $slider{
|
||||
// background-color: $fg_color;
|
||||
// border-color: $border-color;
|
||||
//
|
||||
// @if $drop_shadow {
|
||||
// box-shadow: 0 0 3px 0 $shadow;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:disabled {
|
||||
// highlight, progress{
|
||||
// background-color: transparentize($fg_color, 0.4);
|
||||
// background-image: none;
|
||||
// }
|
||||
//
|
||||
// slider {
|
||||
// @if $slider {
|
||||
// background-color: transparentize($fg_color, 0.5);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @if $focusable {
|
||||
// trough:focus{
|
||||
// background-color: $hover;
|
||||
// box-shadow: inset 0 0 0 $border_width $accent;
|
||||
//
|
||||
// slider {
|
||||
// @if $slider {
|
||||
// background-color: $fg_color;
|
||||
// box-shadow: inset 0 0 0 $border_width $accent;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin shader($width: 3em){
|
||||
// @include common;
|
||||
//
|
||||
// label {
|
||||
// color: $shader_fg;
|
||||
// text-shadow: $text_shadow;
|
||||
// }
|
||||
//
|
||||
// @if $theme == 'dark' {
|
||||
// box-shadow: inset 0 0 $width $width/3 transparentize($bg_color, 0.3);
|
||||
// }
|
||||
//
|
||||
// @if $theme == 'light' {
|
||||
// background-color: transparentize($bg_color, 0.8);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin text_border{
|
||||
// text-shadow:
|
||||
// -1 * $border_width -1 * $border_width 0 $border_color,
|
||||
// $border_width $border_width 0 $border_color,
|
||||
// -1 * $border_width $border_width 0 $border_color,
|
||||
// $border_width -1 * $border_width 0 $border_color;
|
||||
// }
|
||||
//
|
||||
// @mixin scrollbar{
|
||||
// scrollbar, scrollbar * {
|
||||
// all: unset;
|
||||
// }
|
||||
//
|
||||
// scrollbar.vertical{
|
||||
// slider{
|
||||
// background: $widget_bg;
|
||||
// border-radius: $radii;
|
||||
// min-width: .6em;
|
||||
// min-height: 2em;
|
||||
// transition: $transition;
|
||||
//
|
||||
// &:hover {
|
||||
// background-color: transparentize($fg_color, 0.6);
|
||||
// min-width: .8em;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @mixin switch{
|
||||
// @include button;
|
||||
//
|
||||
// slider {
|
||||
// background-color: $fg_color;
|
||||
// border-radius: $radii;
|
||||
// min-width: 24px;
|
||||
// min-height: 24px;
|
||||
// }
|
||||
//
|
||||
// image { color: transparent; }
|
||||
// }
|
||||
//
|
||||
// tooltip {
|
||||
// @include common;
|
||||
// background-color: transparent;
|
||||
// border: none;
|
||||
//
|
||||
// > * > *{
|
||||
// background-color: $bg_color;
|
||||
// border-radius: $radii;
|
||||
// border: $border_width solid $popover_border_color;
|
||||
// color: $fg_color;
|
||||
// padding: 8px;
|
||||
// margin: 4px;
|
||||
// box-shadow: 0 0 3px 0 $shadow;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// window.popup {
|
||||
// > * {
|
||||
// border: none;
|
||||
// box-shadow: none;
|
||||
// }
|
||||
//
|
||||
// menu {
|
||||
// border-radius: $popover_radius;
|
||||
// background-color: $bg_color;
|
||||
// padding: $spacing;
|
||||
// border: $border;
|
||||
//
|
||||
// menuitem {
|
||||
// @include button;
|
||||
// padding: $spacing/2;
|
||||
// margin: $spacing/2 0;
|
||||
// > * { margin-left: -30px; }
|
||||
// &:first-child { margin-top: 0; }
|
||||
// &:last-child { margin-bottom: 0; }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
14
modules/dwl/ags/old/scss/main.scss
Normal file
14
modules/dwl/ags/old/scss/main.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
// @import './generated';
|
||||
@import './common';
|
||||
// @import './widgets/notifications';
|
||||
// @import './widgets/media';
|
||||
// @import './widgets/datemenu';
|
||||
// @import './widgets/applauncher';
|
||||
// @import './widgets/quicksettings';
|
||||
// @import './widgets/powermenu';
|
||||
// @import './widgets/overview';
|
||||
// @import './widgets/desktop';
|
||||
// @import './widgets/dashboard';
|
||||
// @import './widgets/bar';
|
||||
// @import './widgets/settings';
|
||||
// @import './additional';
|
||||
@@ -58,9 +58,6 @@ export async function setupScss(theme) {
|
||||
try {
|
||||
await writeFile(generated(scss(theme)), `${path}/scss/generated.scss`);
|
||||
await writeFile(generated(theme.additional_scss || ''), `${path}/scss/additional.scss`);
|
||||
await execAsync(['sassc', `${path}/scss/main.scss`, `${path}/style.css`]);
|
||||
ags.App.resetCss();
|
||||
ags.App.applyCss(`${path}/style.css`);
|
||||
} catch (error) {
|
||||
logError(error);
|
||||
}
|
||||
@@ -16,7 +16,6 @@ class ThemeService extends Service {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
exec('swww init');
|
||||
this.setup();
|
||||
}
|
||||
|
||||
@@ -39,7 +38,6 @@ class ThemeService extends Service {
|
||||
};
|
||||
setupScss(theme);
|
||||
this.setupOther();
|
||||
this.setupWallpaper();
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -59,15 +57,6 @@ class ThemeService extends Service {
|
||||
// execAsync(`cp ${wezterm}/charm${darkmode ? '' : '-light'}.lua ${wezterm}/theme.lua`).catch(print);
|
||||
}
|
||||
|
||||
setupWallpaper() {
|
||||
execAsync([
|
||||
'swww', 'img',
|
||||
'--transition-type', 'grow',
|
||||
// '--transition-pos', exec('hyprctl cursorpos').replace(' ', ''),
|
||||
this.getSetting('wallpaper'),
|
||||
]).catch(print);
|
||||
}
|
||||
|
||||
get settings() {
|
||||
if (this._settings)
|
||||
return this._settings;
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "ags-dotfiles",
|
||||
"version": "1.0.0",
|
||||
"description": "My config files for AGS",
|
||||
"main": "config.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint . --fix",
|
||||
"stylelint": "stylelint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Aylur/dotfiles.git"
|
||||
},
|
||||
"author": "Aylur",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Aylur/dotfiles/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Aylur/dotfiles#readme",
|
||||
"devDependencies": {
|
||||
"eslint": "^8.44.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
window {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@mixin common{
|
||||
all: unset;
|
||||
|
||||
* {
|
||||
font-size: $font_size;
|
||||
font-family: $font, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin widget{
|
||||
@include common;
|
||||
border-radius: $radii;
|
||||
color: $fg_color;
|
||||
background-color: $widget_bg;
|
||||
border: $border;
|
||||
}
|
||||
|
||||
@mixin button_focus() {
|
||||
box-shadow: inset 0 0 0 $border_width $accent;
|
||||
background-color: $hover;
|
||||
color: $hover_fg;
|
||||
}
|
||||
|
||||
@mixin button_hover() {
|
||||
box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
background-color: $hover;
|
||||
color: $hover_fg;
|
||||
}
|
||||
|
||||
@mixin button_active() {
|
||||
box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
background-image: $active_gradient;
|
||||
background-color: $accent;
|
||||
color: $accent_fg;
|
||||
}
|
||||
|
||||
@mixin button_disabled() {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
color: transparentize($fg_color, 0.7);
|
||||
}
|
||||
|
||||
@mixin button($flat: false, $reactive: true, $radii: $radii, $focusable: true){
|
||||
@include common;
|
||||
transition: $transition;
|
||||
border-radius: $radii;
|
||||
color: $fg_color;
|
||||
|
||||
@if $flat{
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
} @else{
|
||||
background-color: $widget_bg;
|
||||
box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
}
|
||||
|
||||
@if $reactive{
|
||||
@if $focusable {
|
||||
&:focus{
|
||||
@include button_focus;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover{
|
||||
@include button_hover;
|
||||
}
|
||||
|
||||
&:active, &.on, &.active, &:checked {
|
||||
@include button_active;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 $border_width $border_color,
|
||||
inset 0 0 0 99px $hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include button_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin accs_button($flat: false, $reactive: true){
|
||||
@include button($flat: true, $reactive: false, $focusable: false);
|
||||
color: $fg_color;
|
||||
|
||||
> * {
|
||||
border-radius: $radii;
|
||||
transition: $transition;
|
||||
|
||||
@if $flat{
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
} @else{
|
||||
background-color: $widget_bg;
|
||||
box-shadow: inset 0 0 0 $border_width $border_color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@if $reactive{
|
||||
&:focus > *, &.focused > * {
|
||||
@include button_focus;
|
||||
}
|
||||
|
||||
&:hover > * {
|
||||
@include button_hover;
|
||||
}
|
||||
|
||||
&:active, &.active, &.on, &:checked {
|
||||
> * {
|
||||
@include button_active;
|
||||
}
|
||||
|
||||
&:hover > * {
|
||||
box-shadow: inset 0 0 0 $border_width $border_color,
|
||||
inset 0 0 0 99px $hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin floating_widget {
|
||||
@include common;
|
||||
|
||||
@if $drop_shadow {
|
||||
box-shadow: 0 0 6px 0 $shadow;
|
||||
}
|
||||
margin: max($spacing, 8px);
|
||||
border: $border_width solid $popover_border_color;
|
||||
border-radius: $popover_radius;
|
||||
background-color: $bg_color;
|
||||
color: $fg_color;
|
||||
padding: $popover_padding;
|
||||
}
|
||||
|
||||
@mixin slider($width: .7em, $slider_width: .5em, $gradient: $active_gradient, $slider: true, $focusable: true, $radii: $radii){
|
||||
@include common;
|
||||
* { all:unset; }
|
||||
|
||||
trough{
|
||||
transition: $transition;
|
||||
border-radius: $radii;
|
||||
border: $border;
|
||||
background-color: $widget_bg;
|
||||
min-height: $width;
|
||||
min-width: $width;
|
||||
|
||||
highlight, progress{
|
||||
border-radius: max($radii - $border_width, 0);
|
||||
background-image: $gradient;
|
||||
min-height: $width;
|
||||
min-width: $width;
|
||||
}
|
||||
}
|
||||
|
||||
slider {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
border: $border_width solid transparent;
|
||||
transition: $transition;
|
||||
border-radius: $radii;
|
||||
min-height: $width;
|
||||
min-width: $width;
|
||||
margin: -$slider_width;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
trough {
|
||||
background-color: $hover;
|
||||
}
|
||||
|
||||
slider {
|
||||
@if $slider{
|
||||
background-color: $fg_color;
|
||||
border-color: $border-color;
|
||||
|
||||
@if $drop_shadow {
|
||||
box-shadow: 0 0 3px 0 $shadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
highlight, progress{
|
||||
background-color: transparentize($fg_color, 0.4);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
slider {
|
||||
@if $slider {
|
||||
background-color: transparentize($fg_color, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if $focusable {
|
||||
trough:focus{
|
||||
background-color: $hover;
|
||||
box-shadow: inset 0 0 0 $border_width $accent;
|
||||
|
||||
slider {
|
||||
@if $slider {
|
||||
background-color: $fg_color;
|
||||
box-shadow: inset 0 0 0 $border_width $accent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@mixin shader($width: 3em){
|
||||
@include common;
|
||||
|
||||
label {
|
||||
color: $shader_fg;
|
||||
text-shadow: $text_shadow;
|
||||
}
|
||||
|
||||
@if $theme == 'dark' {
|
||||
box-shadow: inset 0 0 $width $width/3 transparentize($bg_color, 0.3);
|
||||
}
|
||||
|
||||
@if $theme == 'light' {
|
||||
background-color: transparentize($bg_color, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin text_border{
|
||||
text-shadow:
|
||||
-1 * $border_width -1 * $border_width 0 $border_color,
|
||||
$border_width $border_width 0 $border_color,
|
||||
-1 * $border_width $border_width 0 $border_color,
|
||||
$border_width -1 * $border_width 0 $border_color;
|
||||
}
|
||||
|
||||
@mixin scrollbar{
|
||||
scrollbar, scrollbar * {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
scrollbar.vertical{
|
||||
slider{
|
||||
background: $widget_bg;
|
||||
border-radius: $radii;
|
||||
min-width: .6em;
|
||||
min-height: 2em;
|
||||
transition: $transition;
|
||||
|
||||
&:hover {
|
||||
background-color: transparentize($fg_color, 0.6);
|
||||
min-width: .8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin switch{
|
||||
@include button;
|
||||
|
||||
slider {
|
||||
background-color: $fg_color;
|
||||
border-radius: $radii;
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
image { color: transparent; }
|
||||
}
|
||||
|
||||
tooltip {
|
||||
@include common;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
> * > *{
|
||||
background-color: $bg_color;
|
||||
border-radius: $radii;
|
||||
border: $border_width solid $popover_border_color;
|
||||
color: $fg_color;
|
||||
padding: 8px;
|
||||
margin: 4px;
|
||||
box-shadow: 0 0 3px 0 $shadow;
|
||||
}
|
||||
}
|
||||
|
||||
window.popup {
|
||||
> * {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
menu {
|
||||
border-radius: $popover_radius;
|
||||
background-color: $bg_color;
|
||||
padding: $spacing;
|
||||
border: $border;
|
||||
|
||||
menuitem {
|
||||
@include button;
|
||||
padding: $spacing/2;
|
||||
margin: $spacing/2 0;
|
||||
> * { margin-left: -30px; }
|
||||
&:first-child { margin-top: 0; }
|
||||
&:last-child { margin-bottom: 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
@import './generated';
|
||||
@import './common';
|
||||
@import './widgets/notifications';
|
||||
@import './widgets/media';
|
||||
@import './widgets/datemenu';
|
||||
@import './widgets/applauncher';
|
||||
@import './widgets/quicksettings';
|
||||
@import './widgets/powermenu';
|
||||
@import './widgets/overview';
|
||||
@import './widgets/desktop';
|
||||
@import './widgets/dashboard';
|
||||
@import './widgets/bar';
|
||||
@import './widgets/settings';
|
||||
@import './additional';
|
||||
31
modules/dwl/ags/style.css
Normal file
31
modules/dwl/ags/style.css
Normal file
@@ -0,0 +1,31 @@
|
||||
* {
|
||||
all: unset;
|
||||
font-family: monospace;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
window {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.module {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.accent {
|
||||
background-color: #94e2d5;
|
||||
color: #1e1e2e;
|
||||
}
|
||||
|
||||
|
||||
.battery.low {
|
||||
color: red;
|
||||
}
|
||||
.battery.charged, .battery.charging {
|
||||
color: green;
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
|
||||
};
|
||||
|
||||
services.upower.enable = true;
|
||||
|
||||
# i18n.inputMethod.enabled = "ibus";
|
||||
# i18n.inputMethod.ibus.engines = with pkgs.ibus-engines; [mozc];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ in {
|
||||
sassc
|
||||
brightnessctl
|
||||
pavucontrol
|
||||
wbg
|
||||
wallpaper
|
||||
];
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
imports = [
|
||||
./hyprland
|
||||
./eww
|
||||
./rofi
|
||||
./apps
|
||||
./zsh
|
||||
@@ -10,7 +9,6 @@
|
||||
./direnv
|
||||
./fcitx5
|
||||
./colors
|
||||
./ntfy
|
||||
];
|
||||
|
||||
home.stateVersion = "22.11";
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
(defpoll icon :interval "1s" "./bar/battery/get-battery.sh icon")
|
||||
(defpoll percent :interval "1s" "./bar/battery/get-battery.sh percent")
|
||||
|
||||
(defwidget battery []
|
||||
(box :class "battery module floating ${percent < 18 ? "red" : ""}"
|
||||
:spacing 6
|
||||
(label :valign "center" :class "icon" :text "${icon}")
|
||||
(label :valign "center" :class "percent" :text "${percent}%"))
|
||||
)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
bat=/sys/class/power_supply/BAT0/
|
||||
per="$(cat "$bat/capacity")"
|
||||
|
||||
icon() {
|
||||
|
||||
[ $(cat "$bat/status") = Charging ] && echo "" && exit
|
||||
|
||||
if [ "$per" -gt "90" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "80" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "70" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "60" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "50" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "40" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "30" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "20" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "10" ]; then
|
||||
icon=""
|
||||
elif [ "$per" -gt "0" ]; then
|
||||
icon=""
|
||||
else
|
||||
icon=""
|
||||
fi
|
||||
echo "$icon"
|
||||
}
|
||||
|
||||
percent() {
|
||||
echo $per
|
||||
}
|
||||
|
||||
[ "$1" = "icon" ] && icon && exit
|
||||
[ "$1" = "percent" ] && percent
|
||||
@@ -1,13 +0,0 @@
|
||||
(defpoll time :initial '{}' :interval "5s" `date +'{"date": "%a %d %b", "hour": "%H", "minute": "%M", "day": "%A"}'`)
|
||||
|
||||
(defwidget clock []
|
||||
(box
|
||||
:class "module accent bold"
|
||||
(label
|
||||
:text "${time.hour}:${time.minute}"
|
||||
:class "hour")))
|
||||
|
||||
(defwidget date []
|
||||
(box
|
||||
:class "module bold"
|
||||
(label :text "${time.date}")))
|
||||
@@ -1,43 +0,0 @@
|
||||
.bar {
|
||||
background-color: $base00;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.module {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.module-margin {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.floating {
|
||||
background: $base01;
|
||||
border-radius: 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.round {
|
||||
border-radius: 50%;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding: 8px 6px;
|
||||
}
|
||||
|
||||
.battery {
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.percent {
|
||||
margin-left: -5px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
(include "./bar/workspaces/eww.yuck")
|
||||
(include "./bar/layout/eww.yuck")
|
||||
(include "./bar/window/eww.yuck")
|
||||
(include "./bar/battery/eww.yuck")
|
||||
(include "./bar/language/eww.yuck")
|
||||
(include "./bar/clock.yuck")
|
||||
|
||||
(defwidget left []
|
||||
(box
|
||||
:space-evenly false
|
||||
:halign "start"
|
||||
(workspaces)
|
||||
(layout)
|
||||
(window)))
|
||||
|
||||
(defwidget right []
|
||||
(box
|
||||
:space-evenly false
|
||||
:halign "end"
|
||||
;; (notification)
|
||||
(battery)
|
||||
(language)
|
||||
(date)
|
||||
(clock)
|
||||
))
|
||||
|
||||
(defwidget bar []
|
||||
(box
|
||||
:class "bar"
|
||||
(left)
|
||||
(right)))
|
||||
|
||||
(defwindow bar
|
||||
:monitor 0
|
||||
:geometry (geometry
|
||||
:x "0%"
|
||||
:y "0%"
|
||||
:width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:stacking "fg"
|
||||
:exclusive true
|
||||
(bar))
|
||||
@@ -1,8 +0,0 @@
|
||||
(defpoll lang :interval "1s" :initial "en" "./bar/language/language.sh")
|
||||
|
||||
(defwidget language []
|
||||
(box :class "module floating"
|
||||
:spacing 6
|
||||
(label :valign "center" :class "icon" :text "")
|
||||
(label :valign "center" :class "percent" :text "${lang}")))
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
IMLIST_FILE="/tmp/fcitx5-imlist"
|
||||
|
||||
# Print out identifier of current input method
|
||||
current() {
|
||||
dbus-send --session --print-reply \
|
||||
--dest=org.fcitx.Fcitx5 \
|
||||
/controller \
|
||||
org.fcitx.Fcitx.Controller1.CurrentInputMethod \
|
||||
| grep -Po '(?<=")[^"]+' \
|
||||
|| echo "en"
|
||||
}
|
||||
|
||||
# List all input methods added to Fcitx
|
||||
imlist() {
|
||||
if [ ! -f "${IMLIST_FILE}" ]; then
|
||||
dbus-send --session --print-reply --dest=org.fcitx.Fcitx5 /controller org.fcitx.Fcitx.Controller1.AvailableInputMethods \
|
||||
| awk 'BEGIN{i=0}{
|
||||
if($0~/struct {/) i=0;
|
||||
else if(i<6){gsub(/"/,"",$2); printf("%s,",$2); i++}
|
||||
else if(i==6){printf("%s\n",$2); i++}
|
||||
}' > ${IMLIST_FILE}
|
||||
# Output like this:
|
||||
# pinyin, 拼音, 拼音, fcitx-pinyin, 拼, zh_CN, true
|
||||
# rime, 中州韻, , fcitx-rime, ㄓ, zh, true
|
||||
# ......
|
||||
fi
|
||||
cat ${IMLIST_FILE}
|
||||
}
|
||||
|
||||
# This script wait for events from `watch` and
|
||||
# update the text by printing a new line.
|
||||
#
|
||||
# Strip `Keyboard - ` part from IM name then print
|
||||
print_pretty_name() {
|
||||
name=$(imlist | grep "^$(current)," | cut -d',' -f5)
|
||||
if [[ -z "$name" ]]; then
|
||||
return
|
||||
fi
|
||||
echo "${name}"
|
||||
}
|
||||
|
||||
react() {
|
||||
print_pretty_name
|
||||
|
||||
# Track input method changes. Each new line read is an event fired from IM switch
|
||||
while true; do
|
||||
# When read someting from dbus-monitor
|
||||
read -r unused
|
||||
print_pretty_name
|
||||
done
|
||||
}
|
||||
|
||||
# Watch for events from Fcitx.
|
||||
# Need --line-buffered to avoid messages being hold in buffer
|
||||
# dbus-monitor --session destination=org.freedesktop.IBus | grep --line-buffered '65505\|65509' | react
|
||||
|
||||
# Dbus watching does not seems to work so /shrug
|
||||
print_pretty_name
|
||||
@@ -1,7 +0,0 @@
|
||||
(deflisten layout :initial "..." "./bar/layout/get-layout.sh")
|
||||
|
||||
(defwidget layout []
|
||||
(box
|
||||
;:class "round module-margin accent px-2"
|
||||
:class "secondary-txt module"
|
||||
(label :text "${layout}")))
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
layout() {
|
||||
WORKSPACE=$(hyprctl monitors -j | jq '.[0].activeWorkspace.id')
|
||||
IS_FULLSCREEN=$(hyprctl workspaces -j | jq ".[] | select(.id == $WORKSPACE).hasfullscreen")
|
||||
[ $IS_FULLSCREEN = "true" ] \
|
||||
&& echo "[$(hyprctl workspaces -j | jq ".[] | select(.id == $WORKSPACE).windows")]" \
|
||||
|| echo "[]="
|
||||
}
|
||||
|
||||
|
||||
layout
|
||||
socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do
|
||||
layout
|
||||
done
|
||||
@@ -1,6 +0,0 @@
|
||||
(deflisten title :initial "" "./bar/window/get-window-title.sh")
|
||||
|
||||
(defwidget window []
|
||||
(box
|
||||
:class {(title != "" && title != 'null') ? "floating module" : ""}
|
||||
(label :text {title != 'null' ? title : ""})))
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
MAXCHAR=40
|
||||
|
||||
hyprctl activewindow -j | jq --raw-output .title | cut -c -$MAXCHAR
|
||||
socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | grep --line-buffered '^activewindow>>' | stdbuf -o0 awk -F '>>|,' '{printf "%.40s\n", $3}'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//@include "colors";
|
||||
@@ -1,17 +0,0 @@
|
||||
(defvar ws-icons '["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"]')
|
||||
|
||||
(deflisten curr :initial "1" "./bar/workspaces/get-active-workspace.sh")
|
||||
(deflisten wps :initial "[1, 2, 3]" "./bar/workspaces/get-workspaces.sh")
|
||||
(defwidget workspaces []
|
||||
(box
|
||||
:space-evenly true
|
||||
(for workspace in wps
|
||||
(eventbox :onclick "hyprctl dispatch workspace ${workspace}"
|
||||
(box
|
||||
:width 36
|
||||
:height 36
|
||||
:class "${workspace == curr ? "accent" : ""}"
|
||||
(label
|
||||
:style "font-size: 1.3rem;"
|
||||
:text "${ws-icons[workspace - 1]}"
|
||||
))))))
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
hyprctl monitors -j | jq --raw-output .[0].activeWorkspace.id
|
||||
socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | stdbuf -o0 grep '^workspace>>' | stdbuf -o0 awk -F '>>|,' '{print $2}'
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
spaces (){
|
||||
hyprctl workspaces -j | jq -c 'map(.id | select(. > 0)) | sort'
|
||||
}
|
||||
|
||||
spaces
|
||||
socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do
|
||||
spaces
|
||||
done
|
||||
@@ -1,62 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.modules.eww;
|
||||
in {
|
||||
options.modules.eww = {enable = mkEnableOption "eww";};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
home.packages = with pkgs; [
|
||||
pamixer
|
||||
brightnessctl
|
||||
material-icons
|
||||
blueberry
|
||||
bluez
|
||||
gnunet
|
||||
jaq
|
||||
networkmanagerapplet
|
||||
pavucontrol
|
||||
playerctl
|
||||
procps
|
||||
socat
|
||||
udev
|
||||
upower
|
||||
util-linux
|
||||
wget
|
||||
wireplumber
|
||||
wlogout
|
||||
bc
|
||||
jq
|
||||
fusuma
|
||||
eww-wayland
|
||||
];
|
||||
xdg.configFile."eww" = {
|
||||
source = ./.;
|
||||
recursive = true;
|
||||
};
|
||||
xdg.configFile."eww/_colors.scss".text = with config.colorScheme.colors; ''
|
||||
$base00: #${base00};
|
||||
$base01: #${base01};
|
||||
$base02: #${base02};
|
||||
$base03: #${base03};
|
||||
$base04: #${base04};
|
||||
$base05: #${base05};
|
||||
$base06: #${base06};
|
||||
$base07: #${base07};
|
||||
$base08: #${base08};
|
||||
$base09: #${base09};
|
||||
$base0A: #${base0A};
|
||||
$base0B: #${base0B};
|
||||
$base0C: #${base0C};
|
||||
$base0D: #${base0D};
|
||||
$base0E: #${base0E};
|
||||
$base0F: #${base0F};
|
||||
'';
|
||||
|
||||
xdg.configFile."fusuma/config.yaml".source = ./fusuma.yaml;
|
||||
};
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
@import 'colors';
|
||||
@import "bar/eww";
|
||||
@import "panel/scss/_init.scss";
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
font-family: "monospace";
|
||||
}
|
||||
|
||||
.accent {
|
||||
background-color: $base0C;
|
||||
color: $base00;
|
||||
}
|
||||
|
||||
.accent-txt {
|
||||
color: $base0C;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: $base0E;
|
||||
color: $base00;
|
||||
}
|
||||
.secondary-txt {
|
||||
color: $base0E;
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: $base08;
|
||||
color: $base02;
|
||||
}
|
||||
.red-txt {
|
||||
color: $base0E;
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: $base0B;
|
||||
}
|
||||
.green-txt {
|
||||
color: $base0B;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: $base0D;
|
||||
}
|
||||
.blue-txt {
|
||||
color: $base0D;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
(include "./bar/eww.yuck")
|
||||
(include "./panel/yuck/_init.yuck")
|
||||
@@ -1,15 +0,0 @@
|
||||
swipe:
|
||||
3:
|
||||
down:
|
||||
command: "hyprctl dispatch fullscreen 1"
|
||||
up:
|
||||
command: "hyprctl dispatch fullscreen 1"
|
||||
4:
|
||||
left:
|
||||
command: "hyprctl dispatch cyclenext prev"
|
||||
right:
|
||||
command: "hyprctl dispatch cyclenext"
|
||||
up:
|
||||
command: bash -c "eww open panel-closer && eww open panel"
|
||||
down:
|
||||
command: bash -c "eww close panel-closer && eww close panel"
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache/}"
|
||||
|
||||
get_status() {
|
||||
s=$1
|
||||
if [ "$s" = "Playing" ]; then
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
get_length_time() {
|
||||
len=$1
|
||||
if [ -n "$len" ]; then
|
||||
len=$(bc <<< "$len / 1000000 + 1")
|
||||
date -d@"$len" +%M:%S
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
get_position() {
|
||||
pos=$1
|
||||
len=$2
|
||||
if [[ -n "$pos" && -n $len ]]; then
|
||||
bc -l <<< "$pos / $len * 100"
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
get_position_time() {
|
||||
pos=$1
|
||||
len=$2
|
||||
if [ -n "$pos" ]; then
|
||||
date -d@"$(bc <<< "$pos / 1000000")" +%M:%S
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
get_cover() {
|
||||
# COVER_URL=$1
|
||||
mkdir -p "$XDG_CACHE_HOME/eww_covers"
|
||||
cd "$XDG_CACHE_HOME/eww_covers" || exit
|
||||
|
||||
IMGPATH="$XDG_CACHE_HOME/eww_covers/cover_art.png"
|
||||
|
||||
playerctl -F metadata mpris:artUrl 2>/dev/null | while read -r COVER_URL; do
|
||||
if [[ "$COVER_URL" = https* ]]; then
|
||||
if [ ! -e "$XDG_CACHE_HOME/eww_covers/$(basename "$COVER_URL")" ]; then
|
||||
wget -N "$COVER_URL" -o /dev/null
|
||||
fi
|
||||
|
||||
rm "$IMGPATH"
|
||||
ln -s "$(basename "$COVER_URL")" "$IMGPATH"
|
||||
|
||||
echo "$IMGPATH"
|
||||
elif [ "$COVER_URL" = "" ]; then
|
||||
echo ""
|
||||
else
|
||||
echo "$COVER_URL"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
sanitize() {
|
||||
echo "$1" | sed 's/"/\"/g'
|
||||
}
|
||||
|
||||
if [ "$1" = "cover" ]; then
|
||||
get_cover
|
||||
else
|
||||
playerctl -F metadata -f '{{title}}\{{artist}}\{{status}}\{{position}}\{{mpris:length}}\' 2>/dev/null | while IFS="$(printf '\\')" read -r title artist status position len; do
|
||||
jaq --null-input \
|
||||
-r -c \
|
||||
--arg artist "$(sanitize "$artist")" \
|
||||
--arg title "$(sanitize "$title")" \
|
||||
--arg status "$(get_status "$status")" \
|
||||
--arg pos "$(get_position "$position" "$len")" \
|
||||
--arg pos_time "$(get_position_time "$position" "$len")" \
|
||||
--arg length "$(get_length_time "$len")" \
|
||||
'{"artist": $artist, "title": $title, "status": $status, "position": $pos, "position_time": $pos_time, "length": $length}'
|
||||
done
|
||||
fi
|
||||
@@ -1,87 +0,0 @@
|
||||
.disclose-cardimage-eventbox {
|
||||
background: linear-gradient(45deg, $base02 20%, $base01 100%);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.disclose-cardimage-eventbox:hover {
|
||||
background: linear-gradient(170deg, $base01 20%, $shade01 100%);
|
||||
}
|
||||
|
||||
.disclose-cardimage-icon {
|
||||
margin: 0 0 0 -0.21em;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.disclose-cardimage-image-box {
|
||||
padding: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
.disclose-cardimage-image {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: -0.4px -0.3px 4px 4px $shade08;
|
||||
border-radius: 50%;
|
||||
border: 0.25em solid $base09;
|
||||
}
|
||||
|
||||
.disclose-cardimage-separator {
|
||||
background-color: $shade02;
|
||||
font-size: 1;
|
||||
padding: 3.4em 0 0 0;
|
||||
margin: 0 10em 0 10em;
|
||||
}
|
||||
|
||||
.disclose-cardimage-body-box {
|
||||
font-size: 15;
|
||||
padding: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.disclose-cardimage-summary-box {
|
||||
padding: 0.5em 0 0.5em 0.9em;
|
||||
background-color: darken($base09, 10%);
|
||||
border-radius: 0.3em 0.3em 0 0;
|
||||
color: $base02;
|
||||
}
|
||||
|
||||
.disclose-cardimage-appname-label {
|
||||
font-size: 16;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardimage-close-button {
|
||||
margin: 0.6em 1em 0.6em 0;
|
||||
padding: 0.2em;
|
||||
background-color: $base11;
|
||||
color: $base01;
|
||||
border-radius: 50%;
|
||||
box-shadow: -0.4px -0.3px 4px -1.8px $shade09;
|
||||
}
|
||||
|
||||
.disclose-cardimage-close-button:hover {
|
||||
background-color: $base01;
|
||||
color: $base11;
|
||||
}
|
||||
|
||||
.disclose-cardimage-body-outer {
|
||||
padding: 0 1em 0 0;
|
||||
}
|
||||
|
||||
.disclose-cardimage-summary-label {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardimage-body-label {
|
||||
font-size: 16;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardimage-timestamp {
|
||||
color: $base10;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,121 +0,0 @@
|
||||
.disclose-cardprog-eventbox {
|
||||
background: linear-gradient(45deg, $base02 20%, $base01 100%);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.disclose-cardprog-eventbox:hover {
|
||||
background: linear-gradient(170deg, $base01 20%, $shade01 100%);
|
||||
}
|
||||
|
||||
.disclose-cardprog-icon {
|
||||
margin: 0 0 0 -0.21em;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.disclose-cardprog-image-box {
|
||||
padding: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
.disclose-cardprog-image {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: -0.4px -0.3px 4px 4px $shade08;
|
||||
border-radius: 50%;
|
||||
border: 0.25em solid $base09;
|
||||
}
|
||||
|
||||
.disclose-cardprog-separator {
|
||||
background-color: $shade02;
|
||||
font-size: 1;
|
||||
padding: 3.4em 0 0 0;
|
||||
margin: 0 10em 0 10em;
|
||||
}
|
||||
|
||||
.disclose-cardprog-body-box {
|
||||
font-size: 15;
|
||||
padding: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.disclose-cardprog-summary-box {
|
||||
padding: 0.5em 0 0.5em 0.9em;
|
||||
background-color: darken($base09, 10%);
|
||||
border-radius: 0.3em 0.3em 0 0;
|
||||
color: $base02;
|
||||
}
|
||||
|
||||
.disclose-cardprog-appname-label {
|
||||
font-size: 16;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardprog-close-button {
|
||||
margin: 0.6em 1em 0.6em 0;
|
||||
padding: 0.2em;
|
||||
background-color: $base11;
|
||||
color: $base01;
|
||||
border-radius: 50%;
|
||||
box-shadow: -0.4px -0.3px 4px -1.8px $shade09;
|
||||
}
|
||||
|
||||
.disclose-cardprog-close-button:hover {
|
||||
background-color: $base01;
|
||||
color: $base11;
|
||||
}
|
||||
|
||||
.disclose-cardprog-body-outer {
|
||||
padding: 0 1em 0 0;
|
||||
}
|
||||
|
||||
.disclose-cardprog-summary-label {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardprog-body-label {
|
||||
font-size: 16;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardprog-timestamp {
|
||||
color: $base10;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardprog-scale {
|
||||
background-color: $shade01;
|
||||
border-radius: 0.2em;
|
||||
margin: 0.5em 0 0.5em;
|
||||
|
||||
contents {
|
||||
trough {
|
||||
border-radius: 0.2em;
|
||||
|
||||
// Uncomment to enable slider pointer
|
||||
// slider {
|
||||
// margin: -8px;
|
||||
// background-color: $base15;
|
||||
// box-shadow: -0.4px -0.3px 4px -1.8px $shade09;
|
||||
// border-radius: 20%;
|
||||
// }
|
||||
|
||||
highlight {
|
||||
background-color: $base09;
|
||||
padding: 0.7em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disclose-cardprog-value-label {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
margin: 0.2em 0 0.2em 0;
|
||||
color: $base02;
|
||||
font-weight: 700;
|
||||
background-color: $base10;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,84 +0,0 @@
|
||||
.disclose-cardradial-eventbox {
|
||||
background: linear-gradient(45deg, $base02 20%, $base01 100%);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.disclose-cardradial-eventbox:hover {
|
||||
background: linear-gradient(170deg, $base01 20%, $shade01 100%);
|
||||
}
|
||||
|
||||
.disclose-cardradial-separator {
|
||||
background-color: $shade02;
|
||||
font-size: 1;
|
||||
padding: 3.4em 0 0 0;
|
||||
margin: 0 10em 0 10em;
|
||||
}
|
||||
|
||||
.disclose-cardradial-body-box {
|
||||
font-size: 15;
|
||||
padding: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.disclose-cardradial-summary-box {
|
||||
padding: 0.5em 0 0.5em 0.9em;
|
||||
background-color: darken($base09, 10%);
|
||||
border-radius: 0.3em 0.3em 0 0;
|
||||
color: $base02;
|
||||
}
|
||||
|
||||
.disclose-cardradial-appname-label {
|
||||
font-size: 16;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardradial-close-button {
|
||||
margin: 0.6em 1em 0.6em 0;
|
||||
padding: 0.2em;
|
||||
background-color: $base11;
|
||||
color: $base01;
|
||||
border-radius: 50%;
|
||||
box-shadow: -0.4px -0.3px 4px -1.8px $shade09;
|
||||
}
|
||||
|
||||
.disclose-cardradial-close-button:hover {
|
||||
background-color: $base01;
|
||||
color: $base11;
|
||||
}
|
||||
|
||||
.disclose-cardradial-body-outer {
|
||||
padding: 0 1em 0 0;
|
||||
}
|
||||
|
||||
.disclose-cardradial-summary-label {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardradial-body-label {
|
||||
font-size: 16;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardradial-timestamp {
|
||||
color: $base10;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardradial-circle-box {
|
||||
padding: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
.disclose-cardradial-circle {
|
||||
font-size: 25;
|
||||
color: $base09;
|
||||
background-color: $shade01;
|
||||
}
|
||||
|
||||
.disclose-cardradial-tasks {
|
||||
padding: 1.4em;
|
||||
font-weight: bolder;
|
||||
color: $base10;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,86 +0,0 @@
|
||||
.disclose-cardscr-eventbox {
|
||||
background: linear-gradient(45deg, $base02 20%, $base01 100%);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.disclose-cardscr-eventbox:hover {
|
||||
background: linear-gradient(170deg, $shade01 20%, $base01 100%);
|
||||
}
|
||||
|
||||
.disclose-cardscr-summary-box {
|
||||
padding: 0.5em 0 0 1em;
|
||||
border-radius: 0.3em 0.3em 0 0;
|
||||
color: $base02;
|
||||
}
|
||||
|
||||
.disclose-cardscr-close-button {
|
||||
margin: 0.6em 1em 0.6em 0;
|
||||
padding: 0.2em;
|
||||
background-color: $base11;
|
||||
color: $base01;
|
||||
border-radius: 50%;
|
||||
box-shadow: -0.4px -0.3px 4px -1.8px $shade09;
|
||||
}
|
||||
|
||||
.disclose-cardscr-close-button:hover {
|
||||
background-color: $base01;
|
||||
color: $base11;
|
||||
}
|
||||
|
||||
.disclose-cardscr-summary-label {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardscr-body-label {
|
||||
font-size: 16;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-cardscr-timestamp {
|
||||
color: $base10;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disclose-cardscr-image {
|
||||
margin: 1em;
|
||||
border-radius: 0.5em;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.disclose-cardscr-timestamp {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.disclose-cardscr-open-button,
|
||||
.disclose-cardscr-delete-button {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
padding: 0.6em 3.72em 0.6em 3.72em;
|
||||
margin-bottom: 0.7em;
|
||||
border-radius: 0.2em;
|
||||
color: $base02;
|
||||
}
|
||||
|
||||
.disclose-cardscr-open-button {
|
||||
background-color: darken($base09, 10%);
|
||||
}
|
||||
|
||||
.disclose-cardscr-open-button:hover {
|
||||
color: $base09;
|
||||
background-color: $shade02;
|
||||
}
|
||||
|
||||
.disclose-cardscr-delete-button {
|
||||
background-color: $shade02;
|
||||
color: $base11;
|
||||
}
|
||||
|
||||
.disclose-cardscr-delete-button:hover {
|
||||
background-color: $base11;
|
||||
color: $shade02;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,21 +0,0 @@
|
||||
.disclose-empty-banner {
|
||||
padding-top: 8em;
|
||||
}
|
||||
|
||||
.disclose-empty-box {
|
||||
padding: 1em 1em 10em 0;
|
||||
}
|
||||
|
||||
.disclose-empty-label {
|
||||
padding-top: 3em;
|
||||
color: $shade00;
|
||||
font-size: 16;
|
||||
}
|
||||
|
||||
.disclose-separator {
|
||||
font-size: 0.5;
|
||||
background-color: $base03;
|
||||
margin: 35em 25em 35em 25em;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,65 +0,0 @@
|
||||
$base10: $base0A;
|
||||
$base11: $base0B;
|
||||
$base12: $base0C;
|
||||
$base13: $base0D;
|
||||
$base14: $base0E;
|
||||
$base15: $base0F;
|
||||
$shade00: #3d464e;
|
||||
$shade01: #2b3238;
|
||||
$shade02: #24292e;
|
||||
$shade03: #1f2429;
|
||||
$shade04: #1c2126;
|
||||
$shade05: #191e23;
|
||||
$shade06: #161b20;
|
||||
$shade07: #151a1f;
|
||||
$shade08: #13181d;
|
||||
$shade09: #11161b;
|
||||
$shade10: #0e1115;
|
||||
$shade11: #0c0f13;
|
||||
$shade12: #0a0d11;
|
||||
$normal: darken(#7ab0df, 50%);
|
||||
$critical: darken(#f87070, 50%);
|
||||
$low: darken(#79dcaa, 50%);
|
||||
$other: darken(#ffe59e, 50%);
|
||||
|
||||
.disclose-closer {
|
||||
padding-top: 36px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.disclose-layout-box {
|
||||
background-color: $base00;
|
||||
font-family: monospace;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.disclose-headers {
|
||||
padding: 1em 2em 0 2em;
|
||||
margin-bottom: 1.5em;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-headers-label {
|
||||
font-size: 20;
|
||||
color: $base05;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.disclose-scroll {
|
||||
margin: 0 1.5em 0 1.5em;
|
||||
}
|
||||
|
||||
@import "./_empty.scss";
|
||||
|
||||
@import "./_cardprog.scss";
|
||||
@import "./_cardscr.scss";
|
||||
@import "./_cardimage.scss";
|
||||
@import "./_cardradial.scss";
|
||||
|
||||
@import "./_urgency.scss";
|
||||
@import "./_stats.scss";
|
||||
@import "./_music.scss";
|
||||
@import "./_modifiers.scss";
|
||||
@import "./_override.scss";
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,16 +0,0 @@
|
||||
.shot-image-bord-dashed {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.Spotify-rectangle {
|
||||
margin: 0.3em;
|
||||
border-radius: 0.2em;
|
||||
border-width: 0
|
||||
}
|
||||
|
||||
.Qbittorrent-rectangle {
|
||||
border-radius: 0;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,49 +0,0 @@
|
||||
.disclose-music-box {
|
||||
background-image: linear-gradient(to bottom left, rgba(0, 0, 0, 1.0), rgba(30, 33, 40, 0.5));
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 1em;
|
||||
border-radius: 1.5em;
|
||||
|
||||
.play-status {
|
||||
padding: 4px;
|
||||
font-size: 25;
|
||||
}
|
||||
|
||||
.volume-container {
|
||||
background-color: $base03;
|
||||
border-radius: 2em;
|
||||
opacity: 0.8;
|
||||
padding-right: 8px;
|
||||
|
||||
|
||||
.volume-icon {
|
||||
margin: 0.3em;
|
||||
padding: 0.2em;
|
||||
border-radius: 1em;
|
||||
font-size: 20;
|
||||
}
|
||||
|
||||
.volume-text {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disclose-dnd-header {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
color: $base05;
|
||||
}
|
||||
|
||||
.disclose-dnd-footer {
|
||||
font-size: 15;
|
||||
color: darken($base06, 10%);
|
||||
}
|
||||
|
||||
.disclose-dnd-labels {
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
|
||||
// vim:filetype=scss
|
||||
@@ -1,7 +0,0 @@
|
||||
.disclose-cardprog-separator,
|
||||
.disclose-cardradial-separator,
|
||||
.disclose-cardimage-separator {
|
||||
opacity: 0.0;
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,41 +0,0 @@
|
||||
.disclose-big-button {
|
||||
font-size: 15;
|
||||
border-radius: 2em;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.disclose-stats-box {
|
||||
background-color: $base01;
|
||||
border-radius: 3em;
|
||||
margin: 0 1em 0 1em;
|
||||
padding: 0.8em;
|
||||
|
||||
.stats-label {
|
||||
padding: 0.9em 0.9em 0.9em 0.83em;
|
||||
font-size: 23;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
circular-progress {
|
||||
background-color: $base02;
|
||||
}
|
||||
|
||||
.stats-separator {
|
||||
font-size: 1;
|
||||
background-color: $base03;
|
||||
padding: 1em;
|
||||
margin-left: 15em;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 20;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: $base0F;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// vim:filetype=scss
|
||||
@@ -1,9 +0,0 @@
|
||||
.disclose-cardimage-summary-box-CRITICAL {
|
||||
background-color: darken($base11, 10%);
|
||||
}
|
||||
|
||||
.disclose-cardimage-summary-box-LOW {
|
||||
background-color: darken($base15, 10%);
|
||||
}
|
||||
|
||||
// vim:ft=scss
|
||||
@@ -1,209 +0,0 @@
|
||||
"""A DBus eavesdropper for org.freedesktop.Notifications
|
||||
interface.
|
||||
|
||||
This is created mainly to cache the raw image data that is
|
||||
sent by stupid applications like Spotify, Discord, etc.
|
||||
Now that I think about it all of the electron clients do this.
|
||||
|
||||
Usually any application, if they had to they'd send the
|
||||
notifications as a path i.e. caching the image themselves
|
||||
and then returning the path to it.
|
||||
|
||||
Also, <https://specifications.freedesktop.org/notification-spec/latest/>
|
||||
is a really nice manual. Give it a read.
|
||||
"""
|
||||
|
||||
# Authored By dharmx <dharmx@gmail.com> under:
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
# Version 3, 29 June 2007
|
||||
#
|
||||
# Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
# Everyone is permitted to copy and distribute verbatim copies
|
||||
# of this license document, but changing it is not allowed.
|
||||
#
|
||||
# Permissions of this strong copyleft license are conditioned on
|
||||
# making available complete source code of licensed works and
|
||||
# modifications, which include larger works using a licensed work,
|
||||
# under the same license. Copyright and license notices must be
|
||||
# preserved. Contributors provide an express grant of patent rights.
|
||||
#
|
||||
# Read the complete license here:
|
||||
# <https://github.com/dharmx/vile/blob/main/LICENSE.txt>
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import typing
|
||||
|
||||
import dbus
|
||||
import utils
|
||||
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from gi.repository import GLib
|
||||
|
||||
|
||||
class Urgency:
|
||||
"""Acts as an Enum for indicating the urgency levels as per
|
||||
the notifications specification.
|
||||
|
||||
You may use these to match wheter a specific notification belongs
|
||||
to a specific urgency class or, not.
|
||||
|
||||
Attributes:
|
||||
LOW: Ads, Login, etc.
|
||||
NORMAL: USB unplugged, Drive mounted, etc.
|
||||
CRITICAL: Your PC is on fire, Storage Full, etc.
|
||||
"""
|
||||
LOW = b"\x00"
|
||||
NORMAL = b"\x01"
|
||||
CRITICAL = b"\x02"
|
||||
|
||||
|
||||
class Eavesdropper:
|
||||
"""A quick and naive way of saving the image-data.
|
||||
|
||||
The main idea is to keep a notification server running that
|
||||
implements image-data and image-path as per the freedesktop
|
||||
notification specification.
|
||||
And, then you'd run the eavesdropper which will connect to that
|
||||
interface (org.freedesktop.Notifications) and will continuously
|
||||
monitor that interface.
|
||||
And, if any application sends a notification, that contains a raw
|
||||
icon then it will be saved into the cache directory.
|
||||
|
||||
Attributes:
|
||||
callback:
|
||||
The arbitrary subroutine that will executed on getting a notification.
|
||||
cache_dir:
|
||||
The directory path that all of those image-data would be saved.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
callback: typing.Callable = print,
|
||||
cache_dir: str = "/tmp"
|
||||
):
|
||||
"""Assigns the CTOR parameters to the field variables (duh..)
|
||||
|
||||
Arguments:
|
||||
callback: The arbitrary subroutine that will executed on getting a notification.
|
||||
cache_dir: The directory path that all of those image-data would be saved.
|
||||
"""
|
||||
self.callback = callback
|
||||
self.cache_dir = f"{os.path.expandvars(cache_dir)}/image-data"
|
||||
# translation: mkdir --parents cache_dir
|
||||
pathlib.PosixPath(self.cache_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def _message_callback(
|
||||
self, _,
|
||||
message: dbus.lowlevel.MethodReturnMessage
|
||||
or dbus.lowlevel.MethodCallMessage
|
||||
):
|
||||
"""A filter callback for parsing the specific messages that are received from the DBus interface.
|
||||
|
||||
Arguments:
|
||||
proxy_bus:
|
||||
The bus that sent the message.
|
||||
message:
|
||||
In this case a message is sent when the
|
||||
Notify method is called AND when the Notify method returns something.
|
||||
|
||||
If the message is of type dbus.lowlevel.MethodCallMessage then this will NOT call the passed callback.
|
||||
"""
|
||||
|
||||
# we will be filtering this out as we only need the value that the method call returns
|
||||
# i.e. dbus.lowlevel.MethodReturnMessage
|
||||
if type(message) != dbus.lowlevel.MethodCallMessage:
|
||||
return
|
||||
|
||||
args_list = message.get_args_list()
|
||||
# convert dbus data types to pythonic ones
|
||||
# eg: dbus.String('Hello') -> 'Hello'
|
||||
args_list = [utils.unwrap(item) for item in args_list]
|
||||
|
||||
# set fallbacks like a fine gentleman
|
||||
details = {
|
||||
"appname": args_list[0].strip() or "Unknown",
|
||||
"summary": args_list[3].strip() or "Summary Unavailable.",
|
||||
"body": args_list[4].strip() or "Body Unavailable.",
|
||||
"id": datetime.datetime.now().strftime("%s"),
|
||||
"urgency": "unknown",
|
||||
}
|
||||
|
||||
if "urgency" in args_list[6]:
|
||||
details["urgency"] = args_list[6]["urgency"]
|
||||
|
||||
if args_list[2].strip():
|
||||
# if the iconpath value is a path i.e. if it has slashes on them
|
||||
# then assign that as the iconpath
|
||||
if "/" in args_list[2] or "." in args_list[2]:
|
||||
details["iconpath"] = args_list[2]
|
||||
else:
|
||||
# and if the iconpath is just a string that has no extensions or,
|
||||
# a pathlike structure like: 'bell' or 'custom-folder-bookmark'
|
||||
# it might have a dash (-) sign but not all the time.
|
||||
# then fetch that actual path of that icon as that is a part of the
|
||||
# icon theme naming convention and the current icon theme should probably have it
|
||||
details["iconpath"] = utils.get_gtk_icon_path(args_list[2])
|
||||
else:
|
||||
# if there are no icon hints then use fallback (generic bell)
|
||||
details["iconpath"] = utils.get_gtk_icon_path(
|
||||
"custom-notification")
|
||||
|
||||
if "image-data" in args_list[6]:
|
||||
# capture the raw image bytes and save them to the cache_dir/x.png path
|
||||
details["iconpath"] = f"{self.cache_dir}/{details['appname']}-{details['id']}.png"
|
||||
utils.save_img_byte(
|
||||
args_list[6]["image-data"], details["iconpath"])
|
||||
|
||||
# BUG: add a print statement -> init logger.py and disown the process
|
||||
# BUG: then you'll notice the notifications with value (progress) hint does not get logged
|
||||
if "value" in args_list[6]:
|
||||
details["progress"] = args_list[6]["value"]
|
||||
|
||||
# execute arbitrary callback and passing details about the current notification.
|
||||
self.callback(details)
|
||||
|
||||
def eavesdrop(
|
||||
self,
|
||||
timeout: int or bool = False,
|
||||
timeout_callback: typing.Callable = print
|
||||
):
|
||||
"""Primes the session bus instance and starts a GLib mainloop.
|
||||
|
||||
Arguments:
|
||||
timeout:
|
||||
Intervals for executing the callback.
|
||||
timeout_callback:
|
||||
Callback that will be executed on intervals.
|
||||
"""
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
rules = {
|
||||
"interface": "org.freedesktop.Notifications",
|
||||
"member": "Notify",
|
||||
"eavesdrop": "true", # https://bugs.freedesktop.org/show_bug.cgi?id=39450
|
||||
}
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
# discard all other interfaces except org.freedesktop.Notifications
|
||||
# setting eavesdrop to true enables DBus to send the messages that are
|
||||
# not meant for you.
|
||||
# removing the eavesdrop key from rules will not send the Notify method's
|
||||
# contents to you (you can try and see what happens)
|
||||
bus.add_match_string(",".join([f"{key}={value}" for key, value in rules.items()]))
|
||||
bus.add_message_filter(self._message_callback)
|
||||
|
||||
try:
|
||||
loop = GLib.MainLoop()
|
||||
if timeout:
|
||||
# executes a callback in intervals
|
||||
GLib.set_timeout(timeout, timeout_callback)
|
||||
loop.run()
|
||||
except (KeyboardInterrupt, Exception) as excep:
|
||||
sys.stderr.write(str(excep) + "\n")
|
||||
bus.close()
|
||||
|
||||
|
||||
# vim:filetype=python
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CACHE_PATH="$XDG_CACHE_HOME/eww/dunst/notifications.txt"
|
||||
QUOTE_PATH="$XDG_CACHE_HOME/eww/dunst/quotes.txt"
|
||||
DEFAULT_QUOTE="To fake it is to stand guard over emptiness. \u2500\u2500 Arthur Herzog"
|
||||
|
||||
mkdir -p "${CACHE_PATH:h}" "${QUOTE_PATH:h}" 2> /dev/null
|
||||
touch "$CACHE_PATH" "$QUOTE_PATH" 2> /dev/null
|
||||
|
||||
INTERVAL='0.2'
|
||||
|
||||
function rand_quote() {
|
||||
local format
|
||||
format="$(tr '\n \t\r' 's' < "$QUOTE_PATH")"
|
||||
if [[ "$format" != "" ]]; then
|
||||
shuf "$QUOTE_PATH" | head -n1
|
||||
else
|
||||
echo "$DEFAULT_QUOTE"
|
||||
fi
|
||||
}
|
||||
|
||||
function empty_format() {
|
||||
local format
|
||||
format=(
|
||||
"(box"
|
||||
":class"
|
||||
"'disclose-empty-box'"
|
||||
":height"
|
||||
"750"
|
||||
":orientation"
|
||||
"'vertical'"
|
||||
":space-evenly"
|
||||
"false"
|
||||
"(image"
|
||||
":class"
|
||||
"'disclose-empty-banner'"
|
||||
":valign"
|
||||
"'end'"
|
||||
":vexpand"
|
||||
"true"
|
||||
":path"
|
||||
"'./assets/wedding-bells.png'"
|
||||
":image-width"
|
||||
"250"
|
||||
":image-height"
|
||||
"250)"
|
||||
"(label"
|
||||
":vexpand"
|
||||
"true"
|
||||
":valign"
|
||||
"'start'"
|
||||
":wrap"
|
||||
"true"
|
||||
":class"
|
||||
"'disclose-empty-label'"
|
||||
":text"
|
||||
"'$(rand_quote)'))"
|
||||
)
|
||||
echo "${format[@]}"
|
||||
}
|
||||
|
||||
function not_empty() {
|
||||
echo -n "(box :spacing 20 :orientation 'vertical' :space-evenly false"
|
||||
if [[ "$(echo "$1" | tr -d ' ')" != "" ]]; then
|
||||
echo -n "$1"
|
||||
else
|
||||
echo -n "$(empty_format)"
|
||||
fi
|
||||
echo ")"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
rmid) sed -i "/:identity ':::###::::XXXWWW$2===::'/d" "$CACHE_PATH" ;;
|
||||
sub)
|
||||
old="$(tr '\n' ' ' < "$CACHE_PATH")"
|
||||
not_empty "$old"
|
||||
while sleep "$INTERVAL"; do
|
||||
new="$(tr '\n' ' ' < "$CACHE_PATH")"
|
||||
if [[ "$old" != "$new" ]]; then
|
||||
not_empty "$new"
|
||||
old="$new"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
quote) rand_quote ;;
|
||||
cls) echo > "$CACHE_PATH" ;;
|
||||
esac
|
||||
|
||||
# vim:filetype=sh
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CONFIG="$XDG_CONFIG_HOME/eww/ewwrc"
|
||||
CACHE_DIR=$(jq --raw-output .github.cache_dir "$CONFIG")
|
||||
eval "CACHE_DIR=${CACHE_DIR}"
|
||||
USER_NAME=$(jq --raw-output .github.username "$CONFIG")
|
||||
DATE=$(date +%F)
|
||||
|
||||
[ -f "$CACHE_DIR" ] || mkdir --parents "$CACHE_DIR"
|
||||
|
||||
fetch_user_info() {
|
||||
[ -f "$CACHE_DIR/users-$USER_NAME-$DATE.json" ] || curl --silent https://api.github.com/users/dharmx > "$CACHE_DIR/users-$USER_NAME-$DATE.json"
|
||||
[ -f "$CACHE_DIR/repos-$USER_NAME-$DATE.json" ] || curl --silent https://api.github.com/users/dharmx/repos > "$CACHE_DIR/repos-$USER_NAME-$DATE.json"
|
||||
}
|
||||
|
||||
fetch_user_info
|
||||
case "$1" in
|
||||
users) cat "$CACHE_DIR/users-$USER_NAME-$DATE.json" ;;
|
||||
repos) cat "$CACHE_DIR/repos-$USER_NAME-$DATE.json" ;;
|
||||
esac
|
||||
|
||||
# vim:filetype=sh
|
||||
@@ -1,320 +0,0 @@
|
||||
"""This file is tightly integrated with logger.py and won't work without it.
|
||||
Unlike the files cache.py and utils.py.
|
||||
|
||||
This module just redirects specific presets of messages based on the source
|
||||
(the application) that sent that message.
|
||||
|
||||
The redirector will call the specific function based off the appname, then
|
||||
the called handler function will evaluate the YUCK literal and replace all
|
||||
of the items on the format string with the passed attributes and then return it.
|
||||
"""
|
||||
|
||||
# Authored By dharmx <dharmx.dev@gmail.com> under:
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
# Version 3, 29 June 2007
|
||||
#
|
||||
# Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
# Everyone is permitted to copy and distribute verbatim copies
|
||||
# of this license document, but changing it is not allowed.
|
||||
#
|
||||
# Permissions of this strong copyleft license are conditioned on
|
||||
# making available complete source code of licensed works and
|
||||
# modifications, which include larger works using a licensed work,
|
||||
# under the same license. Copyright and license notices must be
|
||||
# preserved. Contributors provide an express grant of patent rights.
|
||||
#
|
||||
# Read the complete license here:
|
||||
# <https://github.com/dharmx/vile/blob/main/LICENSE.txt>
|
||||
|
||||
import datetime
|
||||
|
||||
import cache
|
||||
import utils
|
||||
|
||||
# WARN: Subject to heavy change.
|
||||
|
||||
def redir_to_handlers(formats, attributes: dict) -> str:
|
||||
r"""Function for evaluating which handler function will be called.
|
||||
|
||||
Before calling the handler function it will do some filtering and
|
||||
then actually call the handler which should return a fully evaluated
|
||||
YUCK literal string.
|
||||
|
||||
Arguments:
|
||||
formats:
|
||||
All of the YUCK literal format strings.
|
||||
attributes:
|
||||
Details about the currently sent notification like summary, body, appname, etc.
|
||||
|
||||
Returns:
|
||||
A str that is a primed YUCK literal with passed attributes.
|
||||
|
||||
Example:
|
||||
Format:
|
||||
(_cardimage :identity ':::###::::XXXWWW%(id)s===::'
|
||||
:close_action './src/shell/logger.py rmid %(id)s'
|
||||
:limit_body '%(BODY_LIMITER)s'
|
||||
:limit_summary '%(SUMMARY_LIMITER)s'
|
||||
:summary '%(summary)s'
|
||||
:body '%(body)s'
|
||||
:close '繁'
|
||||
:image_height 100
|
||||
:image_width 100
|
||||
:image '%(iconpath)s'
|
||||
:appname '%(appname)s'
|
||||
:icon '%(iconpath)s'
|
||||
:icon_height 32
|
||||
:icon_width 32
|
||||
:timestamp '%(TIMESTAMP)s'
|
||||
:urgency '%(URGENCY)s')
|
||||
Primed:
|
||||
(_cardimage :identity ':::###::::XXXWWW1658665761===::'
|
||||
:close_action './src/shell/logger.py rmid 1658665761'
|
||||
:limit_body '110'
|
||||
:limit_summary '30'
|
||||
:summary 'Picom'
|
||||
:body 'The compositer is now disabled.'
|
||||
:close '繁'
|
||||
:image_height 100
|
||||
:image_width 100
|
||||
:image '/home/maker/.icons/custom/stock/128/custom-crying.png'
|
||||
:appname 'Picom'
|
||||
:icon '/home/maker/.icons/custom/stock/128/custom-crying.png'
|
||||
:icon_height 32
|
||||
:icon_width 32
|
||||
:timestamp '17:59'
|
||||
:urgency 'CRITICAL')
|
||||
"""
|
||||
|
||||
# assign the timestamp of the notification.
|
||||
attributes["TIMESTAMP"] = datetime.datetime.now().strftime(
|
||||
attributes["TIMESTAMP_FORMAT"])
|
||||
|
||||
# turn the urgency values (which are in bytes) to a more readable format (string).
|
||||
match attributes["urgency"]:
|
||||
case cache.Urgency.LOW:
|
||||
attributes["URGENCY"] = "LOW"
|
||||
case cache.Urgency.NORMAL:
|
||||
attributes["URGENCY"] = "NORMAL"
|
||||
case cache.Urgency.CRITICAL:
|
||||
attributes["URGENCY"] = "CRITICAL"
|
||||
case _:
|
||||
attributes["URGENCY"] = "NORMAL"
|
||||
|
||||
# handle next lines (especially discord code blocks)
|
||||
# NOTE: may make this only discord / firefox specific
|
||||
attributes["body"] = attributes["body"].replace("\n", " ")
|
||||
attributes["summary"] = attributes["summary"].replace("\n", " ")
|
||||
|
||||
# check if there are any pango tags on the body and summary and if so
|
||||
# it will then remove it.
|
||||
if utils.contains_pango(attributes["body"]):
|
||||
attributes["body"] = utils.strip_pango_tags(attributes["body"])
|
||||
if utils.contains_pango(attributes["summary"]):
|
||||
attributes["summary"] = utils.strip_pango_tags(
|
||||
attributes["summary"]
|
||||
)
|
||||
|
||||
# it will replace all of the apostrophes with \' i.e. escape them.
|
||||
if "'" in attributes["body"]:
|
||||
attributes["body"] = attributes["body"].replace("'", "\\'")
|
||||
if "'" in attributes["summary"]:
|
||||
attributes["summary"] = attributes["summary"].replace("'", "\\'")
|
||||
|
||||
# only 15 chars would be taken if the summary has non-english charecters.
|
||||
attributes["SUMMARY_LIMITER"] = ""
|
||||
summary_lang_char_check = utils.has_non_english_chars(
|
||||
attributes["summary"][:15]
|
||||
)
|
||||
# if summary has asian characters like Japanese, Hindi or, Chinese
|
||||
if summary_lang_char_check["CJK"]:
|
||||
attributes["SUMMARY_LIMITER"] = 14
|
||||
# if summary has Cyrillic characters
|
||||
elif summary_lang_char_check["CYR"]:
|
||||
attributes["SUMMARY_LIMITER"] = 30
|
||||
|
||||
attributes["BODY_LIMITER"] = ""
|
||||
body_lang_char_check = utils.has_non_english_chars(attributes["body"][:70])
|
||||
if body_lang_char_check["CJK"]:
|
||||
attributes["BODY_LIMITER"] = 80
|
||||
elif body_lang_char_check["CYR"]:
|
||||
attributes["BODY_LIMITER"] = 110
|
||||
else:
|
||||
attributes["BODY_LIMITER"] = 100
|
||||
|
||||
# redirect to handlers
|
||||
match attributes["appname"]:
|
||||
case "notify-send":
|
||||
return notify_send_handler(formats, attributes)
|
||||
case "volume":
|
||||
return volume_handler(formats, attributes)
|
||||
case "brightness":
|
||||
return brightness_handler(formats, attributes)
|
||||
case "shot":
|
||||
return shot_handler(formats, attributes)
|
||||
case "shot_icon":
|
||||
return shot_icon_handler(formats, attributes)
|
||||
case "todo":
|
||||
return todo_handler(formats, attributes)
|
||||
case "Spotify":
|
||||
return Spotify_handler(formats, attributes)
|
||||
case _:
|
||||
return default_handler(formats, attributes)
|
||||
|
||||
|
||||
# TODO: Segregate redir_to_handlers into more utility / helper functions.
|
||||
# NOTE: Currently it is a bit hard to utilize config values without making the code dirtier.
|
||||
# NOTE: For instance shot_handler will eventually load the command strings from the config JSON for attributes["DELETE"]. See logger.py
|
||||
def shot_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for screenshot related notifications.
|
||||
|
||||
Note that this handler will only handle the screenshots itself.
|
||||
That is, it won't handle it if say.. the screenshot is copied to the clipboard, etc.
|
||||
All of those are handled by shot_icon_handler.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
# TODO: Make this better
|
||||
attributes["DELETE"] = f"rm --force \\'{attributes['iconpath']}\\' && ./src/shell/logger.py rmid {attributes['id']}"
|
||||
attributes["OPEN"] = f"xdg-open \\'{attributes['iconpath']}\\'"
|
||||
|
||||
# capitalize words i.e. notify_send -> Notify Send
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["shot"] % attributes
|
||||
|
||||
|
||||
def Spotify_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for notifications related to the official electron client for Spotify.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
return formats["Spotify"] % attributes
|
||||
|
||||
|
||||
def default_handler(formats, attributes: dict) -> str:
|
||||
r"""Handler for basic notifications. The notifications that are ordinary.
|
||||
Or, rather the notifications that do not match any of the match-cases in
|
||||
the redir_to_handlers function.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Example:
|
||||
notify-send Hello
|
||||
notify-send Greetings
|
||||
notify-send -u low -i bell Greetings Ding\!
|
||||
notify-send -a appname-does-not-exist -i bell Yo
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["default"] % attributes
|
||||
|
||||
|
||||
def notify_send_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for notifications related to the notify-send command.
|
||||
|
||||
See:
|
||||
default_handler
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["notify-send"] % attributes
|
||||
|
||||
|
||||
def brightness_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for notifications related to brightness control.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["brightness"] % attributes
|
||||
|
||||
|
||||
def volume_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for notifications related to volume control.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["volume"] % attributes
|
||||
|
||||
|
||||
# TODO: Make this general purpose and not just todo specific.
|
||||
def todo_handler(formats, attributes: dict) -> str:
|
||||
"""Handler for notifications related to todo-bin CLI application by Siddomy.
|
||||
|
||||
The notification body needs to be in a particular format in order for it to register.
|
||||
That is: <completed_tasks> tasks done and <total_tasks> are remaining.
|
||||
|
||||
The fragments <completed_tasks> and <total_tasks> will be picked up by this handler.
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
splitted = attributes["body"].split(" ")
|
||||
attributes["TOTAL"] = int(splitted[4])
|
||||
attributes["DONE"] = int(splitted[0])
|
||||
|
||||
# handle division by zero
|
||||
attributes["PERC"] = (attributes["DONE"] / attributes["TOTAL"]
|
||||
) * 100 if attributes["DONE"] > 0 else 0
|
||||
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["todo"] % attributes
|
||||
|
||||
|
||||
def shot_icon_handler(formats, attributes: dict) -> str:
|
||||
"""Almost same as default_handler only just it uses a different icon.
|
||||
|
||||
Redundant, but still nice to if you want to add additional
|
||||
functionalities on this particular appname.
|
||||
|
||||
See:
|
||||
default_handler
|
||||
shot_handler
|
||||
|
||||
Arguments:
|
||||
formats: See redir_to_handlers.
|
||||
attributes: See redir_to_handlers.
|
||||
|
||||
Returns:
|
||||
See redir_to_handlers
|
||||
"""
|
||||
attributes["appname"] = utils.prettify_name(attributes["appname"])
|
||||
return formats["shot_icon"] % attributes
|
||||
|
||||
|
||||
# vim:filetype=python
|
||||
@@ -1,146 +0,0 @@
|
||||
#!/usr/bin/env --split-string=python -u
|
||||
"""Script for logging desktop notifications in the form of YUCK literal.
|
||||
|
||||
General idea is to have a file log the notifications in the form of YUCK
|
||||
literal containing a widget structure which will then be concatenated into
|
||||
a box widget to take a list-like structure.
|
||||
|
||||
The said structure needs to be re-rendered whenever the log file notices
|
||||
a change. Like, deleting an entry or, adding an entry or, editing an entry.
|
||||
|
||||
Note, if you still have not guessed already, if you make any changes to the
|
||||
log file then the list of notifications will be re-rendered again.
|
||||
"""
|
||||
|
||||
# Authored By dharmx <dharmx.dev@gmail.com> under:
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
# Version 3, 29 June 2007
|
||||
#
|
||||
# Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
# Everyone is permitted to copy and distribute verbatim copies
|
||||
# of this license document, but changing it is not allowed.
|
||||
#
|
||||
# Permissions of this strong copyleft license are conditioned on
|
||||
# making available complete source code of licensed works and
|
||||
# modifications, which include larger works using a licensed work,
|
||||
# under the same license. Copyright and license notices must be
|
||||
# preserved. Contributors provide an express grant of patent rights.
|
||||
#
|
||||
# Read the complete license here:
|
||||
# <https://github.com/dharmx/vile/blob/main/LICENSE.txt>
|
||||
|
||||
# WARN: This script is under active development and is subject to frequent changes.
|
||||
# WARN: Currently this script is quite inefficient. So, you have been warned.
|
||||
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import cache
|
||||
import handlers
|
||||
import utils
|
||||
|
||||
# presetted notification format strings i.e. they are just utility items that help reducing the size of code
|
||||
# another reason is convenience i.e. it would be easier to have all the formats in one place and defining them
|
||||
# would be less cumbersome.
|
||||
FORMATS = {
|
||||
"spotifyd": "(_cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"ncspot": "(_cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"Spotify": "(_cardimage :class 'Spotify-rectangle' :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"shot_icon": "(_cardimage :class 'shot-image-bord-dashed' :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"shot": "(_cardscr :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :delete '%(DELETE)s' :open '%(OPEN)s' :summary '%(summary)s' :image '%(iconpath)s' :image_height 250 :image_width 100 :urgency '%(URGENCY)s' :close '繁' :timestamp '%(TIMESTAMP)s')",
|
||||
"default": "(_cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"notify-send": "(_cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon './assets/browser.png' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"empty": "(box :class 'disclose-empty-box' :height 750 :orientation 'vertical' :space-evenly false (image :class 'disclose-empty-banner' :valign 'end' :vexpand true :path './assets/wedding-bells.png' :image-width 250 :image-height 250) (label :vexpand true :valign 'start' :wrap true :class 'disclose-empty-label' :text '%(QUOTE)s'))",
|
||||
"brightness": "(_cardprog :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s' :progress '%(progress)s')",
|
||||
"todo": "(_cardradial :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :appname '%(appname)s' :progress %(PERC)s :thickness 20.0 :total %(TOTAL)s :done %(DONE)s :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')",
|
||||
"volume": "(_cardprog :identity ':::###::::XXXWWW%(id)s===::' :close_action './src/shell/combine.bash rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '繁' :image_height 100 :image_width 100 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s' :progress '%(progress)s')"
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
# load only notification related options from the config JSON
|
||||
config = json.loads(
|
||||
pathlib.PosixPath(
|
||||
os.path.expandvars("$XDG_CONFIG_HOME/eww/ewwrc")
|
||||
).read_text()
|
||||
)["notify"]
|
||||
|
||||
# kind of like the sliding window algorithm i.e. will pop the notifications if this number is reached
|
||||
HISTORY_LIMIT = config["limit"]
|
||||
# file path where the notifications will be saved
|
||||
CACHE_PATH = os.path.expandvars(config["cache_path"])
|
||||
# directory path where the notifications will be saved
|
||||
# WARN: Do not edit this; Only edit this if you know what you are doing!
|
||||
CACHE_DIR = os.path.dirname(CACHE_PATH)
|
||||
# file path where the quotes are stored
|
||||
QUOTE_PATH = os.path.expandvars(config["quote_path"])
|
||||
# fallback if the quote DB has no quotes in them
|
||||
DEFAULT_QUOTE = config["default_quote"]
|
||||
# Watcher interval; Reflects how fast disclose will be rendered.
|
||||
INTERVAL = config["interval"]
|
||||
|
||||
# handle IndexError
|
||||
if len(sys.argv) < 2:
|
||||
sys.argv = ("dummy", "dummy")
|
||||
match sys.argv[1]:
|
||||
case "subscribe":
|
||||
utils.create_parents_file(CACHE_PATH) # mkdir --parents
|
||||
utils.create_parents_file(QUOTE_PATH) # mkdir --parents
|
||||
utils.watcher(
|
||||
CACHE_PATH,
|
||||
lambda contents: sys.stdout.write(
|
||||
"(box :spacing 20 :orientation 'vertical' :space-evenly false " +
|
||||
# handle empty and display fallback
|
||||
contents.replace("\n", " ") + ")\n"
|
||||
if contents.strip()
|
||||
else (
|
||||
(FORMATS["empty"] + "\n")
|
||||
% {"QUOTE": utils.get_rand_quote(QUOTE_PATH, DEFAULT_QUOTE)}
|
||||
)
|
||||
),
|
||||
INTERVAL,
|
||||
)
|
||||
case "rmid":
|
||||
# grep based off the notification id and then remove that YUCK literal entry from the log file
|
||||
utils.file_matched_index_rm(
|
||||
CACHE_PATH, f":identity ':::###::::XXXWWW{sys.argv[2]}===::'"
|
||||
)
|
||||
case "stats":
|
||||
sys.stdout.write(
|
||||
json.dumps(
|
||||
utils.parse_and_print_stats(
|
||||
pathlib.PosixPath(CACHE_PATH).read_text()
|
||||
)
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
case "rm":
|
||||
utils.file_rm_line(CACHE_PATH, int(sys.argv[2]))
|
||||
case "quote":
|
||||
sys.stdout.write(utils.get_rand_quote(QUOTE_PATH, DEFAULT_QUOTE))
|
||||
case "cls":
|
||||
pathlib.PosixPath(CACHE_PATH).write_text("")
|
||||
case "init":
|
||||
def master_callback(details: dict):
|
||||
r"""Callback function that handles fetching and logging the notification details.
|
||||
|
||||
Arguments:
|
||||
details: the JSON that should have a similar structure to the following:
|
||||
{
|
||||
"summary": "Hello",
|
||||
"body": "Who's there?",
|
||||
"id": 16444442,
|
||||
"urgency": "LOW"
|
||||
}
|
||||
"""
|
||||
details["TIMESTAMP_FORMAT"] = config["timestamp"]
|
||||
if not config["excluded_appnames"] or details["appname"] not in config["excluded_appnames"]:
|
||||
saved_path = handlers.redir_to_handlers(FORMATS, details)
|
||||
# actual point where the notification is being logged.
|
||||
utils.file_add_line(CACHE_PATH, saved_path, HISTORY_LIMIT)
|
||||
|
||||
# start eavesdropping on the org.freedesktop.Notifications interface and log the notification info
|
||||
cache.Eavesdropper(master_callback, CACHE_DIR).eavesdrop()
|
||||
|
||||
# vim:filetype=python
|
||||
@@ -1,463 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Utility module. Shared across almost all of the python scripts / modules."""
|
||||
|
||||
# Authored By dharmx <dharmx.dev@gmail.com> under:
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
# Version 3, 29 June 2007
|
||||
#
|
||||
# Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
# Everyone is permitted to copy and distribute verbatim copies
|
||||
# of this license document, but changing it is not allowed.
|
||||
#
|
||||
# Permissions of this strong copyleft license are conditioned on
|
||||
# making available complete source code of licensed works and
|
||||
# modifications, which include larger works using a licensed work,
|
||||
# under the same license. Copyright and license notices must be
|
||||
# preserved. Contributors provide an express grant of patent rights.
|
||||
#
|
||||
# Read the complete license here:
|
||||
# <https://github.com/dharmx/vile/blob/main/LICENSE.txt>
|
||||
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import typing
|
||||
import unicodedata
|
||||
|
||||
from wand.image import COLORSPACE_TYPES, Image
|
||||
from html.parser import HTMLParser
|
||||
from io import StringIO
|
||||
|
||||
import dbus
|
||||
import gi
|
||||
|
||||
# supress GIO warnings
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("GdkPixbuf", "2.0")
|
||||
|
||||
import requests
|
||||
from gi.repository import GdkPixbuf, Gio, GLib, Gtk
|
||||
|
||||
|
||||
class PangoStripper(HTMLParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.reset()
|
||||
self.strict = False
|
||||
self.convert_charrefs = True
|
||||
self.text = StringIO()
|
||||
|
||||
def handle_data(self, d):
|
||||
self.text.write(d)
|
||||
|
||||
def get_data(self):
|
||||
return self.text.getvalue()
|
||||
|
||||
|
||||
def contains_pango(string: str) -> bool:
|
||||
"""Checks if a string contains HTML tags or not. Since a pango string has to have at least two tags
|
||||
|
||||
Arguments:
|
||||
string: The Pango markup format string.
|
||||
|
||||
Returns:
|
||||
A bool value i.e. whether a has pango markup or not.
|
||||
"""
|
||||
return any(item in string for item in ["<span>", "</a>", "</span>"])
|
||||
|
||||
|
||||
def strip_pango_tags(pango: str) -> str:
|
||||
"""Removes HTML tags like <span>, <span align='left'>, etc.
|
||||
|
||||
Arguments:
|
||||
pango: The Pango format string that needs to be stripped of its tags.
|
||||
|
||||
Returns:
|
||||
A str without the tags.
|
||||
"""
|
||||
stripper = PangoStripper()
|
||||
stripper.feed(pango)
|
||||
return stripper.get_data()
|
||||
|
||||
|
||||
def create_parents_file(file_path: str):
|
||||
'''A replication of [ -f "$FILE_PATH" ] || mkdir --parents "$FILE_PATH"
|
||||
|
||||
Arguments:
|
||||
file_path: The location of the file.
|
||||
'''
|
||||
pathlib.PosixPath(os.path.dirname(file_path)).mkdir(
|
||||
parents=True, exist_ok=True)
|
||||
pathlib.PosixPath(file_path).touch(exist_ok=True)
|
||||
|
||||
|
||||
def watcher(file_path: str, callback: typing.Callable, interval: int, initial: bool = True):
|
||||
"""A file watcher function that executes a callback function if any change is made to a file.
|
||||
|
||||
Arguments:
|
||||
file_path: The file location that needs to be monitored.
|
||||
callback: A callback function that would be invoked when any change is noticed.
|
||||
interval: The time rate at which the file will be checked for changes.
|
||||
initial: Initially execute the callback once before entering the watch loop.
|
||||
"""
|
||||
try:
|
||||
old = pathlib.PosixPath(file_path).read_text()
|
||||
if initial:
|
||||
callback(old)
|
||||
while not time.sleep(interval):
|
||||
new = pathlib.PosixPath(file_path).read_text()
|
||||
if new != old:
|
||||
callback(new)
|
||||
old = new
|
||||
except KeyboardInterrupt:
|
||||
sys.stdout.write("Closed.\n")
|
||||
except FileNotFoundError:
|
||||
sys.stderr.write("The path does not exist!\n")
|
||||
except Exception as excep:
|
||||
sys.stderr.write(f"{excep}\n")
|
||||
|
||||
|
||||
def get_rand_quote(file_path: str, default_quote: str) -> str:
|
||||
"""Get a random quote or, rather a random line.
|
||||
|
||||
Arguments:
|
||||
file_path: The location of the file.
|
||||
default_quote: Fallback quote if the file doesn't exist r, is empty.
|
||||
"""
|
||||
loaded_quotes: str = pathlib.PosixPath(file_path).read_text().strip()
|
||||
return random.choice(loaded_quotes.splitlines()) if loaded_quotes else default_quote
|
||||
|
||||
|
||||
def file_matched_index_rm(file_path: str, pattern: str):
|
||||
"""Removes line from a file if a pattern matches that line.
|
||||
|
||||
Arguments:
|
||||
file_path: The location of the file.
|
||||
pattern: Pattern to match the line that needs to be removed.
|
||||
"""
|
||||
posix_file_path = pathlib.PosixPath(file_path)
|
||||
lines = posix_file_path.read_text().splitlines()
|
||||
|
||||
# skip the line if pattern matches
|
||||
rm_index_lines = [
|
||||
lines[index]
|
||||
for index in range(len(lines))
|
||||
if not re.search(pattern, lines[index])
|
||||
]
|
||||
|
||||
if len(lines) != len(rm_index_lines):
|
||||
posix_file_path.write_text("\n".join(rm_index_lines))
|
||||
|
||||
|
||||
def file_rm_line(file_path: str, position: int | bool | range = True):
|
||||
"""Ranged line removal, indexed line removal and stack-like (pop / reverse pop) line removal.
|
||||
|
||||
Arguments:
|
||||
file_path: The location of the file.
|
||||
position: If the value is of type:
|
||||
range: then that the lines starting from that range will be removed.
|
||||
int: then that specific file line number will be removed.
|
||||
bool: True will pop and False will pop from the end.
|
||||
"""
|
||||
file = pathlib.PosixPath(file_path)
|
||||
match str(type(position)):
|
||||
case "<class 'int'>":
|
||||
file_contents = file.read_text().splitlines()
|
||||
# no need to run the loop if all you need to do is remove the first line
|
||||
if position == 0:
|
||||
file_rm_line(file_path, position=True)
|
||||
return
|
||||
elif position == len(file_contents) - 1:
|
||||
file_rm_line(file_path, position=False)
|
||||
return
|
||||
line_removed_contents = []
|
||||
# skip the line that matches the position value.
|
||||
for index in range(len(file_contents)):
|
||||
if index != position:
|
||||
line_removed_contents += [file_contents[index]]
|
||||
file.write_text("\n".join(line_removed_contents))
|
||||
# filter falsey and truthy and use the actual bool class signature
|
||||
case "<class 'bool'>":
|
||||
file_contents = file.read_text().splitlines()
|
||||
file_contents = file_contents[1:] if position else file_contents[:-1]
|
||||
file.write_text("\n".join(file_contents))
|
||||
case "<class 'range'>":
|
||||
if not position:
|
||||
file.write_text("")
|
||||
return
|
||||
file_contents = file.read_text().splitlines()
|
||||
write_contents = []
|
||||
# keep on skipping the lines if they are in the range.
|
||||
for index in range(len(file_contents)):
|
||||
if index not in position:
|
||||
write_contents += [file_contents[index]]
|
||||
file.write_text("\n".join(write_contents))
|
||||
|
||||
|
||||
def prettify_name(name: str) -> str:
|
||||
"""Transforms 'Hello-World' -> 'Hello World', 'notify-send++lol' -> 'Notify Send Lol'
|
||||
|
||||
Arguments:
|
||||
name: The string that needs to be prettified.
|
||||
|
||||
Returns:
|
||||
A prettified str.
|
||||
"""
|
||||
return " ".join(
|
||||
item.capitalize()
|
||||
for item in name.replace("-", " ").replace("_", " ").split(" ")
|
||||
)
|
||||
|
||||
|
||||
def file_add_line(file_path: str, write_contents: str, limit: int, top: bool = True):
|
||||
"""Functions like sliding window algorithm <https://towardsdev.com/sliding-window-algorithm-145f8e201a64>
|
||||
|
||||
That is after the file hits a certain line limit, lines from the behind will be dropped (older entries)
|
||||
and THEN the newer ones will be entered.
|
||||
|
||||
Arguments:
|
||||
file_path: The location of the file.
|
||||
write_contents: The contents that needs to be written to the file.
|
||||
limit: The line limit.
|
||||
top: Adds new entry at the beginning of the file if True, at the end otherwise.
|
||||
"""
|
||||
file = pathlib.PosixPath(file_path)
|
||||
file_contents = file.read_text().splitlines()
|
||||
if len(file_contents) == limit:
|
||||
file_contents = file_contents[:-1]
|
||||
file_contents = (
|
||||
[write_contents] + file_contents if top else file_contents + [write_contents]
|
||||
)
|
||||
file.write_text("\n".join(file_contents))
|
||||
|
||||
|
||||
def parse_and_print_stats(file_contents: str) -> dict:
|
||||
"""Looks the words CRITICAL, LOW and NORMAL and calcuates its frequency.
|
||||
|
||||
Arguments:
|
||||
file_contents: the string that needs to be calculated for frequency.
|
||||
|
||||
Returns:
|
||||
Individual frequency of the words in dict format.
|
||||
{"CRITICAL": 10.00, "NORMAL": 85.00, "LOW": 5.00}
|
||||
"""
|
||||
stats = {"critical": 0, "low": 0, "normal": 0, "total": 0}
|
||||
|
||||
for line in file_contents.splitlines():
|
||||
if "CRITICAL" in line:
|
||||
stats["critical"] += 1
|
||||
stats["total"] += 1
|
||||
elif "LOW" in line:
|
||||
stats["low"] += 1
|
||||
stats["total"] += 1
|
||||
elif "NORMAL" in line:
|
||||
stats["normal"] += 1
|
||||
stats["total"] += 1
|
||||
|
||||
# handle division / zero
|
||||
stats["critical"] = (
|
||||
stats["critical"] * 100 /
|
||||
stats["total"] if stats["critical"] > 0 else 0
|
||||
)
|
||||
stats["normal"] = (
|
||||
stats["normal"] * 100 / stats["total"] if stats["normal"] > 0 else 0
|
||||
)
|
||||
stats["low"] = stats["low"] * 100 / \
|
||||
stats["total"] if stats["low"] > 0 else 0
|
||||
return stats
|
||||
|
||||
|
||||
def has_non_english_chars(string: str) -> dict:
|
||||
"""Check if there is any CJK / Cyrillic characters in the given string.
|
||||
|
||||
Arguments:
|
||||
string: the string that needs to be checked.
|
||||
|
||||
Returns:
|
||||
A dict containing True / False for keys representing if such characters exist or not.
|
||||
{"CJK": True, "CYR": False} for string value: "おはようございます means Good Morning!"
|
||||
"""
|
||||
return {
|
||||
"CJK": any(unicodedata.category(char) == "Lo" for char in string),
|
||||
"CYR": any(unicodedata.category(char) == "Lu" for char in string),
|
||||
}
|
||||
|
||||
|
||||
def unwrap(value: dbus.Array
|
||||
| dbus.Boolean
|
||||
| dbus.Byte
|
||||
| dbus.Dictionary
|
||||
| dbus.Double
|
||||
| dbus.Int16
|
||||
| dbus.ByteArray
|
||||
| dbus.Int32
|
||||
| dbus.Int64
|
||||
| dbus.Signature
|
||||
| dbus.UInt16
|
||||
| dbus.UInt32
|
||||
| dbus.UInt64
|
||||
| dbus.String) -> str | int | list | tuple | float | dict | bool | bytes:
|
||||
"""Try to trivially translate a dictionary's elements into nice string formatting.
|
||||
|
||||
Arguments:
|
||||
value: A type out of:
|
||||
dbus.Boolean,
|
||||
dbus.Byte,
|
||||
dbus.Dictionary,
|
||||
dbus.Double,
|
||||
dbus.Int16,
|
||||
dbus.ByteArray,
|
||||
dbus.Int32,
|
||||
dbus.Int64,
|
||||
dbus.Signature,
|
||||
dbus.UInt16,
|
||||
dbus.UInt32,
|
||||
dbus.UInt64 and dbus.String
|
||||
|
||||
Returns:
|
||||
A str or int or list or tuple or float or dict or bool or bytes depending on the value.
|
||||
"""
|
||||
if isinstance(value, dbus.ByteArray):
|
||||
return "".join([str(byte) for byte in value])
|
||||
if isinstance(value, (dbus.Array, list, tuple)):
|
||||
return [unwrap(item) for item in value]
|
||||
if isinstance(value, (dbus.Dictionary, dict)):
|
||||
return dict([(unwrap(x), unwrap(y)) for x, y in value.items()])
|
||||
if isinstance(value, (dbus.Signature, dbus.String)):
|
||||
return str(value)
|
||||
if isinstance(value, dbus.Boolean):
|
||||
return bool(value)
|
||||
if isinstance(
|
||||
value,
|
||||
(dbus.Int16, dbus.UInt16, dbus.Int32,
|
||||
dbus.UInt32, dbus.Int64, dbus.UInt64),
|
||||
):
|
||||
return int(value)
|
||||
if isinstance(value, dbus.Byte):
|
||||
return bytes([int(value)])
|
||||
return value
|
||||
|
||||
|
||||
def save_img_byte(px_args: typing.Iterable, save_path: str):
|
||||
"""Converts image data to an image file.
|
||||
|
||||
See <https://docs.gtk.org/gdk-pixbuf/ctor.Pixbuf.new_from_bytes.html> for the whole description.
|
||||
|
||||
Arguments:
|
||||
px_args: Should contain an iterable in the following format.
|
||||
[image_width, image_height, rowstride, has_alpha, bits_per_sample, _, image_bytes]
|
||||
save_path: the filepath where this pixbuf should be saved to.
|
||||
"""
|
||||
# https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html
|
||||
# https://specifications.freedesktop.org/notification-spec/latest/ar01s05.html
|
||||
GdkPixbuf.Pixbuf.new_from_bytes(
|
||||
width=px_args[0],
|
||||
height=px_args[1],
|
||||
has_alpha=px_args[3],
|
||||
data=GLib.Bytes(px_args[6]),
|
||||
colorspace=GdkPixbuf.Colorspace.RGB,
|
||||
rowstride=px_args[2],
|
||||
bits_per_sample=px_args[4],
|
||||
).savev(save_path, "png")
|
||||
|
||||
|
||||
def get_gtk_icon_path(icon_name: str, size: int = 128) -> str:
|
||||
"""Returns the icon path by the specified name from your current icon theme.
|
||||
|
||||
Recursively search for lower sizes until 32x32 and
|
||||
return a fallback if the requested path does not exists.
|
||||
|
||||
Arguments:
|
||||
icon_name: Icon name as per the icon naming specification:
|
||||
<https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html>
|
||||
size: the pixel size (widthxheight -> heightxheight -> size) of the requested icon.
|
||||
"""
|
||||
if size < 32:
|
||||
return os.path.expandvars("$XDG_CONFIG_HOME/eww/assets/bell.png")
|
||||
if info := Gtk.IconTheme.get_default().lookup_icon(icon_name, size, 0):
|
||||
return info.get_filename()
|
||||
return get_gtk_icon_path(icon_name, size - 1)
|
||||
|
||||
|
||||
def get_mime_icon_path(mimetype: str, size: int = 32) -> str:
|
||||
"""Gets the default icon path from the current GTK icon theme for the specified mime type.
|
||||
|
||||
Arguments:
|
||||
mimetype: The file type like png, json, etc.
|
||||
size: The of the returned icon
|
||||
|
||||
Returns:
|
||||
A str that is the path to the requested icon.
|
||||
"""
|
||||
icon = Gio.content_type_get_icon(mimetype)
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
if info := theme.choose_icon(icon.get_names(), size, 0):
|
||||
return info.get_filename()
|
||||
|
||||
|
||||
def get_location() -> dict | None:
|
||||
try:
|
||||
response = requests.get('https://api64.ipify.org?format=json').json()
|
||||
ip_address = response["ip"]
|
||||
response = requests.get(f'https://ipapi.co/{ip_address}/json/').json()
|
||||
return {
|
||||
"latitude": response.get("latitude"),
|
||||
"longitude": response.get("longitude"),
|
||||
"city": response.get("city"),
|
||||
"country": response.get("country_name"),
|
||||
"lang": response.get("languages").split(",")[0],
|
||||
}
|
||||
except requests.exceptions.ConnectionError:
|
||||
return None
|
||||
|
||||
|
||||
def auto_locate(cache_dir: str) -> dict | None:
|
||||
cache_posix_path = pathlib.PosixPath(f"{cache_dir}/location.json")
|
||||
if not cache_posix_path.is_file(): # assuming the directory exists
|
||||
fetched_location = get_location()
|
||||
if not fetched_location:
|
||||
return None
|
||||
|
||||
cache_posix_path.write_text(json.dumps(fetched_location))
|
||||
return fetched_location
|
||||
return json.loads(cache_posix_path.read_text())
|
||||
|
||||
|
||||
def fetch_save(link: str, save_path: str, callback: typing.Callable = None) -> bool:
|
||||
try:
|
||||
data = requests.get(link)
|
||||
if data.status_code == 200:
|
||||
metadata = data.json()
|
||||
if callback:
|
||||
metadata = callback(metadata)
|
||||
pathlib.PosixPath(save_path).write_text(json.dumps(metadata))
|
||||
return True
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
return False
|
||||
|
||||
|
||||
def img_dark_bright_col(filepath: str, colors: int = 10) -> tuple:
|
||||
with Image(filename=filepath) as image:
|
||||
image.quantize(
|
||||
number_colors=colors,
|
||||
colorspace_type=COLORSPACE_TYPES[21],
|
||||
dither=True, measure_error=False, treedepth=8)
|
||||
return tuple(
|
||||
"#%02X%02X%02X" % (item.red_int8, item.green_int8, item.blue_int8)
|
||||
for item in image.histogram)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
match sys.argv[1]:
|
||||
case "histogram":
|
||||
image_path = sys.argv[2]
|
||||
histogram_colors = int(sys.argv[3])
|
||||
print(json.dumps(img_dark_bright_col(image_path, histogram_colors)))
|
||||
|
||||
# vim:filetype=python
|
||||
@@ -1,4 +0,0 @@
|
||||
; REAL THINGS
|
||||
(defpoll disclose_stats :interval "5s" "./src/shell/logger.py stats")
|
||||
(deflisten disclose_sub "./src/shell/combine.bash sub")
|
||||
(defpoll disclose_dnd_state :interval "1s" "dunstctl is-paused")
|
||||
@@ -1,29 +0,0 @@
|
||||
(include "./panel/yuck/_env.yuck")
|
||||
|
||||
(include "./panel/yuck/cards/_cardimage.yuck")
|
||||
(include "./panel/yuck/cards/_cardprog.yuck")
|
||||
(include "./panel/yuck/cards/_cardscr.yuck")
|
||||
(include "./panel/yuck/cards/_cardradial.yuck")
|
||||
|
||||
(include "./panel/yuck/_stats.yuck")
|
||||
(include "./panel/yuck/_music.yuck")
|
||||
(include "./panel/yuck/_layout.yuck")
|
||||
|
||||
(defwidget closer [window]
|
||||
(eventbox :onclick "eww close ${window} && eww close ${window}-closer"))
|
||||
|
||||
(defwindow panel-closer
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%" :height "100%")
|
||||
:stacking "fg"
|
||||
:focusable false
|
||||
(closer :window "panel"))
|
||||
|
||||
(defwindow panel :stacking "fg"
|
||||
:windowtype "normal"
|
||||
:wm-ignore true
|
||||
:monitor 0
|
||||
:geometry (geometry :width "26%" :height "100%" :anchor "right bottom")
|
||||
(panel_layout))
|
||||
|
||||
;; vim:ft=yuck
|
||||
@@ -1,25 +0,0 @@
|
||||
(defwidget panel_layout []
|
||||
(box :class "disclose-closer"
|
||||
(box :orientation "vertical"
|
||||
:space-evenly false
|
||||
:class "disclose-layout-box"
|
||||
(box :space-evenly false
|
||||
:class "disclose-headers"
|
||||
:spacing 6
|
||||
(label :text "Notifications"
|
||||
:class "disclose-headers-label"
|
||||
:halign "start"
|
||||
:hexpand true))
|
||||
(scroll :hscroll false
|
||||
:vscroll true
|
||||
:vexpand true
|
||||
:hexpand true
|
||||
:class "disclose-scroll"
|
||||
(literal :content disclose_sub))
|
||||
(label :class "disclose-separator" :text "")
|
||||
(wifi)
|
||||
(box :space-evenly false :class "disclose-misc-box"
|
||||
(dstats)
|
||||
(dmusic)))))
|
||||
|
||||
;; vim:filetype=yuck
|
||||
@@ -1,47 +0,0 @@
|
||||
(defwidget _sundialinfo [class halign ?hexpand ?prefix]
|
||||
(label :class "${class}-sundial-label" :halign halign :hexpand hexpand
|
||||
:text "${prefix}${time.hour >= 2 && time.hour <= 4 ? "Early Morning" :
|
||||
time.hour <= 5 ? "Dawn" :
|
||||
time.hour >= 6 && (time.hour <= 8 && time.min <= 59) ? "Morning" :
|
||||
time.hour >= 9 && (time.hour <= 11 && time.min <= 59) ? "Late Morning" :
|
||||
time.hour == 12 && time.min <= 29 ? "Midday" :
|
||||
time.hour >= 12 && time.hour <= 16 ? "Afternoon" :
|
||||
time.hour > 16 && time.hour <= 17 ? "Late Afternoon" :
|
||||
(time.hour >= 17 && time.min <= 1) || (time.hour <= 18 && time.min <= 20) ? "Early Evening" :
|
||||
time.hour >= 18 && time.hour <= 19 ? "Dusk" :
|
||||
time.hour > 19 && time.hour <= 21 ? "Late Evening" :
|
||||
time.hour > 21 ? "Night" : "Midnight"}"))
|
||||
|
||||
(defwidget _profile [path size ?tooltip ?button-class ?image-class ?M ?L ?R]
|
||||
(button :onmiddleclick M
|
||||
:onclick L
|
||||
:onrightclick R
|
||||
:timeout "2s"
|
||||
:tooltip tooltip
|
||||
:class "vertigo-button ${button-class}"
|
||||
(image :path path
|
||||
:image-width size
|
||||
:class "vertigo-image ${image-class}")))
|
||||
|
||||
(defwidget _infobatnolbl [battery status one two three four five six seven charge ?class]
|
||||
(box :class "lumin-battery-box ${class}"
|
||||
:space-evenly false
|
||||
:spacing 8
|
||||
(label :class "lumin-battery-icon ${class}" :text {status == 'Charging' ? charge :
|
||||
battery < 15 ? seven :
|
||||
battery < 30 ? six :
|
||||
battery < 45 ? five :
|
||||
battery < 60 ? four :
|
||||
battery < 75 ? three :
|
||||
battery < 95 ? two : one})))
|
||||
|
||||
(defwidget _infonetnolbl [strength offline excellent good okay slow ?class]
|
||||
(box :class "lumin-network-box ${class}"
|
||||
:space-evenly false
|
||||
:spacing 8
|
||||
(label :class "lumin-network-icon ${class}" :text {strength == "" ? offline :
|
||||
strength < 26 ? slow :
|
||||
strength < 51 ? okay :
|
||||
strength < 76 ? good : excellent})))
|
||||
|
||||
; vim:filetype=yuck
|
||||
@@ -1,38 +0,0 @@
|
||||
(deflisten music :initial '{"title": "", "status": ""}' "./panel/music.sh")
|
||||
(deflisten music_cover "./panel/music.sh cover")
|
||||
|
||||
(defpoll volume :interval "1s" "pamixer --get-volume")
|
||||
(defpoll is_mute :interval "1s" "pamixer --get-mute")
|
||||
|
||||
(defwidget dmusic []
|
||||
(box :vexpand true
|
||||
:hexpand true
|
||||
:class "disclose-music-box"
|
||||
:style "background-image: linear-gradient(to bottom left, rgba(0, 0, 0, 0.7), rgba(30, 33, 40, 0.2)), url(\"${music_cover}\");"
|
||||
:space-evenly false
|
||||
(box :class "disclose-dnd-labels"
|
||||
:hexpand true
|
||||
:orientation "vertical"
|
||||
:space-evenly false
|
||||
(label :halign "start"
|
||||
:wrap true
|
||||
:class "disclose-dnd-header"
|
||||
:text {music.title == '' ? "No music" : music.title})
|
||||
(label :halign "start"
|
||||
:valign "start"
|
||||
:wrap true
|
||||
:class "disclose-dnd-footer"
|
||||
:vexpand true
|
||||
:text {music.artist == 'null' ? "" : music.artist})
|
||||
(box :valign "end" :space-evenly false :class "disclose-dnd-waiting-toggle"
|
||||
(button :onclick "action"
|
||||
:hexpand true
|
||||
:halign "start"
|
||||
:valign "end"
|
||||
:class "play-status accent-txt" {music.status})
|
||||
(box :space-evenly false :class "volume-container"
|
||||
(label :class "volume-icon"
|
||||
:text {is_mute == 'true' ? "婢" : "墳"})
|
||||
(label :class "volume-text" :text "${volume}%"))))))
|
||||
|
||||
;; vim:filetype=yuck
|
||||
@@ -1,64 +0,0 @@
|
||||
; TODO: Update this value when pressing the button.
|
||||
(defpoll bright :interval '1s' "brightnessctl -m | cut -d , -f 4 | head -c -2")
|
||||
|
||||
(defpoll ssid :interval "1s" `nmcli -terse -fields SSID,ACTIVE device wifi | awk --field-separator ':' '{if($2=="yes")print$1}'`)
|
||||
(defpoll bluethooth :interval '1s' "echo info | timeout 1 bluetoothctl | grep 'Name'")
|
||||
(defpoll theme :interval '1s' "cat ~/.local/state/theme")
|
||||
|
||||
|
||||
(defwidget stat_card [icon name percent value colorClass ?click]
|
||||
(button
|
||||
:click click
|
||||
(box
|
||||
:space-evenly false
|
||||
:class "disclose-stats-box"
|
||||
(circular-progress :value percent
|
||||
:thickness 10
|
||||
:class "${colorClass}-txt"
|
||||
(label :halign "center" :class "stats-label ${colorClass}-txt" :text icon))
|
||||
(label :class "stats-separator" :text "")
|
||||
(box :hexpand true :halign "center" :orientation "vertical" :class "disclose-stats-info-box"
|
||||
(label :halign "center" :class "info-value ${colorClass}-txt" :text value)
|
||||
(label :halign "start" :class "info-label" :text name)))))
|
||||
|
||||
(defwidget dstats []
|
||||
(box :space-evenly false
|
||||
:orientation "vertical"
|
||||
:class "disclose-stats"
|
||||
:spacing 10
|
||||
(stat_card
|
||||
:icon ""
|
||||
:name "CPU"
|
||||
:percent 0
|
||||
:value {round(EWW_CPU.avg, 2)}
|
||||
:colorClass "red")
|
||||
(stat_card
|
||||
:icon ""
|
||||
:name "Memory"
|
||||
:percent {EWW_RAM.used_mem_perc}
|
||||
:value "${round(EWW_RAM.used_mem_perc, 0)}%"
|
||||
:colorClass "green")
|
||||
(stat_card
|
||||
:icon {theme == "light" ? "" : ""}
|
||||
:name "Brightness"
|
||||
:percent bright
|
||||
:value "${bright}%"
|
||||
:colorClass "blue"
|
||||
:click "settheme")))
|
||||
|
||||
|
||||
(defwidget wifi []
|
||||
(box
|
||||
:class "module"
|
||||
:spacing 16
|
||||
:style "margin: 8px 16px 16px 16px"
|
||||
(button
|
||||
:class "accent disclose-big-button"
|
||||
:click "nm-connection-editor"
|
||||
"直 : ${ssid != "" ? ssid : "Disconnected"}")
|
||||
(button
|
||||
:class "secondary disclose-big-button"
|
||||
:onclick "blueberry"
|
||||
" : ${bluethooth != "" ? bluethooth : 'Disconnected'}")))
|
||||
|
||||
;; vim:filetype=yuck
|
||||
@@ -1,85 +0,0 @@
|
||||
(defwidget _cardimage [
|
||||
summary
|
||||
body
|
||||
|
||||
?limit_summary
|
||||
?limit_body
|
||||
|
||||
appname
|
||||
timestamp
|
||||
urgency
|
||||
|
||||
icon
|
||||
icon_width
|
||||
icon_height
|
||||
close
|
||||
?close_action
|
||||
|
||||
?style
|
||||
?class
|
||||
|
||||
image
|
||||
image_width
|
||||
image_height
|
||||
]
|
||||
(eventbox :class "disclose-cardimage-eventbox disclose-cardimage-eventbox-${urgency} disclose-cardimage-eventbox-${appname}"
|
||||
(box :orientation "vertical"
|
||||
:space-evenly false
|
||||
:class "disclose-cardimage-container-box disclose-cardimage-container-box-${urgency} disclose-cardimage-container-box-${appname}"
|
||||
(box :class "disclose-cardimage-summary-box disclose-cardimage-summary-box-${urgency} disclose-cardimage-summary-box-${appname}"
|
||||
:space-evenly false
|
||||
:spacing 6
|
||||
|
||||
(box :style "background-image: url('${icon}')"
|
||||
:width icon_width
|
||||
:height icon_height
|
||||
:space-evenly false
|
||||
:class "disclose-cardimage-icon disclose-cardimage-icon-${urgency} disclose-cardimage-icon-${appname}")
|
||||
(label :text appname
|
||||
:hexpand true
|
||||
:halign "start"
|
||||
:class "disclose-cardimage-appname-label disclose-cardimage-appname-label-${urgency} disclose-cardimage-appname-label-${appname}")
|
||||
(button :class "disclose-cardimage-close-button disclose-cardimage-close-button-${urgency} disclose-cardimage-close-button-${appname}"
|
||||
:onclick close_action
|
||||
:timeout "2s"
|
||||
(label :text close
|
||||
:class "disclose-cardimage-close-icon disclose-cardimage-close-icon-${urgency} disclose-cardimage-close-icon-${appname}")))
|
||||
|
||||
(box :class "disclose-cardimage-separator disclose-cardimage-separator-${urgency} disclose-cardimage-separator-${appname}"
|
||||
:space-evenly false)
|
||||
|
||||
(box :class "disclose-cardimage-body-box disclose-cardimage-body-box-${urgency} disclose-cardimage-body-box-${appname}"
|
||||
:space-evenly false
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:class "disclose-cardimage-image-box disclose-cardimage-image-box-${urgency} disclose-cardimage-image-box-${appname}"
|
||||
(box :style "background-image: url('${image}');${style}"
|
||||
:hexpand false
|
||||
:vexpand false
|
||||
:width image_width
|
||||
:height image_height
|
||||
:space-evenly false
|
||||
:class "disclose-cardimage-image disclose-cardimage-image-${urgency} disclose-cardimage-image-${appname} ${class}"))
|
||||
|
||||
(box :hexpand true
|
||||
:vexpand true
|
||||
:valign "center"
|
||||
:orientation "vertical"
|
||||
:spacing 5
|
||||
:class "disclose-cardimage-body-outer disclose-cardimage-body-outer-${urgency} disclose-cardimage-body-outer-${appname}"
|
||||
:space-evenly false
|
||||
(label :text summary
|
||||
:limit-width {limit_summary != "" ? limit_summary : 25}
|
||||
:halign "start"
|
||||
:class "disclose-cardimage-summary-label disclose-cardimage-summary-label-${urgency} disclose-cardimage-summary-label-${appname}")
|
||||
(label :text body
|
||||
:halign "start"
|
||||
:limit-width {limit_body != "" ? limit_body : 110}
|
||||
:xalign 0.0
|
||||
:wrap true
|
||||
:class "disclose-cardimage-body-label disclose-cardimage-body-${urgency} disclose-cardimage-body-${appname}")
|
||||
(label :text timestamp
|
||||
:halign "end"
|
||||
:class "disclose-cardimage-timestamp disclose-cardimage-timestamp-${urgency} disclose-cardimage-timestamp-${appname}"))))))
|
||||
|
||||
;; vim:ft=yuck
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user