From c66e819abe1737d0d138f619e9e76e2313e0f7f9 Mon Sep 17 00:00:00 2001 From: Root At Skic Date: Tue, 20 Oct 2026 10:55:00 +0300 Subject: [PATCH] =?UTF-8?q?Archify=20|=20New=20Registry=E2=80=A6;=20creato?= =?UTF-8?q?rs=20ordered=20Model,=20Registry,=20Diagram,=20Decision;=200.8.?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++ README.md | 2 +- package-lock.json | 4 +-- package.json | 58 ++++++++++++++++++++++------------- src/commands/modelCommands.ts | 22 ++++++++++++- test/integration/suite.ts | 24 +++++++++++++++ 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff17f69..3918627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.8.1 — 2026-10-20 + +- **Archify | New Registry…** on a folder (also *File → New File…* and the Command Palette) creates an `.archify-registry` file from the template and opens it. +- The Archify entries in the Explorer menu and *File → New File…* come in the order New Model, New Registry, New Diagram, New Decision. + ## 0.8.0 — 2026-10-19 - **Architecture decisions.** A model can hold decisions: one `*.archify-decision` file each, anywhere under the model root, with an id (unique in the model), title, status, date, the registry `elements` and `diagrams` it is about, `supersedes`, and prose for context, decision and consequences. diff --git a/README.md b/README.md index b00b56d..dcd11de 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ The Archify CLI and the AI agents that use it write diagrams as JSON, named `nam - **Schema help**: completion and hover from Archify's own schemas, through the YAML extension for `.archify` files. - **Source links**: in source-backed diagrams, clicking a source reference opens that file and line range in the editor. If the file is not in the workspace, the web link opens instead. - **Export**: the diagram's own Export menu (PNG, SVG, share card, …) saves through a VS Code save dialog. -- **Commands**: *Archify | New Diagram…*, *Archify | New Model…*, *Archify | New Decision…*, *Archify | Open Model*, *Archify | Render to HTML File…*, *Archify | Open in Browser*, *Archify | Refresh Preview*, *Archify | Show Source*. +- **Commands**: *Archify | New Model…*, *Archify | New Registry…*, *Archify | New Diagram…*, *Archify | New Decision…*, *Archify | Open Model*, *Archify | Render to HTML File…*, *Archify | Open in Browser*, *Archify | Refresh Preview*, *Archify | Show Source*. ## 3D view diff --git a/package-lock.json b/package-lock.json index fa849a9..22af545 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "archify-vscode-ext", - "version": "0.8.0", + "version": "0.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "archify-vscode-ext", - "version": "0.8.0", + "version": "0.8.1", "license": "MIT", "dependencies": { "three": "^0.186.1", diff --git a/package.json b/package.json index eed3412..701ca03 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "archify-vscode-ext", "displayName": "Archify Diagram Viewer", "description": "Archify diagrams in VS Code. Write diagrams as .archify files in YAML: they open as the interactive diagram, with live updates, validation, templates and completion. Also previews Archify's own JSON files (*.architecture.json, …) and opens rendered Archify HTML.", - "version": "0.8.0", + "version": "0.8.1", "publisher": "root-at-skic", "license": "MIT", "sponsor": { @@ -49,16 +49,21 @@ ], "contributes": { "commands": [ - { - "command": "archify.newDiagram", - "title": "Archify | New Diagram…", - "icon": "$(new-file)" - }, { "command": "archify.newModel", "title": "Archify | New Model…", "icon": "$(new-folder)" }, + { + "command": "archify.newRegistry", + "title": "Archify | New Registry…", + "icon": "$(symbol-field)" + }, + { + "command": "archify.newDiagram", + "title": "Archify | New Diagram…", + "icon": "$(new-file)" + }, { "command": "archify.newDecision", "title": "Archify | New Decision…", @@ -179,39 +184,44 @@ ], "explorer/context": [ { - "command": "archify.newDiagram", + "command": "archify.newModel", "when": "explorerResourceIsFolder", "group": "zzz_archify@1" }, { - "command": "archify.newModel", + "command": "archify.newRegistry", "when": "explorerResourceIsFolder", "group": "zzz_archify@2" }, + { + "command": "archify.newDiagram", + "when": "explorerResourceIsFolder", + "group": "zzz_archify@3" + }, { "command": "archify.newDecision", "when": "explorerResourceIsFolder", - "group": "zzz_archify@2" + "group": "zzz_archify@4" }, { "command": "archify.showPreview", "when": "resourceFilename =~ /(\\.archify|\\.archify-registry|\\.archify-decision|\\.(architecture|workflow|sequence|dataflow|lifecycle)\\.json)$/", - "group": "zzz_archify@3" - }, - { - "command": "archify.renderToHtml", - "when": "resourceFilename =~ /(\\.archify|\\.(architecture|workflow|sequence|dataflow|lifecycle)\\.json)$/", - "group": "zzz_archify@4" + "group": "zzz_archify@5" }, { "command": "archify.openHtmlViewer", "when": "resourceExtname == .html", - "group": "zzz_archify@3" + "group": "zzz_archify@6" + }, + { + "command": "archify.renderToHtml", + "when": "resourceFilename =~ /(\\.archify|\\.(architecture|workflow|sequence|dataflow|lifecycle)\\.json)$/", + "group": "zzz_archify@7" }, { "command": "archify.openModel", "when": "resourceFilename =~ /\\.archify(-registry|-decision)?$/", - "group": "zzz_archify@5" + "group": "zzz_archify@8" } ], "editor/title/context": [ @@ -223,16 +233,20 @@ ], "file/newFile": [ { - "command": "archify.newDiagram", - "group": "file" + "command": "archify.newModel", + "group": "file@1" }, { - "command": "archify.newModel", - "group": "file" + "command": "archify.newRegistry", + "group": "file@2" + }, + { + "command": "archify.newDiagram", + "group": "file@3" }, { "command": "archify.newDecision", - "group": "file" + "group": "file@4" } ] }, diff --git a/src/commands/modelCommands.ts b/src/commands/modelCommands.ts index 53f776f..87bbbf0 100644 --- a/src/commands/modelCommands.ts +++ b/src/commands/modelCommands.ts @@ -2,7 +2,8 @@ import * as vscode from 'vscode'; import { MODEL_EXAMPLES, mainFromExample } from '../language/examples'; import { diagramName, invalidNameReason } from '../language/templates'; import { DECISION_EXT, slugify } from '../model/decisions'; -import { MAIN_FILE } from '../model/format'; +import { MAIN_FILE, REGISTRY_EXT } from '../model/format'; +import { REGISTRY_VIEW_TYPE } from '../views/registryView'; import { DECISION_VIEW_TYPE } from '../views/decisionView'; import { readText } from '../util/files'; import { DIAGRAM_EDITOR_VIEW_TYPE } from '../views/diagramView'; @@ -32,6 +33,25 @@ export function modelCommands(ctx: CommandContext): vscode.Disposable[] { await vscode.commands.executeCommand('vscode.openWith', vscode.Uri.joinPath(root, MAIN_FILE), DIAGRAM_EDITOR_VIEW_TYPE, vscode.ViewColumn.Active); }), + // Creates a registry file in the clicked folder and opens it. + vscode.commands.registerCommand('archify.newRegistry', async (arg?: unknown) => { + const folder = await parentFolder(arg); + if (!folder) return; + const root = await ctx.models.rootOf(vscode.Uri.joinPath(folder, 'x')); + const name = await vscode.window.showInputBox({ + title: 'Archify | New Registry', + prompt: `File name in ${vscode.workspace.asRelativePath(folder, true)}${root ? '' : ' (not inside a model: add a main.archify above it so diagrams can refer to its elements)'}`, + value: 'elements', + validateInput: async (value) => + invalidNameReason(value) ?? ((await exists(vscode.Uri.joinPath(folder, `${diagramName(value).replace(/\.archify-registry$/i, '')}${REGISTRY_EXT}`))) ? `${diagramName(value)}${REGISTRY_EXT} already exists.` : undefined), + }); + if (name === undefined) return; + const target = vscode.Uri.joinPath(folder, `${diagramName(name).replace(/\.archify-registry$/i, '')}${REGISTRY_EXT}`); + const template = await readText(vscode.Uri.joinPath(ctx.extension.extensionUri, 'templates', 'registry.archify-registry')); + await vscode.workspace.fs.writeFile(target, Buffer.from(template, 'utf8')); + await vscode.commands.executeCommand('vscode.openWith', target, REGISTRY_VIEW_TYPE); + }), + // Creates a decision file in the clicked folder of a model and opens it. vscode.commands.registerCommand('archify.newDecision', async (arg?: unknown) => { const folder = await parentFolder(arg); diff --git a/test/integration/suite.ts b/test/integration/suite.ts index f37b1f0..d6565fb 100644 --- a/test/integration/suite.ts +++ b/test/integration/suite.ts @@ -349,6 +349,30 @@ export async function run(): Promise { await vscode.workspace.fs.delete(bad); }); + await check('Archify | New Registry… creates a registry file and opens the registry view; creators are ordered', async () => { + const commands = vscode.extensions.getExtension(EXTENSION_ID)?.packageJSON.contributes as { commands: { command: string }[]; menus: Record }; + const creators = commands.menus['explorer/context'].filter((m) => /^archify\.new/.test(m.command)); + assert.deepEqual(creators.map((m) => m.command), ['archify.newModel', 'archify.newRegistry', 'archify.newDiagram', 'archify.newDecision']); + assert.deepEqual(creators.map((m) => m.group), ['zzz_archify@1', 'zzz_archify@2', 'zzz_archify@3', 'zzz_archify@4']); + const folder = workspaceFile('model/registry'); + const window = vscode.window as unknown as Record; + const originalInput = window.showInputBox; + window.showInputBox = async () => 'partners'; + try { + await vscode.commands.executeCommand('archify.newRegistry', folder); + } finally { + window.showInputBox = originalInput; + } + const file = vscode.Uri.joinPath(folder, 'partners.archify-registry'); + const text = new TextDecoder().decode(await vscode.workspace.fs.readFile(file)); + assert.match(text, /^elements:/m); + const tab = await waitFor('registry view tab', () => + vscode.window.tabGroups.all.flatMap((g) => g.tabs).find((t) => t.isActive && t.input instanceof vscode.TabInputCustom && t.input.viewType === 'archify.registry' && t.input.uri.toString() === file.toString()), + ); + assert.ok(tab); + await vscode.workspace.fs.delete(file); + }); + await check('Archify | New Diagram… → Model creates main.archify from the folder name', async () => { const folder = workspaceFile('new-shop'); await vscode.workspace.fs.createDirectory(folder);