Explorar o código

fix(app): terminal issues/regression

Adam hai 1 mes
pai
achega
3ba1111ed0

+ 24 - 5
packages/app/src/components/session/session-sortable-terminal-tab.tsx

@@ -18,12 +18,22 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
   const [menuPosition, setMenuPosition] = createSignal({ x: 0, y: 0 })
   const [menuPosition, setMenuPosition] = createSignal({ x: 0, y: 0 })
   const [blurEnabled, setBlurEnabled] = createSignal(false)
   const [blurEnabled, setBlurEnabled] = createSignal(false)
 
 
+  const isDefaultTitle = () => {
+    const number = props.terminal.titleNumber
+    if (!Number.isFinite(number) || number <= 0) return false
+    const match = props.terminal.title.match(/^Terminal (\d+)$/)
+    if (!match) return false
+    const parsed = Number(match[1])
+    if (!Number.isFinite(parsed) || parsed <= 0) return false
+    return parsed === number
+  }
+
   const label = () => {
   const label = () => {
     language.locale()
     language.locale()
+    if (props.terminal.title && !isDefaultTitle()) return props.terminal.title
+
     const number = props.terminal.titleNumber
     const number = props.terminal.titleNumber
-    if (Number.isFinite(number) && number > 0) {
-      return language.t("terminal.title.numbered", { number })
-    }
+    if (Number.isFinite(number) && number > 0) return language.t("terminal.title.numbered", { number })
     if (props.terminal.title) return props.terminal.title
     if (props.terminal.title) return props.terminal.title
     return language.t("terminal.title")
     return language.t("terminal.title")
   }
   }
@@ -102,8 +112,15 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
   }
   }
 
 
   return (
   return (
-    // @ts-ignore
-    <div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
+    <div
+      // @ts-ignore
+      use:sortable
+      class="outline-none focus:outline-none focus-visible:outline-none"
+      classList={{
+        "h-full": true,
+        "opacity-0": sortable.isActiveDraggable,
+      }}
+    >
       <div class="relative h-full">
       <div class="relative h-full">
         <Tabs.Trigger
         <Tabs.Trigger
           classes={{ button: "border-0" }}
           classes={{ button: "border-0" }}
@@ -111,6 +128,8 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
           onClick={focus}
           onClick={focus}
           onMouseDown={(e) => e.preventDefault()}
           onMouseDown={(e) => e.preventDefault()}
           onContextMenu={menu}
           onContextMenu={menu}
+          class="!shadow-none"
+          classes={{ button: "outline-none focus:outline-none focus-visible:outline-none !shadow-none !ring-0" }}
           closeButton={
           closeButton={
             <IconButton
             <IconButton
               icon="close"
               icon="close"

+ 6 - 1
packages/app/src/components/terminal.tsx

@@ -10,6 +10,7 @@ export interface TerminalProps extends ComponentProps<"div"> {
   pty: LocalPTY
   pty: LocalPTY
   onSubmit?: () => void
   onSubmit?: () => void
   onCleanup?: (pty: LocalPTY) => void
   onCleanup?: (pty: LocalPTY) => void
+  onConnect?: () => void
   onConnectError?: (error: unknown) => void
   onConnectError?: (error: unknown) => void
 }
 }
 
 
@@ -40,7 +41,7 @@ export const Terminal = (props: TerminalProps) => {
   const settings = useSettings()
   const settings = useSettings()
   const theme = useTheme()
   const theme = useTheme()
   let container!: HTMLDivElement
   let container!: HTMLDivElement
-  const [local, others] = splitProps(props, ["pty", "class", "classList", "onConnectError"])
+  const [local, others] = splitProps(props, ["pty", "class", "classList", "onConnect", "onConnectError"])
   let ws: WebSocket | undefined
   let ws: WebSocket | undefined
   let term: Term | undefined
   let term: Term | undefined
   let ghostty: Ghostty
   let ghostty: Ghostty
@@ -241,6 +242,7 @@ export const Terminal = (props: TerminalProps) => {
     // console.log("Scroll position:", ydisp)
     // console.log("Scroll position:", ydisp)
     // })
     // })
     socket.addEventListener("open", () => {
     socket.addEventListener("open", () => {
+      local.onConnect?.()
       sdk.client.pty
       sdk.client.pty
         .update({
         .update({
           ptyID: local.pty.id,
           ptyID: local.pty.id,
@@ -255,10 +257,12 @@ export const Terminal = (props: TerminalProps) => {
       t.write(event.data)
       t.write(event.data)
     })
     })
     socket.addEventListener("error", (error) => {
     socket.addEventListener("error", (error) => {
+      if (disposed) return
       console.error("WebSocket error:", error)
       console.error("WebSocket error:", error)
       local.onConnectError?.(error)
       local.onConnectError?.(error)
     })
     })
     socket.addEventListener("close", (event) => {
     socket.addEventListener("close", (event) => {
+      if (disposed) return
       // Normal closure (code 1000) means PTY process exited - server event handles cleanup
       // Normal closure (code 1000) means PTY process exited - server event handles cleanup
       // For other codes (network issues, server restart), trigger error handler
       // For other codes (network issues, server restart), trigger error handler
       if (event.code !== 1000) {
       if (event.code !== 1000) {
@@ -268,6 +272,7 @@ export const Terminal = (props: TerminalProps) => {
   })
   })
 
 
   onCleanup(() => {
   onCleanup(() => {
+    disposed = true
     if (handleResize) {
     if (handleResize) {
       window.removeEventListener("resize", handleResize)
       window.removeEventListener("resize", handleResize)
     }
     }

+ 22 - 7
packages/app/src/pages/session.tsx

@@ -1231,11 +1231,15 @@ export default function Page() {
     language.locale()
     language.locale()
 
 
     const label = (pty: LocalPTY) => {
     const label = (pty: LocalPTY) => {
+      const title = pty.title
       const number = pty.titleNumber
       const number = pty.titleNumber
-      if (Number.isFinite(number) && number > 0) {
-        return language.t("terminal.title.numbered", { number })
-      }
-      if (pty.title) return pty.title
+      const match = title.match(/^Terminal (\d+)$/)
+      const parsed = match ? Number(match[1]) : undefined
+      const isDefaultTitle = Number.isFinite(number) && number > 0 && Number.isFinite(parsed) && parsed === number
+
+      if (title && !isDefaultTitle) return title
+      if (Number.isFinite(number) && number > 0) return language.t("terminal.title.numbered", { number })
+      if (title) return title
       return language.t("terminal.title")
       return language.t("terminal.title")
     }
     }
 
 
@@ -2002,7 +2006,12 @@ export default function Page() {
                           <Terminal
                           <Terminal
                             pty={pty}
                             pty={pty}
                             onCleanup={(data) => terminal.update({ ...data, id: pty.id })}
                             onCleanup={(data) => terminal.update({ ...data, id: pty.id })}
+                            onConnect={() => {
+                              terminal.update({ id: pty.id, error: false })
+                              setDismissed(false)
+                            }}
                             onConnectError={() => {
                             onConnectError={() => {
+                              setDismissed(false)
                               terminal.update({ id: pty.id, error: true })
                               terminal.update({ id: pty.id, error: true })
                             }}
                             }}
                           />
                           />
@@ -2056,11 +2065,17 @@ export default function Page() {
                         {(t) => (
                         {(t) => (
                           <div class="relative p-1 h-10 flex items-center bg-background-stronger text-14-regular">
                           <div class="relative p-1 h-10 flex items-center bg-background-stronger text-14-regular">
                             {(() => {
                             {(() => {
+                              const title = t().title
                               const number = t().titleNumber
                               const number = t().titleNumber
-                              if (Number.isFinite(number) && number > 0) {
+                              const match = title.match(/^Terminal (\d+)$/)
+                              const parsed = match ? Number(match[1]) : undefined
+                              const isDefaultTitle =
+                                Number.isFinite(number) && number > 0 && Number.isFinite(parsed) && parsed === number
+
+                              if (title && !isDefaultTitle) return title
+                              if (Number.isFinite(number) && number > 0)
                                 return language.t("terminal.title.numbered", { number })
                                 return language.t("terminal.title.numbered", { number })
-                              }
-                              if (t().title) return t().title
+                              if (title) return title
                               return language.t("terminal.title")
                               return language.t("terminal.title")
                             })()}
                             })()}
                           </div>
                           </div>