fix(quick-261005-blw): Links in Notiz-Kacheln oeffnen in neuem Tab

NoteLink ersetzt <a> der Markdown-Vorschau: target=_blank,
rel=noopener noreferrer; #-Sprungmarken bleiben im Fenster.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-05 08:22:43 +02:00
parent f3e816fafa
commit 781bc9f9ee
8 changed files with 89 additions and 99 deletions
-96
View File
@@ -1,96 +0,0 @@
{
"version": "1.0",
"timestamp": "2026-10-03T01:28:08.977Z",
"phase": null,
"phase_name": "Quick-Auftraege 02./03.10. nach Freigabe 1.9.2 (keine GSD-Phase)",
"phase_dir": null,
"plan": null,
"task": 0,
"total_tasks": 0,
"status": "paused",
"completed_tasks": [
{
"id": 1,
"name": "261002-fm5: Finanzbuchhaltung - Module Kantinenabrechnung + Handelsware (aus HeadFlow portiert, DATEV-Export)",
"status": "done",
"commit": "b94d267"
},
{
"id": 2,
"name": "261002-icv: Freigabestufe Benutzen/Verwalten (@ModuleManage), Matrix + Benutzerdetails",
"status": "done",
"commit": "7188733"
},
{
"id": 3,
"name": "261002-k67 + 261002-kxc: Modul Nextcloud-Status (Ampel, Logos, Dashboard-Kachel) + Benachrichtigung bei Rot (Mail + Desktop)",
"status": "done",
"commit": "14674fc"
},
{
"id": 4,
"name": "Duenne Widget-Scrollleiste; Nextcloud-Logos breiter/eckig, Upload auf 256 px verkleinert; Doppelklick-Sperre; Docker-Log je Anfrage + je Pruefung",
"status": "done",
"commit": "19340fc"
},
{
"id": 5,
"name": "Desktop: Dateidialog folgt Dunkelmodus (set_theme); Linux-Dashboard ohne Einblend-Animation, Melder-Drossel 10 s",
"status": "done",
"commit": "53a49a1"
},
{
"id": 6,
"name": "261003-387: Kategorien durch Admins bearbeitbar; Oberflaechentexte ohne Mandant",
"status": "done",
"commit": "3562b15"
}
],
"remaining_tasks": [],
"blockers": [],
"async_jobs": [],
"human_actions_pending": [
{
"action": "alpha auf 3562b15 ziehen (CI 491 gruen)",
"context": "Kategorien-Verwaltung + neutrale Texte",
"blocking": false
},
{
"action": "Linux-Desktop-App per Tray aktualisieren, Upload im Dunkelmodus und Dashboard-Aufbau pruefen",
"context": "set_theme-Fix und Animations-Abschaltung nur per Messung/Unit-Test geprueft, nicht im echten Client des Users",
"blocking": false
},
{
"action": "Logo der Schallicloud auf alpha neu hochladen",
"context": "altes Logo 2346 px wird erst beim Neuhochladen verkleinert",
"blocking": false
},
{
"action": "Woche ab 05.10.: Kollege testet echte Kantinen-CSV und Handelsware-XLSX auf alpha + DATEV-Import",
"context": "Umlaute der Handelsware-TXT (UTF-8) evtl. auf Windows-1252 umstellen",
"blocking": false
}
],
"decisions": [
{
"decision": "Freigaben vergeben nur Admins; Verwalten darf Modul-Einstellungen",
"rationale": "User 02.10.",
"phase": "quick"
},
{
"decision": "Firmenwerte (Beraternr, Mandantennr, Lohnart, Erloeskonto) nur als Einstellungen, nie als Vorgabe",
"rationale": "keine kundenspezifischen Defaults",
"phase": "quick"
},
{
"decision": "Nextcloud-Ampel: gruen=neuester Patch im unterstuetzten Zweig, gelb=Update oder EOL<90 Tage, rot=EOL/unerreichbar/Wartung",
"rationale": "User-Auswahl 02.10.",
"phase": "quick"
}
],
"uncommitted_files": [
"?? .planning/quick/261002-fm5-finanzbuchhaltung-module-kantinenabrechn/testdata/"
],
"next_action": "Beim Start fragen: alpha gezogen? Linux-App (dunkler Upload-Dialog, Dashboard-Aufbau) ok? Danach im alpha-Log pruefen, ob Anfrage-Schuebe (/reminders, /modules/active, /alerts) weg sind. Offen beantworten lassen: Menuename 'Administrator' vs. Seitentitel 'Verwaltung' angleichen?",
"context_notes": "main = origin/main = 3562b15 (+ wip-Commit). live = v1.9.2; seither nur Beta. DISPLAY :10 auf dem Dev-Host ist die SICHTBARE Sitzung des Users - keine GUI-Tests ohne Ankuendigung (Rezept in Memory reference_linux_client_test). Lokal: MailHog laeuft (8025), Testkategorie 'Serverraum', Test-Clouds im Nextcloud-Modul. Docker-Log hat jetzt [HTTP]-Zeilen je Anfrage."
}
+2 -1
View File
@@ -31,7 +31,7 @@ See: .planning/PROJECT.md (updated 2026-07-17)
Phase: 18 (desktop-client-fertigstellen) — COMPLETE (2026-09-17, Verifikation passed, Windows-Bedienprobe bestanden) Phase: 18 (desktop-client-fertigstellen) — COMPLETE (2026-09-17, Verifikation passed, Windows-Bedienprobe bestanden)
Plan: 6 of 6 Plan: 6 of 6
Status: Alle 18 Phasen abgeschlossen; Version 1.2.0 freigegeben. Kein laufender Meilenstein. Nach 1.2.0 auf main (Beta): Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse, Update in der App (signiert), Versionszeile auf der Setup-Seite — alles verifiziert und auf VM/CI nachgewiesen Status: Alle 18 Phasen abgeschlossen; Version 1.2.0 freigegeben. Kein laufender Meilenstein. Nach 1.2.0 auf main (Beta): Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse, Update in der App (signiert), Versionszeile auf der Setup-Seite — alles verifiziert und auf VM/CI nachgewiesen
Last activity: 2026-10-03 - Quick 261003-387 Kategorien bearbeitbar (lokal nachgewiesen, nicht gepusht) Last activity: 2026-10-05 - Quick 261005-blw Notiz-Links in neuem Tab
Progress: [██████████] 99% Progress: [██████████] 99%
@@ -492,6 +492,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests.
| 261002-k67 | Modul Nextcloud-Status mit Ampel-Kacheln und Dashboard-Uebersicht | 2026-10-02 | 5ef7b0c..87a7b7c | [261002-k67-modul-nextcloud-status-mit-ampel-kacheln](.planning/quick/261002-k67-modul-nextcloud-status-mit-ampel-kacheln/) | | 261002-k67 | Modul Nextcloud-Status mit Ampel-Kacheln und Dashboard-Uebersicht | 2026-10-02 | 5ef7b0c..87a7b7c | [261002-k67-modul-nextcloud-status-mit-ampel-kacheln](.planning/quick/261002-k67-modul-nextcloud-status-mit-ampel-kacheln/) |
| 261002-kxc | Nextcloud-Status: Benachrichtigung bei Rot je Benutzer (Mail + Desktop-Hinweis), Klartext-Fehler | 2026-10-02 | faed0d7..6c4bff6 | [261002-kxc-nextcloud-status-benachrichtigung-bei-ro](.planning/quick/261002-kxc-nextcloud-status-benachrichtigung-bei-ro/) | | 261002-kxc | Nextcloud-Status: Benachrichtigung bei Rot je Benutzer (Mail + Desktop-Hinweis), Klartext-Fehler | 2026-10-02 | faed0d7..6c4bff6 | [261002-kxc-nextcloud-status-benachrichtigung-bei-ro](.planning/quick/261002-kxc-nextcloud-status-benachrichtigung-bei-ro/) |
| 261003-387 | Kategorien durch Admins bearbeitbar (anlegen, umbenennen, sortieren, loeschen mit Verschieben, Module zuordnen) | 2026-10-03 | 8ec116c..f2c0a89 | [261003-387-kategorien-durch-admins-bearbeitbar-umbe](.planning/quick/261003-387-kategorien-durch-admins-bearbeitbar-umbe/) | | 261003-387 | Kategorien durch Admins bearbeitbar (anlegen, umbenennen, sortieren, loeschen mit Verschieben, Module zuordnen) | 2026-10-03 | 8ec116c..f2c0a89 | [261003-387-kategorien-durch-admins-bearbeitbar-umbe](.planning/quick/261003-387-kategorien-durch-admins-bearbeitbar-umbe/) |
| 261005-blw | Links in Notiz-Kacheln oeffnen in neuem Tab | 2026-10-05 | (dieser Commit) | [261005-blw-notiz-links-in-neuem-tab](.planning/quick/261005-blw-notiz-links-in-neuem-tab/) |
## Deferred Items ## Deferred Items
@@ -0,0 +1,14 @@
---
quick_id: 261005-blw
slug: notiz-links-in-neuem-tab
date: 2026-10-05
---
# Notiz-Links in neuem Tab
User 05.10.: URL in einer Notiz oeffnet im selben Fenster. Links sollen immer in einem neuen Tab oeffnen.
## Task 1
- `NoteLink` in `note-task-list.tsx`: ersetzt `<a>` der Markdown-Vorschau, setzt `target="_blank"` + `rel="noopener noreferrer"`, `#`-Sprungmarken bleiben im Fenster, `node` nicht ins DOM.
- In `note-widget.tsx` per `previewOptions.components.a` einhaengen.
- Test mit echtem `MDEditor.Markdown` + `rehypeSanitize` (Markdown-Link, nackte URL, Sprungmarke).
@@ -0,0 +1,13 @@
---
quick_id: 261005-blw
status: complete
date: 2026-10-05
---
# 261005-blw: Notiz-Links in neuem Tab — SUMMARY
- `NoteLink` (note-task-list.tsx) ersetzt Links der Notiz-Vorschau: neuer Tab, `rel="noopener noreferrer"`; `#...` bleibt im selben Fenster.
- Eingehaengt ueber `previewOptions.components.a` in note-widget.tsx.
- Desktop-App: Opener-Link-Skript faengt `target="_blank"` auf `document` ab und oeffnet im System-Browser.
- Nachweis: Vitest note-* 24/24 gruen (neuer Test mit echtem MDEditor.Markdown + rehypeSanitize), tsc sauber; Biome-Befunde der drei Dateien unveraendert gegenueber HEAD (5, vorbestehend).
- Nicht im Browser geprueft (lokaler Web-Container nicht neu gebaut).
+4
View File
@@ -15,6 +15,10 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
- Handelsware: Eine Excel-Liste mit Handelswaren-Umsätzen hochladen. Tessera ordnet jedem Produkt sein Konto zu, markiert neue Produkte mit „neu“ und vergibt ihnen das nächste freie Gegenkonto. Das Buchungsdatum wird aus dem Dateinamen abgeleitet (letzter Tag des Monats) und lässt sich ändern. Neue Konten werden erst beim Herunterladen der Buchungsdatei gespeichert. Im Reiter „Konten“ pflegen Sie die Kontenliste, lesen sie aus einer CSV-Datei ein (ersetzt alle vorhandenen Konten, nach Rückfrage) und exportieren sie als CSV. Standard-Erlöskonto und Startwert für das Gegenkonto trägt ein Administrator einmalig ein. - Handelsware: Eine Excel-Liste mit Handelswaren-Umsätzen hochladen. Tessera ordnet jedem Produkt sein Konto zu, markiert neue Produkte mit „neu“ und vergibt ihnen das nächste freie Gegenkonto. Das Buchungsdatum wird aus dem Dateinamen abgeleitet (letzter Tag des Monats) und lässt sich ändern. Neue Konten werden erst beim Herunterladen der Buchungsdatei gespeichert. Im Reiter „Konten“ pflegen Sie die Kontenliste, lesen sie aus einer CSV-Datei ein (ersetzt alle vorhandenen Konten, nach Rückfrage) und exportieren sie als CSV. Standard-Erlöskonto und Startwert für das Gegenkonto trägt ein Administrator einmalig ein.
- Modul-Freigaben haben jetzt zwei Stufen: „Benutzen“ (wie bisher) und „Verwalten“. Wer ein Modul verwalten darf, ändert dessen Einstellungen selbst, ohne Administrator zu sein, zum Beispiel in der Kantinenabrechnung, bei Handelsware und bei den Proxmox-Servern. Ein Administrator wählt die Stufe je Gruppe in der Freigaben-Matrix oder je Benutzer in den Benutzerdetails; bestehende Freigaben bleiben „Benutzen“. Freigaben vergeben und Module aktivieren dürfen weiterhin nur Administratoren. Das Modul DKV-Rechnung steht Administratoren und Benutzern mit „Verwalten“ zur Verfügung. - Modul-Freigaben haben jetzt zwei Stufen: „Benutzen“ (wie bisher) und „Verwalten“. Wer ein Modul verwalten darf, ändert dessen Einstellungen selbst, ohne Administrator zu sein, zum Beispiel in der Kantinenabrechnung, bei Handelsware und bei den Proxmox-Servern. Ein Administrator wählt die Stufe je Gruppe in der Freigaben-Matrix oder je Benutzer in den Benutzerdetails; bestehende Freigaben bleiben „Benutzen“. Freigaben vergeben und Module aktivieren dürfen weiterhin nur Administratoren. Das Modul DKV-Rechnung steht Administratoren und Benutzern mit „Verwalten“ zur Verfügung.
### Behoben
- Links in Notiz-Kacheln öffnen jetzt immer in einem neuen Tab, statt das Dashboard im selben Fenster zu verlassen. In der Desktop-App öffnen sie wie gewohnt im Browser.
## 1.9.2 – 2026-10-02 ## 1.9.2 – 2026-10-02
### Neu ### Neu
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
import { describe, expect, it, vi } from 'vitest'; import { describe, expect, it, vi } from 'vitest';
import MDEditor from '@uiw/react-md-editor'; import MDEditor from '@uiw/react-md-editor';
import rehypeSanitize from 'rehype-sanitize'; import rehypeSanitize from 'rehype-sanitize';
import { NoteCheckbox, isTaskLine, toggleTaskLine } from './note-task-list'; import { NoteCheckbox, NoteLink, isTaskLine, toggleTaskLine } from './note-task-list';
describe('note-task-list', () => { describe('note-task-list', () => {
describe('isTaskLine', () => { describe('isTaskLine', () => {
@@ -143,5 +143,30 @@ describe('note-task-list', () => {
expect((boxes[2] as HTMLInputElement).checked).toBe(true); expect((boxes[2] as HTMLInputElement).checked).toBe(true);
expect((boxes[3] as HTMLInputElement).checked).toBe(false); expect((boxes[3] as HTMLInputElement).checked).toBe(false);
}); });
it('oeffnet Links in einem neuen Tab, Sprungmarken im selben Fenster', () => {
const SRC =
'[Doku](https://example.com/doku) und https://example.org\n\n[oben](#oben)';
const { container } = render(
<MDEditor.Markdown
source={SRC}
rehypePlugins={[[rehypeSanitize]]}
components={{ a: NoteLink }}
/>,
);
const doku = container.querySelector('a[href="https://example.com/doku"]');
const auto = container.querySelector('a[href="https://example.org"]');
const hash = container.querySelector('a[href="#oben"]');
for (const link of [doku, auto]) {
expect(link?.getAttribute('target')).toBe('_blank');
expect(link?.getAttribute('rel')).toBe('noopener noreferrer');
expect(link?.hasAttribute('node')).toBe(false);
}
expect(hash).not.toBeNull();
expect(hash?.hasAttribute('target')).toBe(false);
});
}); });
}); });
@@ -115,3 +115,31 @@ export function NoteCheckbox({
/> />
); );
} }
/**
* Ersetzt das von react-markdown gerenderte `<a>`: Links aus Notizen oeffnen
* immer in einem neuen Tab, damit das Dashboard nicht verlassen wird.
* Sprungmarken innerhalb der Notiz (`#...`) bleiben im selben Fenster.
* `node` wird wie bei NoteCheckbox nicht ins DOM durchgereicht. In der
* Desktop-App faengt das Opener-Link-Skript `target="_blank"` ab und
* oeffnet den Link im System-Browser.
*/
export function NoteLink({
node: _node,
href,
children,
...rest
}: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) {
if (href?.startsWith('#')) {
return (
<a href={href} {...rest}>
{children}
</a>
);
}
return (
<a {...rest} href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
);
}
@@ -8,7 +8,7 @@ import MDEditor, { commands } from '@uiw/react-md-editor';
import rehypeSanitize from 'rehype-sanitize'; import rehypeSanitize from 'rehype-sanitize';
import type { WidgetProps } from '../widget-registry'; import type { WidgetProps } from '../widget-registry';
import { updateWidgetConfig } from '@/lib/dashboard-api'; import { updateWidgetConfig } from '@/lib/dashboard-api';
import { NoteCheckbox, toggleTaskLine } from './note-task-list'; import { NoteCheckbox, NoteLink, toggleTaskLine } from './note-task-list';
const DEBOUNCE_MS = 1500; const DEBOUNCE_MS = 1500;
@@ -156,6 +156,7 @@ export function NoteWidget({ instanceId, config }: WidgetProps) {
onToggle={(element) => handleCheckboxToggleRef.current(element)} onToggle={(element) => handleCheckboxToggleRef.current(element)}
/> />
), ),
a: NoteLink,
}, },
}), }),
[], [],