feat(web): IDE unique comme seule coquille, ancien dashboard retiré

L'IDE (/ide) devient l'unique interface sur tous les supports (navigateur,
PWA, app Electron chargent la même SPA). Tout le cycle de vie de l'ancien
monde est réintégré dans l'IDE ; AppShell et les vues Dashboard / Sessions /
Groups / GroupView / SessionView sont supprimés.

- Infra: pile de modals Teleport (stores/modals.ts + ModalHost) et menu
  contextuel (useContextMenu + ContextMenu), montés dans IdeShell sans le
  démonter (attaches WS du dock préservées).
- Explorer: créer worktree (branche + baseRef + session), travailler sur main,
  prune, promote (escalade force), supprimer worktree, commit/push, masquer/
  supprimer repo, ajouter dépôt local ou cloner.
- Sessions: create/kill/resume/fork, hide/unhide, archive/unarchive, masquer
  les découvertes, supervision AttentionList + réponse aux dialogues.
- Groupes: create/delete, composition, lancement de session de groupe (ouvre
  le terminal dans le dock).
- Compte: Réglages et Aide en overlays par-dessus l'IDE ; langue, push/PWA,
  tokens, connexions git, RGPD, déconnexion. Command Palette et deep-links
  recâblés pour rester dans l'IDE.
- Routes: les anciens chemins rendent IdeShell via meta (overlay/panel/
  terminal) ; /workspace/:repoId/:wt inchangé (contrat extension VS Code).
  Mobile: nav à 6 panneaux (explorer/editor/terminal/git/sessions/groups).

La grille multi-terminaux de groupe est retirée (supplantée par la session de
groupe unique P6 via --add-dir + le dock multi-onglets).
This commit is contained in:
2026-07-20 12:10:12 +02:00
parent e48798ebff
commit c694f9f2dd
57 changed files with 1457 additions and 3045 deletions

View File

@@ -0,0 +1,53 @@
<template>
<Teleport to="body">
<div v-if="isOpen" class="fixed inset-0 z-[60]" @pointerdown="close" @contextmenu.prevent="close">
<div
class="absolute min-w-44 rounded-md border border-border bg-surface-1 py-1 shadow-lg"
:style="menuStyle"
@pointerdown.stop
>
<button
v-for="(item, i) in items"
:key="i"
type="button"
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm transition-colors disabled:opacity-40"
:class="item.danger ? 'text-red-400 hover:bg-red-500/10' : 'text-fg-muted hover:bg-surface-2 hover:text-fg'"
:disabled="item.disabled"
@click="select(item)"
>
<component :is="item.icon" v-if="item.icon" :size="15" :stroke-width="1.75" />
{{ item.label }}
</button>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
// Menu contextuel unique, positionné au curseur, monté une fois par IdeShell. Ferme au
// pointerdown hors menu (overlay plein écran transparent) et sur Escape. Recalage simple pour ne
// pas déborder du bas / de la droite de la fenêtre.
import { computed, onBeforeUnmount, onMounted } from 'vue';
import { useContextMenu, type ContextMenuItem } from '../../composables/useContextMenu';
const { isOpen, posX, posY, items, close } = useContextMenu();
const menuStyle = computed(() => {
const menuW = 208; // min-w-44 (176px) + marge
const menuH = items.value.length * 34 + 8;
const x = Math.max(8, Math.min(posX.value, window.innerWidth - menuW - 8));
const y = Math.max(8, Math.min(posY.value, window.innerHeight - menuH - 8));
return { left: `${x}px`, top: `${y}px` };
});
function select(item: ContextMenuItem): void {
if (item.disabled) return;
close();
item.onSelect();
}
function onKey(e: KeyboardEvent): void {
if (e.key === 'Escape' && isOpen.value) close();
}
onMounted(() => window.addEventListener('keydown', onKey));
onBeforeUnmount(() => window.removeEventListener('keydown', onKey));
</script>