1- import { For , Show , createEffect , createMemo , on } from "solid-js"
1+ import { For , Show , createEffect , createMemo , on , onCleanup } from "solid-js"
22import { createStore } from "solid-js/store"
33import { createMediaQuery } from "@solid-primitives/media"
44import { useParams } from "@solidjs/router"
@@ -17,7 +17,7 @@ import { useLanguage } from "@/context/language"
1717import { useLayout } from "@/context/layout"
1818import { useTerminal , type LocalPTY } from "@/context/terminal"
1919import { terminalTabLabel } from "@/pages/session/terminal-label"
20- import { createPresence , createSizing , focusTerminalById } from "@/pages/session/helpers"
20+ import { createSizing , focusTerminalById } from "@/pages/session/helpers"
2121import { getTerminalHandoff , setTerminalHandoff } from "@/pages/session/handoff"
2222
2323export function TerminalPanel ( ) {
@@ -33,7 +33,6 @@ export function TerminalPanel() {
3333
3434 const opened = createMemo ( ( ) => view ( ) . terminal . opened ( ) )
3535 const open = createMemo ( ( ) => isDesktop ( ) && opened ( ) )
36- const panel = createPresence ( open )
3736 const size = createSizing ( )
3837 const height = createMemo ( ( ) => layout . terminal . height ( ) )
3938 const close = ( ) => view ( ) . terminal . close ( )
@@ -66,21 +65,42 @@ export function TerminalPanel() {
6665 ) ,
6766 )
6867
68+ const focus = ( id : string ) => {
69+ focusTerminalById ( id )
70+
71+ const frame = requestAnimationFrame ( ( ) => {
72+ if ( ! open ( ) ) return
73+ if ( terminal . active ( ) !== id ) return
74+ focusTerminalById ( id )
75+ } )
76+
77+ const timers = [ 120 , 240 ] . map ( ( ms ) =>
78+ window . setTimeout ( ( ) => {
79+ if ( ! open ( ) ) return
80+ if ( terminal . active ( ) !== id ) return
81+ focusTerminalById ( id )
82+ } , ms ) ,
83+ )
84+
85+ return ( ) => {
86+ cancelAnimationFrame ( frame )
87+ for ( const timer of timers ) clearTimeout ( timer )
88+ }
89+ }
90+
6991 createEffect (
7092 on (
71- ( ) => terminal . active ( ) ,
72- ( activeId ) => {
73- if ( ! activeId || ! panel . open ( ) ) return
74- if ( document . activeElement instanceof HTMLElement ) {
75- document . activeElement . blur ( )
76- }
77- setTimeout ( ( ) => focusTerminalById ( activeId ) , 0 )
93+ ( ) => [ open ( ) , terminal . active ( ) ] as const ,
94+ ( [ next , id ] ) => {
95+ if ( ! next || ! id ) return
96+ const stop = focus ( id )
97+ onCleanup ( stop )
7898 } ,
7999 ) ,
80100 )
81101
82102 createEffect ( ( ) => {
83- if ( panel . open ( ) ) return
103+ if ( open ( ) ) return
84104 const active = document . activeElement
85105 if ( ! ( active instanceof HTMLElement ) ) return
86106 if ( ! root ?. contains ( active ) ) return
@@ -138,30 +158,38 @@ export function TerminalPanel() {
138158
139159 const activeId = terminal . active ( )
140160 if ( ! activeId ) return
141- setTimeout ( ( ) => {
161+ requestAnimationFrame ( ( ) => {
162+ if ( terminal . active ( ) !== activeId ) return
142163 focusTerminalById ( activeId )
143- } , 0 )
164+ } )
144165 }
145166
146167 return (
147- < Show when = { panel . show ( ) } >
168+ < Show when = { isDesktop ( ) } >
148169 < div
149170 ref = { root }
150171 id = "terminal-panel"
151172 role = "region"
152173 aria-label = { language . t ( "terminal.title" ) }
153- aria-hidden = { ! panel . open ( ) }
154- inert = { ! panel . open ( ) }
174+ aria-hidden = { ! open ( ) }
175+ inert = { ! open ( ) }
155176 class = "relative w-full shrink-0 overflow-hidden"
156177 classList = { {
157- "opacity-100" : panel . open ( ) ,
158- "opacity-0 pointer-events-none" : ! panel . open ( ) ,
159- "transition-[height,opacity] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[height] motion-reduce:transition-none" :
178+ "transition-[height] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[height] motion-reduce:transition-none" :
160179 ! size . active ( ) ,
161180 } }
162- style = { { height : panel . open ( ) ? `${ height ( ) } px` : "0px" } }
181+ style = { { height : open ( ) ? `${ height ( ) } px` : "0px" } }
163182 >
164- < div class = "size-full flex flex-col border-t border-border-weak-base" >
183+ < div
184+ class = "absolute inset-x-0 top-0 flex flex-col border-t border-border-weak-base"
185+ classList = { {
186+ "translate-y-0 opacity-100" : open ( ) ,
187+ "translate-y-full opacity-0 pointer-events-none" : ! open ( ) ,
188+ "transition-[transform,opacity] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[transform,opacity] motion-reduce:transition-none" :
189+ ! size . active ( ) ,
190+ } }
191+ style = { { height : `${ height ( ) } px` } }
192+ >
165193 < div onPointerDown = { ( ) => size . start ( ) } >
166194 < ResizeHandle
167195 direction = "vertical"
0 commit comments