Skip to content

feat(ux): abrir y cerrar el detalle con clic directo sobre el orbe #170

Open

Description

@Rafaeldelinares

Qué propone

Abrir y cerrar el panel de detalle con un clic directo sobre el orbe, además del hover actual. Es un cambio de UX multiplataforma, así que va por separado del arreglo de Wayland (#169).

Motivación

Hoy el panel abre por mouseenter con OPEN_DELAY y cierra por mouseleave + temporizador. En Linux eso es frágil:

Decisión de producto que hace falta

El clic tiene que coexistir con el hover, y ahí está la pregunta que no quiero resolver por mi cuenta:

  1. Clic como atajo, hover intacto. OPEN_DELAY se queda como está; el clic abre al instante. Simple y retrocompatible. El coste: en Linux el hover a 350 ms sigue sintiéndose lento para quien ya se acostumbró al clic.
  2. Clic como interruptor, hover reducido. Un clic sobre un orbe con el panel abierto lo cierra, y sobre otro orbe lo cambia directamente. Es el comportamiento que many desktop apps usan para menús popover. El coste: es un cambio de modelo mental respecto al hover puro, y hay que decidir si el hover sigue abriendo o solo cambia.
  3. Clic excluyente en Linux. Donde el hover es frágil, el clic es el mecanismo principal y el hover solo cambia entre orbes ya abierto. Es lo más coherente con el diagnóstico, pero diverge por plataforma.

También hay que decidir si el clic debe abrir también el panel ya visible desde el orbe correspondiente (por ejemplo, para cambiar de provider sin cerrar antes).

Por qué no va en #169

Porque no es un arreglo de una plataforma: es una decisión de interacción que afecta a los tres. En #169, OPEN_DELAY quedó acotado a Linux y el toggle por clic fuera, para no mezclar un cambio de comportamiento global con el acotamiento de los recoveries por plataforma.

Alcance de la implementación, si se aprueba

  • ProviderGauge acepta un onClick opcional; si no se pasa, el comportamiento actual queda intacto.
  • openFor(id, el, immediate) con immediate para saltar el temporizador.
  • Un test que fije que el clic abre, que el segundo clic sobre el mismo orbe cierra, y que sobre otro orbe cambia.
  • Decisión de plataforma documentada en el propio código.

Nota

Mientras tanto, en Linux el OPEN_DELAY de #169 queda en 350 ms. Si esta issue se aprueba con la opción 3, ese valor podría volver a 90 ms para Linux porque el clic pasa a ser la vía principal.

Activity

  1. egdev6 commented on Oct 22, 2026

    @egdev6
    Owner

    Tras valorar las opciones de la issue, propuesta de decisión de producto:

    Clic = anclar / desanclar, no abrir

    Tenéis razón en que un clic que abre entra en conflicto con el hover (hacen lo mismo, y el hover ya abrió el panel a mitad del recorrido del cursor hacia el clic). En cambio, si el clic hace algo que el hover no puede, ambos mecanismos coexisten sin pelearse:

    • Hover → abre transitorio, como hoy: se cierra al salir (mouseleave + temporizador). Sin cambios.
    • Clic sobre el orbe → ancla el panel: deja de cerrarse al mover el cursor fuera, hasta segundo clic sobre el mismo orbe o clic fuera del panel.
    • Clic sobre otro orbe con panel anclado → cambia de provider manteniendo el anclaje.
    • Clic sobre el orbe ya anclado → desancla y cierra.

    Casos de uso que esto habilita y el hover no cubre: copiar un dato del panel, cambiar de provider varias veces sin perseguir el cursor, y acceso por teclado/touch de forma natural.

    Notas de implementación (coherentes con el alcance ya descrito en la issue):

    • ProviderGauge acepta onClick opcional; openFor(id, el, immediate) se mantiene para el cambio de orbe anclado.
    • El anclaje convive con mouseleave: el cierre solo procede si el panel no está anclado. Cuidado con detailInsideRef y con la interacción del clic con el umbral de arrastre del grip (distinguir por pointerup sin movimiento).
    • Tests mínimos: clic ancla → mover fuera no cierra → segundo clic desancla y cierra; clic sobre otro orbe con panel anclado cambia manteniendo anclaje; hover sigue cerrando normal si nunca hubo clic.
    • Multiplataforma, sin divergencia: el anclaje se comporta igual en Linux, macOS y Windows, y no depende del OPEN_DELAY.

    El retardo de apertura se resuelve aparte

    Para la queja del OPEN_DELAY lento a propósito, el ajuste de duración de la animación (estético) se ha movido a #171, y el retardo de apertura (conductual) queda pendiente de decidir: los presets de sensibilidad del hover parecen la vía natural, con la ventaja de que un default de ~250 ms podría sustituir al 350 ms por plataforma de #169. Eso sería nota de cierre de #169, no requisito de esta issue.

  2. Rafaeldelinares commented on Oct 29, 2026

    @Rafaeldelinares
    ContributorAuthor

    @egdev6

    Gracias por la propuesta: nos encaja y la tomamos tal cual. El hover sigue abriendo transitorio y el clic ancla/desancla, que además habilita el caso que más nos faltaba (copiar un dato del panel sin perseguir el cursor).

    Pedimos asignación para implementarla.

    Sobre la decisión que dejás pendiente —el retardo de apertura—, aportamos tres datos medidos sobre main (4a5363e), para que no tengas que ir a buscarlos:

    Dato Valor Dónde
    Retardo de apertura 350 ms en Linux, 90 ms en Windows/macOS src/lib/os-detect.ts → openDelayForOs()
    Cierre explícito no existe: ni Escape ni clic fuera; se cierra solo al salir de tira y panel (280 ms) src/Widget.tsx → CLOSE_DELAY, scheduleClose()
    Presencia nativa del cursor existe sólo en macOS (allí WebKit pierde el mouseleave); en Linux el cierre depende del DOM src/lib/detail-presence.ts → shouldSampleNativePresence()

    El efecto que describe Rafael en su uso diario encaja con el tercero: el panel vive en otra ventana nativa, así que al ir del orbe al panel hay que cruzar el hueco; al cruzarlo, la tira dispara su mouseleave y arranca el cierre, y si el puntero no entra al panel dentro de los 280 ms, el panel se cierra bajo el puntero. En macOS eso lo resuelve el muestreo nativo; en Linux no hay red de contención.

    Nuestra recomendación: no tocar el retardo dentro de esta issue. Si más adelante hay presets de sensibilidad del hover, el default puede pasar a ~250 ms y sustituir al 350 ms por plataforma (como señalás, como nota de cierre de #179); mientras tanto, 350 ms es lo que evita el barrido accidental hacia otro orbe.

    Un dato de contexto que te puede servir: con la corrección de #179 (PR #205, abierta y ya actualizada con el main actual) el arrastre bajo X11 mueve la tira y la deja pegada al borde del área de trabajo en nuestra sesión, así que el caso "hover + clic-ancla" es usable también en Linux.

  3. egdev6 commented on Oct 29, 2026

    @egdev6
    Owner

    @Rafaeldelinares Gracias por los datos, los comprobé contra main y son correctos. Os asigno la issue.

    Estoy de acuerdo en dejar el retardo de apertura fuera de esta issue: 350 ms en Linux se queda como está, y los presets de sensibilidad del hover irán por separado.

    Antes de escribir código, pido que el plan resuelva un punto: cómo se cierra el panel anclado cuando se hace clic fuera. La tira y el panel son ventanas nativas transparentes que no toman el foco. Un clic fuera de las dos le llega a otra aplicación, así que el DOM no se entera. Lo mismo pasa con Escape, porque esas ventanas no reciben el teclado. Si no hay una señal nativa (pérdida de foco u otra equivalente) por plataforma, el panel anclado puede quedarse abierto de forma indefinida. Me vale una respuesta por plataforma, aunque en alguna la única salida sea el segundo clic sobre el orbe, siempre que quede documentada en el código.

    Un detalle más: el clic tiene que respetar el umbral de arrastre de los orbes que ya existe (orbDrag.moved en Widget.tsx), para que reordenar no ancle el panel por accidente. Los tests deberían cubrir ese caso.

  4. Rafaeldelinares commented on Oct 29, 2026

    @Rafaeldelinares
    ContributorAuthor

    @egdev6 plan para el anclaje por clic, con la respuesta a tu pregunta por plataforma.

    Cómo se cierra el panel anclado

    Tu pregunta tiene dos partes: qué señal existe para notar el clic fuera, y qué hace el panel mientras está anclado. Empiezo por la segunda, porque condiciona la primera.

    El panel anclado nunca es una trampa. Las salidas, siempre disponibles:

    • segundo clic sobre el orbe (desancla y cierra);
    • clic en otra aplicación: la tira pierde el foco y el anclaje se suelta (ver más abajo);
    • Escape: lo atiende la tira, que tiene el foco desde el clic.

    No añadimos ningún control nuevo al panel: el que ya tiene (ocultar el provider) es otra cosa, y un botón de cerrar sería un control redundante para quien ya conoce el segundo clic.

    La señal: la tira pierde el foco

    Al hacer clic en un orbe, la ventana de la tira toma el foco (es una ventana normal y focusable; focus: false en la config es sólo el arranque). A partir de ahí:

    • si el usuario hace clic en otra aplicación, la tira pierde el foco ⇒ WindowEvent::Focused(false) en la ventana widget ⇒ desancamos y cerramos.

    Esa señal es nuestra (la de una ventana que ya controlamos), no un watch global de la entrada: no requiere XGrabButton ni tocar el servidor X, no añade dependencias y no estorba a otras aplicaciones.

    Por qué descartamos la alternativa: un XGrabButton pasivo sobre la ventana raíz recibe los clics globales, pero bajo XWayland el servidor X no ve los clics de las aplicaciones nativas, así que no resolvería el caso de GNOME; y a cambio sería un watch global de la entrada mientras el panel está anclado.

    Qué se puede y qué no se puede detectar

    Plataforma ¿el clic fuera cierra el panel? Por qué
    X11 Sí el clic en otra aplicación quita el foco a la tira
    XWayland Sí el compositor mueve el foco al clicar en la app nativa o X11
    Wayland nativo Sí, mientras el compositor conceda el foco a la tira al hacer clic en ella si no lo concede, la tira nunca tiene foco y no hay señal: quedan las salidas explícitas

    En el caso de que el compositor no dé el foco a la tira, el panel anclado se cierra con el segundo clic sobre el orbe, y eso queda documentado en el código (es el mismo criterio que aceptaste para el caso de una plataforma sin señal).

    El clic no debe anclar al reordenar

    El umbral ya existe: orbDrag marca moved a partir de 6 px (Math.hypot(...) < 6 en Widget.tsx), y hay además una guarda de 400 ms tras reordenar (lastReorderAt). El anclaje sólo se activa si el gesto fue un clic y no un arrastre: orbDrag.current?.moved === false y fuera de la ventana de lastReorderAt, con test determinista de que reordenar un orbe no ancla el panel.

    Qué cambia y qué no

    • El hover sigue como hoy: abre transitorio y se cierra al salir (280 ms). No se toca openDelayForOs (350 ms en Linux, como acordamos).
    • Un solo estado, no dos banderas: el estado del panel dice qué provider se ve y si está anclado. Con eso, las reglas son explícitas y no pueden discrepar:
      • el hover no cierra un panel anclado;
      • el temporizador de 8 s del "abrir el más cargado" no cierra un panel anclado;
      • la tira no se colapsa mientras el panel esté anclado;
      • ocultar el provider desde el panel, o cerrar la app, limpian el anclaje.
    • Al anclar, la tira toma el foco —es el clic quien se lo da— y Escape se atiende ahí mismo. El teclado pasa a Quorb hasta que el usuario hace clic en otra aplicación; el coste y sus límites por plataforma van en el mensaje siguiente.
    • Clic sobre otro orbe con el panel anclado: cambia de provider manteniendo el anclaje.
    • Segundo clic sobre el mismo orbe: desancla y cierra.

    Verificación prevista

    • Tests deterministas (frontend): el clic ancla; el segundo clic desancla; el hover sigue cerrando si nunca hubo clic; un arrastre de reordenación no ancla; cambiar de orbe con el panel anclado mantiene el anclaje; perder el foco suelta el anclaje; el panel anclado no se colapsa ni lo cierra el temporizador.
    • Lado nativo: la decisión «la tira perdió el foco» se prueba con un seam, sin servidor X.
    • Prueba manual en KDE/X11: anclar → clic en otra aplicación → se cierra; clic dentro del panel → sigue; segundo clic → se cierra; Escape → se cierra; y el hover sin cambios.
    • En Wayland nativo: documentar en la evidencia de la PR el comportamiento real (si el compositor concede el foco a la tira o no).

    Tamaño esperado

    Unas cientos de líneas: estado del panel en el frontend, un evento nuevo en el backend y sus tests. Sin dependencias nuevas, sin watch global de la entrada y sin tocar la capa de colocación. Nada del tamaño de #179, que fue cambiar un protocolo nativo entero.


    Editado (30-10-2026): la viñeta «Al anclar, la tira conserva el foco (no le quitamos el teclado a la aplicación del usuario)» era falsa: el clic sí se lleva el teclado. Queda corregida arriba. Los cuatro puntos que pediste están respondidos en el mensaje siguiente.

  5. egdev6 commented on Oct 29, 2026

    @egdev6
    Owner

    @Rafaeldelinares gracias por el plan. La dirección es buena: un solo estado para proveedor y anclaje, reglas explícitas frente al hover, el temporizador y el colapso de la tira, la pérdida de foco de una ventana propia en vez de un watch global de la entrada, y el umbral de reordenación cubierto con test (6 px de orbDrag.moved más los 400 ms de lastReorderAt). Antes de empezar el código, necesito que el plan resuelva cuatro puntos:

    1. Windows y macOS. La tabla solo cubre X11, XWayland y Wayland nativo, y la pregunta era por plataforma. Explicad cómo se comporta la pérdida de foco en Windows y en macOS al hacer clic en otra aplicación, teniendo en cuenta que en macOS WebKit ya obligó a muestrear la presencia nativa (bug(macOS): el popup de detalle permanece visible tras dejar el hover (incluso moviendo el widget) #154). Si en alguna plataforma la única salida es el segundo clic, documentadlo igual.

    2. Clic dentro del panel. La ventana de detalle se puede enfocar: en src-tauri/src/lib.rs (constructor de DETAIL) solo lleva .focused(false), sin el .focusable(false) del overlay. Al hacer clic en el panel para copiar un dato, el foco pasa a esa ventana y la tira recibe Focused(false). Con la regla del plan, el panel anclado se cerraría justo en el caso de uso principal. La condición tiene que ser que el foco salga de las dos ventanas propias, la tira y el detalle, y conviene cubrirlo con un test sobre el seam de foco, no solo con la prueba manual.

    3. Foco y teclado. El plan dice que la tira conserva el foco «sin quitarle el teclado a la aplicación del usuario», pero que el clic en el orbe dé el foco a la tira es precisamente quitárselo: de eso dependen Escape y la señal de pérdida de foco. Me parece un coste aceptable, pero tiene que quedar descrito tal cual, por plataforma. Por ejemplo: «tras anclar, lo que se escribe va a Quorb hasta hacer clic en otra aplicación, y ese clic cierra el panel».

    4. Escape con el foco en el panel. Si el usuario hizo clic dentro del panel, el foco está en la ventana de detalle: Escape tiene que cerrar el panel anclado también desde ahí, no solo desde la tira.

    Con esos cuatro puntos en el plan, adelante con la implementación.

  6. Rafaeldelinares commented on Oct 30, 2026

    @Rafaeldelinares
    ContributorAuthor

    @egdev6 los cuatro puntos, respondidos con el código a la vista. Y una corrección de fondo: en el mensaje anterior escribí que «la tira conserva el foco (no le quitamos el teclado a la aplicación del usuario)». Es falso —el clic se lleva el teclado— y ya lo he corregido en ese mensaje. Aquí va la respuesta completa.

    1. Windows y macOS

    La señal es la misma en las tres plataformas: WindowEvent::Focused(false) en una ventana propia. Lo que cambia es el evento nativo que la produce, y eso lo he leído en tao 0.37.1, el crate que usa el proyecto:

    Plataforma Evento nativo que tao traduce a Focused(false) Fuente
    Linux (X11 y XWayland) focus_out_event de GTK tao-0.37.1/src/platform_impl/linux/event_loop.rs:655-664
    Linux (Wayland nativo) el mismo focus_out_event, que GTK emite cuando el compositor revoca el foco ídem
    Windows WM_NCACTIVATE con wparam = 0 y WM_KILLFOCUS → lose_active_focus tao-0.37.1/src/platform_impl/windows/event_loop.rs:1729-1757 y 825-850
    macOS windowDidResignKey tao-0.37.1/src/platform_impl/macos/window_delegate.rs:384-411

    Y dos precisiones que salen del mismo código:

    • La tira y el detalle son focusable de verdad en las tres plataformas. En macOS, tao hace que canBecomeKeyWindow devuelva el atributo focusable (tao-0.37.1/src/platform_impl/macos/window.rs:415-424): la tira y el detalle lo son por defecto —sólo el overlay lleva focusable(false), src-tauri/src/lib.rs:1072—, así que las dos pueden recibir el foco. En Linux, la tira nace con accept_focus = false porque se crea con focus: false, pero tao lo restituye a true en el primer dibujo (tao-0.37.1/src/platform_impl/linux/window.rs:90 y 230-240): la tira acepta el foco en cuanto está visible, y el clic se lo da.
    • El problema de macOS que obligó al muestreo nativo (bug(macOS): el popup de detalle permanece visible tras dejar el hover (incluso moviendo el widget) #154) no toca esta señal. Lo que se perdió en bug(macOS): el popup de detalle permanece visible tras dejar el hover (incluso moviendo el widget) #154 fueron los eventos DOM de ratón (mouseleave) que WebKit deja de entregar en ventanas transparentes; por eso el hover usa muestreo nativo de la posición del cursor (src/Widget.tsx:786-828, sólo macOS). El foco es un evento de ventana, no de WebKit: llega igual en las tres plataformas.

    Qué está verificado y qué no:

    Plataforma ¿el clic fuera cierra el panel? Verificado Pendiente
    X11 Sí señal (fuente de tao) + medición real en esta máquina —
    XWayland Sí ídem —
    Wayland nativo Depende del compositor la señal la medición: aquí hay sesión Wayland (KDE Plasma 6.7.5) y la hacemos con el build de la PR
    Windows Sí por construcción la señal (fuente de tao) smoke en un Windows real: aquí no lo hay
    macOS Sí por construcción la señal (fuente de tao) smoke en un macOS real: aquí no lo hay

    «Por construcción» quiere decir exactamente eso: el evento nativo que produce el Focused(false) está leído en el código de tao, pero el recorrido completo no está medido en esas dos plataformas. No lo damos por bueno: en la evidencia de la PR irá como verificado en fuente y pendiente de smoke, y si alguien con esos sistemas lo prueba, mejor.

    En el caso de que el compositor no conceda el foco a la tira (Wayland nativo es el caso posible), la única salida es el segundo clic sobre el orbe, y queda escrito en el código, como aceptaste para el caso de una plataforma sin señal.

    En Wayland nativo ese caso es más probable que en las otras plataformas, y por un motivo que viene de #180/#210: el arrastre lo delega el compositor (startDragging), así que el gesto no necesita que la tira tenga el foco de teclado. Nada obliga entonces al compositor a dárselo al hacer clic, y de ahí que el comportamiento real del foco bajo Wayland nativo haya que medirlo y no deducirlo.

    2. Clic dentro del panel

    Tienes razón: el detalle es focusable. En src-tauri/src/lib.rs:1046-1056 lleva .focused(false) —que sólo es el arranque— y no lleva el .focusable(false) del overlay (src-tauri/src/lib.rs:1072). Al hacer clic dentro del panel para copiar un dato, el foco pasa a la ventana de detalle y la tira recibe Focused(false).

    La condición cambia: el anclaje sólo se suelta cuando el foco sale de las dos ventanas propias —la tira y el detalle—. El foco en el detalle es «sigo dentro de mi propia interfaz».

    Hay una trampa de orden en las tres plataformas, y por eso la decisión no se toma en el evento sino después: la pérdida de foco de la ventana antigua llega normalmente antes que la ganancia de la nueva (en X11 el gestor envía FocusOut a la vieja y FocusIn a la nueva; en macOS windowDidResignKey precede a windowDidBecomeKey; en Windows WM_KILLFOCUS precede a WM_SETFOCUS). Una regla que reaccionase al primer Focused(false) cerraría el panel justo en el caso de uso principal.

    La solución: guardamos el estado de foco de las dos ventanas y no soltamos nada hasta 150 ms después del último evento de foco; si en ese momento ninguna de las dos ventanas propias tiene el foco, el anclaje se suelta. La transferencia de foco entre nuestras dos ventanas ocurre en el mismo lote de eventos, así que 150 ms sobran; el número va como constante y con su test. El diseño no depende del orden: el settling window es lo que lo hace robusto.

    3. Foco y teclado

    El coste, dicho sin rodeos: tras anclar, el teclado va a Quorb. Lo que escribes va a la tira, no a la aplicación en la que estabas. Y no lo hacemos artificialmente: no llamamos a set_focus, no añadimos atajo global ni watch de la entrada; el teclado se mueve porque el usuario ha clicado nuestra ventana.

    Plataforma Dónde queda el teclado tras anclar Cómo se sale del panel
    X11 / XWayland en Quorb (la tira toma el foco al hacer clic) clic en otra aplicación → se cierra
    Windows en Quorb ídem
    macOS en Quorb, y además el clic activa la aplicación ídem
    Wayland nativo en Quorb si el compositor concede el foco; si no, donde estaba ídem, o el segundo clic sobre el orbe

    El coste queda acotado por la misma regla que lo produce: el clic que te devuelve a tu aplicación es el clic que cierra el panel. Y si el compositor no da el foco, no hay coste —ni hay señal—.

    4. Escape con el foco en el panel

    Hoy no hay ningún Escape en la tira ni en el panel (en src/ sólo lo atiende Settings.tsx), así que esto es nuevo:

    • la tira atiende Escape → cierra el panel y suelta el anclaje;
    • el panel atiende Escape → avisa a la tira por el mismo canal que ya usa detail-dismiss (src/lib/detail-panel.ts:8-15) → cierra el panel y suelta el anclaje;
    • en los dos casos el panel se cierra tanto si estaba anclado como si era transitorio.

    Qué cambia y qué no

    • El hover sigue como hoy: transitorio, se cierra al salir (280 ms). openDelayForOs no se toca (350 ms en Linux, como acordamos).
    • Un solo estado: el estado del panel dice qué provider se ve y si está anclado. Con eso:
      • el hover no cierra un panel anclado;
      • el temporizador de 8 s del «abrir el más cargado» no lo cierra;
      • la tira no se colapsa mientras el panel esté anclado;
      • ocultar el provider desde el panel, o cerrar la app, limpian el anclaje.
    • El anclaje se suelta por: segundo clic sobre el orbe, foco fuera de las dos ventanas propias, Escape desde cualquiera de las dos, ocultar el provider o cerrar la app.
    • El clic sobre otro orbe con el panel anclado cambia de provider manteniendo el anclaje.
    • Reordenar no ancla: se respetan el umbral de 6 px (orbDrag.moved) y la guarda de 400 ms (lastReorderAt) (src/Widget.tsx:964-1007).

    Verificación prevista

    Frontend (vitest):

    • el clic ancla y el segundo clic desancla;
    • el hover sigue cerrando si nunca hubo clic;
    • reordenar un orbe no ancla;
    • cambiar de orbe con el panel anclado mantiene el anclaje;
    • el panel anclado no se colapsa ni lo cierra el temporizador de 8 s;
    • Escape en la tira cierra el panel; Escape en el panel también.

    Seam de foco (vitest, con reloj falso):

    • tira con el foco y anclada → la tira pierde el foco y el detalle lo toma (el clic dentro del panel) → el anclaje aguanta;
    • el mismo caso con el orden real de los eventos (pérdida antes que ganancia) → el anclaje aguanta;
    • tira con el foco y anclada → la tira pierde el foco y nadie lo toma → el anclaje se suelta 150 ms después;
    • sin anclar, los eventos de foco no cambian nada.

    Nativo: el backend sólo reenvía los eventos de foco de las dos ventanas propias; la decisión y la ventana de 150 ms viven en el seam del frontend, así que se prueban sin servidor X.

    Manual (KDE Plasma 6.7.5, X11/XWayland): anclar → clic dentro del panel (copiar un dato) → sigue abierto; clic en otra aplicación → se cierra; segundo clic → se cierra; Escape desde la tira y desde el panel → se cierra; el hover sin cambios.

    Wayland nativo: medimos en esta máquina si el compositor concede el foco a la tira. Si lo concede, el clic fuera cierra el panel; si no, la única salida es el segundo clic, y eso queda en la evidencia de la PR.

    Windows y macOS: sin host para medirlo aquí; queda en la evidencia de la PR como verificado en fuente y pendiente de smoke.

    Tamaño esperado

    Unas cientos de líneas: el seam de foco y sus tests, el reenvío de los eventos en el backend, el estado de anclaje en la tira y el Escape en las dos ventanas. Sin dependencias nuevas, sin watch global de la entrada y sin tocar la capa de colocación. Nada del tamaño de #179, que fue cambiar un protocolo nativo entero.

  7. egdev6 commented on Oct 31, 2026

    @egdev6
    Owner

    @Rafaeldelinares, el plan responde los cuatro puntos de la revisión anterior y la dirección es buena: foco tratado como conjunto de las dos ventanas, Escape desde ambas y coste de teclado descrito explícitamente. Para avanzar, dejemos estas precisiones incorporadas al plan y sus tests:

    • Los 150 ms son una ventana de estabilización heurística, no una garantía multiplataforma de que la transferencia de foco ya terminó. Probar tira → detalle y detalle → tira, y cancelar o invalidar temporizadores pendientes al cerrar o volver a anclar, para que una decisión antigua no cierre una sesión nueva.
    • Inicializar correctamente el estado de foco: la tira puede estar enfocada antes del clic que ancla. El seguimiento no debe empezar con ambas ventanas en falso y depender únicamente de eventos futuros.
    • Conservar el resultado del gesto para el clic: actualmente onPointerUp limpia orbDrag.current antes de click, por lo que comprobar allí moved === false no alcanza. Cubrir clic normal, arrastre de reordenación y activación por teclado, respetando también la guarda posterior al reordenamiento.
    • Explicitar que el hover sobre otro orbe no cambia el proveedor anclado; el cambio anclado debe requerir clic. Añadir la prueba de que segundo clic y Escape cierran sin reapertura por un temporizador de hover pendiente o un evento de foco, observando también lo que ocurre después del retardo de apertura.

    La distinción entre foco nativo y eventos DOM de ratón es correcta. Mantengamos Windows/macOS como pendientes de smoke: la lectura de tao acredita la señal disponible, pero no el recorrido completo. Con estas precisiones, adelante con la implementación dentro del alcance acordado, sin modificar los retardos de apertura.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

status:approvedApproved for implementation by the repository ownertype:featureNew functionality

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions