mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-09 07:29:51 +00:00
feat(web): session header files and outline view toggles (#952)
* feat(web): session header files and outline view toggles Files and outline icons in SessionHeader act as depressed toggles; files view shares the session header and places refresh beside the search box. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(web): add Playwright handoff script for session view toggles Supports new-feature-intake visual gate: files toggle pressed + refresh beside search. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(dev): handoff script avoid networkidle on live hub SSE HAPI keeps connections open on :3006; domcontentloaded is the correct wait. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): place filesystem refresh outside search field Refresh is a sibling of the search pill, not inside it, so the control is visually and structurally separate from file search. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -374,6 +374,8 @@ type SessionChatProps = {
|
||||
// user dismisses or starts editing.
|
||||
sendError?: ComposerSendError | null
|
||||
onClearSendError?: () => void
|
||||
initialOutlineOpen?: boolean
|
||||
onInitialOutlineConsumed?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,7 +409,15 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
const blocksByIdRef = useRef<Map<string, ChatBlock>>(new Map())
|
||||
const visibleGroupsRef = useRef<ToolGroupBlock[]>([])
|
||||
const [forceScrollToken, setForceScrollToken] = useState(0)
|
||||
const [outlineOpen, setOutlineOpen] = useState(false)
|
||||
const [outlineOpen, setOutlineOpen] = useState(props.initialOutlineOpen ?? false)
|
||||
useEffect(() => {
|
||||
if (!props.initialOutlineOpen) {
|
||||
return
|
||||
}
|
||||
setOutlineOpen(true)
|
||||
props.onInitialOutlineConsumed?.()
|
||||
}, [props.initialOutlineOpen, props.onInitialOutlineConsumed])
|
||||
|
||||
const [cursorSelectedBase, setCursorSelectedBase] = useState('auto')
|
||||
const lastSyncedCursorModelRef = useRef<string | null | undefined>(undefined)
|
||||
const scratchlist = useScratchlist(props.session.id)
|
||||
@@ -995,13 +1005,18 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
props.onRefresh()
|
||||
}, [switchSession, props.onRefresh])
|
||||
|
||||
const handleViewFiles = useCallback(() => {
|
||||
const handleToggleFiles = useCallback(() => {
|
||||
setOutlineOpen(false)
|
||||
navigate({
|
||||
to: '/sessions/$sessionId/files',
|
||||
params: { sessionId: props.session.id }
|
||||
})
|
||||
}, [navigate, props.session.id])
|
||||
|
||||
const handleToggleOutline = useCallback(() => {
|
||||
setOutlineOpen((open) => !open)
|
||||
}, [])
|
||||
|
||||
const handleViewTerminal = useCallback(() => {
|
||||
navigate({
|
||||
to: '/sessions/$sessionId/terminal',
|
||||
@@ -1089,8 +1104,10 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
<SessionHeader
|
||||
session={props.session}
|
||||
onBack={props.onBack}
|
||||
onViewFiles={props.session.metadata?.path ? handleViewFiles : undefined}
|
||||
onOpenOutline={() => setOutlineOpen(true)}
|
||||
onToggleFiles={props.session.metadata?.path ? handleToggleFiles : undefined}
|
||||
filesActive={false}
|
||||
onToggleOutline={handleToggleOutline}
|
||||
outlineActive={outlineOpen}
|
||||
api={props.api}
|
||||
onSessionDeleted={props.onBack}
|
||||
onSessionReopened={(newSessionId) => {
|
||||
|
||||
Reference in New Issue
Block a user