mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
fix: keep empty-session launch controls visually attached
The active empty-session composer used the same launch controls as the new-session surface but rendered them outside the glass panel, creating an inconsistent gap between the prompt box and project/branch/worktree selectors. Embed the desktop hero launch row in the composer panel and keep the existing mobile and post-message context-chip paths unchanged. Constraint: Preserve the existing new-session composer layout and shared RepositoryLaunchControls behavior. Rejected: Adjust outer margins only | would keep the active empty-session DOM structurally different from the already-correct new-session composer. Confidence: high Scope-risk: narrow Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx Tested: cd desktop && bun run lint Tested: git diff --check Tested: agent-browser DOM check confirmed the launch row is inside chat-input-panel with 1px bottom gap Not-tested: Full bun run verify gate
This commit is contained in:
parent
c39a8331c7
commit
a9bd96f8b0
@ -320,9 +320,16 @@ describe('ChatInput file mentions', () => {
|
|||||||
|
|
||||||
render(<ChatInput variant="hero" />)
|
render(<ChatInput variant="hero" />)
|
||||||
|
|
||||||
|
const panel = screen.getByTestId('chat-input-panel')
|
||||||
|
expect(panel).toHaveClass('rounded-xl')
|
||||||
|
expect(panel).not.toHaveClass('rounded-b-none')
|
||||||
|
|
||||||
expect(await screen.findByRole('button', { name: /Select branch: main/ })).toBeInTheDocument()
|
expect(await screen.findByRole('button', { name: /Select branch: main/ })).toBeInTheDocument()
|
||||||
expect(screen.getByText('Current worktree')).toBeInTheDocument()
|
expect(screen.getByText('Current worktree')).toBeInTheDocument()
|
||||||
expect(screen.queryByText('Select a project...')).not.toBeInTheDocument()
|
expect(screen.queryByText('Select a project...')).not.toBeInTheDocument()
|
||||||
|
const branchButton = screen.getByRole('button', { name: /Select branch: main/ })
|
||||||
|
expect(panel).toContainElement(branchButton.parentElement)
|
||||||
|
expect(branchButton.parentElement).toHaveClass('bg-transparent')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('uses the persisted message count to keep reopened sessions in context mode while history loads', async () => {
|
it('uses the persisted message count to keep reopened sessions in context mode while history loads', async () => {
|
||||||
|
|||||||
@ -151,6 +151,7 @@ export function ChatInput({ variant = 'default', compact = false }: ChatInputPro
|
|||||||
const useCompactControls = compact || isMobileComposer
|
const useCompactControls = compact || isMobileComposer
|
||||||
const iconOnlyAction = compact || isMobileComposer
|
const iconOnlyAction = compact || isMobileComposer
|
||||||
const activeLaunchWorkDir = showLaunchControls ? (launchWorkDir || resolvedWorkDir || '') : (resolvedWorkDir || '')
|
const activeLaunchWorkDir = showLaunchControls ? (launchWorkDir || resolvedWorkDir || '') : (resolvedWorkDir || '')
|
||||||
|
const embedLaunchControlsInHero = isHeroComposer && !useCompactControls && showLaunchControls
|
||||||
const pendingSlashUiAction = !isMemberSession && input.trim().startsWith('/')
|
const pendingSlashUiAction = !isMemberSession && input.trim().startsWith('/')
|
||||||
? resolveSlashUiAction(input.trim().slice(1))
|
? resolveSlashUiAction(input.trim().slice(1))
|
||||||
: null
|
: null
|
||||||
@ -802,7 +803,7 @@ export function ChatInput({ variant = 'default', compact = false }: ChatInputPro
|
|||||||
<div
|
<div
|
||||||
data-testid="chat-input-panel"
|
data-testid="chat-input-panel"
|
||||||
className={isHeroComposer
|
className={isHeroComposer
|
||||||
? 'glass-panel relative flex flex-col gap-3 rounded-t-xl rounded-b-none p-4 transition-colors'
|
? `glass-panel relative flex flex-col gap-3 ${embedLaunchControlsInHero ? 'rounded-xl' : 'rounded-t-xl rounded-b-none'} p-4 transition-colors`
|
||||||
: compact
|
: compact
|
||||||
? `glass-panel relative p-3 transition-colors ${isMobileComposer ? 'rounded-2xl shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl'}`
|
? `glass-panel relative p-3 transition-colors ${isMobileComposer ? 'rounded-2xl shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl'}`
|
||||||
: `glass-panel relative transition-colors ${isMobileComposer ? 'rounded-2xl p-3 shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl p-4'}`}
|
: `glass-panel relative transition-colors ${isMobileComposer ? 'rounded-2xl p-3 shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl p-4'}`}
|
||||||
@ -1049,11 +1050,27 @@ export function ChatInput({ variant = 'default', compact = false }: ChatInputPro
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{embedLaunchControlsInHero && (
|
||||||
|
<div className="-mx-4 -mb-4 mt-3">
|
||||||
|
<RepositoryLaunchControls
|
||||||
|
workDir={activeLaunchWorkDir}
|
||||||
|
onWorkDirChange={handleLaunchWorkDirChange}
|
||||||
|
branch={launchBranch}
|
||||||
|
onBranchChange={setLaunchBranch}
|
||||||
|
useWorktree={launchUseWorktree}
|
||||||
|
onUseWorktreeChange={setLaunchUseWorktree}
|
||||||
|
onLaunchReadyChange={setLaunchReady}
|
||||||
|
disabled={isActive || launchTransitioning}
|
||||||
|
placement="composer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input ref={fileInputRef} type="file" multiple className="hidden" onChange={handleFileSelect} />
|
<input ref={fileInputRef} type="file" multiple className="hidden" onChange={handleFileSelect} />
|
||||||
|
|
||||||
{!isMemberSession && (
|
{!isMemberSession && !embedLaunchControlsInHero && (
|
||||||
<div className={useCompactControls ? 'mt-2 flex min-w-0 px-1' : 'mt-3 px-1'}>
|
<div className={useCompactControls ? 'mt-2 flex min-w-0 px-1' : 'mt-3 px-1'}>
|
||||||
{messageCount > 0 ? (
|
{messageCount > 0 ? (
|
||||||
<ProjectContextChip
|
<ProjectContextChip
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user