feat(web): coquille IDE + arbre de projets unifié (route /ide)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled

components/ide/ : IdeShell (grille barre d'activité + panneau gauche + zone centrale + dock + barre de statut, splitters persistés, réconciliation des ressources ouvertes contre les données live), ActivityBar (Explorer/Git/Sessions/Groupes + Réglages/Aide), PrimarySidebar, ProjectTree + ProjectTreeNode (arbre unique repos -> worktrees -> sessions, fichiers embarqués via FileTree en mode sans chrome, dépliage paresseux), StatusBar.

Route /ide (layout 'ide'), entrée de nav « IDE », namespace i18n ide (EN/FR). Clics de l'arbre pilotent le store (contexte worktree, ouverture de fichier/terminal). Centre et dock en placeholders (remplis en B4/B5). FileTree gagne un mode embedded. 427 tests.
This commit is contained in:
2026-07-17 17:08:35 +02:00
parent 6acd4a16fd
commit c96d929c7a
13 changed files with 446 additions and 11 deletions

View File

@@ -0,0 +1,67 @@
<template>
<div class="flex h-dvh min-h-0 flex-col bg-surface-0 text-fg">
<div class="flex min-h-0 flex-1">
<ActivityBar />
<template v-if="ide.leftVisible">
<PrimarySidebar />
<PanelSplitter v-model="ide.leftWidth" axis="x" :min="200" :max="560" />
</template>
<div class="flex min-w-0 flex-1 flex-col">
<!-- centre : éditeur à onglets (livré en B4). Placeholder pour l'instant. -->
<main class="min-h-0 flex-1 overflow-hidden">
<EmptyState :icon="FileCode" :title="t('ide.noFileOpen')" :hint="t('ide.noFileHint')" class="m-6" />
</main>
<!-- dock terminaux (livré en B5). Placeholder pour l'instant. -->
<template v-if="ide.bottomVisible">
<PanelSplitter v-model="ide.bottomHeight" axis="y" :min="120" :max="640" invert />
<section class="shrink-0 overflow-hidden border-t border-border" :style="{ height: `${ide.bottomHeight}px` }">
<EmptyState :icon="SquareTerminal" :title="t('ide.noTerminal')" :hint="t('ide.noTerminalHint')" class="m-6" />
</section>
</template>
</div>
</div>
<StatusBar />
</div>
</template>
<script setup lang="ts">
// Coquille de la vue IDE (layout 'ide', plein écran). Assemble barre d'activité, panneau gauche,
// zone centrale (éditeur), dock terminaux et barre de statut. Propriétaire de la réconciliation
// des ressources ouvertes contre les données live (worktrees / sessions).
import { watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { FileCode, SquareTerminal } from '@lucide/vue';
import { useIdeStore, wtKey } from '../../stores/ide';
import { useWorktreesStore } from '../../stores/worktrees';
import { useSessionsStore } from '../../stores/sessions';
import ActivityBar from './ActivityBar.vue';
import PrimarySidebar from './PrimarySidebar.vue';
import PanelSplitter from './PanelSplitter.vue';
import StatusBar from './StatusBar.vue';
import EmptyState from '../ui/EmptyState.vue';
const { t } = useI18n();
const ide = useIdeStore();
const worktrees = useWorktreesStore();
const sessions = useSessionsStore();
function prune(): void {
const live = new Set(sessions.sessions.filter((s) => s.live).map((s) => s.id));
const known = new Set(worktrees.worktrees.map((w) => wtKey(w.repoId, w.path)));
ide.pruneDeadResources(live, known);
}
// On ne réconcilie qu'une fois les données prêtes (fetch initial terminé) pour ne pas fermer les
// onglets/terminaux persistés avant leur chargement. Puis à chaque ajout/retrait de worktree ou
// de session.
watch(
[() => worktrees.loading, () => sessions.sessions.length, () => worktrees.worktrees.length],
() => {
if (!worktrees.loading) prune();
},
{ immediate: true },
);
</script>