feat(editor): Warn-Zeilennummer trägt ihre Meldung als Tooltip

Der Streifen sagte bisher nur, DASS eine Zeile eine Warnung hat; welche,
stand allein unter dem Diagramm — man sah die orange Zahl und suchte dann
in der Liste die passende Zeilennummer.

- warnings.js: `warningText()` neben `formatWarning()`, beide aus demselben
  `switch` (`build(w, t, esc)`) — der Warnungsbereich ist HTML, ein `title`
  ist Klartext. Ohne die Trennung stünde dort „Drag & Drop“.
- app.js: `lineNoWarn` ist jetzt Map Zeile -> Text; mehrere Warnungen einer
  Zeile stehen im Tooltip untereinander. Titel verschwinden mit der Warnung.
- style.css: Der Kasten der Warn-Zahl reicht über die ganze Streifenbreite
  (Ziffern bleiben punktgenau stehen, nachgemessen 21,14 px), `cursor:help`.

Nachgemessen (4 Warnungen in 3 Zeilen): Tooltips wortgleich mit dem
Warnungsbereich, `&`/`"` unverfälscht, keine Zahl ohne Warnung mit `title`;
nach dem Beheben verschwinden Klasse und Titel mit. Werkbaum-Plan: 0.

SPEC §9 (Zeilennummern) + D33-Nachtrag; Plan: #ed.lineno.tip [x].
243 Tests grün (3 neue).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-24 10:41:05 +02:00
co-authored by Claude Opus 5
parent 0bead77e7f
commit cb966e4e20
8 changed files with 133 additions and 7 deletions
+22 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { formatWarning } from '../src/warnings.js';
import { formatWarning, warningText } from '../src/warnings.js';
/* Stub bildet key + interpolierte Variablen sichtbar ab, damit wir prüfen
können, welche Werte (inkl. Escaping) formatWarning durchreicht. */
@@ -40,3 +40,24 @@ describe('formatWarning — vereinheitlichtes Warnungs-Modell', () => {
expect(formatWarning({type: 'x'}, t)).toBe('x (?)');
});
});
/* Der `title` der Zeilennummer ist kein HTML — dort stünde sonst wörtlich
„Drag &amp; Drop“ (D33-Nachtrag). Dieselben Typen, dieselbe Vorlage, nur
ohne Escaping. */
describe('warningText — dieselbe Meldung als Klartext', () => {
it('lässt das Label unangetastet', () => {
expect(warningText({type: 'mixedGate', line: 7, label: 'A & <B>'}, t))
.toBe('mixedWarn|{"line":7,"label":"A & <B>"}');
});
it('lässt die URL unangetastet', () => {
expect(warningText({type: 'sourceLoad', url: 'https://a/?x=1&y=2', error: 'HTTP 404'}, t))
.toBe('sourceLoadWarn|{"url":"https://a/?x=1&y=2","error":"HTTP 404"}');
});
it('deckt dieselben Typen ab wie formatWarning', () => {
expect(warningText({type: 'descStray', line: 4}, t)).toBe('descStrayWarn|{"line":4}');
expect(warningText({type: 'cheapApprox'}, t)).toBe('cheapApproxWarn|undefined');
expect(warningText({type: 'was?', line: 9}, t)).toBe('was? (9)');
});
});