// Arbre Repositories → Worktrees → Sessions, rafraîchi en temps réel (store.onDidChange). // Les nœuds sont reconstruits et mis en cache à chaque changement pour donner une identité stable à // getParent()/reveal() (conscience du workspace, Phase D). import * as vscode from 'vscode'; import type { Store } from '../state/store.js'; import type { ReposNode } from './nodes.js'; import { repoTreeItem, sessionTreeItem, worktreeTreeItem } from './tree-items.js'; function normPath(p: string): string { return p.replace(/\/+$/, '') || '/'; } export class ReposTreeProvider implements vscode.TreeDataProvider { private readonly changeEmitter = new vscode.EventEmitter(); readonly onDidChangeTreeData = this.changeEmitter.event; private readonly repoNodes = new Map(); private readonly worktreeNodes = new Map(); constructor(private readonly store: Store) { store.onDidChange(() => { this.rebuild(); this.changeEmitter.fire(undefined); }); this.rebuild(); } private wtKey(repoId: string, path: string): string { return `${repoId}${normPath(path)}`; } private rebuild(): void { this.repoNodes.clear(); this.worktreeNodes.clear(); for (const repo of this.store.listRepos()) { this.repoNodes.set(repo.id, { kind: 'repo', repo }); for (const worktree of this.store.listWorktrees(repo.id)) { this.worktreeNodes.set(this.wtKey(repo.id, worktree.path), { kind: 'worktree', repoId: repo.id, worktree }); } } } /** nœud worktree mis en cache (pour reveal depuis le mapping workspace). */ worktreeNode(repoId: string, path: string): ReposNode | undefined { return this.worktreeNodes.get(this.wtKey(repoId, path)); } getTreeItem(node: ReposNode): vscode.TreeItem { switch (node.kind) { case 'repo': return repoTreeItem(node.repo); case 'worktree': return worktreeTreeItem(node.worktree); case 'session': { const item = sessionTreeItem(node.session); if (node.session.live) { item.command = { command: 'arboretum.attachSession', title: 'Attach', arguments: [node] }; } return item; } } } getChildren(node?: ReposNode): ReposNode[] { if (!node) return [...this.repoNodes.values()]; if (node.kind === 'repo') { return this.store .listWorktrees(node.repo.id) .map((worktree) => this.worktreeNodes.get(this.wtKey(node.repo.id, worktree.path))) .filter((n): n is ReposNode => n !== undefined); } if (node.kind === 'worktree') { return this.store .sessionsForWorktree(node.worktree.path) .map((session) => ({ kind: 'session', session, repoId: node.repoId, worktreePath: node.worktree.path })); } return []; } getParent(node: ReposNode): ReposNode | undefined { if (node.kind === 'repo') return undefined; if (node.kind === 'worktree') return this.repoNodes.get(node.repoId); return this.worktreeNodes.get(this.wtKey(node.repoId, node.worktreePath)); } }