0.10.0: Archify Sync, optional live collaboration on a model
Several people edit one model at once through a server you run yourself (archify-vscode-ext-sync); the files stay ordinary files on each disk. Sign-in through the browser, offline-first with later merge, teammates' cursors in the Source view and text editors, and a Shared section on the model page with people, versions, Compare and Restore. Without a server nothing changes: the sync code is a separate bundle that loads only when a model is shared, after a sign-in, or when a sync command or invite link is used.
This commit is contained in:
1 parent
a9021312b0
commit
c99f3e5fe8
27 files changed
+2899
-14
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// What the model page shows of a shared model. Lives in the main bundle; the
|
||||
// sync bundle plugs in as the provider once it loads. Without it, the model
|
||||
// page has no Live section and nothing else changes.
|
||||
|
||||
export interface LivePerson {
|
||||
name: string;
|
||||
/** Model-relative path of the file the person has open. */
|
||||
file?: string;
|
||||
me?: boolean;
|
||||
}
|
||||
|
||||
export interface LiveVersion {
|
||||
id: number;
|
||||
label: string;
|
||||
kind: 'snapshot' | 'publish' | 'restore';
|
||||
name?: string;
|
||||
/** ISO time. */
|
||||
at: string;
|
||||
restoredFrom?: string;
|
||||
}
|
||||
|
||||
export interface LivePanel {
|
||||
name: string;
|
||||
host: string;
|
||||
state: 'connecting' | 'online' | 'offline' | 'signed-out' | 'forbidden' | 'outdated';
|
||||
people: LivePerson[];
|
||||
versions?: LiveVersion[];
|
||||
versionsProblem?: string;
|
||||
}
|
||||
|
||||
/** Someone else's cursor in a file, as offsets into its "\n" text. */
|
||||
export interface LiveCursor {
|
||||
name: string;
|
||||
color: string;
|
||||
anchor: number;
|
||||
head: number;
|
||||
}
|
||||
|
||||
/** A selection as offsets into a file's "\n" text. */
|
||||
export interface LiveSelection {
|
||||
anchor: number;
|
||||
head: number;
|
||||
}
|
||||
|
||||
export type LiveAction =
|
||||
| { kind: 'publish' }
|
||||
| { kind: 'invite' }
|
||||
| { kind: 'signIn' }
|
||||
| { kind: 'versions' }
|
||||
| { kind: 'compare'; version: number }
|
||||
| { kind: 'restore'; version: number };
|
||||
|
||||
export interface LiveProvider {
|
||||
panel(root: vscode.Uri): Promise<LivePanel | undefined>;
|
||||
act(root: vscode.Uri, action: LiveAction): Promise<void>;
|
||||
/** Others' cursors in a shared file; empty when the file is not shared. */
|
||||
cursors(file: vscode.Uri): LiveCursor[];
|
||||
/** This person's cursor in a page's Source view; undefined when it closes. */
|
||||
setCursor(file: vscode.Uri, selection: LiveSelection | undefined): void;
|
||||
}
|
||||
|
||||
class LiveBridge implements vscode.Disposable {
|
||||
private provider: LiveProvider | undefined;
|
||||
private readonly emitter = new vscode.EventEmitter<vscode.Uri | undefined>();
|
||||
/** A model root whose Live section changed; undefined for all of them. */
|
||||
readonly onDidChange = this.emitter.event;
|
||||
private readonly cursorEmitter = new vscode.EventEmitter<void>();
|
||||
/** Someone's cursor moved somewhere; ask `cursors` for the file you show. */
|
||||
readonly onDidChangeCursors = this.cursorEmitter.event;
|
||||
|
||||
setProvider(provider: LiveProvider | undefined): void {
|
||||
this.provider = provider;
|
||||
this.emitter.fire(undefined);
|
||||
this.cursorEmitter.fire();
|
||||
}
|
||||
|
||||
changed(root?: vscode.Uri): void {
|
||||
this.emitter.fire(root);
|
||||
}
|
||||
|
||||
async panel(root: vscode.Uri): Promise<LivePanel | undefined> {
|
||||
return this.provider?.panel(root);
|
||||
}
|
||||
|
||||
async act(root: vscode.Uri, action: LiveAction): Promise<void> {
|
||||
await this.provider?.act(root, action);
|
||||
}
|
||||
|
||||
cursorsChanged(): void {
|
||||
this.cursorEmitter.fire();
|
||||
}
|
||||
|
||||
cursors(file: vscode.Uri): LiveCursor[] {
|
||||
return this.provider?.cursors(file) ?? [];
|
||||
}
|
||||
|
||||
setCursor(file: vscode.Uri, selection: LiveSelection | undefined): void {
|
||||
this.provider?.setCursor(file, selection);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.emitter.dispose();
|
||||
this.cursorEmitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export const liveBridge = new LiveBridge();
|
||||
export type { LiveBridge };
|
||||
Reference in new issue
Block a user