diff --git a/flake.lock b/flake.lock
index 1620d37d..4389b016 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,5 +1,24 @@
{
"nodes": {
+ "ags": {
+ "inputs": {
+ "nixpkgs": "nixpkgs",
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1721306136,
+ "narHash": "sha256-VKPsIGf3/a+RONBipx4lEE4LXG2sdMNkWQu22LNQItg=",
+ "owner": "Aylur",
+ "repo": "ags",
+ "rev": "344ea72cd3b8d4911f362fec34bce7d8fb37028c",
+ "type": "github"
+ },
+ "original": {
+ "owner": "Aylur",
+ "repo": "ags",
+ "type": "github"
+ }
+ },
"cachix": {
"locked": {
"lastModified": 1635350005,
@@ -70,11 +89,11 @@
]
},
"locked": {
- "lastModified": 1721305797,
- "narHash": "sha256-6ZXDxEqq11tHivRGKSFuOkrpfvoSuNbGMSjXFIQx2cE=",
+ "lastModified": 1721431043,
+ "narHash": "sha256-xmJwGXg/y0k40JETVhWGLOMtWmeOb/k2WWMm1yhLbYE=",
"owner": "nix-community",
"repo": "flake-firefox-nightly",
- "rev": "96dbe6709c070c0329e7fee3385784ade6b07703",
+ "rev": "6ba26a0f3d9d69e9910bab0bed0f5d994089a196",
"type": "github"
},
"original": {
@@ -172,7 +191,7 @@
},
"flake-utils": {
"inputs": {
- "systems": "systems"
+ "systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
@@ -210,7 +229,7 @@
},
"flake-utils_3": {
"inputs": {
- "systems": "systems_2"
+ "systems": "systems_3"
},
"locked": {
"lastModified": 1710146030,
@@ -228,7 +247,7 @@
},
"flake-utils_4": {
"inputs": {
- "systems": "systems_3"
+ "systems": "systems_4"
},
"locked": {
"lastModified": 1701680307,
@@ -320,11 +339,11 @@
},
"hardware": {
"locked": {
- "lastModified": 1721323232,
- "narHash": "sha256-T4K2P8FayLshsIkY04i7M8qs0aQY5ugf8HsghqGONCc=",
+ "lastModified": 1721413321,
+ "narHash": "sha256-0GdiQScDceUrVGbxYpV819LHesK3szHOhJ09e6sgES4=",
"owner": "nixos",
"repo": "nixos-hardware",
- "rev": "ede1f14cc21e811a605f16c6b69bd3e8425383d7",
+ "rev": "ab165a8a6cd12781d76fe9cbccb9e975d0fb634f",
"type": "github"
},
"original": {
@@ -380,7 +399,7 @@
"flake-compat": "flake-compat_2",
"flake-parts": "flake-parts",
"flake-utils": "flake-utils_3",
- "nixpkgs": "nixpkgs",
+ "nixpkgs": "nixpkgs_2",
"pre-commit-hooks-nix": "pre-commit-hooks-nix",
"rust-overlay": "rust-overlay"
},
@@ -457,16 +476,16 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1717794163,
- "narHash": "sha256-Ch6ZpFPVvi7Bb6gmmuufpTEFkXqa43pC94XMfU5FEt0=",
+ "lastModified": 1718714799,
+ "narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "121f68ed7c6c32de5a8ce91a08ef25713d1c4755",
+ "rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e",
"type": "github"
},
"original": {
"owner": "NixOS",
- "ref": "nixos-unstable-small",
+ "ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
@@ -520,11 +539,27 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1721138476,
- "narHash": "sha256-+W5eZOhhemLQxelojLxETfbFbc19NWawsXBlapYpqIA=",
+ "lastModified": 1717794163,
+ "narHash": "sha256-Ch6ZpFPVvi7Bb6gmmuufpTEFkXqa43pC94XMfU5FEt0=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "121f68ed7c6c32de5a8ce91a08ef25713d1c4755",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable-small",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_3": {
+ "locked": {
+ "lastModified": 1721379653,
+ "narHash": "sha256-8MUgifkJ7lkZs3u99UDZMB4kbOxvMEXQZ31FO3SopZ0=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "ad0b5eed1b6031efaed382844806550c3dcb4206",
+ "rev": "1d9c2c9b3e71b9ee663d11c5d298727dace8d374",
"type": "github"
},
"original": {
@@ -548,11 +583,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
- "lastModified": 1721224205,
- "narHash": "sha256-W0+l7HNzZfEmIx/1Yp3Tow/GZILVjrMjWfTt1Mos7mI=",
+ "lastModified": 1721421992,
+ "narHash": "sha256-4Mu+O2/S5XU1D8HLTU53pv20hEH6aiTkUqjLHowYdY8=",
"owner": "nix-community",
"repo": "nixvim",
- "rev": "55bda0cc3b230255d271e5eef82f3279dae9f859",
+ "rev": "e80a8874accd45cac90616a7b5faa49c5a68e6b9",
"type": "github"
},
"original": {
@@ -590,15 +625,16 @@
},
"root": {
"inputs": {
+ "ags": "ags",
"firefox-nightly": "firefox-nightly",
"flake-utils": "flake-utils_2",
"hardware": "hardware",
"home-manager": "home-manager",
"lanzaboote": "lanzaboote",
- "nixpkgs": "nixpkgs_2",
+ "nixpkgs": "nixpkgs_3",
"nixpkgs-stable": "nixpkgs-stable_2",
"nixvim": "nixvim",
- "systems": "systems_4"
+ "systems": "systems_5"
}
},
"rust-overlay": {
@@ -628,16 +664,16 @@
},
"systems": {
"locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
- "repo": "default",
+ "repo": "default-linux",
"type": "github"
}
},
@@ -672,6 +708,21 @@
}
},
"systems_4": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "systems_5": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
diff --git a/flake.nix b/flake.nix
index 6341392c..11738e30 100644
--- a/flake.nix
+++ b/flake.nix
@@ -39,7 +39,7 @@
# url = "github:nix-community/fenix";
# inputs.nixpkgs.follows = "nixpkgs";
#};
- # ags.url = "github:Aylur/ags";
+ ags.url = "github:Aylur/ags";
};
outputs = {
self,
diff --git a/home/core/gui/ags/README.md b/home/core/gui/ags/README.md
deleted file mode 100644
index 86a276ce..00000000
--- a/home/core/gui/ags/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-# Starter Config
-
-if suggestions don't work, first make sure
-you have TypeScript LSP working in your editor
-
-if you do not want typechecking only suggestions
-
-```json
-// tsconfig.json
-"checkJs": false
-```
-
-types are symlinked to:
-/home/cnst/.local/share/com.github.Aylur.ags/types
diff --git a/home/core/gui/ags/assets/icons/ai-openai-symbolic.svg b/home/core/gui/ags/assets/icons/ai-openai-symbolic.svg
new file mode 100644
index 00000000..c9ee0b32
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/ai-openai-symbolic.svg
@@ -0,0 +1 @@
+openai-symbolic.svg
\ No newline at end of file
diff --git a/home/core/gui/ags/assets/icons/ai-oxygen-symbolic.svg b/home/core/gui/ags/assets/icons/ai-oxygen-symbolic.svg
new file mode 100644
index 00000000..5e1cc193
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/ai-oxygen-symbolic.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/ai-zukijourney.png b/home/core/gui/ags/assets/icons/ai-zukijourney.png
new file mode 100644
index 00000000..917335e7
Binary files /dev/null and b/home/core/gui/ags/assets/icons/ai-zukijourney.png differ
diff --git a/home/core/gui/ags/assets/icons/arch-symbolic.svg b/home/core/gui/ags/assets/icons/arch-symbolic.svg
new file mode 100644
index 00000000..7de9094e
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/arch-symbolic.svg
@@ -0,0 +1,113 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/icons/cachyos-symbolic.svg b/home/core/gui/ags/assets/icons/cachyos-symbolic.svg
new file mode 100644
index 00000000..4a9db19a
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/cachyos-symbolic.svg
@@ -0,0 +1,318 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/cloudflare-dns-symbolic.svg b/home/core/gui/ags/assets/icons/cloudflare-dns-symbolic.svg
new file mode 100644
index 00000000..bd48d3c9
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/cloudflare-dns-symbolic.svg
@@ -0,0 +1,10 @@
+
diff --git a/home/core/gui/ags/assets/icons/crosshair-symbolic.svg b/home/core/gui/ags/assets/icons/crosshair-symbolic.svg
new file mode 100644
index 00000000..22967493
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/crosshair-symbolic.svg
@@ -0,0 +1,65 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/icons/debian-symbolic.svg b/home/core/gui/ags/assets/icons/debian-symbolic.svg
new file mode 100644
index 00000000..252f8533
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/debian-symbolic.svg
@@ -0,0 +1,91 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/icons/endeavouros-symbolic.svg b/home/core/gui/ags/assets/icons/endeavouros-symbolic.svg
new file mode 100644
index 00000000..3be4cc40
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/endeavouros-symbolic.svg
@@ -0,0 +1,96 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/fedora-symbolic.svg b/home/core/gui/ags/assets/icons/fedora-symbolic.svg
new file mode 100644
index 00000000..1a4e8c87
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/fedora-symbolic.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/flatpak-symbolic.svg b/home/core/gui/ags/assets/icons/flatpak-symbolic.svg
new file mode 100644
index 00000000..0c2bf628
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/flatpak-symbolic.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/github-symbolic.svg b/home/core/gui/ags/assets/icons/github-symbolic.svg
new file mode 100644
index 00000000..c1c9f19c
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/github-symbolic.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/google-gemini-symbolic.svg b/home/core/gui/ags/assets/icons/google-gemini-symbolic.svg
new file mode 100644
index 00000000..9de741be
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/google-gemini-symbolic.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/icons/linux-symbolic.svg b/home/core/gui/ags/assets/icons/linux-symbolic.svg
new file mode 100644
index 00000000..63f9c7e5
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/linux-symbolic.svg
@@ -0,0 +1,113 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/icons/nixos-symbolic.svg b/home/core/gui/ags/assets/icons/nixos-symbolic.svg
new file mode 100644
index 00000000..b697b0d1
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/nixos-symbolic.svg
@@ -0,0 +1,77 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/ollama-symbolic.svg b/home/core/gui/ags/assets/icons/ollama-symbolic.svg
new file mode 100644
index 00000000..01454815
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/ollama-symbolic.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/openai-symbolic.svg b/home/core/gui/ags/assets/icons/openai-symbolic.svg
new file mode 100644
index 00000000..8ffc912a
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/openai-symbolic.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/openrouter-symbolic.svg b/home/core/gui/ags/assets/icons/openrouter-symbolic.svg
new file mode 100644
index 00000000..32aaaf50
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/openrouter-symbolic.svg
@@ -0,0 +1,39 @@
+
+
diff --git a/home/core/gui/ags/assets/icons/ubuntu-symbolic.svg b/home/core/gui/ags/assets/icons/ubuntu-symbolic.svg
new file mode 100644
index 00000000..07746c9f
--- /dev/null
+++ b/home/core/gui/ags/assets/icons/ubuntu-symbolic.svg
@@ -0,0 +1,85 @@
+
+
+
+
diff --git a/home/core/gui/ags/assets/themes/sourceviewtheme-dark-monokai-license.txt b/home/core/gui/ags/assets/themes/sourceviewtheme-dark-monokai-license.txt
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/home/core/gui/ags/assets/themes/sourceviewtheme-dark-monokai-license.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/home/core/gui/ags/assets/themes/sourceviewtheme-light.xml b/home/core/gui/ags/assets/themes/sourceviewtheme-light.xml
new file mode 100644
index 00000000..bf39bfb5
--- /dev/null
+++ b/home/core/gui/ags/assets/themes/sourceviewtheme-light.xml
@@ -0,0 +1,95 @@
+
+
+ end_4
+ <_description>Catppuccin port but very random
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/home/core/gui/ags/assets/themes/sourceviewtheme.xml b/home/core/gui/ags/assets/themes/sourceviewtheme.xml
new file mode 100644
index 00000000..2f1b903f
--- /dev/null
+++ b/home/core/gui/ags/assets/themes/sourceviewtheme.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+ Leo Iannacone
+ <_description>Based on SublimeText Monokai Extended - Generated with tm2gtksw2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/home/core/gui/ags/config.js b/home/core/gui/ags/config.js
index 12da5cba..49c5ebed 100644
--- a/home/core/gui/ags/config.js
+++ b/home/core/gui/ags/config.js
@@ -1,45 +1,92 @@
-import { App, Audio, Notifications, Utils } from "./imports.js";
-import Bar from "./windows/bar/main.js";
-import Music from "./windows/music/main.js";
-import NotificationPopup from "./windows/notifications/popups.js";
-import Osd from "./windows/osd/main.js";
-import SystemMenu from "./windows/system-menu/main.js";
+"use strict";
+// Import
+import Gdk from "gi://Gdk";
+import GLib from "gi://GLib";
+import App from "resource:///com/github/Aylur/ags/app.js";
+import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
+// Stuff
+import userOptions from "./modules/.configuration/user_options.js";
+import {
+ firstRunWelcome,
+ startBatteryWarningService,
+} from "./services/messages.js";
+import { startAutoDarkModeService } from "./services/darkmode.js";
+// Widgets
+import {
+ Bar,
+ BarCornerTopleft,
+ BarCornerTopright,
+} from "./modules/bar/main.js";
+import Cheatsheet from "./modules/cheatsheet/main.js";
+// import DesktopBackground from './modules/desktopbackground/main.js';
+import Dock from "./modules/dock/main.js";
+import Corner from "./modules/screencorners/main.js";
+import Crosshair from "./modules/crosshair/main.js";
+import Indicator from "./modules/indicators/main.js";
+import Osk from "./modules/onscreenkeyboard/main.js";
+import Overview from "./modules/overview/main.js";
+import Session from "./modules/session/main.js";
+import SideLeft from "./modules/sideleft/main.js";
+import SideRight from "./modules/sideright/main.js";
+import { COMPILED_STYLE_DIR } from "./init.js";
-const scss = App.configDir + "/style.scss";
-const css = App.configDir + "/style.css";
+const range = (length, start = 1) =>
+ Array.from({ length }, (_, i) => i + start);
+function forMonitors(widget) {
+ const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
+ return range(n, 0).map(widget).flat(1);
+}
+function forMonitorsAsync(widget) {
+ const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
+ return range(n, 0).forEach((n) => widget(n).catch(print));
+}
-Utils.exec(`sass ${scss} ${css}`);
+// Start stuff
+handleStyles(true);
+startAutoDarkModeService().catch(print);
+firstRunWelcome().catch(print);
+startBatteryWarningService().catch(print);
-App.connect("config-parsed", () => print("config parsed"));
+const Windows = () => [
+ // forMonitors(DesktopBackground),
+ forMonitors(Crosshair),
+ Overview(),
+ forMonitors(Indicator),
+ forMonitors(Cheatsheet),
+ SideLeft(),
+ SideRight(),
+ forMonitors(Osk),
+ forMonitors(Session),
+ ...(userOptions.dock.enabled ? [forMonitors(Dock)] : []),
+ ...(userOptions.appearance.fakeScreenRounding !== 0
+ ? [
+ forMonitors((id) => Corner(id, "top left", true)),
+ forMonitors((id) => Corner(id, "top right", true)),
+ ]
+ : []),
+ forMonitors((id) =>
+ Corner(id, "bottom left", userOptions.appearance.fakeScreenRounding !== 0),
+ ),
+ forMonitors((id) =>
+ Corner(id, "bottom right", userOptions.appearance.fakeScreenRounding !== 0),
+ ),
+ forMonitors(BarCornerTopleft),
+ forMonitors(BarCornerTopright),
+];
+
+const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully
+const closeWindowDelays = {}; // For animations
+for (let i = 0; i < (Gdk.Display.get_default()?.get_n_monitors() || 1); i++) {
+ closeWindowDelays[`osk${i}`] = CLOSE_ANIM_TIME;
+}
App.config({
- style: css,
- closeWindowDelay: {
- "system-menu": 200,
- },
+ css: `${COMPILED_STYLE_DIR}/style.css`,
+ stackTraceOnError: true,
+ closeWindowDelay: closeWindowDelays,
+ windows: Windows().flat(1),
});
-Notifications.popupTimeout = 5000;
-Notifications.forceTimeout = false;
-Notifications.cacheActions = true;
-Audio.maxStreamVolume = 1;
-
-function reloadCss() {
- console.log("scss change detected");
- Utils.exec(`sass ${scss} ${css}`);
- App.resetCss();
- App.applyCss(css);
-}
-
-Utils.monitorFile(`${App.configDir}/style`, reloadCss);
-
-/**
- * @param {import("types/widgets/window.js").Window[]} windows
- */
-function addWindows(windows) {
- windows.forEach((win) => App.addWindow(win));
-}
-
-addWindows([Bar(), Music(), Osd(), SystemMenu(), NotificationPopup()]);
-
-export {};
+// Stuff that don't need to be toggled. And they're async so ugh...
+forMonitorsAsync(Bar);
+// Bar().catch(print); // Use this to debug the bar. Single monitor only.
diff --git a/home/core/gui/ags/config_overviewOnly.js b/home/core/gui/ags/config_overviewOnly.js
new file mode 100644
index 00000000..0be1ebe8
--- /dev/null
+++ b/home/core/gui/ags/config_overviewOnly.js
@@ -0,0 +1,22 @@
+// Want only the overview from my config? this is what you're looking for!
+// Remember to install: `dart-sass`, `ags`, `material-symbols`, and `xorg-xrandr`
+// To launch this, run the following
+// ags -c ~/.config/ags/config_overviewOnly.js
+// To toggle the overview, run:
+// ags -t overview
+// You might wanna add that as a keybind (in hyprland.conf)
+// bind = Super, Tab, exec, ags -t overview
+
+// Import
+import App from "resource:///com/github/Aylur/ags/app.js";
+// Widgets
+import Overview from "./modules/overview/main.js";
+import { COMPILED_STYLE_DIR } from "./init.js";
+
+handleStyles(true);
+
+App.config({
+ css: `${COMPILED_STYLE_DIR}/style.css`,
+ stackTraceOnError: true,
+ windows: [Overview()],
+});
diff --git a/home/core/gui/ags/default.nix b/home/core/gui/ags/default.nix
deleted file mode 100644
index 6dab7971..00000000
--- a/home/core/gui/ags/default.nix
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- inputs,
- pkgs,
- lib,
- config,
- ...
-}: let
- requiredDeps = with pkgs; [
- bash
- coreutils
- dart-sass
- gawk
- imagemagick
- procps
- ripgrep
- util-linux
- ];
-
- guiDeps = with pkgs; [
- gnome.gnome-control-center
- mission-center
- overskride
- wlogout
- ];
-
- dependencies = requiredDeps ++ guiDeps;
-
- cfg = config.programs.ags;
-in {
- imports = [
- inputs.ags.homeManagerModules.default
- ];
-
- programs.ags.enable = true;
-
- systemd.user.services.ags = {
- Unit = {
- Description = "Aylur's Gtk Shell";
- PartOf = [
- "tray.target"
- "graphical-session.target"
- ];
- };
- Service = {
- Environment = "PATH=/run/wrappers/bin:${lib.makeBinPath dependencies}";
- ExecStart = "${cfg.package}/bin/ags";
- Restart = "on-failure";
- };
- Install.WantedBy = ["graphical-session.target"];
- };
-}
diff --git a/home/core/gui/ags/dprint.json b/home/core/gui/ags/dprint.json
deleted file mode 100644
index d0cc30e4..00000000
--- a/home/core/gui/ags/dprint.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "typescript": {},
- "excludes": ["**/node_modules"],
- "plugins": ["https://plugins.dprint.dev/typescript-0.88.10.wasm"]
-}
diff --git a/home/core/gui/ags/imports.js b/home/core/gui/ags/imports.js
deleted file mode 100644
index e19ed541..00000000
--- a/home/core/gui/ags/imports.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Required components
-import GLib from "gi://GLib";
-import App from "resource:///com/github/Aylur/ags/app.js";
-import Service from "resource:///com/github/Aylur/ags/service.js";
-import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
-import Variable from "resource:///com/github/Aylur/ags/variable.js";
-import Widget from "resource:///com/github/Aylur/ags/widget.js";
-
-// Services
-import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
-import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
-import Bluetooth from "resource:///com/github/Aylur/ags/service/bluetooth.js";
-import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
-import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
-import Network from "resource:///com/github/Aylur/ags/service/network.js";
-import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
-import SystemTray from "resource:///com/github/Aylur/ags/service/systemtray.js";
-
-import Icons from "./utils/icons.js";
-
-export {
- App,
- Audio,
- Battery,
- Bluetooth,
- GLib,
- Hyprland,
- Icons,
- Mpris,
- Network,
- Notifications,
- Service,
- SystemTray,
- Utils,
- Variable,
- Widget,
-};
diff --git a/home/core/gui/ags/init.js b/home/core/gui/ags/init.js
new file mode 100644
index 00000000..a0ca9e56
--- /dev/null
+++ b/home/core/gui/ags/init.js
@@ -0,0 +1,39 @@
+import GLib from "gi://GLib";
+import App from "resource:///com/github/Aylur/ags/app.js";
+import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
+import { darkMode } from "./modules/.miscutils/system.js";
+
+export const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`;
+
+globalThis["handleStyles"] = (resetMusic) => {
+ // Reset
+ Utils.exec(`mkdir -p "${GLib.get_user_state_dir()}/ags/scss"`);
+ if (resetMusic) {
+ Utils.exec(
+ `bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`,
+ ); // reset music styles
+ Utils.exec(
+ `bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss'`,
+ ); // reset music styles
+ }
+ // Generate overrides
+ let lightdark = darkMode.value ? "dark" : "light";
+ Utils.writeFileSync(
+ `@mixin symbolic-icon {
+ -gtk-icon-theme: '${userOptions.icons.symbolicIconTheme[lightdark]}';
+}
+`,
+ `${GLib.get_user_state_dir()}/ags/scss/_lib_mixins_overrides.scss`,
+ );
+ // Compile and apply
+ async function applyStyle() {
+ Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`);
+ Utils.exec(
+ `sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/main.scss" "${COMPILED_STYLE_DIR}/style.css"`,
+ );
+ App.resetCss();
+ App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
+ console.log("[LOG] Styles loaded");
+ }
+ applyStyle().catch(print);
+};
diff --git a/home/core/gui/ags/services/brightness.js b/home/core/gui/ags/services/brightness.js
deleted file mode 100644
index f21c0029..00000000
--- a/home/core/gui/ags/services/brightness.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Service, Utils } from "../imports.js";
-import Gio from "gi://Gio";
-import GLib from "gi://GLib";
-
-const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
-
-class BrightnessService extends Service {
- static {
- Service.register(
- this,
- { "screen-changed": ["float"] },
- { "screen-value": ["float", "rw"] },
- );
- }
-
- #screenValue = 0;
-
- #interface = Utils.exec("sh -c 'ls -w1 /sys/class/backlight | head -1'");
- #path = `/sys/class/backlight/${this.#interface}`;
- #brightness = `${this.#path}/brightness`;
-
- #max = Number(Utils.readFile(`${this.#path}/max_brightness`));
-
- get screen_value() {
- return this.#screenValue;
- }
-
- set screen_value(percent) {
- percent = clamp(percent, 0, 1);
- this.#screenValue = percent;
-
- const file = Gio.File.new_for_path(this.#brightness);
- const string = `${Math.round(percent * this.#max)}`;
-
- new Promise((resolve, _) => {
- file.replace_contents_bytes_async(
- new GLib.Bytes(new TextEncoder().encode(string)),
- null,
- false,
- Gio.FileCreateFlags.NONE,
- null,
- (self, res) => {
- try {
- self.replace_contents_finish(res);
- resolve(self);
- } catch (error) {
- print(error);
- }
- },
- );
- });
- }
-
- constructor() {
- super();
-
- this.#updateScreenValue();
- Utils.monitorFile(this.#brightness, () => this.#onChange());
- }
-
- #updateScreenValue() {
- this.#screenValue = Number(Utils.readFile(this.#brightness)) / this.#max;
- }
-
- #onChange() {
- this.#updateScreenValue();
-
- this.notify("screen-value");
- this.emit("screen-changed", this.#screenValue);
- }
-
- connectWidget(widget, callback, event = "screen-changed") {
- super.connectWidget(widget, callback, event);
- }
-}
-
-const service = new BrightnessService();
-
-export default service;
diff --git a/home/core/gui/ags/services/osd.js b/home/core/gui/ags/services/osd.js
deleted file mode 100644
index 01ee3f69..00000000
--- a/home/core/gui/ags/services/osd.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Audio, Icons, Service, Utils } from "../imports.js";
-import { audioIcon, micIcon } from "../utils/audio.js";
-import Brightness from "./brightness.js";
-
-class Indicator extends Service {
- static {
- Service.register(this, {
- popup: ["jsobject", "boolean"],
- });
- }
-
- #delay = 1500;
- #count = 0;
-
- popup(value, label, icon, showProgress = true) {
- const props = {
- value,
- label,
- icon,
- showProgress,
- };
- this.emit("popup", props, true);
- this.#count++;
- Utils.timeout(this.#delay, () => {
- this.#count--;
-
- if (this.#count === 0) {
- this.emit("popup", props, false);
- }
- });
- }
-
- bluetooth(addr) {
- this.popup(0, getBluetoothDevice(addr), Icons.bluetooth.active, false);
- }
-
- speaker() {
- this.popup(
- Audio.speaker?.volume ?? 0,
- Audio.speaker?.description ?? "",
- audioIcon(),
- );
- }
-
- mic() {
- this.popup(
- Audio.microphone?.volume || 0,
- Audio.microphone?.description || "",
- micIcon(),
- );
- }
-
- display() {
- // brightness is async, so lets wait a bit
- Utils.timeout(10, () =>
- this.popup(Brightness.screenValue, "Brightness", Icons.brightness),
- );
- }
-
- connect(event = "popup", callback) {
- return super.connect(event, callback);
- }
-}
-
-export default new Indicator();
diff --git a/home/core/gui/ags/style.scss b/home/core/gui/ags/style.scss
deleted file mode 100644
index 8a656e3a..00000000
--- a/home/core/gui/ags/style.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-/* style aggregator */
-
-/* setup */
-@import "style/prelude";
-@import "style/colors";
-@import "style/general";
-
-/* modules & windows */
-@import "style/bar";
-@import "style/music";
-@import "style/osd";
-@import "style/system-menu";
-@import "style/notifications";
diff --git a/home/core/gui/ags/style/bar.scss b/home/core/gui/ags/style/bar.scss
deleted file mode 100644
index b43264c9..00000000
--- a/home/core/gui/ags/style/bar.scss
+++ /dev/null
@@ -1,134 +0,0 @@
-.bar {
- background: $bar-bg;
- min-height: 32px;
-
- .module {
- margin: 0 0.5rem;
- }
-}
-
-/* workspaces */
-.bar .workspaces {
- margin: 0.2rem 0.5rem;
-
- button {
- background: rgba(0, 0, 0, 0.3);
- border-radius: 2rem;
- margin: 0.7rem 0.2rem;
- min-width: 1rem;
- transition: 100ms linear;
- }
-
- .focused {
- min-width: 2rem;
- }
-
- .monitor0 {
- background: $red;
- }
-
- .monitor1 {
- background: $yellow;
- }
-
- .monitor2 {
- background: $green;
- }
-
- .monitor3 {
- background: $blue;
- }
-}
-
-/* music */
-.bar .music {
- &>box {
- @include animate;
- border-radius: $round2;
- margin: 0.4rem;
- }
-
- &.active>box {
- background: $surface;
- }
-
- .cover {
- background-size: cover;
- background-position: center;
- border-radius: 50%;
- min-width: 2rem;
- min-height: 2rem;
- }
-}
-
-/* tray */
-.tray button {
- @include button;
- background: none;
- margin: 0.5rem 0;
-
- &:not(:last-child) {
- margin-right: 0.3rem;
- }
-
- &.active {
- background: $surface;
- }
-}
-
-menu {
- background: $tooltip-bg;
- border-radius: $round;
-
- separator {
- background-color: $surface;
- }
-
- menuitem {
- @include button;
- border-radius: 0;
- padding: 0.4rem 0.7rem;
-
- &:first-child {
- border-radius: $round $round 0 0;
- }
-
- &:last-child {
- border-radius: 0 0 $round $round;
- }
-
- &:only-child {
- border-radius: $round;
- }
- }
-}
-
-/* system-info */
-.bar .system-info {
- margin: 0 0.2rem;
-
- &>box {
- margin: 0 0.3rem;
- }
-
- .type {
- font-size: 0.55rem;
- font-weight: 300;
- }
-
- .value {
- font-size: 0.8rem;
- }
-}
-
-.system-menu-toggler {
- box {
- @include animate;
- margin: 0.4rem 0;
- border-radius: $round2;
- }
-
- &.active box {
- background: $surface;
- }
-}
diff --git a/home/core/gui/ags/style/colors-dark.scss b/home/core/gui/ags/style/colors-dark.scss
deleted file mode 100644
index b27bb7f7..00000000
--- a/home/core/gui/ags/style/colors-dark.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-$red: #f38ba8;
-$yellow: #f9e2af;
-$green: #a6e3a1;
-$blue: #89b4fa;
-
-$tooltip-bg: #000000;
-$fg: #ffffff;
-$bg: rgba(0, 0, 0, 0.3);
-$bar-bg: rgba(0, 0, 0, 0.21);
-
-$surface: rgba(255, 255, 255, 0.15);
-$overlay: rgba(255, 255, 255, 0.7);
-
-$accent: #9d5b7a;
-
-/* buttons */
-$button-enabled: $accent;
-$button-enabled-hover: adjust_color($button-enabled, $lightness: -10%);
-
-$button-disabled: $surface;
-$button-disabled-hover: adjust_color($button-disabled, $alpha: +0.1);
-
-* {
- text-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
-}
-
-@mixin border {
- // border: 1px solid rgba(0, 0, 0, 0.1);
- box-shadow:
- // inset 0 0 0 1px rgba(255, 255, 255, 0.1),
- 0 3px 5px 1px rgba(0, 0, 0, 0.3);
-}
diff --git a/home/core/gui/ags/style/colors-light.scss b/home/core/gui/ags/style/colors-light.scss
deleted file mode 100644
index 44abe710..00000000
--- a/home/core/gui/ags/style/colors-light.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-$red: #f38ba8;
-$yellow: #f9e2af;
-$green: #a6e3a1;
-$blue: #89b4fa;
-
-$tooltip-bg: #ffffff;
-$fg: #000000;
-$bg: rgba(255, 255, 255, 0.5);
-$bar-bg: rgba(255, 255, 255, 0.3);
-
-$surface: rgba(255, 255, 255, 0.3);
-$overlay: rgba(0, 0, 0, 0.5);
-
-$accent: #ddbaef;
-
-/* buttons */
-$button-enabled: $accent;
-$button-enabled-hover: adjust_color($button-enabled, $lightness: -10%);
-
-$button-disabled: $surface;
-$button-disabled-hover: adjust_color($button-disabled, $alpha: +0.1);
-
-* {
- text-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
-}
-
-@mixin border {
- // border: 1px solid rgba(0, 0, 0, 0.1);
- box-shadow:
- // inset 0 0 0 1px rgba(255, 255, 255, 0.1),
- 0 3px 5px 1px rgba(0, 0, 0, 0.3);
-}
diff --git a/home/core/gui/ags/style/colors.scss b/home/core/gui/ags/style/colors.scss
deleted file mode 100644
index b27bb7f7..00000000
--- a/home/core/gui/ags/style/colors.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-$red: #f38ba8;
-$yellow: #f9e2af;
-$green: #a6e3a1;
-$blue: #89b4fa;
-
-$tooltip-bg: #000000;
-$fg: #ffffff;
-$bg: rgba(0, 0, 0, 0.3);
-$bar-bg: rgba(0, 0, 0, 0.21);
-
-$surface: rgba(255, 255, 255, 0.15);
-$overlay: rgba(255, 255, 255, 0.7);
-
-$accent: #9d5b7a;
-
-/* buttons */
-$button-enabled: $accent;
-$button-enabled-hover: adjust_color($button-enabled, $lightness: -10%);
-
-$button-disabled: $surface;
-$button-disabled-hover: adjust_color($button-disabled, $alpha: +0.1);
-
-* {
- text-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
-}
-
-@mixin border {
- // border: 1px solid rgba(0, 0, 0, 0.1);
- box-shadow:
- // inset 0 0 0 1px rgba(255, 255, 255, 0.1),
- 0 3px 5px 1px rgba(0, 0, 0, 0.3);
-}
diff --git a/home/core/gui/ags/style/general.scss b/home/core/gui/ags/style/general.scss
deleted file mode 100644
index 0e0dff3d..00000000
--- a/home/core/gui/ags/style/general.scss
+++ /dev/null
@@ -1,100 +0,0 @@
-/* general styles */
-
-$round: 8px;
-$round2: calc($round * 2);
-$margin: 0.4rem;
-$padding: 0.4rem;
-$border-width: 2px;
-$scale: 0.5rem;
-
-@mixin animate {
- transition: 200ms;
-}
-
-* {
- color: $fg;
-}
-
-/* mixins */
-@mixin window-rounding {
- border-radius: $round2;
-}
-
-@mixin rounding {
- border-radius: calc($round2 - $padding - $border-width);
-}
-
-@mixin window-box {
- @include rounding;
-
- background: $surface;
- box-shadow: 0 1px 5px -5px rgba(0, 0, 0, 0.5);
- margin: $margin;
- padding: $padding;
-}
-
-@mixin window {
- @include border;
- @include window-rounding;
-
- background: $bg;
- margin: 5px 10px 15px;
- padding: $padding;
-}
-
-tooltip {
- background: $tooltip-bg;
- box-shadow:
- inset 0 0 0 1px rgba(255, 255, 255, 0.1),
- 0 0 rgba(0, 0, 0, 0.4);
- border-radius: $round;
-}
-
-/* scales & progress bars */
-scale,
-progressbar {
- trough {
- background-color: $surface;
- border-radius: $scale;
- min-width: calc($scale * 10);
- padding: 0 calc($scale / 2);
- }
-
- highlight,
- progress {
- background: $overlay;
- border-radius: $scale;
- margin: 0 calc(0px - $scale / 2);
- min-height: $scale;
- }
-}
-
-@mixin button-active {
- @include animate;
- background: $button-enabled;
- border-radius: 5rem;
- padding: 0.4rem;
-
- &:hover {
- background: $button-enabled-hover;
- }
-}
-
-@mixin button {
- @include animate;
- background: $button-disabled;
- border-radius: 5rem;
- padding: 0.4rem;
-
- &:hover {
- background: $button-disabled-hover;
- }
-}
-
-.button {
- @include button-active;
-}
-
-.button.disabled {
- @include button;
-}
diff --git a/home/core/gui/ags/style/music.scss b/home/core/gui/ags/style/music.scss
deleted file mode 100644
index fe430833..00000000
--- a/home/core/gui/ags/style/music.scss
+++ /dev/null
@@ -1,54 +0,0 @@
-.music.window {
- @include window;
-
- .cover {
- background-position: center;
- background-size: cover;
- border-radius: $round;
- box-shadow: 0 1px 2px -1px $bg;
- margin: 0.4rem;
- min-height: 13rem;
- min-width: 13rem;
- }
-}
-
-.music.window .info {
- margin: 0.5rem;
-
- label,
- scale {
- margin: 0.3rem 0;
- }
-
- label.position,
- label.length {
- font-size: 0.8rem;
- margin-bottom: 0;
- }
-
- scale {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .title {
- font-size: 1.5rem;
- font-weight: bold;
- min-width: 14rem;
- }
-}
-
-.music.window .controls {
- button {
- margin: 0 0.2rem;
- font-size: 1.5rem;
- }
-}
-
-.music.window .player-info {
- margin-bottom: 0;
-
- .player-icon {
- font-size: 1.2rem;
- }
-}
diff --git a/home/core/gui/ags/style/notifications.scss b/home/core/gui/ags/style/notifications.scss
deleted file mode 100644
index 97a12410..00000000
--- a/home/core/gui/ags/style/notifications.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-.notification {
- @include window;
- margin: 5px 5px 5px 10px;
-
- min-width: 25rem;
-
- border-radius: $round2;
- background-color: $bg;
-
- &.critical {
- border: 1px solid red;
- }
-}
-
-.notifications widget:last-child .notification {
- margin-bottom: 15px;
-}
-
-.notification .icon {
- image {
- font-size: 5rem;
- margin: 0.5rem;
- min-height: 5rem;
- min-width: 5rem;
- }
-
- >box {
- border-radius: $round;
- margin: 0.5rem;
- min-height: 5rem;
- min-width: 5rem;
- }
-}
-
-.notification .actions .action-button {
- @include window-box;
- @include animate;
- padding: 0.5rem 0;
-
- &:hover {
- background: $button-disabled-hover;
- }
-}
-
-.notification .text {
- margin: 0.5rem;
-
- .title {
- margin-bottom: 0.2rem;
- font-weight: 500;
- }
-
- .body {
- color: rgba(255, 255, 255, 0.7);
- font-weight: 500;
- }
-}
diff --git a/home/core/gui/ags/style/osd.scss b/home/core/gui/ags/style/osd.scss
deleted file mode 100644
index 5fdd65df..00000000
--- a/home/core/gui/ags/style/osd.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-.osd {
- @include window;
- padding: 0;
- margin-bottom: 2rem;
-
- image {
- margin-left: 1rem;
- color: rgba(0, 0, 0, 0.6);
- }
-
- progressbar trough {
- border-radius: 16px;
- background: none;
- min-width: 12.5rem;
- min-height: 2.5rem;
- }
-
- progressbar progress {
- border-radius: 0;
- border-radius: 16px;
- min-height: 2.5rem;
- }
-}
diff --git a/home/core/gui/ags/style/prelude.scss b/home/core/gui/ags/style/prelude.scss
deleted file mode 100644
index ae254b47..00000000
--- a/home/core/gui/ags/style/prelude.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-/* get rid of GTK theme's styles and set defaults */
-* {
- all: unset;
- font-family: Inter, Roboto, sans-serif;
-}
diff --git a/home/core/gui/ags/style/system-menu.scss b/home/core/gui/ags/style/system-menu.scss
deleted file mode 100644
index 4a252474..00000000
--- a/home/core/gui/ags/style/system-menu.scss
+++ /dev/null
@@ -1,77 +0,0 @@
-/* general */
-.system-menu {
- @include window;
- margin-top: 4px;
- margin-right: 4px;
-
- &>box {
- @include window-box;
- }
-}
-
-/* toggles */
-.system-menu .toggle {
- min-width: 20rem;
-
- &:not(:last-child) {
- margin-bottom: 0.3rem;
- }
-
- .button {
- margin-right: 0.5rem;
- }
-}
-
-/* power profiles */
-.system-menu .power-profiles {
- padding: 0;
-
- .current-profile {
- padding: 0.3rem;
- }
-
- image,
- label {
- margin: 0.3rem;
- }
-
- .options {
- padding: 0;
-
- widget {
- @include button;
- border-radius: 0;
-
- &:last-child {
- border-radius: 0 0 $round $round;
- }
-
- box {
- padding: 0.3rem;
- }
- }
- }
-}
-
-/* sliders */
-.system-menu .sliders {
- image {
- margin: 0.3rem;
- }
-
- scale {
- margin: 0 0.5rem;
- }
-}
-
-.system-menu .battery-box {
-
- image,
- label {
- margin: 0 0.3rem;
- }
-
- .time {
- color: rgba(255, 255, 255, 0.7);
- }
-}
diff --git a/home/core/gui/ags/tsconfig.json b/home/core/gui/ags/tsconfig.json
deleted file mode 100644
index f03f2d12..00000000
--- a/home/core/gui/ags/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2022",
- "module": "ES2022",
- "lib": [
- "ES2022"
- ],
- "allowJs": true,
- "checkJs": true,
- "strict": true,
- "noImplicitAny": false,
- "baseUrl": ".",
- "typeRoots": [
- "./types"
- ],
- "skipLibCheck": true
- }
-}
\ No newline at end of file
diff --git a/home/core/gui/ags/user_options.js b/home/core/gui/ags/user_options.js
new file mode 100644
index 00000000..0101d943
--- /dev/null
+++ b/home/core/gui/ags/user_options.js
@@ -0,0 +1,8 @@
+// For every option, see ~/.config/ags/modules/.configuration/user_options.js
+// (vscode users ctrl+click this: file://./modules/.configuration/user_options.js)
+// (vim users: `:vsp` to split window, move cursor to this path, press `gf`. `Ctrl-w` twice to switch between)
+// options listed in this file will override the default ones in the above file
+
+const userConfigOptions = {};
+
+export default userConfigOptions;
diff --git a/home/core/gui/ags/utils/audio.js b/home/core/gui/ags/utils/audio.js
deleted file mode 100644
index df1921e0..00000000
--- a/home/core/gui/ags/utils/audio.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Audio, Icons } from "../imports.js";
-
-export const audioIcon = () => {
- if (Audio.speaker?.stream.isMuted) return Icons.volume.muted;
-
- const vol = Audio.speaker?.volume * 100;
- const icon = [
- [101, "overamplified"],
- [67, "high"],
- [34, "medium"],
- [1, "low"],
- [0, "muted"],
- ].find(([threshold]) => threshold <= vol)[1];
-
- return Icons.volume[icon];
-};
-
-export const micIcon = () => {
- if (Audio.microphone?.stream.isMuted) return Icons.microphone.muted;
-
- const vol = Audio.microphone?.volume * 100;
- const icon = [
- [67, "high"],
- [34, "medium"],
- [1, "low"],
- [0, "muted"],
- ].find(([threshold]) => threshold <= vol)[1];
-
- return Icons.microphone[icon];
-};
diff --git a/home/core/gui/ags/utils/battery.js b/home/core/gui/ags/utils/battery.js
deleted file mode 100644
index a933564e..00000000
--- a/home/core/gui/ags/utils/battery.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Battery } from "../imports.js";
-
-export const toTime = (time) => {
- const MINUTE = 60;
- const HOUR = MINUTE * 60;
-
- if (time > 24 * HOUR) return "";
-
- const hours = Math.round(time / HOUR);
- const minutes = Math.round((time - hours * HOUR) / MINUTE);
-
- const hoursDisplay = hours > 0 ? `${hours}h ` : "";
- const minutesDisplay = minutes > 0 ? `${minutes}m ` : "";
-
- return `${hoursDisplay}${minutesDisplay}`;
-};
-
-export const batteryTime = () => {
- return Battery.timeRemaining > 0 && toTime(Battery.timeRemaining) != ""
- ? `${toTime(Battery.timeRemaining)}remaining`
- : "";
-};
diff --git a/home/core/gui/ags/utils/bluetooth.js b/home/core/gui/ags/utils/bluetooth.js
deleted file mode 100644
index df8236e0..00000000
--- a/home/core/gui/ags/utils/bluetooth.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Bluetooth, Icons } from "../imports.js";
-
-export const getBluetoothDevice = (addr) =>
- Bluetooth.getDevice(addr).alias ?? Bluetooth.getDevice(addr).name;
-
-export const getBluetoothIcon = (connected) => {
- if (!Bluetooth.enabled) return Icons.bluetooth.disabled;
- if (connected.length > 0) return Icons.bluetooth.active;
- return Icons.bluetooth.disconnected;
-};
-
-export const getBluetoothText = (connected) => {
- if (!Bluetooth.enabled) return "Bluetooth off";
-
- if (connected.length > 0) {
- const dev = Bluetooth.getDevice(connected[0].address);
- let battery_str = "";
-
- if (dev.battery_percentage > 0) {
- battery_str += ` ${dev.battery_percentage}%`;
- }
-
- return dev.name + battery_str;
- }
-
- return "Bluetooth on";
-};
diff --git a/home/core/gui/ags/utils/hyprland.js b/home/core/gui/ags/utils/hyprland.js
deleted file mode 100644
index 19a29e1c..00000000
--- a/home/core/gui/ags/utils/hyprland.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Hyprland } from "../imports.js";
-
-export let DEFAULT_MONITOR;
-const connID = Hyprland.connect("notify::workspaces", () => {
- Hyprland.disconnect(connID);
-
- DEFAULT_MONITOR = {
- name: Hyprland.monitors[0].name,
- id: Hyprland.monitors[0].id,
- };
-});
-
-export const changeWorkspace = (ws) =>
- Hyprland.messageAsync(`dispatch workspace ${ws}`);
-
-export const focusedSwitch = (self) => {
- const id = Hyprland.active.workspace.id;
- if (self.lastFocused == id) return;
-
- self.children[self.lastFocused - 1].toggleClassName("focused", false);
- self.children[id - 1].toggleClassName("focused", true);
- self.lastFocused = id;
-};
-
-export const added = (self, name) => {
- if (!name) return;
- const ws = Hyprland.workspaces.find((e) => e.name == name);
- const id = ws?.id ?? Number(name);
- const child = self.children[id - 1];
-
- child.monitor = {
- name: ws?.monitor ?? DEFAULT_MONITOR.name,
- id: ws?.monitorID ?? DEFAULT_MONITOR.id,
- };
-
- child.active = true;
- child.toggleClassName(`monitor${child.monitor.id}`, true);
-
- // if this id is bigger than the last biggest id, visibilise all other ws before it
- if (id > self.biggestId) {
- for (let i = self.biggestId; i <= id; i++) {
- self.children[i - 1].visible = true;
- }
- self.biggestId = id;
- }
-};
-
-const makeInvisible = (self, id) => {
- if (id <= 1) return;
-
- const child = self.children[id - 1];
- if (child.active) {
- self.biggestId = id;
- return;
- }
-
- child.visible = false;
- makeInvisible(self, id - 1);
-};
-
-export const removed = (self, name) => {
- if (!name) return;
-
- const id = Number(name);
- const child = self.children[id - 1];
-
- child.toggleClassName(`monitor${child.monitor.id}`, false);
- child.active = false;
-
- // if this id is the biggest id, unvisibilise it and all other inactives until the next active before it
- if (id == self.biggestId) {
- makeInvisible(self, id);
- }
-};
-
-export const moveWorkspace = (self, data) => {
- const [id, name] = data.split(",");
-
- const child = self.children[id - 1];
-
- // remove previous monitor class
- child.toggleClassName(`monitor${child.monitor.id}`, false);
-
- // add new monitor and class
- const monitor = Hyprland.monitors.find((e) => e.name == name);
-
- child.monitor = {
- name,
- id: monitor?.id ?? DEFAULT_MONITOR.id,
- };
-
- print(`child ${id}: monitor ${name} ${child.monitor.id}`);
- child.toggleClassName(`monitor${child.monitor.id}`, true);
-};
-
-export const sortWorkspaces = () => {
- return Hyprland.workspaces
- .sort((x, y) => {
- return x.id - y.id;
- })
- .filter((x) => {
- return x.name.indexOf("special") == -1;
- });
-};
-
-export const getLastWorkspaceId = () => sortWorkspaces().slice(-1)[0].id;
-export const workspaceActive = (id) => sortWorkspaces().some((e) => e.id == id);
diff --git a/home/core/gui/ags/utils/icons.js b/home/core/gui/ags/utils/icons.js
deleted file mode 100644
index 92ce7db5..00000000
--- a/home/core/gui/ags/utils/icons.js
+++ /dev/null
@@ -1,35 +0,0 @@
-export default {
- bluetooth: {
- active: "bluetooth-active-symbolic",
- disabled: "bluetooth-disabled-symbolic",
- disconnected: "bluetooth-disconnected-symbolic",
- },
-
- brightness: "display-brightness-symbolic",
-
- media: {
- play: "media-playback-start-symbolic",
- pause: "media-playback-pause-symbolic",
- next: "media-skip-forward-symbolic",
- previous: "media-skip-backward-symbolic",
-
- player: "multimedia-player-symbolic",
- },
-
- volume: {
- muted: "audio-volume-muted-symbolic",
- low: "audio-volume-low-symbolic",
- medium: "audio-volume-medium-symbolic",
- high: "audio-volume-high-symbolic",
- overamplified: "audio-volume-overamplified-symbolic",
- },
-
- microphone: {
- muted: "microphone-sensitivity-muted-symbolic",
- low: "microphone-sensitivity-low-symbolic",
- medium: "microphone-sensitivity-medium-symbolic",
- high: "microphone-sensitivity-high-symbolic",
- },
-
- powerButton: "system-shutdown-symbolic",
-};
diff --git a/home/core/gui/ags/utils/mpris.js b/home/core/gui/ags/utils/mpris.js
deleted file mode 100644
index 148f921f..00000000
--- a/home/core/gui/ags/utils/mpris.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Icons, Utils } from "../imports.js";
-import GLib from "gi://GLib";
-
-export const findPlayer = (players) => {
- // try to get the first active player
- const activePlayer = players.find((p) => p.playBackStatus == "Playing");
- if (activePlayer != null) return activePlayer;
-
- // otherwise get the first "working" player
- for (const p of players) {
- if (p.title != "undefined") return p;
- }
-};
-
-export const mprisStateIcon = (status) => {
- const state = status == "Playing" ? "pause" : "play";
- return Icons.media[state];
-};
-
-export const MEDIA_CACHE_PATH = Utils.CACHE_DIR + "/media";
-export const blurredPath = MEDIA_CACHE_PATH + "/blurred";
-
-export const generateBackground = (cover_path) => {
- const url = cover_path;
- if (!url) return "";
-
- const makeBg = (bg) => `background: center/cover url('${bg}')`;
-
- const blurred = blurredPath + url.substring(MEDIA_CACHE_PATH.length);
-
- if (GLib.file_test(blurred, GLib.FileTest.EXISTS)) {
- return makeBg(blurred);
- }
-
- Utils.ensureDirectory(blurredPath);
- Utils.exec(`convert ${url} -blur 0x22 ${blurred}`);
-
- return makeBg(blurred);
-};
-
-export function lengthStr(length) {
- const min = Math.floor(length / 60);
- const sec = Math.floor(length % 60);
- const sec0 = sec < 10 ? "0" : "";
- return `${min}:${sec0}${sec}`;
-}
diff --git a/home/core/gui/ags/utils/net.js b/home/core/gui/ags/utils/net.js
deleted file mode 100644
index 5d9fd22f..00000000
--- a/home/core/gui/ags/utils/net.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Network } from "../imports.js";
-
-export const getNetIcon = () => {
- if (Network.connectivity == "none") return "";
- if (Network.primary == "wired") return "network-wired";
-
- return Network.wifi.icon_name;
-};
-
-export const getNetText = () => {
- // no connection
- if (Network.connectivity == "none") return "No connection";
-
- // wired
- if (Network.primary == "wired") return "Wired";
-
- // wifi
- const wifi = Network.wifi;
- switch (wifi.internet) {
- case "connected":
- return wifi.ssid;
- case "connecting":
- return "Connecting";
- case "disconnected":
- return "Disconnected";
- }
-};
diff --git a/home/core/gui/ags/utils/popup_window.js b/home/core/gui/ags/utils/popup_window.js
deleted file mode 100644
index 9318f5e8..00000000
--- a/home/core/gui/ags/utils/popup_window.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import App from "resource:///com/github/Aylur/ags/app.js";
-import { Widget } from "../imports.js";
-const { Box, Revealer, Window } = Widget;
-
-export default ({
- name,
- child,
- revealerSetup = null,
- transition = "crossfade",
- transitionDuration = 200,
- ...props
-}) => {
- const window = Window({
- name,
- popup: false,
- focusable: false,
- visible: false,
- ...props,
-
- setup: (self) => (self.getChild = () => child),
-
- child: Box({
- css: `
- min-height: 1px;
- min-width: 1px;
- padding: 1px;
- `,
- child: Revealer({
- transition,
- transitionDuration,
- child: child,
-
- setup:
- revealerSetup ??
- ((self) =>
- self.hook(App, (self, currentName, visible) => {
- if (currentName === name) {
- self.reveal_child = visible;
- }
- })),
- }),
- }),
- });
-
- return window;
-};
diff --git a/home/core/gui/ags/variables.js b/home/core/gui/ags/variables.js
new file mode 100644
index 00000000..998c5784
--- /dev/null
+++ b/home/core/gui/ags/variables.js
@@ -0,0 +1,58 @@
+const { Gdk, Gtk } = imports.gi;
+import App from "resource:///com/github/Aylur/ags/app.js";
+import Variable from "resource:///com/github/Aylur/ags/variable.js";
+import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
+import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
+const { exec, execAsync } = Utils;
+
+Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`);
+
+// Global vars for external control (through keybinds)
+export const showMusicControls = Variable(false, {});
+export const showColorScheme = Variable(false, {});
+globalThis["openMusicControls"] = showMusicControls;
+globalThis["openColorScheme"] = showColorScheme;
+globalThis["mpris"] = Mpris;
+
+// Mode switching
+export const currentShellMode = Variable("normal", {}); // normal, focus
+globalThis["currentMode"] = currentShellMode;
+globalThis["cycleMode"] = () => {
+ if (currentShellMode.value === "normal") {
+ currentShellMode.value = "focus";
+ } else if (currentShellMode.value === "focus") {
+ currentShellMode.value = "nothing";
+ } else {
+ currentShellMode.value = "normal";
+ }
+};
+
+// Window controls
+const range = (length, start = 1) =>
+ Array.from({ length }, (_, i) => i + start);
+globalThis["toggleWindowOnAllMonitors"] = (name) => {
+ range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach((id) => {
+ App.toggleWindow(`${name}${id}`);
+ });
+};
+globalThis["closeWindowOnAllMonitors"] = (name) => {
+ range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach((id) => {
+ App.closeWindow(`${name}${id}`);
+ });
+};
+globalThis["openWindowOnAllMonitors"] = (name) => {
+ range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach((id) => {
+ App.openWindow(`${name}${id}`);
+ });
+};
+
+globalThis["closeEverything"] = () => {
+ const numMonitors = Gdk.Display.get_default()?.get_n_monitors() || 1;
+ for (let i = 0; i < numMonitors; i++) {
+ App.closeWindow(`cheatsheet${i}`);
+ App.closeWindow(`session${i}`);
+ }
+ App.closeWindow("sideleft");
+ App.closeWindow("sideright");
+ App.closeWindow("overview");
+};
diff --git a/home/core/gui/ags/windows/bar/main.js b/home/core/gui/ags/windows/bar/main.js
deleted file mode 100644
index 946377cf..00000000
--- a/home/core/gui/ags/windows/bar/main.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import { App, Widget } from "../../imports.js";
-import Battery from "./modules/battery.js";
-import Bluetooth from "./modules/bluetooth.js";
-import Date from "./modules/date.js";
-import Music from "./modules/music.js";
-import Net from "./modules/net.js";
-import CpuRam from "./modules/cpu_ram.js";
-import Tray from "./modules/tray.js";
-import Workspaces from "./modules/workspaces.js";
-
-const SystemInfo = () =>
- Widget.EventBox({
- className: "system-menu-toggler",
- onPrimaryClick: () => App.toggleWindow("system-menu"),
-
- child: Widget.Box({
- children: [Net(), Bluetooth(), Battery()],
- }),
- }).hook(App, (self, window, visible) => {
- if (window === "system-menu") {
- self.toggleClassName("active", visible);
- }
- });
-
-const Start = () =>
- Widget.Box({
- hexpand: true,
- hpack: "start",
- children: [
- Workspaces(),
- // Indicators
- ],
- });
-
-const Center = () =>
- Widget.Box({
- children: [Music()],
- });
-
-const End = () =>
- Widget.Box({
- hexpand: true,
- hpack: "end",
-
- children: [Tray(), CpuRam(), SystemInfo(), Date()],
- });
-
-export default () =>
- Widget.Window({
- monitor: 0,
- name: `bar`,
- anchor: ["top", "left", "right"],
- exclusivity: "exclusive",
-
- child: Widget.CenterBox({
- className: "bar",
-
- startWidget: Start(),
- centerWidget: Center(),
- endWidget: End(),
- }),
- });
diff --git a/home/core/gui/ags/windows/bar/modules/battery.js b/home/core/gui/ags/windows/bar/modules/battery.js
deleted file mode 100644
index 5dba54ea..00000000
--- a/home/core/gui/ags/windows/bar/modules/battery.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { Battery, Widget } from "../../../imports.js";
-
-export default () =>
- Widget.Icon({ className: "battery module" })
- .bind("icon", Battery, "icon-name")
- .bind("tooltip-text", Battery, "percent", (p) => `Battery on ${p}%`);
diff --git a/home/core/gui/ags/windows/bar/modules/bluetooth.js b/home/core/gui/ags/windows/bar/modules/bluetooth.js
deleted file mode 100644
index b47b891f..00000000
--- a/home/core/gui/ags/windows/bar/modules/bluetooth.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Bluetooth, Widget } from "../../../imports.js";
-import {
- getBluetoothIcon,
- getBluetoothText,
-} from "../../../utils/bluetooth.js";
-
-export default () =>
- Widget.Icon({ className: "bluetooth module" })
- .bind("icon", Bluetooth, "connected-devices", getBluetoothIcon)
- .bind("tooltip-text", Bluetooth, "connected-devices", getBluetoothText);
diff --git a/home/core/gui/ags/windows/bar/modules/cpu_ram.js b/home/core/gui/ags/windows/bar/modules/cpu_ram.js
deleted file mode 100644
index 993b879f..00000000
--- a/home/core/gui/ags/windows/bar/modules/cpu_ram.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { Utils, Widget } from "../../../imports.js";
-
-const Indicator = (props) =>
- Widget.Box({
- vertical: true,
- vexpand: true,
- vpack: "center",
-
- children: [
- Widget.Label({
- className: "type",
- label: props.type,
- }),
- Widget.Label({ className: "value" }).poll(2000, props.poll),
- ],
- }).poll(2000, props.boxpoll);
-
-const cpu = {
- type: "CPU",
-
- poll: (self) =>
- Utils.execAsync([
- "sh",
- "-c",
- `top -bn1 | rg '%Cpu' | tail -1 | awk '{print 100-$8}'`,
- ])
- .then((r) => (self.label = Math.round(Number(r)) + "%"))
- .catch((err) => print(err)),
-
- boxpoll: (self) =>
- Utils.execAsync(["sh", "-c", "lscpu --parse=MHZ"])
- .then((r) => {
- const mhz = r.split("\n").slice(4);
- const freq = mhz.reduce((acc, e) => acc + Number(e), 0) / mhz.length;
- self.tooltipText = Math.round(freq) + " MHz";
- })
- .catch((err) => print(err)),
-};
-
-const ram = {
- type: "MEM",
- poll: (self) =>
- Utils.execAsync([
- "sh",
- "-c",
- `free | tail -2 | head -1 | awk '{print $3/$2*100}'`,
- ])
- .then((r) => (self.label = Math.round(Number(r)) + "%"))
- .catch((err) => print(err)),
-
- boxpoll: (self) =>
- Utils.execAsync([
- "sh",
- "-c",
- "free --si -h | tail -2 | head -1 | awk '{print $3}'",
- ])
- .then((r) => (self.tooltipText = r))
- .catch((err) => print(err)),
-};
-
-export default () =>
- Widget.EventBox({
- onPrimaryClick: () => Utils.execAsync(["missioncenter"]),
-
- child: Widget.Box({
- className: "system-info module",
-
- children: [Indicator(cpu), Indicator(ram)],
- }),
- });
diff --git a/home/core/gui/ags/windows/bar/modules/date.js b/home/core/gui/ags/windows/bar/modules/date.js
deleted file mode 100644
index 614e3aaf..00000000
--- a/home/core/gui/ags/windows/bar/modules/date.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Utils, Widget } from "../../../imports.js";
-
-export default () =>
- Widget.EventBox({
- child: Widget.Label({ className: "date module" }).poll(1000, (self) =>
- Utils.execAsync(["date", "+%a %b %d %H:%M"]).then(
- (r) => (self.label = r),
- ),
- ),
- });
diff --git a/home/core/gui/ags/windows/bar/modules/music.js b/home/core/gui/ags/windows/bar/modules/music.js
deleted file mode 100644
index 8e49863f..00000000
--- a/home/core/gui/ags/windows/bar/modules/music.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Mpris, Widget } from "../../../imports.js";
-import App from "resource:///com/github/Aylur/ags/app.js";
-import { findPlayer } from "../../../utils/mpris.js";
-
-const Cover = (player) =>
- Widget.Box({ className: "cover" }).bind(
- "css",
- player,
- "cover-path",
- (cover) => `background-image: url('${cover ?? ""}');`,
- );
-
-const Title = (player) =>
- Widget.Label({ className: "title module" }).bind(
- "label",
- player,
- "track-title",
- (title) => ((title ?? "") == "Unknown title" ? "" : title),
- );
-
-export const MusicBox = (player) =>
- Widget.Box({
- children: [Cover(player), Title(player)],
- });
-
-export default () =>
- Widget.EventBox({
- className: "music",
- onPrimaryClick: () => App.toggleWindow("music"),
- })
- .hook(App, (self, window, visible) => {
- if (window === "music") {
- self.toggleClassName("active", visible);
- }
- })
- .bind("visible", Mpris, "players", (p) => p.length > 0)
- .bind("child", Mpris, "players", (players) => {
- if (players.length == 0) return Widget.Box();
- return MusicBox(findPlayer(players));
- });
diff --git a/home/core/gui/ags/windows/bar/modules/net.js b/home/core/gui/ags/windows/bar/modules/net.js
deleted file mode 100644
index 6fa26f7b..00000000
--- a/home/core/gui/ags/windows/bar/modules/net.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Network, Widget } from "../../../imports.js";
-import { getNetIcon, getNetText } from "../../../utils/net.js";
-
-export default () =>
- Widget.Icon({ className: "net module" })
- .bind("icon", Network, "connectivity", getNetIcon)
- .bind("tooltip-text", Network, "connectivity", getNetText);
diff --git a/home/core/gui/ags/windows/bar/modules/tray.js b/home/core/gui/ags/windows/bar/modules/tray.js
deleted file mode 100644
index 4d180d9d..00000000
--- a/home/core/gui/ags/windows/bar/modules/tray.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { SystemTray, Widget } from "../../../imports.js";
-import Gdk from "gi://Gdk?version=3.0";
-
-const Item = (item) =>
- Widget.Button({
- child: Widget.Icon().bind("icon", item, "icon"),
-
- onPrimaryClick: (_, ev) => {
- try {
- item.activate(ev);
- } catch (err) {
- print(err);
- }
- },
-
- setup: (self) => {
- const id = item.menu?.connect("popped-up", (menu) => {
- self.toggleClassName("active");
- menu.connect("notify::visible", (menu) => {
- self.toggleClassName("active", menu.visible);
- });
- menu.disconnect(id);
- });
-
- if (id) {
- self.connect("destroy", () => item.menu?.disconnect(id));
- }
-
- self.bind("tooltip-markup", item, "tooltip-markup");
- },
-
- onSecondaryClick: (btn) =>
- item.menu?.popup_at_widget(
- btn,
- Gdk.Gravity.SOUTH,
- Gdk.Gravity.NORTH,
- null,
- ),
- });
-
-export default () =>
- Widget.Box({ className: "tray module" }).bind(
- "children",
- SystemTray,
- "items",
- (items) => items.map(Item),
- );
diff --git a/home/core/gui/ags/windows/bar/modules/workspaces.js b/home/core/gui/ags/windows/bar/modules/workspaces.js
deleted file mode 100644
index c8b68540..00000000
--- a/home/core/gui/ags/windows/bar/modules/workspaces.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Hyprland, Widget } from "../../../imports.js";
-import {
- added,
- changeWorkspace,
- DEFAULT_MONITOR,
- focusedSwitch,
- getLastWorkspaceId,
- moveWorkspace,
- removed,
- workspaceActive,
-} from "../../../utils/hyprland.js";
-
-globalThis.hyprland = Hyprland;
-
-const makeWorkspaces = () =>
- [...Array(10)].map((_, i) => {
- const id = i + 1;
-
- return Widget.Button({
- onPrimaryClick: () => changeWorkspace(id),
-
- visible: getLastWorkspaceId() >= id,
-
- setup: (self) => {
- const ws = Hyprland.getWorkspace(id);
- self.id = id;
- self.active = workspaceActive(id);
- self.monitor = DEFAULT_MONITOR;
-
- if (self.active) {
- self.monitor = {
- name: ws?.monitor ?? DEFAULT_MONITOR.name,
- id: ws?.monitorID ?? DEFAULT_MONITOR.id,
- };
- self.toggleClassName(`monitor${self.monitor.id}`, true);
- }
- },
- });
- });
-
-export default () =>
- Widget.EventBox({
- onScrollUp: () => changeWorkspace("+1"),
- onScrollDown: () => changeWorkspace("-1"),
-
- child: Widget.Box({
- className: "workspaces module",
-
- // The Hyprland service is ready later than ags is done parsing the config,
- // so only build the widget when we receive a signal from it.
- setup: (self) => {
- const connID = Hyprland.connect("notify::workspaces", () => {
- Hyprland.disconnect(connID);
-
- self.children = makeWorkspaces();
- self.lastFocused = Hyprland.active.workspace.id;
- self.biggestId = getLastWorkspaceId();
- self
- .hook(Hyprland.active.workspace, focusedSwitch)
- .hook(Hyprland, added, "workspace-added")
- .hook(Hyprland, removed, "workspace-removed")
- .hook(
- Hyprland,
- (self, name, data) => {
- if (name === "moveworkspace") moveWorkspace(self, data);
- },
- "event",
- );
- });
- },
- }),
- });
diff --git a/home/core/gui/ags/windows/music/controls.js b/home/core/gui/ags/windows/music/controls.js
deleted file mode 100644
index 10d20cd9..00000000
--- a/home/core/gui/ags/windows/music/controls.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Icons, Widget } from "../../imports.js";
-import { mprisStateIcon } from "../../utils/mpris.js";
-
-export default (player) =>
- Widget.CenterBox({
- className: "controls",
- hpack: "center",
-
- startWidget: Widget.Button({
- onClicked: () => player.previous(),
- child: Widget.Icon(Icons.media.previous),
- }),
-
- centerWidget: Widget.Button({
- onClicked: () => player.playPause(),
-
- child: Widget.Icon().bind(
- "icon",
- player,
- "play-back-status",
- mprisStateIcon,
- ),
- }),
-
- endWidget: Widget.Button({
- onClicked: () => player.next(),
- child: Widget.Icon(Icons.media.next),
- }),
- });
diff --git a/home/core/gui/ags/windows/music/cover.js b/home/core/gui/ags/windows/music/cover.js
deleted file mode 100644
index 79032498..00000000
--- a/home/core/gui/ags/windows/music/cover.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Widget } from "../../imports.js";
-
-export default (player) =>
- Widget.Box({ className: "cover" }).bind(
- "css",
- player,
- "cover-path",
- (cover) => `background-image: url('${cover ?? ""}')`,
- );
diff --git a/home/core/gui/ags/windows/music/main.js b/home/core/gui/ags/windows/music/main.js
deleted file mode 100644
index d71d8da3..00000000
--- a/home/core/gui/ags/windows/music/main.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Mpris, Widget } from "../../imports.js";
-import { findPlayer, generateBackground } from "../../utils/mpris.js";
-import PopupWindow from "../../utils/popup_window.js";
-
-import Cover from "./cover.js";
-import { Artists, Title } from "./title_artists.js";
-import TimeInfo from "./time_info.js";
-import Controls from "./controls.js";
-import PlayerInfo from "./player_info.js";
-
-const Info = (player) =>
- Widget.Box({
- className: "info",
- vertical: true,
- vexpand: false,
- hexpand: false,
- homogeneous: true,
-
- children: [
- PlayerInfo(player),
- Title(player),
- Artists(player),
- Controls(player),
- TimeInfo(player),
- ],
- });
-
-const MusicBox = (player) =>
- Widget.Box({
- className: "music window",
- children: [Cover(player), Info(player)],
- }).bind("css", player, "cover-path", generateBackground);
-
-export default () =>
- PopupWindow({
- monitor: 0,
- anchor: ["top"],
- name: "music",
- child: Widget.Box(),
- }).bind("child", Mpris, "players", (players) => {
- if (players.length == 0) return Widget.Box();
- return MusicBox(findPlayer(players));
- });
diff --git a/home/core/gui/ags/windows/music/player_info.js b/home/core/gui/ags/windows/music/player_info.js
deleted file mode 100644
index 5377db30..00000000
--- a/home/core/gui/ags/windows/music/player_info.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Icons, Utils, Widget } from "../../imports.js";
-
-export default (player) =>
- Widget.Box({
- className: "player-info",
- vexpand: true,
- vpack: "start",
-
- children: [
- Widget.Icon({
- hexpand: true,
- hpack: "end",
- className: "player-icon",
- tooltipText: player.identity ?? "",
- }).bind("icon", player, "entry", (entry) => {
- // the Spotify icon is called spotify-client
- if (entry == "spotify") entry = "spotify-client";
- return Utils.lookUpIcon(entry ?? "") ? entry : Icons.media.player;
- }),
- ],
- });
diff --git a/home/core/gui/ags/windows/music/time_info.js b/home/core/gui/ags/windows/music/time_info.js
deleted file mode 100644
index 37734aad..00000000
--- a/home/core/gui/ags/windows/music/time_info.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { Widget } from "../../imports.js";
-import { lengthStr } from "../../utils/mpris.js";
-
-export const PositionLabel = (player) =>
- Widget.Label({
- className: "position",
- hexpand: true,
- xalign: 0,
-
- setup: (self) => {
- const update = (_, time) => {
- player.length > 0
- ? (self.label = lengthStr(time || player.position))
- : (self.visible = !!player);
- };
-
- self.hook(player, update, "position").poll(1000, update);
- },
- });
-
-export const LengthLabel = (player) =>
- Widget.Label({
- className: "length",
- hexpand: true,
- xalign: 1,
- })
- .bind("visible", player, "length", (length) => length > 0)
- .bind("label", player, "length", (length) => lengthStr(length));
-
-export const Position = (player) =>
- Widget.Slider({
- className: "position",
- draw_value: false,
-
- onChange: ({ value }) => (player.position = player.length * value),
-
- setup: (self) => {
- const update = () => {
- if (self.dragging) return;
-
- self.visible = player.length > 0;
-
- if (player.length > 0) {
- self.value = player.position / player.length;
- }
- };
-
- self
- .hook(player, update)
- .hook(player, update, "position")
- .poll(1000, update);
- },
- });
-
-export default (player) =>
- Widget.Box({
- vertical: true,
- vexpand: true,
- vpack: "end",
-
- children: [
- Widget.Box({
- hexpand: true,
- children: [PositionLabel(player), LengthLabel(player)],
- }),
- Position(player),
- ],
- });
diff --git a/home/core/gui/ags/windows/music/title_artists.js b/home/core/gui/ags/windows/music/title_artists.js
deleted file mode 100644
index b625162e..00000000
--- a/home/core/gui/ags/windows/music/title_artists.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Widget } from "../../imports.js";
-
-export const Title = (player) =>
- Widget.Scrollable({
- className: "title",
- vscroll: "never",
- hscroll: "automatic",
-
- child: Widget.Label({
- className: "title",
- label: "Nothing playing",
- }).bind(
- "label",
- player,
- "track-title",
- (title) => title ?? "Nothing playing",
- ),
- });
-
-export const Artists = (player) =>
- Widget.Scrollable({
- className: "artists",
- vscroll: "never",
- hscroll: "automatic",
-
- child: Widget.Label({ className: "artists" }).bind(
- "label",
- player,
- "track-artists",
- (artists) => artists.join(", ") ?? "",
- ),
- });
diff --git a/home/core/gui/ags/windows/notifications/popups.js b/home/core/gui/ags/windows/notifications/popups.js
deleted file mode 100644
index c256bc7d..00000000
--- a/home/core/gui/ags/windows/notifications/popups.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import { Hyprland, Notifications, Utils, Widget } from "../../imports.js";
-
-const closeAll = () => {
- Notifications.popups.map((n) => n.dismiss());
-};
-
-/** @param {import("types/service/notifications").Notification} n */
-const NotificationIcon = ({ app_entry, app_icon, image }) => {
- if (image) {
- return Widget.Box({
- css: `
- background-image: url("${image}");
- background-size: contain;
- background-repeat: no-repeat;
- background-position: center;
- `,
- });
- }
-
- if (Utils.lookUpIcon(app_icon)) {
- return Widget.Icon(app_icon);
- }
-
- if (app_entry && Utils.lookUpIcon(app_entry)) {
- return Widget.Icon(app_entry);
- }
-
- return null;
-};
-
-/** @param {import('types/service/notifications').Notification} n */
-export const Notification = (n) => {
- const icon = Widget.Box({
- vpack: "start",
- class_name: "icon",
- // @ts-ignore
- setup: (self) => {
- let icon = NotificationIcon(n);
- if (icon !== null) {
- self.child = icon;
- }
- },
- });
-
- const title = Widget.Label({
- class_name: "title",
- xalign: 0,
- justification: "left",
- hexpand: true,
- max_width_chars: 24,
- truncate: "end",
- wrap: true,
- label: n.summary,
- use_markup: true,
- });
-
- const body = Widget.Label({
- class_name: "body",
- hexpand: true,
- use_markup: true,
- xalign: 0,
- justification: "left",
- max_width_chars: 100,
- wrap: true,
- label: n.body,
- });
-
- const actions = Widget.Box({
- class_name: "actions",
- children: n.actions
- .filter(({ id }) => id != "default")
- .map(({ id, label }) =>
- Widget.Button({
- class_name: "action-button",
- on_clicked: () => n.invoke(id),
- hexpand: true,
- child: Widget.Label(label),
- }),
- ),
- });
-
- return Widget.EventBox({
- on_primary_click: () => {
- if (n.actions.length > 0) n.invoke(n.actions[0].id);
- },
- on_middle_click: closeAll,
- on_secondary_click: () => n.dismiss(),
- child: Widget.Box({
- class_name: `notification ${n.urgency}`,
- vertical: true,
-
- children: [
- Widget.Box({
- class_name: "info",
- children: [
- icon,
- Widget.Box({
- vertical: true,
- class_name: "text",
- vpack: "center",
-
- setup: (self) => {
- if (n.body.length > 0) {
- self.children = [title, body];
- } else {
- self.children = [title];
- }
- },
- }),
- ],
- }),
- actions,
- ],
- }),
- });
-};
-
-let lastMonitor;
-export const notificationPopup = () =>
- Widget.Window({
- name: "notifications",
- anchor: ["top", "right"],
- child: Widget.Box({
- css: "padding: 1px;",
- class_name: "notifications",
- vertical: true,
- // @ts-ignore
- children: Notifications.bind("popups").transform((popups) => {
- return popups.map(Notification);
- }),
- }),
- }).hook(Hyprland.active, (self) => {
- // prevent useless resets
- if (lastMonitor === Hyprland.active.monitor) return;
-
- self.monitor = Hyprland.active.monitor.id;
- });
-
-export default notificationPopup;
diff --git a/home/core/gui/ags/windows/osd/main.js b/home/core/gui/ags/windows/osd/main.js
deleted file mode 100644
index a6125bf0..00000000
--- a/home/core/gui/ags/windows/osd/main.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import App from "resource:///com/github/Aylur/ags/app.js";
-import { Audio, Hyprland, Widget } from "../../imports.js";
-
-import Brightness from "../../services/brightness.js";
-import Indicators from "../../services/osd.js";
-import PopupWindow from "../../utils/popup_window.js";
-
-// connections
-Audio.connect("speaker-changed", () =>
- Audio.speaker.connect("changed", () => {
- if (!App.getWindow("system-menu")?.visible) {
- Indicators.speaker();
- }
- }),
-);
-Audio.connect("microphone-changed", () =>
- Audio.microphone.connect("changed", () => Indicators.mic()),
-);
-
-Brightness.connect("screen-changed", () => {
- if (!App.getWindow("system-menu")?.visible) {
- Indicators.display();
- }
-});
-
-let lastMonitor;
-
-const child = () =>
- Widget.Box({
- className: "osd",
-
- children: [
- Widget.Overlay({
- hexpand: true,
- visible: false,
- passThrough: true,
-
- child: Widget.ProgressBar({
- hexpand: true,
- vertical: false,
- }).hook(Indicators, (self, props) => {
- self.value = props?.value ?? 0;
- self.visible = props?.showProgress ?? false;
- }),
-
- overlays: [
- Widget.Box({
- hexpand: true,
-
- children: [
- Widget.Icon().hook(
- Indicators,
- (self, props) => (self.icon = props?.icon ?? ""),
- ),
- Widget.Box({
- hexpand: true,
- }),
- ],
- }),
- ],
- }),
- ],
- });
-
-export default () =>
- PopupWindow({
- name: "osd",
- monitor: 0,
- layer: "overlay",
- child: child(),
- click_through: true,
- anchor: ["bottom"],
- revealerSetup: (self) =>
- self.hook(Indicators, (revealer, _, visible) => {
- revealer.reveal_child = visible;
- }),
- })
- .hook(Hyprland.active, (self) => {
- // prevent useless resets
- if (lastMonitor === Hyprland.active.monitor) return;
-
- self.monitor = Hyprland.active.monitor.id;
- })
- .hook(Indicators, (win, _, visible) => {
- win.visible = visible;
- });
diff --git a/home/core/gui/ags/windows/system-menu/battery_info.js b/home/core/gui/ags/windows/system-menu/battery_info.js
deleted file mode 100644
index 316c29b0..00000000
--- a/home/core/gui/ags/windows/system-menu/battery_info.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { App, Battery, Icons, Utils, Widget } from "../../imports.js";
-import { batteryTime } from "../../utils/battery.js";
-
-const batteryEnergy = () => {
- return Battery.energyRate > 0.1 ? `${Battery.energyRate.toFixed(1)} W ` : "";
-};
-
-const BatteryIcon = () =>
- Widget.Icon()
- .bind("icon", Battery, "percent", () => Battery.iconName)
- .bind("tooltip-text", Battery, "energy-rate", batteryEnergy);
-
-const BatteryPercent = () =>
- Widget.Label().bind("label", Battery, "percent", (percent) => `${percent}%`);
-
-const BatteryTime = () =>
- Widget.Label({
- className: "time",
- vexpand: true,
- vpack: "center",
- })
- .bind("label", Battery, "charging", batteryTime)
- .bind("label", Battery, "energy-rate", batteryTime);
-
-const BatteryBox = () =>
- Widget.Box({
- className: "battery-box",
- visible: Battery.available,
-
- children: [BatteryIcon(), BatteryPercent(), BatteryTime()],
- });
-
-const PowerButton = () =>
- Widget.Button({
- className: "button disabled",
- hexpand: true,
- hpack: "end",
-
- onPrimaryClick: () => {
- App.toggleWindow("system-menu");
- Utils.exec("wlogout");
- },
-
- child: Widget.Icon(Icons.powerButton),
- });
-
-export default () =>
- Widget.Box({
- className: "battery-info",
-
- children: [BatteryBox(), PowerButton()],
- });
diff --git a/home/core/gui/ags/windows/system-menu/main.js b/home/core/gui/ags/windows/system-menu/main.js
deleted file mode 100644
index 29dd7268..00000000
--- a/home/core/gui/ags/windows/system-menu/main.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Widget } from "../../imports.js";
-import PopupWindow from "../../utils/popup_window.js";
-
-import Toggles from "./toggles.js";
-import Sliders from "./sliders.js";
-import BatteryInfo from "./battery_info.js";
-
-const SystemMenuBox = () =>
- Widget.Box({
- className: "system-menu",
- vertical: true,
-
- children: [Toggles(), Sliders(), BatteryInfo()],
- });
-
-export default () =>
- PopupWindow({
- monitor: 0,
- anchor: ["top", "right"],
- name: "system-menu",
- child: SystemMenuBox(),
- });
diff --git a/home/core/gui/ags/windows/system-menu/sliders.js b/home/core/gui/ags/windows/system-menu/sliders.js
deleted file mode 100644
index c0b4ce2c..00000000
--- a/home/core/gui/ags/windows/system-menu/sliders.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { App, Audio, Icons, Utils, Widget } from "../../imports.js";
-import Brightness from "../../services/brightness.js";
-import { audioIcon } from "../../utils/audio.js";
-
-const Slider = (args) =>
- Widget.Box({
- ...(args.props ?? {}),
- className: args.name,
-
- children: [
- Widget.Button({
- onPrimaryClick: args.icon.action ?? null,
- child: Widget.Icon({
- icon: args.icon.icon ?? "",
- setup: args.icon.setup,
- }),
- }),
- Widget.Slider({
- drawValue: false,
- hexpand: true,
- setup: args.slider.setup,
- onChange: args.slider.onChange ?? null,
- }),
- ],
- });
-
-const vol = () => {
- return {
- name: "volume",
- icon: {
- icon: "",
- action: () => {
- App.toggleWindow("system-menu");
- Utils.execAsync("pwvucontrol");
- },
- setup: (self) =>
- self
- .bind("icon", Audio.speaker, "volume", audioIcon)
- .bind("icon", Audio.speaker.stream, "is-muted", audioIcon),
- },
- slider: {
- setup: (self) => self.bind("value", Audio.speaker, "volume"),
- onChange: ({ value }) => (Audio.speaker.volume = value),
- },
- };
-};
-
-const brightness = () => {
- return {
- name: "brightness",
- icon: {
- icon: Icons.brightness,
- },
- slider: {
- setup: (self) => self.bind("value", Brightness, "screen-value"),
- onChange: ({ value }) => (Brightness.screenValue = value),
- },
- };
-};
-
-export default () =>
- Widget.Box({
- className: "sliders",
- vertical: true,
-
- // The Audio service is ready later than ags is done parsing the config,
- // so only build the widget when we receive a signal from it.
- setup: (self) => {
- const connID = Audio.connect("notify::speaker", () => {
- Audio.disconnect(connID);
- self.children = [Slider(vol()), Slider(brightness())];
- });
- },
- });
diff --git a/home/core/gui/ags/windows/system-menu/toggles.js b/home/core/gui/ags/windows/system-menu/toggles.js
deleted file mode 100644
index f2afa640..00000000
--- a/home/core/gui/ags/windows/system-menu/toggles.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { App, Bluetooth, Network, Utils, Widget } from "../../imports.js";
-
-import { getNetIcon, getNetText } from "../../utils/net.js";
-import { getBluetoothIcon, getBluetoothText } from "../../utils/bluetooth.js";
-
-const Toggle = (args) =>
- Widget.Box({
- ...(args.props ?? {}),
- className: `toggle ${args.name}`,
- hexpand: true,
- hpack: "start",
-
- children: [
- Widget.Button({
- className: "button",
-
- child: Widget.Icon({
- setup: args.icon.setup,
- }),
- setup: args.icon.buttonSetup,
- }),
- Widget.Button({
- hexpand: true,
- child: Widget.Label({
- hpack: "start",
- setup: args.label.setup,
- }),
- setup: args.label.buttonSetup,
- }),
- ],
- });
-
-const net = {
- name: "net",
- icon: {
- setup: (self) =>
- self
- .bind("icon", Network, "connectivity", getNetIcon)
- .bind("icon", Network.wifi, "icon-name", getNetIcon),
-
- buttonSetup: (self) => {
- self.onPrimaryClick = () => Network.toggleWifi();
- self.hook(
- Network,
- (btn) =>
- btn.toggleClassName("disabled", Network.connectivity != "full"),
- "notify::connectivity",
- );
- },
- },
- label: {
- setup: (self) =>
- self
- .bind("label", Network, "connectivity", () => getNetText())
- .bind("label", Network.wifi, "ssid", () => getNetText()),
-
- buttonSetup: (self) => {
- self.onPrimaryClick = () => {
- App.toggleWindow("system-menu");
- Utils.execAsync([
- "sh",
- "-c",
- "XDG_CURRENT_DESKTOP=GNOME gnome-control-center",
- ]);
- };
- },
- },
-};
-
-const bt = {
- name: "bluetooth",
- icon: {
- setup: (self) =>
- self.bind("icon", Bluetooth, "connected-devices", getBluetoothIcon),
- buttonSetup: (self) => {
- self.onPrimaryClick = () => Bluetooth.toggle();
- self.hook(
- Bluetooth,
- (btn) => btn.toggleClassName("disabled", !Bluetooth.enabled),
- "notify::enabled",
- );
- },
- },
- label: {
- setup: (self) =>
- self.bind("label", Bluetooth, "connected-devices", getBluetoothText),
- buttonSetup: (self) => {
- self.onPrimaryClick = () => {
- App.toggleWindow("system-menu");
- Utils.execAsync("overskride");
- };
- },
- },
-};
-
-export default () =>
- Widget.Box({
- className: "toggles",
- vertical: true,
-
- children: [Toggle(net), Toggle(bt)],
- });
diff --git a/home/core/services/power-monitor/default.nix b/home/core/services/power-monitor/default.nix
new file mode 100644
index 00000000..33fc8ba9
--- /dev/null
+++ b/home/core/services/power-monitor/default.nix
@@ -0,0 +1,95 @@
+{
+ pkgs,
+ lib,
+ config,
+ ...
+}: let
+ script = pkgs.writeShellScript "power_monitor.sh" ''
+ BAT=$(echo /sys/class/power_supply/BAT*)
+ BAT_STATUS="$BAT/status"
+ BAT_CAP="$BAT/capacity"
+
+ AC_PROFILE="performance"
+ BAT_PROFILE="power-saver"
+
+ # wait a while if needed
+ [ -z "$STARTUP_WAIT" ] || sleep "$STARTUP_WAIT"
+
+ # start the monitor loop
+ currentStatus=$(cat "$BAT_STATUS")
+ prevProfile=$AC_PROFILE
+ prevStatus=Charging
+
+ # initial run
+ if [ "$currentStatus" = "Discharging" ]; then
+ profile="$BAT_PROFILE"
+ hyprctl -i 0 --batch 'keyword decoration:blur:enabled false; keyword animations:enabled false'
+ else
+ profile="$AC_PROFILE"
+ hyprctl -i 0 --batch 'keyword decoration:blur:enabled true; keyword animations:enabled true'
+ fi
+
+ # set the new profile
+ if [ $prevProfile != "$profile" ]; then
+ echo setting power profile to "$profile"
+ powerprofilesctl set "$profile"
+ fi
+
+ prevProfile="$profile"
+ prevStatus="$currentStatus"
+
+ # event loop
+ while true; do
+ currentStatus=$(cat "$BAT_STATUS")
+ if [ "$currentStatus" != "$prevStatus" ]; then
+ # read the current state
+ if [ "$currentStatus" = "Discharging" ]; then
+ profile="$BAT_PROFILE"
+ hyprctl -i 0 --batch 'keyword decoration:blur:enabled false; keyword animations:enabled false'
+ else
+ profile="$AC_PROFILE"
+ hyprctl -i 0 --batch 'keyword decoration:blur:enabled true; keyword animations:enabled true'
+ fi
+
+ # set the new profile
+ if [ $prevProfile != "$profile" ]; then
+ echo setting power profile to "$profile"
+ powerprofilesctl set "$profile"
+ fi
+
+ prevProfile="$profile"
+ prevStatus="$currentStatus"
+ fi
+
+ # wait for the next power change event
+ inotifywait -qq "$BAT_STATUS" "$BAT_CAP"
+ done
+ '';
+
+ dependencies = with pkgs; [
+ coreutils
+ config.wayland.windowManager.hyprland.package
+ power-profiles-daemon
+ inotify-tools
+ jaq
+ ];
+in {
+ # Power state monitor. Switches Power profiles based on charging state.
+ # Plugged in - performance
+ # Unplugged - power-saver
+ systemd.user.services.power-monitor = {
+ Unit = {
+ Description = "Power Monitor";
+ After = ["power-profiles-daemon.service"];
+ };
+
+ Service = {
+ Environment = "PATH=/run/wrappers/bin:${lib.makeBinPath dependencies}";
+ Type = "simple";
+ ExecStart = script;
+ Restart = "on-failure";
+ };
+
+ Install.WantedBy = ["default.target"];
+ };
+}
diff --git a/home/users/cnst/imports.nix b/home/users/cnst/imports.nix
index 6e7e9721..d3820576 100644
--- a/home/users/cnst/imports.nix
+++ b/home/users/cnst/imports.nix
@@ -3,6 +3,7 @@
# core.gui
../../core/gui/gtk
../../core/gui/waybar
+ ../../core/gui/ags
../../core/gui/browsers
../../core/gui/xdg
../../core/gui/discord
diff --git a/nixos/hosts/cnix/imports.nix b/nixos/hosts/cnix/imports.nix
index c7b99630..3a599656 100644
--- a/nixos/hosts/cnix/imports.nix
+++ b/nixos/hosts/cnix/imports.nix
@@ -23,6 +23,7 @@
../../services/udisks.nix
../../services/xserver-cnix.nix
../../services/locate.nix
+ ../../services/power.nix
# extra
../../extra/gaming.nix
@@ -62,6 +63,7 @@
wlroots
xdg-user-dirs
xdg-utils
+ zed-editor
# Dev
binutils
diff --git a/nixos/services/power.nix b/nixos/services/power.nix
new file mode 100644
index 00000000..8e76d348
--- /dev/null
+++ b/nixos/services/power.nix
@@ -0,0 +1,6 @@
+{
+ services = {
+ power-profiles-daemon.enable = true;
+ upower.enable = true;
+ };
+}