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:
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user