chatdeck 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+ # Brings the Windows Terminal tab titled $env:DASH_MARK to the front, on any virtual desktop, and
2
+ # optionally types $env:DASH_TYPE into it.
3
+ # Used by terminals.py; set $env:DASH_VDLIB to VirtualDesktopLib.cs to switch desktops reliably.
4
+ $m = $env:DASH_MARK; $lib = $env:DASH_VDLIB
5
+ Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes
6
+ Add-Type @'
7
+ using System; using System.Text; using System.Runtime.InteropServices; using System.Collections.Generic;
8
+ public static class F {
9
+ delegate bool P(IntPtr h, IntPtr l);
10
+ [DllImport("user32.dll")] static extern bool EnumWindows(P cb, IntPtr l);
11
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
12
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetClassName(IntPtr h, StringBuilder s, int n);
13
+ [DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
14
+ [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr h, int c);
15
+ [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr h);
16
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
17
+ [DllImport("user32.dll")] static extern void keybd_event(byte k, byte s, uint f, UIntPtr e);
18
+ [DllImport("dwmapi.dll")] static extern int DwmGetWindowAttribute(IntPtr h, int a, out int v, int s);
19
+ public static List<IntPtr> Terminals() { var r = new List<IntPtr>();
20
+ EnumWindows((h, l) => { var c = new StringBuilder(64); GetClassName(h, c, 64);
21
+ if (c.ToString() == "CASCADIA_HOSTING_WINDOW_CLASS") r.Add(h); return true; }, IntPtr.Zero); return r; }
22
+ public static string Title(IntPtr h) { var t = new StringBuilder(512); GetWindowText(h, t, 512); return t.ToString(); }
23
+ public static bool Cloaked(IntPtr h) { int v; DwmGetWindowAttribute(h, 14, out v, 4); return v != 0; } // on another virtual desktop
24
+ public static bool Raise(IntPtr h) {
25
+ if (IsIconic(h)) ShowWindow(h, 9);
26
+ keybd_event(0x12, 0, 0, UIntPtr.Zero); keybd_event(0x12, 0, 2, UIntPtr.Zero); // Alt tap lets us take focus
27
+ return SetForegroundWindow(h);
28
+ }
29
+ }
30
+ '@
31
+ $vd = $false
32
+ function Show($h) {
33
+ if ([F]::Cloaked($h) -and $lib -and (Test-Path $lib)) {
34
+ if (-not $script:vd) { Add-Type -Path $lib; $script:vd = $true }
35
+ [VirtualDesktop.Desktop]::FromWindow($h).MakeVisible(); Start-Sleep -Milliseconds 250
36
+ }
37
+ [F]::Raise($h) | Out-Null
38
+ }
39
+ $A = [System.Windows.Automation.AutomationElement]
40
+ $tabCond = New-Object System.Windows.Automation.PropertyCondition($A::ControlTypeProperty, [System.Windows.Automation.ControlType]::TabItem)
41
+ function Tab($h) { # the tab carrying the mark, if this window's tabs are readable (not on another desktop)
42
+ foreach ($t in $A::FromHandle($h).FindAll([System.Windows.Automation.TreeScope]::Descendants, $tabCond)) { if ($t.Current.Name -eq $m) { return $t } }
43
+ }
44
+ function Done($h) {
45
+ # With $env:DASH_TYPE set, type that text into the chat and press Enter - but only once the chat's
46
+ # terminal really is the window in front, so keystrokes never land anywhere else.
47
+ if ($env:DASH_TYPE) {
48
+ Start-Sleep -Milliseconds 400
49
+ if ([F]::GetForegroundWindow() -ne $h) { "notfront"; exit }
50
+ Add-Type -AssemblyName System.Windows.Forms
51
+ [System.Windows.Forms.SendKeys]::SendWait($env:DASH_TYPE + "{ENTER}")
52
+ }
53
+ "ok"; exit
54
+ }
55
+ for ($i = 0; $i -lt 6; $i++) { # the terminal takes a moment to pick up the new title
56
+ $wins = [F]::Terminals()
57
+ foreach ($h in $wins) { if ([F]::Title($h) -eq $m) { Show $h; Done $h } }
58
+ foreach ($h in $wins) { if (-not [F]::Cloaked($h)) { $t = Tab $h; if ($t) {
59
+ $t.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern).Select(); Show $h; Done $h } } }
60
+ Start-Sleep -Milliseconds 250
61
+ }
62
+ # Last resort: a background tab in a window on another desktop. Those tabs can only be read once
63
+ # the window is on screen, so visit each such window, then go back if the chat isn't there.
64
+ $back = [F]::GetForegroundWindow()
65
+ foreach ($h in $wins) { if ([F]::Cloaked($h)) { Show $h; Start-Sleep -Milliseconds 300; $t = Tab $h; if ($t) {
66
+ $t.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern).Select(); Done $h } } }
67
+ if ($back -ne [IntPtr]::Zero) { Show $back }
68
+ "missing"
@@ -0,0 +1,35 @@
1
+ # chatdeck launcher for Windows (written by `chatdeck autostart on`): starts the chatdeck server
2
+ # in WSL if it isn't running, then the corner bubble and tray icon. With -Open it also opens the
3
+ # full dashboard window, or brings it forward if it's already open.
4
+ param([switch]$Open)
5
+ $Port = __PORT__
6
+ $Distro = "__DISTRO__"
7
+ $Cmd = "__CMD__"
8
+ $Url = "http://127.0.0.1:$Port/"
9
+ [System.Net.WebRequest]::DefaultWebProxy = $null
10
+ function Up { try { (Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 $Url).Content -match "<title>chatdeck</title>" } catch { $false } }
11
+
12
+ if (-not (Up)) {
13
+ # A login shell, so the server finds `claude` on the same PATH as in a terminal.
14
+ Start-Process wsl.exe -ArgumentList "-d $Distro -- bash -lc ""$Cmd serve --port $Port""" -WindowStyle Hidden
15
+ for ($i = 0; $i -lt 60 -and -not (Up); $i++) { Start-Sleep -Milliseconds 500 }
16
+ }
17
+
18
+ Start-Process powershell.exe -WindowStyle Hidden -ArgumentList "-NoProfile", "-WindowStyle", "Hidden", "-ExecutionPolicy", "Bypass",
19
+ "-File", "$PSScriptRoot\bubble.ps1", "-Port", "$Port"
20
+
21
+ if ($Open) {
22
+ Add-Type -Namespace Chatdeck -Name W -MemberDefinition @'
23
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr FindWindow(string c, string t);
24
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
25
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int c);
26
+ '@
27
+ $h = [Chatdeck.W]::FindWindow("Chrome_WidgetWin_1", "chatdeck")
28
+ if ($h -ne [IntPtr]::Zero) { [Chatdeck.W]::ShowWindow($h, 9) | Out-Null; [Chatdeck.W]::SetForegroundWindow($h) | Out-Null }
29
+ else {
30
+ $b = @("$env:ProgramFiles\Google\Chrome\Application\chrome.exe", "${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
31
+ "$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe", "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe") |
32
+ Where-Object { Test-Path $_ } | Select-Object -First 1
33
+ if ($b) { Start-Process $b -ArgumentList "--app=$Url", "--window-size=520,900" } else { Start-Process $Url }
34
+ }
35
+ }
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: chatdeck
3
+ Version: 0.1.0
4
+ Summary: A local dashboard for your Claude Code chats: what's running, what needs you, and what it costs.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/Abdifatah09/chatdeck
7
+ Project-URL: Issues, https://github.com/Abdifatah09/chatdeck/issues
8
+ Keywords: claude,claude-code,dashboard,ai,cli
9
+ Classifier: Environment :: Console
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # chatdeck
20
+
21
+ A local dashboard for your Claude Code chats: see which chats are running and which need you,
22
+ read and search old chats, track usage against your plan limits, and clean up transcripts.
23
+ Python 3.9+ standard library only, no dependencies.
24
+
25
+ ![A tour of chatdeck: a chat waiting for permission, chats grouped by project, searching and reading an old chat, and the light theme](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/media/tour.gif)
26
+
27
+ ## Features
28
+
29
+ - **Running now** – every live session with its context size and cost. Sessions stuck on a
30
+ permission prompt or a question get an amber **Waiting for you** badge and an optional chime.
31
+ **Open** jumps to the chat's terminal tab, **Rename** runs `/rename` in it so Claude names it after what it's about (double-click the name to type your own), and big chats get a
32
+ **Compact** button that runs `/compact`.
33
+ - **Saved chats** – read any chat without resuming it, search across all messages,
34
+ rename (so you know what's in it), export to Markdown, resume in a terminal, delete.
35
+ - **Usage** – tokens today and this week, top projects, 24h cost, and your 5-hour and weekly
36
+ limits with a forecast of when you'd hit them at the current pace.
37
+ - **Cleanup** – delete empty chats or chats older than 7, 30 or 90 days. Shows disk usage.
38
+ Running chats are never deleted.
39
+ - **By project** view: one section per project (git repo, including its worktrees and subfolders)
40
+ with its running and saved chats, and a **New chat** button that starts Claude Code in that project. Sort by recent, size, cost or messages. Light and dark themes.
41
+ Keyboard shortcuts: `/` search, `j`/`k` move, `Enter` read, `r` resume, `e` rename, `x` select, `Del` delete, `?` help.
42
+ - **Windows corner button and tray icon** – pop the dashboard up from the corner of the screen.
43
+
44
+ ## Install
45
+
46
+ ```sh
47
+ pipx install chatdeck # once it's on PyPI; until then:
48
+ pipx install git+https://github.com/Abdifatah09/chatdeck
49
+ ```
50
+
51
+ No pip? Clone the repo and run `./install.sh`. It puts a `chatdeck` command in `~/.local/bin`
52
+ that runs straight from the clone.
53
+
54
+ ## Try it first
55
+
56
+ ```sh
57
+ chatdeck demo
58
+ ```
59
+
60
+ opens chatdeck on a set of made-up chats (in a temporary folder, deleted when you stop it with Ctrl+C),
61
+ so you can click around without touching your own. The screenshots here are from the demo.
62
+
63
+ ## Use
64
+
65
+ ```sh
66
+ chatdeck # start the dashboard and open it
67
+ chatdeck setup # add the Claude Code hooks for plan limits and "waiting for you"
68
+ chatdeck autostart on # start it at login (Windows via WSL, macOS, Linux)
69
+ chatdeck bubble # Windows: the corner button and tray icon
70
+ chatdeck demo # try it with made-up chats
71
+ chatdeck stop # stop the background server
72
+ chatdeck uninstall # take the hooks and autostart out again
73
+ ```
74
+
75
+ `chatdeck setup` keeps your existing status line: it saves the usage data first, then runs
76
+ yours, so your status line looks the same. `chatdeck uninstall` puts it back, and your previous
77
+ settings are backed up in `~/.chatdeck/backups`.
78
+
79
+ ## Privacy and security
80
+
81
+ - Everything stays on your machine. chatdeck reads the transcripts Claude Code already writes to
82
+ `~/.claude/projects` and sends nothing anywhere: no accounts, no telemetry, no network calls
83
+ except to itself.
84
+ - The server binds to 127.0.0.1 only. Every API call needs a token that changes each run, sent in a
85
+ custom header, so other websites open in your browser can't call it.
86
+ - It only changes things you ask it to: deleting a chat removes its transcript (running chats are
87
+ never deleted), renaming adds a title line to it, and Rename and Compact on a running chat type
88
+ `/rename` or `/compact` into that chat's terminal, only while it's idle.
89
+ - `chatdeck setup` edits `~/.claude/settings.json` and backs it up first; `chatdeck uninstall` undoes it.
90
+
91
+ ## See it in action
92
+
93
+ | Find any chat by what was said in it | Every project in one place |
94
+ |---|---|
95
+ | ![searching for "upgrade" and opening the matching chat](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/media/search.gif) | ![switching to the by-project view, folding a project and the New chat button](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/media/projects.gif) |
96
+
97
+ | Keyboard only: `j`/`k`, `Enter`, `e` to rename, `?` for help | Running chats, at a glance |
98
+ |---|---|
99
+ | ![moving through saved chats, reading one and renaming it from the keyboard](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/media/keyboard.gif) | ![three running chats: one waiting for permission, one working, one getting big](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/media/running.png) |
100
+
101
+ ## Screenshots
102
+
103
+ ![chatdeck showing three running chats, one waiting for permission, plan limits and a week of usage](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/dashboard.png)
104
+
105
+ | By project | Reading a chat |
106
+ |---|---|
107
+ | ![chats grouped by project](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/by-project.png) | ![a saved chat open in the viewer](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/viewer.png) |
108
+
109
+ | Light theme | The Windows pop-up |
110
+ |---|---|
111
+ | ![the light theme](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/light.png) | ![the pop-up from the corner button](https://raw.githubusercontent.com/Abdifatah09/chatdeck/main/docs/popup.png) |
112
+
113
+ ## Windows corner button and tray icon
114
+
115
+ A small always-on-top button sits in the bottom-left corner of the screen. Click it and the
116
+ dashboard pops up above it; click again, or anywhere else, and it tucks away. The badge counts
117
+ running chats and turns amber, with a pulsing ring, when one is waiting for you. Drag it to move
118
+ it; right-click to hide it or for more. The same badge sits in the notification area by the
119
+ clock, where a click pops the dashboard up in the bottom-right corner. It needs Chrome or Edge.
120
+ Start it with `chatdeck bubble`, or let `chatdeck autostart on` start it at login.
121
+
122
+ ## Terminals
123
+
124
+ **Resume**, **Open** and **Agents view** open or find a terminal for you:
125
+
126
+ | Platform | Opens chats in | Open on a running chat |
127
+ |---|---|---|
128
+ | WSL | Windows Terminal (a new window each time) | finds its tab on any virtual desktop and switches to it |
129
+ | macOS | Terminal, or iTerm2 if you run the dashboard from it | finds its tab by tty |
130
+ | Linux | gnome-terminal, konsole, kitty, alacritty, wezterm, xfce4-terminal, tilix, foot or xterm | finds its window with `wmctrl` or `xdotool` (X11) |
131
+
132
+ When no terminal can be opened, the dashboard copies the command to paste instead.
133
+ Finding a running chat works by briefly giving its tab a unique title, so it doesn't work for
134
+ tabs whose title is locked (Windows Terminal's `suppressApplicationTitle`).
135
+
136
+ ## Settings
137
+
138
+ Settings live in `~/.chatdeck/config.json`, and environment variables override them:
139
+
140
+ - `port` / `CHATDECK_PORT`: default 47123
141
+ - `terminal` / `CHATDECK_TERMINAL`: which terminal to use, e.g. `kitty` or `iterm`
142
+ - `tabs` / `CHATDECK_TABS` (WSL): `true` opens chats as tabs in your last Windows Terminal window instead of a new window each
143
+ - `vdlib` / `CHATDECK_VDLIB` (WSL): Windows path to [VirtualDesktopLib.cs](https://github.com/MScholtes/VirtualDesktop),
144
+ which makes switching to a chat on another virtual desktop reliable
145
+ - `CHATDECK_DIR`: where chatdeck keeps its data, default `~/.chatdeck`
146
+
147
+ ## Tests
148
+
149
+ ```sh
150
+ python3 -m unittest discover -s tests
151
+ ```
152
+
153
+ ## Screenshots and videos
154
+
155
+ Everything in `docs/` is recorded from `chatdeck demo` by a script, so it can be redone after UI changes
156
+ (needs Node; uses Segoe UI from `/mnt/c/Windows/Fonts` when run under WSL):
157
+
158
+ ```sh
159
+ cd scripts/promo && npm install && npx playwright-core install chromium-headless-shell
160
+ npm run record # or just some: npm run record -- shots tour search projects keyboard
161
+ ```
162
+
163
+ It writes the README screenshots to `docs/` and the GIFs, MP4s, framed screenshots and a 1280×640
164
+ social preview card to `docs/media/`.
@@ -0,0 +1,26 @@
1
+ chatdeck/__init__.py,sha256=naiHQ5__Xp-CLtjKv2NWz_2wEw5fZbvWEKH0XniAy1A,79
2
+ chatdeck/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
3
+ chatdeck/autostart.py,sha256=NjZtpNRYA48uCa_91AH80ktW0m2-m13AjtliLuvA8pU,5144
4
+ chatdeck/cli.py,sha256=bAjLmm1dSul18cjWUknidE-oHZmLa1kA2zCQyg8_uOM,7135
5
+ chatdeck/config.py,sha256=n8bjCfk9T-59EFeMzxUx3oiynPEm_vDBeHcgI5soTMM,1034
6
+ chatdeck/demo.py,sha256=B1fRshU4JW4bz6cEUG5-_RwFVqhR6O9vRrN6OvN1w5I,9906
7
+ chatdeck/desktop.py,sha256=HowVaaHvZW5GaAd2RBPwDJ57Qu_p_LUGweeoHYmMfg0,4669
8
+ chatdeck/hooks.py,sha256=KSBbqVpVHXWE-2AoscQaIpzhFfMOTjC_G94oeLxVTuI,5578
9
+ chatdeck/server.py,sha256=39TubZww5zeD4cD9hDwxbOnSBf_s78iNB63QPIMwZV8,18396
10
+ chatdeck/terminals.py,sha256=Ado3dn9OjoGo49NxyEd1YxpbK7FmAp2h-b02VKSRrAQ,9431
11
+ chatdeck/transcripts.py,sha256=bFoD53CluwSHzrRHSFzj_TWlU4zDzDVJHLBROSiJLMU,10158
12
+ chatdeck/usage.py,sha256=Hu7tmj0dJl8iNQtYwkw6BJhDU_K38oFqcoaImdPNfsA,4739
13
+ chatdeck/static/index.html,sha256=g7IUn3Rsblq5IzPvHUnIrbWz-w1UZCJx93NkZ5ssvEk,38412
14
+ chatdeck/static/logo-192.png,sha256=Dp9ZJJYHiG6GwTKX9vMCI69nn0Jr0jQowYvGjOumBXM,11287
15
+ chatdeck/static/logo-512.png,sha256=4_-BdCcVR5gqEBZvk5l6au_4w9Pv8cZmG0Sv2Rshsno,58695
16
+ chatdeck/static/logo.ico,sha256=qyXoiqBNwKAOkrDybrROQvIeEReKUhdQ7dv7Y1gId3A,21571
17
+ chatdeck/static/logo.svg,sha256=KVp6I1TbkePvfcG8ZMJ6OyNCG4QGBzk5a-7PN-DrkCo,740
18
+ chatdeck/windows/bubble.ps1,sha256=3KU5xGk4Ndz8zJ7ET4ULrMzJXr6L7DMCtgK0jlNy8ZM,18033
19
+ chatdeck/windows/focus_wt.ps1,sha256=5jg0slyKmKiRc98NI7L8iofbVxS1mpkmitbrc7OMLqk,4222
20
+ chatdeck/windows/start.ps1,sha256=8fOiuPPnOEeKFw6H4jp6UA1vmgSDn0HSFO7PkZTfnuo,2052
21
+ chatdeck-0.1.0.dist-info/licenses/LICENSE,sha256=MHNkmlUcd8LMZnGi58S4nAzCkH42kbjr2A0DxsLikWQ,1066
22
+ chatdeck-0.1.0.dist-info/METADATA,sha256=Vuhp42c0KiBR0sTcgW9wdWgQoqT2QFh9hSixZ4ebO48,8709
23
+ chatdeck-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
24
+ chatdeck-0.1.0.dist-info/entry_points.txt,sha256=b8jWrnNWd27e6Y8wookIN-9J1F4HWKp-GAUG46UudPo,47
25
+ chatdeck-0.1.0.dist-info/top_level.txt,sha256=LnJ9TcoohY4YMngg38zawhZVxAO6QB_9d3VHGQhglaU,9
26
+ chatdeck-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ chatdeck = chatdeck.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abdifatah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ chatdeck