From 4798732856121af758a50ad91a7e613d15d7b0fd Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Fri, 17 Jul 2026 17:40:44 +0200 Subject: [PATCH] =?UTF-8?q?feat(web):=20IDE=20responsive=20(panneau=20uniq?= =?UTF-8?q?ue=20mobile)=20+=20d=C3=A9faut=20adapt=C3=A9=20=C3=A0=20l'?= =?UTF-8?q?=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IdeShell dégrade sous le breakpoint md en un panneau unique (Explorateur/Éditeur/Terminal/Git) commuté par une barre d'onglets basse, avec un bouton retour vers le dashboard. La redirection / envoie le mobile vers /dashboard (supervision + nav) et le desktop vers /ide. Garde-fou : pas de '<' dans les commentaires de template Vue (rolldown casse le build alors que vue-tsc passe). 431 tests. --- packages/web/src/components/ide/IdeShell.vue | 101 +++++++++++++++---- packages/web/src/router/index.ts | 4 +- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/packages/web/src/components/ide/IdeShell.vue b/packages/web/src/components/ide/IdeShell.vue index 786e166..de76c65 100644 --- a/packages/web/src/components/ide/IdeShell.vue +++ b/packages/web/src/components/ide/IdeShell.vue @@ -1,29 +1,57 @@ @@ -31,8 +59,10 @@ // 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 { onMounted, watch } from 'vue'; +import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { useRoute } from 'vue-router'; +import { useI18n } from 'vue-i18n'; +import { FileCode, FolderTree, GitCompare, Home, SquareTerminal } from '@lucide/vue'; import { decodeWtKey } from '@arboretum/shared'; import { useIdeStore, wtKey } from '../../stores/ide'; import { useWorktreesStore } from '../../stores/worktrees'; @@ -43,12 +73,39 @@ import PanelSplitter from './PanelSplitter.vue'; import StatusBar from './StatusBar.vue'; import EditorArea from './EditorArea.vue'; import TerminalDock from './TerminalDock.vue'; +import ProjectTree from './ProjectTree.vue'; +import GitPanel from './GitPanel.vue'; +const { t } = useI18n(); const ide = useIdeStore(); const worktrees = useWorktreesStore(); const sessions = useSessionsStore(); const route = useRoute(); +// --- responsive : panneau unique sous le breakpoint md (768px) --- +const isMobile = ref(false); +let mql: MediaQueryList | null = null; +const syncMobile = (): void => { + isMobile.value = !!mql?.matches; +}; +onMounted(() => { + mql = window.matchMedia('(max-width: 767px)'); + syncMobile(); + mql.addEventListener('change', syncMobile); +}); +onBeforeUnmount(() => mql?.removeEventListener('change', syncMobile)); + +const mobilePanels = computed(() => [ + { key: 'explorer', icon: FolderTree, label: t('ide.activity.explorer') }, + { key: 'editor', icon: FileCode, label: t('workspace.editor') }, + { key: 'terminal', icon: SquareTerminal, label: t('ide.terminals') }, + { key: 'git', icon: GitCompare, label: t('ide.activity.git') }, +]); +const mobileTitle = computed(() => mobilePanels.value.find((p) => p.key === ide.mobilePanel)?.label ?? ''); +const setMobilePanel = (key: string): void => { + ide.mobilePanel = key; +}; + // Deep-link : /workspace/:repoId/:wt (ex. lien de l'extension VS Code) cible un worktree précis. // On le rend actif, on déplie son sous-arbre, et on ouvre éventuellement ?file=. function applyDeepLink(): void { diff --git a/packages/web/src/router/index.ts b/packages/web/src/router/index.ts index 8bec1cf..50653ae 100644 --- a/packages/web/src/router/index.ts +++ b/packages/web/src/router/index.ts @@ -14,7 +14,9 @@ export const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', name: 'login', component: () => import('../views/LoginView.vue'), meta: { layout: 'bare' } }, - { path: '/', redirect: { name: 'ide' } }, + // Desktop : l'IDE est la vue par défaut. Mobile : supervision (dashboard + nav mobile), + // l'IDE reste accessible via la nav mais dégrade en panneau unique. + { path: '/', redirect: () => (typeof window !== 'undefined' && window.innerWidth < 768 ? { name: 'dashboard' } : { name: 'ide' }) }, { path: '/dashboard', name: 'dashboard', component: () => import('../views/DashboardView.vue'), meta: { layout: 'shell' } }, { path: '/sessions', name: 'sessions', component: () => import('../views/SessionsListView.vue'), meta: { layout: 'shell' } }, { path: '/sessions/:id', name: 'session', component: () => import('../views/SessionView.vue'), meta: { layout: 'fullbleed' } },