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 hostRef = useRef<HTMLDivElement>(null)
const [connected, setConnected] = useState(false) const [connected, setConnected] = useState(false)
const [fatal, setFatal] = useState<string | null>(null) const [fatal, setFatal] = useState<string | null>(null)
const [lastUrl, setLastUrl] = useState<string | null>(null)
const connectRef = useRef<(() => void) | null>(null) const connectRef = useRef<(() => void) | null>(null)
useEffect(() => { useEffect(() => {
@@ -48,10 +49,15 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
let failures = 0 let failures = 0
const wsUrl = (): string => { const wsUrl = (): string => {
const proto = location.protocol === 'https:' ? 'wss' : 'ws'
const params = new URLSearchParams({ sessionId: scope.sessionId, tab: tabId }) const params = new URLSearchParams({ sessionId: scope.sessionId, tab: tabId })
if (scope.cwd !== undefined && scope.cwd !== '') params.set('cwd', scope.cwd) 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 => { const sendResize = (): void => {
@@ -62,7 +68,9 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
const connect = (): void => { const connect = (): void => {
if (closed) return if (closed) return
socket = new WebSocket(wsUrl()) const url = wsUrl()
setLastUrl(url)
socket = new WebSocket(url)
socket.onopen = () => { socket.onopen = () => {
failures = 0 failures = 0
setConnected(true) setConnected(true)
@@ -86,6 +94,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
failures += 1 failures += 1
if (failures >= FAILURE_LIMIT) { if (failures >= FAILURE_LIMIT) {
const detail = event.reason !== '' ? ` (${event.code}: ${event.reason})` : ` (${event.code})` 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}`) setFatal(`${t('terminalConnectFailed')}${detail}`)
return return
} }
@@ -133,6 +142,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) {
{fatal !== null && ( {fatal !== null && (
<div className={css.terminalBanner}> <div className={css.terminalBanner}>
{t('terminalError')}: {fatal} {t('terminalError')}: {fatal}
{lastUrl !== null && <div className={css.terminalBannerUrl}>{lastUrl}</div>}
<button <button
type="button" type="button"
className={css.terminalRetry} className={css.terminalRetry}
+9
View File
@@ -662,6 +662,7 @@
flex: none; flex: none;
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap;
gap: 8px; gap: 8px;
padding: 3px 10px; padding: 3px 10px;
font: var(--dsw-font-xxxs-11); font: var(--dsw-font-xxxs-11);
@@ -669,6 +670,14 @@
background: var(--dsw-alias-state-warn-tertiary); 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 { .boundaryError {
position: fixed; position: fixed;
right: 0; right: 0;