redesign: 2-col layout, peers in sidebar, IRC-style messages, NAT dot
- Peers moved from right panel into sidebar below Rooms - Messages left-aligned IRC style: time · alias · text - Per-peer colored dot: green=direct/NAT-punched, amber=relay/connecting, red=failed - Hover title on peer row shows full ID, connection type, remote address, last seen - Removed right PeerList panel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -257,8 +257,10 @@ class PeerConn {
|
||||
this._esk = kp.privateKey
|
||||
this._epk = kp.publicKey
|
||||
this.pc.ondatachannel = (ev) => this._wire(ev.channel)
|
||||
this.pc.onconnectionstatechange = () =>
|
||||
this.pc.onconnectionstatechange = () => {
|
||||
this.on('status', { peer: this.peerId, state: this.pc.connectionState })
|
||||
if (this.pc.connectionState === 'connected') this.reportStats()
|
||||
}
|
||||
}
|
||||
|
||||
private _seal(obj: object, preferEph: boolean): [string, boolean] {
|
||||
@@ -386,6 +388,28 @@ class PeerConn {
|
||||
}
|
||||
}
|
||||
|
||||
async reportStats() {
|
||||
try {
|
||||
const stats = await this.pc.getStats()
|
||||
let candidateType: string = 'unknown'
|
||||
let remoteAddress = ''
|
||||
stats.forEach((s) => {
|
||||
if (s.type === 'candidate-pair' && (s as RTCIceCandidatePairStats).state === 'succeeded') {
|
||||
const pair = s as RTCIceCandidatePairStats & { nominated?: boolean }
|
||||
if (pair.nominated) {
|
||||
stats.forEach((c) => {
|
||||
if (c.type === 'remote-candidate' && c.id === (pair as unknown as Record<string,string>)['remoteCandidateId']) {
|
||||
candidateType = (c as unknown as Record<string,string>)['candidateType'] || 'unknown'
|
||||
remoteAddress = (c as unknown as Record<string,string>)['address'] || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
this.on('ice_stats', { peer: this.peerId, candidateType, remoteAddress })
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
sendChat(room: string, text: string) {
|
||||
this._dc({ type: 'chat', room, text, ts: Date.now() })
|
||||
}
|
||||
@@ -608,6 +632,19 @@ export class BrowserAdapter {
|
||||
ts,
|
||||
},
|
||||
})
|
||||
} else if (event === 'status') {
|
||||
this.emit({
|
||||
type: 'peer_status',
|
||||
peer_id: data['peer'] as unknown as import('../types').PeerID,
|
||||
conn_state: data['state'] as import('../types').PeerConnState,
|
||||
})
|
||||
} else if (event === 'ice_stats') {
|
||||
this.emit({
|
||||
type: 'peer_status',
|
||||
peer_id: data['peer'] as unknown as import('../types').PeerID,
|
||||
candidate_type: data['candidateType'] as import('../types').CandidateType,
|
||||
remote_address: data['remoteAddress'] as string,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user