A transparent, fullscreen overlay OS layer for Linux — triggered by a hotkey, driven by natural language, rendered as floating glass widgets.
"It's a shell that lives on top of your entire screen — like a heads-up display for your desktop. You press a key, a transparent layer appears, you type what you want in plain English, and it just… does it."
What is LSD? • Architecture • Status • Getting Started • Adding Extensions • Widget Conventions • Entity Resolution • Automations • Roadmap
LSD stands for Lexicon Shell Daemon — but in plain terms, it's a see-through layer that sits on top of your entire Linux desktop. Think of it like a HUD in a video game, except it's for your real computer.
Here's what that means in practice:
- You press a hotkey (like
Super + ``) and a transparent fullscreen window instantly appears over whatever you were doing — your browser, your code editor, everything stays visible underneath. - You type in plain English into a bar at the bottom (the "Synapse Bar"): things like
"clock","timer 5m","what's the date","theme cyberpunk", or even shell commands likelsandgit status. - Floating glass widgets appear on the overlay — a clock, a timer, a sticky note, a system monitor — whatever you asked for.
- Press Escape and the whole thing vanishes. Zero CPU when hidden. Your desktop is untouched.
It's not a terminal emulator. It's not an app launcher. It's a daemon (a background process) that gives your desktop a programmable glass nervous system you can talk to in natural language.
The name is a triple meaning:
- Lexicon Shell Daemon — what it literally is
- LSD — because it puts a trippy transparent layer over your reality
- It's also just a daemon (
lexicond) — it runs silently in the background, always listening
LSD is a multi-layer system with body-inspired naming:
| Layer | Name | Tech | Role |
|---|---|---|---|
| 0 | The Body | Tauri + Bun + SvelteKit | Transparent fullscreen window, Rust IPC, WebView rendering |
| 0+ | Organs | Playwright ghost browser + DOM scraping + automation | Real web apps (GitHub, WhatsApp, etc.) as headless tabs — scraped and automated, not embedded |
| 1 | The Brain | Python + FastAPI + uv | Rule-based grammar engine, WebSocket hub, extension loader, organ orchestration, automation executor |
| 1+ | The Shell | Python PTY microservice | Real pseudo-terminal sessions (zsh/bash), full interactive shell over WebSocket |
| 2 | The Spine | ZeroMQ (pyzmq) | PUSH/PULL + PUB event bus between all layers |
| 3 | The Memory | SurrealDB (embedded) | Persistent document storage — UI state, history, workspaces, scraped data, automations, themes |
| 4 | External Sensors | CLI scripts, daemons | System monitors, ad-hoc data pushers via Spine |
Boot → dev.sh starts Brain + Shell + Spine + Tauri client
→ Tauri window opens briefly (WebView boots, JS connects)
→ Svelte connects to WebSocket (ws://127.0.0.1:8000/ws)
→ Brain sends RESTORE_STATE (widgets) + active theme from Memory
→ Rust auto-hides the window after 2s (WebView stays alive)
→ LSD is now idle — zero CPU, no taskbar icon
User presses Super+` → lexicon-toggle PUSHes "lexicon/toggle" to Spine (:5557)
→ Spine dispatches to Brain handler
→ Brain broadcasts TOGGLE_VISIBILITY over WebSocket
→ Svelte calls invoke("toggle_window") → Rust IPC
→ Rust shows window + sets fullscreen + focuses
→ Saved widgets reappear instantly (already in memory)
User presses Escape → Svelte calls invoke("toggle_window") → Rust hides window
→ Window vanishes instantly, WebView stays connected
User types "clock" → Svelte sends { type: "query", text: "clock" } via WebSocket
→ Brain logs command to Memory, runs GrammarEngine
→ extensions/clock.py match() hits → action() returns RENDER_WIDGET
→ Sent back over WebSocket
→ Svelte looks up registry["clock"], renders <ClockWidget>
→ Widget appears at (x, y) with glass blur frame
User types "!ls" → Svelte detects shell prefix, sends shell_spawn + shell_input
→ Brain relays to Shell microservice (ws://127.0.0.1:8765)
→ Shell service spawns real PTY zsh session
→ Output streams back: Shell → Brain → WebSocket → xterm.js widget
→ Full-screen terminal widget with colors, scrollback, resize
User types → Svelte sends { type: "apply_theme", name: "cyberpunk" }
"theme cyberpunk" → Brain looks up CSS from Memory (SurrealDB)
→ Broadcasts APPLY_THEME { css: "..." } to ALL connected clients
→ Svelte injects <style id="lexicon-theme"> into <head>
→ Every widget, bar, sidebar re-skins instantly via lx-* classes
User types "organs" → OrganManagerWidget spawns on canvas
→ Register any URL as an "organ" (e.g. github.com, web.whatsapp.com)
→ Brain's OrganManager opens a tab in a Playwright ghost browser
→ User pastes outer HTML of a page element → names it → scrapes
→ Playwright deep-scrapes all matching elements with field extraction
→ Structured data stored in Memory, viewable in DataViewWidget
User types → AutomationWidget spawns on canvas
"automations" → Select an organ (must be running)
→ Build step sequences: click, scroll, type, wait, navigate, extract
→ Save automation → run it → watch step-by-step progress
→ Extracted data auto-stored in Memory, auto-resolved to entities
→ One-shot actions: click/type/scroll any element, take screenshots
Current phase: All core layers functional — Body + Brain + Shell + Spine + Organs + Automations + Theming.
| Component | Status | Details |
|---|---|---|
| Tauri shell (Layer 0) | ✅ Complete | Transparent, borderless, always-on-top, fullscreen. Boots visible (WebView boots + WebSocket connects), Rust auto-hides after 2s. Toggle via lexicon-toggle (ZeroMQ → Spine → Brain → WS → Svelte → Rust IPC). Escape hides. Builds to ~16MB release binary. |
| Svelte frontend (Layer 0) | ✅ Complete | SPA with static adapter, frost-glass overlay, Synapse Bar with command history (↑↓), feedback toasts, connection status dot, lx-* CSS anchor classes for theming. |
| Paged workspace (Layer 0) | ✅ Complete | Vertically scrolling canvas divided into pages by thin divider lines. Sidebar with page numbers for smooth scroll navigation. Auto-expands as content grows. Widgets freely span across dividers. |
| Widget system (Layer 0) | ✅ Complete | Dynamic render list driven by WebSocket. Absolute positioning at (x, y, w, h). Glass-blur frames, pop-in animation, per-widget dismiss. Pointer-based dragging via handle strip. Corner resize handle. All positions/sizes persist to Memory. |
| Widget registry (Layer 0) | ✅ Complete | src/lib/widgets/index.js — maps widget_type → Svelte component. 13 widgets: clock, timer, date, note, calculator, sysmon, weather, help, terminal, organmanager, dataview, person, automation. |
| Multi-session shell (Layer 0+1) | ✅ Complete | Full PTY shell via dedicated Shell microservice (:8765). Multiple concurrent sessions — each is a real zsh/bash PTY with colors, env persistence, interactive programs. Rendered in xterm.js TerminalWidgets on the canvas. Ctrl+C, resize, signals all work natively. Synapse Bar routes to active session or spawns new ones (Ctrl+`, Ctrl+Tab). |
| Workspaces (Layer 0+3) | ✅ Complete | Named workspaces in SurrealDB. ✦ logo → workspace menu: create, switch, delete. Each workspace has independent widgets, shell state. 🧹 clear button wipes canvas + DB. Auto-saves on switch, auto-restores on load. |
| WebSocket protocol (Layer 0↔1) | ✅ Complete | Auto-reconnect with exponential backoff (2s → 30s). Message types: RENDER_WIDGET, REMOVE_WIDGET, CLEAR_WIDGETS, CLEAR_SHELL, FEEDBACK, RESTORE_STATE, SHELL_SPAWNED, SHELL_OUTPUT, SHELL_EXITED, SHELL_ERROR, WORKSPACE_INFO, TOGGLE_VISIBILITY, ORGAN_STATUS, ORGAN_LIST, APPLY_THEME, THEME_LIST, THEME_INFO, AUTOMATION_PROGRESS. |
| FastAPI Brain (Layer 1) | ✅ Complete | WebSocket at /ws, health at /health, toggle at POST /toggle, system stats at /system. Organ CRUD: POST/GET/DELETE /organs, /organs/:id/launch, /organs/:id/kill, /organs/:id/match, /organs/:id/scrape, /organs/:id/rescrape, /organs/:id/data. Automation CRUD: POST/GET/DELETE /organs/:id/automations, /organs/:id/automations/:name/run. One-shot actions: /organs/:id/actions/{click,type,scroll,navigate,screenshot,eval,extract,paginate}. Entity endpoints: GET /entities, GET /entities/:id, GET /entities/search/:q, POST /entities/resolve, DELETE /entities, GET /entities/stats/summary. Connection manager with broadcast. CORS enabled. Workspace CRUD + theme CRUD over WebSocket. |
| Grammar engine (Layer 1) | ✅ Complete | Dynamically loads every .py from extensions/, runs match() → action() pipeline. 15 extensions loaded. Fallback feedback for unknown commands. Help entries auto-collected. |
| Shell microservice (Layer 1+) | ✅ Complete | Standalone Python PTY server on :8765. Auto-detects user's default shell. Real PTY with TIOCSWINSZ resize, SIGHUP/SIGINT/SIGTSTP signals. Raw byte streaming. Multiple concurrent sessions. |
| Organ system (Layer 0+) | ✅ Complete | Generic organ framework — any URL can be an organ. Single headed Playwright Chromium browser (off-screen, persistent cookies). Organs are tabs. Deep structural scraping: paste outer HTML → tree parser discovers fields → CSS selector extraction → structured objects per match. 3-stage pipeline: similarity → structural validation → deduplication. OrganManagerWidget for CRUD. DataViewWidget for recursive layout rendering. |
| Automation engine (Layer 0+1) | ✅ Complete | Programmable browser automation — goes beyond static scraping. Build named step sequences: click, type, scroll, wait, navigate, extract, paginate, eval_js, screenshot, conditional, loop. Saved to Memory per organ. Run automations with step-by-step progress broadcast. One-shot actions for ad-hoc interaction. Paginate across multiple pages with auto-extraction. Extracted data auto-stored and auto-resolved to entity nodes. AutomationWidget for visual workflow building + execution. |
| Entity resolver (Layer 1+3) | ✅ Complete | Multi-strategy consensus identity resolution — zero external dependencies (except spaCy NER). Scraped data from any organ is automatically resolved into person-entity nodes. 5 voting strategies: fingerprint (phone/email exact), name similarity (Jaro-Winkler + Soundex + Double Metaphone), username correlation (cross-platform handle matching), token overlap (IDF-weighted + substring containment), contextual (avatar URL + phone digit matching). Only applicable strategies vote — absent signals don't dilute strong matches. Entities stored in SurrealDB with full source provenance. |
| Person widget (Layer 0) | ✅ Complete | PersonWidget — searchable grid of all resolved people, click-to-expand detail profiles with avatar, name, aliases, handles, contact info, source provenance. Built entirely from DataView meta node primitives. Full lx-person-* theme anchors. Search via "people", "person Rishi", "who is Mehta", "contacts". |
| Theming (Layer 0+3) | ✅ Complete | Full theme system with CSS injection. 4 built-in themes: cyberpunk, midnight, rose-pine, ember. Themes stored in SurrealDB Memory, auto-seeded from themes/*.css on boot. Apply via natural language ("theme cyberpunk"), WebSocket messages, or Spine channel (lexicon/theme). Active theme persists across restarts and broadcasts to all connected clients. Every UI element has lx-* anchor classes for granular styling. Reset to default with "reset theme". |
| SurrealDB Memory (Layer 3) | ✅ Complete | Embedded file-backed SurrealDB (surrealkv://). Persists: UI state (widgets), command history, shell sessions, named workspaces, organ registrations, scrape patterns, scraped data, automations, themes, active theme. All widget/shell data is workspace-scoped. Auto-restores on reconnect. No external server needed. |
| ZeroMQ Spine (Layer 2) | ✅ Complete | PUSH/PULL on :5557 + PUB on :5556. Channels: lexicon/toggle (show/hide), lexicon/theme (apply theme by name). External scripts PUSH commands → Brain dispatches → WebSocket broadcast. HTTP fallback at POST /toggle. |
| Dev tooling | ✅ Complete | dev.sh — menu-driven launcher (build / preview / dev mode). Starts Brain + Shell + Spine + Tauri. One command for everything. lexicon-toggle for hotkey binding. |
| Extension | Command Examples | What it does |
|---|---|---|
| clock | clock, time, what time is it |
Live-updating HH:MM:SS with gradient text |
| timer | timer 5m, countdown 1h30m, set timer 30s |
Countdown with progress bar, pause/reset controls |
| date | date, what day is it, today |
Weekday, date, year, day-of-year, week number, year-progress bar |
| note | note buy groceries, remind me to call bob |
Sticky notes with click-to-edit |
| calculator | calc 2+2, = pi * 2, math sqrt(144) |
Safe math eval with trig, log, constants |
| sysmon | system, stats, cpu |
Live CPU/RAM/disk bars polling from /proc |
| weather | weather, forecast |
Weather widget (demo mode, ready for API) |
| help | help, commands, ? |
Auto-generated guide from all extensions |
| clear | clear, dismiss all, close |
Wipe all widgets from canvas |
| organ | organs, scrape, organ manager |
Opens the Organ Manager widget |
| view | view github, dashboard, show data |
Data view — renders scraped organ data |
| theme | theme cyberpunk, themes, reset theme |
Apply, list, or reset visual themes |
| person | people, person Rishi, who is Mehta, contacts |
Person dashboard — resolved entity identity graph |
| automation | automations, automate, crawl, workflow |
Programmable browser automation — build, save, run crawl sequences |
| Component | Layer | Notes |
|---|---|---|
| Scheduled Automations | 1 | Cron-like scheduling — run automations on a timer automatically. |
| More Organs | 0+ | Discord, Gmail, etc. — same Playwright tab + scrape pattern. |
| CLI event tool | 4 | lexicon push "meeting in 5min" from terminal via ZeroMQ PUSH to Spine. |
| SysMon daemon | 4 | Push system metrics on schedule via Spine. |
- Linux (any desktop environment — GNOME, KDE, Hyprland, Sway, etc.)
- Rust + Cargo
- Bun
- uv (Python package manager)
- Python 3.13+
git clone https://github.com/vardhin/lexicon.git
cd lexicon
# Backend — install Python deps
cd lexicon-backend
uv sync
cd ..
# Shell microservice
cd lexicon-shell
uv sync
cd ..
# Frontend — install JS deps
cd lexicon-frontend
bun install
cd .../dev.shThis will:
- Build the Svelte static site (
bun run build) - Build the Tauri release binary (
bun run tauri build) - Start the Brain (FastAPI :8000) + Shell microservice (:8765) + ZeroMQ Spine (:5557/:5556)
- Launch the Tauri client in the background (hidden until toggled)
Bind lexicon-toggle to your preferred hotkey:
| DE | How to bind |
|---|---|
| GNOME | Settings → Keyboard → Custom Shortcuts → Super+`` → /path/to/lexicon/lexicon-toggle |
| KDE | System Settings → Shortcuts → Custom Shortcuts → add lexicon-toggle |
| Hyprland | bind = $mainMod, grave, exec, /path/to/lexicon/lexicon-toggle |
| Sway | bindsym $mod+grave exec /path/to/lexicon/lexicon-toggle |
Or toggle manually:
# Via ZeroMQ (instant, <5ms round-trip)
./lexicon-toggle
# Via HTTP (works from anywhere)
curl -X POST localhost:8000/togglePress Escape (with empty input) to hide the overlay.
How it works:
lexicon-togglePUSHes"lexicon/toggle"via ZeroMQ to the Spine (:5557). The Brain receives it, broadcastsTOGGLE_VISIBILITYover WebSocket to the Svelte frontend, which callsinvoke("toggle_window")— a Rust IPC command that does the actualwindow.show()/window.hide(). This bypasses Wayland permission issues. The entire toggle round-trip is <5ms.
LSD ships with 4 built-in themes. Type themes to list them, theme <name> to apply:
| Theme | Vibe |
|---|---|
cyberpunk |
Neon green on deep black, scanline overlay, terminal hacker aesthetic |
midnight |
Deep navy blue with soft purple accents, calm and focused |
rose-pine |
Warm muted tones — salmon, gold, teal from the Rosé Pine palette |
ember |
Amber and orange on dark charcoal, warm and cozy |
theme cyberpunk ← apply a theme
themes ← list all themes
reset theme ← revert to default
Themes are stored in SurrealDB and persist across restarts. The active theme auto-restores on reconnect and broadcasts to all connected clients. You can also trigger themes externally via the Spine: push "lexicon/theme cyberpunk" to :5557.
Custom themes: drop a .css file in themes/ — it's auto-seeded on next boot. Target lx-* classes (lx-widget, lx-bar, lx-input, lx-sidebar, etc.) to style any element.
Each extension is a single Python file in extensions/ with a standard interface:
# extensions/timer.py
import re, uuid
def match(text):
m = re.search(r"timer\s+(\d+)\s*(m|min|s|sec)?", text)
if m:
amount = int(m.group(1))
unit = (m.group(2) or "s")[0]
return amount * (60 if unit == "m" else 1)
return None
def action(original_text, seconds):
return {
"type": "RENDER_WIDGET",
"widget_id": f"timer-{uuid.uuid4().hex[:6]}",
"widget_type": "timer",
"x": 100, "y": 100, "w": 300, "h": 180,
"props": {"duration_seconds": seconds},
}
EXTENSION = {
"name": "timer",
"match": match,
"action": action,
"help": {
"title": "Timer",
"icon": "⏱",
"description": "Set a countdown timer",
"examples": ["timer 5m", "countdown 30s"],
},
}<!-- lexicon-frontend/src/lib/widgets/TimerWidget.svelte -->
<script>
export let props = {};
export let onDismiss = () => {};
// ... timer logic
</script>
<div class="timer-widget lx-timer">
<!-- ... timer UI with lx-* classes for theming -->
</div>// lexicon-frontend/src/lib/widgets/index.js
import TimerWidget from './TimerWidget.svelte';
const registry = {
// ...
timer: TimerWidget, // ← add here
};Restart the backend (it auto-reloads via uvicorn), rebuild the frontend (./dev.sh). Done.
Extensions can also return custom action types (not just RENDER_WIDGET) — the Brain's WebSocket handler intercepts them. See extensions/theme.py for an example that returns THEME_APPLY / THEME_RESET / THEME_LIST_REQUEST.
Every widget in LSD follows a strict set of conventions so that theming, layout, and composability work uniformly across the system.
Every widget Svelte component must export exactly two props:
<script>
export let props = {}; // Data/config from the backend (RENDER_WIDGET.props)
export let onDismiss = () => {}; // Called when the user clicks the ✕ dismiss button
</script>The outermost <div> must:
- Have a widget-specific CSS class: e.g.,
clock-widget,person-widget - Have a
lx-*theme anchor class: e.g.,lx-clock,lx-person,lx-widget - Fill its container:
width: 100%; height: 100%; - Include a dismiss button with class
dismiss lx-dismiss
<div class="my-widget lx-mywidget lx-widget">
<button class="dismiss lx-dismiss" on:click={onDismiss}>✕</button>
<!-- widget content -->
</div>Every significant element should have an lx-* class so themes can target it. The convention:
| Scope | Class Pattern | Example |
|---|---|---|
| Widget root | lx-{widget} |
lx-clock, lx-person, lx-sysmon |
| Generic widget | lx-widget |
All widgets |
| Dismiss button | lx-dismiss |
✕ close button |
| Label/header | lx-label |
"CLOCK", "SYSTEM" section labels |
| Input fields | lx-input |
Search bars, text inputs |
| Sub-elements | lx-{widget}-{part} |
lx-person-header, lx-clock-time, lx-person-card |
Themes target these via CSS:
/* themes/cyberpunk.css */
.lx-person-card { border-color: rgba(0, 255, 65, 0.2); }
.lx-person-name { color: #00ff41; }
.lx-dismiss:hover { color: #ff0040; }/* Standard glass widget styling */
.my-widget {
position: relative; width: 100%; height: 100%;
display: flex; flex-direction: column;
color: rgba(255,255,255,0.92);
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
padding: 10px 14px;
box-sizing: border-box;
overflow: hidden;
}
/* Standard dismiss button */
.dismiss {
position: absolute; top: 6px; right: 10px;
background: none; border: none;
color: rgba(255,255,255,0.3); font-size: 14px;
cursor: pointer; z-index: 10;
padding: 2px 6px; border-radius: 4px;
}
.dismiss:hover { color: #ff5f57; background: rgba(255,95,87,0.12); }The backend action() returns a RENDER_WIDGET message:
{
"type": "RENDER_WIDGET",
"widget_id": "unique-id",
"widget_type": "mywidget", # Must match registry key in index.js
"x": 100, "y": 100,
"w": 400, "h": 300,
"props": { ... }, # Passed to the Svelte component as `props`
}- Backend extension in
extensions/withmatch(),action(),EXTENSIONdict, andhelpentry - Frontend widget in
src/lib/widgets/withexport let props+export let onDismiss - Root element has widget-specific class +
lx-*theme anchor(s) - Dismiss button with
lx-dismiss - Registered in
src/lib/widgets/index.js - All interactive elements have
lx-*classes for theme injection - Widget fills container (
width: 100%; height: 100%) - Scrollable content areas use
scrollbar-width: thin
LSD includes a built-in identity resolution engine that automatically merges scraped data from different organs (WhatsApp, GitHub, etc.) into unified person nodes.
When you scrape data from an organ (manually or via automation), the Brain's entity resolver automatically:
- Extracts identity signals from each scraped item — names, usernames, phone numbers, emails, avatar URLs (using spaCy NER for classification)
- Compares signals against all existing entity nodes using 5 independent voting strategies
- Merges or creates — if the consensus score exceeds the threshold, the item is merged into an existing entity; otherwise a new entity is created
| Strategy | Weight | What it matches | Example |
|---|---|---|---|
| Fingerprint | 5.0 | Exact phone/email match (deterministic) | +91-9876543210 = +91 9876 543210 → auto-merge |
| Name Similarity | 2.5 | Jaro-Winkler + Soundex + Double Metaphone | "Rishi Metha" ~ "Rishi Mehta" → 0.95 |
| Username Match | 2.0 | Cross-platform handle correlation | rishimehta04 ~ rishi_mehta → 0.90 |
| Token Overlap | 1.5 | IDF-weighted Jaccard + substring containment | "rishimehta" contains {"rishi", "mehta"} |
| Contextual | 4.5 | Same avatar URL, phone digit patterns | Shared avatar → 0.85 |
The consensus engine only averages strategies that are applicable — if neither side has a phone number, the fingerprint strategy is excluded rather than dragging the score to zero.
people ← show all resolved person nodes
person Rishi ← search for a specific person
who is Mehta ← search by name
contacts ← alias for people
LSD's automation engine goes beyond static scraping — it lets you dynamically crawl through Playwright tabs by executing programmable action sequences.
An automation is a named sequence of steps. Each step is an action that runs against an organ's live Playwright page. Steps execute in order, and extracted data is automatically stored and resolved into entity nodes.
| Step | What it does | Key params |
|---|---|---|
| click | Click an element | selector, button, count, wait_after |
| type | Type text into an input | selector, text, clear, press_enter, delay |
| scroll | Scroll the page or element | direction (down/up/bottom/top), amount, selector |
| wait | Wait for a condition | selector + state, or delay (ms), or network idle |
| navigate | Go to a URL | url (absolute or relative), wait_until |
| extract | Scrape data from the page | outer_html (full structural) or selector + attribute |
| paginate | Click through pages, extracting each | next_selector, extract, max_pages, stop_if_empty |
| eval_js | Run arbitrary JavaScript | js (must return a value) |
| screenshot | Capture the page | full_page, selector, quality |
| conditional | Run sub-steps if selector exists | selector, then, otherwise |
| loop | Repeat sub-steps N times | count, steps, stop_selector, stop_if_no_change |
{
"name": "github_trending",
"description": "Scroll through GitHub trending and extract all repos",
"steps": [
{ "type": "navigate", "url": "https://github.com/trending" },
{ "type": "wait", "delay": 2000 },
{ "type": "scroll", "direction": "bottom", "wait_after": 1500 },
{ "type": "extract", "selector": "article.Box-row h2 a", "attribute": "href" }
]
}{
"name": "search_python_repos",
"steps": [
{ "type": "navigate", "url": "https://github.com/search?q=python&type=repositories" },
{ "type": "wait", "selector": "[data-testid='results-list']" },
{ "type": "paginate",
"next_selector": "a.next_page",
"extract": { "selector": "div.search-title a", "attribute": "textContent" },
"max_pages": 3 }
]
}{
"name": "scroll_and_extract",
"steps": [
{ "type": "loop", "count": 5, "stop_if_no_change": true, "steps": [
{ "type": "scroll", "direction": "down", "amount": 1200, "wait_after": 2000 },
{ "type": "extract", "selector": ".feed-item h3", "attribute": "textContent" }
]}
]
}For quick ad-hoc interactions without building a full automation:
| Endpoint | Method | Description |
|---|---|---|
/organs/{id}/actions/click |
POST | Click an element |
/organs/{id}/actions/type |
POST | Type into an input |
/organs/{id}/actions/scroll |
POST | Scroll the page |
/organs/{id}/actions/navigate |
POST | Navigate to a URL |
/organs/{id}/actions/screenshot |
POST | Take a screenshot |
/organs/{id}/actions/eval |
POST | Evaluate JavaScript |
/organs/{id}/actions/extract |
POST | Extract data with a pattern |
/organs/{id}/actions/paginate |
POST | Paginate + extract |
automations ← open the automation builder widget
automate ← same
crawl ← same
workflow ← same
lexicon/
├── dev.sh # Menu-driven launcher (build / preview / dev)
├── lexicon-toggle # Toggle script — bind to your DE hotkey
├── lexicon-toggle.sh # Alternative toggle script
│
├── extensions/ # Backend extensions (Python, auto-loaded)
│ ├── automation.py # Automation manager widget trigger
│ ├── calculator.py # Inline math evaluator
│ ├── clear.py # Clear all widgets
│ ├── clock.py # Clock widget
│ ├── date.py # Date display widget
│ ├── help.py # Help guide (auto-collects from all extensions)
│ ├── note.py # Sticky notes
│ ├── organ.py # Organ Manager widget trigger
│ ├── person.py # Person dashboard — entity identity graph
│ ├── sysmon.py # System monitor widget
│ ├── theme.py # Theme apply / list / reset
│ ├── timer.py # Countdown timer widget
│ ├── view.py # Data view / dashboard widget
│ └── weather.py # Weather widget (demo)
│
├── themes/ # Built-in themes (auto-seeded to SurrealDB)
│ ├── cyberpunk.css
│ ├── ember.css
│ ├── midnight.css
│ └── rose-pine.css
│
├── lexicon-backend/ # Layer 1: The Brain
│ ├── pyproject.toml
│ ├── run.sh
│ └── src/
│ ├── main.py # FastAPI + WS + organ + automation + entity endpoints
│ ├── engine.py # Grammar engine (auto-loads extensions/)
│ ├── automation.py # Automation engine — programmable browser actions
│ ├── entity_resolver.py # Multi-strategy identity resolution (spaCy NER)
│ ├── memory.py # SurrealDB embedded memory (Layer 3)
│ ├── spine.py # ZeroMQ event bus (Layer 2)
│ ├── shell.py # Shell session manager
│ ├── organ_manager.py # Playwright ghost browser + deep structural scraping
│ └── connection_manager.py # WebSocket connection tracking + broadcast
│
├── lexicon-shell/ # Layer 1+: Shell Microservice
│ ├── pyproject.toml
│ ├── run.sh
│ └── shell_server.py # PTY server on :8765
│
├── lexicon-frontend/ # Layer 0: The Body
│ ├── package.json
│ ├── src/
│ │ ├── app.html
│ │ ├── routes/+page.svelte
│ │ └── lib/
│ │ ├── ws.js # WebSocket client (auto-reconnect)
│ │ └── widgets/
│ │ ├── index.js # Widget registry (13 widgets)
│ │ ├── AutomationWidget.svelte
│ │ ├── ClockWidget.svelte
│ │ ├── TimerWidget.svelte
│ │ ├── DateWidget.svelte
│ │ ├── NoteWidget.svelte
│ │ ├── CalculatorWidget.svelte
│ │ ├── SysMonWidget.svelte
│ │ ├── WeatherWidget.svelte
│ │ ├── HelpWidget.svelte
│ │ ├── TerminalWidget.svelte
│ │ ├── OrganManagerWidget.svelte
│ │ ├── DataViewWidget.svelte
│ │ └── PersonWidget.svelte
│ └── src-tauri/
│ ├── tauri.conf.json
│ └── src/
│ ├── main.rs
│ └── lib.rs
│
├── infra/
│ └── data/ # SurrealDB file store (auto-created)
│
└── architecture/ # Architecture diagram (Mermaid)
- SurrealDB Memory — persist UI state, command history, auto-restore on launch
- Extensions — clock, timer, date, weather, notes, calculator, system monitor, help, clear, organ manager, data view, theme, person, automation
- Widget dragging + resizing — pointer-based repositioning, corner resize, persisted
- Paged workspace — scrollable multi-page canvas with sidebar navigation
- Multi-session shell — real PTY sessions via Shell microservice, xterm.js rendering, multiple terminals
- ZeroMQ Spine — PUSH/PULL + PUB event bus,
lexicon-toggle,lexicon/themechannels - Named workspaces — create, switch, delete workspaces with independent state
- Generic organ system — any URL as a Playwright ghost browser tab, deep structural HTML scraping, pattern matching, field extraction
- Theming — 4 built-in themes, SurrealDB persistence,
lx-*CSS anchors, Spine channel, natural language control - Entity resolver — multi-strategy consensus identity resolution (spaCy NER), auto-resolves scraped data into person nodes, PersonWidget dashboard
- Automation engine — programmable browser automation: click, scroll, type, wait, navigate, extract, paginate, eval, conditional, loop. Saved workflows per organ. One-shot actions. Auto-entity-resolution on extracted data
- Scheduled automations — cron-like scheduling for automations to run on a timer
- More Organs — Discord, Gmail, etc.
- CLI event tool —
lexicon push "reminder text"from terminal via Spine - SysMon daemon — push system metrics on schedule via Spine
MIT
Built by @vardhin