From 5175a9f7fb469c226d0d2f47e032af830496983d Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Fri, 15 May 2026 00:53:45 -0600 Subject: [PATCH 01/13] fix: open TunnelCard dropdown below for first item, above for rest --- web-svelte/src/lib/components/TunnelCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-svelte/src/lib/components/TunnelCard.svelte b/web-svelte/src/lib/components/TunnelCard.svelte index ed5f602..a8a5e02 100644 --- a/web-svelte/src/lib/components/TunnelCard.svelte +++ b/web-svelte/src/lib/components/TunnelCard.svelte @@ -50,7 +50,7 @@ }: TunnelCardProps = $props(); let t = $derived($translate); - const dropdownAlign = $derived(index === totalItems - 1 ? "top-left" : "left"); + const dropdownAlign = $derived(index === 0 ? "left" : "top-left"); const toast = useToast(); From 240226f764885400711aaac78915626a21d44a65 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Fri, 15 May 2026 19:04:10 -0600 Subject: [PATCH 02/13] fix: remove hover scale on TunnelCard --- web-svelte/src/lib/components/TunnelCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-svelte/src/lib/components/TunnelCard.svelte b/web-svelte/src/lib/components/TunnelCard.svelte index a8a5e02..05254ab 100644 --- a/web-svelte/src/lib/components/TunnelCard.svelte +++ b/web-svelte/src/lib/components/TunnelCard.svelte @@ -200,7 +200,7 @@
Date: Fri, 15 May 2026 19:04:15 -0600 Subject: [PATCH 03/13] fix: improve Dropdown visibility and click handling - Keep menu in DOM with opacity control instead of conditional rendering - Add pointer-events control to prevent clicking hidden menu - Simplify animation logic by removing isAnimating state - Add stopPropagation on trigger to prevent immediate close on click --- web-svelte/src/lib/components/Dropdown.svelte | 156 ++++++++---------- 1 file changed, 72 insertions(+), 84 deletions(-) diff --git a/web-svelte/src/lib/components/Dropdown.svelte b/web-svelte/src/lib/components/Dropdown.svelte index 910a5e5..038a723 100644 --- a/web-svelte/src/lib/components/Dropdown.svelte +++ b/web-svelte/src/lib/components/Dropdown.svelte @@ -38,46 +38,23 @@ }: DropdownProps = $props(); let isOpen = $state(false); - let isAnimating = $state(false); let menuEl: HTMLDivElement | undefined = $state(); - const isVisible = $derived(isOpen || isAnimating); const menuPosition = $derived.by(() => { const vert = align.startsWith("top") ? "" : "top-full mt-1.5"; return `${POSITION_MAP[align]} ${vert}`; }); function open() { - if (isOpen) return; + if (isOpen || !menuEl) return; isOpen = true; - isAnimating = true; - requestAnimationFrame(() => { - if (!menuEl) return; - animate( - menuEl, - { opacity: 1, scale: 1, y: 0 }, - { type: "spring" }, - ).finished.then(() => { - isAnimating = false; - }); - }); + animate(menuEl, { opacity: 1, scale: 1, y: 0 }, { type: "spring" }); } function close() { - if (!isOpen || !menuEl) { - isOpen = false; - isAnimating = false; - return; - } + if (!isOpen || !menuEl) return; isOpen = false; - isAnimating = true; - animate( - menuEl, - { opacity: 0, scale: 1, y: -4 }, - { type: "spring" }, - ).finished.then(() => { - isAnimating = false; - }); + animate(menuEl, { opacity: 0, scale: 1, y: -4 }, { type: "spring" }); } function toggle() { @@ -85,34 +62,45 @@ } function handleOutsideClick(e: MouseEvent) { - if (!(e.target as HTMLElement).closest(".dropdown-container")) close(); + if (!isOpen) return; + const target = e.target as HTMLElement; + if ( + target.closest(".dropdown-trigger") || + target.closest(".dropdown-menu") + ) return; + close(); + } + + function handleKeydown(e: KeyboardEvent) { + if (e.key === "Escape") close(); } $effect(() => { - if (!isOpen) return; - document.addEventListener("click", handleOutsideClick); - document.addEventListener("keydown", handleKeydown); + if (isOpen) { + document.addEventListener("click", handleOutsideClick, true); + document.addEventListener("keydown", handleKeydown); + } else { + document.removeEventListener("click", handleOutsideClick, true); + document.removeEventListener("keydown", handleKeydown); + } + return () => { - document.removeEventListener("click", handleOutsideClick); + document.removeEventListener("click", handleOutsideClick, true); document.removeEventListener("keydown", handleKeydown); }; }); - - function handleKeydown(e: KeyboardEvent) { - if (e.key === "Escape") close(); - }
- {#if isVisible} - - {/if} -
+ + +
\ No newline at end of file From 3efd89e7d82913afabdc70f74c4b67f29cc7a05d Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Fri, 15 May 2026 19:18:50 -0600 Subject: [PATCH 04/13] fix: only allow esc/ctrl+c to exit forms, not b/q --- internal/app/model_types.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/app/model_types.go b/internal/app/model_types.go index 755a55f..2adf416 100644 --- a/internal/app/model_types.go +++ b/internal/app/model_types.go @@ -113,12 +113,12 @@ var DefaultKeys = KeyMap{ key.WithHelp("s", "settings"), ), Back: key.NewBinding( - key.WithKeys("esc", "b"), - key.WithHelp("esc/b", "back"), + key.WithKeys("esc"), + key.WithHelp("esc", "back"), ), Quit: key.NewBinding( - key.WithKeys("q", "ctrl+c"), - key.WithHelp("q/ctrl+c", "quit"), + key.WithKeys("ctrl+c"), + key.WithHelp("ctrl+c", "quit"), ), Help: key.NewBinding( key.WithKeys("?"), From efb6bcc134778daef1dfb5b07214dcea1fb05c28 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 13:22:45 -0600 Subject: [PATCH 05/13] refactor: improve accessibility --- web-svelte/src/lib/components/Dropdown.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web-svelte/src/lib/components/Dropdown.svelte b/web-svelte/src/lib/components/Dropdown.svelte index 038a723..e452db8 100644 --- a/web-svelte/src/lib/components/Dropdown.svelte +++ b/web-svelte/src/lib/components/Dropdown.svelte @@ -120,6 +120,8 @@ bind:this={menuEl} id={id ? `${id}-menu` : undefined} role="menu" + inert={!isOpen} + aria-hidden={!isOpen} aria-orientation="vertical" style="opacity: 0; scale: 0.95; transform: translateY(-4px);" class={cn( From dc6d7a2eafdc719649d25f182d02c48c0c3e0141 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 13:23:07 -0600 Subject: [PATCH 06/13] chore: updte gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 57cc20a..7d7aa73 100644 --- a/.gitignore +++ b/.gitignore @@ -52,5 +52,6 @@ desktop/frontend/ # .specs *.specs .docs +.pi AGENTS.md \ No newline at end of file From eb2e44cef3123596a6946b2493b88a460790c668 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 13:52:03 -0600 Subject: [PATCH 07/13] fix: improve navigation hints and capitalization in UI text --- internal/i18n/locales/en.yaml | 8 ++++---- internal/i18n/locales/es.yaml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/i18n/locales/en.yaml b/internal/i18n/locales/en.yaml index 74e81e6..55d79e3 100644 --- a/internal/i18n/locales/en.yaml +++ b/internal/i18n/locales/en.yaml @@ -158,10 +158,10 @@ tip_dashboard: "💡 Tip: Web dashboard at" # Settings UI settings_title: "⚙ Settings" -settings_nav_hint: "↑/↓ navigate • space/enter toggle • esc back" +settings_nav_hint: "↑/↓ Navigate • space/enter Toggle • esc Back" tunnel_logs: "📋 Tunnel Logs" -logs_nav_hint: "esc/b: back • ↑/↓: scroll" +logs_nav_hint: "esc/b: Back • ↑/↓: Scroll" # UI elements play_indicator: "▶" @@ -243,8 +243,8 @@ validation_required_fields: "Name and Port are required" # TUI Elements app_name_tui: "🎲 Foundry Tunnel Manager" -web: "web" -config: "config" +web: "Web" +config: "Config" # CLI uninstall_not_found: "ftm is not installed or not in PATH" diff --git a/internal/i18n/locales/es.yaml b/internal/i18n/locales/es.yaml index ea0fe7a..179855d 100644 --- a/internal/i18n/locales/es.yaml +++ b/internal/i18n/locales/es.yaml @@ -139,7 +139,7 @@ arrow_hint: "← → para cambiar" numbers_hint: "solo números" submit_new: "Crear Túnel" submit_edit: "Guardar Cambios" -form_nav_hint: "TAB: navegar • ENTER: enviar • ESC: cancelar" +form_nav_hint: "TAB: Navegar • ENTER: Enviar • ESC: Cancelar" # Empty states no_tunnels: "No hay túneles configurados" @@ -158,10 +158,10 @@ tip_dashboard: "💡 Tip: Panel web en" # Settings UI settings_title: "⚙ Configuración" -settings_nav_hint: "↑/↓ navegar • espacio/enter cambiar • esc volver" +settings_nav_hint: "↑/↓ Navegar • espacio/enter Cambiar • esc Volver" tunnel_logs: "📋 Logs del Túnel" -logs_nav_hint: "esc/b: volver • ↑/↓: scroll" +logs_nav_hint: "esc/b: Volver • ↑/↓: Scroll" # UI elements play_indicator: "▶" @@ -243,8 +243,8 @@ validation_required_fields: "Nombre y Puerto son requeridos" # TUI Elements app_name_tui: "🎲 Foundry Tunnel Manager" -web: "web" -config: "configuración" +web: "Web" +config: "Configuración" # CLI uninstall_not_found: "ftm no está instalado o no está en PATH" From cbcc85c8b8ee8bdd33c26d6054122421f1bbd128 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 15:12:40 -0600 Subject: [PATCH 08/13] fix: update notification sound field alignment and add esc key to quit binding --- internal/app/model_types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/app/model_types.go b/internal/app/model_types.go index 2adf416..5b1e057 100644 --- a/internal/app/model_types.go +++ b/internal/app/model_types.go @@ -29,7 +29,7 @@ const ( type Settings struct { NotificationsEnabled bool - NotificationSound bool + NotificationSound bool Theme string } @@ -118,6 +118,7 @@ var DefaultKeys = KeyMap{ ), Quit: key.NewBinding( key.WithKeys("ctrl+c"), + key.WithKeys("esc"), key.WithHelp("ctrl+c", "quit"), ), Help: key.NewBinding( From c7f85dbe940a23469e58e6415ea73a60f69ba896 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 15:20:15 -0600 Subject: [PATCH 09/13] fix: prevent event propagation in dropdown toggle function --- web-svelte/src/lib/components/Dropdown.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web-svelte/src/lib/components/Dropdown.svelte b/web-svelte/src/lib/components/Dropdown.svelte index e452db8..7a50a18 100644 --- a/web-svelte/src/lib/components/Dropdown.svelte +++ b/web-svelte/src/lib/components/Dropdown.svelte @@ -57,7 +57,8 @@ animate(menuEl, { opacity: 0, scale: 1, y: -4 }, { type: "spring" }); } - function toggle() { + function toggle(e: MouseEvent) { + e?.stopPropagation(); isOpen ? close() : open(); } @@ -95,7 +96,7 @@
-
\ No newline at end of file + From 86111272f456797742533ed6587108ee61fa8380 Mon Sep 17 00:00:00 2001 From: Bryan Villafuerte Date: Wed, 3 Jun 2026 15:34:09 -0600 Subject: [PATCH 13/13] fix: update quit key bindings to include esc for improved usability --- internal/app/model_types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/app/model_types.go b/internal/app/model_types.go index 5b1e057..ef6bd2a 100644 --- a/internal/app/model_types.go +++ b/internal/app/model_types.go @@ -117,9 +117,9 @@ var DefaultKeys = KeyMap{ key.WithHelp("esc", "back"), ), Quit: key.NewBinding( - key.WithKeys("ctrl+c"), - key.WithKeys("esc"), + key.WithKeys("ctrl+c", "esc"), key.WithHelp("ctrl+c", "quit"), + key.WithHelp("esc", "quit"), ), Help: key.NewBinding( key.WithKeys("?"),