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:
@@ -52,13 +52,17 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch, type Component } from 'vue';
|
||||
import { useRouter, type RouteLocationRaw } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Search, GitBranch, TerminalSquare, Boxes, Plus, FolderPlus } from '@lucide/vue';
|
||||
import type { WorktreeSummary } from '@arboretum/shared';
|
||||
import { useSessionsStore } from '../stores/sessions';
|
||||
import { useWorktreesStore } from '../stores/worktrees';
|
||||
import { useGroupsStore } from '../stores/groups';
|
||||
import { useIdeStore } from '../stores/ide';
|
||||
import { useModalsStore } from '../stores/modals';
|
||||
import { useCommandPalette } from '../composables/useCommandPalette';
|
||||
import NewSessionModal from './ide/modals/NewSessionModal.vue';
|
||||
import GroupCreateModal from './ide/modals/GroupCreateModal.vue';
|
||||
|
||||
interface PaletteItem {
|
||||
id: string;
|
||||
@@ -67,16 +71,27 @@ interface PaletteItem {
|
||||
sublabel?: string;
|
||||
icon: Component;
|
||||
keywords: string;
|
||||
to?: RouteLocationRaw;
|
||||
run?: () => void;
|
||||
run: () => void;
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const { open, close, toggle } = useCommandPalette();
|
||||
const sessions = useSessionsStore();
|
||||
const worktrees = useWorktreesStore();
|
||||
const groups = useGroupsStore();
|
||||
const ide = useIdeStore();
|
||||
const modals = useModalsStore();
|
||||
|
||||
// Toutes les actions restent DANS l'IDE (aucune navigation vers l'ancien monde).
|
||||
function revealRepo(id: string): void {
|
||||
ide.setActivity('explorer');
|
||||
if (!ide.expandedRepoIds.includes(id)) ide.toggleRepo(id);
|
||||
}
|
||||
function revealWorktree(w: WorktreeSummary): void {
|
||||
ide.revealWorktree(w.repoId, w.path);
|
||||
const live = sessions.sessions.find((s) => s.live && s.cwd === w.path);
|
||||
if (live) ide.openTerminal(live.id);
|
||||
}
|
||||
|
||||
const query = ref('');
|
||||
const activeIndex = ref(0);
|
||||
@@ -87,24 +102,21 @@ const basename = (p: string): string => p.split('/').filter(Boolean).pop() ?? p;
|
||||
const items = computed<PaletteItem[]>(() => {
|
||||
const out: PaletteItem[] = [];
|
||||
for (const r of worktrees.repos) {
|
||||
out.push({ id: `repo-${r.id}`, type: 'repo', label: r.label, sublabel: r.path, icon: GitBranch as Component, keywords: `${r.label} ${r.path}`.toLowerCase(), to: { name: 'dashboard' } });
|
||||
out.push({ id: `repo-${r.id}`, type: 'repo', label: r.label, sublabel: r.path, icon: GitBranch as Component, keywords: `${r.label} ${r.path}`.toLowerCase(), run: () => revealRepo(r.id) });
|
||||
}
|
||||
for (const w of worktrees.worktrees) {
|
||||
const target: RouteLocationRaw = w.sessions[0]
|
||||
? { name: 'session', params: { id: w.sessions[0].id } }
|
||||
: { name: 'dashboard' };
|
||||
out.push({ id: `wt-${w.path}`, type: 'worktree', label: w.branch ?? basename(w.path), sublabel: w.path, icon: GitBranch as Component, keywords: `${w.branch ?? ''} ${w.path}`.toLowerCase(), to: target });
|
||||
out.push({ id: `wt-${w.path}`, type: 'worktree', label: w.branch ?? basename(w.path), sublabel: w.path, icon: GitBranch as Component, keywords: `${w.branch ?? ''} ${w.path}`.toLowerCase(), run: () => revealWorktree(w) });
|
||||
}
|
||||
for (const s of sessions.sessions) {
|
||||
out.push({ id: `ses-${s.id}`, type: 'session', label: s.title ?? basename(s.cwd), sublabel: s.cwd, icon: TerminalSquare as Component, keywords: `${s.title ?? ''} ${s.cwd} ${s.command}`.toLowerCase(), to: { name: 'session', params: { id: s.id } } });
|
||||
out.push({ id: `ses-${s.id}`, type: 'session', label: s.title ?? basename(s.cwd), sublabel: s.cwd, icon: TerminalSquare as Component, keywords: `${s.title ?? ''} ${s.cwd} ${s.command}`.toLowerCase(), run: () => ide.openTerminal(s.id) });
|
||||
}
|
||||
for (const g of groups.groups) {
|
||||
out.push({ id: `grp-${g.id}`, type: 'group', label: g.label, ...(g.description ? { sublabel: g.description } : {}), icon: Boxes as Component, keywords: `${g.label} ${g.description ?? ''}`.toLowerCase(), to: { name: 'group', params: { id: g.id } } });
|
||||
out.push({ id: `grp-${g.id}`, type: 'group', label: g.label, ...(g.description ? { sublabel: g.description } : {}), icon: Boxes as Component, keywords: `${g.label} ${g.description ?? ''}`.toLowerCase(), run: () => ide.setActivity('groups') });
|
||||
}
|
||||
// actions globales
|
||||
out.push({ id: 'act-newSession', type: 'action', label: t('palette.actions.newSession'), icon: Plus as Component, keywords: t('palette.actions.newSession').toLowerCase(), to: { name: 'sessions' } });
|
||||
out.push({ id: 'act-addRepo', type: 'action', label: t('palette.actions.addRepo'), icon: FolderPlus as Component, keywords: t('palette.actions.addRepo').toLowerCase(), to: { name: 'dashboard' } });
|
||||
out.push({ id: 'act-newGroup', type: 'action', label: t('palette.actions.newGroup'), icon: Boxes as Component, keywords: t('palette.actions.newGroup').toLowerCase(), to: { name: 'groups' } });
|
||||
// actions globales : ouvrent des modals / panneaux DANS l'IDE.
|
||||
out.push({ id: 'act-newSession', type: 'action', label: t('palette.actions.newSession'), icon: Plus as Component, keywords: t('palette.actions.newSession').toLowerCase(), run: () => void modals.open(NewSessionModal) });
|
||||
out.push({ id: 'act-addRepo', type: 'action', label: t('palette.actions.addRepo'), icon: FolderPlus as Component, keywords: t('palette.actions.addRepo').toLowerCase(), run: () => ide.setActivity('explorer') });
|
||||
out.push({ id: 'act-newGroup', type: 'action', label: t('palette.actions.newGroup'), icon: Boxes as Component, keywords: t('palette.actions.newGroup').toLowerCase(), run: () => void modals.open(GroupCreateModal) });
|
||||
return out;
|
||||
});
|
||||
|
||||
@@ -144,8 +156,7 @@ function move(delta: number): void {
|
||||
function run(item: PaletteItem | undefined): void {
|
||||
if (!item) return;
|
||||
close();
|
||||
if (item.run) item.run();
|
||||
else if (item.to) void router.push(item.to);
|
||||
item.run();
|
||||
}
|
||||
|
||||
watch(open, async (v) => {
|
||||
|
||||
Reference in New Issue
Block a user