fix(desktop): make the logs pane summon-only via the command palette
The logs pane no longer exists as standing chrome: it isn't registered in any default layout or preset, never rides the terminal strip as a secondary tab, and never opens automatically. The contribution is registered only while the palette's "Toggle logs" command has it summoned, and closing it (toggle, tab ✕, ⌘W) removes it from the registry and the tree entirely — including sweeping it out of persisted layouts from before it was summon-only. The open state is session-only, so a fresh boot always starts without it.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { computed } from 'nanostores'
|
||||
import { atom, computed } from 'nanostores'
|
||||
import type { CSSProperties, ReactElement, PointerEvent as ReactPointerEvent } from 'react'
|
||||
|
||||
import { PREVIEW_RAIL_MAX_WIDTH, PREVIEW_RAIL_MIN_WIDTH } from '@/app/chat/right-rail'
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
registerLayoutResetHandler,
|
||||
registerPaneCloser,
|
||||
registerPaneOpener,
|
||||
removeTreePane,
|
||||
resetLayoutTree,
|
||||
revealTreePane,
|
||||
setPaneCollapsed,
|
||||
@@ -38,7 +39,6 @@ import { discoverRuntimePlugins } from '@/contrib/runtime-loader'
|
||||
import { sessionTitle as storedSessionTitle } from '@/lib/chat-runtime'
|
||||
import { FileText, LayoutDashboard, PanelBottom, Zap } from '@/lib/icons'
|
||||
import { type KeybindContribution, KEYBINDS_AREA } from '@/lib/keybinds/actions'
|
||||
import { Codecs, persistentAtom } from '@/lib/persisted'
|
||||
import { setYoloEnabled } from '@/lib/yolo-session'
|
||||
import { pruneComposerPopoutZones } from '@/store/composer-popout'
|
||||
import {
|
||||
@@ -227,17 +227,6 @@ registry.registerMany([
|
||||
maxWidth: FILE_BROWSER_MAX_WIDTH
|
||||
},
|
||||
render: () => idle(<ReviewPaneContent />)
|
||||
},
|
||||
{
|
||||
// Optional chrome — in NO default layout. Adoption stacks it with the
|
||||
// terminal; $logsOpen (default off, ⌘K "Toggle logs") reveals it.
|
||||
id: 'logs',
|
||||
area: 'panes',
|
||||
title: 'logs',
|
||||
// revealOnPreset: the Quad layout places logs, so applying it turns the
|
||||
// logs pane on (like a ⌘K "Toggle logs") instead of leaving it collapsed.
|
||||
data: { placement: 'bottom', height: '20vh', minHeight: '7.5rem', maxHeight: '80vh', revealOnPreset: true },
|
||||
render: () => idle(<LogsPane />)
|
||||
}
|
||||
])
|
||||
|
||||
@@ -381,7 +370,7 @@ const QUAD_TREE = split(
|
||||
'column',
|
||||
[
|
||||
split('row', [group(['sessions', 'files']), group(['workspace'])], [1, 3]),
|
||||
split('row', [group(['terminal']), group(['preview', 'review', 'logs'])], [1.4, 1])
|
||||
split('row', [group(['terminal']), group(['preview', 'review'])], [1.4, 1])
|
||||
],
|
||||
[3, 1]
|
||||
)
|
||||
@@ -588,15 +577,60 @@ const $previewVisible = computed($previewTabs, tabs => tabs.length > 0)
|
||||
|
||||
bindPaneVisibility('preview', $previewVisible, closeRightRail)
|
||||
|
||||
// Logs are optional chrome: off by default, toggled from ⌘K, persisted.
|
||||
const $logsOpen = persistentAtom('hermes.desktop.logsOpen', false, Codecs.bool)
|
||||
// Logs are ⌘K-ONLY chrome: the pane contribution EXISTS only while $logsOpen
|
||||
// is on. Off (the default) keeps logs out of the registry and the tree
|
||||
// entirely — no secondary tab riding the terminal strip, no preset or
|
||||
// adoption path that resurrects it. Session-only on purpose (not persisted):
|
||||
// a fresh boot never re-opens logs automatically. The palette toggle is the
|
||||
// single door in; tab ✕ / ⌘W / the toggle itself remove it again.
|
||||
const $logsOpen = atom(false)
|
||||
|
||||
let unregisterLogsPane: (() => void) | null = null
|
||||
|
||||
const syncLogsPane = (open: boolean) => {
|
||||
if (open) {
|
||||
unregisterLogsPane ??= registry.register({
|
||||
id: 'logs',
|
||||
area: 'panes',
|
||||
title: 'logs',
|
||||
// Same tool-panel sizing rule as the terminal above. dock: its OWN zone
|
||||
// beside the terminal — never a tab in the terminal's strip.
|
||||
data: {
|
||||
placement: 'bottom',
|
||||
dock: { pane: 'terminal', pos: 'right' },
|
||||
height: '20vh',
|
||||
minHeight: '7.5rem',
|
||||
maxHeight: '80vh'
|
||||
},
|
||||
render: () => idle(<LogsPane />)
|
||||
})
|
||||
// Summoning logs is explicit intent — front it (un-dismisses if a ✕ close
|
||||
// left a dismissal record behind).
|
||||
revealTreePane('logs')
|
||||
} else {
|
||||
unregisterLogsPane?.()
|
||||
unregisterLogsPane = null
|
||||
|
||||
// No dismissal record — the next toggle-on must re-adopt cleanly. Also
|
||||
// sweeps 'logs' out of persisted trees from before it was summon-only.
|
||||
// Guarded: removePane rebuilds the tree even for an absent pane, and a
|
||||
// no-op boot sweep would commit (and persist) a fresh identical tree.
|
||||
const tree = $layoutTree.get()
|
||||
|
||||
if (tree && allPaneIds(tree).includes('logs')) {
|
||||
removeTreePane('logs')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tool-panel tab semantics (✕ / ⌘W route through the store) so the palette
|
||||
// toggle stays truthful either way.
|
||||
markCollapsePane('logs')
|
||||
registerPaneCloser('logs', () => $logsOpen.set(false))
|
||||
registerPaneOpener('logs', () => $logsOpen.set(true))
|
||||
syncLogsPane($logsOpen.get())
|
||||
$logsOpen.listen(syncLogsPane)
|
||||
|
||||
bindPaneCollapse(
|
||||
'logs',
|
||||
$logsOpen,
|
||||
() => $logsOpen.set(false),
|
||||
() => $logsOpen.set(true)
|
||||
)
|
||||
registry.register(
|
||||
paletteToggle({
|
||||
id: 'logs.toggle',
|
||||
|
||||
@@ -31,8 +31,9 @@ import { $previewTarget, openPreview } from '@/store/preview'
|
||||
import { $currentCwd } from '@/store/session'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Logs — live agent-log tail. OPTIONAL chrome: not in any default layout,
|
||||
// hidden until the ⌘K "Toggle logs" command opens it ($logsOpen).
|
||||
// Logs — live agent-log tail. ⌘K-only chrome: the pane contribution exists
|
||||
// only while the "Toggle logs" palette command has it summoned ($logsOpen in
|
||||
// the controller) — never in a default layout, never a standing tab.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function LogsPane() {
|
||||
|
||||
@@ -1203,9 +1203,8 @@ export function applyTree(tree: LayoutNode, presetId: string) {
|
||||
// (the terminal, whose visibility a store owns) would otherwise stay
|
||||
// collapsed after the tree changes — so reveal the ones that opt in through
|
||||
// their owning store, keeping the ⌃`/toggle state truthful. Iterate the
|
||||
// preset's DECLARED panes (not the adopted result): logs is auto-adopted
|
||||
// hidden into every tree, so only a preset that explicitly places it (Quad)
|
||||
// should turn it on.
|
||||
// preset's DECLARED panes (not the adopted result) so only panes a preset
|
||||
// explicitly places are turned on.
|
||||
const panes = registry.getArea('panes')
|
||||
|
||||
for (const paneId of allPaneIds(tree)) {
|
||||
|
||||
Reference in New Issue
Block a user