@@ -31,14 +31,14 @@ import type { Server, ServerUser } from '@/types/api'
3131/** Por encima, la letra tarda en aparecer y conviene adelantarla. */
3232const LATENCIA_PARA_ADELANTAR_MS = 90
3333
34- /** Media que sigue a la red sin saltar con una medida suelta . */
35- const PESO_DE_LA_ULTIMA = 0.25
34+ /** Se mira el mínimo de estas: el ruido del navegador solo suma tiempo . */
35+ const MEDIDAS_A_RECORDAR = 5
3636
3737/** Se avisa por tramos: al ojo no le dice nada un milisegundo arriba. */
3838const TRAMO_DE_LATENCIA_MS = 10
3939
40- /** Más tarde no es el eco de la tecla: falsearía la media . */
41- const LATENCIA_CREIBLE_MS = 1500
40+ /** Cada cuánto se mide la ida y vuelta con el servidor . */
41+ const PING_CADA_MS = 2000
4242
4343/** Sin eco, la shell no repite lo tecleado: pide una contraseña. */
4444const SIN_ECO_MS = 600
@@ -66,8 +66,9 @@ export function useTerminalSocket({
6666 const reconexiones = useRef ( 0 )
6767 const [ intento , setIntento ] = useState ( 0 )
6868 const eco = useRef ( crearEcoPredictivo ( ) )
69- const tecleadoEn = useRef < number | null > ( null )
7069 const esperaDeEco = useRef < number | undefined > ( undefined )
70+ const pings = useRef ( new Map < number , number > ( ) )
71+ const medidas = useRef < number [ ] > ( [ ] )
7172 const latencia = useRef ( 0 )
7273 const avisada = useRef ( 0 )
7374 const [ subida , setSubida ] = useState < AvanceDeSubida | null > ( null )
@@ -88,6 +89,26 @@ export function useTerminalSocket({
8889 }
8990 } , [ ] )
9091
92+ /** Lo mejor de las últimas medidas; decide también si conviene adelantar. */
93+ const anotarLatencia = ( numero : number ) => {
94+ const salida = pings . current . get ( numero )
95+ if ( salida === undefined ) return
96+ pings . current . delete ( numero )
97+
98+ medidas . current = [ ...medidas . current , performance . now ( ) - salida ] . slice (
99+ - MEDIDAS_A_RECORDAR ,
100+ )
101+ latencia . current = Math . min ( ...medidas . current )
102+ eco . current . activar ( latencia . current > LATENCIA_PARA_ADELANTAR_MS )
103+
104+ const tramo =
105+ Math . round ( latencia . current / TRAMO_DE_LATENCIA_MS ) * TRAMO_DE_LATENCIA_MS
106+ if ( tramo !== avisada . current ) {
107+ avisada . current = tramo
108+ avisar . current ( tramo )
109+ }
110+ }
111+
91112 const enviarTamano = useCallback ( ( ) => {
92113 const vista = terminal . current
93114 if ( vista ) enviar ( { resize : { cols : vista . cols , rows : vista . rows } } )
@@ -112,7 +133,6 @@ export function useTerminalSocket({
112133 } , SIN_ECO_MS )
113134 }
114135 }
115- if ( tecleadoEn . current === null ) tecleadoEn . current = performance . now ( )
116136 enviar ( { command : datos } )
117137 } ,
118138 [ enviar , terminal ] ,
@@ -234,6 +254,7 @@ export function useTerminalSocket({
234254 useEffect ( ( ) => {
235255 let vigente = true
236256 let reintento : number | undefined
257+ let reloj : number | undefined
237258 let inicialPendiente = buildInitialCommand ( rutaInicial )
238259
239260 const escribir = ( texto : string ) => terminal . current ?. write ( texto )
@@ -293,12 +314,22 @@ export function useTerminalSocket({
293314 )
294315 socket . current = abierto
295316
317+ let numeroDePing = 0
318+ const medirLatencia = ( ) => {
319+ if ( abierto . readyState !== WebSocket . OPEN ) return
320+ numeroDePing += 1
321+ pings . current . set ( numeroDePing , performance . now ( ) )
322+ abierto . send ( JSON . stringify ( { ping : numeroDePing } ) )
323+ }
324+ reloj = window . setInterval ( medirLatencia , PING_CADA_MS )
325+
296326 abierto . onopen = ( ) => {
297327 if ( reconexiones . current > 0 ) escribir ( '\r\n\x1b[2m— Reconectado —\x1b[0m\r\n' )
298328 reconexiones . current = 0
299329 onEstado ( { fase : 'conectada' } )
300330 terminal . current ?. focus ( )
301331 enviarTamano ( )
332+ medirLatencia ( )
302333 }
303334
304335 abierto . onmessage = ( evento ) => {
@@ -327,22 +358,11 @@ export function useTerminalSocket({
327358 window . clearTimeout ( esperaDeEco . current )
328359 esperaDeEco . current = undefined
329360
330- if ( tecleadoEn . current !== null ) {
331- const ida = performance . now ( ) - tecleadoEn . current
332- tecleadoEn . current = null
333- if ( ida <= LATENCIA_CREIBLE_MS ) {
334- latencia . current =
335- latencia . current * ( 1 - PESO_DE_LA_ULTIMA ) + ida * PESO_DE_LA_ULTIMA
336- eco . current . activar ( latencia . current > LATENCIA_PARA_ADELANTAR_MS )
337-
338- const tramo =
339- Math . round ( latencia . current / TRAMO_DE_LATENCIA_MS ) * TRAMO_DE_LATENCIA_MS
340- if ( tramo !== avisada . current ) {
341- avisada . current = tramo
342- avisar . current ( tramo )
343- }
344- }
361+ if ( mensaje . type === 'pong' ) {
362+ anotarLatencia ( mensaje . ping )
363+ return
345364 }
365+
346366 escribir ( eco . current . reconciliar ( mensaje . message ) )
347367
348368 // El cd espera a la primera salida (el prompt): en onopen la shell
@@ -355,7 +375,6 @@ export function useTerminalSocket({
355375
356376 abierto . onclose = ( evento ) => {
357377 escribir ( eco . current . limpiar ( ) )
358- tecleadoEn . current = null
359378 soltarTurnos ( )
360379 const contestar = respuesta . current
361380 respuesta . current = null
@@ -384,6 +403,7 @@ export function useTerminalSocket({
384403 document . removeEventListener ( 'visibilitychange' , reintentarYa )
385404 window . clearTimeout ( apertura )
386405 window . clearTimeout ( reintento )
406+ window . clearInterval ( reloj )
387407 window . clearTimeout ( esperaDeEco . current )
388408
389409 const abierto = socket . current
0 commit comments