hmgr 0.0.44 → 0.0.46
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/zustand-json.js +2 -1
- package/dist/aenderungen.json +6 -0
- package/dist/cli/hilfe.js +10 -3
- package/dist/cli/main.js +35 -10
- package/dist/gui/main.js +83 -15
- package/dist/gui/preload.cjs +2 -0
- package/dist/gui/renderer/renderer.js +338 -92
- package/dist/gui/renderer/styles.css +8 -0
- package/dist/kern/anwendungsfaelle.js +131 -45
- package/package.json +1 -1
|
@@ -659,7 +659,7 @@ export class Anwendungsfaelle {
|
|
|
659
659
|
await this.p.ziele.linkEntfernen(ziel, verzeichnis, name);
|
|
660
660
|
entfernteLinks.push(name);
|
|
661
661
|
}
|
|
662
|
-
if (Anwendungsfaelle.
|
|
662
|
+
if (Anwendungsfaelle.bundleVerweiseFallenLassen(nachZustand, ziel))
|
|
663
663
|
bundlesGeaendert = true;
|
|
664
664
|
const konfigurationZurueckgebaut = kannZurueckbauen(harness) ? await harness.zurueckbauen(ziel.ort) : false;
|
|
665
665
|
zielErgebnisse.push({ ziel, entfernteLinks, konfigurationZurueckgebaut });
|
|
@@ -801,12 +801,12 @@ export class Anwendungsfaelle {
|
|
|
801
801
|
throw new QuelleAufraeumenLokaleAenderungen(inventar.checkoutsMitLokalenAenderungen);
|
|
802
802
|
for (const link of inventar.links)
|
|
803
803
|
await this.p.ziele.linkEntfernen(link.ziel, link.verzeichnis, link.name);
|
|
804
|
-
// Wo dabei ein Bundle-Mitglied fiel,
|
|
805
|
-
//
|
|
804
|
+
// Wo dabei ein Bundle-Mitglied fiel, gilt es als herausgenommen (E114), wie beim einzelnen
|
|
805
|
+
// `linkEntfernen`: das Bundle bleibt, der Abgleich legt das Mitglied nicht wieder an.
|
|
806
806
|
const zustand = await this.p.zustand.lesen();
|
|
807
807
|
let bundlesGeaendert = false;
|
|
808
808
|
for (const link of inventar.links)
|
|
809
|
-
if (Anwendungsfaelle.
|
|
809
|
+
if (Anwendungsfaelle.bundleMitgliederHerausnehmen(zustand, link.ziel, [link.name]).length > 0)
|
|
810
810
|
bundlesGeaendert = true;
|
|
811
811
|
if (bundlesGeaendert)
|
|
812
812
|
await this.p.zustand.schreiben(zustand);
|
|
@@ -1122,8 +1122,11 @@ export class Anwendungsfaelle {
|
|
|
1122
1122
|
}
|
|
1123
1123
|
return angelegt;
|
|
1124
1124
|
}
|
|
1125
|
-
/**
|
|
1126
|
-
|
|
1125
|
+
/**
|
|
1126
|
+
* Die Zugehörigkeit der Links eines Bundles im Ziel merken (E69), einen alten Eintrag ersetzend.
|
|
1127
|
+
* `herausgenommen` (E114) wird nur geschrieben, wenn es etwas enthält.
|
|
1128
|
+
*/
|
|
1129
|
+
async bundleInstallationMerken(ziel, bundle, skills, herausgenommen = []) {
|
|
1127
1130
|
const zustand = await this.p.zustand.lesen();
|
|
1128
1131
|
const schluessel = zielSchluessel(ziel);
|
|
1129
1132
|
const eigener = bundleSchluessel(bundle);
|
|
@@ -1135,27 +1138,43 @@ export class Anwendungsfaelle {
|
|
|
1135
1138
|
bundle: bundle.name,
|
|
1136
1139
|
quelle: bundle.quelle,
|
|
1137
1140
|
skills,
|
|
1141
|
+
...(herausgenommen.length === 0 ? {} : { herausgenommen }),
|
|
1138
1142
|
});
|
|
1139
1143
|
await this.p.zustand.schreiben(zustand);
|
|
1140
1144
|
}
|
|
1141
1145
|
/**
|
|
1142
|
-
* Zerbrechen
|
|
1143
|
-
* Bundle
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
+
* Herausnehmen (E114 L2, ersetzt das Zerbrechen aus E69): Wird ein einzelner Link entfernt, der
|
|
1147
|
+
* zu einem Bundle gehört, bleibt das Bundle im Ziel und merkt sich das Mitglied als herausgenommen
|
|
1148
|
+
* — auch wenn danach keines mehr verlinkt ist („0 von N", weiter entfernbar). Ändert nur den
|
|
1149
|
+
* übergebenen Zustand, geschrieben wird er vom Aufrufer. Gibt die Namen der betroffenen Bundles
|
|
1150
|
+
* zurück, damit die Oberfläche es sagen kann.
|
|
1146
1151
|
*/
|
|
1147
|
-
static
|
|
1152
|
+
static bundleMitgliederHerausnehmen(zustand, ziel, namen) {
|
|
1148
1153
|
const schluessel = zielSchluessel(ziel);
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1154
|
+
const betroffen = [];
|
|
1155
|
+
for (const installation of zustand.bundleInstallationen) {
|
|
1151
1156
|
if (zielSchluessel(Anwendungsfaelle.installationZiel(installation)) !== schluessel)
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1157
|
+
continue;
|
|
1158
|
+
const raus = installation.skills.filter((name) => namen.includes(name));
|
|
1159
|
+
if (raus.length === 0)
|
|
1160
|
+
continue;
|
|
1161
|
+
installation.skills = installation.skills.filter((name) => !raus.includes(name));
|
|
1162
|
+
installation.herausgenommen = [...(installation.herausgenommen ?? []), ...raus];
|
|
1163
|
+
betroffen.push(installation.bundle);
|
|
1164
|
+
}
|
|
1165
|
+
return betroffen;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Bundle-Verweise eines Ziels fallen lassen, wenn „Säubern" dort alle Links abbaut (E69). Kein
|
|
1169
|
+
* Herausnehmen: Säubern räumt den Harness ab, ein Bundle mit „0 von N" bliebe ein Rest, den der
|
|
1170
|
+
* Nutzer gerade loswerden wollte. Darum fallen alle Verweise des Ziels, auch die von Bundles, von
|
|
1171
|
+
* denen gerade kein Link mehr lag.
|
|
1172
|
+
*/
|
|
1173
|
+
static bundleVerweiseFallenLassen(zustand, ziel) {
|
|
1174
|
+
const schluessel = zielSchluessel(ziel);
|
|
1175
|
+
const vorher = zustand.bundleInstallationen.length;
|
|
1176
|
+
zustand.bundleInstallationen = zustand.bundleInstallationen.filter((installation) => zielSchluessel(Anwendungsfaelle.installationZiel(installation)) !== schluessel);
|
|
1177
|
+
return zustand.bundleInstallationen.length !== vorher;
|
|
1159
1178
|
}
|
|
1160
1179
|
/**
|
|
1161
1180
|
* Bundle installieren (E69): das Bundle als Ganzes in ein Ziel. Erst wird alles geprüft — jedes
|
|
@@ -1205,9 +1224,11 @@ export class Anwendungsfaelle {
|
|
|
1205
1224
|
return treffer[0];
|
|
1206
1225
|
}
|
|
1207
1226
|
/**
|
|
1208
|
-
* Bundle entfernen (E69): alle seine
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1227
|
+
* Bundle entfernen (E69): alle seine noch verlinkten Mitglieder im Ziel und den Bundle-Verweis,
|
|
1228
|
+
* auch nach Teil-Löschung (E114 L2): herausgenommene stehen nicht in `skills` und bleiben
|
|
1229
|
+
* unberührt, ein Link gleichen Namens an ihrer Stelle gehört nicht dem Bundle. Entfernt wird
|
|
1230
|
+
* direkt über den Ziele-Port, nicht über den Anwendungsfall `linkEntfernen` — der macht
|
|
1231
|
+
* Zeiger-Arbeit (E57) und nähme jedes Mitglied einzeln heraus.
|
|
1211
1232
|
*/
|
|
1212
1233
|
async bundleEntfernen(args) {
|
|
1213
1234
|
const harness = await this.harnessGeprueft(args.ziel.harness);
|
|
@@ -1237,6 +1258,11 @@ export class Anwendungsfaelle {
|
|
|
1237
1258
|
* Wirkt auf das Bundle als Ganzes: Lässt sich eine Definitionsänderung nicht vollständig nachziehen
|
|
1238
1259
|
* (Quelle weg, Pfad kein Skill mehr, Namenskonflikt), bleibt das Bundle im betroffenen Ziel auf dem
|
|
1239
1260
|
* alten Stand und der Lauf meldet es, statt es halb nachzuziehen (F66b).
|
|
1261
|
+
*
|
|
1262
|
+
* Herausgenommene Mitglieder (E114 L5) lässt der Abgleich aus: kein Link, keine Konfliktprüfung
|
|
1263
|
+
* (ein anderer Link gleichen Namens an ihrer Stelle ist dann kein Konflikt). Fällt eines aus der
|
|
1264
|
+
* Definition, fällt es auch aus der Liste. `aufnehmen` holt herausgenommene zurück: 'alle' ist
|
|
1265
|
+
* „Vervollständigen" (L4), eine Namensliste „Wieder verlinken" einzelner Mitglieder (L3).
|
|
1240
1266
|
*/
|
|
1241
1267
|
async bundleAbgleich(args) {
|
|
1242
1268
|
const zustand = await this.p.zustand.lesen();
|
|
@@ -1256,7 +1282,10 @@ export class Anwendungsfaelle {
|
|
|
1256
1282
|
// ein Fehlschlag (kein Netz) arbeitet mit dem vorhandenen Stand weiter.
|
|
1257
1283
|
await this.refsNachziehen(installation.quelle);
|
|
1258
1284
|
const bundle = await this.bundleAusQuelle(installation.bundle, installation.quelle);
|
|
1259
|
-
const { funde, neue } = await this.bundleAufloesen(bundle);
|
|
1285
|
+
const { funde: alle, neue } = await this.bundleAufloesen(bundle);
|
|
1286
|
+
const aufnehmen = args?.aufnehmen;
|
|
1287
|
+
const herausgenommen = (installation.herausgenommen ?? []).filter((name) => aufnehmen !== 'alle' && !(aufnehmen ?? []).includes(name) && alle.some((f) => f.skill.name === name));
|
|
1288
|
+
const funde = alle.filter((f) => !herausgenommen.includes(f.skill.name));
|
|
1260
1289
|
const verzeichnis = harness.zielVerzeichnis(ziel.ort);
|
|
1261
1290
|
const { konflikte } = await this.bundleKonflikte(bundle, ziel, verzeichnis, funde);
|
|
1262
1291
|
if (konflikte.length > 0)
|
|
@@ -1272,7 +1301,7 @@ export class Anwendungsfaelle {
|
|
|
1272
1301
|
await this.p.ziele.linkEntfernen(ziel, verzeichnis, name);
|
|
1273
1302
|
entfernt.push(name);
|
|
1274
1303
|
}
|
|
1275
|
-
await this.bundleInstallationMerken(ziel, bundle, namen);
|
|
1304
|
+
await this.bundleInstallationMerken(ziel, bundle, namen, herausgenommen);
|
|
1276
1305
|
const gelegt = namen.filter((n) => !installation.skills.includes(n));
|
|
1277
1306
|
ergebnis.push({
|
|
1278
1307
|
...kopf,
|
|
@@ -1486,7 +1515,8 @@ export class Anwendungsfaelle {
|
|
|
1486
1515
|
const version = args.version;
|
|
1487
1516
|
if (version === undefined) {
|
|
1488
1517
|
const fund = await this.skillFinden(args.skill, args.quelle);
|
|
1489
|
-
|
|
1518
|
+
const ergebnis = await this.linkAusFund(harness, args.ziel, fund, args.ersetzen, args.lokal);
|
|
1519
|
+
return Anwendungsfaelle.hinweisAnhaengen(ergebnis, await this.bundleMitgliedZurueckholen(args.ziel, fund));
|
|
1490
1520
|
}
|
|
1491
1521
|
if (args.lokal === true || args.ziel.ort.art !== 'arbeitsverzeichnis')
|
|
1492
1522
|
throw new EinschraenkungOhneZeiger(args.ziel, version);
|
|
@@ -1500,7 +1530,57 @@ export class Anwendungsfaelle {
|
|
|
1500
1530
|
const link = await this.linkAnlegen(harness, args.ziel, fund, args.ersetzen);
|
|
1501
1531
|
await this.zeigerErgaenzen(zeigerPfad, fund, version);
|
|
1502
1532
|
const hinweis = await this.harnessHinweis(harness, args.ziel.ort);
|
|
1503
|
-
return hinweis === undefined ? link : { ...link, hinweis };
|
|
1533
|
+
return Anwendungsfaelle.hinweisAnhaengen(hinweis === undefined ? link : { ...link, hinweis }, await this.bundleMitgliedZurueckholen(args.ziel, fund));
|
|
1534
|
+
}
|
|
1535
|
+
/** Weitere Hinweiszeilen an ein Link-Ergebnis hängen, ohne einen vorhandenen Hinweis zu verdrängen. */
|
|
1536
|
+
static hinweisAnhaengen(ergebnis, zeilen) {
|
|
1537
|
+
if (zeilen.length === 0)
|
|
1538
|
+
return ergebnis;
|
|
1539
|
+
const alle = ergebnis.hinweis === undefined ? zeilen : [ergebnis.hinweis, ...zeilen];
|
|
1540
|
+
return { ...ergebnis, hinweis: alle.join('\n') };
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* `link` eines herausgenommenen Bundle-Mitglieds (E114 L6): Ist es derselbe Skill aus derselben
|
|
1544
|
+
* Quelle, wie ihn die Definition nennt (Quelle und Pfad, dieselbe Probe wie die Übernahme in
|
|
1545
|
+
* F67), gehört er wieder zum Bundle. Die Definition wird nur gelesen, wenn der Name in diesem
|
|
1546
|
+
* Ziel herausgenommen ist; lässt sie sich nicht lesen, bleibt es beim Einzel-Link. Gibt je
|
|
1547
|
+
* zurückgeholtem Bundle eine Hinweiszeile zurück.
|
|
1548
|
+
*/
|
|
1549
|
+
async bundleMitgliedZurueckholen(ziel, fund) {
|
|
1550
|
+
const name = fund.skill.name;
|
|
1551
|
+
const kandidaten = Anwendungsfaelle.installationenAn(await this.p.zustand.lesen(), ziel).filter((i) => (i.herausgenommen ?? []).includes(name));
|
|
1552
|
+
if (kandidaten.length === 0)
|
|
1553
|
+
return [];
|
|
1554
|
+
const passend = [];
|
|
1555
|
+
for (const installation of kandidaten) {
|
|
1556
|
+
try {
|
|
1557
|
+
const bundle = await this.bundleAusQuelle(installation.bundle, installation.quelle);
|
|
1558
|
+
const gleich = bundle.mitglieder.some((m) => quelleSchluessel(m.quelle) === quelleSchluessel(fund.quelle) && m.pfad === fund.skill.pfad);
|
|
1559
|
+
if (gleich)
|
|
1560
|
+
passend.push(bundleSchluessel(bundle));
|
|
1561
|
+
}
|
|
1562
|
+
catch {
|
|
1563
|
+
// Definition nicht lesbar: der Link bleibt ein Einzel-Link, das Mitglied herausgenommen.
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (passend.length === 0)
|
|
1567
|
+
return [];
|
|
1568
|
+
const zustand = await this.p.zustand.lesen();
|
|
1569
|
+
const zeilen = [];
|
|
1570
|
+
for (const installation of Anwendungsfaelle.installationenAn(zustand, ziel)) {
|
|
1571
|
+
if (!passend.includes(bundleSchluessel({ name: installation.bundle, quelle: installation.quelle })))
|
|
1572
|
+
continue;
|
|
1573
|
+
const rest = (installation.herausgenommen ?? []).filter((n) => n !== name);
|
|
1574
|
+
if (rest.length === 0)
|
|
1575
|
+
delete installation.herausgenommen;
|
|
1576
|
+
else
|
|
1577
|
+
installation.herausgenommen = rest;
|
|
1578
|
+
if (!installation.skills.includes(name))
|
|
1579
|
+
installation.skills.push(name);
|
|
1580
|
+
zeilen.push(`„${name}" gehört wieder zum Bundle „${installation.bundle}".`);
|
|
1581
|
+
}
|
|
1582
|
+
await this.p.zustand.schreiben(zustand);
|
|
1583
|
+
return zeilen;
|
|
1504
1584
|
}
|
|
1505
1585
|
/** Link legen, wenn Quelle, Ref und Skill schon feststehen. Die Zeiger-Arbeit ist dieselbe. */
|
|
1506
1586
|
async linkAusFund(harness, ziel, fund, ersetzen, lokal) {
|
|
@@ -1553,9 +1633,9 @@ export class Anwendungsfaelle {
|
|
|
1553
1633
|
* Link entfernen. Ohne `lokal` verschwindet der Skill auch aus dem Zeiger (E17). Mit `lokal` an
|
|
1554
1634
|
* einem gebundenen Ziel bleibt der Zeiger unangetastet, ein Hinweis sagt das (E57, F32).
|
|
1555
1635
|
*
|
|
1556
|
-
* Gehörte der Link zu einem Bundle,
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1636
|
+
* Gehörte der Link zu einem Bundle, bleibt das Bundle im Ziel und merkt sich den Skill als
|
|
1637
|
+
* herausgenommen (E114 L2). `herausgenommenAus` nennt die Bundles, damit die Oberfläche es sagen
|
|
1638
|
+
* kann.
|
|
1559
1639
|
*/
|
|
1560
1640
|
async linkEntfernen(args) {
|
|
1561
1641
|
const harness = await this.harnessGeprueft(args.ziel.harness);
|
|
@@ -1563,25 +1643,25 @@ export class Anwendungsfaelle {
|
|
|
1563
1643
|
await this.zeigerSchreibenPruefen(args.ziel, args.lokal);
|
|
1564
1644
|
await this.p.ziele.linkEntfernen(args.ziel, harness.zielVerzeichnis(args.ziel.ort), args.skill);
|
|
1565
1645
|
const zustand = await this.p.zustand.lesen();
|
|
1566
|
-
const
|
|
1567
|
-
if (
|
|
1646
|
+
const herausgenommenAus = Anwendungsfaelle.bundleMitgliederHerausnehmen(zustand, args.ziel, [args.skill]);
|
|
1647
|
+
if (herausgenommenAus.length > 0)
|
|
1568
1648
|
await this.p.zustand.schreiben(zustand);
|
|
1569
1649
|
if (args.lokal === true && args.ziel.ort.art === 'arbeitsverzeichnis') {
|
|
1570
1650
|
const zeigerPfad = await this.bindungFinden(args.ziel);
|
|
1571
1651
|
if (zeigerPfad !== undefined)
|
|
1572
|
-
return { hinweis: 'Der Link ist nur lokal entfernt, der Zeiger bleibt unangetastet (F32).',
|
|
1573
|
-
return {
|
|
1652
|
+
return { hinweis: 'Der Link ist nur lokal entfernt, der Zeiger bleibt unangetastet (F32).', herausgenommenAus };
|
|
1653
|
+
return { herausgenommenAus };
|
|
1574
1654
|
}
|
|
1575
1655
|
if (args.lokal === true || args.ziel.ort.art !== 'arbeitsverzeichnis')
|
|
1576
|
-
return {
|
|
1656
|
+
return { herausgenommenAus };
|
|
1577
1657
|
const zeigerPfad = await this.bindungFinden(args.ziel);
|
|
1578
1658
|
if (zeigerPfad === undefined)
|
|
1579
|
-
return {
|
|
1659
|
+
return { herausgenommenAus };
|
|
1580
1660
|
const zeiger = await this.p.zeiger.lesen(zeigerPfad);
|
|
1581
1661
|
if (!zeiger.skills.delete(args.skill))
|
|
1582
|
-
return {
|
|
1662
|
+
return { herausgenommenAus };
|
|
1583
1663
|
await this.p.zeiger.schreiben(zeigerPfad, zeiger);
|
|
1584
|
-
return {
|
|
1664
|
+
return { herausgenommenAus };
|
|
1585
1665
|
}
|
|
1586
1666
|
/**
|
|
1587
1667
|
* Ignoriert einen fremden Eintrag (E43): idempotent, kein Duplikat. Gleichheit über harness, Ort
|
|
@@ -2580,14 +2660,20 @@ export class Anwendungsfaelle {
|
|
|
2580
2660
|
for (const ei of eintraege)
|
|
2581
2661
|
if (ei.art === 'fremd')
|
|
2582
2662
|
uebernahme.set(ei.name, await this.erkennenFremd(harness, verzeichnis, ei, bekannt, true));
|
|
2583
|
-
// Bundles dieses Ziels (E69), rein aus dem Zustand:
|
|
2584
|
-
// wirklich noch liegen
|
|
2663
|
+
// Bundles dieses Ziels (E69), rein aus dem Zustand: verlinkt sind die Mitglieder, die dort
|
|
2664
|
+
// auch wirklich noch liegen; herausgenommene und von Hand gelöschte stehen als nicht
|
|
2665
|
+
// verlinkt daneben (E114 L3), die Gruppe zeigt alle.
|
|
2585
2666
|
const eigeneNamen = new Set(eintraege.filter((ei) => ei.art !== 'fremd').map((ei) => (ei.art === 'link' ? ei.link.name : ei.name)));
|
|
2586
|
-
const bundles = Anwendungsfaelle.installationenAn(zustand, ziel).map((i) =>
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2667
|
+
const bundles = Anwendungsfaelle.installationenAn(zustand, ziel).map((i) => {
|
|
2668
|
+
const herausgenommen = i.herausgenommen ?? [];
|
|
2669
|
+
return {
|
|
2670
|
+
name: i.bundle,
|
|
2671
|
+
quelle: i.quelle,
|
|
2672
|
+
skills: i.skills.filter((name) => eigeneNamen.has(name)),
|
|
2673
|
+
nichtVerlinkt: [...i.skills.filter((name) => !eigeneNamen.has(name)), ...herausgenommen],
|
|
2674
|
+
herausgenommen,
|
|
2675
|
+
};
|
|
2676
|
+
});
|
|
2591
2677
|
ergebnis.push({
|
|
2592
2678
|
ziel,
|
|
2593
2679
|
verzeichnis,
|