diff --git a/.planning/quick/261001-cxo-desktop-client-links-mit-target-blank-oe/261001-cxo-SUMMARY.md b/.planning/quick/261001-cxo-desktop-client-links-mit-target-blank-oe/261001-cxo-SUMMARY.md index fef599f..c9c8ae7 100644 --- a/.planning/quick/261001-cxo-desktop-client-links-mit-target-blank-oe/261001-cxo-SUMMARY.md +++ b/.planning/quick/261001-cxo-desktop-client-links-mit-target-blank-oe/261001-cxo-SUMMARY.md @@ -12,3 +12,8 @@ commit: 61a971c - 4 Tests (desktop-external-links.test.tsx), tsc + biome sauber. CHANGELOG „Unveröffentlicht → Behoben“. - Reine Web-Aenderung: kein neuer Client noetig, wirkt nach Pull des web-Images. - Offen: Nachweis auf VM nach alpha-Pull (User). + +## Nachtrag (gleicher Tag): erste Fassung wirkte nicht +- Nach alpha-Pull weiter ohne Wirkung. Diagnose per temporaerem Klick-Protokoll (lokaler Stack, VM-Client per portproxy auf localhost:3000): `preventDefault` kam aus `:1:442` = Link-Skript von tauri-plugin-opener (init-iife.js, Listener auf `window`): faengt `target=_blank`-Klicks ab und ruft `plugin:opener|open_url` – von der Server-Seite nicht freigegeben, Klick verpufft. Unser Listener auf `window` lief danach und sah den Klick als verhindert. +- Fix: Listener auf `document` (Bubble) – nach React (Wurzel document), vor dem Opener-Skript. Auf VM nachgewiesen: Favorit oeffnet Edge, im Bearbeiten-Modus nichts (React-onClick verhindert). +- Commit siehe git log; Test „kommt dem Link-Skript des Clients auf window zuvor“. diff --git a/apps/web/src/components/desktop/desktop-external-links.test.tsx b/apps/web/src/components/desktop/desktop-external-links.test.tsx index 9697de8..b6f43f7 100644 --- a/apps/web/src/components/desktop/desktop-external-links.test.tsx +++ b/apps/web/src/components/desktop/desktop-external-links.test.tsx @@ -56,6 +56,25 @@ describe('DesktopExternalLinks', () => { expect(openSpy).toHaveBeenCalledWith('https://chatgpt.com/', '_blank', 'noopener,noreferrer'); }); + it('mit Cookie: kommt dem Link-Skript des Clients auf window zuvor', async () => { + setCookie(); + render(); + await act(async () => {}); + + // Nachbau von tauri-plugin-opener (init-iife.js): bricht ab, wenn der + // Klick schon verhindert ist, sonst preventDefault + IPC-Aufruf. + const openerSaw: boolean[] = []; + const opener = (e: MouseEvent) => openerSaw.push(e.defaultPrevented); + window.addEventListener('click', opener); + try { + fire(link('https://chatgpt.com/', '_blank')); + } finally { + window.removeEventListener('click', opener); + } + expect(openSpy).toHaveBeenCalledTimes(1); + expect(openerSaw).toEqual([true]); + }); + it('mit Cookie: Mittelklick auf _blank-Link oeffnet ebenfalls', async () => { setCookie(); render(); diff --git a/apps/web/src/components/desktop/desktop-external-links.tsx b/apps/web/src/components/desktop/desktop-external-links.tsx index 1cba46c..6476248 100644 --- a/apps/web/src/components/desktop/desktop-external-links.tsx +++ b/apps/web/src/components/desktop/desktop-external-links.tsx @@ -6,16 +6,20 @@ import { useIsDesktopClient } from '@/lib/desktop-client'; /** * DesktopExternalLinks (quick-261001-cxo) — Links mit `target="_blank"` * (Favoriten, „In neuem Tab öffnen“ bei XFrame/eigenen Modulen, Quellen im - * Ausschreibungs-Radar) taten im Desktop-Client unter Windows beim Klick - * nichts, `window.open` (Such-Widget) dagegen landet ueber `on_new_window` - * im System-Browser (VM 8233, 01.10.2026: Favorit ohne Wirkung, Suche oeffnet - * Edge). Im Client leitet dieser Helfer solche Klicks deshalb auf - * `window.open` um. + * Ausschreibungs-Radar) taten im Desktop-Client beim Klick nichts, waehrend + * `window.open` (Such-Widget) ueber `on_new_window` im System-Browser landet. * - * Lauscht auf `window` in der Bubble-Phase, also NACH Reacts Handlern am - * Dokument: ein Klick, den die Seite selbst verhindert (Favoriten im - * Bearbeiten-Modus), bleibt verhindert. Nur http/https -- alles andere - * verwirft der Client ohnehin. + * Ursache (VM 8233, 01.10.2026, gemessen): tauri-plugin-opener schleust ein + * Skript ein, das auf `window` solche Klicks abfaengt, `preventDefault()` + * ruft und `plugin:opener|open_url` aufruft -- von der Server-Seite aus ist + * dieser Aufruf nicht freigegeben, der Klick verpufft. Dieser Helfer kommt + * ihm zuvor und oeffnet per `window.open`; das Opener-Skript sieht den Klick + * dann als verhindert und tut nichts. + * + * Lauscht deshalb auf `document` in der Bubble-Phase: NACH Reacts Handlern + * (Wurzel `document`, frueher registriert) -- ein Klick, den die Seite selbst + * verhindert (Favoriten im Bearbeiten-Modus), bleibt verhindert --, aber VOR + * dem Opener-Skript auf `window`. Nur http/https. */ function externalTarget(event: MouseEvent): HTMLAnchorElement | null { if (event.defaultPrevented) return null; @@ -45,11 +49,11 @@ export function DesktopExternalLinks() { window.open(anchor.href, '_blank', 'noopener,noreferrer'); }; - window.addEventListener('click', handler); - window.addEventListener('auxclick', handler); + document.addEventListener('click', handler); + document.addEventListener('auxclick', handler); return () => { - window.removeEventListener('click', handler); - window.removeEventListener('auxclick', handler); + document.removeEventListener('click', handler); + document.removeEventListener('auxclick', handler); }; }, [isDesktop]);