feat(settings): réglages Claude CLI (binaire + ~/.claude) & notif « disponible »
- Réglages → Claude CLI : override du chemin du binaire `claude` (effet à la prochaine session, fallback `which claude`), diagnostic de détection, et override de la racine ~/.claude (effet au redémarrage). Validateurs stricts. - Push : notifie aussi sur le front busy→idle (session redevenue disponible). - Renomme l'état affiché idle → « disponible » / « available » (web EN/FR + VS Code) ; l'enum SessionActivity du protocole reste inchangé.
This commit is contained in:
@@ -137,6 +137,44 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Claude CLI -->
|
||||
<section class="card flex flex-col gap-3">
|
||||
<h2 class="flex items-center gap-2 text-sm font-semibold text-zinc-100">
|
||||
<Terminal :size="16" /> {{ t('settings.claudeCli') }}
|
||||
</h2>
|
||||
<p class="text-xs text-zinc-500">{{ t('settings.claudeCliHint') }}</p>
|
||||
|
||||
<!-- Diagnostic de détection -->
|
||||
<div v-if="settings.server" class="card-inset flex flex-wrap items-center gap-x-2 gap-y-1 text-xs">
|
||||
<component :is="claudeBin.ok ? CheckCircle2 : AlertCircle" :size="15" :class="claudeBin.ok ? 'text-emerald-400' : 'text-rose-400'" />
|
||||
<span :class="claudeBin.ok ? 'text-zinc-300' : 'text-rose-300'">
|
||||
{{ claudeBin.ok ? t('settings.claudeBinStatusOk') : (claudeBin.source === 'configured' ? t('settings.claudeBinStatusBadPath') : t('settings.claudeBinStatusNotFound')) }}
|
||||
</span>
|
||||
<code v-if="claudeBin.path" class="min-w-0 truncate font-mono text-zinc-200" :title="claudeBin.path">{{ claudeBin.path }}</code>
|
||||
<span v-if="claudeBin.source" class="badge bg-zinc-700/40 text-zinc-300">
|
||||
{{ claudeBin.source === 'configured' ? t('settings.claudeBinSourceConfigured') : t('settings.claudeBinSourcePath') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-400">{{ t('settings.claudeBinPathLabel') }}</span>
|
||||
<input v-model.trim="claudeBinDraft" class="input font-mono" :placeholder="t('settings.claudeBinPathPlaceholder')" />
|
||||
<span class="text-xs text-zinc-500">{{ t('settings.claudeBinPathHint') }}</span>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-400">{{ t('settings.claudeHomeLabel') }}</span>
|
||||
<input v-model.trim="claudeHomeDraft" class="input font-mono" :placeholder="t('settings.claudeHomePlaceholder')" />
|
||||
<span class="text-xs text-zinc-500">{{ t('settings.claudeHomeHint') }}</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<BaseButton variant="primary" :loading="settings.saving" :disabled="!claudeDirty" @click="saveClaude">
|
||||
{{ t('settings.save') }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Sécurité & conformité -->
|
||||
<section class="card flex flex-col gap-3">
|
||||
<h2 class="flex items-center gap-2 text-sm font-semibold text-zinc-100">
|
||||
@@ -202,7 +240,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Bell, BellOff, Check, Coffee, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Trash2, X } from '@lucide/vue';
|
||||
import { AlertCircle, Bell, BellOff, Check, CheckCircle2, Coffee, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Terminal, Trash2, X } from '@lucide/vue';
|
||||
import type {
|
||||
AuditLogEntry,
|
||||
AuditLogsResponse,
|
||||
@@ -328,6 +366,27 @@ async function saveDiscovery(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Claude CLI ----
|
||||
const claudeBinDraft = ref('');
|
||||
const claudeHomeDraft = ref('');
|
||||
const claudeBin = computed(() => settings.server?.claudeBin ?? { path: null, source: null, ok: false });
|
||||
const claudeDirty = computed(
|
||||
() => claudeBinDraft.value !== (settings.claudeBinPath ?? '') || claudeHomeDraft.value !== (settings.claudeHome ?? ''),
|
||||
);
|
||||
function syncClaudeDraft(): void {
|
||||
claudeBinDraft.value = settings.claudeBinPath ?? '';
|
||||
claudeHomeDraft.value = settings.claudeHome ?? '';
|
||||
}
|
||||
async function saveClaude(): Promise<void> {
|
||||
try {
|
||||
await settings.save({ claudeBinPath: claudeBinDraft.value, claudeHome: claudeHomeDraft.value });
|
||||
syncClaudeDraft();
|
||||
toasts.success(t('settings.saved'));
|
||||
} catch (e) {
|
||||
toasts.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Sécurité & conformité (audit / RGPD) ----
|
||||
const audit = ref<AuditLogEntry[]>([]);
|
||||
const auditing = ref(false);
|
||||
@@ -378,6 +437,7 @@ async function deleteMyData(): Promise<void> {
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadTokens(), settings.loaded ? Promise.resolve() : settings.fetch()]);
|
||||
syncDiscoveryDraft();
|
||||
syncClaudeDraft();
|
||||
void push.refresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user