Projekt-Registry statt festem ~/claude-projects-Root: projects.json (Name → Pfad, ~-relativ) mit Alt-Layout-Fallback; Ordner frei mappbar (add/remove_project, Wizard-Ablageort); Session-Sync per Projekt-Repo (add/commit/pull --rebase/push) statt git-sync-Skript; README + GitHub-Release-Fassung
This commit is contained in:
@@ -205,6 +205,8 @@ async function saveSettings() {
|
||||
interface Wizard {
|
||||
name: string;
|
||||
pool: string;
|
||||
dirMode: "default" | "custom";
|
||||
dir: string | null;
|
||||
workDirMode: "none" | "default" | "custom";
|
||||
workDir: string | null;
|
||||
title: string;
|
||||
@@ -219,6 +221,8 @@ function openWizard() {
|
||||
wizard.value = {
|
||||
name: "",
|
||||
pool: "",
|
||||
dirMode: "default",
|
||||
dir: null,
|
||||
workDirMode: "none",
|
||||
workDir: null,
|
||||
title: "",
|
||||
@@ -227,6 +231,26 @@ function openWizard() {
|
||||
};
|
||||
}
|
||||
|
||||
// Bestehenden Ordner als Projekt aufnehmen — Name ist der Ordnername.
|
||||
async function addFolder() {
|
||||
const dir = await open({ directory: true, multiple: false });
|
||||
if (typeof dir !== "string") return;
|
||||
try {
|
||||
await invoke("add_project", { path: dir });
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
error.value = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function pickProjectDir() {
|
||||
const dir = await open({ directory: true, multiple: false });
|
||||
if (typeof dir === "string") {
|
||||
wizard.value!.dir = dir;
|
||||
wizard.value!.dirMode = "custom";
|
||||
}
|
||||
}
|
||||
|
||||
async function pickWorkDir() {
|
||||
const dir = await open({ directory: true, multiple: false });
|
||||
if (typeof dir === "string") {
|
||||
@@ -248,6 +272,8 @@ async function createProject() {
|
||||
try {
|
||||
await invoke("create_project_full", {
|
||||
name,
|
||||
// custom: gewählter Ordner ist der Ablageort, das Projekt entsteht darin
|
||||
dir: w.dirMode === "custom" ? `${w.dir}/${name}` : null,
|
||||
pool: w.pool === "" ? null : w.pool,
|
||||
workDir,
|
||||
createWorkDir: w.workDirMode === "default",
|
||||
@@ -300,6 +326,19 @@ async function confirmDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
// Nur den Registry-Eintrag entfernen; der Ordner bleibt.
|
||||
async function unlinkProject() {
|
||||
const d = pendingDelete.value!;
|
||||
try {
|
||||
await invoke("remove_project", { name: d.name });
|
||||
pendingDelete.value = null;
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
error.value = String(e);
|
||||
pendingDelete.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
let timer: number;
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
@@ -313,6 +352,9 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
<button class="primary" @click="openWizard">
|
||||
{{ $t("projects.newProject") }}
|
||||
</button>
|
||||
<button :title="$t('projects.addFolderTitle')" @click="addFolder">
|
||||
{{ $t("projects.addFolder") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="error" class="error">{{ error }}</p>
|
||||
@@ -393,6 +435,28 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
{{ $t("projects.name") }}
|
||||
<input v-model="wizard.name" autofocus required />
|
||||
</label>
|
||||
<label class="field">
|
||||
{{ $t("projects.projectDir") }}
|
||||
<span class="icon-row">
|
||||
<select v-model="wizard.dirMode">
|
||||
<option value="default">
|
||||
~/claude-projects/{{ wizard.name.trim() || "…" }}
|
||||
</option>
|
||||
<option value="custom">{{ $t("projects.projectDirCustom") }}</option>
|
||||
</select>
|
||||
<button
|
||||
v-if="wizard.dirMode === 'custom'"
|
||||
type="button"
|
||||
@click="pickProjectDir"
|
||||
>
|
||||
{{ $t("projects.chooseFolder") }}
|
||||
</button>
|
||||
</span>
|
||||
</label>
|
||||
<label v-if="wizard.dirMode === 'custom' && wizard.dir" class="field">
|
||||
<span></span>
|
||||
<span class="icon-path">{{ wizard.dir }}/{{ wizard.name.trim() || "…" }}</span>
|
||||
</label>
|
||||
<label class="field">
|
||||
{{ $t("projects.pool") }}
|
||||
<select v-model="wizard.pool">
|
||||
@@ -454,6 +518,7 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
class="primary"
|
||||
:disabled="
|
||||
wizard.name.trim() === '' ||
|
||||
(wizard.dirMode === 'custom' && !wizard.dir) ||
|
||||
(wizard.workDirMode === 'custom' && !wizard.workDir)
|
||||
"
|
||||
>
|
||||
@@ -479,10 +544,14 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
{{ $t("projects.workDirsStay") }}
|
||||
</p>
|
||||
</template>
|
||||
<p class="hint">{{ $t("projects.unlinkHint") }}</p>
|
||||
<div class="actions">
|
||||
<button type="button" @click="pendingDelete = null">
|
||||
{{ $t("projects.cancel") }}
|
||||
</button>
|
||||
<button @click="unlinkProject">
|
||||
{{ $t("projects.unlink") }}
|
||||
</button>
|
||||
<button class="danger" @click="confirmDelete">
|
||||
{{ $t("projects.delete") }}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user