diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 0000000..e69de29
diff --git a/docs/CNAME b/docs/CNAME
new file mode 100644
index 0000000..779c3a3
--- /dev/null
+++ b/docs/CNAME
@@ -0,0 +1 @@
+xsistant.highfunk.uk
diff --git a/docs/app.js b/docs/app.js
new file mode 100644
index 0000000..09c10de
--- /dev/null
+++ b/docs/app.js
@@ -0,0 +1,46 @@
+(async () => {
+ const btn = document.getElementById("download-btn");
+ const meta = document.getElementById("download-meta");
+ const pill = document.getElementById("version-pill");
+ const debName = document.getElementById("deb-name");
+
+ const fallbackHref = "https://github.com/overthelex/aisisstant/releases/latest";
+
+ try {
+ const res = await fetch(
+ "https://api.github.com/repos/overthelex/aisisstant/releases/latest",
+ { headers: { Accept: "application/vnd.github+json" } }
+ );
+ if (!res.ok) throw new Error("HTTP " + res.status);
+ const data = await res.json();
+
+ const deb = (data.assets || []).find(
+ (a) => a.name.endsWith(".deb") && a.content_type === "application/x-debian-package"
+ );
+
+ const tag = data.tag_name || "";
+ const size = deb ? (deb.size / 1024).toFixed(0) + " KB" : "";
+
+ if (deb) {
+ btn.href = deb.browser_download_url;
+ meta.textContent = `Ubuntu .deb · ${tag}${size ? " · " + size : ""}`;
+ if (debName) debName.textContent = deb.name;
+ } else {
+ btn.href = fallbackHref;
+ meta.textContent = `${tag} · Ubuntu .deb`;
+ }
+
+ if (pill) {
+ pill.textContent = tag ? `v${tag}` : "latest";
+ pill.classList.remove("pill-muted");
+ }
+ } catch (err) {
+ // Fall back to the latest-release page
+ btn.href = fallbackHref;
+ meta.textContent = "latest release · Ubuntu .deb";
+ if (pill) {
+ pill.textContent = "latest";
+ pill.classList.add("pill-muted");
+ }
+ }
+})();
diff --git a/docs/assets/favicon-16.png b/docs/assets/favicon-16.png
new file mode 100644
index 0000000..d8c3bbc
Binary files /dev/null and b/docs/assets/favicon-16.png differ
diff --git a/docs/assets/favicon-32.png b/docs/assets/favicon-32.png
new file mode 100644
index 0000000..9972a3f
Binary files /dev/null and b/docs/assets/favicon-32.png differ
diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico
new file mode 100644
index 0000000..b42efc2
Binary files /dev/null and b/docs/assets/favicon.ico differ
diff --git a/docs/assets/logo-mark-light.svg b/docs/assets/logo-mark-light.svg
new file mode 100644
index 0000000..6082d14
--- /dev/null
+++ b/docs/assets/logo-mark-light.svg
@@ -0,0 +1,10 @@
+
+ aisisstant
+
+
+
+
+
+
+
+
diff --git a/docs/assets/logo-mark-on-dark-512.png b/docs/assets/logo-mark-on-dark-512.png
new file mode 100644
index 0000000..0562060
Binary files /dev/null and b/docs/assets/logo-mark-on-dark-512.png differ
diff --git a/docs/assets/logo-wordmark-light.svg b/docs/assets/logo-wordmark-light.svg
new file mode 100644
index 0000000..c70b7f2
--- /dev/null
+++ b/docs/assets/logo-wordmark-light.svg
@@ -0,0 +1,13 @@
+
+ aisisstant
+
+
+
+
+
+
+
+ aisisstant
+
diff --git a/docs/assets/readme-hero.png b/docs/assets/readme-hero.png
new file mode 100644
index 0000000..deab7e8
Binary files /dev/null and b/docs/assets/readme-hero.png differ
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..f296c7d
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+Xsistant — Activity Intelligence for Ubuntu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
XSISTANT
+
ACTIVITY INTELLIGENCE
+
+
+ An activity-aware agent for Ubuntu/GNOME. Turns keyboard, mouse and window focus
+ into per-project time — automatically, without a manual timer.
+
+
+
+ loading…
+ Ubuntu 22.04+
+ GNOME Shell 45+
+ MIT
+
+
+
+
+
+ What it does
+
+
+
⌨
+
Raw input capture
+
Reads keyboard and mouse events straight from /dev/input/event* via evdev. Honest, app-agnostic counts.
+
+
+
🪟
+
Focused-window tracking
+
AT-SPI over D-Bus with switchamba fallback. Knows exactly which window owns your attention, not just which app.
+
+
+
📂
+
Project attribution
+
Walks /proc/<pid>/cwd for the focused process and its children. Titles lie — working directories don't.
+
+
+
🐘
+
PostgreSQL storage
+
Dockerized Postgres 16 as the backing store. Second-level resolution, SQL-queryable, never leaves your box.
+
+
+
🎛
+
GNOME top-bar indicator
+
Live activity badge, pause/resume, stats panel, and full-quit — all from the Shell panel.
+
+
+
📦
+
Native .deb
+
Built via dpkg-buildpackage on every push to master. CalVer versioning. One apt install away.
+
+
+
+
+
+ Install
+
+
+
From the .deb
+
sudo apt install ./aisisstant_*.deb
+gnome-extensions enable aisisstant-tracker@vovkes
+
Download the package above, then run these two commands.
+
+
+
From source
+
git clone https://github.com/overthelex/aisisstant.git
+cd aisisstant
+./scripts/install.sh
+gnome-extensions enable aisisstant-tracker@vovkes
+
Installs Python deps, starts Postgres via Docker Compose, registers the systemd user service, copies the GNOME extension.
+
+
+
+
+
+ What's next
+
+
coming up
+
Plane integration — worklog without a stopwatch
+
+ Xsistant already knows which project you're in. The next layer matches the active
+ task — by branch name (feature/PROJ-123-…), IDE window title, or an explicit
+ pick in the top-bar indicator — and writes minutes back to Plane via its API.
+
+
No manual time entry. Honest numbers at the end of the sprint.
+
+
+
+
+ Stack
+
+ Python 3.12
+ asyncio
+ asyncpg
+ evdev
+ pydbus
+ PostgreSQL 16
+ Docker Compose
+ GTK 4
+ libadwaita
+ GNOME Shell ext.
+ systemd --user
+ AT-SPI / D-Bus
+ dpkg-buildpackage
+ GitHub Actions
+ CalVer
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 0000000..6e8558c
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,342 @@
+:root {
+ --bg: #0d1117;
+ --bg-2: #161b22;
+ --bg-3: #1a2332;
+ --surface: rgba(255, 255, 255, 0.04);
+ --surface-hi: rgba(255, 255, 255, 0.08);
+ --border: rgba(255, 255, 255, 0.08);
+ --border-hi: rgba(88, 166, 255, 0.35);
+ --text: #e6edf3;
+ --text-dim: rgba(255, 255, 255, 0.65);
+ --text-mute: rgba(255, 255, 255, 0.4);
+ --accent: #58a6ff;
+ --accent-hi: #7db9ff;
+ --accent-glow: rgba(88, 166, 255, 0.45);
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
+ "Helvetica Neue", Arial, sans-serif;
+ --radius: 14px;
+ --radius-lg: 22px;
+}
+
+* { box-sizing: border-box; }
+html, body { margin: 0; padding: 0; }
+html { scroll-behavior: smooth; }
+
+body {
+ font-family: var(--sans);
+ color: var(--text);
+ background: var(--bg);
+ background-image:
+ radial-gradient(1100px 600px at 15% -10%, rgba(88, 166, 255, 0.12), transparent 60%),
+ radial-gradient(900px 500px at 110% 10%, rgba(124, 58, 237, 0.08), transparent 60%),
+ linear-gradient(160deg, #0d1117 0%, #161b22 40%, #1a2332 100%);
+ background-attachment: fixed;
+ line-height: 1.55;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+}
+
+a { color: var(--accent); text-decoration: none; transition: color 160ms; }
+a:hover { color: var(--accent-hi); }
+
+code {
+ font-family: var(--mono);
+ font-size: 0.92em;
+ background: rgba(255, 255, 255, 0.06);
+ padding: 0.1em 0.4em;
+ border-radius: 5px;
+ color: #d5dae1;
+}
+
+pre {
+ background: rgba(0, 0, 0, 0.35);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 18px 20px;
+ overflow-x: auto;
+ color: #d5dae1;
+ font-size: 13.5px;
+ line-height: 1.65;
+}
+pre code { background: transparent; padding: 0; }
+
+h2 {
+ font-size: 32px;
+ letter-spacing: -0.01em;
+ margin: 0 0 28px;
+}
+h3 { font-size: 17px; margin: 0 0 10px; letter-spacing: -0.005em; }
+.muted { color: var(--text-mute); }
+
+/* ---------- nav ---------- */
+.nav {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 40px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ color: var(--text);
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ font-size: 15px;
+}
+.brand img { filter: drop-shadow(0 0 8px var(--accent-glow)); }
+.nav-links { display: flex; gap: 26px; }
+.nav-links a {
+ color: var(--text-dim);
+ font-size: 14px;
+ font-weight: 500;
+}
+.nav-links a:hover { color: var(--text); }
+
+/* ---------- hero ---------- */
+.hero {
+ max-width: 860px;
+ margin: 40px auto 80px;
+ padding: 40px 40px 60px;
+ text-align: center;
+}
+.hero-inner { display: flex; flex-direction: column; align-items: center; }
+.hero-mark {
+ filter: drop-shadow(0 0 24px var(--accent-glow));
+ margin-bottom: 28px;
+}
+.hero-title {
+ font-size: clamp(48px, 8vw, 88px);
+ font-weight: 800;
+ letter-spacing: 0.12em;
+ margin: 0;
+ text-shadow: 0 2px 18px rgba(88, 166, 255, 0.35);
+}
+.hero-subtitle {
+ margin: 6px 0 0;
+ color: var(--accent);
+ font-size: 15px;
+ letter-spacing: 0.32em;
+ font-weight: 500;
+}
+.hero-divider {
+ width: 88px;
+ height: 2px;
+ background: var(--accent);
+ opacity: 0.55;
+ margin: 32px 0;
+ border-radius: 2px;
+}
+.hero-lead {
+ max-width: 620px;
+ font-size: 18px;
+ color: var(--text-dim);
+ margin: 0 0 40px;
+ line-height: 1.6;
+}
+.hero-actions {
+ display: flex;
+ gap: 14px;
+ flex-wrap: wrap;
+ justify-content: center;
+ margin-bottom: 30px;
+}
+.hero-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ justify-content: center;
+ margin: 6px 0 0;
+}
+
+/* ---------- buttons ---------- */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 14px;
+ padding: 14px 22px;
+ border-radius: 999px;
+ font-weight: 600;
+ font-size: 15px;
+ transition: transform 160ms, box-shadow 200ms, background 200ms, border-color 200ms;
+ cursor: pointer;
+}
+.btn-primary {
+ background: linear-gradient(135deg, #58a6ff 0%, #3b82f6 100%);
+ color: #0b1220;
+ box-shadow: 0 10px 30px -10px var(--accent-glow),
+ inset 0 1px 0 rgba(255, 255, 255, 0.35);
+}
+.btn-primary:hover {
+ transform: translateY(-1px);
+ color: #0b1220;
+ box-shadow: 0 14px 36px -10px var(--accent-glow),
+ inset 0 1px 0 rgba(255, 255, 255, 0.35);
+}
+.btn-icon { font-size: 18px; line-height: 1; }
+.btn-label {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 2px;
+ line-height: 1.15;
+}
+.btn-label strong { font-size: 16px; font-weight: 700; }
+.btn-label em {
+ font-style: normal;
+ font-weight: 500;
+ font-size: 12px;
+ opacity: 0.75;
+ letter-spacing: 0.01em;
+}
+.btn-ghost {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ color: var(--text);
+}
+.btn-ghost:hover {
+ background: var(--surface-hi);
+ border-color: var(--border-hi);
+ color: var(--text);
+}
+
+/* ---------- pills ---------- */
+.pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 5px 12px;
+ border-radius: 999px;
+ font-size: 12px;
+ letter-spacing: 0.04em;
+ font-weight: 600;
+ background: rgba(88, 166, 255, 0.12);
+ color: var(--accent);
+ border: 1px solid rgba(88, 166, 255, 0.25);
+}
+.pill-muted {
+ background: var(--surface);
+ border-color: var(--border);
+ color: var(--text-dim);
+ font-weight: 500;
+}
+
+/* ---------- features ---------- */
+.features, .install, .roadmap, .tech {
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 70px 40px;
+}
+.feature-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
+ gap: 18px;
+}
+.feature {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 24px;
+ transition: transform 200ms, border-color 200ms, background 200ms;
+}
+.feature:hover {
+ transform: translateY(-2px);
+ border-color: var(--border-hi);
+ background: var(--surface-hi);
+}
+.feature-icon {
+ width: 44px; height: 44px;
+ display: inline-flex; align-items: center; justify-content: center;
+ background: rgba(88, 166, 255, 0.1);
+ border: 1px solid rgba(88, 166, 255, 0.25);
+ border-radius: 12px;
+ font-size: 20px;
+ margin-bottom: 14px;
+}
+.feature p {
+ color: var(--text-dim);
+ font-size: 14.5px;
+ margin: 0;
+ line-height: 1.6;
+}
+
+/* ---------- install ---------- */
+.install-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+ gap: 22px;
+}
+.install-col {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 24px;
+}
+.install-col p { color: var(--text-dim); font-size: 13.5px; margin: 12px 0 0; }
+
+/* ---------- roadmap ---------- */
+.roadmap-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 34px;
+ position: relative;
+ overflow: hidden;
+}
+.roadmap-card::before {
+ content: "";
+ position: absolute; inset: 0;
+ background: radial-gradient(500px 260px at 90% -10%, rgba(88, 166, 255, 0.16), transparent 60%);
+ pointer-events: none;
+}
+.roadmap-tag {
+ display: inline-block;
+ padding: 5px 11px;
+ font-size: 11px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ background: rgba(88, 166, 255, 0.15);
+ color: var(--accent);
+ border: 1px solid rgba(88, 166, 255, 0.3);
+ border-radius: 999px;
+ margin-bottom: 18px;
+}
+.roadmap-card h3 { font-size: 22px; margin-bottom: 14px; }
+.roadmap-card p { color: var(--text-dim); margin: 0 0 10px; font-size: 15px; }
+
+/* ---------- tech pills ---------- */
+.tech-pills { display: flex; flex-wrap: wrap; gap: 8px; }
+.tech-pill {
+ display: inline-block;
+ padding: 7px 13px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ font-size: 13px;
+ color: var(--text-dim);
+ font-family: var(--mono);
+}
+
+/* ---------- footer ---------- */
+.footer {
+ text-align: center;
+ padding: 50px 40px 60px;
+ color: var(--text-dim);
+ font-size: 14px;
+ border-top: 1px solid var(--border);
+ margin-top: 40px;
+}
+.footer p { margin: 6px 0; }
+
+/* ---------- mobile ---------- */
+@media (max-width: 720px) {
+ .nav { padding: 16px 22px; }
+ .nav-links { display: none; }
+ .hero { padding: 24px 22px 40px; margin-top: 20px; margin-bottom: 40px; }
+ .features, .install, .roadmap, .tech { padding: 50px 22px; }
+ h2 { font-size: 26px; }
+ .roadmap-card { padding: 26px; }
+}