feat(vscode): extension VS Code native (intégration native, pas un webview)
Nouveau workspace packages/vscode (git-arboretum, privé, non publié sur npm), client REST/WS réutilisant @arboretum/shared. Auth Authorization: Bearer sur REST et l'upgrade WS (via `ws`) ; un client Node sans en-tête Origin passe le check Origin strict du serveur. - Arbres temps réel : Repositories (repos → worktrees → sessions) et Groups, via le WebSocket. - Terminaux natifs (vscode.Pseudoterminal) pour attacher/observer une session — rendu et scrollback de VS Code ; décodage UTF-8 streaming + comptabilité ACK dans des modules purs. - Status bar (compteur waiting) + notifications natives sur passage en waiting, réponses Yes/No via la commande WS answer. - Mutations git (create worktree, commit, push, promote), start/kill/hide/resume/fork, session de groupe ; conscience du workspace (reveal + start/create here). - Bundle esbuild (format cjs, external vscode) inlinant @arboretum/shared → VSIX autonome. Logique réutilisable sans import vscode → testée par vitest (19 tests). - CI : .gitea/workflows/vscode-release.yml package le VSIX sur tag vscode-vX.Y.Z (artefact + asset de release best-effort). build:vscode hors du build principal (comme le site). - spikes/s5-vscode/STUDY.md : décision de conception (GO phasé A→D), marquée implémentée.
This commit is contained in:
64
packages/vscode/src/views/groups-tree.ts
Normal file
64
packages/vscode/src/views/groups-tree.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
// Arbre Groups → repos membres → worktrees/sessions, plus les sessions de groupe (multi-repo, --add-dir)
|
||||
// rattachées directement au groupe. Rafraîchi en temps réel (store.onDidChange).
|
||||
import * as vscode from 'vscode';
|
||||
import type { Store } from '../state/store.js';
|
||||
import type { GroupsNode } from './nodes.js';
|
||||
import { groupTreeItem, repoTreeItem, sessionTreeItem, worktreeTreeItem } from './tree-items.js';
|
||||
|
||||
export class GroupsTreeProvider implements vscode.TreeDataProvider<GroupsNode> {
|
||||
private readonly changeEmitter = new vscode.EventEmitter<GroupsNode | undefined>();
|
||||
readonly onDidChangeTreeData = this.changeEmitter.event;
|
||||
|
||||
constructor(private readonly store: Store) {
|
||||
store.onDidChange(() => this.changeEmitter.fire(undefined));
|
||||
}
|
||||
|
||||
getTreeItem(node: GroupsNode): vscode.TreeItem {
|
||||
switch (node.kind) {
|
||||
case 'group':
|
||||
return groupTreeItem(node.group);
|
||||
case 'repo':
|
||||
return repoTreeItem(node.repo);
|
||||
case 'worktree':
|
||||
return worktreeTreeItem(node.worktree);
|
||||
case 'group-session':
|
||||
case 'session': {
|
||||
const item = sessionTreeItem(node.session);
|
||||
if (node.session.live) {
|
||||
item.command = { command: 'arboretum.attachSession', title: 'Attach', arguments: [node] };
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getChildren(node?: GroupsNode): GroupsNode[] {
|
||||
if (!node) {
|
||||
return this.store.listGroups().map((group) => ({ kind: 'group', group }));
|
||||
}
|
||||
if (node.kind === 'group') {
|
||||
const children: GroupsNode[] = [];
|
||||
// sessions de groupe (couvrant plusieurs repos) en tête
|
||||
for (const session of this.store.sessionsForGroup(node.group.id)) {
|
||||
children.push({ kind: 'group-session', session, groupId: node.group.id });
|
||||
}
|
||||
// puis les repos membres
|
||||
for (const repoId of node.group.repoIds) {
|
||||
const repo = this.store.getRepo(repoId);
|
||||
if (repo) children.push({ kind: 'repo', repo });
|
||||
}
|
||||
return children;
|
||||
}
|
||||
if (node.kind === 'repo') {
|
||||
return this.store
|
||||
.listWorktrees(node.repo.id)
|
||||
.map((worktree) => ({ kind: 'worktree', repoId: node.repo.id, worktree }));
|
||||
}
|
||||
if (node.kind === 'worktree') {
|
||||
return this.store
|
||||
.sessionsForWorktree(node.worktree.path)
|
||||
.map((session) => ({ kind: 'session', session, repoId: node.repoId, worktreePath: node.worktree.path }));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user