Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
963d2f5413 |
@@ -56,7 +56,7 @@ The tmux server runs in its own unit, so every Claude process lives in **that**
|
||||
- **Search past conversations** (⋯ menu, palette): full text over every Claude conversation on the host; **Resume** it in a new session or **Fork** it.
|
||||
- **Plan usage:** the 5-hour and weekly windows as meters in the footer (warning at 90 %); a **PR** chip on sessions whose branch has a pull request.
|
||||
- **Quiet phone at the desk:** while a tab shows a session, Remote Control holds back phone pushes for it (client-presence marker, expires ~2 min after you leave).
|
||||
- **Context:** a Claude `statusLine` command (`devdesk-statusline`) snapshots each session's model, context-window use and cost to `/var/lib/devdesk/context/<session>.json`. The Sessions panel shows it as a bar (cost in the tooltip), and Claude's own footer shows a one-line summary.
|
||||
- **Context:** a Claude `statusLine` command (`devdesk-statusline`) snapshots each session's model, context-window use, plan usage and cost to `/var/lib/devdesk/context/<session>.json`. The Sessions panel shows it as a bar and Claude's own footer shows a one-line summary. On a Pro/Max plan that summary shows 5-hour plan usage; the dollar figure Claude reports there is an API-price estimate, not a bill, so it appears only for API-key billing.
|
||||
- **Light mode:** the terminal switches palettes with the page. DevDesk starts Claude with its `dark-ansi` theme so Claude draws with the terminal's 16 colours, and the light palette maps bright white to ink; tmux and the status line use ANSI colours too. A session picks up Claude's theme the next time Claude starts in it (`/exit`, then ⏎ resumes the same conversation).
|
||||
- **Phone:** a key bar replaces the shortcut hints (Esc, Tab, ⇧Tab, ^C, ↑, ↓, ⏎, Claude/Shell window), and the panels open as a drawer.
|
||||
- **SSH:** `devdesk attach <name>`, `devdesk new <name> [project]`, `devdesk rename <old> <new>`, `devdesk end <name>`, `devdesk ls`, `devdesk pick`, `devdesk repo <name>`, `devdesk alerts`.
|
||||
|
||||
Vendored
+15
@@ -1,3 +1,18 @@
|
||||
devdesk (0.9.3) bookworm; urgency=medium
|
||||
|
||||
* Claude's footer shows 5-hour plan use instead of a dollar figure on a
|
||||
Pro/Max subscription (#40). Claude Code's cost is an API-price
|
||||
estimate, not a bill; it is still shown for API-key billing. The
|
||||
Sessions tooltip follows the same rule.
|
||||
* The bottom row is larger and easier to read (#41): 40px tall, larger
|
||||
hints, and 5h/week usage meters in bold monospace with wider bars.
|
||||
Narrow windows drop the least-used hints instead of clipping the
|
||||
meters.
|
||||
* Tests clear inherited DevDesk variables, so they pass when run from
|
||||
inside a DevDesk session.
|
||||
|
||||
-- Jared Vititoe <jjvititoe1@gmail.com> Sun, 11 Oct 2026 22:08:00 -0400
|
||||
|
||||
devdesk (0.9.2) bookworm; urgency=medium
|
||||
|
||||
* Ops Alerts → Ask Claude starts an investigation (#39):
|
||||
|
||||
@@ -43,13 +43,17 @@ if [[ $session =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$ ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Footer line: session · model · context % · cost. ANSI colour numbers, not RGB,
|
||||
# so the browser's dark or light palette applies (devdesk#31).
|
||||
# Footer line: session · model · context % · plan use. ANSI colour numbers, not
|
||||
# RGB, so the browser's dark or light palette applies (devdesk#31). Claude sends
|
||||
# rate_limits only to Pro/Max subscribers; there .cost is an API-price estimate,
|
||||
# not a bill, so show the 5-hour plan use instead and keep $ for API-key billing.
|
||||
jq -rM --arg s "$session" '
|
||||
def c($sgr; $t): "\u001b[\($sgr)m\($t)\u001b[0m";
|
||||
def pct: if . == null then "—" else "\(. | floor)%" end;
|
||||
[ (if $s != "" then c("33"; "◈ \($s)") else empty end),
|
||||
c("36"; .model.display_name // "Claude"),
|
||||
c("37"; "ctx \(.context_window.used_percentage | pct)"),
|
||||
c("37"; "$\((.cost.total_cost_usd // 0) * 100 | round / 100)")
|
||||
(if .rate_limits.five_hour.used_percentage != null
|
||||
then c("37"; "5h \(.rate_limits.five_hour.used_percentage | pct)")
|
||||
else c("37"; "$\((.cost.total_cost_usd // 0) * 100 | round / 100)") end)
|
||||
] | join(c("90"; " · "))' <<<"$input" 2>/dev/null || true
|
||||
|
||||
@@ -5,6 +5,7 @@ REPO=$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)
|
||||
|
||||
setup() {
|
||||
TMP=$(mktemp -d /tmp/devdesk-test.XXXXXX) # short path: unix socket limit
|
||||
unset DEVDESK_REMOTE_CONTROL CLAUDE_ARGS DEVDESK_SESSION TMUX TMUX_PANE # when run from inside DevDesk
|
||||
export DEVDESK_ENV=/dev/null
|
||||
export DEVDESK_PROJECTS_ROOT="$TMP/projects"
|
||||
export DEVDESK_STATE_DIR="$TMP/state"
|
||||
@@ -200,6 +201,17 @@ claude_args() {
|
||||
[ "$(jq -r '.sessions[] | select(.name=="alpha") | .context.model' <<<"$output")" = Opus ]
|
||||
}
|
||||
|
||||
@test "statusline footer shows plan use on a subscription and dollars only on API billing" {
|
||||
sub='{"model":{"display_name":"Opus"},"context_window":{"used_percentage":10},
|
||||
"cost":{"total_cost_usd":1186.99},"rate_limits":{"five_hour":{"used_percentage":8.4}}}'
|
||||
run bash -c "echo '$sub' | '$REPO/libexec/devdesk-statusline'"
|
||||
[[ $output == *"5h 8%"* ]]
|
||||
[[ $output != *'$'* ]]
|
||||
api='{"model":{"display_name":"Opus"},"context_window":{"used_percentage":10},"cost":{"total_cost_usd":1.5}}'
|
||||
run bash -c "echo '$api' | '$REPO/libexec/devdesk-statusline'"
|
||||
[[ $output == *'$1.5'* ]]
|
||||
}
|
||||
|
||||
@test "hooks ignore Claude running in some other tmux server" {
|
||||
start_server
|
||||
other="$TMP/other.sock"
|
||||
|
||||
+2
-1
@@ -278,7 +278,8 @@ export class SessionsPanel {
|
||||
const title = [
|
||||
s.dir,
|
||||
status.key === 'waiting' && s.status?.message,
|
||||
s.context?.model && `${s.context.model}${s.context.cost_usd != null ? ` · $${s.context.cost_usd.toFixed(2)}` : ''}`,
|
||||
// rate_limits means a Pro/Max plan, where cost_usd is only an API-price estimate
|
||||
s.context?.model && `${s.context.model}${s.context.rate_limits ? '' : s.context.cost_usd != null ? ` · $${s.context.cost_usd.toFixed(2)}` : ''}`,
|
||||
].filter(Boolean).join('\n');
|
||||
const iconButton = (label, glyph, onclick, extra = '') =>
|
||||
el('button', { class: `dd-icon-btn ${extra}`, type: 'button', title: label, 'aria-label': `${label} ${s.name}`, onclick }, glyph);
|
||||
|
||||
+20
-4
@@ -2,7 +2,7 @@
|
||||
everything visual comes from the TDS tokens defined in base.css :root. */
|
||||
|
||||
:root {
|
||||
--dd-footer-height: 30px;
|
||||
--dd-footer-height: 40px;
|
||||
--dd-sidebar-width: 320px;
|
||||
--dd-gap: 8px;
|
||||
}
|
||||
@@ -492,7 +492,19 @@ body.dd-sidebar-open .dd-sidebar {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dd-footer .lt-footer-key { margin-right: 4px; }
|
||||
.dd-footer .lt-footer-key { margin-right: 5px; font-size: 0.86rem; }
|
||||
/* devdesk#41: the bottom row was too small to read at a glance */
|
||||
.dd-footer .lt-footer-hint { font-size: 0.9rem; }
|
||||
.dd-footer .lt-footer-sep { font-size: 0.9rem; }
|
||||
/* Narrower windows keep the usage meters and drop the least-used hints. */
|
||||
@media (max-width: 1400px) {
|
||||
.dd-footer .lt-footer-hints .lt-footer-hint:nth-child(n + 13),
|
||||
.dd-footer .lt-footer-sep:nth-child(n + 12) { display: none; }
|
||||
}
|
||||
@media (max-width: 1150px) {
|
||||
.dd-footer .lt-footer-hints .lt-footer-hint:nth-child(n + 9),
|
||||
.dd-footer .lt-footer-sep:nth-child(n + 8) { display: none; }
|
||||
}
|
||||
|
||||
.dd-keybar { display: flex; gap: 4px; width: 100%; overflow-x: auto; scrollbar-width: none; }
|
||||
.dd-keybar[hidden] { display: none; }
|
||||
@@ -625,7 +637,7 @@ html[data-theme="light"] .lt-cmd-item.is-selected { background: var(--accent-cya
|
||||
background: none;
|
||||
color: var(--accent-cyan);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.64rem;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dd-footer-help:hover { border-color: var(--accent-cyan); }
|
||||
@@ -1005,11 +1017,15 @@ html[data-theme="light"] .dd-files.is-narrow .dd-explorer { box-shadow: 8px 0 24
|
||||
.dd-history-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
|
||||
.dd-history-actions .lt-input { width: 220px; font-size: 0.74rem; }
|
||||
|
||||
.dd-footer-usage { margin-left: auto; padding-left: 12px; }
|
||||
.dd-footer-usage { margin-left: auto; padding-left: 16px; }
|
||||
.dd-footer-usage[hidden],
|
||||
.dd-usage-row[hidden] { display: none; }
|
||||
.dd-usage { display: inline-flex; align-items: center; gap: 10px; }
|
||||
.dd-footer-usage { display: inline-flex; align-items: center; gap: 18px; }
|
||||
.dd-meter { display: inline-flex; align-items: center; gap: 5px; color: var(--text-secondary); font-size: 0.66rem; white-space: nowrap; }
|
||||
.dd-footer-usage .dd-meter { gap: 7px; color: var(--text-primary); font-family: var(--font-mono); font-size: 1rem; font-weight: 600; }
|
||||
.dd-footer-usage .dd-meter-label { font-weight: 400; }
|
||||
.dd-footer-usage .dd-meter-bar { width: 80px; height: 8px; }
|
||||
.dd-meter-label { color: var(--text-muted); }
|
||||
.dd-meter-bar { width: 42px; height: 4px; background: var(--bg-tertiary); }
|
||||
.dd-meter-fill { display: block; height: 100%; background: var(--accent-cyan); }
|
||||
|
||||
Reference in New Issue
Block a user