Add New Diagram command with starter templates

- Archify: New Diagram… (Explorer folder menu, File > New File…, Command
  Palette) creates a .archify file from a template and opens the diagram
- Six YAML templates: architecture, workflow, sequence, dataflow,
  lifecycle and an everyday plan; connections carry ids, workflows use
  schema 2
- Title and meta.output follow the file name; template comments are kept
- Tests: every template passes archify validate; integration check for
  the command

Closes #2
This commit is contained in:
2026-10-04 21:33:11 +03:00
parent d1eab34f7f
commit 9357176673
13 changed files with 582 additions and 1 deletions
+59
View File
@@ -7,6 +7,7 @@ import { HTML_VIEW_TYPE, HtmlViewerProvider } from './htmlViewer';
import { DIAGRAM_EDITOR_VIEW_TYPE, PreviewManager } from './preview';
import { isEvidenceError, renderDiagram, renderToFile } from './renderer';
import { clearGitRootCache, evidenceMode, log, readText, runtimeFor } from './runtime';
import { diagramFromTemplate, diagramName, invalidNameReason, TEMPLATES } from './templates';
const JSON_LANGUAGES = new Set(['json', 'jsonc']);
@@ -61,6 +62,26 @@ export function activate(context: vscode.ExtensionContext): void {
const openHtmlViewer = (uri: vscode.Uri, viewColumn?: vscode.ViewColumn) =>
vscode.commands.executeCommand('vscode.openWith', uri, HTML_VIEW_TYPE, viewColumn);
const exists = async (uri: vscode.Uri) => {
try {
await vscode.workspace.fs.stat(uri);
return true;
} catch {
return false;
}
};
/** Folder for a new diagram: the clicked folder (or a file's folder), else a workspace folder. */
async function newDiagramFolder(arg?: unknown): Promise<vscode.Uri | undefined> {
if (arg instanceof vscode.Uri) {
const stat = await vscode.workspace.fs.stat(arg);
return stat.type & vscode.FileType.Directory ? arg : vscode.Uri.joinPath(arg, '..');
}
const folders = vscode.workspace.workspaceFolders ?? [];
if (folders.length <= 1) return folders[0]?.uri;
return (await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Folder for the new diagram' }))?.uri;
}
context.subscriptions.push(
vscode.commands.registerCommand('archify.showPreview', (arg?: unknown) => {
const uri = sourceUri(arg);
@@ -88,6 +109,44 @@ export function activate(context: vscode.ExtensionContext): void {
if (uri) return openHtmlViewer(uri);
}),
vscode.commands.registerCommand('archify.newDiagram', async (arg?: unknown) => {
const template = await vscode.window.showQuickPick(
TEMPLATES.map((item) => ({ label: item.label, description: item.description, template: item })),
{ title: 'New Archify Diagram', placeHolder: 'Kind of diagram' },
);
if (!template) return;
const { file, defaultName } = template.template;
const folder = await newDiagramFolder(arg);
let target: vscode.Uri | undefined;
if (folder) {
const name = await vscode.window.showInputBox({
title: 'New Archify Diagram',
prompt: `File name in ${vscode.workspace.asRelativePath(folder, true)}`,
value: defaultName,
validateInput: async (value) =>
invalidNameReason(value) ??
((await exists(vscode.Uri.joinPath(folder, `${diagramName(value)}.archify`))) ? `${diagramName(value)}.archify already exists.` : undefined),
});
if (name === undefined) return;
target = vscode.Uri.joinPath(folder, `${diagramName(name)}.archify`);
} else {
// No workspace: let the user choose where to save.
target = await vscode.window.showSaveDialog({
title: 'New Archify Diagram',
filters: { 'Archify Diagram': ['archify'] },
saveLabel: 'Create',
});
if (!target) return;
if (!isArchifyFile(target.path)) target = target.with({ path: `${target.path}.archify` });
}
const templateText = await readText(vscode.Uri.joinPath(context.extensionUri, 'templates', file));
const text = diagramFromTemplate(templateText, diagramBaseName(target.path));
await vscode.workspace.fs.writeFile(target, Buffer.from(text, 'utf8'));
await vscode.commands.executeCommand('vscode.openWith', target, DIAGRAM_EDITOR_VIEW_TYPE);
}),
vscode.commands.registerCommand('archify.refreshPreview', () => previews.activePreview?.update(true)),
vscode.commands.registerCommand('archify.showSource', async () => {