feat: groupes de travail (P5)
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.
This commit is contained in:
39
packages/web/src/components/TerminalCell.vue
Normal file
39
packages/web/src/components/TerminalCell.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user