2 Commits

Author SHA1 Message Date
schalli 5919a55cbf fix(desktop): Link-Klicks vor dem Opener-Skript des Clients abfangen
Tessera CI/CD / Lint & Type Check (push) Successful in 53s
Tessera CI/CD / Tests (push) Successful in 1m33s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 19s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m13s
Die erste Fassung lauschte auf window und lief damit nach dem von
tauri-plugin-opener eingeschleusten Link-Skript. Dieses ruft bei
target=_blank preventDefault und plugin:opener|open_url auf, was von der
Server-Seite aus nicht freigegeben ist - der Klick verpuffte weiter
(auf VM 8233 per Klick-Protokoll gemessen). Der Helfer lauscht jetzt auf
document: nach Reacts Handlern, vor dem Opener-Skript.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:20:42 +02:00
schalli 7edaf8c00b docs(quick-261001-cxo): Summary und STATE
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 09:20:49 +02:00
4 changed files with 57 additions and 14 deletions
+2 -1
View File
@@ -6,7 +6,7 @@ current_phase_name: desktop-client-fertigstellen
status: verified
stopped_at: "22.09.2026: 1.3.0 freigegeben; danach quick-260922-hk4 — Bilderrahmen-Bilder liegen jetzt im Dateibereich (user-files) statt in der Datenbank, Umzug laeuft automatisch beim Start, Selbstheilung aus der alten data-Spalte eingebaut; im Browser nachgewiesen. NAECHSTER SCHRITT, vom Nutzer noch nicht bestaetigt: (1) einmaliges Aufraeumen, damit ein Modul seine Dashboard-Kachel selbst mitbringt (heute sieben Hartkodierungen je Kachel; Katalog zeigt auch Kacheln gesperrter Module; gesperrte Kachel bleibt leer statt zu erklaeren) — das Geruest WIDGET_MODULE_MAP existiert und ist leer; (2) danach das Proxmox-Modul (PVE/PBS/PMG) und seine Kachel. Offen beim Nutzer: Live-Server auf 1.3.0 ziehen, neuen Client per Browser installieren."
last_updated: "2026-09-23T15:30:00.000Z"
last_activity: 2026-09-30
last_activity: 2026-10-01
last_activity_desc: Quick 260928-ujj — Design Mosaik uebernommen, Hintergrund pro Benutzer in der DB; Freigabe 1.5.0
state_head: 4d485432c003a6caf68f6d85aff7de0bd27794e2
progress:
@@ -483,6 +483,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests.
| 260929-dzu | **Eigene Module fuer jeden Benutzer (persoenlich).** `CustomModule.ownerUserId` (null = gemeinsam), RLS-Muster SearchProvider, Einstellungen > Eigene Module (nur eigene), Verwaltung nur gemeinsame; Browser: Sichtbarkeit/Rechte wie verlangt. Nebenbei ohne eigenen Quick: Zentrierung entfernt (bc4c011), Desktop neue Fenster -> System-Browser (76a9234, Windows-VM bestaetigt), Single-Instance auf VM bestaetigt. | 2026-09-29 | c703d87,ee97b4e,8f41bd2 | [260929-dzu-eigene-module-fuer-jeden-benutzer-persoe](./quick/260929-dzu-eigene-module-fuer-jeden-benutzer-persoe/) |
| 260929-if2 | **Erinnerungen-Widget (Reminder).** Modell `Reminder` + RLS, API /reminders (anlegen/listen/bearbeiten/loeschen/erledigt/snooze, 409/404-Regeln), E-Mail-Scheduler alle 30 s mit Claim-once + max. 3 Versuche, globaler ReminderNotifier (Browser-Notification, Desktop via Tauri-Notification mit Laufzeit-Capability nur fuer die Server-Origin, Pattern escaped + vorab geprueft). Verifier human_needed (Windows-Toast offen); Browser dunkel bestanden inkl. echter Mail ueber MailHog. api 1570, web 1069, cargo 57. Nebenbei: eigene Module ohne Kopfzeile (cd1f8f6), Update-Klick prueft frisch (41d00a3). | 2026-09-29 | 325c5dd,709b41a,6879c75 | [260929-if2-reminder-widget-mit-benachrichtigung](./quick/260929-if2-reminder-widget-mit-benachrichtigung/) |
| 260929-lh3 | **Favoriten: eigene Symbol-Adresse wirkt.** Neue iconUrl ersetzt Upload + bumpt iconVersion; iconUrl wird auch gespeichert, wenn nur der Browser sie laden kann (kein 422 mehr, nur Formpruefung); Kachel: Proxy -> iconUrl direkt -> origin/favicon -> Buchstabe; Discovery liest <link rel=icon> auch aus Nicht-2xx-Seiten (docuvita 400). | 2026-09-29 | 7188c5b,b15c746,0e72ad4 | [260929-lh3-favoriten-eigenes-symbol-wirkt-nicht](./quick/260929-lh3-favoriten-eigenes-symbol-wirkt-nicht/) |
| 261001-cxo | Desktop-Client: Links mit target=_blank (Favoriten) oeffnen jetzt im System-Browser (DesktopExternalLinks -> window.open) | 2026-10-01 | 61a971c | [261001-cxo](./quick/261001-cxo-desktop-client-links-mit-target-blank-oe/) |
## Deferred Items
@@ -0,0 +1,19 @@
---
quick_id: 261001-cxo
status: complete
date: 2026-10-01
commit: 61a971c
---
# Summary: Desktop-Client – Links mit target=_blank
- Nachgestellt auf VM 8233 (Client 1.9.0, alpha): Favorit-Klick ohne Wirkung, Such-Widget (`window.open`) oeffnet Edge.
- Neu `DesktopExternalLinks` (apps/web/src/components/desktop/desktop-external-links.tsx), in `app/layout.tsx` eingehaengt: im Client Links-/Mittelklick auf `a[target=_blank]` mit http/https -> `window.open(href,'_blank','noopener,noreferrer')`; verhinderte Klicks bleiben verhindert.
- 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 `<anonymous>: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“.
@@ -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(<DesktopExternalLinks />);
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(<DesktopExternalLinks />);
@@ -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]);