hmgr 0.0.7 → 0.0.12
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/agents.js +14 -5
- package/dist/adapter/claude-code.js +53 -12
- package/dist/adapter/dateisystem.js +85 -43
- package/dist/adapter/heime.js +76 -0
- package/dist/adapter/hermes.js +16 -7
- package/dist/adapter/npm-registry.js +61 -0
- package/dist/adapter/umgebung.js +6 -1
- package/dist/adapter/verlauf-json.js +39 -0
- package/dist/adapter/wsl.js +212 -0
- package/dist/adapter/zustand-json.js +36 -15
- package/dist/cli/hilfe.js +100 -13
- package/dist/cli/main.js +219 -18
- package/dist/gui/hmgr-cli.js +44 -0
- package/dist/gui/main.js +344 -4
- package/dist/gui/preload.cjs +26 -2
- package/dist/gui/renderer/index.html +67 -15
- package/dist/gui/renderer/renderer.js +441 -228
- package/dist/gui/renderer/styles.css +69 -16
- package/dist/kern/anwendungsfaelle.js +244 -16
- package/dist/kern/modell.js +112 -1
- package/dist/kern/version.js +13 -0
- package/dist/ports/dateizugriff.js +1 -0
- package/dist/ports/harness.js +4 -0
- package/dist/ports/heime.js +1 -0
- package/dist/ports/verlauf.js +4 -0
- package/dist/ports/zustand.js +4 -3
- package/package.json +7 -1
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
// Pfade, die hier angezeigt werden, baut der Kern mit `node:path`, also mit dem Trenner der
|
|
21
21
|
// Plattform — auch dann, wenn HOME selbst in POSIX-Schreibweise gesetzt ist (MSYS/Git-Bash).
|
|
22
22
|
var TRENNER = '/';
|
|
23
|
+
// Die Plattform selbst (E78): entscheidet, ob der Weg zu einem Homedir in WSL überhaupt im Bild
|
|
24
|
+
// steht. Die Grenze zieht der Kern; hier soll nur kein Knopf stehen, der sicher abbricht.
|
|
25
|
+
var PLATTFORM = '';
|
|
23
26
|
/** Trenner einer Plattform, wie `process.platform` sie nennt. Rein, nur für die Anzeige. */
|
|
24
27
|
function plattformTrenner(plattform) { return plattform === 'win32' ? '\\' : '/'; }
|
|
25
28
|
/**
|
|
@@ -163,15 +166,20 @@
|
|
|
163
166
|
|
|
164
167
|
/* ---------------- Ort- und Grund-Texte ---------------- */
|
|
165
168
|
|
|
169
|
+
/* Ein zusätzliches Homedir (E78) heißt nach seiner Adresse, damit die Karten „claude-code · Home"
|
|
170
|
+
und „claude-code · wsl:Ubuntu" im Bild auseinanderzuhalten sind. */
|
|
166
171
|
function ortName(ort) {
|
|
167
|
-
if (ort.art === 'home') return 'Home';
|
|
172
|
+
if (ort.art === 'home') return ort.heim ? ort.heim : 'Home';
|
|
168
173
|
if (ort.art === 'profil') return 'Profil „' + ort.name + '“';
|
|
169
174
|
return 'Arbeitsverzeichnis ' + pfadKurz(ort.pfad);
|
|
170
175
|
}
|
|
171
176
|
function zielTitel(z) { return harnessName(z.ziel.harness) + ' · ' + ortName(z.ziel.ort); }
|
|
177
|
+
/* Gleichlautend mit zielSchluessel im Kern: ein Ort ohne heim behält seinen bisherigen Schlüssel. */
|
|
172
178
|
function zielKey(ziel) {
|
|
173
179
|
var o = ziel.ort;
|
|
174
|
-
var ort = o.art === 'home'
|
|
180
|
+
var ort = o.art === 'home'
|
|
181
|
+
? (o.heim ? 'home:' + o.heim : 'home')
|
|
182
|
+
: o.art === 'profil' ? 'profil:' + o.name : 'dir:' + o.pfad;
|
|
175
183
|
return ziel.harness + '/' + ort;
|
|
176
184
|
}
|
|
177
185
|
function quelleKurz(quelle) { return quelle.art === 'git' ? quelle.url : quelle.pfad; }
|
|
@@ -312,6 +320,9 @@
|
|
|
312
320
|
var HARNESSE_ALLE = [];
|
|
313
321
|
var AKTIVE_HARNESSE = [];
|
|
314
322
|
var HARNESSE_NICHT_INSTALLIERT = [];
|
|
323
|
+
// Homedirs je Harness (E78): je Eintrag { harness, heime: [{ adresse?, pfad, eingestellt }],
|
|
324
|
+
// kannZusaetzliche }. Grundlage der Homedir-Zeilen in den Einstellungen.
|
|
325
|
+
var HARNESS_HEIME = [];
|
|
315
326
|
// MCP-Katalog (E61-Folgerunde): je Eintrag { eintrag, eingehaengt, fehlendeVariablen }, dazu
|
|
316
327
|
// Einträge in Harness-Konfigurationen, die nicht im Katalog stehen (nur genannt). Seit F46 nur
|
|
317
328
|
// noch Grundlage für den MCP-Abschnitt der Ziel-Karten (mcpZeilenFuerZiel), die MCP-Ansicht selbst
|
|
@@ -326,6 +337,14 @@
|
|
|
326
337
|
// Ziel-Karten — nie am Harness-Namen entschieden, sondern an dieser Liste.
|
|
327
338
|
var MCP_PLAETZE = [];
|
|
328
339
|
var quellenFilter = 'alle';
|
|
340
|
+
// Sortierung der Skills-Rubrik (E79, Richtung E81): hält für die Sitzung wie quellenFilter,
|
|
341
|
+
// übersteht also ladeAlles. SKILLS selbst bleibt unsortiert nach Datum, damit li.dataset.index
|
|
342
|
+
// stabil bleibt.
|
|
343
|
+
var skillSortierung = 'name';
|
|
344
|
+
// Sortierrichtung je Schlüssel (E81): 'auf' oder 'ab'. Ein Klick auf den bereits aktiven Chip
|
|
345
|
+
// kehrt sie um, ein Klick auf den anderen Chip setzt die Standardrichtung des neuen Schlüssels.
|
|
346
|
+
var skillRichtung = 'auf';
|
|
347
|
+
var SKILL_RICHTUNG_STANDARD = { name: 'auf', datum: 'ab' };
|
|
329
348
|
var zielFilter = 'alle';
|
|
330
349
|
// Standard-Ref je Quelle (E60), aus dem Katalog abgeleitet (SKILLS trägt ihn schon je Skill): ein
|
|
331
350
|
// Ref an einem Link erscheint nur, wenn er davon abweicht (`@v2.1`). Schlüssel wie quelleKurz.
|
|
@@ -335,19 +354,43 @@
|
|
|
335
354
|
|
|
336
355
|
// Ignorierte fremde Einträge zählen hier nicht mit (E43): Ignorieren blendet die Warnung aus,
|
|
337
356
|
// sonst bliebe das Ziel im Filter „braucht Aufmerksamkeit" stehen.
|
|
338
|
-
function
|
|
357
|
+
function zielAusloeser(z) {
|
|
339
358
|
var ignoriert = z.ignoriert || [];
|
|
340
|
-
return z.eintraege.
|
|
359
|
+
return z.eintraege.filter(function (ei) {
|
|
341
360
|
if (ei.art === 'fremd') return ignoriert.indexOf(ei.name) === -1;
|
|
342
361
|
return ei.art === 'link-ohne-quelle';
|
|
343
|
-
});
|
|
362
|
+
}).length;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Warnung und Warnzahl (E75) sind dieselbe Regel, einmal geschrieben: die Warnung steht genau
|
|
366
|
+
// dann, wenn die Zählung etwas findet — sonst könnten Dreieck und Zahl auseinanderlaufen.
|
|
367
|
+
function zielBraucht(z) { return zielAusloeser(z) > 0; }
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Warnzeichen für den Kartenkopf, mit der Zahl der Auslöser daneben (E75): eine Karte mit
|
|
371
|
+
* „0 Skills" und fünfzehn fremden Einträgen wirkte sonst leer, obwohl der Ordner voll ist. Die
|
|
372
|
+
* E71-Zahl (eigene Skills) bleibt daneben stehen, die Warnzahl ist eine eigene Information.
|
|
373
|
+
* Zeichen statt Text-Badge: trägt die Farbe selbst (Emoji-Darstellung), aria-label hält den
|
|
374
|
+
* Akkordeon-Knopf-Namen brauchbar (role="img", die Bedeutung steht nicht im Glyph selbst) und
|
|
375
|
+
* nennt die Zahl mit, sonst hörte sie im Screenreader gar nicht.
|
|
376
|
+
*/
|
|
377
|
+
function warnzeichen(anzahl) {
|
|
378
|
+
var span = e('span', 'warnung');
|
|
379
|
+
span.setAttribute('role', 'img');
|
|
380
|
+
span.setAttribute(
|
|
381
|
+
'aria-label',
|
|
382
|
+
anzahl === 1 ? '1 Eintrag braucht Aufmerksamkeit' : anzahl + ' Einträge brauchen Aufmerksamkeit',
|
|
383
|
+
);
|
|
384
|
+
span.appendChild(document.createTextNode('⚠️'));
|
|
385
|
+
span.appendChild(e('span', 'warnzahl', String(anzahl)));
|
|
386
|
+
return span;
|
|
344
387
|
}
|
|
345
388
|
|
|
346
|
-
// E45: ein Ziel eines inaktiven Harness
|
|
347
|
-
// fremde Einträge
|
|
389
|
+
// E74 (revidiert E45): ein Ziel eines inaktiven Harness erscheint gar nicht, auch wenn dort noch
|
|
390
|
+
// Links, fremde Einträge oder eine harness-eigene Registrierung liegen — was hmgr nicht managed,
|
|
391
|
+
// zeigt es nicht. Beim Aktivieren taucht das Ziel samt allem, was liegen blieb, wieder auf.
|
|
348
392
|
function zielSichtbar(z) {
|
|
349
|
-
|
|
350
|
-
return z.eintraege.length > 0 || z.istRegistriert;
|
|
393
|
+
return harnessAktiv(z.ziel.harness);
|
|
351
394
|
}
|
|
352
395
|
|
|
353
396
|
// Von Hand aufgeklappte Ziele bleiben über Neuaufbauten der Liste offen (E45), Schlüssel wie
|
|
@@ -391,6 +434,7 @@
|
|
|
391
434
|
rufe(function () { return window.hmgr.plattform(); }),
|
|
392
435
|
]);
|
|
393
436
|
HOME = werte[0];
|
|
437
|
+
PLATTFORM = werte[1];
|
|
394
438
|
TRENNER = plattformTrenner(werte[1]);
|
|
395
439
|
$('mcpkatalogpfad').textContent = pfadSchreibweise('~/.hmgr/mcp.yaml');
|
|
396
440
|
}
|
|
@@ -410,6 +454,7 @@
|
|
|
410
454
|
rufe(function () { return window.hmgr.mcpUebersicht(); }),
|
|
411
455
|
rufe(function () { return window.hmgr.mcpPlaetze(); }),
|
|
412
456
|
rufe(function () { return window.hmgr.bundles(); }),
|
|
457
|
+
rufe(function () { return window.hmgr.harnessHeime(); }),
|
|
413
458
|
]);
|
|
414
459
|
ZIELE = ergebnisse[0];
|
|
415
460
|
SKILLS = ergebnisse[1];
|
|
@@ -427,6 +472,7 @@
|
|
|
427
472
|
BUNDLES = ergebnisse[9].bundles;
|
|
428
473
|
BUNDLES.sort(function (a, b) { return a.name.localeCompare(b.name, 'de') || a.quelleText.localeCompare(b.quelleText, 'de'); });
|
|
429
474
|
BUNDLES_KAPUTT = ergebnisse[9].kaputte;
|
|
475
|
+
HARNESS_HEIME = ergebnisse[10] || [];
|
|
430
476
|
STANDARD_REF = {};
|
|
431
477
|
SKILLS.forEach(function (s) { if (STANDARD_REF[s.quelleText] === undefined) STANDARD_REF[s.quelleText] = s.ref; });
|
|
432
478
|
|
|
@@ -493,8 +539,12 @@
|
|
|
493
539
|
else a.removeAttribute('aria-current');
|
|
494
540
|
});
|
|
495
541
|
ANSICHTEN.forEach(function (x) { $('ansicht-' + x).hidden = x !== name; });
|
|
496
|
-
|
|
497
|
-
|
|
542
|
+
if (name === 'update') {
|
|
543
|
+
// Die Ansicht ist jetzt offen, die Ankündigung „mit Neuem" hat ihren Dienst getan (E80).
|
|
544
|
+
if (standNeu !== null) { standNeu = null; standText(); }
|
|
545
|
+
// Der Verlauf zeigt immer den aktuellen Stand der Datei, nicht nur den des letzten Laufs hier.
|
|
546
|
+
void ladeVerlauf();
|
|
547
|
+
}
|
|
498
548
|
window.scrollTo(0, 0);
|
|
499
549
|
}
|
|
500
550
|
document.querySelectorAll('[data-ansicht]').forEach(function (k) {
|
|
@@ -503,26 +553,6 @@
|
|
|
503
553
|
|
|
504
554
|
/* ================= ZIELE ================= */
|
|
505
555
|
|
|
506
|
-
/** Hinweiszeile, wenn ein inaktiver Harness noch Links oder eine eigene Registrierung an diesem
|
|
507
|
-
Ziel hat (E38 R7): Säubern-Weg direkt in der Karte. Von linkTeil und alleTeil geteilt. */
|
|
508
|
-
function ruheZeile(z, hatEigene) {
|
|
509
|
-
var rz = e('div', 'ruhezeile');
|
|
510
|
-
var rsvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
511
|
-
rsvg.setAttribute('viewBox', '0 0 16 16');
|
|
512
|
-
rsvg.setAttribute('stroke-width', '1.6');
|
|
513
|
-
rsvg.setAttribute('aria-hidden', 'true');
|
|
514
|
-
rsvg.innerHTML = '<circle cx="8" cy="8" r="6.5"/><path d="M8 4.5v4M8 10.5v.5"/>';
|
|
515
|
-
rz.appendChild(rsvg);
|
|
516
|
-
rz.appendChild(e('p', null, harnessName(z.ziel.harness) + ' ist inaktiv. '
|
|
517
|
-
+ (hatEigene ? 'hmgr pflegt diese Links nicht mehr.' : 'Ein harness-eigener Eintrag verweist noch auf dieses Ziel.')));
|
|
518
|
-
var rk = e('button', 'knopf leise klein', 'Säubern');
|
|
519
|
-
rk.type = 'button';
|
|
520
|
-
rk.setAttribute('aria-label', harnessName(z.ziel.harness) + ' säubern');
|
|
521
|
-
rk.addEventListener('click', function () { frageSaeubern(z.ziel.harness); });
|
|
522
|
-
rz.appendChild(rk);
|
|
523
|
-
return rz;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
556
|
/** Eine Zeile im Fremdkasten für einen fremden Eintrag x an einem Ziel z. Mit mitZeichen=true
|
|
527
557
|
steht davor das kleine Harness-Zeichen seines Fundorts (E53 Pkt. 5, „Alle"-Ansicht). Gibt
|
|
528
558
|
neben dem li auch zurück, ob eine Quelle erkannt wurde (für den Erklärtext). */
|
|
@@ -770,13 +800,6 @@
|
|
|
770
800
|
var eigene = z.eintraege.filter(function (ei) { return ei.art !== 'fremd'; });
|
|
771
801
|
var fremde = z.eintraege.filter(function (ei) { return ei.art === 'fremd'; });
|
|
772
802
|
|
|
773
|
-
// Harness abgeschaltet, Konfiguration liegt noch (E38, GRILL.md Entwurfskritik 4): der Weg,
|
|
774
|
-
// sie loszuwerden, steht hier oben in der Karte. Zählt auch ohne Links, wenn die harness-eigene
|
|
775
|
-
// Registrierung noch da ist (E38 R7), etwa Hermes' external_dirs-Eintrag ohne Links darunter.
|
|
776
|
-
if (!harnessAktiv(z.ziel.harness) && (eigene.length > 0 || z.istRegistriert)) {
|
|
777
|
-
teil.appendChild(ruheZeile(z, eigene.length > 0));
|
|
778
|
-
}
|
|
779
|
-
|
|
780
803
|
var kz = konfigZeile(z);
|
|
781
804
|
if (kz) teil.appendChild(kz);
|
|
782
805
|
|
|
@@ -1022,15 +1045,8 @@
|
|
|
1022
1045
|
kopf.setAttribute('aria-expanded', offen ? 'true' : 'false');
|
|
1023
1046
|
kopf.appendChild(beschriftet(e('h3', null, zielTitel(z)), z.ziel.harness));
|
|
1024
1047
|
kopf.appendChild(e('span', 'mono', pfadKurz(z.verzeichnis)));
|
|
1025
|
-
|
|
1026
|
-
if (
|
|
1027
|
-
// Zeichen statt Text-Badge: trägt die Farbe selbst (Emoji-Darstellung), aria-label hält den
|
|
1028
|
-
// Akkordeon-Knopf-Namen brauchbar (role="img", die Bedeutung steht nicht im Glyph selbst).
|
|
1029
|
-
var warnspan = e('span', 'warnung', '⚠️');
|
|
1030
|
-
warnspan.setAttribute('role', 'img');
|
|
1031
|
-
warnspan.setAttribute('aria-label', 'braucht Aufmerksamkeit');
|
|
1032
|
-
kopf.appendChild(warnspan);
|
|
1033
|
-
}
|
|
1048
|
+
var ausloeser = zielAusloeser(z);
|
|
1049
|
+
if (ausloeser > 0) kopf.appendChild(warnzeichen(ausloeser));
|
|
1034
1050
|
// Der Kopf zählt Skills, nicht Einträge (E71), über dieselbe Bündelung, aus der der Körper zeilt.
|
|
1035
1051
|
// Bei einem Harness fällt die Zahl mit der Zahl eigener Einträge zusammen, die Regel bleibt eine.
|
|
1036
1052
|
var zahl = buendleEigene([z]).reihenfolge.length;
|
|
@@ -1074,17 +1090,10 @@
|
|
|
1074
1090
|
|
|
1075
1091
|
/** Körper des Chips „Alle" einer Verzeichnis-Karte (E53 Pkt. 2, 4, 5): eine gemeinsame Liste
|
|
1076
1092
|
statt gestapelter Harness-Abschnitte. Eigene Links gebündelt (Schlüssel Name+Quelle), fremde
|
|
1077
|
-
Einträge
|
|
1093
|
+
Einträge je Harness mit dessen Zeichen am Fundort. */
|
|
1078
1094
|
function alleTeil(pfad, teile) {
|
|
1079
1095
|
var teil = document.createDocumentFragment();
|
|
1080
1096
|
|
|
1081
|
-
teile.forEach(function (z) {
|
|
1082
|
-
var eigeneZ = z.eintraege.filter(function (ei) { return ei.art !== 'fremd'; });
|
|
1083
|
-
if (!harnessAktiv(z.ziel.harness) && (eigeneZ.length > 0 || z.istRegistriert)) {
|
|
1084
|
-
teil.appendChild(ruheZeile(z, eigeneZ.length > 0));
|
|
1085
|
-
}
|
|
1086
|
-
});
|
|
1087
|
-
|
|
1088
1097
|
var beachteteAlle = [], ignorierteAlle = [];
|
|
1089
1098
|
teile.forEach(function (z) {
|
|
1090
1099
|
var ignoriertNamen = z.ignoriert || [];
|
|
@@ -1267,12 +1276,10 @@
|
|
|
1267
1276
|
.forEach(function (z) { symbole.appendChild(harnessZeichen(z.ziel.harness)); });
|
|
1268
1277
|
kopf.appendChild(symbole);
|
|
1269
1278
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
kopf.appendChild(warnspan);
|
|
1275
|
-
}
|
|
1279
|
+
// An der Verzeichnis-Karte ist die Warnzahl die Summe über die Teile (E75): der Nutzer sieht
|
|
1280
|
+
// hier einen Ordner, nicht die Harness-Abschnitte darunter.
|
|
1281
|
+
var ausloeserGesamt = teile.reduce(function (summe, z) { return summe + zielAusloeser(z); }, 0);
|
|
1282
|
+
if (ausloeserGesamt > 0) kopf.appendChild(warnzeichen(ausloeserGesamt));
|
|
1276
1283
|
// Skills statt Einträge (E71): eine Summe über die Harness-Abschnitte zählte jeden Skill so oft,
|
|
1277
1284
|
// wie ihn Harnesses am selben Verzeichnis führen — der Körper bündelt sie zu einer Zeile.
|
|
1278
1285
|
var gesamtZahl = buendleEigene(teile).reihenfolge.length;
|
|
@@ -1323,7 +1330,8 @@
|
|
|
1323
1330
|
var gewaehlt = teile.filter(function (z) { return z.ziel.harness === aktivChip; })[0];
|
|
1324
1331
|
// Welcher Ordner im Verzeichnis gelesen wird, stand vorher im Abschnittskopf (25cddf6) und
|
|
1325
1332
|
// gehört zum gewählten Harness, nicht zur Karte: er steht rechts in der Chip-Leiste, relativ
|
|
1326
|
-
// zum Verzeichnis,
|
|
1333
|
+
// zum Verzeichnis. Chips gibt es nur für aktive Harnesses (E74), eine Inaktiv-Marke braucht
|
|
1334
|
+
// die Leiste darum nicht mehr.
|
|
1327
1335
|
if (gewaehlt) {
|
|
1328
1336
|
// Nur die Anzeige: liegt der gelesene Ordner unterhalb des Verzeichnisses, steht er
|
|
1329
1337
|
// relativ, sonst gekürzt. Beide Trenner, sonst greift das unter Windows nie (E68).
|
|
@@ -1331,7 +1339,6 @@
|
|
|
1331
1339
|
|| gewaehlt.verzeichnis.indexOf(pfad + '\\') === 0;
|
|
1332
1340
|
chipleiste.appendChild(e('span', 'kartenordner mono',
|
|
1333
1341
|
unterhalb ? gewaehlt.verzeichnis.slice(pfad.length + 1) : pfadKurz(gewaehlt.verzeichnis)));
|
|
1334
|
-
if (!harnessAktiv(aktivChip)) chipleiste.appendChild(e('span', 'ruhend', 'Harness inaktiv'));
|
|
1335
1342
|
}
|
|
1336
1343
|
koerper.appendChild(chipleiste);
|
|
1337
1344
|
if (gewaehlt) koerper.appendChild(linkTeil(gewaehlt));
|
|
@@ -1799,12 +1806,44 @@
|
|
|
1799
1806
|
return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
|
1800
1807
|
}
|
|
1801
1808
|
|
|
1809
|
+
/** Reihenfolge der Skills-Indizes für die Katalog-Ansicht (E79, Richtung E81). SKILLS selbst
|
|
1810
|
+
bleibt unverändert (Name, dann Quelle sortiert, siehe ladeAlles), damit li.dataset.index
|
|
1811
|
+
stabil bleibt. Bei 'name'/'ab' einfach umgekehrt. Bei 'datum' stehen je nach Richtung die
|
|
1812
|
+
jüngsten oder die ältesten zuerst, Skills ohne Datum in BEIDEN Richtungen ganz hinten, bei
|
|
1813
|
+
Gleichstand (Datum oder „ohne Datum") in beiden Richtungen wie bisher nach Name/Quelle (also
|
|
1814
|
+
die ursprüngliche Reihenfolge). */
|
|
1815
|
+
function skillReihenfolge() {
|
|
1816
|
+
var indizes = SKILLS.map(function (_, i) { return i; });
|
|
1817
|
+
if (skillSortierung === 'name') {
|
|
1818
|
+
if (skillRichtung === 'ab') indizes.reverse();
|
|
1819
|
+
return indizes;
|
|
1820
|
+
}
|
|
1821
|
+
indizes.sort(function (a, b) {
|
|
1822
|
+
// Numerisch vergleichen, nicht als String: geaendert ist ISO-8601 mit dem Zeitzonen-Offset
|
|
1823
|
+
// des Committers (git %cI), zwei verschiedene Offsets ordnet ein String-Vergleich falsch.
|
|
1824
|
+
// Nicht parsebar zählt wie „ohne Datum" (hinten, in beiden Richtungen).
|
|
1825
|
+
var ta = SKILLS[a].geaendert ? Date.parse(SKILLS[a].geaendert) : NaN;
|
|
1826
|
+
var tb = SKILLS[b].geaendert ? Date.parse(SKILLS[b].geaendert) : NaN;
|
|
1827
|
+
var oa = isNaN(ta);
|
|
1828
|
+
var ob = isNaN(tb);
|
|
1829
|
+
if (oa && ob) return a - b;
|
|
1830
|
+
if (oa) return 1;
|
|
1831
|
+
if (ob) return -1;
|
|
1832
|
+
if (ta === tb) return a - b;
|
|
1833
|
+
if (skillRichtung === 'auf') return ta < tb ? -1 : 1;
|
|
1834
|
+
return ta < tb ? 1 : -1;
|
|
1835
|
+
});
|
|
1836
|
+
return indizes;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1802
1839
|
function baueSkills() {
|
|
1803
1840
|
var liste = $('katalog');
|
|
1804
1841
|
liste.textContent = '';
|
|
1805
|
-
|
|
1842
|
+
skillReihenfolge().forEach(function (i) {
|
|
1843
|
+
var skill = SKILLS[i];
|
|
1806
1844
|
var li = e('li', 'zeile');
|
|
1807
1845
|
li.dataset.index = i;
|
|
1846
|
+
if (skill.geaendert) li.dataset.geaendert = skill.geaendert;
|
|
1808
1847
|
var idx = quellenIndex(skill.quelleText);
|
|
1809
1848
|
li.style.borderLeftColor = 'var(--q' + (idx + 1) + ')';
|
|
1810
1849
|
|
|
@@ -1903,6 +1942,32 @@
|
|
|
1903
1942
|
}
|
|
1904
1943
|
$('suchfeld').addEventListener('input', filtereSkills);
|
|
1905
1944
|
|
|
1945
|
+
/** Sortierknöpfe der Skills-Rubrik (E79, Richtung E81): statisches Markup, deshalb Listener nur
|
|
1946
|
+
einmal gebunden, nicht in baueSkills. Klick auf den bereits aktiven Chip kehrt nur die
|
|
1947
|
+
Richtung um; Klick auf den anderen Chip wechselt den Schlüssel und setzt dessen
|
|
1948
|
+
Standardrichtung. Nur der aktive Chip trägt den Pfeil (↑/↓) und data-richtung, das liest der
|
|
1949
|
+
Smoke. */
|
|
1950
|
+
function setzeSkillSortierung(wert) {
|
|
1951
|
+
if (wert === skillSortierung) {
|
|
1952
|
+
skillRichtung = skillRichtung === 'auf' ? 'ab' : 'auf';
|
|
1953
|
+
} else {
|
|
1954
|
+
skillSortierung = wert;
|
|
1955
|
+
skillRichtung = SKILL_RICHTUNG_STANDARD[wert];
|
|
1956
|
+
}
|
|
1957
|
+
var pfeil = skillRichtung === 'auf' ? '↑' : '↓';
|
|
1958
|
+
[['sortnach-name', 'name'], ['sortnach-datum', 'datum']].forEach(function (paar) {
|
|
1959
|
+
var chip = $(paar[0]);
|
|
1960
|
+
var aktiv = skillSortierung === paar[1];
|
|
1961
|
+
chip.setAttribute('aria-pressed', aktiv ? 'true' : 'false');
|
|
1962
|
+
chip.querySelector('.sortpfeil').textContent = aktiv ? pfeil : '';
|
|
1963
|
+
if (aktiv) chip.dataset.richtung = skillRichtung;
|
|
1964
|
+
else delete chip.dataset.richtung;
|
|
1965
|
+
});
|
|
1966
|
+
baueSkills();
|
|
1967
|
+
}
|
|
1968
|
+
$('sortnach-name').addEventListener('click', function () { setzeSkillSortierung('name'); });
|
|
1969
|
+
$('sortnach-datum').addEventListener('click', function () { setzeSkillSortierung('datum'); });
|
|
1970
|
+
|
|
1906
1971
|
var laufendeSkill = null;
|
|
1907
1972
|
function starteVerlinken(index) {
|
|
1908
1973
|
laufendeSkill = index;
|
|
@@ -1910,7 +1975,14 @@
|
|
|
1910
1975
|
$('dz-skill').textContent = skill.name;
|
|
1911
1976
|
var box = $('dz-ziele');
|
|
1912
1977
|
box.textContent = '';
|
|
1913
|
-
|
|
1978
|
+
// E76: Ziele inaktiver Harnesses stehen gar nicht erst zur Wahl — der Kern lehnte den Link
|
|
1979
|
+
// ohnehin ab, und seit E74 zeigt keine Ansicht sie. Dieselbe Regel wie die Zielliste.
|
|
1980
|
+
var waehlbar = ZIELE.filter(zielSichtbar);
|
|
1981
|
+
if (waehlbar.length === 0) {
|
|
1982
|
+
box.appendChild(e('p', 'leerhinweis',
|
|
1983
|
+
'Kein Ziel wählbar: alle Harnesses sind deaktiviert. Unter Einstellungen einen aktivieren.'));
|
|
1984
|
+
}
|
|
1985
|
+
waehlbar.forEach(function (z) {
|
|
1914
1986
|
var schon = z.eintraege.some(function (ei) {
|
|
1915
1987
|
var n = ei.art === 'link' ? ei.link.name : ei.name;
|
|
1916
1988
|
return n === skill.name;
|
|
@@ -2319,100 +2391,140 @@
|
|
|
2319
2391
|
/* ================= AKTUALISIEREN ================= */
|
|
2320
2392
|
|
|
2321
2393
|
/**
|
|
2322
|
-
* Ein Aktualisieren-Lauf, von Hand über den
|
|
2394
|
+
* Ein Aktualisieren-Lauf, von Hand über den Kringel oder selbsttätig angestoßen (Start, Intervall
|
|
2395
|
+
* — E44) — `ausloeser` sagt, welcher, und geht an den Kern für den Verlauf (E80). Eine Sperre
|
|
2323
2396
|
* über den disabled-Zustand des Knopfs verhindert, dass ein Anstoß während eines laufenden Laufs
|
|
2324
|
-
* etwas tut.
|
|
2397
|
+
* etwas tut. Start und Intervall rufen den Kern ohne Rückfrage, sie räumen also nichts weg.
|
|
2325
2398
|
*/
|
|
2326
|
-
/* Stand-Zeile
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2399
|
+
/* Stand-Zeile, gemeinsam für Seitenleiste (#standtext) und Update-Ansicht (#updatestand) (E80):
|
|
2400
|
+
vor dem ersten Lauf „Noch nicht geprüft", während eines Laufs „Prüft …", danach ruhig „Alles
|
|
2401
|
+
aktuell, HH:MM geprüft", oder bei Fehlschlägen (E51) „Prüfung fehlgeschlagen, HH:MM" (alle
|
|
2402
|
+
Quellen-Abrufe gescheitert) bzw. „Geprüft mit Fehlern, HH:MM" (Mischung). Hat ein Start- oder
|
|
2403
|
+
Intervall-Lauf Neues gebracht und die Ansicht war noch nicht offen, hervorgehoben „N Skills
|
|
2404
|
+
aktualisiert, HH:MM" (Fallback „N Quellen"/„N Bundles mit Neuem, HH:MM") — dieses Signal hat
|
|
2405
|
+
Vorrang, bis die Ansicht öffnet (zeigeAnsicht räumt `standNeu` dann ab) oder ein neuer Lauf es
|
|
2406
|
+
ersetzt. `.neu` am Standknopf bleibt die Hervorhebung, solange der Zustand gilt. */
|
|
2332
2407
|
var standZeit = null;
|
|
2333
2408
|
var standFehlerZustand = 'keine';
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2409
|
+
var standLaeuft = false;
|
|
2410
|
+
var standNeu = null; // { zahlwort: string, anhang: string } | null
|
|
2411
|
+
|
|
2412
|
+
function fuelleStand(el) {
|
|
2413
|
+
el.textContent = '';
|
|
2414
|
+
if (standLaeuft) { el.textContent = 'Prüft …'; return; }
|
|
2415
|
+
if (standNeu !== null) {
|
|
2416
|
+
el.appendChild(e('b', null, standNeu.zahlwort));
|
|
2417
|
+
el.appendChild(document.createTextNode(' ' + standNeu.anhang + ', ' + standZeit));
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
var text = 'Noch nicht geprüft';
|
|
2421
|
+
if (standZeit !== null) {
|
|
2422
|
+
text = standFehlerZustand === 'alle' ? 'Prüfung fehlgeschlagen, ' + standZeit
|
|
2423
|
+
: standFehlerZustand === 'gemischt' ? 'Geprüft mit Fehlern, ' + standZeit
|
|
2424
|
+
: 'Alles aktuell, ' + standZeit + ' geprüft';
|
|
2425
|
+
}
|
|
2426
|
+
el.textContent = text;
|
|
2427
|
+
}
|
|
2428
|
+
function standText() {
|
|
2429
|
+
fuelleStand($('standtext'));
|
|
2430
|
+
fuelleStand($('updatestand'));
|
|
2431
|
+
$('standknopf').classList.toggle('neu', standNeu !== null);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
function standNachLauf(ergebnis, ausloeser, uhrzeit) {
|
|
2344
2435
|
standZeit = uhrzeit;
|
|
2345
2436
|
var quellen = {};
|
|
2346
2437
|
ergebnis.checkouts.forEach(function (c) {
|
|
2347
2438
|
if (c.aktualisierung.ergebnis === 'aktualisiert') quellen[quelleKurz(c.checkout.quelle)] = true;
|
|
2348
2439
|
});
|
|
2349
2440
|
var neue = Object.keys(quellen).length;
|
|
2350
|
-
// Skill-Ebene (E70): Namen über alle Checkouts, doppelte zählen einmal. Damit sagt
|
|
2351
|
-
//
|
|
2441
|
+
// Skill-Ebene (E70): Namen über alle Checkouts, doppelte zählen einmal. Damit sagt die
|
|
2442
|
+
// Stand-Zeile „5 Skills aktualisiert" statt nur „1 Quelle mit Neuem".
|
|
2352
2443
|
var geaenderteSkills = {};
|
|
2353
2444
|
ergebnis.checkouts.forEach(function (c) {
|
|
2354
2445
|
(c.geaenderteSkills || []).forEach(function (name) { geaenderteSkills[name] = true; });
|
|
2355
2446
|
});
|
|
2356
2447
|
var skillZahl = Object.keys(geaenderteSkills).length;
|
|
2357
|
-
// Ein nachgezogenes Bundle ist Neues wie ein neuer Commit (E69, F64=O1): sonst bliebe
|
|
2358
|
-
//
|
|
2448
|
+
// Ein nachgezogenes Bundle ist Neues wie ein neuer Commit (E69, F64=O1): sonst bliebe die
|
|
2449
|
+
// Stand-Zeile still, obwohl sich am Ziel gerade Links geändert haben.
|
|
2359
2450
|
var bundlesNeu = (ergebnis.bundles || []).filter(function (b) { return b.ergebnis === 'aktualisiert'; }).length;
|
|
2360
2451
|
var fehlgeschlagen = ergebnis.checkouts.filter(function (c) { return c.aktualisierung.ergebnis === 'fehlgeschlagen'; }).length;
|
|
2361
2452
|
var gesamt = ergebnis.checkouts.length;
|
|
2362
2453
|
standFehlerZustand = fehlgeschlagen === 0 ? 'keine' : fehlgeschlagen === gesamt ? 'alle' : 'gemischt';
|
|
2363
|
-
// Von Hand läuft es in der Update-Ansicht,
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
p.appendChild(document.createTextNode(' aktualisiert'));
|
|
2454
|
+
// Von Hand (Klick) läuft es in der Update-Ansicht, die schon offen ist: nichts anzukündigen.
|
|
2455
|
+
var automatisch = ausloeser !== 'klick';
|
|
2456
|
+
if ((neue === 0 && bundlesNeu === 0) || !automatisch || !$('ansicht-update').hidden) {
|
|
2457
|
+
standNeu = null;
|
|
2458
|
+
} else if (skillZahl > 0) {
|
|
2459
|
+
// Ist die Skill-Zahl bekannt, sagt sie mehr als die Zahl der Quellen (E70). Sonst bleibt es
|
|
2460
|
+
// beim alten Signal: eine Quelle kann Neues haben, ohne dass ein Skill davon betroffen ist.
|
|
2461
|
+
standNeu = { zahlwort: zahlwort(skillZahl, 'Skill', 'Skills'), anhang: 'aktualisiert' };
|
|
2372
2462
|
} else {
|
|
2373
|
-
|
|
2374
|
-
? zahlwort(neue, 'Quelle', 'Quellen')
|
|
2375
|
-
:
|
|
2376
|
-
|
|
2463
|
+
standNeu = {
|
|
2464
|
+
zahlwort: neue > 0 ? zahlwort(neue, 'Quelle', 'Quellen') : zahlwort(bundlesNeu, 'Bundle', 'Bundles'),
|
|
2465
|
+
anhang: 'mit Neuem',
|
|
2466
|
+
};
|
|
2377
2467
|
}
|
|
2378
|
-
|
|
2379
|
-
$('standknopf').textContent = 'Ansehen';
|
|
2468
|
+
standText();
|
|
2380
2469
|
}
|
|
2381
2470
|
|
|
2382
|
-
async function aktualisierenLauf(
|
|
2471
|
+
async function aktualisierenLauf(ausloeser) {
|
|
2383
2472
|
var knopf = $('updatestart');
|
|
2384
2473
|
if (knopf.disabled) return;
|
|
2385
2474
|
knopf.disabled = true;
|
|
2386
|
-
knopf.
|
|
2387
|
-
|
|
2475
|
+
knopf.classList.add('laeuft');
|
|
2476
|
+
standLaeuft = true;
|
|
2477
|
+
standText();
|
|
2478
|
+
var lauf = rufe(function () {
|
|
2479
|
+
return window.hmgr.aktualisieren({ rueckfragen: ausloeser !== 'klick', ausloeser: ausloeser });
|
|
2480
|
+
});
|
|
2481
|
+
// Jeder Lauf prüft auch hmgr selbst (E80): angestoßen, nicht abgewartet — ein Fund meldet sich
|
|
2482
|
+
// später von sich aus in der Seitenleiste (onSelbstUpdate).
|
|
2483
|
+
window.hmgr.selbstUpdatePruefen();
|
|
2388
2484
|
try {
|
|
2389
|
-
var ergebnis = await
|
|
2390
|
-
return automatisch ? window.hmgr.aktualisieren({ rueckfragen: false }) : window.hmgr.aktualisieren();
|
|
2391
|
-
});
|
|
2392
|
-
baueBericht(ergebnis);
|
|
2485
|
+
var ergebnis = await lauf;
|
|
2393
2486
|
var uhrzeit = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
standNachLauf(ergebnis, automatisch, uhrzeit);
|
|
2487
|
+
standNachLauf(ergebnis, ausloeser, uhrzeit);
|
|
2488
|
+
await ladeVerlauf();
|
|
2397
2489
|
await ladeAlles();
|
|
2398
2490
|
} finally {
|
|
2491
|
+
standLaeuft = false;
|
|
2399
2492
|
knopf.disabled = false;
|
|
2400
|
-
knopf.
|
|
2493
|
+
knopf.classList.remove('laeuft');
|
|
2401
2494
|
// Scheiterte der Lauf, bliebe sonst „Prüft …" stehen.
|
|
2402
|
-
if ($('standtext').textContent === 'Prüft …')
|
|
2403
|
-
}
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2495
|
+
if ($('standtext').textContent === 'Prüft …') standText();
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/* Update-Hinweise über hmgr selbst (E77/E80), unten in der Seitenleiste über dem
|
|
2500
|
+
Einstellungen-Knopf. Zwei getrennte Zeilen, weil beide zugleich zutreffen können: ein
|
|
2501
|
+
geladenes GUI-Paket und eine neuere CLI auf npm sind zwei Pakete und zwei Kanäle (Gitea bzw.
|
|
2502
|
+
npm). Nur die GUI-Zeile trägt seit E80 einen Knopf (Neustart installiert das geladene Paket),
|
|
2503
|
+
die CLI installiert die GUI nicht selbst (F82). */
|
|
2504
|
+
function selbstUpdateZeigen(nachricht) {
|
|
2505
|
+
if (nachricht.art === 'gui') {
|
|
2506
|
+
$('selbstupdate-gui').textContent = 'hmgr ' + nachricht.version + ' bereit — wird beim Beenden installiert';
|
|
2507
|
+
$('selbstupdate-gui').hidden = false;
|
|
2508
|
+
$('selbstupdate-neustart').hidden = false;
|
|
2509
|
+
} else if (nachricht.art === 'cli') {
|
|
2510
|
+
var p = $('selbstupdate-cli');
|
|
2511
|
+
p.textContent = '';
|
|
2512
|
+
p.appendChild(document.createTextNode('hmgr-CLI ' + nachricht.installiert + ' — npm hat ' + nachricht.neu + ': '));
|
|
2513
|
+
p.appendChild(e('code', null, nachricht.befehl));
|
|
2514
|
+
p.hidden = false;
|
|
2515
|
+
} else return;
|
|
2516
|
+
$('selbstupdate').hidden = false;
|
|
2517
|
+
// Die Datei hat gerade ein hmgr-Ereignis bekommen (E80): der Verlauf zeigt den aktuellen Stand.
|
|
2518
|
+
void ladeVerlauf();
|
|
2519
|
+
}
|
|
2520
|
+
window.hmgr.onSelbstUpdate(selbstUpdateZeigen);
|
|
2521
|
+
$('selbstupdate-neustart').addEventListener('click', function () { window.hmgr.neuStarten(); });
|
|
2522
|
+
|
|
2523
|
+
$('updatestart').addEventListener('click', function () { aktualisierenLauf('klick'); });
|
|
2524
|
+
// Der Standknopf öffnet nur noch die Ansicht (E80): kein eigener Lauf mehr an diesem Klick, der
|
|
2525
|
+
// Kringel in der Ansicht startet ihn bei Bedarf.
|
|
2526
|
+
$('standknopf').addEventListener('click', function () { zeigeAnsicht('update'); });
|
|
2527
|
+
window.hmgr.onAktualisierenAnstossen(function (ausloeser) { aktualisierenLauf(ausloeser); });
|
|
2416
2528
|
|
|
2417
2529
|
// Stilles Neuladen bei Änderungen außerhalb der GUI (E62, Fokus-Wechsel und Datei-Watcher): nur
|
|
2418
2530
|
// ladeAlles, keine Meldung, kein Spinner-Zwang. Nachrutsch-sicher: höchstens ein Durchlauf läuft,
|
|
@@ -2443,89 +2555,76 @@
|
|
|
2443
2555
|
}
|
|
2444
2556
|
window.hmgr.onNeuLadenAnstossen(function () { void stillerReload(); });
|
|
2445
2557
|
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
var
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
li.appendChild(
|
|
2468
|
-
+ (
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
pluginBlock.appendChild(pul);
|
|
2485
|
-
box.appendChild(pluginBlock);
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
|
-
// Bundles im selben Lauf (E69, F64=O1): eine geänderte Definition wird beim Aktualisieren
|
|
2489
|
-
// nachgezogen, eine Zeile je Bundle im Stil der Checkout-Zeilen darüber.
|
|
2490
|
-
var bundleEintraege = ergebnis.bundles || [];
|
|
2491
|
-
if (bundleEintraege.length > 0) {
|
|
2492
|
-
var bundleBlock = e('div', 'block');
|
|
2493
|
-
bundleBlock.appendChild(e('span', 'etikett', 'Bundles'));
|
|
2494
|
-
var bul = e('ul');
|
|
2495
|
-
bundleEintraege.forEach(function (b) {
|
|
2496
|
-
var li = e('li');
|
|
2497
|
-
var text = b.ergebnis === 'aktualisiert' ? 'aktualisiert'
|
|
2498
|
-
: b.ergebnis === 'unveraendert' ? 'unverändert'
|
|
2499
|
-
: 'fehlgeschlagen (' + (b.fehler || '').split('\n')[0] + ')';
|
|
2500
|
-
li.appendChild(e('span', b.ergebnis === 'fehlgeschlagen' ? 'marke-aus' : 'marke-ok', text));
|
|
2501
|
-
// Was sich geändert hat, steht hinter dem Namen; ein Fehlschlag sagt, dass nichts geschah.
|
|
2502
|
-
var teile = [];
|
|
2503
|
-
if (b.gelegt.length > 0) teile.push('+ ' + b.gelegt.join(', '));
|
|
2504
|
-
if (b.entfernt.length > 0) teile.push('− ' + b.entfernt.join(', '));
|
|
2505
|
-
if (b.neueQuellen.length > 0) teile.push('neue Quellen: ' + b.neueQuellen.length);
|
|
2506
|
-
if (b.ergebnis === 'fehlgeschlagen') teile.push('bleibt auf dem alten Stand');
|
|
2507
|
-
li.appendChild(e('span', 'pfad', b.bundle + ' · ' + harnessName(b.ziel.harness) + ' · ' + ortName(b.ziel.ort)
|
|
2508
|
-
+ (teile.length > 0 ? ' · ' + teile.join(' · ') : '')));
|
|
2509
|
-
bul.appendChild(li);
|
|
2510
|
-
});
|
|
2511
|
-
bundleBlock.appendChild(bul);
|
|
2512
|
-
box.appendChild(bundleBlock);
|
|
2558
|
+
/**
|
|
2559
|
+
* Eine Ereigniszeile im Verlauf (E80): Marke + Text, Muster wie die frühere baueBericht-Zeile
|
|
2560
|
+
* (Checkout/Bundle) und die CLI-Ausgabe (verlaufEreignisZeile, src/cli/main.ts).
|
|
2561
|
+
*/
|
|
2562
|
+
function verlaufEreignisZeile(li, ereignis) {
|
|
2563
|
+
if (ereignis.art === 'checkout') {
|
|
2564
|
+
li.appendChild(e('span', 'marke-ok', 'aktualisiert'));
|
|
2565
|
+
var skills = ereignis.skills.length > 0
|
|
2566
|
+
? ' · ' + zahlwort(ereignis.skills.length, 'Skill', 'Skills') + ': ' + ereignis.skills.join(', ')
|
|
2567
|
+
: '';
|
|
2568
|
+
var stand = ' (' + ereignis.von.slice(0, 7) + ' → ' + ereignis.nach.slice(0, 7) + ')';
|
|
2569
|
+
li.appendChild(e('span', 'pfad', ereignis.quelle + ' @ ' + ereignis.ref + skills + stand));
|
|
2570
|
+
} else if (ereignis.art === 'bundle') {
|
|
2571
|
+
li.appendChild(e('span', 'marke-ok', 'Bundle'));
|
|
2572
|
+
var teile = [];
|
|
2573
|
+
if (ereignis.gelegt.length > 0) teile.push('gelegt ' + ereignis.gelegt.join(', '));
|
|
2574
|
+
if (ereignis.entfernt.length > 0) teile.push('entfernt ' + ereignis.entfernt.join(', '));
|
|
2575
|
+
// Ziel als Objekt seit E80-Nachtrag; ein String-Ziel (ältere Einträge) bleibt unverändert.
|
|
2576
|
+
var zielText = typeof ereignis.ziel === 'string'
|
|
2577
|
+
? ereignis.ziel
|
|
2578
|
+
: harnessName(ereignis.ziel.harness) + ' · ' + ortName(ereignis.ziel.ort);
|
|
2579
|
+
li.appendChild(e('span', 'pfad', ereignis.bundle + ' → ' + zielText
|
|
2580
|
+
+ (teile.length > 0 ? ' · ' + teile.join(' · ') : '')));
|
|
2581
|
+
} else if (ereignis.art === 'aufgeraeumt') {
|
|
2582
|
+
li.appendChild(e('span', 'marke-ok', 'aufgeräumt'));
|
|
2583
|
+
li.appendChild(e('span', 'pfad', ereignis.checkout));
|
|
2584
|
+
} else if (ereignis.art === 'fehler') {
|
|
2585
|
+
li.appendChild(e('span', 'marke-aus', 'fehlgeschlagen'));
|
|
2586
|
+
li.appendChild(e('span', 'pfad', ereignis.betrifft + ': ' + ereignis.fehler));
|
|
2587
|
+
} else if (ereignis.art === 'hmgr-installiert') {
|
|
2588
|
+
li.appendChild(e('span', 'marke-ok', 'installiert'));
|
|
2589
|
+
li.appendChild(e('span', 'pfad', (ereignis.paket === 'gui' ? 'hmgr-GUI ' : 'hmgr ') + ereignis.version));
|
|
2590
|
+
} else if (ereignis.art === 'gui-update-geladen') {
|
|
2591
|
+
li.appendChild(e('span', 'marke-ok', 'geladen'));
|
|
2592
|
+
li.appendChild(e('span', 'pfad', 'GUI-Update ' + ereignis.version));
|
|
2593
|
+
} else if (ereignis.art === 'cli-verfuegbar') {
|
|
2594
|
+
li.appendChild(e('span', 'marke-ok', 'verfügbar'));
|
|
2595
|
+
li.appendChild(e('span', 'pfad', 'CLI ' + ereignis.version));
|
|
2513
2596
|
}
|
|
2597
|
+
}
|
|
2514
2598
|
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2599
|
+
/**
|
|
2600
|
+
* Baut das Changelog in der Update-Ansicht aus ~/.hmgr/verlauf.json (E80): ein Block je Lauf,
|
|
2601
|
+
* neueste zuerst, das Etikett der lokale Zeitpunkt, darunter eine Zeile je Ereignis. Die Datei
|
|
2602
|
+
* ist die Wahrheit, kein eigener Zustand im Renderer — jeder Aufruf holt sie neu.
|
|
2603
|
+
*/
|
|
2604
|
+
async function ladeVerlauf() {
|
|
2605
|
+
var eintraege = await rufe(function () { return window.hmgr.verlauf(); });
|
|
2606
|
+
var box = $('verlauf');
|
|
2607
|
+
box.textContent = '';
|
|
2608
|
+
if (eintraege.length === 0) {
|
|
2609
|
+
box.appendChild(e('p', 'leerhinweis', 'Noch keine Ereignisse.'));
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
eintraege.forEach(function (eintrag) {
|
|
2613
|
+
var block = e('div', 'block');
|
|
2614
|
+
var zeit = new Date(eintrag.zeit);
|
|
2615
|
+
var zwei = function (n) { return String(n).padStart(2, '0'); };
|
|
2616
|
+
var etikett = zwei(zeit.getDate()) + '.' + zwei(zeit.getMonth() + 1) + '.' + zeit.getFullYear()
|
|
2617
|
+
+ ' ' + zwei(zeit.getHours()) + ':' + zwei(zeit.getMinutes());
|
|
2618
|
+
block.appendChild(e('span', 'etikett', etikett));
|
|
2619
|
+
var ul = e('ul');
|
|
2620
|
+
eintrag.ereignisse.forEach(function (ereignis) {
|
|
2520
2621
|
var li = e('li');
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
li.appendChild(quelleSpan('pfad', v.checkout.quelle, ' @ ' + v.checkout.ref));
|
|
2524
|
-
rul.appendChild(li);
|
|
2622
|
+
verlaufEreignisZeile(li, ereignis);
|
|
2623
|
+
ul.appendChild(li);
|
|
2525
2624
|
});
|
|
2526
|
-
|
|
2527
|
-
box.appendChild(
|
|
2528
|
-
}
|
|
2625
|
+
block.appendChild(ul);
|
|
2626
|
+
box.appendChild(block);
|
|
2627
|
+
});
|
|
2529
2628
|
}
|
|
2530
2629
|
|
|
2531
2630
|
/* ================= PLUGINS ================= */
|
|
@@ -2999,6 +3098,132 @@
|
|
|
2999
3098
|
+ '. Nicht von hmgr verwaltete Einträge liegen unverändert in den Verzeichnissen.';
|
|
3000
3099
|
}
|
|
3001
3100
|
|
|
3101
|
+
/* ---------------- Homedirs eines Harness (E78) ---------------- */
|
|
3102
|
+
|
|
3103
|
+
/**
|
|
3104
|
+
* Die Homedir-Zeilen unter einem Harness-Schalter (E78): das primäre mit „Ändern"/„Zurücksetzen"
|
|
3105
|
+
* (F89), darunter jedes zusätzliche mit „Vergessen" (F87), zuletzt der Knopf zum Eintragen eines
|
|
3106
|
+
* weiteren. Der Knopf steht nur bei einem Harness, der die Fähigkeit hat, und nur unter Windows —
|
|
3107
|
+
* dieselbe Grenze wie im Kern, hier nur damit niemand einen Weg vor sich sieht, der abbricht.
|
|
3108
|
+
*/
|
|
3109
|
+
function heimeFuerHarness(h) {
|
|
3110
|
+
var kasten = e('div', 'heime');
|
|
3111
|
+
var eintrag = null;
|
|
3112
|
+
HARNESS_HEIME.forEach(function (x) { if (x.harness === h) eintrag = x; });
|
|
3113
|
+
if (!eintrag) return kasten;
|
|
3114
|
+
eintrag.heime.forEach(function (heim) {
|
|
3115
|
+
var zeile = e('div', 'heimzeile');
|
|
3116
|
+
zeile.appendChild(e('span', 'heimart', heim.adresse ? heim.adresse : 'Homedir'));
|
|
3117
|
+
var pfad = e('code', 'heimpfad', pfadKurz(heim.pfad));
|
|
3118
|
+
pfad.title = heim.pfad;
|
|
3119
|
+
zeile.appendChild(pfad);
|
|
3120
|
+
var knoepfe = e('span', 'heimknoepfe');
|
|
3121
|
+
if (heim.adresse) {
|
|
3122
|
+
var weg = e('button', 'knopf leise klein', 'Vergessen');
|
|
3123
|
+
weg.type = 'button';
|
|
3124
|
+
weg.setAttribute('aria-label', heim.adresse + ' bei ' + harnessName(h) + ' vergessen');
|
|
3125
|
+
weg.addEventListener('click', function () { frageHeimVergessen(h, heim.adresse); });
|
|
3126
|
+
knoepfe.appendChild(weg);
|
|
3127
|
+
} else {
|
|
3128
|
+
var aendern = e('button', 'knopf leise klein', 'Ändern …');
|
|
3129
|
+
aendern.type = 'button';
|
|
3130
|
+
aendern.setAttribute('aria-label', 'Homedir von ' + harnessName(h) + ' ändern');
|
|
3131
|
+
aendern.addEventListener('click', function () { oeffneHeimSetzen(h, heim.pfad); });
|
|
3132
|
+
knoepfe.appendChild(aendern);
|
|
3133
|
+
if (heim.eingestellt) {
|
|
3134
|
+
var zurueck = e('button', 'knopf leise klein', 'Zurücksetzen');
|
|
3135
|
+
zurueck.type = 'button';
|
|
3136
|
+
zurueck.addEventListener('click', function () {
|
|
3137
|
+
rufe(function () { return window.hmgr.harnessHeimZuruecksetzen(h); }).then(async function () {
|
|
3138
|
+
await ladeAlles();
|
|
3139
|
+
melde(harnessName(h) + ' liest sein Homedir wieder am Standardort. Am alten Ort bleibt alles liegen.');
|
|
3140
|
+
});
|
|
3141
|
+
});
|
|
3142
|
+
knoepfe.appendChild(zurueck);
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
zeile.appendChild(knoepfe);
|
|
3146
|
+
kasten.appendChild(zeile);
|
|
3147
|
+
});
|
|
3148
|
+
if (eintrag.kannZusaetzliche && PLATTFORM === 'win32') {
|
|
3149
|
+
var neu = e('button', 'knopf leise klein', 'Homedir in WSL hinzufügen …');
|
|
3150
|
+
neu.type = 'button';
|
|
3151
|
+
neu.addEventListener('click', function () { oeffneHeimHinzufuegen(h); });
|
|
3152
|
+
kasten.appendChild(neu);
|
|
3153
|
+
}
|
|
3154
|
+
return kasten;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
var heimHarness = null;
|
|
3158
|
+
function oeffneHeimSetzen(h, jetzt) {
|
|
3159
|
+
heimHarness = h;
|
|
3160
|
+
$('dhh-titel').textContent = 'Homedir von ' + harnessName(h);
|
|
3161
|
+
$('dhh-pfad').value = jetzt;
|
|
3162
|
+
zeige($('d-heim-pfad'));
|
|
3163
|
+
$('dhh-pfad').focus();
|
|
3164
|
+
}
|
|
3165
|
+
nachDialog($('d-heim-pfad'), async function (wert) {
|
|
3166
|
+
var h = heimHarness;
|
|
3167
|
+
heimHarness = null;
|
|
3168
|
+
if (wert !== 'ok' || !h) return;
|
|
3169
|
+
var pfad = $('dhh-pfad').value.trim();
|
|
3170
|
+
if (!pfad) return;
|
|
3171
|
+
var gesetzt = await rufe(function () { return window.hmgr.harnessHeimSetzen({ harness: h, pfad: pfad }); });
|
|
3172
|
+
await ladeAlles();
|
|
3173
|
+
melde(harnessName(h) + ' liest sein Homedir jetzt unter ' + gesetzt
|
|
3174
|
+
+ '. Was am alten Ort liegt, bleibt dort liegen — hmgr zeigt es nicht mehr.');
|
|
3175
|
+
});
|
|
3176
|
+
|
|
3177
|
+
async function oeffneHeimHinzufuegen(h) {
|
|
3178
|
+
heimHarness = h;
|
|
3179
|
+
var auswahl = $('dhw-distro');
|
|
3180
|
+
auswahl.textContent = '';
|
|
3181
|
+
var distros = await rufe(function () { return window.hmgr.wslDistros(); });
|
|
3182
|
+
(distros || []).forEach(function (name) {
|
|
3183
|
+
var option = document.createElement('option');
|
|
3184
|
+
option.value = name;
|
|
3185
|
+
option.textContent = name;
|
|
3186
|
+
auswahl.appendChild(option);
|
|
3187
|
+
});
|
|
3188
|
+
$('dhw-leer').hidden = (distros || []).length > 0;
|
|
3189
|
+
$('dhw-distro').hidden = (distros || []).length === 0;
|
|
3190
|
+
$('dhw-ok').disabled = (distros || []).length === 0;
|
|
3191
|
+
zeige($('d-heim-wsl'));
|
|
3192
|
+
}
|
|
3193
|
+
nachDialog($('d-heim-wsl'), async function (wert) {
|
|
3194
|
+
var h = heimHarness;
|
|
3195
|
+
heimHarness = null;
|
|
3196
|
+
if (wert !== 'ok' || !h) return;
|
|
3197
|
+
var distro = $('dhw-distro').value;
|
|
3198
|
+
if (!distro) return;
|
|
3199
|
+
var heim = await rufe(function () {
|
|
3200
|
+
return window.hmgr.harnessHeimHinzufuegen({ harness: h, adresse: 'wsl:' + distro });
|
|
3201
|
+
});
|
|
3202
|
+
await ladeAlles();
|
|
3203
|
+
melde(harnessName(h) + ' hat jetzt ein zweites Ziel: ' + heim.adresse + ' mit dem Homedir ' + heim.pfad
|
|
3204
|
+
+ '. Links legst du dort einzeln, gespiegelt wird nichts.');
|
|
3205
|
+
});
|
|
3206
|
+
|
|
3207
|
+
function frageHeimVergessen(h, adresse) {
|
|
3208
|
+
heimHarness = h;
|
|
3209
|
+
$('dhv-titel').textContent = adresse + ' vergessen?';
|
|
3210
|
+
$('dhv-text').textContent = 'hmgr verwaltet dieses Homedir dann nicht mehr und zeigt sein Ziel nicht mehr an. '
|
|
3211
|
+
+ 'Die Links, die dort liegen, bleiben liegen.';
|
|
3212
|
+
$('d-heim-weg').dataset.adresse = adresse;
|
|
3213
|
+
zeige($('d-heim-weg'));
|
|
3214
|
+
}
|
|
3215
|
+
nachDialog($('d-heim-weg'), async function (wert) {
|
|
3216
|
+
var h = heimHarness;
|
|
3217
|
+
heimHarness = null;
|
|
3218
|
+
var adresse = $('d-heim-weg').dataset.adresse;
|
|
3219
|
+
if (wert !== 'ok' || !h || !adresse) return;
|
|
3220
|
+
await rufe(function () { return window.hmgr.harnessHeimEntfernen({ harness: h, adresse: adresse }); });
|
|
3221
|
+
await ladeAlles();
|
|
3222
|
+
melde(harnessName(h) + ' verwaltet ' + adresse + ' nicht mehr. Die Links dort bleiben liegen.');
|
|
3223
|
+
});
|
|
3224
|
+
|
|
3225
|
+
enterBestaetigt($('dhh-pfad'), $('dhh-ok'));
|
|
3226
|
+
|
|
3002
3227
|
function baueHarnessliste() {
|
|
3003
3228
|
var ul = $('harnessliste');
|
|
3004
3229
|
// Der Neuaufbau wirft den Schalter weg, auf dem der Fokus steht. Nach einer Rückfrage ist genau
|
|
@@ -3029,6 +3254,7 @@
|
|
|
3029
3254
|
txt.appendChild(e('small', null, stand));
|
|
3030
3255
|
label.appendChild(txt);
|
|
3031
3256
|
li.appendChild(label);
|
|
3257
|
+
li.appendChild(heimeFuerHarness(h));
|
|
3032
3258
|
ul.appendChild(li);
|
|
3033
3259
|
});
|
|
3034
3260
|
if (merk) {
|
|
@@ -3077,40 +3303,27 @@
|
|
|
3077
3303
|
var liegen = bilanzText(harnessBilanz(h));
|
|
3078
3304
|
await rufe(function () { return window.hmgr.harnessDeaktivieren(h); });
|
|
3079
3305
|
await ladeAlles();
|
|
3080
|
-
|
|
3306
|
+
// E74: die Ziele verschwinden mit dem Harness aus der Ansicht, ein Säubern-Knopf im Ziel
|
|
3307
|
+
// existiert nicht mehr. Was bleibt, ist der Weg über das Wiedereinschalten.
|
|
3308
|
+
melde(harnessName(h) + ' deaktiviert. ' + liegen + ' bleiben liegen, hmgr pflegt sie nicht mehr. Die Ziele verschwinden aus der Liste und erscheinen wieder, sobald du ' + harnessName(h) + ' wieder einschaltest.');
|
|
3081
3309
|
} else {
|
|
3082
3310
|
// Abbruch: der Schalter im DOM steht noch auf aus, die Einstellung nicht. Neu aufbauen setzt ihn zurück.
|
|
3083
3311
|
baueHarnessliste();
|
|
3084
3312
|
}
|
|
3085
3313
|
});
|
|
3086
3314
|
|
|
3087
|
-
/*
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
saeuberKandidat = h;
|
|
3091
|
-
var bilanz = harnessBilanz(h);
|
|
3092
|
-
$('ds-titel').textContent = harnessName(h) + ' säubern?';
|
|
3093
|
-
beschriftet($('ds-titel'), h);
|
|
3094
|
-
$('ds-text').textContent = harnessName(h) + ' ist inaktiv, Konfiguration liegt noch (' + bilanzText(bilanz)
|
|
3095
|
-
+ '). Säubern entfernt die hmgr-Links und harness-eigene Einträge, nicht von hmgr verwaltete Einträge bleiben.';
|
|
3096
|
-
fuelleBilanzListe($('ds-liste'), bilanz);
|
|
3097
|
-
zeige($('d-saeubern'));
|
|
3098
|
-
}
|
|
3099
|
-
nachDialog($('d-saeubern'), async function (wert) {
|
|
3100
|
-
var h = saeuberKandidat;
|
|
3101
|
-
saeuberKandidat = null;
|
|
3102
|
-
if (wert !== 'los' || !h) return;
|
|
3103
|
-
var ergebnis = await rufe(function () { return window.hmgr.saeubern(h); });
|
|
3104
|
-
await ladeAlles();
|
|
3105
|
-
melde(saeuberMeldung(harnessName(h) + ' gesäubert.', ergebnis));
|
|
3106
|
-
});
|
|
3315
|
+
/* Einen eigenen Säubern-Weg für einen schon inaktiven Harness gibt es nicht mehr (E74): sein Ziel
|
|
3316
|
+
steht nicht mehr im Bild, also auch kein Knopf darin. Wer doch säubern will, schaltet den
|
|
3317
|
+
Harness ein und wieder aus — dann fragt der Dialog oben erneut. */
|
|
3107
3318
|
|
|
3108
3319
|
function baueEinstellungen() {
|
|
3109
3320
|
baueHarnessliste();
|
|
3110
3321
|
var liste = $('quellenliste');
|
|
3111
3322
|
liste.textContent = '';
|
|
3112
3323
|
if (QUELLEN.length === 0) {
|
|
3113
|
-
|
|
3324
|
+
// Eigene Klasse statt nacktem Text im Listen-Grid (E75): `.quellenliste li` ist ein Grid mit
|
|
3325
|
+
// 20px-Icon-Spalte, in die der anonyme Textknoten fiel — jedes Wort brach einzeln um.
|
|
3326
|
+
liste.appendChild(e('li', 'leerhinweis', 'Noch keine Quelle. „Quelle hinzufügen …" legt die erste an.'));
|
|
3114
3327
|
return;
|
|
3115
3328
|
}
|
|
3116
3329
|
QUELLEN.forEach(function (q) {
|