Support .archify diagram files (0.2.0)

.archify files are Archify JSON diagram sources. They open as JSON,
preview with the same commands, report validation problems, and get
completion from a schema that dispatches on diagram_type. Adds a
hello.archify example and tests for detection, the dispatching schema
and the VS Code behaviour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 12:56:12 +03:00
co-authored by Claude Opus 5.5
parent 22815a9940
commit 30e2faa38c
15 changed files with 458 additions and 24 deletions
+17
View File
@@ -13,6 +13,7 @@ export const EVIDENCE_COLLECTIONS: Record<DiagramType, string> = {
};
const FILENAME_RE = /\.(architecture|workflow|sequence|dataflow|lifecycle)\.json$/i;
const ARCHIFY_EXT_RE = /\.archify$/i;
const MAX_SNIFF_BYTES = 8 * 1024 * 1024;
export function isDiagramType(value: unknown): value is DiagramType {
@@ -24,6 +25,22 @@ export function typeFromFilename(fileName: string): DiagramType | undefined {
return match ? (match[1].toLowerCase() as DiagramType) : undefined;
}
/** `name.archify`: an Archify diagram whose type comes from `diagram_type`. */
export function isArchifyFile(fileName: string): boolean {
return ARCHIFY_EXT_RE.test(fileName);
}
/** File names that are diagram sources regardless of their content. */
export function isDiagramFileName(fileName: string): boolean {
return isArchifyFile(fileName) || FILENAME_RE.test(fileName);
}
/** Base name without the diagram source extension (`web.archify` → `web`). */
export function diagramBaseName(fileName: string): string {
const base = fileName.split(/[\\/]/).pop() ?? fileName;
return base.replace(ARCHIFY_EXT_RE, '').replace(/\.json$/i, '');
}
export interface DiagramSource {
type: DiagramType;
diagram: Record<string, unknown>;
+8 -6
View File
@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import * as vscode from 'vscode';
import { isArchifyHtml, readDiagramSource } from './detect';
import { diagramBaseName, isArchifyHtml, isDiagramFileName, readDiagramSource } from './detect';
import { DiagnosticsController } from './diagnostics';
import { HTML_VIEW_TYPE, HtmlViewerProvider } from './htmlViewer';
import { PreviewManager } from './preview';
@@ -35,7 +35,9 @@ export function activate(context: vscode.ExtensionContext): void {
let contextTimer: NodeJS.Timeout | undefined;
const updateContext = () => {
const doc = vscode.window.activeTextEditor?.document;
const diagram = Boolean(doc && JSON_LANGUAGES.has(doc.languageId) && readDiagramSource(doc.getText(), doc.fileName));
const diagram = Boolean(
doc && JSON_LANGUAGES.has(doc.languageId) && (isDiagramFileName(doc.fileName) || readDiagramSource(doc.getText(), doc.fileName)),
);
const html = Boolean(doc && doc.languageId === 'html' && isArchifyHtml(doc.getText()));
void vscode.commands.executeCommand('setContext', 'archify.isDiagramSource', diagram);
void vscode.commands.executeCommand('setContext', 'archify.isArchifyHtml', html);
@@ -96,12 +98,12 @@ export function activate(context: vscode.ExtensionContext): void {
const text = await readText(uri);
const source = readDiagramSource(text, uri.path);
if (!source) {
vscode.window.showWarningMessage(`Archify: ${path.basename(uri.path)} is not an Archify diagram source.`);
vscode.window.showWarningMessage(`Archify: ${path.basename(uri.path)} has no "diagram_type" (architecture, workflow, sequence, dataflow or lifecycle).`);
return;
}
const meta = (source.diagram.meta ?? {}) as { output?: unknown };
const authored = typeof meta.output === 'string' && !/[\\/]/.test(meta.output) ? meta.output : undefined;
const fileName = authored ?? `${path.basename(uri.path).replace(/\.json$/i, '')}.html`;
const fileName = authored ?? `${diagramBaseName(uri.path)}.html`;
const defaultUri = uri.scheme === 'file' ? vscode.Uri.joinPath(uri, '..', fileName) : undefined;
const target = await vscode.window.showSaveDialog({
defaultUri,
@@ -148,7 +150,7 @@ export function activate(context: vscode.ExtensionContext): void {
const text = await readText(uri);
const source = readDiagramSource(text, uri.path);
if (!source) {
vscode.window.showWarningMessage(`Archify: ${path.basename(uri.path)} is not an Archify diagram source.`);
vscode.window.showWarningMessage(`Archify: ${path.basename(uri.path)} has no "diagram_type" (architecture, workflow, sequence, dataflow or lifecycle).`);
return;
}
const runtime = await runtimeFor(context, uri);
@@ -162,7 +164,7 @@ export function activate(context: vscode.ExtensionContext): void {
}
const dir = path.join(context.globalStorageUri.fsPath, 'browser');
await fs.mkdir(dir, { recursive: true });
const file = path.join(dir, `${path.basename(uri.path).replace(/\.json$/i, '')}.html`);
const file = path.join(dir, `${diagramBaseName(uri.path)}.html`);
await fs.writeFile(file, result.html, 'utf8');
await vscode.env.openExternal(vscode.Uri.file(file));
}),
+4 -1
View File
@@ -92,7 +92,10 @@ class DiagramPreview implements vscode.Disposable {
}
const source = readDiagramSource(text, this.uri.path);
if (!source) {
this.showMessage('Not an Archify diagram', 'The file has no "diagram_type" of architecture, workflow, sequence, dataflow or lifecycle.');
this.showMessage(
'Nothing to show yet',
'Set "diagram_type" to architecture, workflow, sequence, dataflow or lifecycle, and make sure the file is valid JSON.',
);
return;
}