terminal: build the ws URL like the app's own downlinks; show the attempted URL on failure

The served bundle's wsUrl is provably correct, yet one report showed
ws://host/ws//host/sidebar/ws/terminal (a /ws/ prefix no version ever
emitted). Mirror the app's own downlink construction (new URL(path,
location.origin) + protocol swap — the construction the chat's working
event streams use), and surface the exact attempted URL in the failure
banner plus a console.error, so a recurrence identifies the source
(stale page vs environment rewrite) immediately.
This commit is contained in:
Menghuan1918
2026-08-08 15:23:01 +08:00
parent 97e1f60e47
commit e80c1f6625
2 changed files with 22 additions and 3 deletions
+13 -3
View File
@@ -24,6 +24,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
const hostRef = useRef<HTMLDivElement>(null)
const [connected, setConnected] = useState(false)
const [fatal, setFatal] = useState<string | null>(null)
const [lastUrl, setLastUrl] = useState<string | null>(null)
const connectRef = useRef<(() => void) | null>(null)
useEffect(() => {
@@ -48,10 +49,15 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
let failures = 0
const wsUrl = (): string => {
const proto = location.protocol === 'https:' ? 'wss' : 'ws'
const params = new URLSearchParams({ sessionId: scope.sessionId, tab: tabId })
if (scope.cwd !== undefined && scope.cwd !== '') params.set('cwd', scope.cwd)
return `${proto}//${location.host}/sidebar/ws/terminal?${params.toString()}`
// Same construction the app's own downlink WebSockets use (new URL
// over location.origin + protocol swap): whatever the environment
// does to the app's websockets applies identically here.
const url = new URL('/sidebar/ws/terminal', location.origin)
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
url.search = params.toString()
return url.toString()
}
const sendResize = (): void => {
@@ -62,7 +68,9 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
const connect = (): void => {
if (closed) return
socket = new WebSocket(wsUrl())
const url = wsUrl()
setLastUrl(url)
socket = new WebSocket(url)
socket.onopen = () => {
failures = 0
setConnected(true)
@@ -86,6 +94,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
failures += 1
if (failures >= FAILURE_LIMIT) {
const detail = event.reason !== '' ? ` (${event.code}: ${event.reason})` : ` (${event.code})`
console.error('[dsh-better-sidebar] terminal connection failed:', event.code, event.reason, url)
setFatal(`${t('terminalConnectFailed')}${detail}`)
return
}
@@ -133,6 +142,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
{fatal !== null && (
<div className={css.terminalBanner}>
{t('terminalError')}: {fatal}
{lastUrl !== null && <div className={css.terminalBannerUrl}>{lastUrl}</div>}
<button
type="button"
className={css.terminalRetry}
+9
View File
@@ -662,6 +662,7 @@
flex: none;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 3px 10px;
font: var(--dsw-font-xxxs-11);
@@ -669,6 +670,14 @@
background: var(--dsw-alias-state-warn-tertiary);
}
/* The attempted ws URL, shown when the connection failed (diagnosis). */
.terminalBannerUrl {
flex-basis: 100%;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
word-break: break-all;
opacity: 0.85;
}
.boundaryError {
position: fixed;
right: 0;