Compare commits
9 Commits
72c76c9fda
...
app-launch
| Author | SHA1 | Date | |
|---|---|---|---|
| 6366f57d6e | |||
| 5c5fce2581 | |||
| 5d08a48b6c | |||
| 82b0cf7aaa | |||
| e4744bab81 | |||
| 872dbfc792 | |||
| 64781af68f | |||
| 0ebfbdb3a9 | |||
| bf3920ad35 |
52
bar/config.py
Normal file
52
bar/config.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
from platformdirs import user_config_dir
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
APP_NAME = "makku_bar"
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME = user_config_dir(appname=APP_NAME)
|
||||||
|
XDG_CONFIG_FILE = os.path.join(XDG_CONFIG_HOME, "config.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(config_path=XDG_CONFIG_FILE):
|
||||||
|
"""Loads configuration from a YAML file."""
|
||||||
|
if config_path is None:
|
||||||
|
print("No configuration file path provided or found.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(config_path, "r") as f:
|
||||||
|
config = yaml.safe_load(f)
|
||||||
|
return config
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Error: Configuration file not found at {config_path}")
|
||||||
|
return None
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
print(f"Error parsing YAML file '{config_path}': {e}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred loading config file '{config_path}': {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def load_args():
|
||||||
|
parser = argparse.ArgumentParser(description="makku_bar")
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--config",
|
||||||
|
help="Path to a custom configuration file.",
|
||||||
|
type=str,
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args.config
|
||||||
|
|
||||||
|
|
||||||
|
app_config = load_config() if not load_args() else load_config(load_args())
|
||||||
|
|
||||||
|
if app_config is None:
|
||||||
|
raise Exception("Config file missing")
|
||||||
|
|
||||||
|
VINYL = app_config.get("vinyl", {"enable": False})
|
||||||
15
bar/main.py
15
bar/main.py
@@ -1,5 +1,3 @@
|
|||||||
# fabric bar.py example
|
|
||||||
# https://github.com/Fabric-Development/fabric/blob/rewrite/examples/bar/bar.py
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from fabric import Application
|
from fabric import Application
|
||||||
@@ -12,16 +10,23 @@ from fabric.utils import (
|
|||||||
get_relative_path,
|
get_relative_path,
|
||||||
)
|
)
|
||||||
from .modules.bar import StatusBar
|
from .modules.bar import StatusBar
|
||||||
|
from .modules.window_fuzzy import FuzzyWindowFinder
|
||||||
|
from .modules.app_launcher import AppLauncher
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
tray = SystemTray(name="system-tray", spacing=4)
|
tray = SystemTray(name="system-tray", spacing=4)
|
||||||
river = get_river_connection()
|
river = get_river_connection()
|
||||||
|
|
||||||
dummy = Window(visible=False)
|
dummy = Window(visible=False)
|
||||||
|
finder = FuzzyWindowFinder()
|
||||||
|
launcher = AppLauncher()
|
||||||
|
|
||||||
bar_windows = []
|
bar_windows = []
|
||||||
|
|
||||||
|
app = Application("bar", dummy, finder, launcher)
|
||||||
|
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))
|
||||||
|
|
||||||
|
|
||||||
def spawn_bars():
|
def spawn_bars():
|
||||||
logger.info("[Bar] Spawning bars after river ready")
|
logger.info("[Bar] Spawning bars after river ready")
|
||||||
outputs = river.outputs
|
outputs = river.outputs
|
||||||
@@ -38,13 +43,13 @@ def main():
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
if river.ready:
|
if river.ready:
|
||||||
spawn_bars()
|
spawn_bars()
|
||||||
else:
|
else:
|
||||||
river.connect("notify::ready", lambda sender, pspec: spawn_bars())
|
river.connect("notify::ready", lambda sender, pspec: spawn_bars())
|
||||||
|
|
||||||
app = Application("bar", dummy)
|
|
||||||
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
192
bar/modules/app_launcher.py
Normal file
192
bar/modules/app_launcher.py
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
"""
|
||||||
|
example configuration shows how to make a simple
|
||||||
|
desktop applications launcher, this example doesn't involve
|
||||||
|
any styling (except a couple of basic style properties)
|
||||||
|
|
||||||
|
|
||||||
|
the purpose of this configuration is to show to to use
|
||||||
|
the given utils and mainly how using lazy executors might
|
||||||
|
make the configuration way more faster than it's supposed to be
|
||||||
|
"""
|
||||||
|
|
||||||
|
import operator
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from fabric.widgets.box import Box
|
||||||
|
from fabric.widgets.label import Label
|
||||||
|
from fabric.widgets.button import Button
|
||||||
|
from fabric.widgets.image import Image
|
||||||
|
from fabric.widgets.entry import Entry
|
||||||
|
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||||
|
from fabric.widgets.wayland import WaylandWindow as Window
|
||||||
|
from fabric.utils import DesktopApp, get_desktop_applications, idle_add, remove_handler
|
||||||
|
import subprocess
|
||||||
|
from time import sleep
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class CustomApp:
|
||||||
|
name: str
|
||||||
|
generic_name: str | None
|
||||||
|
display_name: str | None
|
||||||
|
description: str | None
|
||||||
|
executable: str | None
|
||||||
|
command_line: str | None
|
||||||
|
hidden: bool
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name,
|
||||||
|
display_name=None,
|
||||||
|
executable=None,
|
||||||
|
generic_name=None,
|
||||||
|
description=None,
|
||||||
|
command_line=None,
|
||||||
|
hidden=False,
|
||||||
|
):
|
||||||
|
self.name = name
|
||||||
|
self.generic_name = generic_name
|
||||||
|
self.display_name = display_name
|
||||||
|
self.description = description
|
||||||
|
self.executable = executable
|
||||||
|
self.command_line = command_line
|
||||||
|
self.hidden = hidden
|
||||||
|
|
||||||
|
def launch(self):
|
||||||
|
def background():
|
||||||
|
subprocess.run([self.command_line])
|
||||||
|
|
||||||
|
threading.Thread(target=background, daemon=True).start()
|
||||||
|
|
||||||
|
def get_icon_pixbuf(
|
||||||
|
self,
|
||||||
|
size: int = 48,
|
||||||
|
default_icon: str | None = "image-missing",
|
||||||
|
) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class AppLauncher(Window):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(
|
||||||
|
layer="top",
|
||||||
|
anchor="center",
|
||||||
|
exclusivity="none",
|
||||||
|
keyboard_mode="on-demand",
|
||||||
|
visible=False,
|
||||||
|
all_visible=False,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
self._arranger_handler: int = 0
|
||||||
|
self._all_apps = get_desktop_applications()
|
||||||
|
self._custom_apps = [
|
||||||
|
CustomApp("Screenshot Clipboard", command_line="grim2clip")
|
||||||
|
]
|
||||||
|
|
||||||
|
self.viewport = Box(spacing=2, orientation="v")
|
||||||
|
self.search_entry = Entry(
|
||||||
|
placeholder="Search Applications...",
|
||||||
|
h_expand=True,
|
||||||
|
notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()),
|
||||||
|
)
|
||||||
|
self.scrolled_window = ScrolledWindow(
|
||||||
|
min_content_size=(280, 320),
|
||||||
|
max_content_size=(280 * 2, 320),
|
||||||
|
child=self.viewport,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(
|
||||||
|
Box(
|
||||||
|
spacing=2,
|
||||||
|
orientation="v",
|
||||||
|
style="margin: 2px",
|
||||||
|
children=[
|
||||||
|
# the header with the search entry
|
||||||
|
Box(
|
||||||
|
spacing=2,
|
||||||
|
orientation="h",
|
||||||
|
children=[
|
||||||
|
self.search_entry,
|
||||||
|
Button(
|
||||||
|
image=Image(icon_name="window-close"),
|
||||||
|
tooltip_text="Exit",
|
||||||
|
on_clicked=lambda *_: self.application.quit(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
# the actual slots holder
|
||||||
|
self.scrolled_window,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
def arrange_viewport(self, query: str = ""):
|
||||||
|
# reset everything so we can filter current viewport's slots...
|
||||||
|
# remove the old handler so we can avoid race conditions
|
||||||
|
remove_handler(self._arranger_handler) if self._arranger_handler else None
|
||||||
|
|
||||||
|
# remove all children from the viewport
|
||||||
|
self.viewport.children = []
|
||||||
|
|
||||||
|
combined_apps = self._all_apps + self._custom_apps
|
||||||
|
# make a new iterator containing the filtered apps
|
||||||
|
filtered_apps_iter = iter(
|
||||||
|
[
|
||||||
|
app
|
||||||
|
for app in combined_apps
|
||||||
|
if query.casefold()
|
||||||
|
in (
|
||||||
|
(app.display_name or "")
|
||||||
|
+ (" " + app.name + " ")
|
||||||
|
+ (app.generic_name or "")
|
||||||
|
).casefold()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
should_resize = operator.length_hint(filtered_apps_iter) == len(self._all_apps)
|
||||||
|
|
||||||
|
# all aboard...
|
||||||
|
# start the process of adding slots with a lazy executor
|
||||||
|
# using this method makes the process of adding slots way more less
|
||||||
|
# resource expensive without blocking the main thread and resulting in a lock
|
||||||
|
self._arranger_handler = idle_add(
|
||||||
|
lambda *args: self.add_next_application(*args)
|
||||||
|
or (self.resize_viewport() if should_resize else False),
|
||||||
|
filtered_apps_iter,
|
||||||
|
pin=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add_next_application(self, apps_iter: Iterator[DesktopApp]):
|
||||||
|
if not (app := next(apps_iter, None)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.viewport.add(self.bake_application_slot(app))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def resize_viewport(self):
|
||||||
|
self.scrolled_window.set_min_content_width(
|
||||||
|
self.viewport.get_allocation().width # type: ignore
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def bake_application_slot(self, app: DesktopApp, **kwargs) -> Button:
|
||||||
|
return Button(
|
||||||
|
child=Box(
|
||||||
|
orientation="h",
|
||||||
|
spacing=12,
|
||||||
|
children=[
|
||||||
|
Image(pixbuf=app.get_icon_pixbuf(), h_align="start", size=32),
|
||||||
|
Label(
|
||||||
|
label=app.display_name or "Unknown",
|
||||||
|
v_align="center",
|
||||||
|
h_align="center",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tooltip_text=app.description,
|
||||||
|
on_clicked=lambda *_: (self.hide(), app.launch()),
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
@@ -19,6 +19,8 @@ from fabric.utils import (
|
|||||||
)
|
)
|
||||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||||
|
|
||||||
|
from bar.config import VINYL
|
||||||
|
|
||||||
|
|
||||||
class StatusBar(Window):
|
class StatusBar(Window):
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -74,6 +76,8 @@ class StatusBar(Window):
|
|||||||
overlays=[self.cpu_progress_bar, self.progress_label],
|
overlays=[self.cpu_progress_bar, self.progress_label],
|
||||||
)
|
)
|
||||||
self.player = Player()
|
self.player = Player()
|
||||||
|
self.vinyl = None
|
||||||
|
if VINYL["enable"]:
|
||||||
self.vinyl = VinylButton()
|
self.vinyl = VinylButton()
|
||||||
|
|
||||||
self.status_container = Box(
|
self.status_container = Box(
|
||||||
@@ -83,11 +87,12 @@ class StatusBar(Window):
|
|||||||
children=self.progress_bars_overlay,
|
children=self.progress_bars_overlay,
|
||||||
)
|
)
|
||||||
|
|
||||||
end_container_children = [
|
end_container_children = []
|
||||||
self.vinyl,
|
|
||||||
self.status_container,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
if self.vinyl:
|
||||||
|
end_container_children.append(self.vinyl)
|
||||||
|
|
||||||
|
end_container_children.append(self.status_container)
|
||||||
if self.system_tray:
|
if self.system_tray:
|
||||||
end_container_children.append(self.system_tray)
|
end_container_children.append(self.system_tray)
|
||||||
|
|
||||||
|
|||||||
75
bar/modules/window_fuzzy.py
Normal file
75
bar/modules/window_fuzzy.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import operator
|
||||||
|
from fabric.widgets.wayland import WaylandWindow as Window
|
||||||
|
from fabric.widgets.box import Box
|
||||||
|
from fabric.widgets.label import Label
|
||||||
|
from fabric.widgets.entry import Entry
|
||||||
|
from fabric.utils import idle_add
|
||||||
|
from gi.repository import Gdk
|
||||||
|
|
||||||
|
|
||||||
|
class FuzzyWindowFinder(Window):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
monitor: int = 0,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name="finder",
|
||||||
|
anchor="center",
|
||||||
|
monitor=monitor,
|
||||||
|
keyboard_mode="on-demand",
|
||||||
|
type="popup",
|
||||||
|
visible=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._all_windows = ["Test", "Uwu", "Tidal"]
|
||||||
|
|
||||||
|
self.viewport = Box(name="viewport", spacing=4, orientation="v")
|
||||||
|
|
||||||
|
self.search_entry = Entry(
|
||||||
|
name="search-entry",
|
||||||
|
placeholder="Search Windows...",
|
||||||
|
h_expand=True,
|
||||||
|
editable=True,
|
||||||
|
notify_text=self.notify_text,
|
||||||
|
on_activate=lambda entry, *_: self.on_search_entry_activate(
|
||||||
|
entry.get_text()
|
||||||
|
),
|
||||||
|
on_key_press_event=self.on_search_entry_key_press,
|
||||||
|
)
|
||||||
|
self.picker_box = Box(
|
||||||
|
name="picker-box",
|
||||||
|
spacing=4,
|
||||||
|
orientation="v",
|
||||||
|
children=[self.search_entry, self.viewport],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(self.picker_box)
|
||||||
|
self.arrange_viewport("")
|
||||||
|
|
||||||
|
def notify_text(self, entry, *_):
|
||||||
|
text = entry.get_text()
|
||||||
|
self.arrange_viewport(text) # Update list on typing
|
||||||
|
print(text)
|
||||||
|
|
||||||
|
def on_search_entry_key_press(self, widget, event):
|
||||||
|
# if event.keyval in (Gdk.KEY_Up, Gdk.KEY_Down, Gdk.KEY_Left, Gdk.KEY_Right):
|
||||||
|
# self.move_selection_2d(event.keyval)
|
||||||
|
# return True
|
||||||
|
print(event.keyval)
|
||||||
|
if event.keyval in [Gdk.KEY_Escape, 103]:
|
||||||
|
self.hide()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def on_search_entry_activate(self, text):
|
||||||
|
print(f"activate {text}")
|
||||||
|
|
||||||
|
def arrange_viewport(self, query: str = ""):
|
||||||
|
self.viewport.children = [] # Clear previous entries
|
||||||
|
|
||||||
|
filtered = [w for w in self._all_windows if query.lower() in w.lower()]
|
||||||
|
|
||||||
|
for window in filtered:
|
||||||
|
self.viewport.add(
|
||||||
|
Box(name="slot-box", orientation="h", children=[Label(label=window)])
|
||||||
|
)
|
||||||
29
bar/styles/finder.css
Normal file
29
bar/styles/finder.css
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#picker-box {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: rgba(40, 40, 40, 0.95); /* darker for contrast */
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#viewport {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: rgba(30, 30, 30, 0.9); /* dark background for contrast */
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
color: white; /* ensure contrast */
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewport > * {
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewport:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15); /* hover feedback */
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
@import url("./menu.css");
|
@import url("./menu.css");
|
||||||
@import url("./vinyl.css");
|
@import url("./vinyl.css");
|
||||||
@import url("./bar.css");
|
@import url("./bar.css");
|
||||||
|
@import url("./finder.css");
|
||||||
|
|
||||||
|
|
||||||
/* unset so we can style everything from the ground up. */
|
/* unset so we can style everything from the ground up. */
|
||||||
|
|||||||
2
example.yaml
Normal file
2
example.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
vinyl:
|
||||||
|
enabled: true
|
||||||
36
flake.nix
36
flake.nix
@@ -32,7 +32,12 @@
|
|||||||
{
|
{
|
||||||
formatter = pkgs.nixfmt-rfc-style;
|
formatter = pkgs.nixfmt-rfc-style;
|
||||||
devShells.default = pkgs.callPackage ./nix/shell.nix { inherit pkgs; };
|
devShells.default = pkgs.callPackage ./nix/shell.nix { inherit pkgs; };
|
||||||
packages.default = pkgs.callPackage ./nix/derivation.nix { inherit (pkgs) lib python3Packages; };
|
packages = {
|
||||||
|
default = pkgs.callPackage ./nix/derivation.nix { inherit (pkgs) lib python3Packages; };
|
||||||
|
makku = pkgs.writeShellScriptBin "makku" ''
|
||||||
|
dbus-send --session --print-reply --dest=org.Fabric.fabric.bar /org/Fabric/fabric org.Fabric.fabric.Evaluate string:"finder.show()" > /dev/null 2>&1
|
||||||
|
'';
|
||||||
|
};
|
||||||
apps.default = {
|
apps.default = {
|
||||||
type = "app";
|
type = "app";
|
||||||
program = "${self.packages.${system}.default}/bin/bar";
|
program = "${self.packages.${system}.default}/bin/bar";
|
||||||
@@ -47,6 +52,11 @@
|
|||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.services.makku-bar;
|
||||||
|
|
||||||
|
settingsFormat = pkgs.formats.yaml { };
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options.services.makku-bar = {
|
options.services.makku-bar = {
|
||||||
enable = lib.mkEnableOption "makku-bar status bar";
|
enable = lib.mkEnableOption "makku-bar status bar";
|
||||||
@@ -56,17 +66,37 @@
|
|||||||
default = self.packages.${pkgs.system}.default;
|
default = self.packages.${pkgs.system}.default;
|
||||||
description = "The makku-bar package to use.";
|
description = "The makku-bar package to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
vinyl = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {
|
||||||
|
vinyl.enable = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf config.services.makku-bar.enable {
|
config = lib.mkIf config.services.makku-bar.enable {
|
||||||
systemd.user.services.makku-bar = {
|
systemd.user.services.makku-bar =
|
||||||
|
let
|
||||||
|
configFile = settingsFormat.generate "config.yaml" cfg.settings;
|
||||||
|
in
|
||||||
|
{
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "Makku Status Bar";
|
Description = "Makku Status Bar";
|
||||||
After = [ "graphical-session.target" ];
|
After = [ "graphical-session.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
Service = {
|
Service = {
|
||||||
ExecStart = "${config.services.makku-bar.package}/bin/bar";
|
ExecStart = "${config.services.makku-bar.package}/bin/bar --config ${configFile}";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,26 @@ python3Packages.buildPythonApplication {
|
|||||||
dependencies = with python3Packages; [
|
dependencies = with python3Packages; [
|
||||||
python-fabric
|
python-fabric
|
||||||
pywayland
|
pywayland
|
||||||
|
pyyaml
|
||||||
|
platformdirs
|
||||||
];
|
];
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
dontWrapGApps = true;
|
dontWrapGApps = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/${python3Packages.python.sitePackages}
|
||||||
|
cp -r bar $out/${python3Packages.python.sitePackages}/
|
||||||
|
|
||||||
|
# If you have any scripts to install
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp scripts/launcher.py $out/bin/bar
|
||||||
|
chmod +x $out/bin/bar
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
preFixup = ''
|
preFixup = ''
|
||||||
makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
|
makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ pkgs.mkShell {
|
|||||||
wayland-scanner
|
wayland-scanner
|
||||||
wayland
|
wayland
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
|
playerctl
|
||||||
|
|
||||||
(python3.withPackages (
|
(python3.withPackages (
|
||||||
ps: with ps; [
|
ps: with ps; [
|
||||||
setuptools
|
setuptools
|
||||||
@@ -39,6 +41,8 @@ pkgs.mkShell {
|
|||||||
pylsp-mypy
|
pylsp-mypy
|
||||||
pyls-isort
|
pyls-isort
|
||||||
python-lsp-ruff
|
python-lsp-ruff
|
||||||
|
pyyaml
|
||||||
|
platformdirs
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -14,14 +14,11 @@ description = "Fabric using Nix example."
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.scripts]
|
|
||||||
bar = "bar.main:main"
|
|
||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
include-package-data = true
|
include-package-data = true
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages]
|
||||||
where = ["."]
|
find = { namespaces = true }
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"*" = ["*.css", "styles"]
|
"*" = ["*.css", "styles"]
|
||||||
|
|||||||
21
scripts/launcher.py
Normal file
21
scripts/launcher.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
site_packages_dir = os.path.join(
|
||||||
|
script_dir,
|
||||||
|
os.pardir,
|
||||||
|
"lib",
|
||||||
|
f"python{sys.version_info.major}.{sys.version_info.minor}",
|
||||||
|
"site-packages",
|
||||||
|
)
|
||||||
|
|
||||||
|
if site_packages_dir not in sys.path:
|
||||||
|
sys.path.insert(0, site_packages_dir)
|
||||||
|
|
||||||
|
|
||||||
|
from bar.main import *
|
||||||
|
|
||||||
|
sys.argv[0] = os.path.join(script_dir, os.path.basename(__file__))
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user