mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Complete the Electron replacement boundary before merging by removing the renderer-side Tauri host fallback, tightening H5/browser access so only desktop navigation is tokenless, and moving desktop release publication to a tag-driven GitHub Actions matrix with a single final publish job. Constraint: H5/browser capability access must not gain tokenless access through localhost or retired Tauri origins Constraint: Desktop release artifacts must be built by GitHub Actions from version tags, not treated as local build outputs Rejected: Keep localhost browser origins trusted for convenience | local browser contexts can access loopback services and must use the H5 token path Rejected: Publish from each matrix job | partial releases can be created before all platforms finish Confidence: high Scope-risk: broad Directive: Do not reintroduce Tauri origins or localhost browser origins into the trusted desktop origin set without a reviewed security design Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/diagnostics-service.test.ts src/server/middleware/cors.test.ts Tested: bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts Tested: bun run check:desktop Tested: bun run check:native Tested: git diff --check Not-tested: bun run check:server is blocked by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import { useUIStore } from '../../stores/uiStore'
|
|
import { useTranslation } from '../../i18n'
|
|
|
|
export function TitleBar() {
|
|
const { activeView, setActiveView } = useUIStore()
|
|
const t = useTranslation()
|
|
|
|
return (
|
|
<div
|
|
className="h-[var(--titlebar-height)] flex items-center border-b border-[var(--color-border)] bg-[var(--color-surface)] select-none"
|
|
data-desktop-drag-region
|
|
>
|
|
{/* macOS traffic light spacer */}
|
|
<div className="w-[78px] flex-shrink-0" data-desktop-drag-region />
|
|
|
|
{/* Logo */}
|
|
<div className="flex items-center gap-2 mr-4" data-desktop-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-desktop-drag-region>
|
|
<TabButton
|
|
active={activeView === 'code'}
|
|
onClick={() => setActiveView('code')}
|
|
icon="code"
|
|
>
|
|
{t('titlebar.code')}
|
|
</TabButton>
|
|
<TabButton
|
|
active={activeView === 'terminal'}
|
|
onClick={() => setActiveView('terminal')}
|
|
icon="terminal"
|
|
>
|
|
{t('titlebar.terminal')}
|
|
</TabButton>
|
|
<TabButton
|
|
active={activeView === 'history'}
|
|
onClick={() => setActiveView('history')}
|
|
icon="history"
|
|
>
|
|
{t('titlebar.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>
|
|
)
|
|
}
|