Ajoute la notion de groupe — collection nommée de repos (membership légère persistée, many-to-many) — pour piloter plusieurs repos en simultané : - DB : migration #5 (tables groups + group_repos, FK ON DELETE CASCADE). - GroupManager (synchrone, EventEmitter) + routes REST /api/v1/groups ; ne persiste que la membership, worktrees/sessions filtrés par repoId côté client. - Protocole : GroupSummary, DTOs, topic WS « groups », events group_update/removed (additifs, PROTOCOL_VERSION inchangé). - Web : store groups, GroupsListView, GroupView (réutilise RepoSection), grille multi-terminaux (TerminalGrid/TerminalCell), action « feature cross-repo » (orchestration client, tolérante aux échecs partiels), routes + i18n EN/FR. - Tests : group-manager.test.ts + extension protocol.test.ts ; acceptance-p5.mjs.
40 lines
1.8 KiB
Vue
40 lines
1.8 KiB
Vue
<template>
|
|
<!-- focus-within → anneau visuel : montre quelle cellule reçoit la frappe clavier -->
|
|
<div class="flex min-h-0 flex-col overflow-hidden rounded-lg border border-zinc-800 bg-[#09090b] focus-within:ring-2 focus-within:ring-sky-600">
|
|
<header class="flex items-center gap-2 border-b border-zinc-800 px-2 py-1">
|
|
<SessionStateBadge :session="session" />
|
|
<span class="truncate font-mono text-xs text-zinc-300" :title="session.cwd">{{ title }}</span>
|
|
<span class="ml-auto shrink-0 font-mono text-[11px] text-zinc-500">{{ session.command }}</span>
|
|
</header>
|
|
<DialogPrompt v-if="isWaiting" :session="session" class="px-2 pt-2" />
|
|
<div class="min-h-0 flex-1">
|
|
<TerminalView :session-id="session.id" mode="interactive" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import type { SessionSummary } from '@arboretum/shared';
|
|
import { useWorktreesStore } from '../stores/worktrees';
|
|
import SessionStateBadge from './SessionStateBadge.vue';
|
|
import DialogPrompt from './DialogPrompt.vue';
|
|
import TerminalView from './TerminalView.vue';
|
|
|
|
const props = defineProps<{ session: SessionSummary }>();
|
|
const worktrees = useWorktreesStore();
|
|
|
|
// libellé : « repo · branche » si le worktree est connu, sinon le dernier segment du cwd.
|
|
const title = computed(() => {
|
|
const wt = worktrees.worktrees.find((w) => w.path === props.session.cwd);
|
|
if (wt) {
|
|
const repo = worktrees.repos.find((r) => r.id === wt.repoId);
|
|
const branch = wt.branch ?? wt.head.slice(0, 7);
|
|
return repo ? `${repo.label} · ${branch}` : branch;
|
|
}
|
|
return props.session.cwd.split('/').filter(Boolean).pop() ?? props.session.cwd;
|
|
});
|
|
|
|
const isWaiting = computed(() => props.session.live && props.session.activity === 'waiting' && !!props.session.dialog);
|
|
</script>
|