Pools initialisieren, Pool-Guard raus, Löschschutz bei laufenden Sessions
- pool-guard-Hook aus Projekt-Scaffold und allen bestehenden Projekten entfernt - neue Pools: settings.json mit aufgeräumten UI-Defaults (promptSuggestionEnabled/awaySummaryEnabled aus) + CLAUDE.md - delete_pool bricht bei laufendem zugeordnetem Projekt ab; Übersicht sperrt den Delete-Button und zeigt den Grund per Hover-Pop (WKWebView-tauglich) - Running-Erkennung zentralisiert (running_projects_using_pool), list_pools liefert pro Pool die laufenden Projekte - Todo-Umschalter im Projekt-Wizard/Settings - ungenutzte Dependencies ureq/getrandom entfernt
This commit is contained in:
@@ -9,6 +9,7 @@ interface Pool {
|
||||
name: string;
|
||||
credentialType: string;
|
||||
projects: string[];
|
||||
running: string[];
|
||||
hasCredentials: boolean;
|
||||
}
|
||||
|
||||
@@ -206,9 +207,20 @@ onMounted(refresh);
|
||||
{{ p.hasCredentials ? $t("pools.changeKey") : $t("pools.insertKey") }}
|
||||
</button>
|
||||
</template>
|
||||
<button class="danger" :disabled="busy" @click="askDelete(p.name)">
|
||||
{{ $t("pools.delete") }}
|
||||
</button>
|
||||
<span class="tip-wrap" :class="{ 'hover-pop': p.running.length }">
|
||||
<button
|
||||
class="danger"
|
||||
:disabled="busy || p.running.length > 0"
|
||||
@click="askDelete(p.name)"
|
||||
>
|
||||
{{ $t("pools.delete") }}
|
||||
</button>
|
||||
<span v-if="p.running.length" class="pop pop-right">
|
||||
<span class="pop-text">
|
||||
{{ $t("pools.deleteBlockedTooltip", { projects: p.running.join(", ") }) }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -239,16 +251,13 @@ onMounted(refresh);
|
||||
<p v-if="dialog.mode === 'oauth'" class="hint">
|
||||
{{ $t("pools.oauthHint") }}
|
||||
</p>
|
||||
<p v-if="busy && dialog.mode === 'oauth'" class="hint busy">
|
||||
{{ $t("pools.oauthWaiting") }}
|
||||
</p>
|
||||
|
||||
<div class="actions">
|
||||
<button type="button" :disabled="busy" @click="closeDialog">
|
||||
{{ $t("pools.cancel") }}
|
||||
</button>
|
||||
<button type="submit" class="primary" :disabled="busy">
|
||||
{{ dialog.mode === "oauth" ? $t("pools.startLogin") : $t("pools.save") }}
|
||||
{{ dialog.mode === "oauth" ? $t("pools.createPool") : $t("pools.save") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -280,13 +289,12 @@ onMounted(refresh);
|
||||
: $t("pools.reloginNoEntry")
|
||||
}}
|
||||
</p>
|
||||
<p v-if="busy" class="hint busy">{{ $t("pools.oauthWaiting") }}</p>
|
||||
<div class="actions">
|
||||
<button type="button" :disabled="busy" @click="reloginPool = null">
|
||||
{{ $t("pools.cancel") }}
|
||||
</button>
|
||||
<button class="primary" :disabled="busy" @click="confirmRelogin">
|
||||
{{ $t("pools.startLogin") }}
|
||||
{{ $t("pools.reset") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -110,17 +110,24 @@ interface TerminalSettings {
|
||||
theme: string;
|
||||
icon: string | null;
|
||||
title: string;
|
||||
todo: boolean;
|
||||
}
|
||||
|
||||
const settings = ref<TerminalSettings | null>(null);
|
||||
|
||||
function openSettings(p: Project) {
|
||||
settings.value = {
|
||||
name: p.name,
|
||||
theme: p.terminal.theme ?? "mocha",
|
||||
icon: p.terminal.icon,
|
||||
title: p.terminal.title ?? "",
|
||||
};
|
||||
async function openSettings(p: Project) {
|
||||
try {
|
||||
const todo = await invoke<boolean>("todo_state", { project: p.name });
|
||||
settings.value = {
|
||||
name: p.name,
|
||||
theme: p.terminal.theme ?? "mocha",
|
||||
icon: p.terminal.icon,
|
||||
title: p.terminal.title ?? "",
|
||||
todo,
|
||||
};
|
||||
} catch (e) {
|
||||
error.value = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function pickIcon() {
|
||||
@@ -142,6 +149,7 @@ async function saveSettings() {
|
||||
icon: s.icon,
|
||||
title: title === "" ? null : title,
|
||||
});
|
||||
await invoke("set_todo", { project: s.name, enabled: s.todo });
|
||||
settings.value = null;
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
@@ -156,6 +164,7 @@ interface Wizard {
|
||||
workDir: string | null;
|
||||
title: string;
|
||||
theme: string;
|
||||
todo: boolean;
|
||||
}
|
||||
|
||||
const wizard = ref<Wizard | null>(null);
|
||||
@@ -169,6 +178,7 @@ function openWizard() {
|
||||
workDir: null,
|
||||
title: "",
|
||||
theme: "mocha",
|
||||
todo: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -201,6 +211,7 @@ async function createProject() {
|
||||
icon: null,
|
||||
title: title === "" ? null : title,
|
||||
},
|
||||
todo: w.todo,
|
||||
});
|
||||
wizard.value = null;
|
||||
await refresh();
|
||||
@@ -378,6 +389,13 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
{{ $t("projects.todo") }}
|
||||
<span class="checkline">
|
||||
<input v-model="wizard.todo" type="checkbox" />
|
||||
{{ $t("projects.todoDesc") }}
|
||||
</span>
|
||||
</label>
|
||||
<p class="hint">{{ $t("projects.wizardHint") }}</p>
|
||||
<div class="actions">
|
||||
<button type="button" @click="wizard = null">
|
||||
@@ -453,6 +471,13 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
<span v-else class="icon-path">{{ $t("projects.defaultIcon") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
<label class="field">
|
||||
{{ $t("projects.todo") }}
|
||||
<span class="checkline">
|
||||
<input v-model="settings.todo" type="checkbox" />
|
||||
{{ $t("projects.todoDesc") }}
|
||||
</span>
|
||||
</label>
|
||||
<p class="hint">{{ $t("projects.appliesNextStart") }}</p>
|
||||
<div class="actions">
|
||||
<button @click="settings = null">{{ $t("projects.cancel") }}</button>
|
||||
|
||||
+26
-18
@@ -59,7 +59,7 @@ const de = {
|
||||
chooseFolder: "Ordner wählen …",
|
||||
create: "Anlegen",
|
||||
wizardHint:
|
||||
"Angelegt werden Projektordner mit memory/, Sentinel-.gitignore, settings.json mit pool-guard-Hook und die Pool-/Terminal-Zuordnung.",
|
||||
"Angelegt werden Projektordner mit memory/, Sentinel-.gitignore, settings.json (Memory-Verzeichnis + Berechtigungen) und die Pool-/Terminal-Zuordnung.",
|
||||
delete: "Projekt löschen",
|
||||
deleteRunning: "Läuft noch — erst beenden",
|
||||
deleteTitle: "{name} löschen",
|
||||
@@ -67,6 +67,8 @@ const de = {
|
||||
"Der Projektordner unter ~/claude-projects (inkl. memory/, Settings und Pool-Zuordnung) wird endgültig gelöscht.",
|
||||
deleteWorkDirs: "Arbeitsordner mitlöschen:",
|
||||
workDirsStay: "Die Arbeitsordner bleiben unangetastet.",
|
||||
todo: "Todoliste",
|
||||
todoDesc: "OFFENE-PUNKTE.md bei jedem Sessionstart einspielen",
|
||||
},
|
||||
pools: {
|
||||
pool: "Pool",
|
||||
@@ -74,7 +76,7 @@ const de = {
|
||||
actions: "Aktionen",
|
||||
newOauth: "+ oAuth",
|
||||
newApikey: "+ apiKey",
|
||||
relogin: "Neu anmelden",
|
||||
relogin: "Zurücksetzen",
|
||||
changeKey: "Key ändern",
|
||||
insertKey: "Key eintragen",
|
||||
delete: "Löschen",
|
||||
@@ -86,22 +88,24 @@ const de = {
|
||||
name: "Name",
|
||||
apiKey: "API-Key",
|
||||
oauthHint:
|
||||
"Nach „Login starten“ öffnet sich der Browser zur Anmeldung gegen dein Abo.",
|
||||
oauthWaiting: "Warte auf die Anmeldung im Browser…",
|
||||
"Der Pool wird angelegt; die Anmeldung macht claude beim ersten Start selbst per /login (Browser gegen dein Abo).",
|
||||
cancel: "Abbrechen",
|
||||
startLogin: "Login starten",
|
||||
createPool: "Anlegen",
|
||||
reset: "Zurücksetzen",
|
||||
save: "Speichern",
|
||||
deletePool: "Pool löschen",
|
||||
deleteWarning:
|
||||
"Pool {name} und seine Credential-Datei werden gelöscht. Das lässt sich nicht rückgängig machen.",
|
||||
deleteUnassigns: "Diese Projekte verlieren ihre Pool-Zuordnung:",
|
||||
reloginTitle: "Neu anmelden – {name}",
|
||||
deleteBlockedTooltip:
|
||||
"Löschen gesperrt — {projects} läuft gerade. Erst beenden.",
|
||||
reloginTitle: "{name} zurücksetzen",
|
||||
reloginWarning:
|
||||
"Der im Schlüsselbund gespeicherte Zugriffstoken von {name} wird gelöscht. Danach öffnet sich der Browser zur Neuanmeldung.",
|
||||
"Der im Schlüsselbund gespeicherte Zugriffstoken von {name} wird gelöscht. Beim nächsten Start verlangt claude erneut /login.",
|
||||
reloginNoEntry:
|
||||
"Kein Schlüsselbund-Eintrag vorhanden — es erfolgt nur die Neuanmeldung im Browser.",
|
||||
"Kein Schlüsselbund-Eintrag vorhanden — beim nächsten Start meldet sich claude ohnehin per /login an.",
|
||||
reloginBlocked:
|
||||
"Neuanmeldung ist nur bei ungenutztem Pool möglich. Diese Projekte laufen noch:",
|
||||
"Zurücksetzen ist nur bei ungenutztem Pool möglich. Diese Projekte laufen noch:",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -164,7 +168,7 @@ const en: typeof de = {
|
||||
chooseFolder: "Choose folder …",
|
||||
create: "Create",
|
||||
wizardHint:
|
||||
"Creates the project folder with memory/, sentinel .gitignore, settings.json with the pool-guard hook, and the pool/terminal assignment.",
|
||||
"Creates the project folder with memory/, sentinel .gitignore, settings.json (memory dir + permissions), and the pool/terminal assignment.",
|
||||
delete: "Delete project",
|
||||
deleteRunning: "Still running — stop it first",
|
||||
deleteTitle: "Delete {name}",
|
||||
@@ -172,6 +176,8 @@ const en: typeof de = {
|
||||
"The project folder under ~/claude-projects (incl. memory/, settings and pool assignment) will be deleted permanently.",
|
||||
deleteWorkDirs: "Also delete working folders:",
|
||||
workDirsStay: "The working folders stay untouched.",
|
||||
todo: "Todo list",
|
||||
todoDesc: "Inject OFFENE-PUNKTE.md at every session start",
|
||||
},
|
||||
pools: {
|
||||
pool: "Pool",
|
||||
@@ -179,7 +185,7 @@ const en: typeof de = {
|
||||
actions: "Actions",
|
||||
newOauth: "+ oAuth",
|
||||
newApikey: "+ apiKey",
|
||||
relogin: "Sign in again",
|
||||
relogin: "Reset",
|
||||
changeKey: "Change key",
|
||||
insertKey: "Insert key",
|
||||
delete: "Delete",
|
||||
@@ -191,22 +197,24 @@ const en: typeof de = {
|
||||
name: "Name",
|
||||
apiKey: "API key",
|
||||
oauthHint:
|
||||
"After “Start login” the browser opens to sign in against your subscription.",
|
||||
oauthWaiting: "Waiting for the sign-in in the browser…",
|
||||
"The pool is created; claude signs in itself on first start via /login (browser against your subscription).",
|
||||
cancel: "Cancel",
|
||||
startLogin: "Start login",
|
||||
createPool: "Create",
|
||||
reset: "Reset",
|
||||
save: "Save",
|
||||
deletePool: "Delete pool",
|
||||
deleteWarning:
|
||||
"Pool {name} and its credential file will be deleted. This cannot be undone.",
|
||||
deleteUnassigns: "These projects lose their pool assignment:",
|
||||
reloginTitle: "Sign in again – {name}",
|
||||
deleteBlockedTooltip:
|
||||
"Deletion blocked — active: {projects}. Stop the session first.",
|
||||
reloginTitle: "Reset {name}",
|
||||
reloginWarning:
|
||||
"The access token for {name} stored in the keychain will be deleted. The browser then opens for a new sign-in.",
|
||||
"The access token for {name} stored in the keychain will be deleted. On the next start claude asks for /login again.",
|
||||
reloginNoEntry:
|
||||
"No keychain entry present — only the new sign-in happens.",
|
||||
"No keychain entry present — claude signs in via /login on the next start anyway.",
|
||||
reloginBlocked:
|
||||
"Signing in again requires an unused pool. These projects are still running:",
|
||||
"Resetting requires an unused pool. These projects are still running:",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+35
-1
@@ -208,6 +208,35 @@ button.danger:hover:not(:disabled) {
|
||||
width: 9.5rem;
|
||||
}
|
||||
|
||||
/* Tooltip-Träger um den Löschen-Button: ein disabled Button empfängt keine
|
||||
Hover-Events, darum sitzt das Hover-Pop am Span und der Button ist im
|
||||
gesperrten Zustand für die Maus durchlässig. WKWebView zeigt keine nativen
|
||||
title-Tooltips, daher das CSS-Pop (hover-pop/pop). */
|
||||
.tip-wrap {
|
||||
display: inline-flex;
|
||||
}
|
||||
.tip-wrap button:disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Pop rechtsbündig — der Löschen-Button steht am rechten Zeilenrand.
|
||||
Schmaler als das Standard-Pop, damit der Hinweis auf mind. zwei Zeilen
|
||||
umbricht statt in einer sehr breiten Zeile zu stehen. */
|
||||
.hover-pop .pop.pop-right {
|
||||
left: auto;
|
||||
right: 0;
|
||||
min-width: 0;
|
||||
width: 13rem;
|
||||
max-width: 13rem;
|
||||
}
|
||||
.pop-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
color: var(--text);
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
button.gear {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -584,6 +613,11 @@ td select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dialog .field .checkline {
|
||||
font-size: 0.85rem;
|
||||
color: var(--subtext);
|
||||
}
|
||||
|
||||
.dialog .affected {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
@@ -604,6 +638,6 @@ td select {
|
||||
|
||||
/* ---------- Formulare (Pools-Dialoge) ---------- */
|
||||
|
||||
form.dialog input {
|
||||
form.dialog input:not([type="checkbox"]) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user