feat(web): coquille IDE + arbre de projets unifié (route /ide)
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:
61
packages/web/src/components/ide/ActivityBar.vue
Normal file
61
packages/web/src/components/ide/ActivityBar.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<nav class="flex w-[var(--ide-activitybar-w)] shrink-0 flex-col items-center gap-1 border-r border-border bg-surface-1 py-2">
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item.view"
|
||||
type="button"
|
||||
class="relative flex h-10 w-10 items-center justify-center rounded-lg transition-colors"
|
||||
:class="isActive(item.view) ? 'bg-surface-3 text-fg' : 'text-fg-subtle hover:bg-surface-2 hover:text-fg'"
|
||||
:title="item.label"
|
||||
:aria-label="item.label"
|
||||
@click="ide.toggleActivity(item.view)"
|
||||
>
|
||||
<component :is="item.icon" :size="20" :stroke-width="1.75" />
|
||||
<span
|
||||
v-if="item.badge"
|
||||
class="absolute top-1 right-1 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-amber-500 px-1 text-[9px] font-semibold text-amber-950"
|
||||
>
|
||||
{{ item.badge }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="mt-auto flex flex-col items-center gap-1">
|
||||
<RouterLink
|
||||
v-for="l in links"
|
||||
:key="l.name"
|
||||
:to="{ name: l.name }"
|
||||
:title="l.label"
|
||||
:aria-label="l.label"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-lg text-fg-subtle transition-colors hover:bg-surface-2 hover:text-fg"
|
||||
>
|
||||
<component :is="l.icon" :size="20" :stroke-width="1.75" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Boxes, FolderTree, GitCompare, LifeBuoy, Settings, SquareTerminal } from '@lucide/vue';
|
||||
import { useIdeStore, type ActivityView } from '../../stores/ide';
|
||||
import { useNav } from '../../composables/useNav';
|
||||
|
||||
const { t } = useI18n();
|
||||
const ide = useIdeStore();
|
||||
const { waitingCount } = useNav();
|
||||
|
||||
const isActive = (v: ActivityView): boolean => ide.activeActivity === v && ide.leftVisible;
|
||||
|
||||
const items = computed(() => [
|
||||
{ view: 'explorer' as ActivityView, icon: FolderTree, label: t('ide.activity.explorer'), badge: 0 },
|
||||
{ view: 'git' as ActivityView, icon: GitCompare, label: t('ide.activity.git'), badge: 0 },
|
||||
{ view: 'sessions' as ActivityView, icon: SquareTerminal, label: t('ide.activity.sessions'), badge: waitingCount.value },
|
||||
{ view: 'groups' as ActivityView, icon: Boxes, label: t('ide.activity.groups'), badge: 0 },
|
||||
]);
|
||||
|
||||
const links = computed(() => [
|
||||
{ name: 'settings', icon: Settings, label: t('nav.settings') },
|
||||
{ name: 'help', icon: LifeBuoy, label: t('nav.help') },
|
||||
]);
|
||||
</script>
|
||||
Reference in New Issue
Block a user