components/ide/TerminalDock.vue + TerminalDockTabs.vue : dock à onglets, une TerminalView par session du dock, toutes montées en v-show pour préserver l'attache WS (seule l'active visible). Fermer un onglet démonte réellement la vue -> detach() propre, pas de fuite. DialogPrompt au-dessus du terminal actif en attente. Repli via la barre de statut ou le chevron. Branché dans IdeShell (section dock redimensionnable, splitter inversé). 427 tests.
51 lines
1.9 KiB
Vue
51 lines
1.9 KiB
Vue
<template>
|
|
<div class="flex h-full min-h-0 flex-col bg-surface-0">
|
|
<TerminalDockTabs />
|
|
<div class="relative min-h-0 flex-1">
|
|
<!-- toutes les sessions restent montées (v-show) pour préserver l'attache WS ; seule l'active
|
|
est visible. Fermer un onglet démonte réellement la TerminalView -> détache propre. -->
|
|
<div
|
|
v-for="sid in ide.dockSessionIds"
|
|
v-show="sid === ide.activeDockSessionId"
|
|
:key="sid"
|
|
class="absolute inset-0 flex flex-col"
|
|
>
|
|
<DialogPrompt v-if="isWaiting(sid)" :session="sessionOf(sid)!" class="px-2 pt-2" />
|
|
<div class="min-h-0 flex-1">
|
|
<TerminalView :session-id="sid" :mode="modeOf(sid)" />
|
|
</div>
|
|
</div>
|
|
<EmptyState
|
|
v-if="ide.dockSessionIds.length === 0"
|
|
:icon="SquareTerminal"
|
|
:title="t('ide.noTerminal')"
|
|
:hint="t('ide.noTerminalHint')"
|
|
class="m-6"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n';
|
|
import { SquareTerminal } from '@lucide/vue';
|
|
import type { SessionSummary } from '@arboretum/shared';
|
|
import { useIdeStore } from '../../stores/ide';
|
|
import { useSessionsStore } from '../../stores/sessions';
|
|
import TerminalDockTabs from './TerminalDockTabs.vue';
|
|
import TerminalView from '../TerminalView.vue';
|
|
import DialogPrompt from '../DialogPrompt.vue';
|
|
import EmptyState from '../ui/EmptyState.vue';
|
|
|
|
const { t } = useI18n();
|
|
const ide = useIdeStore();
|
|
const sessions = useSessionsStore();
|
|
|
|
const sessionOf = (sid: string): SessionSummary | null => sessions.sessions.find((s) => s.id === sid) ?? null;
|
|
const modeOf = (sid: string): 'interactive' | 'observer' => (sessionOf(sid)?.attachable ? 'interactive' : 'observer');
|
|
const isWaiting = (sid: string): boolean => {
|
|
const s = sessionOf(sid);
|
|
return !!s && s.live && s.activity === 'waiting' && !!s.dialog;
|
|
};
|
|
</script>
|