summaryrefslogtreecommitdiffhomepage
path: root/packages/app
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app')
-rw-r--r--packages/app/src/pages/session/terminal-panel.tsx70
1 files changed, 49 insertions, 21 deletions
diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx
index 8fd652e90..19a656b53 100644
--- a/packages/app/src/pages/session/terminal-panel.tsx
+++ b/packages/app/src/pages/session/terminal-panel.tsx
@@ -1,4 +1,4 @@
-import { For, Show, createEffect, createMemo, on } from "solid-js"
+import { For, Show, createEffect, createMemo, on, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import { createMediaQuery } from "@solid-primitives/media"
import { useParams } from "@solidjs/router"
@@ -17,7 +17,7 @@ import { useLanguage } from "@/context/language"
import { useLayout } from "@/context/layout"
import { useTerminal, type LocalPTY } from "@/context/terminal"
import { terminalTabLabel } from "@/pages/session/terminal-label"
-import { createPresence, createSizing, focusTerminalById } from "@/pages/session/helpers"
+import { createSizing, focusTerminalById } from "@/pages/session/helpers"
import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
export function TerminalPanel() {
@@ -33,7 +33,6 @@ export function TerminalPanel() {
const opened = createMemo(() => view().terminal.opened())
const open = createMemo(() => isDesktop() && opened())
- const panel = createPresence(open)
const size = createSizing()
const height = createMemo(() => layout.terminal.height())
const close = () => view().terminal.close()
@@ -66,21 +65,42 @@ export function TerminalPanel() {
),
)
+ const focus = (id: string) => {
+ focusTerminalById(id)
+
+ const frame = requestAnimationFrame(() => {
+ if (!open()) return
+ if (terminal.active() !== id) return
+ focusTerminalById(id)
+ })
+
+ const timers = [120, 240].map((ms) =>
+ window.setTimeout(() => {
+ if (!open()) return
+ if (terminal.active() !== id) return
+ focusTerminalById(id)
+ }, ms),
+ )
+
+ return () => {
+ cancelAnimationFrame(frame)
+ for (const timer of timers) clearTimeout(timer)
+ }
+ }
+
createEffect(
on(
- () => terminal.active(),
- (activeId) => {
- if (!activeId || !panel.open()) return
- if (document.activeElement instanceof HTMLElement) {
- document.activeElement.blur()
- }
- setTimeout(() => focusTerminalById(activeId), 0)
+ () => [open(), terminal.active()] as const,
+ ([next, id]) => {
+ if (!next || !id) return
+ const stop = focus(id)
+ onCleanup(stop)
},
),
)
createEffect(() => {
- if (panel.open()) return
+ if (open()) return
const active = document.activeElement
if (!(active instanceof HTMLElement)) return
if (!root?.contains(active)) return
@@ -138,30 +158,38 @@ export function TerminalPanel() {
const activeId = terminal.active()
if (!activeId) return
- setTimeout(() => {
+ requestAnimationFrame(() => {
+ if (terminal.active() !== activeId) return
focusTerminalById(activeId)
- }, 0)
+ })
}
return (
- <Show when={panel.show()}>
+ <Show when={isDesktop()}>
<div
ref={root}
id="terminal-panel"
role="region"
aria-label={language.t("terminal.title")}
- aria-hidden={!panel.open()}
- inert={!panel.open()}
+ aria-hidden={!open()}
+ inert={!open()}
class="relative w-full shrink-0 overflow-hidden"
classList={{
- "opacity-100": panel.open(),
- "opacity-0 pointer-events-none": !panel.open(),
- "transition-[height,opacity] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[height] motion-reduce:transition-none":
+ "transition-[height] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[height] motion-reduce:transition-none":
!size.active(),
}}
- style={{ height: panel.open() ? `${height()}px` : "0px" }}
+ style={{ height: open() ? `${height()}px` : "0px" }}
>
- <div class="size-full flex flex-col border-t border-border-weak-base">
+ <div
+ class="absolute inset-x-0 top-0 flex flex-col border-t border-border-weak-base"
+ classList={{
+ "translate-y-0 opacity-100": open(),
+ "translate-y-full opacity-0 pointer-events-none": !open(),
+ "transition-[transform,opacity] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[transform,opacity] motion-reduce:transition-none":
+ !size.active(),
+ }}
+ style={{ height: `${height()}px` }}
+ >
<div onPointerDown={() => size.start()}>
<ResizeHandle
direction="vertical"