umbau 1.3
This commit is contained in:
@ -13,27 +13,37 @@
|
||||
*
|
||||
* Vollbild: native Browser-Fullscreen-API auf dem Instanz-Wurzelelement
|
||||
* (Konzept 4.3).
|
||||
* Copy & Paste: xterm.js liefert Markieren-zum-Kopieren + Strg+Umschalt+V
|
||||
* bereits nativ. Aufgabe #11 (vom Nutzer nachtraeglich hinzugefuegt, siehe
|
||||
* FORTSETZUNG_Aufgabe11.md) ergaenzt zwei konkret gewuenschte Wege:
|
||||
* `copyOnSelect: true` versucht, eine Markierung sofort in die System-
|
||||
* Zwischenablage zu kopieren (kein Strg+C/Cmd+C mehr noetig -- Strg+C bleibt
|
||||
* ohne Markierung weiterhin SIGINT, xterm.js' Standardverhalten). Das ist
|
||||
* aber eine Automatik OHNE direkte Benutzergeste (sie laeuft aus xterm.js'
|
||||
* eigenem mouseup-Handler heraus) und schlaegt daher in der Praxis manchmal
|
||||
* fehl, ohne dass die App das mitbekommt (Live-Test 2.9.: Markierung+Ziehen
|
||||
* hat sichtbar funktioniert, in der Zwischenablage kam aber nichts an).
|
||||
* Deshalb zusaetzlich ein Rechtsklick-Kontextmenue, das PuTTYs bekannte
|
||||
* Konvention nachbildet: Rechtsklick MIT aktiver Markierung kopiert (direkter
|
||||
* Versuch ueber die Clipboard-API, da ein Rechtsklick selbst eine
|
||||
* "transiente Aktivierung"/Benutzergeste ist -- das ist deutlich
|
||||
* zuverlaessiger als copyOnSelects impliziter Aufruf); schlaegt das fehl
|
||||
* oder ist keine Markierung aktiv, faellt es auf "Einfuegen" zurueck --
|
||||
* ebenfalls zuerst direkt ueber die Clipboard-API versucht, sonst ein
|
||||
* winziges Kontextmenue mit "Kopieren"- bzw. "Einfuegen"-Eintrag, dessen
|
||||
* Klick selbst wieder eine frische Benutzergeste ist (siehe _wireClipboard()
|
||||
* unten). Keine serverseitige Sonderbehandlung noetig, im Gegensatz zu RDP
|
||||
* (siehe rdp.js).
|
||||
* Copy & Paste (Aufgabe #11, KORRIGIERT nach Live-Test 3.9.):
|
||||
*
|
||||
* FEHLERURSACHE der bisherigen Fassung: sie setzte `copyOnSelect: true` in
|
||||
* den Terminal-Optionen. DIESE OPTION EXISTIERT IN XTERM.JS NICHT -- sie ist
|
||||
* eine VS-Code-Einstellung ("terminal.integrated.copyOnSelection"). xterm.js'
|
||||
* OptionsService uebernimmt aus dem Konstruktor-Objekt ausschliesslich
|
||||
* Schluessel, die in seinen Default-Optionen vorkommen, und verwirft alle
|
||||
* uebrigen KOMMENTARLOS -- kein Fehler, keine Warnung, keine Wirkung.
|
||||
* Gegenprobe: `grep -c copyOnSelect static/js/vendor/xterm.js` liefert 0.
|
||||
* "Markieren kopiert" hat damit nie funktioniert; das im Live-Test am 2.9.
|
||||
* beobachtete "Markierung sichtbar, Zwischenablage bleibt leer" war genau
|
||||
* das und NICHT das dort vermutete Benutzergesten-Problem.
|
||||
*
|
||||
* Jetzt drei Wege, keiner davon auf eine Berechtigungsabfrage angewiesen:
|
||||
* 1. Markieren kopiert -- selbst implementiert ueber term.onSelectionChange().
|
||||
* 2. Strg+Einfg kopiert, Umschalt+Einfg fuegt ein (PuTTY-Konvention, per
|
||||
* attachCustomKeyEventHandler). BEWUSST NICHT Strg+Umschalt+C/V: die
|
||||
* sind in Chrome/Edge fest fuer die Entwicklertools belegt und erreichen
|
||||
* die Seite gar nicht -- die alten Fehlertexte ("Bitte Strg+Umschalt+C
|
||||
* verwenden") liefen dort also ins Leere. Strg+V und Umschalt+Einfg
|
||||
* erzeugen ein natives 'paste'-Ereignis auf xterm.js' verstecktem
|
||||
* <textarea>, das xterm.js selbst verarbeitet -- ganz ohne Clipboard-API.
|
||||
* 3. Rechtsklick: mit Markierung kopieren, ohne Markierung einfuegen
|
||||
* (Ruecksicherung ueber ein Mini-Kontextmenue, dessen Klick eine frische
|
||||
* Benutzergeste ist).
|
||||
* Das Kopieren faellt bei blockierter Clipboard-API auf
|
||||
* document.execCommand("copy") zurueck (siehe _copyViaExecCommand): veraltet,
|
||||
* aber ohne Berechtigung nutzbar und auch dann funktionsfaehig, wenn der
|
||||
* Browser die Clipboard-API verweigert -- etwa auf einer Seite mit
|
||||
* selbstsigniertem Zertifikat. Keine serverseitige Sonderbehandlung noetig,
|
||||
* im Gegensatz zu RDP (siehe rdp.js).
|
||||
* Markieren mit gedrueckter Umschalttaste: xterm.js reicht Mausklicks an ein
|
||||
* Vollbild-Zielprogramm mit eigener Maussteuerung (tmux, vim, htop, mc, ...)
|
||||
* weiter, sobald dieses per ANSI-Escape-Sequenz Maus-Tracking anfordert --
|
||||
@ -298,11 +308,9 @@
|
||||
fontFamily: "Menlo, Consolas, monospace",
|
||||
fontSize: 14,
|
||||
theme: TERMINAL_THEMES[this._themeName].theme,
|
||||
// Aufgabe #11: eine Markierung wird sofort in die System-
|
||||
// Zwischenablage kopiert -- kein zusaetzliches Strg+C/Cmd+C noetig.
|
||||
// Strg+C OHNE Markierung bleibt unveraendert SIGINT (xterm.js'
|
||||
// Standardverhalten, wird von copyOnSelect nicht beruehrt).
|
||||
copyOnSelect: true,
|
||||
// KEIN copyOnSelect hier -- die Option gibt es in xterm.js nicht
|
||||
// (siehe Kopfkommentar). Kopieren-beim-Markieren macht jetzt
|
||||
// _wireClipboard() selbst ueber term.onSelectionChange().
|
||||
});
|
||||
this.fitAddon = new FitAddon.FitAddon();
|
||||
this.term.loadAddon(this.fitAddon);
|
||||
@ -493,6 +501,37 @@
|
||||
// ist und dadurch bessere Chancen auf die Berechtigung hat).
|
||||
|
||||
_wireClipboard() {
|
||||
// (1) Markieren kopiert -- SELBST implementiert, weil xterm.js keine
|
||||
// copyOnSelect-Option kennt (siehe Kopfkommentar). onSelectionChange
|
||||
// feuert aus xterm.js' mouseup-/Tastatur-Handler heraus, also noch
|
||||
// innerhalb der transienten Benutzeraktivierung des Browsers --
|
||||
// writeText() ist dort erlaubt. Schlaegt es dennoch fehl, greift der
|
||||
// execCommand-Ruecksprung in _copyToClipboard().
|
||||
this.term.onSelectionChange(() => {
|
||||
const text = this.term.getSelection();
|
||||
if (!text) return;
|
||||
this._copyToClipboard(text);
|
||||
});
|
||||
|
||||
// (2) Tastatur nach PuTTY-Konvention. BEWUSST Einfg-Kombinationen und
|
||||
// nicht Strg+Umschalt+C/V -- letztere sind in Chrome/Edge fest fuer
|
||||
// die Entwicklertools reserviert und erreichen die Seite nie.
|
||||
this.term.attachCustomKeyEventHandler((ev) => {
|
||||
if (ev.type !== "keydown") return true;
|
||||
// Strg+Einfg: Markierung kopieren, nicht an die Sitzung schicken.
|
||||
if (ev.ctrlKey && !ev.shiftKey && ev.key === "Insert") {
|
||||
const text = this.term.getSelection();
|
||||
if (text) this._copyToClipboard(text);
|
||||
return false;
|
||||
}
|
||||
// Umschalt+Einfg und Strg+V: NICHT abfangen. Der Browser erzeugt
|
||||
// daraus selbst ein natives 'paste'-Ereignis auf xterm.js'
|
||||
// verstecktem <textarea>, das xterm.js bereits verarbeitet -- ganz
|
||||
// ohne Clipboard-API und damit ohne Berechtigungsabfrage.
|
||||
return true;
|
||||
});
|
||||
|
||||
// (3) Rechtsklick: mit Markierung kopieren, ohne Markierung einfuegen.
|
||||
this.terminalContainer.addEventListener("contextmenu", (ev) => {
|
||||
ev.preventDefault();
|
||||
if (this.term.hasSelection()) {
|
||||
@ -503,17 +542,54 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** Kopiert Text in die Zwischenablage. Zuerst ueber die Clipboard-API,
|
||||
* bei Fehlschlag ueber document.execCommand("copy"). Letzteres gilt als
|
||||
* veraltet, ist aber weiterhin in allen aktuellen Browsern implementiert,
|
||||
* braucht KEINE Berechtigung und funktioniert auch dann, wenn der Browser
|
||||
* die Clipboard-API verweigert (haeufigster Fall in diesem Projekt: eine
|
||||
* Seite mit selbstsigniertem Zertifikat). Liefert true bei Erfolg. */
|
||||
async _copyToClipboard(text) {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
} catch (_) {
|
||||
// Weiter mit dem Ruecksprung unten.
|
||||
}
|
||||
}
|
||||
return this._copyViaExecCommand(text);
|
||||
}
|
||||
|
||||
_copyViaExecCommand(text) {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
// Positionierung ueber eine CSS-Klasse, NICHT ueber element.style --
|
||||
// die CSP dieses Projekts verbietet Inline-Styles (style-src 'self',
|
||||
// kein 'unsafe-inline'), siehe app/main.py. Und bewusst nicht
|
||||
// display:none/hidden: ein nicht gerendertes Element laesst sich nicht
|
||||
// selektieren, execCommand("copy") kopierte dann einen leeren Text.
|
||||
ta.className = "clipboard-helper";
|
||||
document.body.appendChild(ta);
|
||||
let ok = false;
|
||||
try {
|
||||
ta.focus();
|
||||
ta.select();
|
||||
ta.setSelectionRange(0, ta.value.length);
|
||||
ok = document.execCommand("copy");
|
||||
} catch (err) {
|
||||
console.error("Kopieren ueber execCommand fehlgeschlagen:", err);
|
||||
ok = false;
|
||||
}
|
||||
ta.remove();
|
||||
this.term.focus();
|
||||
return ok;
|
||||
}
|
||||
|
||||
async _attemptDirectCopy(x, y) {
|
||||
const text = this.term.getSelection();
|
||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
||||
this._showCopyMenu(x, y, text);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} catch (_) {
|
||||
// Wie bei _attemptDirectPaste(): Ruecksicherung unten, deren Klick
|
||||
// selbst wieder eine frische Benutzergeste ist.
|
||||
if (!(await this._copyToClipboard(text))) {
|
||||
// Beide Wege verweigert -- Ruecksicherung, deren Klick selbst wieder
|
||||
// eine frische Benutzergeste ist.
|
||||
this._showCopyMenu(x, y, text);
|
||||
}
|
||||
}
|
||||
@ -615,7 +691,7 @@
|
||||
} catch (err) {
|
||||
console.error("Einfuegen aus der Zwischenablage nicht moeglich:", err);
|
||||
this._setStatus(
|
||||
"Einfuegen nicht moeglich -- Browser verweigert Zwischenablage-Zugriff. Bitte Strg+Umschalt+V verwenden.",
|
||||
"Einfuegen nicht moeglich -- der Browser verweigert den Lesezugriff auf die Zwischenablage. Bitte stattdessen Strg+V oder Umschalt+Einfg druecken (das geht ohne Berechtigung, das Terminal muss nur den Fokus haben).",
|
||||
true
|
||||
);
|
||||
}
|
||||
@ -625,11 +701,11 @@
|
||||
_showCopyMenu(x, y, text) {
|
||||
this._showClipboardMenu(x, y, "Kopieren", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
if (!(await this._copyToClipboard(text))) throw new Error("execCommand(copy) lieferte false");
|
||||
} catch (err) {
|
||||
console.error("Kopieren in die Zwischenablage nicht moeglich:", err);
|
||||
this._setStatus(
|
||||
"Kopieren nicht moeglich -- Browser verweigert Zwischenablage-Zugriff. Bitte Strg+Umschalt+C verwenden.",
|
||||
"Kopieren nicht moeglich -- der Browser verweigert den Schreibzugriff auf die Zwischenablage. Bitte den markierten Text mit Strg+Einfg kopieren.",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user