import * as path from 'node:path'; import * as vscode from 'vscode'; import type { ModelService } from '../model/service'; import { log } from '../util/log'; import type { SyncFeature } from './index'; import { liveBridge } from './liveBridge'; // Kept in step with SYNC_COMMANDS in ./index (which lives in the other bundle). const COMMANDS = [ 'archify.sync.signIn', 'archify.sync.signOut', 'archify.sync.share', 'archify.sync.invite', 'archify.sync.join', 'archify.sync.publish', 'archify.sync.versions', 'archify.sync.stop', 'archify.sync.menu', 'archify.sync.setServer', ] as const; /** * Archify Sync is optional. This registers its commands and the URI handler, * and loads dist/sync.js (Yjs, the WebSocket) only when it is needed: a model * in the workspace is shared, someone signed in on this machine, or a sync * command or link runs. Without that, nothing else happens. */ export function registerSync(context: vscode.ExtensionContext, models: ModelService): vscode.Disposable[] { let feature: Promise | undefined; // Until the sync bundle loads, this entry is the way in; then the bundle's own one takes over. const entry = vscode.window.createStatusBarItem('archify.sync', vscode.StatusBarAlignment.Left, 50); entry.name = 'Archify Sync'; entry.text = '$(cloud) Archify Sync'; entry.tooltip = 'Archify Sync (optional): sign in, share a model with others or join one. Set the server under Settings › Archify › Sync.'; entry.command = 'archify.sync.menu'; // Only where there are Archify files: the extension also starts for JSON and HTML. let archifyHere = false; const showEntry = () => { const wanted = archifyHere && !feature && vscode.workspace.getConfiguration('archify.sync').get('statusBar', true); if (wanted) entry.show(); else entry.hide(); }; void Promise.resolve(vscode.workspace.findFiles('**/*.{archify,archify-registry,archify-decision}', '{**/node_modules/**,**/.git/**}', 1)).then((found) => { archifyHere = found.length > 0; showEntry(); }); const load = (): Promise => (feature ??= (async () => { showEntry(); // A runtime path, so esbuild leaves the sync bundle out of this one. const bundle = path.join(__dirname, 'sync.js'); const mod = require(bundle) as typeof import('./index'); const sync = new mod.SyncFeature(context, models, log(), liveBridge); context.subscriptions.push(sync); await sync.start(); return sync; })()); void (async () => { const used = context.globalState.get('archifySync.used') === true; const shared = used || (await vscode.workspace.findFiles('**/.archify-sync', '{**/node_modules/**,**/.git/**}', 1)).length > 0; if (shared) load().catch((error) => log().error(`sync: did not start: ${String(error)}`)); })(); return [ entry, vscode.workspace.onDidChangeConfiguration((e) => e.affectsConfiguration('archify.sync.statusBar') && showEntry()), liveBridge, ...COMMANDS.map((id) => vscode.commands.registerCommand(id, async (...args: unknown[]) => (await load()).run(id, ...args)), ), vscode.window.registerUriHandler({ handleUri: async (uri) => (await load()).handleUri(uri) }), ]; }