hmgr 0.0.12 → 0.0.15
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/npm-registry.js +16 -9
- package/dist/gui/hmgr-cli.js +11 -8
- package/dist/gui/main.js +107 -13
- package/dist/gui/renderer/index.html +5 -1
- package/dist/gui/renderer/renderer.js +161 -8
- package/dist/gui/renderer/styles.css +17 -0
- package/package.json +1 -1
|
@@ -42,20 +42,27 @@ export async function npmVersion(paket, optionen = {}) {
|
|
|
42
42
|
clearTimeout(wecker);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
/** Die eine Zeile, die GUI-Sidebar und `hmgr update` gleichlautend zeigen. */
|
|
46
|
-
export function cliHinweisText(installiert, neu) {
|
|
47
|
-
return `hmgr-CLI ${installiert} — npm hat ${neu}: ${NPM_BEFEHL}`;
|
|
48
|
-
}
|
|
49
45
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
46
|
+
* Die eine Zeile, die GUI-Sidebar und `hmgr update` gleichlautend zeigen. `installiert === null`
|
|
47
|
+
* heißt: eine hmgr-CLI ist da, aber so alt (vor 0.0.7, E72), dass sie ihre Version nicht sagen
|
|
48
|
+
* kann — dieser Fall meldet sich auch ohne `neu` (Registry unerreichbar), denn die Warnung „veraltet"
|
|
49
|
+
* hängt nicht am Netz, nur der Vergleich mit npm tut das.
|
|
53
50
|
*/
|
|
51
|
+
export function cliHinweisText(installiert, neu) {
|
|
52
|
+
if (installiert !== null && neu !== null)
|
|
53
|
+
return `hmgr-CLI ${installiert} — npm hat ${neu}: ${NPM_BEFEHL}`;
|
|
54
|
+
if (neu !== null)
|
|
55
|
+
return `hmgr-CLI veraltet (vor 0.0.7, kennt --version nicht) — npm hat ${neu}: ${NPM_BEFEHL}`;
|
|
56
|
+
return `hmgr-CLI veraltet (vor 0.0.7, kennt --version nicht): ${NPM_BEFEHL}`;
|
|
57
|
+
}
|
|
54
58
|
export async function cliUpdateHinweis(installiert, optionen = {}) {
|
|
55
59
|
const neu = await npmVersion('hmgr', optionen);
|
|
60
|
+
if (installiert === null) {
|
|
61
|
+
// `text` für die CLI (eine Zeile), die Einzelteile für die GUI, die den Befehl hervorhebt. Der
|
|
62
|
+
// Befehl kommt mit, damit er nicht ein zweites Mal im Renderer steht.
|
|
63
|
+
return { installiert: null, neu, befehl: NPM_BEFEHL, text: cliHinweisText(null, neu) };
|
|
64
|
+
}
|
|
56
65
|
if (neu === null || !versionIstNeuer(neu, installiert))
|
|
57
66
|
return null;
|
|
58
|
-
// `text` für die CLI (eine Zeile), die Einzelteile für die GUI, die den Befehl hervorhebt. Der
|
|
59
|
-
// Befehl kommt mit, damit er nicht ein zweites Mal im Renderer steht.
|
|
60
67
|
return { installiert, neu, befehl: NPM_BEFEHL, text: cliHinweisText(installiert, neu) };
|
|
61
68
|
}
|
package/dist/gui/hmgr-cli.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
import spawn from 'cross-spawn';
|
|
5
5
|
import { versionLesen } from "../kern/version.js";
|
|
6
6
|
/**
|
|
7
|
-
* `hmgr --version` (E72: nur die Nummer, Exit 0).
|
|
8
|
-
*
|
|
9
|
-
* eine fehlende CLI ist der Normalfall bei einer reinen GUI-Installation, keine Störung.
|
|
7
|
+
* `hmgr --version` (E72: nur die Nummer, Exit 0). Kein Fehler dringt nach außen — der Aufrufer
|
|
8
|
+
* entscheidet anhand des Befunds, was zu melden ist.
|
|
10
9
|
*
|
|
11
10
|
* `spawn` von cross-spawn wie im claude-Adapter (E65/B7): unter Windows ist die npm-installierte
|
|
12
11
|
* `hmgr` ein `.cmd`-Shim, das Node seit dem Fix zu CVE-2024-27980 ohne Umweg über cmd.exe nicht
|
|
13
|
-
* mehr startet. stdin zu und ein Timeout, damit nichts hängen bleibt
|
|
12
|
+
* mehr startet. stdin zu und ein Timeout, damit nichts hängen bleibt — ein Timeout liefert `fehlt`
|
|
13
|
+
* (cross-spawn beendet den Prozess dann mit `close` und `null`-Code samt Signal, kein `error`).
|
|
14
14
|
*/
|
|
15
15
|
export function hmgrCliVersion() {
|
|
16
16
|
return new Promise((aufloesen) => {
|
|
@@ -29,16 +29,19 @@ export function hmgrCliVersion() {
|
|
|
29
29
|
});
|
|
30
30
|
let stdout = '';
|
|
31
31
|
kind.stdout.on('data', (stueck) => (stdout += stueck));
|
|
32
|
-
kind.on('error', () => melden(
|
|
32
|
+
kind.on('error', () => melden({ art: 'fehlt' }));
|
|
33
33
|
kind.on('close', (code) => {
|
|
34
|
+
if (code === null)
|
|
35
|
+
return melden({ art: 'fehlt' });
|
|
34
36
|
if (code !== 0)
|
|
35
|
-
return melden(
|
|
37
|
+
return melden({ art: 'ohne-version' });
|
|
36
38
|
const text = stdout.trim().split('\n')[0]?.trim() ?? '';
|
|
37
|
-
|
|
39
|
+
const version = versionLesen(text);
|
|
40
|
+
melden(version === null ? { art: 'ohne-version' } : { art: 'version', version: text });
|
|
38
41
|
});
|
|
39
42
|
}
|
|
40
43
|
catch {
|
|
41
|
-
melden(
|
|
44
|
+
melden({ art: 'fehlt' });
|
|
42
45
|
}
|
|
43
46
|
});
|
|
44
47
|
}
|
package/dist/gui/main.js
CHANGED
|
@@ -24,6 +24,10 @@ import { hmgrCliVersion } from "./hmgr-cli.js";
|
|
|
24
24
|
import { cliUpdateHinweis } from "../adapter/npm-registry.js";
|
|
25
25
|
const hierVerzeichnis = path.dirname(fileURLToPath(import.meta.url));
|
|
26
26
|
const smoketest = process.env.HMGR_GUI_SMOKETEST === '1';
|
|
27
|
+
// E84 Teil 1: Pfad eines im Fixture noch unbekannten Verzeichnisses, das der Smoke-Lauf per Dialog
|
|
28
|
+
// hinzufügt, um den Sprung zur neuen Karte zu belegen. Nur zur Konstruktionszeit des DOM-Auszug-
|
|
29
|
+
// Skripts gelesen (Node-Seite), nicht zur Laufzeit im Renderer.
|
|
30
|
+
const smoketestNeuesVerzeichnis = process.env.HMGR_SMOKETEST_NEUES_VERZEICHNIS ?? '';
|
|
27
31
|
/**
|
|
28
32
|
* Nur eine hmgr-Instanz je Nutzer (E67). Grund: zwei laufende Fenster schrieben nacheinander auf
|
|
29
33
|
* dieselbe `state.json` und überschrieben sich gegenseitig — der Zustand ist einer, nicht einer je
|
|
@@ -945,6 +949,67 @@ function fensterOeffnen() {
|
|
|
945
949
|
} catch (fehler) {
|
|
946
950
|
dmhFehler = String(fehler);
|
|
947
951
|
}
|
|
952
|
+
// E84 Teil 2: Suchfeld über der Zielliste filtert Agent- und Verzeichnis-Karten. Erst
|
|
953
|
+
// zurück auf die Ziele-Ansicht (der Lauf steht seit der MCP-Prüfung oben auf „mcp").
|
|
954
|
+
document.querySelector('[data-ansicht="ziele"]')?.click();
|
|
955
|
+
var zielsucheVorhanden = document.getElementById('zielsuche') !== null;
|
|
956
|
+
var setzeZielsuche = function (wert) {
|
|
957
|
+
var feld = document.getElementById('zielsuche');
|
|
958
|
+
if (!feld) return;
|
|
959
|
+
feld.value = wert;
|
|
960
|
+
feld.dispatchEvent(new Event('input', { bubbles: true }));
|
|
961
|
+
};
|
|
962
|
+
// „kunde-x" trifft im Fixture nur die Verzeichnis-Karte, kein Agent-Titel heißt so.
|
|
963
|
+
setzeZielsuche('kunde-x');
|
|
964
|
+
var zielzahlNachSuche = document.getElementById('zielzahl')?.textContent ?? null;
|
|
965
|
+
var zielKartenNachSucheZahl = document.querySelectorAll('#zielliste .ziel').length;
|
|
966
|
+
var agentsKopfNachSucheVorhanden = Array.from(document.querySelectorAll('#zielliste .gruppenkopf'))
|
|
967
|
+
.some(function (h) { return h.textContent === 'Agents'; });
|
|
968
|
+
setzeZielsuche('');
|
|
969
|
+
var zielzahlNachLeeren = document.getElementById('zielzahl')?.textContent ?? null;
|
|
970
|
+
var zielKartenNachLeerenZahl = document.querySelectorAll('#zielliste .ziel').length;
|
|
971
|
+
// Nachbesserung E84 Teil 2: die Suche muss auch die gekürzte Schreibweise (pfadKurz)
|
|
972
|
+
// einer Agent-Karte treffen, nicht nur ihren rohen Pfad — genau das steht im
|
|
973
|
+
// Kartenkopf. „~/.hermes" trifft im Fixture „Hermes · Home" und „Hermes · Profil
|
|
974
|
+
// „researcher““ (beide unter ~/.hermes/…), aber weder Claude Code noch Agents noch
|
|
975
|
+
// die Verzeichnis-Karte „kunde-x".
|
|
976
|
+
setzeZielsuche('~/.hermes');
|
|
977
|
+
var zielzahlNachSucheKurz = document.getElementById('zielzahl')?.textContent ?? null;
|
|
978
|
+
var zielKartenNachSucheKurzTitel = Array.from(document.querySelectorAll('#zielliste .ziel h3'))
|
|
979
|
+
.map(function (h) { return h.textContent; }).join('|');
|
|
980
|
+
var kundeXNachSucheKurzVorhanden = zielKartenNachSucheKurzTitel.indexOf('kunde-x') > -1;
|
|
981
|
+
setzeZielsuche('');
|
|
982
|
+
// E84 Teil 1: ein neues Arbeitsverzeichnis über den Dialog anlegen, danach muss seine
|
|
983
|
+
// Karte anspringen (aufgeklappt, kurz hervorgehoben). Der Pfad kommt aus dem Fixture
|
|
984
|
+
// (leer, wenn keiner mitgegeben wurde, dann bleibt dieser Block ein No-Op).
|
|
985
|
+
var sprungNeuesVerzeichnis = ${JSON.stringify(smoketestNeuesVerzeichnis)};
|
|
986
|
+
var sprungFehler = null;
|
|
987
|
+
var sprungKarteVorhanden = false;
|
|
988
|
+
var sprungKarteExpanded = null;
|
|
989
|
+
var sprungKarteHervorgehoben = null;
|
|
990
|
+
try {
|
|
991
|
+
if (sprungNeuesVerzeichnis) {
|
|
992
|
+
document.getElementById('verzneu')?.click();
|
|
993
|
+
var dvPfadFeld = document.getElementById('dv-pfad');
|
|
994
|
+
dvPfadFeld.value = sprungNeuesVerzeichnis;
|
|
995
|
+
document.querySelector('#d-verzeichnis button[value="los"]')?.click();
|
|
996
|
+
// arbeitsverzeichnisHinzufuegen läuft über IPC, dann ladeAlles: kurz nachfassen.
|
|
997
|
+
var sprungKarte = null;
|
|
998
|
+
for (var wartezeitSprung = 0; wartezeitSprung < 3000; wartezeitSprung += 100) {
|
|
999
|
+
await new Promise(function (r) { setTimeout(r, 100); });
|
|
1000
|
+
sprungKarte = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
1001
|
+
.find(function (k) { return (k.querySelector('.zielkopf h3')?.textContent || '').indexOf('neu-verzeichnis') > -1; });
|
|
1002
|
+
if (sprungKarte) break;
|
|
1003
|
+
}
|
|
1004
|
+
if (sprungKarte) {
|
|
1005
|
+
sprungKarteVorhanden = true;
|
|
1006
|
+
sprungKarteExpanded = sprungKarte.querySelector('.zielkopf')?.getAttribute('aria-expanded') ?? null;
|
|
1007
|
+
sprungKarteHervorgehoben = sprungKarte.classList.contains('ziel-neu');
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
} catch (fehler) {
|
|
1011
|
+
sprungFehler = String(fehler);
|
|
1012
|
+
}
|
|
948
1013
|
// Schriften aus fonts/ (Zusatzpunkt 6): erzwingt das Laden und belegt es danach.
|
|
949
1014
|
await Promise.all([
|
|
950
1015
|
document.fonts.load('400 16px Inter'),
|
|
@@ -1074,6 +1139,19 @@ function fensterOeffnen() {
|
|
|
1074
1139
|
verlaufVorhanden,
|
|
1075
1140
|
verlaufText,
|
|
1076
1141
|
verlaufBloecke,
|
|
1142
|
+
zielsucheVorhanden,
|
|
1143
|
+
zielzahlNachSuche,
|
|
1144
|
+
zielKartenNachSucheZahl,
|
|
1145
|
+
agentsKopfNachSucheVorhanden,
|
|
1146
|
+
zielzahlNachLeeren,
|
|
1147
|
+
zielKartenNachLeerenZahl,
|
|
1148
|
+
zielzahlNachSucheKurz,
|
|
1149
|
+
zielKartenNachSucheKurzTitel,
|
|
1150
|
+
kundeXNachSucheKurzVorhanden,
|
|
1151
|
+
sprungFehler,
|
|
1152
|
+
sprungKarteVorhanden,
|
|
1153
|
+
sprungKarteExpanded,
|
|
1154
|
+
sprungKarteHervorgehoben,
|
|
1077
1155
|
});
|
|
1078
1156
|
})()`)
|
|
1079
1157
|
.then((auszug) => console.log(`[smoke] DOM-Auszug: ${auszug}`))
|
|
@@ -1182,24 +1260,40 @@ function selbstUpdateMelden(nachricht) {
|
|
|
1182
1260
|
* nie installiert: ein `npm install -g` aus der App scheitert leicht an nvm-Pfaden und Rechten
|
|
1183
1261
|
* (F82).
|
|
1184
1262
|
*
|
|
1185
|
-
* Still bei
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1263
|
+
* Still bei „fehlt": keine CLI im PATH, das ist der Normalfall bei einer reinen GUI-Installation.
|
|
1264
|
+
* Bei „version" wie bisher still bei Fehlercode, Registry unerreichbar, gleicher oder älterer
|
|
1265
|
+
* Version auf npm. Bei „ohne-version" (E82: CLI vor 0.0.7, kennt --version nicht) gibt es IMMER
|
|
1266
|
+
* eine Sidebar-Meldung — sie hängt nicht am Netz —, der Verlaufseintrag aber nur, wenn npm
|
|
1267
|
+
* erreichbar war und eine Version lieferte. Der Login-PATH (F47) muss vorher stehen, sonst findet
|
|
1268
|
+
* der Aufruf die per nvm installierte CLI nicht.
|
|
1188
1269
|
*/
|
|
1189
1270
|
async function cliUpdatePruefen() {
|
|
1190
1271
|
await pathBereit;
|
|
1191
|
-
const
|
|
1192
|
-
if (
|
|
1272
|
+
const befund = await hmgrCliVersion();
|
|
1273
|
+
if (befund.art === 'fehlt')
|
|
1193
1274
|
return;
|
|
1194
|
-
|
|
1195
|
-
|
|
1275
|
+
if (befund.art === 'version') {
|
|
1276
|
+
const hinweis = await cliUpdateHinweis(befund.version, { protokoll: (zeile) => console.log(zeile) });
|
|
1277
|
+
if (hinweis === null)
|
|
1278
|
+
return;
|
|
1279
|
+
// Verlaufseintrag „CLI X verfügbar" (E80), einmal je neuer Version, vor der Sidebar-Meldung.
|
|
1280
|
+
await anwendungsfaelle
|
|
1281
|
+
.hmgrEreignisVermerken({ art: 'cli-verfuegbar', version: hinweis.neu }, letzterAusloeser)
|
|
1282
|
+
.catch((fehler) => {
|
|
1283
|
+
console.log(`[verlauf] ${fehler instanceof Error ? fehler.message : String(fehler)}`);
|
|
1284
|
+
});
|
|
1285
|
+
selbstUpdateMelden({ art: 'cli', ...hinweis });
|
|
1196
1286
|
return;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1287
|
+
}
|
|
1288
|
+
// befund.art === 'ohne-version'
|
|
1289
|
+
const hinweis = await cliUpdateHinweis(null, { protokoll: (zeile) => console.log(zeile) });
|
|
1290
|
+
if (hinweis.neu !== null) {
|
|
1291
|
+
await anwendungsfaelle
|
|
1292
|
+
.hmgrEreignisVermerken({ art: 'cli-verfuegbar', version: hinweis.neu }, letzterAusloeser)
|
|
1293
|
+
.catch((fehler) => {
|
|
1294
|
+
console.log(`[verlauf] ${fehler instanceof Error ? fehler.message : String(fehler)}`);
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1203
1297
|
selbstUpdateMelden({ art: 'cli', ...hinweis });
|
|
1204
1298
|
}
|
|
1205
1299
|
/**
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
ansteht. Nur bei der GUI ein Knopf (E80): Neustart installiert das geladene Paket. -->
|
|
53
53
|
<div class="selbstupdate" id="selbstupdate" role="status" hidden>
|
|
54
54
|
<p id="selbstupdate-gui" hidden></p>
|
|
55
|
-
<p id="selbstupdate-cli" hidden></p>
|
|
55
|
+
<p id="selbstupdate-cli" class="warnung" hidden></p>
|
|
56
56
|
<button class="standknopf" type="button" id="selbstupdate-neustart" hidden>Jetzt neu starten</button>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
@@ -109,6 +109,10 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<div class="werkzeuge">
|
|
112
|
+
<div class="suche">
|
|
113
|
+
<svg viewBox="0 0 16 16" stroke-width="1.6" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 14.5 14.5"/></svg>
|
|
114
|
+
<input type="search" id="zielsuche" placeholder="Ziel oder Pfad suchen" aria-label="Ziele durchsuchen" spellcheck="false" autocomplete="off">
|
|
115
|
+
</div>
|
|
112
116
|
<div class="filter" role="group" aria-label="Ziele filtern">
|
|
113
117
|
<button class="chip" data-zielfilter="alle" aria-pressed="true">Alle</button>
|
|
114
118
|
<button class="chip" data-zielfilter="achtung" aria-pressed="false">Benötigt Aufmerksamkeit (0)</button>
|
|
@@ -404,6 +404,20 @@
|
|
|
404
404
|
// gilt „alle". Nicht persistiert, nur für die Dauer der Sitzung.
|
|
405
405
|
var VERZ_CHIP = {};
|
|
406
406
|
|
|
407
|
+
// Suchbegriff über die ganze Zielliste (E84 Teil 2, F100): Agent- wie Verzeichnis-Karten, in
|
|
408
|
+
// Kleinschreibung verglichen. Nicht persistiert, nur für die Dauer der Sitzung, und anders als
|
|
409
|
+
// der Chip-Wechsel leert die Suche weder ZIEL_OFFEN noch VERZ_CHIP.
|
|
410
|
+
var zielsuche = '';
|
|
411
|
+
|
|
412
|
+
// Ob schon einmal geladen wurde (E84 Teil 1, F99): der allererste Aufbau kennt noch keinen alten
|
|
413
|
+
// Stand, gegen den sich ein „neu hinzugekommenes" Verzeichnis abgrenzen ließe.
|
|
414
|
+
var ersteLadungAlles = true;
|
|
415
|
+
// Pfad eines Arbeitsverzeichnisses, dessen Karte anspringen soll, sobald die Ziele-Ansicht
|
|
416
|
+
// sichtbar wird (E84 Teil 1): gesetzt, wenn beim Laden eine andere Ansicht offen war. Ein
|
|
417
|
+
// weiteres neues Verzeichnis vor dem Wechsel ersetzt die Vormerkung, zeigeAnsicht('ziele')
|
|
418
|
+
// verbraucht sie.
|
|
419
|
+
var zielSprungVorgemerkt = null;
|
|
420
|
+
|
|
407
421
|
// Aufgeklappte Skill-Details im Katalog (F10), Schlüssel Quelle + Pfad: der Index in SKILLS
|
|
408
422
|
// verschiebt sich, wenn ladeAlles Quellen oder Skills anders sortiert oder eine wegfällt.
|
|
409
423
|
// Nicht persistiert, nur für die Dauer der Sitzung.
|
|
@@ -456,6 +470,10 @@
|
|
|
456
470
|
rufe(function () { return window.hmgr.bundles(); }),
|
|
457
471
|
rufe(function () { return window.hmgr.harnessHeime(); }),
|
|
458
472
|
]);
|
|
473
|
+
// Alter Stand der Verzeichnis-Pfade, vor dem Überschreiben unten (E84 Teil 1): zielKarten()
|
|
474
|
+
// liest hier noch ZIELE/AKTIVE_HARNESSE von vor diesem Lauf. null heißt „kein Vergleich", der
|
|
475
|
+
// allererste Aufbau hat noch keinen alten Stand.
|
|
476
|
+
var vorherVerzeichnisPfade = ersteLadungAlles ? null : zielKarten().verzeichnisse.map(function (v) { return v.pfad; });
|
|
459
477
|
ZIELE = ergebnisse[0];
|
|
460
478
|
SKILLS = ergebnisse[1];
|
|
461
479
|
// Eine alphabetische Liste über alle Quellen; wer nach Quelle sucht, hat die Filter-Chips.
|
|
@@ -476,6 +494,8 @@
|
|
|
476
494
|
STANDARD_REF = {};
|
|
477
495
|
SKILLS.forEach(function (s) { if (STANDARD_REF[s.quelleText] === undefined) STANDARD_REF[s.quelleText] = s.ref; });
|
|
478
496
|
|
|
497
|
+
ersteLadungAlles = false;
|
|
498
|
+
|
|
479
499
|
baueZiele();
|
|
480
500
|
baueSkills();
|
|
481
501
|
baueQuellenfilter();
|
|
@@ -483,6 +503,18 @@
|
|
|
483
503
|
baueEinstellungen();
|
|
484
504
|
baueMcp();
|
|
485
505
|
aktualisiereKopf();
|
|
506
|
+
|
|
507
|
+
// Genau ein neuer Verzeichnis-Pfad ⇒ Sprungziel (E84 Teil 1, F99): mehrere neue Pfade oder der
|
|
508
|
+
// allererste Aufbau lösen keinen Sprung aus. Gilt für beide Wege, GUI-Dialog wie CLI-Watcher
|
|
509
|
+
// (E62): beide laufen über ladeAlles. Erst NACH baueZiele() oben: springeZuVerzeichnis baut die
|
|
510
|
+
// Liste selbst noch einmal (Filter/Suche zurücksetzen kann nötig sein) und hebt die Karte
|
|
511
|
+
// hervor — ein baueZiele() danach würde diese frische Karte sonst wieder verwerfen.
|
|
512
|
+
if (vorherVerzeichnisPfade !== null) {
|
|
513
|
+
var neueVerzeichnisPfade = zielKarten().verzeichnisse
|
|
514
|
+
.map(function (v) { return v.pfad; })
|
|
515
|
+
.filter(function (p) { return vorherVerzeichnisPfade.indexOf(p) === -1; });
|
|
516
|
+
if (neueVerzeichnisPfade.length === 1) zielSprungVormerken(neueVerzeichnisPfade[0]);
|
|
517
|
+
}
|
|
486
518
|
}
|
|
487
519
|
|
|
488
520
|
/** Karten, wie die Zielliste sie zeigt: je Agent-Ziel eine, je Arbeitsverzeichnis eine gebündelte
|
|
@@ -545,6 +577,15 @@
|
|
|
545
577
|
// Der Verlauf zeigt immer den aktuellen Stand der Datei, nicht nur den des letzten Laufs hier.
|
|
546
578
|
void ladeVerlauf();
|
|
547
579
|
}
|
|
580
|
+
// Vorgemerkter Sprung zu einem neu hinzugekommenen Arbeitsverzeichnis (E84 Teil 1): kam er
|
|
581
|
+
// rein, während eine andere Ansicht offen war, holt der Wechsel hierher ihn jetzt nach. Die
|
|
582
|
+
// Ansicht steht schon sichtbar (siehe ANSICHTEN.forEach oben), springeZuVerzeichnis findet die
|
|
583
|
+
// Karte also.
|
|
584
|
+
if (name === 'ziele' && zielSprungVorgemerkt !== null) {
|
|
585
|
+
var vorgemerkterPfad = zielSprungVorgemerkt;
|
|
586
|
+
zielSprungVorgemerkt = null;
|
|
587
|
+
springeZuVerzeichnis(vorgemerkterPfad);
|
|
588
|
+
}
|
|
548
589
|
window.scrollTo(0, 0);
|
|
549
590
|
}
|
|
550
591
|
document.querySelectorAll('[data-ansicht]').forEach(function (k) {
|
|
@@ -1258,6 +1299,9 @@
|
|
|
1258
1299
|
kopf.setAttribute('role', 'button');
|
|
1259
1300
|
kopf.setAttribute('tabindex', '0');
|
|
1260
1301
|
var schluessel = verzKey(pfad);
|
|
1302
|
+
// Wiederfindbar für den Sprung nach dem Hinzufügen (E84 Teil 1): kein CSS-Selektor auf den
|
|
1303
|
+
// Pfad, der könnte Zeichen tragen, die eine Attribut-Selektion nicht unverändert übersteht.
|
|
1304
|
+
karte.dataset.verzKey = schluessel;
|
|
1261
1305
|
var bodyId = 'zielkoerper-' + zielKoerperNr++;
|
|
1262
1306
|
var offen = zielFilter === 'achtung' || !!ZIEL_OFFEN[schluessel];
|
|
1263
1307
|
kopf.setAttribute('aria-controls', bodyId);
|
|
@@ -1377,16 +1421,35 @@
|
|
|
1377
1421
|
return karte;
|
|
1378
1422
|
}
|
|
1379
1423
|
|
|
1424
|
+
/** Ob eine Agent-Karte zum aktuellen Suchbegriff passt (E84 Teil 2): ihr Titel, ihr voller
|
|
1425
|
+
Verzeichnis-Pfad oder dessen gekürzte Schreibweise (pfadKurz) — genau das, was im Kartenkopf
|
|
1426
|
+
steht (`e('span', 'mono', pfadKurz(z.verzeichnis))` in zielKarte). Ohne die Kürzung fand die
|
|
1427
|
+
Suche nach „~/.claude" nichts, weil im rohen Pfad der volle Home-Pfad steht. In
|
|
1428
|
+
Kleinschreibung, leerer Suchbegriff heißt „alles". */
|
|
1429
|
+
function zielKartePasstZuSuche(z) {
|
|
1430
|
+
var s = zielsuche.trim().toLowerCase();
|
|
1431
|
+
if (!s) return true;
|
|
1432
|
+
var text = (zielTitel(z) + ' ' + (z.verzeichnis || '') + ' ' + pfadKurz(z.verzeichnis || '')).toLowerCase();
|
|
1433
|
+
return text.indexOf(s) > -1;
|
|
1434
|
+
}
|
|
1435
|
+
/** Ob eine Verzeichnis-Karte zum aktuellen Suchbegriff passt (E84 Teil 2): voller Pfad oder
|
|
1436
|
+
gekürzte Schreibweise (pfadKurz). */
|
|
1437
|
+
function verzKartePasstZuSuche(pfad) {
|
|
1438
|
+
var s = zielsuche.trim().toLowerCase();
|
|
1439
|
+
if (!s) return true;
|
|
1440
|
+
return pfad.toLowerCase().indexOf(s) > -1 || pfadKurz(pfad).toLowerCase().indexOf(s) > -1;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1380
1443
|
function baueZiele() {
|
|
1381
1444
|
var liste = $('zielliste');
|
|
1382
1445
|
liste.textContent = '';
|
|
1383
1446
|
var karten = zielKarten();
|
|
1384
1447
|
|
|
1385
1448
|
var agentsSichtbar = karten.agents
|
|
1386
|
-
.filter(function (z) { return zielFilter === 'alle' || zielBraucht(z); })
|
|
1449
|
+
.filter(function (z) { return (zielFilter === 'alle' || zielBraucht(z)) && zielKartePasstZuSuche(z); })
|
|
1387
1450
|
.sort(function (a, b) { return zielTitel(a).localeCompare(zielTitel(b), 'de'); });
|
|
1388
1451
|
var verzeichnisseSichtbar = karten.verzeichnisse
|
|
1389
|
-
.filter(function (g) { return zielFilter === 'alle' || g.teile.some(zielBraucht); })
|
|
1452
|
+
.filter(function (g) { return (zielFilter === 'alle' || g.teile.some(zielBraucht)) && verzKartePasstZuSuche(g.pfad); })
|
|
1390
1453
|
.sort(function (a, b) { return pfadKurz(a.pfad).localeCompare(pfadKurz(b.pfad), 'de'); });
|
|
1391
1454
|
|
|
1392
1455
|
if (agentsSichtbar.length > 0) {
|
|
@@ -1399,10 +1462,16 @@
|
|
|
1399
1462
|
}
|
|
1400
1463
|
var gezeigt = agentsSichtbar.length + verzeichnisseSichtbar.length;
|
|
1401
1464
|
var gesamt = karten.agents.length + karten.verzeichnisse.length;
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1465
|
+
var suchbegriffGetrimmt = zielsuche.trim();
|
|
1466
|
+
if (gezeigt === 0) {
|
|
1467
|
+
if (suchbegriffGetrimmt) {
|
|
1468
|
+
liste.appendChild(e('p', 'leerhinweis', 'Kein Ziel passt zu „' + suchbegriffGetrimmt + '".'));
|
|
1469
|
+
} else {
|
|
1470
|
+
liste.appendChild(e('p', 'leerhinweis', zielFilter === 'achtung'
|
|
1471
|
+
? 'Kein Ziel braucht gerade Aufmerksamkeit.'
|
|
1472
|
+
: 'Noch keine Ziele. Füge unten ein Arbeitsverzeichnis hinzu oder verlinke einen Skill.'));
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1406
1475
|
|
|
1407
1476
|
$('zielzahl').textContent = gezeigt + ' von ' + gesamt + ' Karten gezeigt';
|
|
1408
1477
|
|
|
@@ -1430,6 +1499,73 @@
|
|
|
1430
1499
|
});
|
|
1431
1500
|
});
|
|
1432
1501
|
|
|
1502
|
+
// Suchfeld über die ganze Zielliste (E84 Teil 2, F100): tippen filtert live, anders als der
|
|
1503
|
+
// Chip-Wechsel bleiben ZIEL_OFFEN und VERZ_CHIP dabei unangetastet.
|
|
1504
|
+
var zielsucheFeld = $('zielsuche');
|
|
1505
|
+
if (zielsucheFeld) {
|
|
1506
|
+
zielsucheFeld.addEventListener('input', function () {
|
|
1507
|
+
zielsuche = zielsucheFeld.value;
|
|
1508
|
+
baueZiele();
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/** Ob die Ziele-Ansicht gerade die sichtbare Ansicht ist (E84 Teil 1). */
|
|
1513
|
+
function ansichtZieleSichtbar() {
|
|
1514
|
+
var ansicht = $('ansicht-ziele');
|
|
1515
|
+
return !!ansicht && !ansicht.hidden;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/** Die im DOM stehende Karte eines Arbeitsverzeichnisses, über data-verz-key gefunden (E84
|
|
1519
|
+
Teil 1): kein CSS-Attribut-Selektor auf den Pfad selbst, der könnte Sonderzeichen tragen. */
|
|
1520
|
+
function findeVerzeichnisKarte(schluessel) {
|
|
1521
|
+
var treffer = Array.prototype.filter.call(
|
|
1522
|
+
document.querySelectorAll('#zielliste .ziel.verzeichnis'),
|
|
1523
|
+
function (k) { return k.dataset.verzKey === schluessel; },
|
|
1524
|
+
);
|
|
1525
|
+
return treffer[0];
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/** Ob die Verzeichnis-Karte unter dem *aktuellen* Filter und Suchbegriff überhaupt sichtbar
|
|
1529
|
+
wäre, ohne beides zurückzusetzen (E84 Teil 1) — dieselbe Regel wie in baueZiele. */
|
|
1530
|
+
function verzKarteWaereSichtbar(pfad) {
|
|
1531
|
+
var gruppe = zielKarten().verzeichnisse.filter(function (g) { return g.pfad === pfad; })[0];
|
|
1532
|
+
if (!gruppe) return false;
|
|
1533
|
+
return (zielFilter === 'alle' || gruppe.teile.some(zielBraucht)) && verzKartePasstZuSuche(pfad);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
/** Klappt die Karte eines Arbeitsverzeichnisses auf, scrollt sie ins Bild und hebt sie kurz
|
|
1537
|
+
hervor (E84 Teil 1, F99). Setzt Suchfeld und Filter nur zurück, wenn die Karte sonst gar
|
|
1538
|
+
nicht sichtbar wäre — die Reihenfolge ist wichtig: der Filterwechsel leert ZIEL_OFFEN, erst
|
|
1539
|
+
danach wird der Schlüssel der Karte wieder aufgeklappt gemerkt. */
|
|
1540
|
+
function springeZuVerzeichnis(pfad) {
|
|
1541
|
+
var schluessel = verzKey(pfad);
|
|
1542
|
+
if (!verzKarteWaereSichtbar(pfad)) {
|
|
1543
|
+
zielFilter = 'alle';
|
|
1544
|
+
zielsuche = '';
|
|
1545
|
+
if (zielsucheFeld) zielsucheFeld.value = '';
|
|
1546
|
+
document.querySelectorAll('#ansicht-ziele .chip').forEach(function (a) {
|
|
1547
|
+
a.setAttribute('aria-pressed', a.dataset.zielfilter === 'alle' ? 'true' : 'false');
|
|
1548
|
+
});
|
|
1549
|
+
ZIEL_OFFEN = {};
|
|
1550
|
+
VERZ_CHIP = {};
|
|
1551
|
+
}
|
|
1552
|
+
ZIEL_OFFEN[schluessel] = true;
|
|
1553
|
+
baueZiele();
|
|
1554
|
+
var karte = findeVerzeichnisKarte(schluessel);
|
|
1555
|
+
if (!karte) return;
|
|
1556
|
+
karte.scrollIntoView({ block: 'nearest' });
|
|
1557
|
+
karte.classList.add('ziel-neu');
|
|
1558
|
+
setTimeout(function () { karte.classList.remove('ziel-neu'); }, 1500);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/** Springt sofort, wenn die Ziele-Ansicht gerade sichtbar ist, sonst merkt sich den Pfad für
|
|
1562
|
+
den nächsten Wechsel dorthin (E84 Teil 1): zeigeAnsicht('ziele') holt die Vormerkung nach
|
|
1563
|
+
und verbraucht sie dabei. Ein späterer Aufruf vor dem Wechsel ersetzt die vorherige. */
|
|
1564
|
+
function zielSprungVormerken(pfad) {
|
|
1565
|
+
if (ansichtZieleSichtbar()) springeZuVerzeichnis(pfad);
|
|
1566
|
+
else zielSprungVorgemerkt = pfad;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1433
1569
|
// entfernenKontext.eintraege: [{harness, ort}], immer eines bei frageEntfernen (Einzel-Ziel),
|
|
1434
1570
|
// mehrere bei frageEntfernenAlle (E53 Pkt. 2, „Alle"-Zeile über mehrere Harnesses).
|
|
1435
1571
|
var entfernenKontext = null;
|
|
@@ -1672,8 +1808,13 @@
|
|
|
1672
1808
|
if (wert !== 'los') return;
|
|
1673
1809
|
var pfad = $('dv-pfad').value.trim();
|
|
1674
1810
|
if (!pfad) return;
|
|
1811
|
+
// Stand vor dem Anlegen (E84 Teil 1): war das Verzeichnis schon eine Karte, findet ladeAlles
|
|
1812
|
+
// unten keine Differenz (kein neuer Pfad) — der Dialog kennt den Pfad trotzdem, diese Karte
|
|
1813
|
+
// ist dann das Sprungziel.
|
|
1814
|
+
var karteVorhanden = zielKarten().verzeichnisse.some(function (g) { return g.pfad === pfad; });
|
|
1675
1815
|
var ergebnis = await rufe(function () { return window.hmgr.arbeitsverzeichnisHinzufuegen(pfad); });
|
|
1676
1816
|
await ladeAlles();
|
|
1817
|
+
if (karteVorhanden) zielSprungVormerken(pfad);
|
|
1677
1818
|
$('de-titel').textContent = 'Arbeitsverzeichnis hinzugefügt';
|
|
1678
1819
|
$('de-text').textContent = ergebnis.hinweis || ('Zeiger: ' + (ergebnis.zeigerPfad || 'keiner'));
|
|
1679
1820
|
setzeErfolgHinweise([]);
|
|
@@ -2500,7 +2641,9 @@
|
|
|
2500
2641
|
Einstellungen-Knopf. Zwei getrennte Zeilen, weil beide zugleich zutreffen können: ein
|
|
2501
2642
|
geladenes GUI-Paket und eine neuere CLI auf npm sind zwei Pakete und zwei Kanäle (Gitea bzw.
|
|
2502
2643
|
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).
|
|
2644
|
+
die CLI installiert die GUI nicht selbst (F82). Die CLI-Zeile ist seit E82 eine Warnung, keine
|
|
2645
|
+
bloße Ansage: eine alte CLI (vor 0.0.7, kennt --version nicht) schreibt dieselben Dateien
|
|
2646
|
+
(state.json, verlauf.json) wie die neue GUI und kennt Bundles, Verlauf und WSL-Heime nicht. */
|
|
2504
2647
|
function selbstUpdateZeigen(nachricht) {
|
|
2505
2648
|
if (nachricht.art === 'gui') {
|
|
2506
2649
|
$('selbstupdate-gui').textContent = 'hmgr ' + nachricht.version + ' bereit — wird beim Beenden installiert';
|
|
@@ -2509,7 +2652,17 @@
|
|
|
2509
2652
|
} else if (nachricht.art === 'cli') {
|
|
2510
2653
|
var p = $('selbstupdate-cli');
|
|
2511
2654
|
p.textContent = '';
|
|
2512
|
-
|
|
2655
|
+
if (nachricht.installiert !== null) {
|
|
2656
|
+
p.appendChild(e('strong', null, 'CLI veraltet.'));
|
|
2657
|
+
p.appendChild(document.createTextNode(' hmgr-CLI ' + nachricht.installiert + ', npm hat ' + nachricht.neu + '.'));
|
|
2658
|
+
} else if (nachricht.neu !== null) {
|
|
2659
|
+
p.appendChild(e('strong', null, 'CLI veraltet.'));
|
|
2660
|
+
p.appendChild(document.createTextNode(' Die installierte hmgr-CLI ist älter als 0.0.7, npm hat ' + nachricht.neu + '.'));
|
|
2661
|
+
} else {
|
|
2662
|
+
p.appendChild(e('strong', null, 'CLI veraltet.'));
|
|
2663
|
+
p.appendChild(document.createTextNode(' Die installierte hmgr-CLI ist älter als 0.0.7.'));
|
|
2664
|
+
}
|
|
2665
|
+
p.appendChild(document.createTextNode(' Beheben im Terminal: '));
|
|
2513
2666
|
p.appendChild(e('code', null, nachricht.befehl));
|
|
2514
2667
|
p.hidden = false;
|
|
2515
2668
|
} else return;
|
|
@@ -181,6 +181,12 @@ a{color:var(--signal-tief)}
|
|
|
181
181
|
.selbstupdate p{font-size:15px;line-height:1.45;color:var(--emaille-hell)}
|
|
182
182
|
.selbstupdate code{font-family:var(--mono);font-size:14px;color:#F1F6EF;word-break:break-all}
|
|
183
183
|
|
|
184
|
+
/* E82: die CLI-Zeile ist eine Warnung, kein Hinweis — heller Kasten in Messing auf der dunklen
|
|
185
|
+
Leiste, damit sie sich vom Fließtext abhebt. */
|
|
186
|
+
.selbstupdate p.warnung{background:var(--messing-blass);color:var(--messing-satt);border-left:3px solid var(--messing);border-radius:2px;padding:6px 8px;margin:0}
|
|
187
|
+
.selbstupdate p.warnung strong{font-weight:600}
|
|
188
|
+
.selbstupdate p.warnung code{color:var(--messing-satt)}
|
|
189
|
+
|
|
184
190
|
.selbstupdate:not([hidden]) + .einstellungen{margin-top:0}
|
|
185
191
|
.einstellungen{
|
|
186
192
|
margin-top:auto;padding:14px 6px 0;display:flex;align-items:center;gap:11px;width:100%;
|
|
@@ -361,6 +367,17 @@ a{color:var(--signal-tief)}
|
|
|
361
367
|
.zielkoerper{padding:16px 18px 18px}
|
|
362
368
|
.zielkoerper[hidden]{display:none}
|
|
363
369
|
|
|
370
|
+
/* Hervorhebung der Karte eines frisch hinzugefügten Arbeitsverzeichnisses (E84 Teil 1): kurzer
|
|
371
|
+
Farbschimmer am Kartenrand, die Klasse fällt nach ~1,5s selbst wieder ab (setTimeout in
|
|
372
|
+
renderer.js, damit ein Neuaufbau der Liste sie nicht wieder einschaltet). Die globale
|
|
373
|
+
prefers-reduced-motion-Regel weiter unten kappt die Animationsdauer schon auf .01ms, der
|
|
374
|
+
Ruhezustand (kein Schimmer) reicht dort als Hinweis. */
|
|
375
|
+
.ziel.ziel-neu{animation:ziel-aufblitzen 1.5s ease-out}
|
|
376
|
+
@keyframes ziel-aufblitzen{
|
|
377
|
+
from{box-shadow:0 0 0 3px var(--signal)}
|
|
378
|
+
to{box-shadow:0 0 0 3px transparent}
|
|
379
|
+
}
|
|
380
|
+
|
|
364
381
|
.gruppenkopf{display:block;margin:16px 0 0;padding:0 0 9px;font-size:19px;font-weight:800;letter-spacing:.2em;color:var(--tinte);border-bottom:2px solid var(--tinte)}
|
|
365
382
|
.gruppenkopf:first-child{margin-top:0}
|
|
366
383
|
|