change: Textfeld bricht Zeilen nicht mehr um, waagerechter Balken (D49)
Gewünscht — und damit die ausdrückliche Umkehrung der D33-Abwägung („Verworfen: wrap=off … der Umbruch ist die wichtigere Eigenschaft"). Der dort notierte Preis bleibt richtig; nicht bedacht war die andere Seite: Ein weicher Umbruch zerstört die Einrückung, und die trägt in dieser Notation die Hierarchie. Die Fortsetzung einer umbrochenen Zeile beginnt am linken Rand und sieht aus wie ein Wurzelknoten — betroffen sind genau die langen Zeilen, also die mit ID, Größe und Kommentar, seit D48 praktisch jede Zeile des Plans. Umgesetzt als `wrap="off"` am Element (Eigenschaft des Feldes, nicht seiner Gestaltung), `overflow:auto` daneben im Stylesheet. Der Spiegel musste mit: `syncMirror()` maß mit `white-space:pre-wrap` und fester Breite — genau die Kombination, die im Spiegel Zeilen umbräche, die im Textfeld ungebrochen stehen; alles darunter läge zu tief und die Zeilennummern wanderten weg. Jetzt `pre` und ohne Breitenvorgabe. Gemessen wird trotzdem weiter statt `Zeilenhöhe × n` zu rechnen: Die Schriftgröße unterscheidet sich zwischen Telefon und Schreibtisch, die Messung stimmt in beiden Fällen von selbst. Der Sprung setzt `scrollLeft` zurück. `jumpToLine()` markiert die ganze Zeile, woraufhin der Browser von sich aus an deren Ende scrollt — man landete am rechten Rand, ohne Einrückung, Zeichen und Statusbox zu sehen. Der Sprung zeigt auf eine Zeile, nicht auf ihr Ende. Nachgemessen am Plan (903 Zeilen, längste 122 Zeichen): 903 Bildzeilen zu 903 logischen Zeilen in beiden Ansichten; Schiebeweg 211 px am Schreibtisch, 432 px auf 375 px Breite; der Zeilennummern-Streifen bleibt beim waagerechten Verschieben stehen (linke Kante unverändert bei 180 px Versatz); Abweichung der Zahlen am Dateiende 2 px auf 903 Zeilen (Rundung von `offsetTop`, sammelt sich nicht auf). 236 Tests grün. SPEC §9 (Zeilennummern) und D49 halten es fest; die D33-Stelle bekommt einen Verweis, dass die Abwägung umgekehrt wurde — gelöscht wird dort nichts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
623b68799f
commit
1b8bf06973
@@ -1829,6 +1829,10 @@ waagerechtes Scrollen — im schmalen Spiegel neben einem eingebetteten Pad (D31
|
|||||||
oder auf dem Telefon (D17) wäre der Text damit unlesbar. Der Umbruch ist die
|
oder auf dem Telefon (D17) wäre der Text damit unlesbar. Der Umbruch ist die
|
||||||
wichtigere Eigenschaft.
|
wichtigere Eigenschaft.
|
||||||
|
|
||||||
|
*(Diese Abwägung ist später umgekehrt worden: **D49** schaltet den Umbruch ab.
|
||||||
|
Übersehen war, was ein weicher Umbruch mit der **Einrückung** macht — und die
|
||||||
|
trägt hier die Hierarchie.)*
|
||||||
|
|
||||||
**Kein Umschalter.** Die Kopfzeile ist eng (D17), und ein Bedienelement kostet
|
**Kein Umschalter.** Die Kopfzeile ist eng (D17), und ein Bedienelement kostet
|
||||||
i18n in neun Sprachen für etwas, das keinen Zustand hat, den jemand pflegen
|
i18n in neun Sprachen für etwas, das keinen Zustand hat, den jemand pflegen
|
||||||
will. Der Streifen ist so schmal wie die Ziffern es verlangen
|
will. Der Streifen ist so schmal wie die Ziffern es verlangen
|
||||||
@@ -3117,3 +3121,66 @@ Beschreibung, **0 Warnungen**, Pfad weiterhin exakt gerechnet mit 24
|
|||||||
Stationen. Der Cursor in einem Beschreibungsblock wählt den beschriebenen
|
Stationen. Der Cursor in einem Beschreibungsblock wählt den beschriebenen
|
||||||
Knoten aus (D40-Nachtrag 2) — auf Kopf- wie Textzeile geprüft; der
|
Knoten aus (D40-Nachtrag 2) — auf Kopf- wie Textzeile geprüft; der
|
||||||
`---`-Trenner selbst wählt nichts.
|
`---`-Trenner selbst wählt nichts.
|
||||||
|
|
||||||
|
## D49 — Das Textfeld bricht nicht mehr um: `wrap="off"` plus waagerechter Balken
|
||||||
|
Gewünscht vom Nutzer, und damit die ausdrückliche Umkehrung der D33-Abwägung
|
||||||
|
(„Verworfen: `wrap=off` … der Umbruch ist die wichtigere Eigenschaft"). Der
|
||||||
|
dort notierte Preis bleibt richtig und wird in Kauf genommen; was dort **nicht**
|
||||||
|
bedacht war, ist die andere Seite:
|
||||||
|
|
||||||
|
**Ein weicher Umbruch zerstört die Einrückung — und die trägt hier die
|
||||||
|
Hierarchie.** In dieser Notation ist der linke Rand keine Formsache, sondern
|
||||||
|
die Ebene (§2). Bricht eine Zeile um, beginnt ihre Fortsetzung am linken Rand
|
||||||
|
und sieht damit aus wie ein Wurzelknoten; wer die Struktur überfliegt, liest
|
||||||
|
eine Ebene, die es nicht gibt. Das trifft genau die langen Zeilen, also die mit
|
||||||
|
ID, Größe und Kommentar — und seit D48 ist das praktisch jede Zeile des
|
||||||
|
mitgelieferten Plans. Waagerechtes Scrollen kostet Bequemlichkeit; der Umbruch
|
||||||
|
kostete Lesbarkeit der Struktur.
|
||||||
|
|
||||||
|
**Umgesetzt am Element, nicht in CSS:** `wrap="off"` am `<textarea>`. Es ist
|
||||||
|
die Eigenschaft des Feldes, nicht seiner Gestaltung, und es wirkt zuverlässig
|
||||||
|
in allen Engines; `overflow:auto` steht daneben im Stylesheet.
|
||||||
|
|
||||||
|
**Der Spiegel musste mit.** `syncMirror()` (D25/D33) maß mit `white-space:
|
||||||
|
pre-wrap` und **fester Breite** — genau die Kombination, die im Spiegel Zeilen
|
||||||
|
umbräche, die im Textfeld ungebrochen stehen. Jede Zeile darunter läge dann zu
|
||||||
|
tief, und die Zeilennummern wanderten weg. Der Spiegel ist deshalb jetzt `pre`
|
||||||
|
und ohne Breitenvorgabe.
|
||||||
|
|
||||||
|
**Gemessen wird trotzdem weiter.** Ohne Umbruch wäre `Zeilenhöhe × n` richtig
|
||||||
|
und der Spiegel überflüssig — so hat D33 den Fall beschrieben. Behalten wurde
|
||||||
|
die Messung dennoch: Die Schriftgröße unterscheidet sich zwischen Telefon und
|
||||||
|
Schreibtisch (D17-Nachtrag 2), und die Messung stimmt in beiden Fällen von
|
||||||
|
selbst, ohne eine zweite Stelle, die dieselbe Zahl kennen muss.
|
||||||
|
|
||||||
|
**Der Sprung setzt die waagerechte Verschiebung zurück.** `jumpToLine()`
|
||||||
|
markiert die **ganze** Zeile (D25) — der Browser scrollt dann von sich aus an
|
||||||
|
deren **Ende**, und man landete am rechten Rand, ohne Einrückung, Zeichen und
|
||||||
|
Statusbox zu sehen. Also `scrollLeft = 0` nach dem Markieren: Der Sprung zeigt
|
||||||
|
auf eine Zeile, nicht auf ihr Ende.
|
||||||
|
|
||||||
|
**Der Zeilennummern-Streifen bleibt stehen.** Er verschiebt sich weiterhin nur
|
||||||
|
gegen `src.scrollTop`; den waagerechten Balken macht er nicht mit. Das war
|
||||||
|
schon so gebaut und ist jetzt das gewünschte Verhalten statt eines
|
||||||
|
Nebenprodukts — nachgemessen: linke Kante unverändert, während der Text um
|
||||||
|
180 px verschoben ist.
|
||||||
|
|
||||||
|
**Nachgemessen** am mitgelieferten Plan (903 Zeilen, längste 122 Zeichen):
|
||||||
|
|
||||||
|
| | Schreibtisch | Telefon (375 px) |
|
||||||
|
|---|---|---|
|
||||||
|
| Bildzeilen zu logischen Zeilen | 903 : 903 | 903 : 903 |
|
||||||
|
| waagerechter Schiebeweg | 211 px | 432 px |
|
||||||
|
| sichtbare Zeichen | rund 110 | rund 53 |
|
||||||
|
| Abweichung der Zahlen am Dateiende | — | 2 px auf 903 Zeilen |
|
||||||
|
|
||||||
|
Die zwei Pixel stammen daher, dass `offsetTop` ganzzahlig rundet, die
|
||||||
|
Zeilenhöhe aber 17,408 px beträgt; sie sammeln sich nicht auf (jede Zahl wird
|
||||||
|
absolut gemessen, nicht fortgeschrieben).
|
||||||
|
|
||||||
|
**Der D33-Einwand bleibt bestehen, nur nicht mehr entscheidend:** Im schmalen
|
||||||
|
Spiegel neben einem eingebetteten Pad (D31) und auf dem Telefon sieht man jetzt
|
||||||
|
rund 53 Zeichen und muss schieben. Wer dort viel liest, zieht den Splitter auf
|
||||||
|
oder wechselt in die Textansicht. Ein Umschalter dafür wurde nicht gebaut — aus
|
||||||
|
demselben Grund wie in D33: ein Bedienelement plus neun Übersetzungen für einen
|
||||||
|
Zustand, den kaum jemand umstellen will.
|
||||||
|
|||||||
+9
-3
@@ -677,14 +677,20 @@ Links neben dem Textfeld steht ein schmaler Streifen mit den **Zeilennummern**
|
|||||||
dieselben, die die Warnungen nennen („Zeile 12: …", §4). Ohne ihn muss man sie
|
dieselben, die die Warnungen nennen („Zeile 12: …", §4). Ohne ihn muss man sie
|
||||||
im Text abzählen.
|
im Text abzählen.
|
||||||
|
|
||||||
- Gezählt werden **logische** Zeilen des Notationstexts. Eine weich umbrochene
|
- **Das Textfeld bricht Zeilen nicht um** (D49). Die Einrückung trägt die
|
||||||
Zeile behält **eine** Nummer; die Nummer steht an ihrer ersten Bildzeile.
|
Hierarchie (§2), und ein weicher Umbruch setzt die Fortsetzung an den linken
|
||||||
|
Rand — die Ebene wäre damit gerade dort nicht mehr abzulesen, wo eine Zeile
|
||||||
|
lang ist. Zu lange Zeilen bekommen einen **waagerechten Balken**; eine
|
||||||
|
logische Zeile ist damit immer genau eine Bildzeile.
|
||||||
|
- Der Streifen scrollt **nur senkrecht** mit: Die Zahlen bleiben stehen, wenn
|
||||||
|
der Text waagerecht verschoben wird. Ein Sprung auf eine Zeile (§9) setzt die
|
||||||
|
waagerechte Verschiebung zurück — er zeigt auf eine Zeile, nicht auf ihr Ende.
|
||||||
- Die **Cursor-Zeile** und Zeilen mit einer **Warnung** heben sich ab (Warnung in
|
- Die **Cursor-Zeile** und Zeilen mit einer **Warnung** heben sich ab (Warnung in
|
||||||
`--warn`, `#B45309`) — genau die beiden Fälle, für die man hinsieht.
|
`--warn`, `#B45309`) — genau die beiden Fälle, für die man hinsieht.
|
||||||
- Reine Lesehilfe: kein Bestandteil des Notationstexts, nicht im Grafikexport
|
- Reine Lesehilfe: kein Bestandteil des Notationstexts, nicht im Grafikexport
|
||||||
und nicht im Druck (dort ist ohnehin nur das Diagramm zu sehen).
|
und nicht im Druck (dort ist ohnehin nur das Diagramm zu sehen).
|
||||||
|
|
||||||
Siehe D33.
|
Siehe D33, D49.
|
||||||
|
|
||||||
### Fokusmarke im Diagramm (`!!!`, §1)
|
### Fokusmarke im Diagramm (`!!!`, §1)
|
||||||
Ein mit `!!!` markierter Knoten trägt einen **Strahlenkranz in Petrol** —
|
Ein mit `!!!` markierter Knoten trägt einen **Strahlenkranz in Petrol** —
|
||||||
|
|||||||
+5
-1
@@ -98,7 +98,11 @@
|
|||||||
Rahmen gegen diese Spalte, nicht gegen das nackte Textfeld. -->
|
Rahmen gegen diese Spalte, nicht gegen das nackte Textfeld. -->
|
||||||
<div class="src-wrap" id="srcWrap">
|
<div class="src-wrap" id="srcWrap">
|
||||||
<div class="lineno" id="lineNos" aria-hidden="true"><div class="lineno-inner"></div></div>
|
<div class="lineno" id="lineNos" aria-hidden="true"><div class="lineno-inner"></div></div>
|
||||||
<textarea id="src" spellcheck="false" data-i18n-aria="editorTitle" aria-label="Struktur (Text)"></textarea>
|
<!-- `wrap="off"`: Zeilen werden nicht umbrochen, lange Zeilen bekommen
|
||||||
|
einen waagerechten Balken (D49). Die Einrückung trägt hier die
|
||||||
|
Hierarchie — ein weicher Umbruch setzt die Fortsetzung an den linken
|
||||||
|
Rand und lässt die Ebene verschwinden. -->
|
||||||
|
<textarea id="src" wrap="off" spellcheck="false" data-i18n-aria="editorTitle" aria-label="Struktur (Text)"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hint-gutter" id="hintGutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="hintGutterAria" title="Ziehen zum Verschieben, Doppelklick setzt zurück" aria-label="Editor und Legende größenverändern" hidden></div>
|
<div class="hint-gutter" id="hintGutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="hintGutterAria" title="Ziehen zum Verschieben, Doppelklick setzt zurück" aria-label="Editor und Legende größenverändern" hidden></div>
|
||||||
|
|||||||
+21
-14
@@ -701,22 +701,23 @@ function lineRange(line){
|
|||||||
return {start, end: start + lines[line - 1].length};
|
return {start, end: start + lines[line - 1].length};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vertikale Position eines Zeichenoffsets im Textfeld. Zeilenhöhe × n scheitert
|
/* Vertikale Position eines Zeichenoffsets im Textfeld: ein unsichtbarer Spiegel
|
||||||
an weichen Umbrüchen (lange Zeilen belegen mehrere Bildzeilen), deshalb ein
|
mit gleicher Typografie plus Marker-Span.
|
||||||
unsichtbarer Spiegel mit gleicher Typografie und Breite plus Marker-Span. */
|
|
||||||
|
Seit `wrap="off"` (D49) bricht das Textfeld nicht mehr um — der Spiegel
|
||||||
|
deshalb ebenfalls `white-space:pre` und **ohne** vorgegebene Breite. Beides
|
||||||
|
gehört zusammen: Eine feste Breite bei `pre-wrap` bräche im Spiegel Zeilen um,
|
||||||
|
die im Textfeld ungebrochen stehen, und alles darunter läge zu tief.
|
||||||
|
Gemessen wird trotzdem weiter, statt `Zeilenhöhe × n` zu rechnen — die
|
||||||
|
Schriftgröße unterscheidet sich zwischen Telefon und Schreibtisch (D17), und
|
||||||
|
die Messung stimmt in beiden Fällen von selbst. */
|
||||||
let mirrorEl = null;
|
let mirrorEl = null;
|
||||||
function syncMirror(){
|
function syncMirror(){
|
||||||
if(!mirrorEl){
|
if(!mirrorEl){
|
||||||
mirrorEl = document.createElement('div');
|
mirrorEl = document.createElement('div');
|
||||||
mirrorEl.setAttribute('aria-hidden', 'true');
|
mirrorEl.setAttribute('aria-hidden', 'true');
|
||||||
/* `box-sizing:border-box` ist Pflicht: `src.clientWidth` **enthält** die
|
|
||||||
Innenabstände. Ohne das ist der Spiegel um genau diese 32 px breiter als
|
|
||||||
das Textfeld und bricht später um — lange Zeilen landeten dadurch zu weit
|
|
||||||
oben (fiel beim Bau der Zeilennummern auf, D33; betraf auch schon das
|
|
||||||
Scrollen beim Sprung, D25). */
|
|
||||||
mirrorEl.style.cssText = 'position:absolute;visibility:hidden;top:0;left:-9999px;' +
|
mirrorEl.style.cssText = 'position:absolute;visibility:hidden;top:0;left:-9999px;' +
|
||||||
'box-sizing:border-box;' +
|
'box-sizing:border-box;white-space:pre;';
|
||||||
'white-space:pre-wrap;overflow-wrap:break-word;';
|
|
||||||
document.body.appendChild(mirrorEl);
|
document.body.appendChild(mirrorEl);
|
||||||
}
|
}
|
||||||
const cs = getComputedStyle(src);
|
const cs = getComputedStyle(src);
|
||||||
@@ -724,7 +725,6 @@ function syncMirror(){
|
|||||||
'paddingTop','paddingLeft','paddingRight','borderTopWidth','tabSize']){
|
'paddingTop','paddingLeft','paddingRight','borderTopWidth','tabSize']){
|
||||||
mirrorEl.style[p] = cs[p];
|
mirrorEl.style[p] = cs[p];
|
||||||
}
|
}
|
||||||
mirrorEl.style.width = src.clientWidth + 'px';
|
|
||||||
return mirrorEl;
|
return mirrorEl;
|
||||||
}
|
}
|
||||||
const ZWSP = '';
|
const ZWSP = '';
|
||||||
@@ -804,9 +804,11 @@ function markCurrentLineNo(){
|
|||||||
if(s) s.classList.add('cur');
|
if(s) s.classList.add('cur');
|
||||||
}
|
}
|
||||||
src.addEventListener('scroll', syncLineNoScroll);
|
src.addEventListener('scroll', syncLineNoScroll);
|
||||||
/* Der Umbruch hängt an der Breite: Splitter, Fenster, Drehung, Tastatur. Beim
|
/* Seit D49 hängen die Zeilenhöhen nicht mehr an der Breite (es wird nicht mehr
|
||||||
Ziehen am Splitter kämen sonst je Bild mehrere Messungen — einmal je Bild
|
umbrochen), wohl aber an der Schriftgröße — und die wechselt beim Übergang
|
||||||
genügt. */
|
zwischen Telefon und Schreibtisch (D17), den der Beobachter als
|
||||||
|
Größenänderung mitbekommt. Beim Ziehen am Splitter kämen sonst je Bild
|
||||||
|
mehrere Messungen; einmal je Bild genügt. */
|
||||||
let lineNoPending = false;
|
let lineNoPending = false;
|
||||||
if(window.ResizeObserver) new ResizeObserver(() => {
|
if(window.ResizeObserver) new ResizeObserver(() => {
|
||||||
if(lineNoPending) return;
|
if(lineNoPending) return;
|
||||||
@@ -868,6 +870,11 @@ function jumpToLine(line){
|
|||||||
src.focus({preventScroll: true});
|
src.focus({preventScroll: true});
|
||||||
src.setSelectionRange(r.start, r.end);
|
src.setSelectionRange(r.start, r.end);
|
||||||
scrollEditorToOffset(r.start);
|
scrollEditorToOffset(r.start);
|
||||||
|
/* Ohne Umbruch (D49) scrollt der Browser beim Markieren einer langen Zeile
|
||||||
|
bis an ihr **Ende** — man landete am rechten Rand und sähe den Anfang der
|
||||||
|
Zeile nicht, also gerade Einrückung, Zeichen und Statusbox. Der Sprung
|
||||||
|
zeigt auf eine Zeile, nicht auf ihr Ende. */
|
||||||
|
src.scrollLeft = 0;
|
||||||
caretLine = line;
|
caretLine = line;
|
||||||
highlightCurrentNode(true);
|
highlightCurrentNode(true);
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -467,9 +467,13 @@
|
|||||||
.docdel:hover{border-color:#D897A8;color:#B4415E}
|
.docdel:hover{border-color:#D897A8;color:#B4415E}
|
||||||
.togglebtn[aria-pressed="true"]{background:var(--or);color:#fff;border-color:var(--or)}
|
.togglebtn[aria-pressed="true"]{background:var(--or);color:#fff;border-color:var(--or)}
|
||||||
.togglebtn[aria-pressed="true"]:hover{color:#fff}
|
.togglebtn[aria-pressed="true"]:hover{color:#fff}
|
||||||
|
/* Kein Zeilenumbruch (D49, `wrap="off"` am Element): Die Einrückung trägt die
|
||||||
|
Hierarchie, und ein weicher Umbruch setzt die Fortsetzung an den linken Rand
|
||||||
|
— die Ebene wäre damit gerade dort nicht mehr abzulesen, wo eine Zeile lang
|
||||||
|
ist. Lange Zeilen bekommen stattdessen einen waagerechten Balken. */
|
||||||
textarea{
|
textarea{
|
||||||
display:block;width:100%;min-height:0;
|
display:block;width:100%;min-height:0;
|
||||||
border:0;outline:none;resize:none;
|
border:0;outline:none;resize:none;overflow:auto;
|
||||||
padding:14px 16px;
|
padding:14px 16px;
|
||||||
font-family:'IBM Plex Mono',monospace;
|
font-family:'IBM Plex Mono',monospace;
|
||||||
font-size:.85rem;line-height:1.7;
|
font-size:.85rem;line-height:1.7;
|
||||||
@@ -482,9 +486,13 @@
|
|||||||
ist der Hauptinhalt und kein ausgegrautes Formularfeld. */
|
ist der Hauptinhalt und kein ausgegrautes Formularfeld. */
|
||||||
textarea.readonly{background:#F1F4F7;cursor:default}
|
textarea.readonly{background:#F1F4F7;cursor:default}
|
||||||
/* Zeilennummern (D33). Die Warnungen nennen Zeilennummern — ohne Streifen
|
/* Zeilennummern (D33). Die Warnungen nennen Zeilennummern — ohne Streifen
|
||||||
muss man sie im Textfeld abzählen. Weich umbrochene Zeilen belegen mehrere
|
muss man sie im Textfeld abzählen. Jede Zahl steht auf einer **gemessenen**
|
||||||
Bildzeilen, deshalb steht jede Zahl auf einer **gemessenen** Höhe
|
Höhe (`renderLineNos()` in app.js), nicht auf „Zeilenhöhe × n"; seit D49
|
||||||
(`renderLineNos()` in app.js), nicht auf „Zeilenhöhe × n". Typografie muss
|
bricht nichts mehr um, aber die Schriftgröße unterscheidet sich zwischen
|
||||||
|
Telefon und Schreibtisch, und die Messung stimmt in beiden Fällen von
|
||||||
|
selbst. Der Streifen scrollt **nur senkrecht** mit: Er verschiebt sich
|
||||||
|
gegen `src.scrollTop`, den waagerechten Balken des Textfelds macht er
|
||||||
|
nicht mit — die Zahlen bleiben stehen, wo sie hingehören. Typografie muss
|
||||||
mit dem Textfeld übereinstimmen, sonst wandern die Zahlen weg. */
|
mit dem Textfeld übereinstimmen, sonst wandern die Zahlen weg. */
|
||||||
.lineno{
|
.lineno{
|
||||||
position:relative;overflow:hidden;flex:0 0 auto;
|
position:relative;overflow:hidden;flex:0 0 auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user