A fast, self-hosted personal startpage, command palette, bookmark manager, and service dashboard. Card-based, multi-page, keyboard-friendly, and configurable without a production build step. Entirely self-contained — no external network required once loaded.
🌐 GitHub: https://github.com/warmbo/wartab
🌐 Forgejo: https://forgejo.example.com/cody/wartab (replace with your instance)
http://localhost:8081 or http://<your-ip>:8081
- Create, rename, and delete pages via the page bar
- Each page has its own set of cards, icon, and custom title
- Keyboard-driven page switching with
Ctrl+Tab - Empty pages show a helpful placeholder with an add-card prompt
Press Ctrl+K or Ctrl+P for a Spotlight-style launcher: fuzzy-search every page,
link, and app action (new card, new page, arrange, settings, shortcuts) and run it
with Enter. Type-ahead ranks results, highlights matches, and opens links in a new
tab or switches pages in place.
WarTab is installable as a real app (manifest + icons + service worker). The app shell and immutable assets are cached offline-first, so repeat loads are near-instant and the dashboard works with no network after first load.
Right-click anywhere in WarTab for a single target-aware menu rendered above every panel and overlay. Links get open/copy/edit/delete actions; cards get edit, arrange, duplicate, JSON export, and delete; page chrome and blank space expose Command, Add Card, Arrange, Pages, Settings, and Reload. Native cut/copy/paste remains available inside text inputs and Notes editors.
The shell now centers daily actions around a visible Command trigger, prominent Add button, compact telemetry disclosure, undo/redo, and a single overflow menu. Cards render as one of five semantic visual roles: Launcher, Metric, Canvas, Feed, or Ambient.
- Card Gallery — search and filter all modules, see purpose/setup/manual-size guidance, or apply a first-run page template.
- Edit Mode — select cards directly and open common or advanced editing tools.
- Layout Studio — preview manual width, height, mobile visibility, and mobile order changes; nothing mutates until Apply layout.
- Experience Presets — Command Center, Dense Homelab, Calm Minimal, Ambient Glass, and Mobile First; presets never move or resize cards.
- Mobile profiles — explicit per-card hide/order controls independent of desktop geometry.
Command accepts bookmarks, pages, cards, actions, direct URLs/IPs, and scoped input:
g proxmox clustering,yt home server build,ddg privacy tools@server plexto search service/bookmark targets> settingsto restrict results to actions192.168.1.100or a hostname to open a direct address
Operational cards use shared healthy/warning/degraded/offline/unknown states, concise latency context, expandable details, and checked/stale timestamps. Weather includes condition, high, low, precipitation probability, forecast, and freshness without becoming a full weather application.
| Section | Description |
|---|---|
| Links | Icon grid — auto-fill columns, per-link icons with Lucide/library/upload/emoji support |
| Link List | Single-column inline link rows |
| Search | Inline search bar with per-section engine selector (Google, DuckDuckGo, Bing, etc.) |
| Clock | Live time (12/24h) + date + optional calendar |
| Weather | Cached Open-Meteo conditions, high/low, precipitation, wind, and forecast — no API key |
| Iframe | Embed any self-hosted app (Home Assistant, Jellyfin, Grafana) |
| Notes | Inline editing directly on the card (click to type, Esc to finish) |
| Quotes | User-defined quotes with author attribution. Click to cycle. |
| Timer | Hours/minutes dropdown-set countdown with Start/Reset |
| API Poller | Fetch JSON from any endpoint, extract a value via dot-path, auto-refresh |
| RSS Feed | Render recent RSS/Atom headlines through the server proxy |
| Agenda | Read a public iCal feed and list upcoming events |
| Service Status | Monitor HTTP services with summary health, latency, detail, and freshness |
| Network | Check gateway, DNS, and internet reachability with summary latency |
| Markdown | Render safe static Markdown content |
| Smart Links | Local-only most-used or recently-used dashboard links |
- Lucide SVG — 2,000+ vector icons, color-matched to your theme
- Selfh.st service icons — 350+ self-hosted app logos (Jellyfin, Home Assistant, Portainer, etc.) — organized in a searchable Services tab
- Emoji — 500+ system emoji in the Icons tab
- Upload — upload custom icon images (auto-resized to 256×256)
- CDN Discovery — preview icons from the selfh.st CDN before downloading locally
All icons — card titles, link items, page tabs, and branding — use the same unified picker.
- Quick Arrange provides keyboard/touch directional movement.
- Layout Studio stages width, height, mobile visibility, and mobile order.
- Geometry changes apply only after explicit confirmation and can be undone.
- WarTab never automatically resizes, spans, packs, or reorders a user's cards.
A slide-out right panel for editing individual cards:
- Title — editable text field
- Icon — unified picker (Lucide + Services + Emoji + Upload), Change/Clear buttons with live preview
- Color — color picker + hex input, sets the card's accent bar
- Width/Height — slider controls, height up to 2 rows
- Sections — add, reorder (drag), delete. Each section has inline label + type selector
- Module-specific editors — tailored fields per section type (link table, clock checkboxes, weather API config, etc.)
- Save / Discard / Delete footer with confirmation modal and undo toast
A slide-out right panel organized into card-like sections:
| Section | Controls |
|---|---|
| Page | Title, icon |
| Background | Type (gradient/solid/image), value/URL, upload, previous images, random rotation toggle |
| Appearance | Card style (dark/light), card transparency, accent color, glass blur (px), animations toggle, accent bar toggle |
| Typography | Font color, font size (small/medium/large), font family — 30 Google Fonts, all pre-loaded |
| Status Bar | Enable/disable, source (local/Glances/custom URL), refresh interval, items selector (hostname/CPU/memory/disk/uptime) |
| Layout | Columns (1–6), card gap (px), page width (full/3/4/half), page height (full/compact) |
| Data | Export (download JSON), Import (file upload), Reset (with confirm + undo toast) |
| API Keys | Reference info for OpenWeatherMap and other services |
| Key | Action |
|---|---|
Ctrl+K or Ctrl+P |
Open command palette (search pages, links, actions) |
Ctrl+L |
Focus first search bar |
Ctrl+Shift+C |
Toggle config panel |
Ctrl+N |
New card |
Ctrl+Shift+N |
New page |
Ctrl+Tab |
Cycle to next page |
Escape |
Close any open panel (config/edit/icon picker/palette) |
Built-in /api/stats endpoint — CPU, memory, disk, uptime, load — all from /proc
with zero Python dependencies (no psutil). Returns JSON for the status bar widget.
All assets can be shipped with the repo or downloaded on demand:
- Lucide icon library — 602KB (
/static/lucide.min.js), included in repo - Inter font — weights 200–700 (
/static/fonts/), included in repo - Service icons — 350+ SVG icons downloaded from selfh.st CDN during setup (
python3 download_icons.sh) - Quotes — user-defined only, no network calls
Only the Font Family's non-Inter options require network (Google Fonts), and external integration endpoints; built-in weather uses Open-Meteo without a key.
- Architecture
- Configuration schema
- Module authoring
- Module UI contract
- API reference
- Testing guide
- Contributing
- Security policy
- Customer release checklist
- Consolidation roadmap
Distribution note: WarTab does not yet include a selected open-source license. Do not assume redistribution rights until the maintainers add one.
Works on a clean Debian install — installs everything automatically:
curl -sL https://github.com/warmbo/wartab/raw/main/setup.sh | bashThis single command:
- Installs Python 3, git, Pillow, and Avahi/mDNS
- Clones WarTab to
/opt/wartab - Creates an initial
config.jsonfrom defaults - Downloads service icons from selfh.st
- Starts WarTab as a systemd user service with mDNS advertising
- Enables linger (keeps running after logout)
Result: WarTab is live at http://localhost:8081.
# Build and run
docker build -t wartab .
docker run -d \
--name wartab \
-p 8081:8081 \
-v "$(pwd)/wartab-data/config.json:/app/config.json" \
-v "$(pwd)/wartab-data/notes:/app/notes" \
-v "$(pwd)/wartab-data/uploads:/app/uploads" \
wartab
# Or with Docker Compose
docker compose up -dData files appear in wartab-data/ for easy editing.
Zero dependencies beyond Python stdlib:
git clone https://github.com/warmbo/wartab.git
cd wartab
cp config.example.json config.json # optional — server falls back to defaults
python3 server.py --port 8081 --open # --open opens browser automatically
# python3 server.py --port 8081 --mdns # advertise via mDNS (install avahi-utils)The server works even without config.json — it serves built-in defaults on first run.
WarTab also ships as a browser extension for Chrome, Edge, and Firefox. See extension/INSTALL.md for installation instructions.
http://localhost:8081
If you installed with Option 1 (setup.sh), mDNS is already configured:
http://<hostname>.local:8081
If you installed manually, install Avahi and run the server with --mdns:
sudo apt install avahi-utils
python3 server.py --port 8081 --mdns| Browser | Method |
|---|---|
| Firefox | Install "New Tab Override" → set URL to http://<hostname>.local:8081 |
| Chrome/Edge/Brave | Install "New Tab Redirect" → set URL to http://<hostname>.local:8081 |
systemctl --user daemon-reload
systemctl --user enable --now wartab.service
journalctl --user -u wartab.service -f # follow logsIf you used Option 1, this is already done for you.
Config is stored server-side in config.json via the /api/config endpoint.
Changes made in the UI are saved automatically with navigator.sendBeacon.
Export, import, and reset are available from the config panel's Data section.
Multi-device: point multiple browsers to the same WarTab server to share config.
wartab/
├── index.html # App shell
├── style.css # Glass + skeuomorphic styles
├── app.js # All logic: render, pages, config, drag-drop, widgets
├── storage.js # Storage adapter (server or browser extension)
├── server.py # Python HTTP server (zero deps)
├── Dockerfile # Container build
├── docker-compose.yml # Container orchestration
├── download_icons.sh # Fetches service icons from selfh.st CDN
├── setup.sh # Debian install script
├── manifest.json # Chrome extension manifest
├── config.json # Server-side config (auto-managed)
├── icons/ # 2,363 SVG service icons (selfhst/icons) + extension icons
├── static/
│ ├── lucide.min.js # Lucide icon library (local copy, 602KB)
│ └── fonts/ # Inter font files + CSS
├── modules/ # Pluggable card section modules
├── notes/ # Notes saved as .md files
├── uploads/ # User-uploaded background images
├── extension/ # Browser extension build files and manifests
├── PLAN.md # Architecture design document
├── HIERARCHY.md # Component tree & class naming reference
└── README.md # This file
Single-page app — no framework, no build step, no node_modules. Just open and go.
- Storage: Server-side
config.jsonvia REST API (/api/config). No database. Fire-and-forget saves vianavigator.sendBeacon. Config syncs across devices. - Server: Python
http.server— single file, no pip deps. Handles static files, config CRUD, image upload/resize (PIL optional), stats endpoint, and icon saving. - Icons: Lucide SVGs (2,000+) + selfhst/icons SVGs (350+) + system emoji (500+).
Dynamic
lucide.iconsdetection — always uses the complete runtime set. - Card System:
CARD_MODULESregistry — each section type owns{ defaults, render, editor }. Add new types without touching core rendering. - Pages: Config is organized into
config.pageswithconfig.pageOrderandconfig.currentPage. Cards live per-page. Zero-touch repointing ofconfig.cards. - Theming: CSS custom properties for accent, glass blur, font, card style — all togglable from the UI. Dark/light modes with dynamic opacity blending.
- Drag & Drop: Pointer Events API, simGrid CSS Grid auto-placement simulation, FLIP animations, column-snap logic with grab-offset tracking.
For a deeper look at the component hierarchy and data model, see HIERARCHY.md and PLAN.md.
Every commit must be pushed to both remotes:
git push github <branch>
git push origin <branch> # Forgejo| Alias | Target |
|---|---|
github |
github.com/warmbo/wartab |
origin |
self-hosted Forgejo remote (e.g. forgejo.example.com/cody/wartab) |
All <script src> and <link rel="stylesheet"> tags in index.html use ?v=BUILD as
a placeholder. The server (server.py) automatically replaces BUILD with the current
git describe --always --tags --dirty output at serve time.
This means:
- Every deploy automatically gets a unique cache-busting version
- No manual version string updates needed
- Browsers always fetch fresh assets after a server restart
- The footer displays the same git hash as the build version
The server reads GIT_VERSION at startup from git describe. If git is unavailable,
the fallback is 'dev'.
- Create
modules/<type>.jswith aregisterModule('type', { defaults, render, editor })call - Add the type to both picker lists in
app.js:- Section editor type selector (around line 760)
- New card modal (around line 2140)
- Add a
<script src="modules/<type>.js?v=BUILD" defer>toindex.html - Commit and dual-push to both remotes
| Branch | Purpose |
|---|---|
main |
Self-hosted server version (current) |
extension |
Browser extension variant — rewrites storage.js for chrome.storage, patches proxy-based fetches to direct fetch, stubs server-only features |
See extension/PLAN.md for the full extension implementation plan.