Files
pineNoteShell/pinenote-shell
fegger 05b18bdade Improve e-ink support with forced refreshes and no animations
- Add `einkRefresh()` helper calling `pdlc-cli --refresh` after rotation,
  brightness changes, waveform changes, and when the widget opens
- Disable CSS transitions and animations globally to reduce ghosting
- Remove button hover transform and transition effects
- Update README with e-ink-specific guidance and tuning notes
2026-07-13 13:38:46 +02:00
..

PineNote Shell Widget

A fullscreen GTK widget designed for Pine64 PineNote running Arch Linux with Hyprland, controllable via hyprgrass gestures.

Overview

This Astal-based GTK application provides:

  • Screen rotation (portrait/landscape)
  • Display brightness control (100%, 75%, 25%, 0% steps)
  • Power operations (suspend, shutdown, reboot)
  • Quick launch shortcuts for terminal, browser, and e-reader
  • Display refresh functionality
  • Audio waveform switching

Requirements

  • OS: Arch Linux with Hyprland
  • Desktop Environment: AGS (Aylur's GTK Shell) v3 / Astal
  • Dependencies: GJS, GTK 3, Astal (libastal-*-git), ags, hyprgrass
  • Display: Pine64 PineNote (or any compatible monitor)

Installation

Prerequisites

Astal and AGS are not distributed via npm. Install the system packages from the AUR:

# Install AGS and Astal core libraries (GTK3 + GJS bindings)
yay -S ags libastal-meta libastal-gjs-git libastal-hyprland-git libastal-brightness-git

# Base GTK/Layer-Shell support
sudo pacman -S gjs gtk3 gtk-layer-shell

Clone and Setup

cd ~/projects/pineNoteShell
# Project already set up in pinenote-shell/

# No npm dependencies are required
cd pinenote-shell

# Make widget executable
chmod +x ./src/main.js

Note: npm install is not needed for this project. The previous package.json referenced a package (@agi/gtk3) that does not exist on the npm registry.

Running

The entry point uses an ags run shebang, so you can start it directly:

./src/main.js

Or use the ags CLI explicitly:

ags run ./src/main.js

You can toggle the window from another terminal:

ags toggle pinenote-fullscreen -i pinenote-shell

Hyprland Configuration

Add the following to your ~/.config/hypr/hyprgrass.conf:

Note: AGS v3 uses ags toggle / ags request instead of the old --js-evaluate flag.

[gesture:toggle_widget]
hotkeys = [Mod1, T]
shorthand = "pinch open"
actions = [
    {
        command = "exec ags toggle pinenote-fullscreen -i pinenote-shell",
        timeout = 300,
        target = "window:pinenote-fullscreen"
    }
]

[gesture:brightness_up]
hotkeys = [Mod1, B]
shorthand = "swipe up"
actions = [
    {
        command = "exec ags request 'brightness 100' -i pinenote-shell",
        timeout = 300,
        target = "window:pinenote-fullscreen"
    }
]

[gesture:brightness_down]
hotkeys = [Mod1, V]
shorthand = "swipe down"
actions = [
    {
        command = "exec ags request 'brightness 0' -i pinenote-shell",
        timeout = 300,
        target = "window:pinenote-fullscreen"
    }
]

If you want the brightness requests to actually work, add a requestHandler to app.start in src/main.js, for example:

app.start({
    instanceName: "pinenote-shell",
    css: readFile(cssPath),
    requestHandler(argv, response) {
        if (argv[0] === "brightness") {
            const level = parseInt(argv[1], 10)
            brightness.set(level)
            response(`brightness set to ${level}`)
        }
        response("unknown command")
    },
    main() {
        PinenoteShell()
    },
})

(You'll need to lift the brightness manager out of the component scope for this.)

Additional Gestures

Add more gestures for other controls:

[gesture:rotate_landscape]
hotkeys = [Mod1, R]
shorthand = "swipe left"
actions = [
    {
        command = "exec ags request 'rotate landscape' -i pinenote-shell",
        timeout = 300,
        target = "window:pinenote-fullscreen"
    }
]

[gesture:launch_terminal]
hotkeys = [Mod1, T]
shorthand = "double tap left"
actions = [
    {
        command = "exec ags request 'launch terminal' -i pinenote-shell",
        timeout = 300,
        target = "window:pinenote-fullscreen"
    }
]

Usage

Basic Controls

  1. Open/Close: Activate with hyprgrass gesture (see configuration)
  2. Screen Rotation: Tap landscape/portrait icons to rotate display
  3. Brightness: Click brightness level buttons or use gestures in config
  4. System Actions: Use suspend/shutdown buttons for power operations
  5. Launch Apps: Click terminal, browser, or e-reader shortcuts

Advanced Features

  • Waveform Switching: Use the waveform control to switch between audio output modes
  • Display Refresh: Force display refresh with the refresh button
  • Status Display: Monitor rotation and brightness levels in footer

Configuration

Custom Applications

Edit src/main.js and modify the LauncherManager.launch() method:

const launchers = {
    terminal: "alacritty",           // Your preferred terminal
    browser: "brave-browser",       // Or chromium/firefox/edge
    "e-reader": "calibre",         // Your e-reader application
};

Hyprland Integration

The widget uses hyprctl and the AstalHyprland library to communicate with Hyprland:

  • Screen rotation: Monitors the focused monitor's transform property
  • Display refresh: Uses pdlc-cli --refresh for PineNote e-ink refresh
  • Brightness: Uses AstalBrightness (/sys/class/backlight/)

Styling

The widget uses CSS styling loaded from $HOME/.config/ags/style.css. The style.css file in this directory is the source of truth; copy or symlink it there before running:

mkdir -p ~/.config/ags
cp ./style.css ~/.config/ags/style.css

Common customization options:

  • background-color: Change widget background
  • border-radius, margin: Adjust widget borders and spacing
  • Icon sets: Use different icon themes for better visual consistency

Troubleshooting

Widget Won't Appear

  1. Check Astal/AGS installation: Ensure ags, gjs, and the Astal libraries are installed correctly
  2. Verify CSS path: Make sure ~/.config/ags/style.css exists; otherwise the app will crash on start
  3. Verify hyprgrass config: Make sure your gesture bindings are correct
  4. Debug output: Run with ags run ./src/main.js in a terminal to see console errors

Controls Not Working

  1. Permission issues: Some operations may require appropriate permissions
  2. Missing applications: Ensure your preferred terminal/browser exist
  3. Hyprland compatibility: Test basic hyprctl commands manually first

E-ink / PineNote-specific Tweaks

The code and CSS are already tuned for e-ink:

  • No CSS transitions or animations in style.css to avoid ghosting.
  • E-ink refreshes are triggered automatically after rotation, brightness changes, waveform changes, and when the widget opens.
  • If you still see ghosting, increase the refresh frequency or add einkRefresh() after other interactive actions in src/main.js.

Performance Issues

  • The widget is designed to be lightweight and efficient
  • For best performance on PineNote, avoid animations and continuous polling
  • E-ink displays update slowly; the app uses GObject signals instead of polling wherever possible
  • Monitor system resources with htop or similar tools

Future Enhancements

Potential features for future development:

  • Multi-monitor support
  • Advanced brightness curves (not just discrete steps)
  • Custom app launcher configuration
  • Audio waveform visualization
  • System monitoring widgets
  • Integration with PineNote-specific settings

License

This project is provided as-is for your PineNote setup. Use responsibly and test changes before deploying to production hardware.