Skip to content

wdes/tab-atelier

Repository files navigation

Tab Atelier

Build codecov Unsafe forbidden

A Guake-style drop-down terminal emulator for Linux (X11), built with Rust using alacritty_terminal, gpui (Zed's GPU-accelerated UI framework), and wattaouille for power monitoring.

Tab Atelier screenshot

Low battery warning

Features

Terminal

  • Drop-down terminal toggled with global hotkeys (default: ` and XF86Calculator, customizable in preferences)
  • Full terminal emulation via alacritty_terminal (colors, scrollback, bracketed paste, ...)
  • GPU-accelerated rendering via gpui
  • Text selection with mouse, copy/paste from context menu
  • Clickable URLs and file paths detected in terminal output
  • Reset input & color for misbehaving programs

Tabs

  • Multiple tabs with drag-and-drop reordering
  • Double-click to rename, right-click context menu
  • Ctrl+Shift+T to open a new tab (inherits working directory)
  • Alt+Tab to cycle between tabs
  • Shell exit detection with close/respawn confirmation

Session

  • Tabs, working directories, and full terminal output persisted across restarts
  • Active tab selection restored on startup

Preferences

  • Theme selection (Dark, Tomorrow Night Blue)
  • Window opacity (1%-100% slider)
  • Language (English, French)
  • Configurable toggle hotkeys (press any key to register, applied immediately)
  • Configurable browser and code editor for opening links

Monitoring

  • Per-tab CPU usage, power draw (watts), energy consumption (Wh), and uptime
  • Low battery warning with visual indicator

Integration

  • HTTP API with token auth and QR code for remote tab management from a phone
  • Wakatime time tracking (reads API key from Zed settings)
  • Screenshots (per-tab or full app) saved as BMP

Installation

cargo build --release
# Binary at target/release/tab-atelier

Requires Rust 2024 edition (rustc 1.92+).

Debian package

cargo deb
sudo apt install ./target/debian/tab-atelier_*.deb

The .deb lays out the following under FHS-standard paths:

Path Permission Contents
/usr/bin/tab-atelier 0755 The binary
/usr/share/applications/tab-atelier.desktop 0644 Desktop entry (registers Tab Atelier in app launchers)
/usr/share/icons/hicolor/scalable/apps/tab-atelier.svg 0644 App icon (scalable SVG, picked up by the desktop environment via the Icon=tab-atelier line in the .desktop)
/usr/share/doc/tab-atelier/ 0644 README.md, LICENSE, copyright

conffiles (per debian-policy §10.7.2): none. tab-atelier ships no system-wide configuration in /etc, so dpkg has no files to track between upgrades. All user-modifiable state — preferences, tab list, scrollback, uptime, energy, single-instance lock — lives under the user's $XDG_CONFIG_HOME and $XDG_STATE_HOME (see State below). The package's conf-files = [] in Cargo.toml records this intentionally.

dirs (per debian-policy §10.5): the package creates only what it installs (under /usr/bin, /usr/share/applications, /usr/share/icons/hicolor/scalable/apps, /usr/share/doc/tab-atelier). It does not pre-create any directory under /etc or /var. The per-user ~/.config/tab-atelier/ and ~/.local/{,state/}tab-atelier/ directories are created lazily by the running application on first save — dpkg never touches them, so a dpkg --purge leaves them in place for the user to remove manually if desired.

Running

tab-atelier               # normal mode
tab-atelier --read-only   # second instance, no writes

A normal launch acquires a single-instance lock on ~/.local/state/tab-atelier/tab-atelier.lock and exits if another normal instance is already running — concurrent writers would race each other and produce inconsistent state files.

--read-only skips the lock so any number of read-only instances can run alongside the primary one. In that mode tab-atelier never writes anything: no tabs.json rewrites, no per-tab output / uptime / energy files, no preference saves, no rename-time file moves. The preferences "Save" button is visually disabled. Useful for snapshotting the running workspace from a script or for poking around without disturbing live state.

Configuration

Font settings

Tab Atelier reads font configuration from your Zed editor settings:

File: $XDG_CONFIG_HOME/zed/settings.json (defaults to ~/.config/zed/settings.json)

Setting Description Default
ui_font_family Terminal font family monospace
ui_font_weight Font weight (100-900) 400
ui_font_size Font size in pixels 16
buffer_font_size Fallback if no ui_font_size 16
scroll_sensitivity Scroll speed multiplier 1.0

Preferences

In-app preferences (theme, opacity, language, browser, code editor) are stored in:

File: $XDG_CONFIG_HOME/tab-atelier/preferences.json (defaults to ~/.config/tab-atelier/preferences.json)

State

Tab Atelier splits persisted state across four files to keep a bad write to any one piece from corrupting the rest. Each file is written atomically (.tmp + fsync + rename) with rotated backups (.bak, .bak.1, .bak.2).

Data Path Notes
Tab list, working directories, active index ~/.local/tab-atelier/tabs.json Tiny, rewritten on every persist tick (~2 s)
Per-tab terminal scrollback ~/.local/state/tab-atelier/output_tab-<sanitized>-<crc32>.json One file per tab. Rewritten on every persist tick
Per-tab uptime (active seconds) ~/.local/state/tab-atelier/uptime_tab-<sanitized>-<crc32>.json One file per tab. Throttled to once every 30 s; final value flushed on shutdown
Per-tab energy (Wh) ~/.local/state/tab-atelier/power_tab-<sanitized>-<crc32>.json One file per tab. Throttled by delta (≥ 0.1 Wh consumed); final value flushed on shutdown
Single-instance lock ~/.local/state/tab-atelier/tab-atelier.lock Empty file. Held via flock(2); released automatically by the kernel on process exit (including crashes), so no manual cleanup needed

Tab filename = sanitized tab name (non-[A-Za-z0-9._-]_) plus an 8-hex-digit CRC32 of the original name, so two tabs whose sanitized forms collide (e.g. foo/bar and foo_bar) still land in distinct files. Renaming a tab in the UI moves all four files (output, uptime, power, plus their .baks) to the new name's slot so history isn't orphaned.

Power monitoring

On Intel systems with readable RAPL counters, each tab shows its estimated power usage in the right-click context menu. The estimate uses the same technique as wattaouille: per-tab watts = package watts * (tab CPU jiffies / total system jiffies). When RAPL is not available, only CPU percentage is shown.

Making RAPL readable

Since CVE-2020-8694 (PLATYPUS side-channel) the kernel ships /sys/class/powercap/intel-rapl/intel-rapl:*/energy_uj as mode 400, owned by root, so a regular user — including the one running tab-atelier — gets Permission denied. Symptom: the watts column on every tab card is empty, the stats popover shows CPU% only, and ~/.local/state/tab-atelier/power_tab-*.json files never get created.

One-shot for the current boot:

sudo chmod -R g+r,o+r /sys/devices/virtual/powercap/intel-rapl

Persistent (every boot) — drop a udev rule:

echo 'SUBSYSTEM=="powercap", ACTION=="add", RUN+="/bin/chmod -R g+r,o+r /sys/devices/virtual/powercap/intel-rapl"' | sudo tee /etc/udev/rules.d/99-rapl.rules
sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=powercap

After either, restart tab-atelier. PowerSensor::detect runs once at startup, so a mid-session permission fix is only picked up by the next launch.

What are jiffies? Jiffies are the Linux kernel's internal time-keeping unit — a counter that increments at a fixed rate (typically 100, 250, or 1000 Hz depending on CONFIG_HZ). Each tick, the kernel records CPU time consumed by every process. Per-process jiffies are read from /proc/[pid]/stat and total system jiffies from /proc/stat. The ratio between them gives the fraction of CPU a tab's shell used, which is multiplied by package power (from Intel RAPL) to estimate per-tab wattage.

HTTP API

Tab Atelier exposes tab state on http://<local-ip>:7890 as JSON. Access requires a bearer token, shown via a QR code in the right-click menu ("Remote control"). The response includes tab names, working directories, active tab index, and per-tab power stats. Tabs can be closed remotely via DELETE /tabs/{index}.

Wakatime

Wakatime integration is automatic if your Zed settings contain wakatime.settings.api-key. Heartbeats are sent with project detection (walks up to find .git).

Logging

Control log output with the RUST_LOG environment variable:

RUST_LOG=tab_atelier=info cargo run    # Startup, restore, API
RUST_LOG=tab_atelier=debug cargo run   # Verbose

Testing

cargo test
cargo clippy

License

MPL-2.0

Terminal rendering patterns based on Zed (Apache-2.0 / GPL-3.0).

About

The TabAtelier app

Topics

Resources

License

MPL-2.0, MPL-2.0 licenses found

Licenses found

MPL-2.0
LICENCE
MPL-2.0
LICENSE

Stars

Watchers

Forks

Contributors