mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-19 17:11:42 -06:00
aed4510bd0
* Control UI: unify chat rails in a tabbed panel * fix(ui): compact chat side panel navigation * Polish rail separators around the active tab * Soften rail tab separators * Inset the rail tab strip from the resize handle * Remove the rail tab strip bottom rule * Add compact close controls to rail tabs * Redistribute rail tabs after closing a surface * Restore bottom docking from the rail terminal * Fade clipped rail tab labels at the edge * Add drag reordering to rail tabs * Polish terminal tab chrome * Polish rail tab close controls * Regularize rail tab separators * Space rail tabs evenly * Add feedback to inactive rail tabs * Smooth rail tab selection * Hide unavailable rail menu items * Preserve browser multi-tab entry * Support bottom-docked rail tabs * Polish rail tab sizing * Refine side panel actions * Neutralize rail empty-state icons * Separate dark rail surfaces * Fade only clipped rail tab labels * Compact rail header controls * Refine the surface picker * Raise and tighten the surface empty state * Keep Browser and Tasks in the chat topbar * Unify surface list cards * Keep embedded rails inside their dock * Reveal rail tab close actions on demand * Show full rail labels only when clipped * Let rail tabs use available space * Align chat topbar surface controls * Unify rail surface empty states * Center rail tab glyphs * Fade clipped rail labels smoothly * Keep rail tab labels visible * Tighten rail tab separator and label fade * Keep the new-tab control beside the rail tabs * Keep rail tab separators from reflowing on activation * Anchor the new-tab control outside the rail tab scroll * Scroll rail tabs at the floor instead of clipping them * Tighten spacing between rail tabs * Report an unusable terminal open response as a readable failure * Keep the terminal session menu inside the panel * Share one dock destination cluster across rails, terminal and browser * Align the terminal mini rail with its management icons * Tidy panel pickers, browser chrome and task detail retry * Type the sidebar callbacks object so tsgo:ui passes * Space out the panel picker rows * Measure tab strip edges by rect and cancel superseded installs * Validate every terminal session field and release unusable sessions * Fold the terminal rail stretch rules into one * test(ui): match rail tab checks to the tabbed panel header Anchoring the new-tab control outside the scroll area wrapped the strip in .side-panel__header-tabs, and every query written as a direct-child chain started matching nothing. The assertions did not fail loudly: they compared an empty label list, so three side-panel journeys and the background-tasks rail went red on a selector detail rather than on behaviour. Give the label query one named owner scoped to the panel's own header, so Terminal and Browser strips in the panel body stay excluded while header layout can keep moving. Freeing that width also means two short tabs now fit where they used to be squeezed, so the closing step asserts the honest invariant - a strip that fits again releases the fade - while the overflow and tooltip regime stays proven earlier in the same journey. Settle the shared side-panel helper on whichever surface renders: an empty panel offers its type list and a populated one offers the header menu, and probing before either existed left it waiting for a control that never comes. Also drop a shadowed seed binding and an unused locator that oxlint flagged in the same file. * fix(ui): adapt session discussion slot to the unified panel layout * test(ui): capture the bottom dock evidence with a resting divider * fix(ui): drop the dead previousDock toggle path * test(ui): align sibling suites with the tabbed panel decisions * fix(ui): keep narrow-pane hides above the icon-button styling cascade * fix(ui): create tooltip descriptions via ownerDocument * fix(lint): merge duplicate terminal header styles and drop dead branches * refactor(ui): extract embedded panel templates from the pane render * refactor(ui): delete rail-era dead exports * test(ui): repair type drift after the rebase * fix(ui): keep the workspace toggle module-local and settle test caps * fix(ui): give side-panel tabs their runtime and their own header actions The tabbed side panel dropped two things the old rails owned. The discussion tab lost its only runtime registration, so `openclaw-session-discussion` was never defined and the tab rendered as a permanently blank box; it now loads through the per-slot lazy runtime map like the other panel types. Panels also have no header of their own anymore, so any action on the active panel's content had to reach the shared header. The header now takes panel contributed actions instead of a discussion-only open-url special case, which brings back the side-chat "Clear thread" overflow menu — its gateway reset had no reachable entry point at all in the embedded rail. * test(ui): anchor the sidebar scroll proof to the panel host The bounding contract moved from `.sidebar-column__panel` to `.side-panel__panel`; mounting the detail panel under the removed class let it grow instead of scroll, so the test failed for its harness rather than for the behavior it guards. * refactor(ui): move side-panel wiring out of the chat render monolith `chat-pane-render.ts` crossed its line budget. The region callbacks are layout policy, so they resolve in the layout module with the pane injecting only what it owns, and the two panel-action producers collapse into the single header contribution the region consumes. * fix(ui): keep one terminal intent queue per document A session route mounts the side-panel terminal beside the shell instance kept for the bottom dock. Each owned a private action array over one sessionStorage key, so their whole-array writes erased each other's intents and a freshly mounted panel drained through a reconnect fence it never saw. The queue, its persisted record, and the fence now belong to the document; panels bind as executors while connected. The chat pane records a toggle intent where it observes it, instead of parking the raw event in memory and handing it to a panel that may not be mounted yet — a service-worker reload in that window dropped the request, thread id and all, with nothing explaining why. That makes the deferred-restore handshake dead weight, so it goes. * fix(ui): let the panel that starts a terminal intent finish it Opening a catalog terminal from a chat route mounts the side-panel terminal right after the request, which swapped the queue's executor mid-flight and stranded the in-flight action; the panel's own mount restore then opened a plain session instead. The executor that began an action stays responsible for it as long as it can still run. * test(ui): cover closed legacy side docks * refactor(ui): split side panel controllers * fix(ui): stabilize embedded panel handoffs * style(ui): format browser panel refresh * test(ui): settle catalog prepend paint incrementally * docs(ui): describe the unified chat side panel * test(ui): settle catalog clock transitions --------- Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
28 lines
1.1 KiB
TypeScript
28 lines
1.1 KiB
TypeScript
export type SessionPanelToggleSlot = "browser" | "desktop" | "terminal";
|
|
|
|
const INTENT_TTL_MS = 10_000;
|
|
const pendingToggles = new Map<SessionPanelToggleSlot, { event: Event; createdAt: number }>();
|
|
|
|
/**
|
|
* The application shell exists before a session pane finishes mounting. Keep
|
|
* the newest panel intent so an early command is delivered to that pane rather
|
|
* than disappearing during route startup.
|
|
*/
|
|
export function rememberSessionPanelToggle(slot: SessionPanelToggleSlot, event: Event): void {
|
|
pendingToggles.set(slot, { event, createdAt: Date.now() });
|
|
}
|
|
|
|
/** Clear an intent that the active pane already handled directly. */
|
|
export function clearSessionPanelToggle(slot: SessionPanelToggleSlot, event: Event): void {
|
|
if (pendingToggles.get(slot)?.event === event) {
|
|
pendingToggles.delete(slot);
|
|
}
|
|
}
|
|
|
|
/** Claim an intent only after a mounted pane becomes its active owner. */
|
|
export function takeSessionPanelToggle(slot: SessionPanelToggleSlot): Event | null {
|
|
const pending = pendingToggles.get(slot) ?? null;
|
|
pendingToggles.delete(slot);
|
|
return pending && Date.now() - pending.createdAt <= INTENT_TTL_MS ? pending.event : null;
|
|
}
|