mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix(desktop): streamline panel and effort controls
This commit is contained in:
parent
c5ad527034
commit
96fa164cb1
@ -44,16 +44,16 @@ describe('ReasoningEffortPopover', () => {
|
||||
renderPopover()
|
||||
|
||||
const popover = screen.getByTestId('reasoning-effort-popover')
|
||||
expect(popover).toHaveStyle({ width: '280px' })
|
||||
expect(popover).toHaveClass('px-4', 'pb-4', 'pt-3.5')
|
||||
expect(popover).toHaveStyle({ width: '240px' })
|
||||
expect(popover).toHaveClass('px-3.5', 'pb-3.5', 'pt-3')
|
||||
expect(popover.querySelectorAll('svg')).toHaveLength(0)
|
||||
expect(screen.getByTestId('reasoning-effort-header')).toHaveClass('mb-3', 'justify-between')
|
||||
expect(screen.getByTestId('reasoning-effort-label')).toHaveClass('text-[15px]')
|
||||
expect(screen.getByTestId('reasoning-effort-context-label')).toHaveClass('text-[11px]')
|
||||
expect(screen.getByTestId('reasoning-effort-header')).toHaveClass('mb-2.5', 'justify-between')
|
||||
expect(screen.getByTestId('reasoning-effort-label')).toHaveClass('text-sm')
|
||||
expect(screen.getByTestId('reasoning-effort-context-label')).toHaveClass('text-[10px]')
|
||||
expect(screen.getByTestId('reasoning-effort-context-label')).toHaveTextContent('推理强度')
|
||||
expect(screen.getByRole('slider', { name: '推理强度' })).toHaveClass('h-11')
|
||||
expect(screen.getByTestId('reasoning-effort-track')).toHaveClass('h-[30px]')
|
||||
expect(screen.getByTestId('reasoning-effort-thumb')).toHaveClass('h-10', 'w-10')
|
||||
expect(screen.getByRole('slider', { name: '推理强度' })).toHaveClass('h-9')
|
||||
expect(screen.getByTestId('reasoning-effort-track')).toHaveClass('h-6')
|
||||
expect(screen.getByTestId('reasoning-effort-thumb')).toHaveClass('h-8', 'w-8')
|
||||
})
|
||||
|
||||
it('renders every model-supported stop and exposes the selected localized value', () => {
|
||||
|
||||
@ -20,7 +20,7 @@ type PopoverPosition = {
|
||||
width: number
|
||||
}
|
||||
|
||||
const POPOVER_WIDTH = 280
|
||||
const POPOVER_WIDTH = 240
|
||||
const VIEWPORT_MARGIN = 16
|
||||
const POPOVER_GAP = 10
|
||||
|
||||
@ -107,22 +107,22 @@ export function ReasoningEffortPopover({
|
||||
<div
|
||||
ref={popoverRef}
|
||||
data-testid="reasoning-effort-popover"
|
||||
className="fixed z-[90] rounded-[18px] border border-[var(--color-border)] bg-[var(--color-surface-container-lowest)] px-4 pb-4 pt-3.5 shadow-[0_14px_36px_rgba(15,23,42,0.14)]"
|
||||
className="fixed z-[90] rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface-container-lowest)] px-3.5 pb-3.5 pt-3 shadow-[0_12px_30px_rgba(15,23,42,0.13)]"
|
||||
style={{ bottom: position.bottom, left: position.left, width: position.width }}
|
||||
>
|
||||
<div
|
||||
data-testid="reasoning-effort-header"
|
||||
className="mb-3 flex items-baseline justify-between gap-3"
|
||||
className="mb-2.5 flex items-baseline justify-between gap-3"
|
||||
>
|
||||
<div
|
||||
data-testid="reasoning-effort-label"
|
||||
className="text-[15px] font-semibold text-[var(--color-text-secondary)]"
|
||||
className="text-sm font-semibold text-[var(--color-text-secondary)]"
|
||||
>
|
||||
{labels[value]}
|
||||
</div>
|
||||
<div
|
||||
data-testid="reasoning-effort-context-label"
|
||||
className="text-[11px] font-medium tracking-wide text-[var(--color-text-tertiary)]"
|
||||
className="text-[10px] font-medium tracking-wide text-[var(--color-text-tertiary)]"
|
||||
>
|
||||
{ariaLabel}
|
||||
</div>
|
||||
@ -137,7 +137,7 @@ export function ReasoningEffortPopover({
|
||||
aria-valuemax={maxIndex}
|
||||
aria-valuenow={selectedIndex}
|
||||
aria-valuetext={labels[value]}
|
||||
className="group relative flex h-11 touch-none cursor-pointer items-center outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-4 focus-visible:ring-offset-[var(--color-surface-container-lowest)]"
|
||||
className="group relative flex h-9 touch-none cursor-pointer items-center outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-3 focus-visible:ring-offset-[var(--color-surface-container-lowest)]"
|
||||
onClick={(event) => selectFromClientX(event.clientX)}
|
||||
onPointerDown={(event) => {
|
||||
draggingRef.current = true
|
||||
@ -182,7 +182,7 @@ export function ReasoningEffortPopover({
|
||||
>
|
||||
<div
|
||||
data-testid="reasoning-effort-track"
|
||||
className="absolute inset-x-0 h-[30px] overflow-hidden rounded-full bg-[var(--color-surface-container-high)] shadow-[inset_0_0_0_1px_var(--color-border)]"
|
||||
className="absolute inset-x-0 h-6 overflow-hidden rounded-full bg-[var(--color-surface-container-high)] shadow-[inset_0_0_0_1px_var(--color-border)]"
|
||||
>
|
||||
<div
|
||||
data-testid="reasoning-effort-fill"
|
||||
@ -191,12 +191,12 @@ export function ReasoningEffortPopover({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-x-0 flex items-center justify-between px-[22px]">
|
||||
<div className="absolute inset-x-0 flex items-center justify-between px-[18px]">
|
||||
{options.map((option, index) => (
|
||||
<span
|
||||
key={option}
|
||||
data-testid="reasoning-effort-stop"
|
||||
className={`h-2 w-2 rounded-full ${index <= selectedIndex ? 'bg-white/45' : 'bg-[var(--color-outline)]/55'}`}
|
||||
className={`h-1.5 w-1.5 rounded-full ${index <= selectedIndex ? 'bg-white/45' : 'bg-[var(--color-outline)]/55'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -204,7 +204,7 @@ export function ReasoningEffortPopover({
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-testid="reasoning-effort-thumb"
|
||||
className="absolute top-1/2 h-10 w-10 -translate-x-1/2 -translate-y-1/2 rounded-full border border-[var(--color-border)] bg-white shadow-[0_3px_9px_rgba(15,23,42,0.14)] transition-[left] duration-200 motion-reduce:transition-none"
|
||||
className="absolute top-1/2 h-8 w-8 -translate-x-1/2 -translate-y-1/2 rounded-full border border-[var(--color-border)] bg-white shadow-[0_2px_7px_rgba(15,23,42,0.13)] transition-[left] duration-200 motion-reduce:transition-none"
|
||||
style={{ left: `${fillPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -295,11 +295,12 @@ describe('TabBar', () => {
|
||||
expect(screen.queryByTestId('session-activity-badge')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the activity button for completed TodoWrite history without a badge', async () => {
|
||||
it('shows the activity button for completed TodoWrite history and hides it while the workspace is open', async () => {
|
||||
const { TabBar } = await import('./TabBar')
|
||||
const { useTabStore } = await import('../../stores/tabStore')
|
||||
const { useChatStore } = await import('../../stores/chatStore')
|
||||
const { useSessionStore } = await import('../../stores/sessionStore')
|
||||
const { useWorkspacePanelStore } = await import('../../stores/workspacePanelStore')
|
||||
const sessionId = 'session-1'
|
||||
const chatSession = makeChatSession('idle')
|
||||
chatSession.messages = [completedTodoWriteMessage()]
|
||||
@ -324,6 +325,12 @@ describe('TabBar', () => {
|
||||
|
||||
expect(screen.getByRole('button', { name: /activity/i })).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('session-activity-badge')).not.toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
useWorkspacePanelStore.getState().openPanel(sessionId)
|
||||
})
|
||||
|
||||
expect(screen.queryByRole('button', { name: /activity/i })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the activity button without a numeric badge for running or failed activity', async () => {
|
||||
|
||||
@ -141,7 +141,7 @@ export function TabBar() {
|
||||
hasVisibleActivity: hasVisibleSessionActivity(model),
|
||||
}
|
||||
}))
|
||||
const showActivityButton = activeTabId && activityState.hasVisibleActivity
|
||||
const showActivityButton = activeTabId && activityState.hasVisibleActivity && !isWorkbenchOpen
|
||||
|
||||
const moveTab = useTabStore((s) => s.moveTab)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@ -468,7 +468,7 @@ describe('ActiveSession task polling', () => {
|
||||
expect(screen.queryByTestId('background-tasks-button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders the activity panel inside the chat column with session activity rows', () => {
|
||||
it('renders the activity panel as a rail and hides it when the workspace opens', async () => {
|
||||
const sessionId = 'activity-panel-open-session'
|
||||
|
||||
useCLITaskStore.setState({
|
||||
@ -577,6 +577,16 @@ describe('ActiveSession task polling', () => {
|
||||
expect(chatColumn).toContainElement(screen.getByTestId('chat-input'))
|
||||
expect(screen.queryByTestId('session-task-bar')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('background-tasks-button')).not.toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
useWorkspacePanelStore.getState().openPanel(sessionId)
|
||||
})
|
||||
|
||||
expect(screen.getByTestId('workbench-panel')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('session-activity-panel')).not.toBeInTheDocument()
|
||||
await waitFor(() => {
|
||||
expect(useActivityPanelStore.getState().isOpen(sessionId)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not render the activity panel when the store is open without visible activity', async () => {
|
||||
|
||||
@ -477,6 +477,11 @@ export function ActiveSession() {
|
||||
closeActivityPanel(activeTabId)
|
||||
}, [activeTabId, closeActivityPanel, hasVisibleActivity, isActivityPanelOpen])
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeTabId || !showWorkbench || !isActivityPanelOpen) return
|
||||
closeActivityPanel(activeTabId)
|
||||
}, [activeTabId, closeActivityPanel, isActivityPanelOpen, showWorkbench])
|
||||
|
||||
const handleOpenSubagentRun = useCallback((payload: { sessionId: string; toolUseId: string; title: string }) => {
|
||||
useTabStore.getState().openSubagentTab(payload.sessionId, payload.toolUseId, payload.title)
|
||||
}, [])
|
||||
@ -725,7 +730,7 @@ export function ActiveSession() {
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{activityModel && hasVisibleActivity && !isMobileLayout && !isMemberSession && isSessionTabState(activeTabId, activeTabType) ? (
|
||||
{activityModel && hasVisibleActivity && !showWorkbench && !isMobileLayout && !isMemberSession && isSessionTabState(activeTabId, activeTabType) ? (
|
||||
<SessionActivityPanel
|
||||
model={activityModel}
|
||||
open={isActivityPanelOpen}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user