程序员阿江(Relakkes) 993b96cd39 Stabilize the desktop transcript so long agent sessions stay readable
The desktop app now keeps the composer stable while turns are active,
reduces low-signal tool noise in the transcript, restores project context
under the composer after session creation, and relies on the CLI's own
permission requests instead of injecting broader desktop-side Bash asks.

This also brings in the supporting desktop app source tree and the server
routes/session metadata needed for git info, filesystem browsing, session
resume, slash commands, and SDK-backed permission bridging so the UI can
operate as a coherent feature instead of a partial patch.

Constraint: Desktop transcript needs to stay usable during long multi-tool sessions without hiding file-change diffs
Constraint: Permission prompts must mirror CLI behavior closely enough that read-only commands do not get desktop-only prompts
Rejected: Keep rendering Read/Bash bodies inline | too noisy and unlike the intended transcript model
Rejected: Commit only the touched desktop files | would leave the newly introduced desktop app incomplete in git history
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: Treat non-writing tools as summary-first transcript events; do not re-expand them by default without validating the UX against long sessions
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run
Tested: bun test src/server/__tests__/conversations.test.ts
Not-tested: Manual visual regression against the exact screenshots in a live desktop session
Not-tested: Full root TypeScript check (repository still has unrelated extracted-native parse failures)
2026-04-06 20:37:44 +08:00

95 lines
3.3 KiB
TypeScript

import { useUIStore } from '../../stores/uiStore'
export function TitleBar() {
const { activeView, setActiveView } = useUIStore()
return (
<div
className="h-[var(--titlebar-height)] flex items-center border-b border-[var(--color-border)] bg-[var(--color-surface)] select-none"
data-tauri-drag-region
>
{/* macOS traffic light spacer */}
<div className="w-[78px] flex-shrink-0" data-tauri-drag-region />
{/* Logo */}
<div className="flex items-center gap-2 mr-4" data-tauri-drag-region>
<span className="text-xs font-bold tracking-wider text-[var(--color-brand)] uppercase">Claude Code Companion</span>
</div>
{/* Navigation arrows */}
<div className="flex items-center gap-1 mr-4">
<button className="p-1 rounded-[var(--radius-md)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 18l-6-6 6-6" />
</svg>
</button>
<button className="p-1 rounded-[var(--radius-md)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M9 18l6-6-6-6" />
</svg>
</button>
</div>
{/* Center tabs */}
<div className="flex-1 flex items-center justify-center gap-1" data-tauri-drag-region>
<TabButton
active={activeView === 'code'}
onClick={() => setActiveView('code')}
icon="code"
>
Code
</TabButton>
<TabButton
active={activeView === 'terminal'}
onClick={() => setActiveView('terminal')}
icon="terminal"
>
Terminal
</TabButton>
<TabButton
active={activeView === 'history'}
onClick={() => setActiveView('history')}
icon="history"
>
History
</TabButton>
</div>
{/* Right: Settings */}
<div className="flex items-center gap-2 mr-4">
<button className="p-1.5 rounded-[var(--radius-md)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors">
<span className="material-symbols-outlined text-[18px]">settings</span>
</button>
</div>
</div>
)
}
function TabButton({
active,
onClick,
icon,
children,
}: {
active: boolean
onClick: () => void
icon: string
children: React.ReactNode
}) {
return (
<button
onClick={onClick}
className={`
flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-[var(--radius-md)] transition-colors duration-200
${active
? 'bg-[var(--color-surface-selected)] text-[var(--color-text-primary)]'
: 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'
}
`}
>
<span className="material-symbols-outlined text-[16px]">{icon}</span>
{children}
</button>
)
}