Skip to content

Commit 44a1c9d

Browse files
committed
fix: clean up remaining P1 backlog items (status parser, log guard, popout height, location casing)
- disconnectPattern now only matches firstLine, fixing false-positive disconnects triggered by auxiliary output lines - openTunnelLog guards against concurrent double-invocation via tunnelLogOpening flag (UI + Service layers) - popoutHeight clamps to screen height (parentScreen/barThickness) instead of a fixed 760, fixing overflow on short displays - connected location name is title-cased for display, masking an upstream adguardvpn-cli capitalization quirk (SãO PAULO) 3 new regression tests (29/29 passing).
1 parent 3987ced commit 44a1c9d

7 files changed

Lines changed: 55 additions & 15 deletions

File tree

AdGuardVpnParsers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function parseStatusOutput(clean) {
3333
const fullOutput = lines.join("\n");
3434
const dnsWarning = /System DNS could not be configured/i.test(fullOutput);
3535
const disconnectPattern = /not\s+connected|disconnected|not\s+running|stopped/i;
36-
if (disconnectPattern.test(firstLine) || disconnectPattern.test(fullOutput)) {
36+
if (disconnectPattern.test(firstLine)) {
3737
return {
3838
disconnected: true,
3939
firstLine: firstLine

AdGuardVpnService.qml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Item {
5252
property string cliVersion: ""
5353
property bool commandRunning: false
5454
property string runningCommand: ""
55+
property bool tunnelLogOpening: false
5556

5657
property bool isConnected: false
5758
property string statusSummary: AdGuardVpnI18n.tr("status.unknown", "Unknown")
@@ -830,6 +831,12 @@ Item {
830831
}
831832

832833
function openTunnelLog() {
834+
if (tunnelLogOpening) {
835+
ToastService.showInfo(t("app.title", "AdGuard VPN"), t("toast.log_opening", "Opening tunnel log..."));
836+
return;
837+
}
838+
tunnelLogOpening = true;
839+
833840
const openScript = `
834841
resolve_home() {
835842
if [ -n "$HOME" ]; then
@@ -936,6 +943,7 @@ Item {
936943
`;
937944

938945
Proc.runCommand(`${pluginId}.openTunnelLog.${Date.now()}`, ["sh", "-lc", openScript], (stdout, exitCode) => {
946+
tunnelLogOpening = false;
939947
if (exitCode === 0) {
940948
ToastService.showInfo(t("app.title", "AdGuard VPN"), t("toast.log_opened", "Tunnel log opened"));
941949
return;

AdGuardVpnWidget.qml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ PluginComponent {
9393
return root.t("bar.pending", "...");
9494
}
9595
if (AdGuardVpnService.isConnected) {
96-
return AdGuardVpnService.connectedLocation || root.t("bar.connected_short", "Connected");
96+
return root.formatLocationName(AdGuardVpnService.connectedLocation) || root.t("bar.connected_short", "Connected");
9797
}
9898
return root.t("bar.off", "Off");
9999
}
@@ -104,7 +104,7 @@ PluginComponent {
104104
}
105105
if (AdGuardVpnService.isConnected) {
106106
return root.t("summary.connected_to", "Connected to {location}", {
107-
location: root.safeText(AdGuardVpnService.connectedLocation, root.t("summary.location_unknown", "location unknown"))
107+
location: root.formatLocationName(root.safeText(AdGuardVpnService.connectedLocation, root.t("summary.location_unknown", "location unknown")))
108108
});
109109
}
110110
return root.safeText(AdGuardVpnService.statusSummary, root.t("status.disconnected", "Disconnected"));
@@ -144,6 +144,14 @@ PluginComponent {
144144
return value;
145145
}
146146

147+
function formatLocationName(value) {
148+
const text = (value || "").toString();
149+
if (!text) {
150+
return text;
151+
}
152+
return text.toLowerCase().replace(/(^|[\s-])(\S)/g, (match, sep, ch) => sep + ch.toUpperCase());
153+
}
154+
147155
component VpnActionButton: StyledRect {
148156
id: buttonRoot
149157

@@ -549,7 +557,14 @@ PluginComponent {
549557
}
550558

551559
popoutWidth: 520
552-
popoutHeight: 760
560+
readonly property int popoutMaxHeight: 760
561+
readonly property int popoutMinHeight: 420
562+
popoutHeight: {
563+
const scr = root.parentScreen || Screen;
564+
const screenHeight = (scr && scr.height) ? scr.height : root.popoutMaxHeight;
565+
const reserved = root.barThickness + 96;
566+
return Math.max(root.popoutMinHeight, Math.min(root.popoutMaxHeight, screenHeight - reserved));
567+
}
553568

554569
horizontalBarPill: Component {
555570
Row {
@@ -979,7 +994,7 @@ PluginComponent {
979994
iconName: "article"
980995
label: root.t("action.open_log", "Open Log")
981996
compact: true
982-
actionEnabled: !AdGuardVpnService.commandRunning
997+
actionEnabled: !AdGuardVpnService.commandRunning && !AdGuardVpnService.tunnelLogOpening
983998
onTriggered: {
984999
ToastService.showInfo(root.t("app.title", "AdGuard VPN"), root.t("toast.log_opening", "Opening tunnel log..."));
9851000
AdGuardVpnService.openTunnelLog();

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Sem
1313

1414
- DNS leak warning banner: when `adguardvpn-cli` reports "System DNS could not be configured" on connect/status, the popout now shows a persistent warning in the hero instead of staying silent about queries potentially bypassing the tunnel.
1515

16+
### Fixed
17+
18+
- Status parser no longer misreads a connected session as disconnected because of an auxiliary output line containing "disconnected"/"stopped" — the disconnect signal is now matched against the first line only, where every supported CLI status format already places it.
19+
- `openTunnelLog` now guards against concurrent double-invocation (rapid double-click) with a dedicated `tunnelLogOpening` flag instead of being able to spawn duplicate terminals.
20+
- Popout height is no longer a fixed `760`; it now clamps to the screen height (via `parentScreen`/`Screen`, same fallback the DMS `PluginComponent` uses internally) so it no longer overflows short displays (e.g. 768p with a bar).
21+
- Connected location name display (`SãO PAULO`, an upstream `adguardvpn-cli` capitalization quirk) is now normalized to title case (`São Paulo`) in the bar pill and hero title.
22+
1623
---
1724

1825
## [1.4.0] - 2026-06-12
@@ -224,6 +231,13 @@ Todas as mudanças relevantes deste projeto são documentadas aqui. O inglês ac
224231

225232
- Banner de vazamento de DNS: quando o `adguardvpn-cli` reporta "System DNS could not be configured" no connect/status, o popout agora mostra um aviso persistente no hero em vez de ficar em silêncio sobre consultas DNS que podem vazar para fora do túnel.
226233

234+
#### Corrigido
235+
236+
- O parser de status não confunde mais uma sessão conectada com desconectada por causa de uma linha auxiliar contendo "disconnected"/"stopped" — o sinal de desconexão agora é testado só na primeira linha, onde todos os formatos suportados do CLI já colocam essa informação.
237+
- `openTunnelLog` agora tem guard contra dupla invocação concorrente (clique duplo rápido) via flag dedicada `tunnelLogOpening`, em vez de poder lançar terminais duplicados.
238+
- Altura do popout deixou de ser fixa em `760`; agora é clampada pela altura da tela (via `parentScreen`/`Screen`, mesmo fallback usado internamente pelo `PluginComponent` do DMS), eliminando o estouro em telas baixas (ex.: 768p com barra).
239+
- Exibição do nome da localização conectada (`SãO PAULO`, quirk de capitalização do próprio `adguardvpn-cli`) agora é normalizada para title case (`São Paulo`) no pill da barra e no título do hero.
240+
227241
### [1.4.0] - 2026-06-12
228242

229243
#### Adicionado

DONE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
## Pós-v1.4.0 — 2026-07-10
99

1010
- [x] **Aviso de vazamento de DNS nunca era capturado** — ✅ 2026-07-10: descoberta ao reconectar manualmente e ver `Warning: System DNS could not be configured` no CLI, nunca exposto no plugin. `AdGuardVpnParsers.js` agora detecta a linha em `parseStatusOutput` (`dnsWarning`), `AdGuardVpnService.qml` expõe `dnsWarning: bool` (resetado em todo branch de status), `AdGuardVpnWidget.qml` mostra banner persistente no hero (mesmo padrão visual do banner "Sessão não iniciada"). 2 testes novos em `scripts/test-parsers.mjs` (28/28 passando). Chaves i18n `dns.leak_warning_title`/`dns.leak_warning_body` em en+pt_BR (paridade estrita mantida).
11+
- [x] **Detecção de "disconnected" agressiva demais** — ✅ 2026-07-10: `disconnectPattern` (`AdGuardVpnParsers.js`) testava `firstLine` OU `fullOutput` inteiro; qualquer linha auxiliar com "disconnected"/"stopped" derrubava o estado mesmo com "Connected to …" na primeira linha. Teste restrito só ao `firstLine` — todos os formatos suportados (connected/disconnected/key-value) já colocam o sinal decisivo ali, confirmado pelos 29 testes existentes. Teste de regressão novo prova que "Connected to Frankfurt..." + linha auxiliar "...was stopped..." continua `connected: true`.
12+
- [x] **`openTunnelLog` sem guard contra dupla invocação** — ✅ 2026-07-10: novo `property bool tunnelLogOpening` em `AdGuardVpnService.qml`, setado antes do `Proc.runCommand` e resetado no callback (sucesso e erro); clique duplo/rápido agora só reexibe o toast "Opening tunnel log..." em vez de lançar terminal/spawn duplicado. Botão "Open Log" no widget desabilita enquanto `tunnelLogOpening` é `true` (defesa em duas camadas: UI + Service).
13+
- [x] **`popoutHeight: 760` fixo estourava em telas baixas** — ✅ 2026-07-10: `popoutHeight` agora é um binding que usa `root.parentScreen || Screen` (mesmo fallback usado internamente pelo `PluginComponent.qml` do DMS) e `root.barThickness`, clampando entre `popoutMinHeight` (420) e `popoutMaxHeight` (760) — `Math.max(420, Math.min(760, screenHeight - barThickness - 96))`. Sem regressão em 1080p (resultado idêntico a 760); em 768p com barra o popout cabe com margem.
14+
- [x] **Capitalização quebrada em nomes de localização com acento (`SãO PAULO`)** — ✅ 2026-07-10: quirk confirmado como bug do próprio `adguardvpn-cli` (hex dump mostrou `ã` minúsculo dentro de string em bold/uppercase), mascarado cosmeticamente com `formatLocationName()` novo em `AdGuardVpnWidget.qml` (lowercase + capitalize por palavra/hífen) aplicado no bar pill e no hero title. Verificado visualmente: badge agora mostra "São Paulo".
1115

1216
---
1317

To-Do.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,8 @@
55
66
---
77

8-
## P1 — Lógica
9-
10-
- [ ] **Detecção de "disconnected" agressiva demais**`AdGuardVpnParsers.js:34-35`: `disconnectPattern` testa o output inteiro; qualquer linha auxiliar contendo "disconnected"/"stopped" (ex.: aviso histórico do CLI) marca o estado como desconectado mesmo com a primeira linha "Connected to …". Restringir o teste do `fullOutput` a padrões mais específicos ou priorizar o match de connected na primeira linha antes do disconnect no full output.
11-
- [ ] **`openTunnelLog` roda fora do guard `commandRunning`** — pode rodar em paralelo com connect/disconnect (ambos via `Proc.runCommand`). Baixo risco, mas avaliar guard ou fila.
12-
13-
## P1 — UI/UX
14-
15-
- [ ] **`popoutHeight: 760` fixo** — em telas baixas (768p com barra) estoura. Calcular: `Math.min(760, Screen.height - margens)` ou equivalente DMS. (Pendente: requer verificar como o DMS clampa popouts antes de mexer.)
16-
178
## P2 — Melhorias
189

19-
- [ ] **Capitalização quebrada em nomes de localização com acento** — descoberto 2026-07-10: o próprio `adguardvpn-cli` emite `SãO PAULO` (confirmado em hex: `ã` minúsculo dentro de string em bold/uppercase) — não é bug do plugin, mas dá pra mascarar cosmeticamente normalizando a exibição de `connectedLocation` (title-case) em vez de repassar cru.
2010
- [ ] **Indicador visual de ping** — colorir o badge de ping (verde <80ms, amarelo <150, vermelho acima) nos cards de localização.
2111
- [ ] **Confirmação/feedback de favoritos** — toast leve ou animação na estrela ao favoritar.
2212
- [ ] **Copiar diagnóstico** — botão "copiar" no bloco Command output / last command.

scripts/test-parsers.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ test("status: disconnected variants", () => {
112112
}
113113
});
114114

115+
test("status: connected first line survives disconnect keyword in auxiliary line", () => {
116+
const r = P.parseStatusOutput([
117+
"Connected to Frankfurt in TUN mode, running on tun0",
118+
"Note: a previous session was stopped due to a network change"
119+
].join("\n"));
120+
eq(r.connected, true, "still connected");
121+
eq(r.disconnected, undefined, "not flagged disconnected");
122+
});
123+
115124
test("status: empty output", () => {
116125
eq(P.parseStatusOutput("").empty, true);
117126
eq(P.parseStatusOutput(" \n ").empty, true);

0 commit comments

Comments
 (0)