hmgr 0.0.42 → 0.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/gui-signal.js +29 -0
- package/dist/aenderungen.json +6 -0
- package/dist/cli/hilfe.js +7 -0
- package/dist/cli/main.js +37 -16
- package/dist/gui/main.js +104 -0
- package/dist/gui/preload.cjs +5 -0
- package/dist/gui/renderer/renderer.js +36 -2
- package/dist/gui/signal.js +58 -0
- package/dist/kern/anwendungsfaelle.js +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Signaldatei von der CLI an eine laufende GUI (E110): `<hmgrHome>/gui-signal.json`. Die CLI
|
|
2
|
+
// schreibt sie nach `dir add` und Verwandten, die GUI beobachtet das Verzeichnis (src/gui/signal.ts).
|
|
3
|
+
// Kein Port: der Kern kennt das Signal nicht, die Entscheidung „Signal ja/nein" fällt in der CLI.
|
|
4
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { ohneBom } from "./datei-text.js";
|
|
7
|
+
export const SIGNAL_DATEI = 'gui-signal.json';
|
|
8
|
+
/** Atomar: Temp-Datei im selben Verzeichnis, dann rename darüber. */
|
|
9
|
+
export async function signalSchreiben(hmgrHome, signal) {
|
|
10
|
+
await mkdir(hmgrHome, { recursive: true });
|
|
11
|
+
const ziel = path.join(hmgrHome, SIGNAL_DATEI);
|
|
12
|
+
const temp = path.join(hmgrHome, `${SIGNAL_DATEI}.${process.pid}.tmp`);
|
|
13
|
+
await writeFile(temp, `${JSON.stringify(signal)}\n`);
|
|
14
|
+
await rename(temp, ziel);
|
|
15
|
+
}
|
|
16
|
+
/** `undefined` bei fehlender, kaputter oder unbekannt geformter Datei. */
|
|
17
|
+
export async function signalLesen(hmgrHome) {
|
|
18
|
+
try {
|
|
19
|
+
const roh = await readFile(path.join(hmgrHome, SIGNAL_DATEI), 'utf8');
|
|
20
|
+
const daten = JSON.parse(ohneBom(roh));
|
|
21
|
+
if (daten.art !== 'verzeichnis-zeigen' || typeof daten.pfad !== 'string' || typeof daten.zeit !== 'number') {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return { art: daten.art, pfad: daten.pfad, zeit: daten.zeit };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/aenderungen.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"fassung": 1,
|
|
3
3
|
"versionen": {
|
|
4
|
+
"0.0.44": [
|
|
5
|
+
"Läuft gerade eine Aktualisierung, zeigt hmgr ein im Terminal hinzugefügtes Verzeichnis trotzdem sofort an, statt bis zum Ende der Aktualisierung zu warten."
|
|
6
|
+
],
|
|
7
|
+
"0.0.43": [
|
|
8
|
+
"Fügst du im Terminal ein Arbeitsverzeichnis hinzu (hmgr dir add oder link mit einem neuen Verzeichnis), kommt ein offenes hmgr-Fenster nach vorn und zeigt das Verzeichnis sofort an."
|
|
9
|
+
],
|
|
4
10
|
"0.0.42": [
|
|
5
11
|
"Der Verlauf meldet jetzt auch neue und entfernte Skills einer Quelle, nicht nur geänderte.",
|
|
6
12
|
"Verlauf und „hmgr update“ sind leichter zu lesen: Quellen erscheinen mit ihrem Namen, darunter Neu, Entfernt und Geändert, ohne Commit-Kürzel. Quellen ohne Änderung an deinen Skills stehen gesammelt in einer Zeile am Ende."
|
package/dist/cli/hilfe.js
CHANGED
|
@@ -104,6 +104,10 @@ Plugins des Vorhabens stehen. Ohne Zeiger bleibt das Verzeichnis gemerkt und jed
|
|
|
104
104
|
(E26). dir forget löst Bindung und Eintrag, die gelegten Links bleiben liegen. Gebunden wird nur für
|
|
105
105
|
Harnesses, die hmgr verwaltet: standardmäßig nur claude-code (E38).
|
|
106
106
|
|
|
107
|
+
Läuft die hmgr-GUI, kommt sie bei dir add nach vorn und zeigt das Verzeichnis (E110). Dasselbe
|
|
108
|
+
passiert bei link, unlink, adopt, bundle add und plugin add mit --ort dir:<pfad>, wenn das Verzeichnis
|
|
109
|
+
dabei neu gemerkt wird. Über SSH bleibt die GUI in Ruhe, und läuft keine GUI, passiert nichts.
|
|
110
|
+
|
|
107
111
|
Ein Arbeitsverzeichnis unter Windows, das in einer WSL-Distro liegt (\\\\wsl$\\<distro>\\… oder
|
|
108
112
|
\\\\wsl.localhost\\<distro>\\…, E96), bedient hmgr über die Bridge dieser Distro, wie ein Homedir in
|
|
109
113
|
WSL: der Link liegt im Linux-Dateisystem und zeigt auf den Windows-Checkout (/mnt/c). MCP-Server
|
|
@@ -135,6 +139,9 @@ nur für ein Arbeitsverzeichnis mit composer.json, an Home und Profil bleibt jed
|
|
|
135
139
|
Das Schreiben in einen Zeiger ist gesperrt, bis das Composer-Thema entschieden ist (E57): an einem
|
|
136
140
|
Arbeitsverzeichnis mit Zeiger bricht link ohne --local ab, der Link wird nicht gelegt.
|
|
137
141
|
|
|
142
|
+
Mit --ort dir:<pfad> kommt eine laufende hmgr-GUI nach vorn und zeigt das Verzeichnis, wenn es dabei
|
|
143
|
+
neu gemerkt wird (nicht per SSH, E110).
|
|
144
|
+
|
|
138
145
|
Schalter:
|
|
139
146
|
--harness <name> claude-code, hermes oder agents, Standard claude-code
|
|
140
147
|
--ort <ort> home, wsl:<distro>, dir:<pfad> oder profil:<name>, Standard home. profil
|
package/dist/cli/main.js
CHANGED
|
@@ -21,6 +21,7 @@ import { paketVersion } from "../adapter/paket-version.js";
|
|
|
21
21
|
import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
|
|
22
22
|
import { TerminalRueckfrage } from "../adapter/terminal.js";
|
|
23
23
|
import { ProzessUmgebung } from "../adapter/umgebung.js";
|
|
24
|
+
import { signalSchreiben } from "../adapter/gui-signal.js";
|
|
24
25
|
import { ZustandJson } from "../adapter/zustand-json.js";
|
|
25
26
|
import { VerlaufJson } from "../adapter/verlauf-json.js";
|
|
26
27
|
import { AenderungenJson } from "../adapter/aenderungen-json.js";
|
|
@@ -259,6 +260,21 @@ const UEBERNAHME_GRUND = {
|
|
|
259
260
|
'lokale-aenderungen': 'Ordner weicht von der Quelle ab',
|
|
260
261
|
'nicht-pruefbar': 'hmgr kann nicht prüfen, ob der Ordner der Quelle entspricht',
|
|
261
262
|
};
|
|
263
|
+
/**
|
|
264
|
+
* E110: gibt einer laufenden GUI Bescheid, dass dieses Verzeichnis gezeigt werden soll. Nie bei
|
|
265
|
+
* SSH-Aufrufen, nie ein Grund zu scheitern.
|
|
266
|
+
*/
|
|
267
|
+
async function guiBenachrichtigen(umgebung, ports, pfad) {
|
|
268
|
+
try {
|
|
269
|
+
const variablen = ports.umgebung.variablen();
|
|
270
|
+
if ((variablen.SSH_CONNECTION ?? '') !== '' || (variablen.SSH_TTY ?? '') !== '')
|
|
271
|
+
return;
|
|
272
|
+
await signalSchreiben(umgebung.hmgrHome, { art: 'verzeichnis-zeigen', pfad, zeit: Date.now() });
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Das Signal ist ein Zusatz; das Kommando selbst hat funktioniert.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
262
278
|
export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
263
279
|
const { anwendungsfaelle, ports } = bauen(umgebung, optionen);
|
|
264
280
|
const kommando = argv[0];
|
|
@@ -426,6 +442,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
426
442
|
const ergebnis = composer === undefined
|
|
427
443
|
? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(pfad)
|
|
428
444
|
: await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(pfad, composer);
|
|
445
|
+
await guiBenachrichtigen(umgebung, ports, ergebnis.pfad);
|
|
429
446
|
console.log(`Arbeitsverzeichnis ${ergebnis.pfad}`);
|
|
430
447
|
if (ergebnis.zeigerPfad !== undefined)
|
|
431
448
|
console.log(` Zeiger ${ergebnis.zeigerPfad}`);
|
|
@@ -465,10 +482,11 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
465
482
|
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
466
483
|
// Verzeichnis merken und binden, damit status es wiederfindet und der Zeiger bekannt ist (E21).
|
|
467
484
|
const composer = werte.get('composer');
|
|
468
|
-
|
|
469
|
-
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
470
|
-
|
|
471
|
-
|
|
485
|
+
const verz = composer === undefined
|
|
486
|
+
? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
487
|
+
: await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
488
|
+
if (verz.neu)
|
|
489
|
+
await guiBenachrichtigen(umgebung, ports, verz.pfad);
|
|
472
490
|
}
|
|
473
491
|
if (kommando === 'unlink') {
|
|
474
492
|
const ergebnis = await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
@@ -504,10 +522,11 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
504
522
|
const name = rest[0];
|
|
505
523
|
if (ziel.ort.art === 'arbeitsverzeichnis' && !probelauf) {
|
|
506
524
|
const composer = werte.get('composer');
|
|
507
|
-
|
|
508
|
-
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
509
|
-
|
|
510
|
-
|
|
525
|
+
const verz = composer === undefined
|
|
526
|
+
? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
527
|
+
: await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
528
|
+
if (verz.neu)
|
|
529
|
+
await guiBenachrichtigen(umgebung, ports, verz.pfad);
|
|
511
530
|
}
|
|
512
531
|
const ergebnis = await anwendungsfaelle.uebernehmen({
|
|
513
532
|
ziel,
|
|
@@ -568,10 +587,11 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
568
587
|
const lokal = flaggen.has('local') || flaggen.has('lokal');
|
|
569
588
|
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
570
589
|
const composer = werte.get('composer');
|
|
571
|
-
|
|
572
|
-
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
573
|
-
|
|
574
|
-
|
|
590
|
+
const verz = composer === undefined
|
|
591
|
+
? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
592
|
+
: await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
593
|
+
if (verz.neu)
|
|
594
|
+
await guiBenachrichtigen(umgebung, ports, verz.pfad);
|
|
575
595
|
}
|
|
576
596
|
const quelle = werte.get('quelle');
|
|
577
597
|
const ergebnis = unterkommando === 'add'
|
|
@@ -627,10 +647,11 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
627
647
|
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
628
648
|
// Wie bei link: Verzeichnis merken und binden, damit status es wiederfindet (E21).
|
|
629
649
|
const composer = werte.get('composer');
|
|
630
|
-
|
|
631
|
-
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
632
|
-
|
|
633
|
-
|
|
650
|
+
const verz = composer === undefined
|
|
651
|
+
? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad)
|
|
652
|
+
: await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
653
|
+
if (verz.neu)
|
|
654
|
+
await guiBenachrichtigen(umgebung, ports, verz.pfad);
|
|
634
655
|
}
|
|
635
656
|
if (unterkommando === 'add') {
|
|
636
657
|
const quelle = werte.get('quelle');
|
package/dist/gui/main.js
CHANGED
|
@@ -21,6 +21,8 @@ import { iconFuerQuelle } from "./icons.js";
|
|
|
21
21
|
import { entscheideOeffnen } from "./oeffnen.js";
|
|
22
22
|
import { mcpUebernahmeSerialisieren, mcpUebersichtSerialisieren, mcpWirkungSerialisieren } from "./mcp-ansicht.js";
|
|
23
23
|
import { beobachten } from "./beobachter.js";
|
|
24
|
+
import { signalSchreiben } from "../adapter/gui-signal.js";
|
|
25
|
+
import { signalBeobachten } from "./signal.js";
|
|
24
26
|
import { loginPathUebernehmen } from "./login-pfad.js";
|
|
25
27
|
import { hmgrCliVersion } from "./hmgr-cli.js";
|
|
26
28
|
import { cliUpdateHinweis } from "../adapter/npm-registry.js";
|
|
@@ -505,6 +507,7 @@ function neuLadenAnstossenFabrik(minAbstandMs) {
|
|
|
505
507
|
}
|
|
506
508
|
const neuLadenAnstossen = neuLadenAnstossenFabrik(FOKUS_RELOAD_MIN_ABSTAND_MS);
|
|
507
509
|
let beobachter;
|
|
510
|
+
let signalBeobachter;
|
|
508
511
|
function fensterOeffnen() {
|
|
509
512
|
hauptfenster = new BrowserWindow({
|
|
510
513
|
width: 1280,
|
|
@@ -1367,6 +1370,87 @@ function fensterOeffnen() {
|
|
|
1367
1370
|
await new Promise((r) => setTimeout(r, 800));
|
|
1368
1371
|
const auszug = await fenster.webContents.executeJavaScript(NEUSTART_AUSZUG_SKRIPT);
|
|
1369
1372
|
console.log(`[smoke] Neustart-Modal: ${String(auszug)}`);
|
|
1373
|
+
})
|
|
1374
|
+
// E110: CLI-Signal gegen die laufende GUI. Das Signal schreibt dieser Main-Prozess in das
|
|
1375
|
+
// Wegwerf-Home (der Renderer kann keine Dateien schreiben). Fall 1: ohne Dialog springt die
|
|
1376
|
+
// GUI aus einer anderen Ansicht zur Karte. Fall 2: mit offenem Dialog bleibt alles stehen,
|
|
1377
|
+
// erst nach dem Schließen folgt der Sprung. Fall 3 (E112): bei laufendem Aktualisieren lädt das Signal trotzdem, der Watcher nicht. Läuft nach der Neustart-Modal-Prüfung, weil der Fall
|
|
1378
|
+
// offene Dialoge schließt.
|
|
1379
|
+
.then(async () => {
|
|
1380
|
+
const pfade = (process.env.HMGR_GUI_SMOKE_SIGNAL_PFADE ?? '').split('\n').filter((p) => p !== '');
|
|
1381
|
+
if (pfade.length < 2)
|
|
1382
|
+
return;
|
|
1383
|
+
const [pfad1, pfad2] = pfade;
|
|
1384
|
+
const js = (code) => fenster.webContents.executeJavaScript(code);
|
|
1385
|
+
const karteJs = (pfad) => `(function () {
|
|
1386
|
+
var schluessel = ${JSON.stringify(`dir:${pfad}`)};
|
|
1387
|
+
var karte = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
1388
|
+
.find(function (k) { return k.dataset.verzKey === schluessel; });
|
|
1389
|
+
return {
|
|
1390
|
+
karte: karte !== undefined,
|
|
1391
|
+
zieleSichtbar: document.getElementById('ansicht-ziele').hidden === false,
|
|
1392
|
+
aufgeklappt: karte?.querySelector('.zielkopf')?.getAttribute('aria-expanded') ?? null,
|
|
1393
|
+
};
|
|
1394
|
+
})()`;
|
|
1395
|
+
const warteAufSprung = async (pfad) => {
|
|
1396
|
+
let stand = null;
|
|
1397
|
+
for (let ms = 0; ms < 4000; ms += 100) {
|
|
1398
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
1399
|
+
stand = await js(karteJs(pfad));
|
|
1400
|
+
const k = stand;
|
|
1401
|
+
if (k.zieleSichtbar && k.aufgeklappt === 'true')
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
return stand;
|
|
1405
|
+
};
|
|
1406
|
+
const ergebnis = {};
|
|
1407
|
+
try {
|
|
1408
|
+
await js(`new Promise(function (r) { var n = 0; (function w() { if (document.querySelector('dialog[open]') || ++n > 30) r(); else setTimeout(w, 50); })(); })`);
|
|
1409
|
+
await js(`document.querySelectorAll('dialog[open]').forEach(function (d) { d.close(); });
|
|
1410
|
+
document.querySelector('[data-ansicht="mcp"]').click(); true`);
|
|
1411
|
+
ergebnis.fall1Vorher = await js(karteJs(pfad1));
|
|
1412
|
+
await signalSchreiben(umgebung.hmgrHome, { art: 'verzeichnis-zeigen', pfad: pfad1, zeit: Date.now() });
|
|
1413
|
+
ergebnis.fall1 = await warteAufSprung(pfad1);
|
|
1414
|
+
// Fall 2: Dialog offen, andere Ansicht, Signal für ein anderes Verzeichnis.
|
|
1415
|
+
await js(`document.querySelector('[data-ansicht="mcp"]').click();
|
|
1416
|
+
document.getElementById('d-einstellungen').showModal(); true`);
|
|
1417
|
+
ergebnis.fall2Vorher = await js(karteJs(pfad2));
|
|
1418
|
+
await signalSchreiben(umgebung.hmgrHome, { art: 'verzeichnis-zeigen', pfad: pfad2, zeit: Date.now() });
|
|
1419
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
1420
|
+
ergebnis.fall2Offen = await js(`({ dialogOffen: document.getElementById('d-einstellungen').open === true, ` +
|
|
1421
|
+
`ansichtMcp: document.getElementById('ansicht-mcp').hidden === false })`);
|
|
1422
|
+
ergebnis.fall2NachWarten = await js(karteJs(pfad2));
|
|
1423
|
+
await js(`document.getElementById('d-einstellungen').close(); true`);
|
|
1424
|
+
ergebnis.fall2 = await warteAufSprung(pfad2);
|
|
1425
|
+
// Fall 3 (E112): ein Aktualisieren-Lauf läuft (so erkennt ihn der Renderer: der
|
|
1426
|
+
// Kringel ist gesperrt). Ein Watcher-/Fokus-Anstoß lädt nicht; das Signal lädt sofort
|
|
1427
|
+
// und springt trotzdem. Ein MutationObserver an der Zielliste belegt das Neuladen
|
|
1428
|
+
// (ladeAlles baut sie neu auf). Karte von Fall 1 vorher zuklappen, Ansicht wechseln.
|
|
1429
|
+
await js(`window.__e112Aenderungen = 0;
|
|
1430
|
+
window.__e112Beobachter = new MutationObserver(function (m) { window.__e112Aenderungen += m.length; });
|
|
1431
|
+
window.__e112Beobachter.observe(document.getElementById('zielliste'), { childList: true, subtree: true });
|
|
1432
|
+
document.getElementById('updatestart').disabled = true;
|
|
1433
|
+
document.querySelector('[data-ansicht="mcp"]').click(); true`);
|
|
1434
|
+
await js(`(function () {
|
|
1435
|
+
var k = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
1436
|
+
.find(function (x) { return x.dataset.verzKey === ${JSON.stringify(`dir:${pfad1}`)}; });
|
|
1437
|
+
var kopf = k.querySelector('.zielkopf');
|
|
1438
|
+
if (kopf.getAttribute('aria-expanded') === 'true') kopf.click();
|
|
1439
|
+
window.__e112Aenderungen = 0; return true; })()`);
|
|
1440
|
+
ergebnis.fall3Vorher = await js(karteJs(pfad1));
|
|
1441
|
+
fenster.webContents.send('hmgr:neuLadenAnstossen');
|
|
1442
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
1443
|
+
ergebnis.fall3WatcherAenderungen = await js(`window.__e112Aenderungen`);
|
|
1444
|
+
await signalSchreiben(umgebung.hmgrHome, { art: 'verzeichnis-zeigen', pfad: pfad1, zeit: Date.now() });
|
|
1445
|
+
ergebnis.fall3 = await warteAufSprung(pfad1);
|
|
1446
|
+
ergebnis.fall3Aenderungen = await js(`window.__e112Aenderungen`);
|
|
1447
|
+
ergebnis.fall3LaufNochGesperrt = await js(`document.getElementById('updatestart').disabled === true`);
|
|
1448
|
+
await js(`document.getElementById('updatestart').disabled = false; window.__e112Beobachter.disconnect(); true`);
|
|
1449
|
+
}
|
|
1450
|
+
catch (fehler) {
|
|
1451
|
+
ergebnis.fehler = String(fehler);
|
|
1452
|
+
}
|
|
1453
|
+
console.log(`[smoke] Signal: ${JSON.stringify(ergebnis)}`);
|
|
1370
1454
|
})
|
|
1371
1455
|
.finally(() => app.quit());
|
|
1372
1456
|
}, 3000);
|
|
@@ -1407,6 +1491,26 @@ function fensterOeffnen() {
|
|
|
1407
1491
|
beobachter = undefined;
|
|
1408
1492
|
});
|
|
1409
1493
|
}
|
|
1494
|
+
// CLI-Signal (E110): `hmgr dir add` und Verwandte schreiben `gui-signal.json` ins hmgr-Home. Anders
|
|
1495
|
+
// als der Watcher oben ohne Entprellen und ohne Drossel; läuft auch im Smoke-Test (mit dessen
|
|
1496
|
+
// Wegwerf-Home), dort aber ohne Fokusklau.
|
|
1497
|
+
{
|
|
1498
|
+
const fenster = hauptfenster;
|
|
1499
|
+
signalBeobachter = signalBeobachten(umgebung.hmgrHome, (pfad) => {
|
|
1500
|
+
if (fenster.isDestroyed())
|
|
1501
|
+
return;
|
|
1502
|
+
if (!smoketest) {
|
|
1503
|
+
fensterNachVornHolen();
|
|
1504
|
+
if (process.platform === 'darwin')
|
|
1505
|
+
app.focus({ steal: true });
|
|
1506
|
+
}
|
|
1507
|
+
fenster.webContents.send('hmgr:verzeichnisZeigen', pfad);
|
|
1508
|
+
});
|
|
1509
|
+
fenster.on('closed', () => {
|
|
1510
|
+
signalBeobachter?.close();
|
|
1511
|
+
signalBeobachter = undefined;
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1410
1514
|
void hauptfenster.loadFile(path.join(hierVerzeichnis, 'renderer', 'index.html'));
|
|
1411
1515
|
}
|
|
1412
1516
|
const { autoUpdater } = electronUpdater;
|
package/dist/gui/preload.cjs
CHANGED
|
@@ -79,6 +79,11 @@ contextBridge.exposeInMainWorld('hmgr', {
|
|
|
79
79
|
onNeuLadenAnstossen: (hoerer) => {
|
|
80
80
|
ipcRenderer.on('hmgr:neuLadenAnstossen', () => hoerer());
|
|
81
81
|
},
|
|
82
|
+
/** Signal der CLI (E110): ein Arbeitsverzeichnis soll gezeigt werden. Main hat das Fenster schon
|
|
83
|
+
nach vorn geholt; kein Entprellen, keine Drossel. */
|
|
84
|
+
onVerzeichnisZeigen: (hoerer) => {
|
|
85
|
+
ipcRenderer.on('hmgr:verzeichnisZeigen', (_event, pfad) => hoerer(pfad));
|
|
86
|
+
},
|
|
82
87
|
/** Prüfung auf neue hmgr-Stände (E77), angestoßen bei jedem Aktualisieren-Lauf (E80: Start,
|
|
83
88
|
Intervall, Klick). Antwortet nicht, meldet sich später über onSelbstUpdate — Netz und Spawn
|
|
84
89
|
dauern länger als ein Knopfdruck. */
|
|
@@ -2797,7 +2797,7 @@
|
|
|
2797
2797
|
var uhrzeit = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
|
2798
2798
|
standNachLauf(ergebnis, ausloeser, uhrzeit);
|
|
2799
2799
|
await ladeVerlauf();
|
|
2800
|
-
await
|
|
2800
|
+
await ladeAllesNacheinander();
|
|
2801
2801
|
} finally {
|
|
2802
2802
|
standLaeuft = false;
|
|
2803
2803
|
knopf.disabled = false;
|
|
@@ -2883,6 +2883,15 @@
|
|
|
2883
2883
|
// überholt. Fehler landen wie sonst über rufe() in der Meldungsleiste, kein stiller Absturz.
|
|
2884
2884
|
var stillerReloadLaeuft = false;
|
|
2885
2885
|
var stillerReloadVorgemerkt = false;
|
|
2886
|
+
// Alle Neuaufbauten der stillen Wege, des Signals und des Laufendes (E112) laufen hintereinander,
|
|
2887
|
+
// nie gleichzeitig. Die Kette reißt nie: ein Fehler (rufe() hat ihn gemeldet) wird für die
|
|
2888
|
+
// Kette verschluckt, dem Aufrufer aber weitergereicht.
|
|
2889
|
+
var ladeKette = Promise.resolve();
|
|
2890
|
+
function ladeAllesNacheinander() {
|
|
2891
|
+
var lauf = ladeKette.then(function () { return ladeAlles(); });
|
|
2892
|
+
ladeKette = lauf.catch(function () {});
|
|
2893
|
+
return lauf;
|
|
2894
|
+
}
|
|
2886
2895
|
async function stillerReload() {
|
|
2887
2896
|
// Läuft gerade ein Aktualisieren-Lauf, wird nicht parallel geladen: er endet ohnehin mit
|
|
2888
2897
|
// ladeAlles und nimmt die Änderung mit. Sonst bauten zwei Läufe gleichzeitig die Ansicht auf.
|
|
@@ -2893,7 +2902,7 @@
|
|
|
2893
2902
|
}
|
|
2894
2903
|
stillerReloadLaeuft = true;
|
|
2895
2904
|
try {
|
|
2896
|
-
await
|
|
2905
|
+
await ladeAllesNacheinander();
|
|
2897
2906
|
} catch (fehler) {
|
|
2898
2907
|
// rufe() hat den Fehler schon gemeldet, hier bleibt nur: den Durchlauf nicht wiederholen.
|
|
2899
2908
|
} finally {
|
|
@@ -2906,6 +2915,31 @@
|
|
|
2906
2915
|
}
|
|
2907
2916
|
window.hmgr.onNeuLadenAnstossen(function () { void stillerReload(); });
|
|
2908
2917
|
|
|
2918
|
+
// CLI-Signal (E110, nach `hmgr dir add` und Verwandten): sofort neu laden, dann zur Karte des
|
|
2919
|
+
// Verzeichnisses springen, auch aus einer anderen Ansicht. Steht ein Dialog offen, bleibt er
|
|
2920
|
+
// unangetastet und der Sprung wartet, bis kein Dialog mehr offen ist; ein neueres Signal ersetzt
|
|
2921
|
+
// die Vormerkung. Die Meldungsleiste bleibt, wie sie ist.
|
|
2922
|
+
var signalPfadVorgemerkt = null;
|
|
2923
|
+
function signalSprungEinloesen() {
|
|
2924
|
+
if (signalPfadVorgemerkt === null) return;
|
|
2925
|
+
if (document.querySelector('dialog[open]')) return;
|
|
2926
|
+
var pfad = signalPfadVorgemerkt;
|
|
2927
|
+
signalPfadVorgemerkt = null;
|
|
2928
|
+
// Eine E84-Vormerkung auf denselben Pfad ist mit diesem Sprung erledigt.
|
|
2929
|
+
if (zielSprungVorgemerkt === pfad) zielSprungVorgemerkt = null;
|
|
2930
|
+
zeigeAnsicht('ziele');
|
|
2931
|
+
springeZuVerzeichnis(pfad);
|
|
2932
|
+
}
|
|
2933
|
+
// Der Signalweg lädt sofort, auch während eines Aktualisieren-Laufs (E112); die Serialisierung
|
|
2934
|
+
// hält ihn von dessen Neuaufbau am Ende getrennt. Der Sprung folgt auch nach einem Fehler.
|
|
2935
|
+
window.hmgr.onVerzeichnisZeigen(function (pfad) {
|
|
2936
|
+
signalPfadVorgemerkt = pfad;
|
|
2937
|
+
void ladeAllesNacheinander().catch(function () {}).then(signalSprungEinloesen);
|
|
2938
|
+
});
|
|
2939
|
+
// `close` bubbelt nicht, der Capture-Listener am Dokument sieht aber jeden geschlossenen Dialog.
|
|
2940
|
+
// Der Aufschub um einen Takt lässt einen unmittelbar folgenden Dialog zuerst aufgehen.
|
|
2941
|
+
document.addEventListener('close', function () { setTimeout(signalSprungEinloesen, 0); }, true);
|
|
2942
|
+
|
|
2909
2943
|
/**
|
|
2910
2944
|
* Eine Ereigniszeile im Verlauf (E80): Marke + Text, Muster wie die frühere baueBericht-Zeile
|
|
2911
2945
|
* und die CLI-Ausgabe (verlaufEreignisZeile, src/cli/main.ts). Checkouts stellt
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Beobachter für das CLI-Signal (E110), ohne Electron-Import (testbar wie beobachter.ts). Beobachtet
|
|
2
|
+
// wird das Verzeichnis hmgrHome mit Filter auf `gui-signal.json` (aus demselben Grund wie in
|
|
3
|
+
// beobachter.ts: die Datei wird per rename ersetzt, ein Watcher auf die Datei würde taub).
|
|
4
|
+
// Anders als beobachter.ts kein Entprellen und kein Inhaltsvergleich: jedes frische Signal zählt.
|
|
5
|
+
import { watch } from 'node:fs';
|
|
6
|
+
import { SIGNAL_DATEI, signalLesen } from "../adapter/gui-signal.js";
|
|
7
|
+
export function signalBeobachten(hmgrHome, callback, optionen = {}) {
|
|
8
|
+
const sammelnMs = optionen.sammelnMs ?? 30;
|
|
9
|
+
const maxAlterMs = optionen.maxAlterMs ?? 10_000;
|
|
10
|
+
let timer;
|
|
11
|
+
let geschlossen = false;
|
|
12
|
+
let zuletzt;
|
|
13
|
+
// Eine beim Start vorhandene Datei gilt als schon gesehen. Das Alter allein reicht nicht: auf macOS
|
|
14
|
+
// liefert fs.watch kurz nach dem Anlegen auch Events für Schreibvorgänge kurz davor.
|
|
15
|
+
const startStand = signalLesen(hmgrHome).then((signal) => {
|
|
16
|
+
if (signal !== undefined)
|
|
17
|
+
zuletzt = `${signal.zeit}\u0000${signal.pfad}`;
|
|
18
|
+
});
|
|
19
|
+
async function auswerten() {
|
|
20
|
+
await startStand;
|
|
21
|
+
const signal = await signalLesen(hmgrHome);
|
|
22
|
+
if (geschlossen || signal === undefined)
|
|
23
|
+
return;
|
|
24
|
+
if (Date.now() - signal.zeit > maxAlterMs)
|
|
25
|
+
return;
|
|
26
|
+
const schluessel = `${signal.zeit}\u0000${signal.pfad}`;
|
|
27
|
+
if (schluessel === zuletzt)
|
|
28
|
+
return;
|
|
29
|
+
zuletzt = schluessel;
|
|
30
|
+
callback(signal.pfad);
|
|
31
|
+
}
|
|
32
|
+
let watcher;
|
|
33
|
+
try {
|
|
34
|
+
watcher = watch(hmgrHome, (_ereignis, name) => {
|
|
35
|
+
if (geschlossen)
|
|
36
|
+
return;
|
|
37
|
+
if (name !== null && name !== SIGNAL_DATEI)
|
|
38
|
+
return;
|
|
39
|
+
if (timer !== undefined)
|
|
40
|
+
return; // schon ein Lesen eingeplant
|
|
41
|
+
timer = setTimeout(() => {
|
|
42
|
+
timer = undefined;
|
|
43
|
+
void auswerten();
|
|
44
|
+
}, sammelnMs);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Verzeichnis fehlt: unbeobachtet.
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
close() {
|
|
52
|
+
geschlossen = true;
|
|
53
|
+
if (timer !== undefined)
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
watcher?.close();
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -1322,7 +1322,8 @@ export class Anwendungsfaelle {
|
|
|
1322
1322
|
hinweis = `Kein Zeiger unter ${verzeichnis}, alles bleibt lokal (E26).`;
|
|
1323
1323
|
}
|
|
1324
1324
|
const zustand = await this.p.zustand.lesen();
|
|
1325
|
-
|
|
1325
|
+
const neu = !zustand.arbeitsverzeichnisse.includes(verzeichnis);
|
|
1326
|
+
if (neu)
|
|
1326
1327
|
zustand.arbeitsverzeichnisse.push(verzeichnis);
|
|
1327
1328
|
if (zeigerPfad !== undefined) {
|
|
1328
1329
|
const gebunden = zeigerPfad;
|
|
@@ -1338,6 +1339,7 @@ export class Anwendungsfaelle {
|
|
|
1338
1339
|
await this.p.zustand.schreiben(zustand);
|
|
1339
1340
|
return {
|
|
1340
1341
|
pfad: verzeichnis,
|
|
1342
|
+
neu,
|
|
1341
1343
|
...(zeigerPfad === undefined ? {} : { zeigerPfad }),
|
|
1342
1344
|
...(hinweis === undefined ? {} : { hinweis }),
|
|
1343
1345
|
};
|