openclaw/ui/config
Peter Steinberger 85fda04df7
feat(ui): surface queued-outbox counts in composer hint, session rows, and offline footer (#112649)
* feat(ui): surface queued-outbox counts in composer hint, session rows, and offline footer

Follow-up to the offline-state unification: queued sends were mechanically
safe (durable outbox with reconnect replay) but only visible inside the
open session's thread.

- Composer offline hint now includes the visible session's queued count.
- Session rows show a clock badge with the per-scope outbox count,
  independent of connection state (covers waiting-idle and failed too),
  with alias-safe scope resolution so agent-main never double-counts.
- The offline footer button appends the aggregate ("· N queued"); the
  connected state stays completely silent.
- One narrow subscription seam (subscribeStoredChatOutboxChanges) added
  in composer-persistence; no send/drain logic touched.

* perf(ui): keep the startup bundle under budget with a lean outbox read module

The queued-count feature statically imported composer-persistence from
startup modules, hoisting the chat page's persistence machinery into the
startup chunk and breaking the Control UI startup JS gzip budget
(319.6 KiB > 314.0 KiB limit).

Split ownership instead of gaming the budget: a lean read/subscribe
module (ui/src/lib/chat/outbox-store.ts + codec/draft-state) serves
startup consumers (app-host, sidebar), while writes, migrations, and
drain stay in the lazy chat chunk (composer-outbox-store/composer-storage);
composer-persistence keeps its export surface for chat callers. Startup
is back to 313.8 KiB gzip at 12 requests with no chunking-config changes.

Also fixes an autoreview finding in the new summary: legacy bare-main
outbox rows now resolve through session defaults (online) or the
persisted mainAlias (offline reload) instead of trusting the row's stale
embedded agent id, so badge counts key to the same scope the sidebar
resolves. The shared footer status renderer is deduplicated into
session-row-badges.

* fix(ui): correct type-only import and const tuple in outbox split

* perf(ui): idle-load the outbox summary so startup carries no outbox code

The lean outbox read module still cost ~2.8 KiB of startup gzip against
1.5 KiB of budget headroom. Follow the sidebar chrome pattern
(lobster-pet/facepile): app-host idle-loads outbox-store, subscribes on
arrival, and passes the sidebar a resolver callback instead of letting
startup modules import scope resolution. Badges and counts hydrate
moments after load; before that the summary is empty by design.

Failed chunk loads recover on browser online events and, because chunks
are usually served by the gateway itself, on gateway reconnect — the
exact moment the offline badges become relevant again.

Raise the initial-graph packing ceiling 448->512 KiB: the grown core
graph split at the old boundary into an extra chunk, costing ~1.9 KiB of
startup gzip to compression-context resets (same documented tradeoff as
the earlier 400->448 bump). Startup lands at 313.5 KiB gzip / 9 requests,
matching the origin/main baseline, limit 315.0.
2026-07-22 10:53:22 -07:00
..
control-ui-chunking.ts feat(ui): surface queued-outbox counts in composer hint, session rows, and offline footer (#112649) 2026-07-22 10:53:22 -07:00