feat: Label-Umbruch bei 32 statt 40 Zeichen (D64-Nachtrag 3)
Lange Titel zogen das Diagramm zu breit. Ein Parameter in wrapLabel(), der 32ch-Rückhalt im CSS und SPEC §9 ziehen mit; Balance-Regel und Erste-Zeile-Verankerung unverändert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
07e4116bd9
commit
ebc5be407e
@@ -19,6 +19,7 @@ reverse.
|
|||||||
|
|
||||||
## 2026-08-24
|
## 2026-08-24
|
||||||
|
|
||||||
|
- Node titles wrap at thirty-two characters now instead of forty — long titles pulled the diagram wide
|
||||||
- Fix: in the vertical layout, the line from a parent to one or two children was torn — the collector rail now always reaches the parent's stub
|
- Fix: in the vertical layout, the line from a parent to one or two children was torn — the collector rail now always reaches the parent's stub
|
||||||
- The legend shows the high-risk status `[!]` and mentions the size check
|
- The legend shows the high-risk status `[!]` and mentions the size check
|
||||||
- `llms.md` tabulates the size ranges and aligns its table columns
|
- `llms.md` tabulates the size ranges and aligns its table columns
|
||||||
|
|||||||
@@ -4916,3 +4916,15 @@ Stub oben an (kein Zentrieren). Beides nachgemessen (Scanner: 0 Befunde in
|
|||||||
ist zoom-invariant, wie `--stem-x`). 405 Tests grün — die Regeln sind
|
ist zoom-invariant, wie `--stem-x`). 405 Tests grün — die Regeln sind
|
||||||
DOM-Geometrie und damit Browser-geprüft, nicht unit-testbar (dieselbe Grenze
|
DOM-Geometrie und damit Browser-geprüft, nicht unit-testbar (dieselbe Grenze
|
||||||
wie `alignStems()`, D29).
|
wie `alignStems()`, D29).
|
||||||
|
|
||||||
|
**Nachtrag 3 zu D64 — die Umbruchgrenze sinkt von 40 auf 32 Zeichen.**
|
||||||
|
Nutzerwunsch nach dem Leben mit der 40er-Grenze: „einige Titel ziehen das
|
||||||
|
Diagramm doch arg in die Breite". Die 40 waren eine gesetzte Zahl, keine
|
||||||
|
hergeleitete — 32 ist es ebenso, nur mit Erfahrung dahinter. Die Änderung ist
|
||||||
|
genau der eine Parameter, für den `wrapLabel()` gebaut wurde: Default in
|
||||||
|
render.js, der `max-width`-Rückhalt im CSS (40ch → 32ch) und die
|
||||||
|
SPEC-§9-Zahl ziehen mit; Balance-Regel, Erste-Zeile-Verankerung und Export
|
||||||
|
bleiben unberührt. Sichtbare Folge: mehr Knoten brechen um (rund 100 Zeichen
|
||||||
|
ergeben jetzt vier statt drei Zeilen), dafür wird der breiteste Fächer
|
||||||
|
schmaler. Testerwartungen mit dem echten Algorithmus nachgerechnet statt
|
||||||
|
geschätzt.
|
||||||
|
|||||||
+1
-1
@@ -499,7 +499,7 @@ Siehe D29.
|
|||||||
deterministisch auf Knotenmitte liegen (Offset 23 px = 5 px Listenabstand
|
deterministisch auf Knotenmitte liegen (Offset 23 px = 5 px Listenabstand
|
||||||
+ halbe Knotenhöhe). Abzweige zielen auf den **Knoten**, nie auf die Mitte
|
+ halbe Knotenhöhe). Abzweige zielen auf den **Knoten**, nie auf die Mitte
|
||||||
des Teilbaums.
|
des Teilbaums.
|
||||||
- **Lange Labels brechen um** (D64): höchstens ~40 Zeichen je Zeile, die
|
- **Lange Labels brechen um** (D64): höchstens ~32 Zeichen je Zeile, die
|
||||||
Zeichen **gleichmäßig** auf die Zeilen verteilt (nicht gierig — sonst stünde
|
Zeichen **gleichmäßig** auf die Zeilen verteilt (nicht gierig — sonst stünde
|
||||||
unter einer vollen Zeile ein einsames Wort). Die Umbrüche setzt der
|
unter einer vollen Zeile ein einsames Wort). Die Umbrüche setzt der
|
||||||
**Renderer**, deterministisch und nur an Leerzeichen; der Kasten schrumpft
|
**Renderer**, deterministisch und nur an Leerzeichen; der Kasten schrumpft
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ function attr(s){ return esc(String(s)).replace(/"/g,'"'); }
|
|||||||
Beschreibung als data-Attribut wäre der einzige andere Weg gewesen. */
|
Beschreibung als data-Attribut wäre der einzige andere Weg gewesen. */
|
||||||
export const TIP_RULE = '─'.repeat(24);
|
export const TIP_RULE = '─'.repeat(24);
|
||||||
|
|
||||||
/* Lange Labels umbrechen (SPEC §9/D64): höchstens ~40 Zeichen je Zeile, und
|
/* Lange Labels umbrechen (SPEC §9/D64): höchstens ~32 Zeichen je Zeile (D64,
|
||||||
|
verengt per D64-Nachtrag 3 — 40 zog das Diagramm zu breit), und
|
||||||
die Zeichen GLEICHMÄSSIG auf die Zeilen verteilt — der gierige CSS-Umbruch
|
die Zeichen GLEICHMÄSSIG auf die Zeilen verteilt — der gierige CSS-Umbruch
|
||||||
machte aus 44 Zeichen eine volle Zeile plus ein einsames Wort. Bewusst im
|
machte aus 44 Zeichen eine volle Zeile plus ein einsames Wort. Bewusst im
|
||||||
Renderer statt per `text-wrap:balance`: So schrumpft der Knotenkasten auf
|
Renderer statt per `text-wrap:balance`: So schrumpft der Knotenkasten auf
|
||||||
@@ -64,7 +65,7 @@ export const TIP_RULE = '─'.repeat(24);
|
|||||||
Regel ist headless testbar. Gebrochen wird nur an Leerzeichen; ein
|
Regel ist headless testbar. Gebrochen wird nur an Leerzeichen; ein
|
||||||
einzelnes Wort über der Grenze bleibt stehen (das CSS fängt es mit
|
einzelnes Wort über der Grenze bleibt stehen (das CSS fängt es mit
|
||||||
`max-width` + `overflow-wrap` ab). */
|
`max-width` + `overflow-wrap` ab). */
|
||||||
export function wrapLabel(label, max = 40){
|
export function wrapLabel(label, max = 32){
|
||||||
const text = String(label);
|
const text = String(label);
|
||||||
if(text.length <= max) return [text];
|
if(text.length <= max) return [text];
|
||||||
const words = text.split(' ');
|
const words = text.split(' ');
|
||||||
|
|||||||
@@ -693,7 +693,7 @@
|
|||||||
bleibt — der 23-px-Abzweig (§9) trifft bei mehrzeiligen Knoten
|
bleibt — der 23-px-Abzweig (§9) trifft bei mehrzeiligen Knoten
|
||||||
deterministisch die Mitte der ERSTEN Zeile. */
|
deterministisch die Mitte der ERSTEN Zeile. */
|
||||||
white-space:pre-line;
|
white-space:pre-line;
|
||||||
max-width:40ch;
|
max-width:32ch;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
overflow-wrap:break-word;
|
overflow-wrap:break-word;
|
||||||
box-shadow:0 1px 2px rgba(36,52,71,.08);
|
box-shadow:0 1px 2px rgba(36,52,71,.08);
|
||||||
|
|||||||
+12
-11
@@ -1,4 +1,5 @@
|
|||||||
// Umbruch langer Labels (SPEC §9/D64): höchstens ~40 Zeichen je Zeile und
|
// Umbruch langer Labels (SPEC §9/D64, verengt auf 32 per Nachtrag 3):
|
||||||
|
// höchstens ~32 Zeichen je Zeile und
|
||||||
// gleichmäßig verteilt — nicht gierig (volle Zeile plus einsames Wort).
|
// gleichmäßig verteilt — nicht gierig (volle Zeile plus einsames Wort).
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { wrapLabel, renderTreeHtml } from '../src/render.js';
|
import { wrapLabel, renderTreeHtml } from '../src/render.js';
|
||||||
@@ -7,13 +8,13 @@ import { parse } from '../src/parser.js';
|
|||||||
const t = key => key;
|
const t = key => key;
|
||||||
|
|
||||||
describe('wrapLabel: balanciert statt gierig', () => {
|
describe('wrapLabel: balanciert statt gierig', () => {
|
||||||
it('bis 40 Zeichen bleibt es eine Zeile, unverändert', () => {
|
it('bis 32 Zeichen bleibt es eine Zeile, unverändert', () => {
|
||||||
const s = 'Ein gewöhnlicher Knotentitel (39 Zeich.)';
|
const s = 'Ein gewöhnlicher Knotentitel, 32';
|
||||||
expect(s.length).toBe(40);
|
expect(s.length).toBe(32);
|
||||||
expect(wrapLabel(s)).toEqual([s]);
|
expect(wrapLabel(s)).toEqual([s]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('44 Zeichen werden zwei etwa gleiche Zeilen — nicht 40 plus Rest', () => {
|
it('44 Zeichen werden zwei etwa gleiche Zeilen — nicht 32 plus Rest', () => {
|
||||||
const s = 'Update the agents version of the spec please';
|
const s = 'Update the agents version of the spec please';
|
||||||
expect(s.length).toBe(44);
|
expect(s.length).toBe(44);
|
||||||
const lines = wrapLabel(s);
|
const lines = wrapLabel(s);
|
||||||
@@ -29,19 +30,19 @@ describe('wrapLabel: balanciert statt gierig', () => {
|
|||||||
expect(wrapLabel(s).join(' ')).toBe(s);
|
expect(wrapLabel(s).join(' ')).toBe(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keine Zeile über 40 Zeichen, solange die Wörter es hergeben', () => {
|
it('keine Zeile über 32 Zeichen, solange die Wörter es hergeben', () => {
|
||||||
const s = 'A dependency needs its target id and nobody remembers a hundred and eighty of them by heart';
|
const s = 'A dependency needs its target id and nobody remembers a hundred and eighty of them by heart';
|
||||||
for(const l of wrapLabel(s)) expect(l.length).toBeLessThanOrEqual(40);
|
for(const l of wrapLabel(s)) expect(l.length).toBeLessThanOrEqual(32);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rund 100 Zeichen ergeben drei gleichmäßige Zeilen', () => {
|
it('rund 100 Zeichen ergeben vier gleichmäßige Zeilen', () => {
|
||||||
const s = 'The list at the caret offers the document ids filtered by what is typed with the node title as ctx';
|
const s = 'The list at the caret offers the document ids filtered by what is typed with the node title as ctx';
|
||||||
expect(s.length).toBe(98);
|
expect(s.length).toBe(98);
|
||||||
const lines = wrapLabel(s);
|
const lines = wrapLabel(s);
|
||||||
expect(lines.length).toBe(3);
|
expect(lines.length).toBe(4);
|
||||||
for(const l of lines){
|
for(const l of lines){
|
||||||
expect(l.length).toBeGreaterThanOrEqual(25);
|
expect(l.length).toBeGreaterThanOrEqual(17);
|
||||||
expect(l.length).toBeLessThanOrEqual(40);
|
expect(l.length).toBeLessThanOrEqual(32);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user