Restructure, prepare launcher
This commit is contained in:
parent
3060c2b06e
commit
709af40296
2
config/ags/bar/.gitignore
vendored
Normal file
2
config/ags/bar/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
@girs/
|
10
config/ags/bar/app.ts
Normal file
10
config/ags/bar/app.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { App } from "astal/gtk3"
|
||||||
|
import style from "./style.scss"
|
||||||
|
import Bar from "./widget/Bar"
|
||||||
|
|
||||||
|
App.start({
|
||||||
|
css: style,
|
||||||
|
main() {
|
||||||
|
App.get_monitors().map(Bar)
|
||||||
|
},
|
||||||
|
})
|
21
config/ags/bar/env.d.ts
vendored
Normal file
21
config/ags/bar/env.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
declare const SRC: string
|
||||||
|
|
||||||
|
declare module "inline:*" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.scss" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.blp" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.css" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
6
config/ags/bar/package.json
Normal file
6
config/ags/bar/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "astal-shell",
|
||||||
|
"dependencies": {
|
||||||
|
"astal": "/usr/share/astal/gjs"
|
||||||
|
}
|
||||||
|
}
|
20
config/ags/bar/style.scss
Normal file
20
config/ags/bar/style.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
|
$fg-color: #{"@theme_fg_color"};
|
||||||
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
|
||||||
|
window.Bar {
|
||||||
|
background: transparent;
|
||||||
|
color: $fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
>centerbox {
|
||||||
|
background: $bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
}
|
14
config/ags/bar/tsconfig.json
Normal file
14
config/ags/bar/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
// "checkJs": true,
|
||||||
|
// "allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "astal/gtk3",
|
||||||
|
}
|
||||||
|
}
|
31
config/ags/bar/widget/Bar.tsx
Normal file
31
config/ags/bar/widget/Bar.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { App, Astal, Gtk, Gdk } from "astal/gtk3"
|
||||||
|
import { Variable } from "astal"
|
||||||
|
|
||||||
|
const time = Variable("").poll(1000, "date")
|
||||||
|
|
||||||
|
export default function Bar(gdkmonitor: Gdk.Monitor) {
|
||||||
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
|
||||||
|
|
||||||
|
return <window
|
||||||
|
className="Bar"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
anchor={TOP | LEFT | RIGHT}
|
||||||
|
application={App}>
|
||||||
|
<centerbox>
|
||||||
|
<button
|
||||||
|
onClicked="echo hello"
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
>
|
||||||
|
Welcome to AGS!
|
||||||
|
</button>
|
||||||
|
<box />
|
||||||
|
<button
|
||||||
|
onClicked={() => print("hello")}
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
>
|
||||||
|
<label label={time()} />
|
||||||
|
</button>
|
||||||
|
</centerbox>
|
||||||
|
</window>
|
||||||
|
}
|
2
config/ags/launcher/.gitignore
vendored
Normal file
2
config/ags/launcher/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
@girs/
|
10
config/ags/launcher/app.ts
Normal file
10
config/ags/launcher/app.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { App } from "astal/gtk3"
|
||||||
|
import style from "./style.scss"
|
||||||
|
import Bar from "./widget/Bar"
|
||||||
|
|
||||||
|
App.start({
|
||||||
|
css: style,
|
||||||
|
main() {
|
||||||
|
App.get_monitors().map(Bar)
|
||||||
|
},
|
||||||
|
})
|
62
config/ags/launcher/definitions/components.d.ts
vendored
Normal file
62
config/ags/launcher/definitions/components.d.ts
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* dotfiles - components.d.ts
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/22/2025, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { UIComponent, ResultElement } from "./rendering";
|
||||||
|
|
||||||
|
|
||||||
|
export interface App extends ResultElement {
|
||||||
|
/**
|
||||||
|
* The app start command that will be executed
|
||||||
|
*/
|
||||||
|
command: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Finish
|
||||||
|
export interface DictionaryEntry extends ResultElement {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Finish
|
||||||
|
export interface CMDOutput extends ResultElement {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Finish
|
||||||
|
export interface Calculation extends ResultElement {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ************* *
|
||||||
|
* UI Components *
|
||||||
|
* ************* */
|
||||||
|
|
||||||
|
export interface LargeUIComponent extends UIComponent {
|
||||||
|
/**
|
||||||
|
* The number of items to display per line. Image size will automatically be scaled
|
||||||
|
* based on width
|
||||||
|
*/
|
||||||
|
itemsPerLine: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediumUIComponent extends UIComponent {}
|
||||||
|
|
||||||
|
export interface ListUIComponent extends UIComponent {}
|
||||||
|
|
||||||
|
export interface DictionaryUIComponent extends UIComponent {
|
||||||
|
elements: DictionaryEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CMDOutputUIComponent extends UIComponent {
|
||||||
|
elements: CMDOutput[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CalculationUIComponent extends UIComponent {
|
||||||
|
elements: Calculation[];
|
||||||
|
}
|
0
config/ags/launcher/definitions/job.d.ts
vendored
Normal file
0
config/ags/launcher/definitions/job.d.ts
vendored
Normal file
60
config/ags/launcher/definitions/rendering.d.ts
vendored
Normal file
60
config/ags/launcher/definitions/rendering.d.ts
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* dotfiles - rendering.d.ts
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/22/2025, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface UIComponent {
|
||||||
|
/**
|
||||||
|
* The title of the component (like a category name), shown above small divider line
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResultElement list, made up of all elements that should be shown
|
||||||
|
*/
|
||||||
|
elements: ResultElement[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose how many elements to show before truncating (will expand when command is run)
|
||||||
|
*/
|
||||||
|
truncate: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The weight of the element (determines order)
|
||||||
|
*/
|
||||||
|
weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ResultElement {
|
||||||
|
/**
|
||||||
|
* The name of the result element
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the image to be displayed in the UI
|
||||||
|
*/
|
||||||
|
img: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The weight of the element (determines order)
|
||||||
|
*/
|
||||||
|
weight: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action to be executed
|
||||||
|
*/
|
||||||
|
action: Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The font size of the text (optional)
|
||||||
|
*/
|
||||||
|
fontSize: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = '';
|
21
config/ags/launcher/env.d.ts
vendored
Normal file
21
config/ags/launcher/env.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
declare const SRC: string
|
||||||
|
|
||||||
|
declare module "inline:*" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.scss" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.blp" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.css" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
6
config/ags/launcher/package.json
Normal file
6
config/ags/launcher/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "astal-shell",
|
||||||
|
"dependencies": {
|
||||||
|
"astal": "/usr/share/astal/gjs"
|
||||||
|
}
|
||||||
|
}
|
20
config/ags/launcher/style.scss
Normal file
20
config/ags/launcher/style.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
|
$fg-color: #{"@theme_fg_color"};
|
||||||
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
|
||||||
|
window.Bar {
|
||||||
|
background: transparent;
|
||||||
|
color: $fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
>centerbox {
|
||||||
|
background: $bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
}
|
14
config/ags/launcher/tsconfig.json
Normal file
14
config/ags/launcher/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
// "checkJs": true,
|
||||||
|
// "allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "astal/gtk3",
|
||||||
|
}
|
||||||
|
}
|
0
config/ags/launcher/ui/AppLauncher.tsx
Normal file
0
config/ags/launcher/ui/AppLauncher.tsx
Normal file
0
config/ags/launcher/ui/components/calc.tsx
Normal file
0
config/ags/launcher/ui/components/calc.tsx
Normal file
0
config/ags/launcher/ui/components/cmd.tsx
Normal file
0
config/ags/launcher/ui/components/cmd.tsx
Normal file
0
config/ags/launcher/ui/components/dict.tsx
Normal file
0
config/ags/launcher/ui/components/dict.tsx
Normal file
0
config/ags/launcher/ui/components/large.tsx
Normal file
0
config/ags/launcher/ui/components/large.tsx
Normal file
0
config/ags/launcher/ui/components/list.tsx
Normal file
0
config/ags/launcher/ui/components/list.tsx
Normal file
0
config/ags/launcher/ui/components/medium.tsx
Normal file
0
config/ags/launcher/ui/components/medium.tsx
Normal file
10
config/ags/launcher/util/file.ts
Normal file
10
config/ags/launcher/util/file.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* dotfiles - file.ts
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/22/2025, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileAsync, writeFileAsync, monitorFile } from "astal";
|
0
config/ags/launcher/util/fzf.ts
Normal file
0
config/ags/launcher/util/fzf.ts
Normal file
0
config/ags/launcher/util/search.ts
Normal file
0
config/ags/launcher/util/search.ts
Normal file
43
config/ags/launcher/util/subprocessRunner.ts
Normal file
43
config/ags/launcher/util/subprocessRunner.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* dotfiles - subprocessRunner.ts
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/22/2025, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { subprocess, execAsync, Process } from "astal/process";
|
||||||
|
|
||||||
|
// TODO: Get cwd and the likes to then use that to run JavaScript files with node / python with python, etc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a subprocess. If you simply want to run a command that doesn't need continuous updates
|
||||||
|
* run executeCommand instead.
|
||||||
|
* @param cmd - The command to be run
|
||||||
|
* @param onOut - Calback function for stdout of the subprocess
|
||||||
|
* @param onErr - [TODO:description]
|
||||||
|
* @returns [TODO:return]
|
||||||
|
*/
|
||||||
|
const startSubProcess = (
|
||||||
|
cmd: string | string[],
|
||||||
|
onOut: (stdout: string) => void,
|
||||||
|
onErr: (stderr: string) => void | undefined,
|
||||||
|
): Process => {
|
||||||
|
return subprocess( cmd, onOut, onErr );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a command. If you need continuous updates, run startSubProcess instead
|
||||||
|
* @param cmd - The command to be run. Either a string or an array of strings
|
||||||
|
* @returns A Promise resolving to stdout of the command
|
||||||
|
*/
|
||||||
|
const executeCommand = (cmd: string | string[]): Promise<string> => {
|
||||||
|
return execAsync( cmd );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
startSubProcess,
|
||||||
|
executeCommand
|
||||||
|
}
|
2
config/ags/notifications/.gitignore
vendored
Normal file
2
config/ags/notifications/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
@girs/
|
29
config/ags/notifications/app.ts
Normal file
29
config/ags/notifications/app.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { App } from "astal/gtk3"
|
||||||
|
import style from "./style.scss"
|
||||||
|
|
||||||
|
import not from "./handler"
|
||||||
|
|
||||||
|
App.start({
|
||||||
|
instanceName: "notifier",
|
||||||
|
css: style,
|
||||||
|
main() {
|
||||||
|
not.startNotificationHandler( 0, App.get_monitors()[0] )
|
||||||
|
},
|
||||||
|
requestHandler(request, res) {
|
||||||
|
if ( request == 'show' ) {
|
||||||
|
not.openNotificationMenu( 0 );
|
||||||
|
res( 'Showing all open notifications' );
|
||||||
|
} else if ( request == 'hide' ) {
|
||||||
|
not.closeNotificationMenu( 0 );
|
||||||
|
res( 'Hid all notifications' );
|
||||||
|
} else if ( request == 'clear' ) {
|
||||||
|
not.clearAllNotifications( 0 );
|
||||||
|
res( 'Cleared all notifications' );
|
||||||
|
} else if ( request == 'clear-newest' ) {
|
||||||
|
not.clearNewestNotifications( 0 );
|
||||||
|
res( 'Cleared newest notification' );
|
||||||
|
} else {
|
||||||
|
res( 'Unknown command!' );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
21
config/ags/notifications/env.d.ts
vendored
Normal file
21
config/ags/notifications/env.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
declare const SRC: string
|
||||||
|
|
||||||
|
declare module "inline:*" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.scss" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.blp" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.css" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
184
config/ags/notifications/handler.tsx
Normal file
184
config/ags/notifications/handler.tsx
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* dotfiles - handler.ts
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/21/2025, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Astal, Gtk, Gdk } from "astal/gtk3"
|
||||||
|
import Notifd from "gi://AstalNotifd";
|
||||||
|
import Notification from "./notifications/notifications";
|
||||||
|
import { type Subscribable } from "astal/binding";
|
||||||
|
import { Variable, bind, timeout } from "astal"
|
||||||
|
|
||||||
|
// Config
|
||||||
|
const TIMEOUT_DELAY = 5000;
|
||||||
|
|
||||||
|
class Notifier implements Subscribable {
|
||||||
|
private display: Map<number, Gtk.Widget> = new Map();
|
||||||
|
private notifications: Map<number, Notifd.Notification> = new Map();
|
||||||
|
|
||||||
|
private notifd: Notifd.Notifd;
|
||||||
|
private subscriberData: Variable<Gtk.Widget[]> = Variable( [] );
|
||||||
|
private instanceID: number;
|
||||||
|
private menuOpen: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the notifier
|
||||||
|
*/
|
||||||
|
constructor( id: number ) {
|
||||||
|
this.instanceID = id;
|
||||||
|
this.menuOpen = false;
|
||||||
|
this.notifd = Notifd.get_default();
|
||||||
|
this.notifd.ignoreTimeout = true;
|
||||||
|
|
||||||
|
this.notifd.connect( 'notified', ( _, id ) => {
|
||||||
|
this.add( id );
|
||||||
|
} );
|
||||||
|
|
||||||
|
this.notifd.connect( 'resolved', ( _, id ) => {
|
||||||
|
this.hide( id );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private notify () {
|
||||||
|
this.subscriberData.set( [ ...this.display.values() ].reverse() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private add ( id: number ) {
|
||||||
|
this.notifications.set( id, this.notifd.get_notification( id )! );
|
||||||
|
this.show( id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an element on screen
|
||||||
|
* @param id - The id of the element to be shown
|
||||||
|
*/
|
||||||
|
private show ( id: number ) {
|
||||||
|
this.set( id, Notification( {
|
||||||
|
notification: this.notifications.get( id )!,
|
||||||
|
onHoverLost: () => this.hide( id ),
|
||||||
|
setup: () => timeout( TIMEOUT_DELAY, () => {
|
||||||
|
if ( !this.menuOpen ) {
|
||||||
|
this.hide( id );
|
||||||
|
}
|
||||||
|
} ),
|
||||||
|
id: id,
|
||||||
|
delete: deleteHelper,
|
||||||
|
instanceID: this.instanceID
|
||||||
|
} ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a selected widget to be shown
|
||||||
|
* @param id - The id of the element to be referenced for later
|
||||||
|
* @param widget - A GTK widget instance
|
||||||
|
*/
|
||||||
|
private set ( id: number, widget: Gtk.Widget ) {
|
||||||
|
this.display.get( id )?.destroy();
|
||||||
|
this.display.set( id, widget );
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide, not delete notification (= send to notification centre)
|
||||||
|
* @param id - The id of the notification to hide
|
||||||
|
*/
|
||||||
|
private hide ( id: number ) {
|
||||||
|
this.display.get( id )?.destroy();
|
||||||
|
this.display.delete( id );
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a notification (from notification centre too)
|
||||||
|
* @param id - The id of the notification to hide
|
||||||
|
*/
|
||||||
|
delete ( id: number ) {
|
||||||
|
this.hide( id );
|
||||||
|
this.notifications.get( id )?.dismiss();
|
||||||
|
this.notifications.delete( id );
|
||||||
|
}
|
||||||
|
|
||||||
|
openNotificationMenu () {
|
||||||
|
// Show all notifications that have not been cleared
|
||||||
|
this.menuOpen = true;
|
||||||
|
this.notifications.forEach( ( _, id ) => {
|
||||||
|
this.show( id );
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
hideNotifications () {
|
||||||
|
this.menuOpen = false;
|
||||||
|
this.notifications.forEach( ( _, id ) => {
|
||||||
|
this.hide( id );
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAllNotifications () {
|
||||||
|
this.menuOpen = false;
|
||||||
|
this.notifications.forEach( ( _, id ) => {
|
||||||
|
this.delete( id );
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
clearNewestNotification () {
|
||||||
|
this.delete( [ ...this.notifications.keys() ][0] );
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(callback: (value: unknown) => void): () => void {
|
||||||
|
return this.subscriberData.subscribe( callback );
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.subscriberData.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifiers: Map<number, Notifier> = new Map();
|
||||||
|
const deleteHelper = ( id: number, instanceID: number ) => {
|
||||||
|
notifiers.get( instanceID )?.delete( id );
|
||||||
|
}
|
||||||
|
|
||||||
|
const openNotificationMenu = ( id: number ) => {
|
||||||
|
notifiers.get( id )?.openNotificationMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeNotificationMenu = ( id: number ) => {
|
||||||
|
notifiers.get( id )?.hideNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearAllNotifications = ( id: number ) => {
|
||||||
|
notifiers.get( id )?.clearAllNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearNewestNotifications = ( id: number ) => {
|
||||||
|
notifiers.get( id )?.clearNewestNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
const startNotificationHandler = (id: number, gdkmonitor: Gdk.Monitor) => {
|
||||||
|
const { TOP, RIGHT } = Astal.WindowAnchor
|
||||||
|
const notifier: Notifier = new Notifier( id );
|
||||||
|
notifiers.set( id, notifier );
|
||||||
|
|
||||||
|
return <window
|
||||||
|
className="NotificationHandler"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
anchor={TOP | RIGHT}>
|
||||||
|
<box vertical noImplicitDestroy>
|
||||||
|
{bind(notifier)}
|
||||||
|
</box>
|
||||||
|
</window>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
startNotificationHandler,
|
||||||
|
openNotificationMenu,
|
||||||
|
closeNotificationMenu,
|
||||||
|
clearAllNotifications,
|
||||||
|
clearNewestNotifications
|
||||||
|
}
|
125
config/ags/notifications/notifications/notifications.scss
Normal file
125
config/ags/notifications/notifications/notifications.scss
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
@use "sass:string";
|
||||||
|
|
||||||
|
@function gtkalpha($c, $a) {
|
||||||
|
@return string.unquote("alpha(#{$c},#{$a})");
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
|
$fg-color: #{"@theme_fg_color"};
|
||||||
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
$error: red;
|
||||||
|
|
||||||
|
window.NotificationPopups {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventbox.Notification {
|
||||||
|
|
||||||
|
&:first-child>box {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child>box {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventboxes can not take margins so we style its inner box instead
|
||||||
|
>box {
|
||||||
|
min-width: 400px;
|
||||||
|
border-radius: 13px;
|
||||||
|
background-color: $bg-color;
|
||||||
|
margin: .5rem 1rem .5rem 1rem;
|
||||||
|
box-shadow: 2px 3px 8px 0 gtkalpha(black, .4);
|
||||||
|
border: 1pt solid gtkalpha($fg-color, .03);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.critical>box {
|
||||||
|
border: 1pt solid gtkalpha($error, .4);
|
||||||
|
|
||||||
|
.header {
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
color: gtkalpha($error, .8);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
color: gtkalpha($error, .6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: .5rem;
|
||||||
|
color: gtkalpha($fg-color, 0.5);
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
margin: 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
margin-right: .3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: .4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
margin: 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: .2rem;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
separator {
|
||||||
|
margin: 0 .4rem;
|
||||||
|
background-color: gtkalpha($fg-color, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 1rem;
|
||||||
|
margin-top: .5rem;
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: $fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
color: gtkalpha($fg-color, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
border: 1px solid gtkalpha($fg-color, .02);
|
||||||
|
margin-right: .5rem;
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: 100px;
|
||||||
|
min-height: 100px;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin: 1rem;
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0 .3rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
config/ags/notifications/notifications/notifications.tsx
Normal file
112
config/ags/notifications/notifications/notifications.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// From astal examples
|
||||||
|
|
||||||
|
import { GLib } from "astal"
|
||||||
|
import { Gtk, Astal } from "astal/gtk3"
|
||||||
|
import { type EventBox } from "astal/gtk3/widget"
|
||||||
|
import Notifd from "gi://AstalNotifd"
|
||||||
|
|
||||||
|
const isIcon = (icon: string) =>
|
||||||
|
!!Astal.Icon.lookup_icon(icon)
|
||||||
|
|
||||||
|
const fileExists = (path: string) =>
|
||||||
|
GLib.file_test(path, GLib.FileTest.EXISTS)
|
||||||
|
|
||||||
|
const time = (time: number, format = "%H:%M") => GLib.DateTime
|
||||||
|
.new_from_unix_local(time)
|
||||||
|
.format(format)!
|
||||||
|
|
||||||
|
const urgency = (n: Notifd.Notification) => {
|
||||||
|
const { LOW, NORMAL, CRITICAL } = Notifd.Urgency
|
||||||
|
// match operator when?
|
||||||
|
switch (n.urgency) {
|
||||||
|
case LOW: return "low"
|
||||||
|
case CRITICAL: return "critical"
|
||||||
|
case NORMAL:
|
||||||
|
default: return "normal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
delete( id: number, instanceID: number ): void
|
||||||
|
setup(self: EventBox): void
|
||||||
|
onHoverLost(self: EventBox): void
|
||||||
|
notification: Notifd.Notification
|
||||||
|
id: number
|
||||||
|
instanceID: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Notification(props: Props) {
|
||||||
|
const { notification: n, onHoverLost, setup, id: id, delete: del, instanceID: instance } = props
|
||||||
|
const { START, CENTER, END } = Gtk.Align
|
||||||
|
|
||||||
|
return <eventbox
|
||||||
|
className={`Notification ${urgency(n)}`}
|
||||||
|
setup={setup}
|
||||||
|
onHoverLost={onHoverLost}>
|
||||||
|
<box vertical>
|
||||||
|
<box className="header">
|
||||||
|
{(n.appIcon || n.desktopEntry) && <icon
|
||||||
|
className="app-icon"
|
||||||
|
visible={Boolean(n.appIcon || n.desktopEntry)}
|
||||||
|
icon={n.appIcon || n.desktopEntry}
|
||||||
|
/>}
|
||||||
|
<label
|
||||||
|
className="app-name"
|
||||||
|
halign={START}
|
||||||
|
truncate
|
||||||
|
label={n.appName || "Unknown"}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="time"
|
||||||
|
hexpand
|
||||||
|
halign={END}
|
||||||
|
label={time(n.time)}
|
||||||
|
/>
|
||||||
|
<button onClicked={() => del( id, instance )}>
|
||||||
|
<icon icon="window-close-symbolic" />
|
||||||
|
</button>
|
||||||
|
</box>
|
||||||
|
<Gtk.Separator visible />
|
||||||
|
<box className="content">
|
||||||
|
{n.image && fileExists(n.image) && <box
|
||||||
|
valign={START}
|
||||||
|
className="image"
|
||||||
|
css={`background-image: url('${n.image}')`}
|
||||||
|
/>}
|
||||||
|
{n.image && isIcon(n.image) && <box
|
||||||
|
expand={false}
|
||||||
|
valign={START}
|
||||||
|
className="icon-image">
|
||||||
|
<icon icon={n.image} expand halign={CENTER} valign={CENTER} />
|
||||||
|
</box>}
|
||||||
|
<box vertical>
|
||||||
|
<label
|
||||||
|
className="summary"
|
||||||
|
halign={START}
|
||||||
|
xalign={0}
|
||||||
|
label={n.summary}
|
||||||
|
truncate
|
||||||
|
/>
|
||||||
|
{n.body && <label
|
||||||
|
className="body"
|
||||||
|
wrap
|
||||||
|
useMarkup
|
||||||
|
halign={START}
|
||||||
|
xalign={0}
|
||||||
|
justifyFill
|
||||||
|
label={n.body}
|
||||||
|
/>}
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
{n.get_actions().length > 0 && <box className="actions">
|
||||||
|
{n.get_actions().map(({ label, id }) => (
|
||||||
|
<button
|
||||||
|
hexpand
|
||||||
|
onClicked={() => n.invoke(id)}>
|
||||||
|
<label label={label} halign={CENTER} hexpand />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</box>}
|
||||||
|
</box>
|
||||||
|
</eventbox>
|
||||||
|
}
|
6
config/ags/notifications/package.json
Normal file
6
config/ags/notifications/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "astal-shell",
|
||||||
|
"dependencies": {
|
||||||
|
"astal": "/usr/share/astal/gjs"
|
||||||
|
}
|
||||||
|
}
|
2
config/ags/notifications/style.scss
Normal file
2
config/ags/notifications/style.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Import notification box style
|
||||||
|
@use "./notifications/notifications.scss"
|
14
config/ags/notifications/tsconfig.json
Normal file
14
config/ags/notifications/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
// "checkJs": true,
|
||||||
|
// "allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "astal/gtk3",
|
||||||
|
}
|
||||||
|
}
|
2
config/ags/quickactions/.gitignore
vendored
Normal file
2
config/ags/quickactions/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
@girs/
|
10
config/ags/quickactions/app.ts
Normal file
10
config/ags/quickactions/app.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { App } from "astal/gtk3"
|
||||||
|
import style from "./style.scss"
|
||||||
|
import Bar from "./widget/Bar"
|
||||||
|
|
||||||
|
App.start({
|
||||||
|
css: style,
|
||||||
|
main() {
|
||||||
|
App.get_monitors().map(Bar)
|
||||||
|
},
|
||||||
|
})
|
21
config/ags/quickactions/env.d.ts
vendored
Normal file
21
config/ags/quickactions/env.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
declare const SRC: string
|
||||||
|
|
||||||
|
declare module "inline:*" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.scss" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.blp" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.css" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
6
config/ags/quickactions/package.json
Normal file
6
config/ags/quickactions/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "astal-shell",
|
||||||
|
"dependencies": {
|
||||||
|
"astal": "/usr/share/astal/gjs"
|
||||||
|
}
|
||||||
|
}
|
20
config/ags/quickactions/style.scss
Normal file
20
config/ags/quickactions/style.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
|
$fg-color: #{"@theme_fg_color"};
|
||||||
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
|
||||||
|
window.Bar {
|
||||||
|
background: transparent;
|
||||||
|
color: $fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
>centerbox {
|
||||||
|
background: $bg-color;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
}
|
14
config/ags/quickactions/tsconfig.json
Normal file
14
config/ags/quickactions/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
// "checkJs": true,
|
||||||
|
// "allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "astal/gtk3",
|
||||||
|
}
|
||||||
|
}
|
31
config/ags/quickactions/widget/Bar.tsx
Normal file
31
config/ags/quickactions/widget/Bar.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { App, Astal, Gtk, Gdk } from "astal/gtk3"
|
||||||
|
import { Variable } from "astal"
|
||||||
|
|
||||||
|
const time = Variable("").poll(1000, "date")
|
||||||
|
|
||||||
|
export default function Bar(gdkmonitor: Gdk.Monitor) {
|
||||||
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
|
||||||
|
|
||||||
|
return <window
|
||||||
|
className="Bar"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
anchor={TOP | LEFT | RIGHT}
|
||||||
|
application={App}>
|
||||||
|
<centerbox>
|
||||||
|
<button
|
||||||
|
onClicked="echo hello"
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
>
|
||||||
|
Welcome to AGS!
|
||||||
|
</button>
|
||||||
|
<box />
|
||||||
|
<button
|
||||||
|
onClicked={() => print("hello")}
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
>
|
||||||
|
<label label={time()} />
|
||||||
|
</button>
|
||||||
|
</centerbox>
|
||||||
|
</window>
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user