Shift existing dark palette tokens up one tier for less harsh contrast while keeping the same color system; sync settings preview swatches and accent-on colors.
When the phone goes to background, JS is paused and HTTP keepalive
connections die. On return, the first poll's fetch can hit "Failed to
fetch" because of a stale connection / DNS / TLS state. The original
code showed an error immediately on a single failure.
Now: same polling loop tolerates up to 5 consecutive errors with
exponential backoff (1s, 2s, 4s, 8s) before surfacing showError. Any
successful poll resets the error counter. This rides through the
typical 1-3 second post-resume blip without any user-visible failure.
Pairs with the earlier WS auto-reconnect + visibilitychange handler
(both bridge channels — WS and HTTP polling — now recover cleanly).
Bump app.js?v=87 -> 88.
When mobile browser is backgrounded the OS pauses/kills WebSocket; on
return the page stays on a dead connection and the next send shows
"bridge 未连接". Conductor's WS already had reconnect+backoff; bridge
did not.
Added to bridge WS:
- close handler now schedules reconnect with exponential backoff
(1s, 2s, 4s, ... capped at 30s)
- visibilitychange listener: when page becomes visible again and ws is
closed, reset retry counter and reconnect immediately
- wsRetries resets on successful open
- skip retry while page is hidden (battery saver)
Bump app.js?v=86 -> 87.
BRIDGE_ORIGIN already used ${location.protocol}//; CONDUCTOR_ORIGIN was
hardcoded to http://, which would have broken under HTTPS deployment
with a Mixed Content block. Now both behave consistently — page over
HTTPS uses https for conductor calls and wss for the WS already (the
WS constant was already correct).
Triggered by SakuraFrp requiring HTTPS access (TCP tunnels reject
plain HTTP with 501). Tunnels now run with 自动 HTTPS enabled, so
browser hits https://...:14168 / :18900, and frontend needs to use
https in its constructed conductor URL.
Bump app.js?v=84 -> 86 (skipping 85 — that was an auth-gate-only bump).
Per teacher request, pull the 14168 / 8900 magic numbers and the URL
construction out of ~17 inline call sites into a single block at the top
of app.js:
const BRIDGE_PORT = 14168;
const CONDUCTOR_PORT = 8900;
const BRIDGE_ORIGIN = `...`; // http(s)://host:14168
const BRIDGE_WS_ORIGIN = `...`; // ws(s)://host:14168
const CONDUCTOR_ORIGIN = `...`; // http://host:8900
const CONDUCTOR_WS_ORIGIN = `...`; // ws(s)://host:8900
All fetch / WebSocket call sites now use those origins. No behavioral
change — bridge still calls 14168, conductor still calls 8900. Sets up
future fixes (same-origin bridge, bridge-proxied conductor, runtime port
swap) to be a one-line change in the constants block.
Bump app.js?v=83 -> 84.
1. Removed the two floating arrow buttons (.sb-toggle / .rp-toggle) that
used to sit at the side seams between sidebar/main/rightpanel.
2. Each page section now owns its own .page-top bar with:
- left: .pt-sb-toggle (caretLeft, borderless icon button) — collapses
left sidebar; svg rotates 180° when sidebar is collapsed.
- right: .pt-rp-toggle (caretRight) — same logic for right panel.
- chat/collab keep run-state centered between the two icons.
- services/token use a .pt-spacer to fill the middle gap.
3. .pt-icon style is borderless / transparent / opacity-0.7 (no boxy
bordered button look); page-top padding tightened to 4px so the
carets hug the screen edges. Bump styles.css?v=95→97.
4. Wiring: app.js queries .pt-sb-toggle/.pt-rp-toggle by class (4 of
each, one per page) and binds them to the existing
.body.classList.toggle('sb-collapsed') / .toggle('rp-collapsed')
handlers. Behavior identical to the old single buttons.
5. Dropped the DEV annotation scaffold: removed annot.css link in head,
annot.js script at end of body, and deleted both files.
Previous commit changed .tok-tabs/.tok-tab/.tok-chart CSS but did not bump the styles.css version, so the browser kept serving the cached v=93 file and the visual changes (underline tabs, taller chart) did not appear in the page.
1. Sidebar nav: move 后台服务 (services) to AFTER 指挥家 (collab).
New order: 聊天 → 指挥家 → 后台服务 → Token 统计.
2. Token trend chart: was flat (container 160px tall, viewBox 600×140).
Now container 280px + max-width 760px to stop it stretching on wide
screens; viewBox bumped to 600×260 with a bit more padding so text
does not get distorted under preserveAspectRatio="none".
3. Token stats tabs (聊天 / Conductor) reuse the .svc-tabs underline
look from the services page — same visual language. CSS-only;
.tok-tabs/.tok-tab classes and the JS click handler are unchanged.
4. Bump app.js?v=81→82 (app.js really changed this time, and the bump
also flushes stale i18n cache for previously-untranslated keys).
Resolved one conflict in app.js: kept the new token trend chart code from
main and adopted PR's services-tab nav handler (replaces the old
channels/status separate handlers, which the PR consolidates anyway).
index.html and styles.css auto-merged cleanly.
Closesdd3xp/GenericAgent_Desktop#7
Pure SVG line chart showing daily spending (¥) over time. Respects
date filter, fills zero-cost gaps, uses local timezone for grouping.
Hidden on Conductor tab.
- Two tabs: 聊天 (chat sessions) and Conductor (cumulative/current)
- Conductor tab hides date filter and stat-row summary
- Remove green background and expand triangle from conductor rows
- Bump app.js and styles.css cache versions
Two rows: cumulative total (survives conductor restart) and current
session. Restart detection via value comparison; data stored alongside
bridge token history in desktop_token_history.json.
Prevents token records from splitting into two entries when polling
occurs mid-generation. The full delta is captured on the next poll
after the session becomes idle.
- conductor.py: install cost_tracker + expose /token-stats endpoint (5 lines)
- app.js: fetch conductor token totals and display as a single summary row at top of token table (not expandable, with tooltip noting it's excluded from cumulative totals)
- styles.css: conductor row styling
UI restructure (frontend static):
- PLAN moved from sticky toggle chip to a preset card (BUILTIN_PRESETS) that
one-shot-sends `presetPrompt.plan`, now pointing at the real
memory/plan_sop.md (Auto was considered too but dropped — it semantically
overlaps with the existing "自主探索" preset). All plan/auto toggle
plumbing removed: state, chip listeners, applyToggleClass, ga_plan/ga_auto
localStorage, planPrefix/autoPrefix injection.
- Chat composer: model chip moved out of the joined .cb-left pill group to
sit next to the send button (composer-bot is now right-aligned model+send).
- Conductor composer: gained its own #collab-model-chip + #collab-model-menu;
openModelMenu/renderModelMenu/updateModelChip refactored to be ctx-aware
(both chips share the model list; each pops its menu into its own
composer; updateModelChip syncs both names).
- Topbar dissolved into per-page headers: the global .topbar was removed
from <main>; the run-state moved into a .page-top inside the chat page,
so the "topbar" now switches with the page instead of persisting.
- Removed the conductor "{n} 个任务进行中" sticky banner (#collab-sticky);
the same info is already in the progress panel and the sidebar badge.
Conductor persistence (conductor.py + frontend wiring):
- conductor.py: add_chat() carries files/images on each chat item; WS
receive passes data.files/data.images through; hello includes a
running flag (any subagent with status="running"). 5 net edits.
- app.js: ws.send now posts {msg, files, images} (was msg only); onWsData
hello/chat branches map files/images and set S.conductorTyping from
data.running so attachments + run animation survive a page refresh.
Session naming (mirror TUI continue_cmd._preview_text):
- New helper displayTitle(sess): if user manually renamed, use sess.title;
otherwise scan messages — prefer the last <summary>…</summary> in any
assistant reply, fall back to the first user message text (≤40 chars,
attachment placeholders stripped), final fallback t('conv.defaultTitle').
- newSession() now defaults sess.title to '' instead of '新对话'; the
earlier "first message → title" auto-rename block in submitInput is
gone — titles only get written when the user renames explicitly.
- isAutoTitle() recognises '新对话'/'New chat'/'agent-N' as placeholder so
legacy sessions and the brief agent-N experiment get re-derived live
in the sidebar without losing user renames.
- Sidebar render + search filter now go through displayTitle.
Note: requires a one-time restart of conductor.py for its part to take
effect; the static frontend changes are just a refresh.
- Render [Image #N]/[File #N] placeholders as inline chips in-place (filename)
in user messages, so you can see where an attachment sits in the text. Image
thumbnails + file cards are unchanged. Applies to chat and conductor via a
shared renderMsgTextWithChips() helper; conductor messages now carry sid+name
so the chip can resolve a filename.
- Conductor: stripAttach() also strips inline desktop_uploads paths, and pushMsg
folds the server echo into the local optimistic message (keep clean text +
chips/cards, drop the raw path) so conductor matches chat instead of showing a
second bubble with the full local upload path.
Known limitation (backend, not in scope here): conductor attachments + running
state don't survive a page refresh — conductor.py only stores/sends
{id,role,msg,ts,read} with no file refs and no run state, so the frontend can't
restore them on reload. Persisting those belongs to the conductor maintainer.
- Replace SVG icon with ⧉ character (same as code block)
- Match font-size, padding, border-radius, background
- Remove border and flex layout, use simple text button
- Both buttons now visually identical (~16.5x14.7px)
Re-skin the desktop frontend in ethan su's "rectilinear workspace" visual
language (PR#3) without dropping any feature, plus two UX fixes. Done as a
content-overlay, NOT a git merge of PR#3 — PR#3's app.js reverts ask_user /
conductor / composer / timing, so only its visual layer is adopted.
Design (PR#3):
- Bundle brand fonts (Azonix wordmark / Lexend / Noto Sans / JetBrains Mono)
+ phosphor-icons.js (auto-hydrates [data-ga-icon]).
- styles.css: adopt ethan's full design-token system (accent #1f6f53,
--radius 4px/2px squared corners, brand fonts) via 3-way merge; keep all
feature CSS (composer chips, ask_user notice, conductor, flatpickr, timing
badge); normalize leftover --blue -> --accent.
- index.html: ethan's phosphor chrome; keep contenteditable composers, the
real conductor UI, flatpickr links, and collab-badge.
- app.js: add GA_ICON() helper; swap dynamic chrome icons (pin, more, model
edit/delete, add-preset, channel, ctx-delete, drawer-close) to Phosphor.
Flags, copy/check, file-chip and preset-card icons left inline by design.
Fixes:
- PLAN/AUTO toggles now show an unmistakable on-state (solid accent fill);
the .cb-left .chip.sm rule was overriding .chip.on by specificity, so the
on/off difference was nearly invisible.
- On refresh, restore the session you were actually viewing (persist
ga_active) instead of the bridge's roaming most-recently-updated session;
show the empty new-session state when none is selected.