0.7.1: Registry panel lists registry files #23

Merged
root-at-skic merged 2 commits from feat/registry-files into main 2026-10-19 10:59:47 -07:00
8 changed files with 57 additions and 22 deletions
Showing only changes of commit de1a57e4c6 - Show all commits
+4
View File
@@ -1,5 +1,9 @@
# Changelog # Changelog
## 0.7.1 — 2026-10-19
- **Model page, Registry panel:** lists the registry files of the model, one collapsible header per file with its element count and an **Open** button that opens the registry view; expand a file to see its elements as cards. Before, all elements of all files were shown in one grid.
## 0.7.0 — 2026-10-19 ## 0.7.0 — 2026-10-19
- **Models:** a folder with a `main.archify` at its top is a model, a set of diagrams that belong together. Opening `main.archify` shows the model page: name and description, the overview diagram if the file has one, the diagrams grouped by folder (click to open), and the registry with where each element is used. - **Models:** a folder with a `main.archify` at its top is a model, a set of diagrams that belong together. Opening `main.archify` shows the model page: name and description, the overview diagram if the file has one, the diagrams grouped by folder (click to open), and the registry with where each element is used.
+1 -1
View File
@@ -116,7 +116,7 @@ online-shop/
0300-components/api.archify 0300-components/api.archify
``` ```
- **The model page.** Click `main.archify` and you get the model's page: its name and description, the overview diagram if the file has one, then two collapsible panels: **Diagrams**, with a collapsible header per folder and a card per diagram (click to open), and **Registry**, the shared elements as cards with the diagrams each is used in. Open or closed is remembered per page. - **The model page.** Click `main.archify` and you get the model's page: its name and description, the overview diagram if the file has one, then two collapsible panels: **Diagrams**, with a collapsible header per folder and a card per diagram (click to open), and **Registry**, one collapsible header per registry file (with an **Open** button for its view) and the file's elements as cards inside, each with the diagrams it is used in. Open or closed is remembered per page.
- **Shared elements.** A registry file defines an element once (`type`, `label`, `sublabel`, `brand`, `description`, …). A diagram draws it with `ref: api` plus `pos` and `size`; it may override `label`, `sublabel` and `tag`, and may still define unshared elements inline. Hover a `ref` for the description, F12 jumps to the registry, completion after `ref:` lists the elements, unknown names appear in Problems. - **Shared elements.** A registry file defines an element once (`type`, `label`, `sublabel`, `brand`, `description`, …). A diagram draws it with `ref: api` plus `pos` and `size`; it may override `label`, `sublabel` and `tag`, and may still define unshared elements inline. Hover a `ref` for the description, F12 jumps to the registry, completion after `ref:` lists the elements, unknown names appear in Problems.
- **Links.** A registry element (or a component) with `diagram: 0300-components/api.archify` points at the diagram that explains it: double-click the node in the 2D view to open it. - **Links.** A registry element (or a component) with `diagram: 0300-components/api.archify` points at the diagram that explains it: double-click the node in the 2D view to open it.
- **Cards.** A model diagram without `cards` of its own shows one card per shared element it draws, from the registry descriptions. The model page shows the registry as the same cards, each with the diagrams it is used in. - **Cards.** A model diagram without `cards` of its own shows one card per shared element it draws, from the registry descriptions. The model page shows the registry as the same cards, each with the diagrams it is used in.
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "archify-vscode-ext", "name": "archify-vscode-ext",
"version": "0.7.0", "version": "0.7.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "archify-vscode-ext", "name": "archify-vscode-ext",
"version": "0.7.0", "version": "0.7.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"three": "^0.186.1", "three": "^0.186.1",
+5 -5
View File
@@ -1,8 +1,8 @@
{ {
"name": "archify-vscode-ext", "name": "archify-vscode-ext",
"displayName": "Archify Diagram Viewer", "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.", "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, \u2026) and opens rendered Archify HTML.",
"version": "0.7.0", "version": "0.7.1",
"publisher": "root-at-skic", "publisher": "root-at-skic",
"license": "MIT", "license": "MIT",
"sponsor": { "sponsor": {
@@ -48,12 +48,12 @@
"commands": [ "commands": [
{ {
"command": "archify.newDiagram", "command": "archify.newDiagram",
"title": "Archify | New Diagram…", "title": "Archify | New Diagram\u2026",
"icon": "$(new-file)" "icon": "$(new-file)"
}, },
{ {
"command": "archify.newModel", "command": "archify.newModel",
"title": "Archify | New Model…", "title": "Archify | New Model\u2026",
"icon": "$(new-folder)" "icon": "$(new-folder)"
}, },
{ {
@@ -73,7 +73,7 @@
}, },
{ {
"command": "archify.renderToHtml", "command": "archify.renderToHtml",
"title": "Archify | Render to HTML File…", "title": "Archify | Render to HTML File\u2026",
"icon": "$(file-code)" "icon": "$(file-code)"
}, },
{ {
+10 -6
View File
@@ -14,9 +14,9 @@ import { handleWebviewMessage, WebviewMessage } from '../webview/messages';
import { makeNonce, messagePage, modelPage, prepareDiagramHtml } from '../webview/page'; import { makeNonce, messagePage, modelPage, prepareDiagramHtml } from '../webview/page';
import { viewer3dOption, viewerRoots } from '../webview/viewer3d'; import { viewer3dOption, viewerRoots } from '../webview/viewer3d';
export const PREVIEW_VIEW_TYPE = 'archify.preview'; import { DIAGRAM_EDITOR_VIEW_TYPE, PREVIEW_VIEW_TYPE, REGISTRY_VIEW_TYPE } from './viewTypes';
/** Default editor for .archify files: the rendered diagram. */
export const DIAGRAM_EDITOR_VIEW_TYPE = 'archify.diagram'; export { DIAGRAM_EDITOR_VIEW_TYPE, PREVIEW_VIEW_TYPE };
interface PreviewState { interface PreviewState {
uri: string; uri: string;
@@ -59,7 +59,11 @@ class DiagramPreview implements vscode.Disposable {
return; return;
} }
if (message.type === 'openDiagram') { if (message.type === 'openDiagram') {
void this.openLinked(message.path); void this.openLinked(message.path, DIAGRAM_EDITOR_VIEW_TYPE);
return;
}
if (message.type === 'openRegistry') {
void this.openLinked(message.file, REGISTRY_VIEW_TYPE);
return; return;
} }
void handleWebviewMessage(message, this.uri, this.panel); void handleWebviewMessage(message, this.uri, this.panel);
@@ -96,7 +100,7 @@ class DiagramPreview implements vscode.Disposable {
} }
/** Opens a diagram of the model, given its path relative to the model root. */ /** Opens a diagram of the model, given its path relative to the model root. */
private async openLinked(relativePath: string): Promise<void> { private async openLinked(relativePath: string, viewType: string): Promise<void> {
if (!this.root) return; if (!this.root) return;
const target = vscode.Uri.joinPath(this.root, ...relativePath.split('/')); const target = vscode.Uri.joinPath(this.root, ...relativePath.split('/'));
try { try {
@@ -105,7 +109,7 @@ class DiagramPreview implements vscode.Disposable {
vscode.window.showWarningMessage(`Archify: ${relativePath} does not exist in this model.`); vscode.window.showWarningMessage(`Archify: ${relativePath} does not exist in this model.`);
return; return;
} }
await vscode.commands.executeCommand('vscode.openWith', target, DIAGRAM_EDITOR_VIEW_TYPE, vscode.ViewColumn.Active); await vscode.commands.executeCommand('vscode.openWith', target, viewType, vscode.ViewColumn.Active);
} }
private showModelPage(model: ModelPanel): void { private showModelPage(model: ModelPanel): void {
+2 -3
View File
@@ -7,10 +7,9 @@ import { readText } from '../util/files';
import { log } from '../util/log'; import { log } from '../util/log';
import { handleWebviewMessage, WebviewMessage } from '../webview/messages'; import { handleWebviewMessage, WebviewMessage } from '../webview/messages';
import { makeNonce, registryPage } from '../webview/page'; import { makeNonce, registryPage } from '../webview/page';
import { DIAGRAM_EDITOR_VIEW_TYPE } from './diagramView'; import { DIAGRAM_EDITOR_VIEW_TYPE, REGISTRY_VIEW_TYPE } from './viewTypes';
/** Default editor for .archify-registry files: the elements as cards, each with where it is used. */ export { REGISTRY_VIEW_TYPE };
export const REGISTRY_VIEW_TYPE = 'archify.registry';
class RegistryDocument implements vscode.CustomDocument { class RegistryDocument implements vscode.CustomDocument {
constructor(readonly uri: vscode.Uri) {} constructor(readonly uri: vscode.Uri) {}
+6
View File
@@ -0,0 +1,6 @@
/** Custom editor view types, in one place so views can refer to each other without importing each other. */
export const PREVIEW_VIEW_TYPE = 'archify.preview';
/** Default editor for .archify files: the rendered diagram. */
export const DIAGRAM_EDITOR_VIEW_TYPE = 'archify.diagram';
/** Default editor for .archify-registry files: the elements as cards. */
export const REGISTRY_VIEW_TYPE = 'archify.registry';
+26 -4
View File
@@ -189,6 +189,7 @@ function bridgeScript(options: PageOptions): string {
// Collapsible panels of the model page; open/closed is kept in the webview state. // Collapsible panels of the model page; open/closed is kept in the webview state.
function panelStates() { return (vscode.getState() || {}).modelPanels || {}; } function panelStates() { return (vscode.getState() || {}).modelPanels || {}; }
function panel(key, className, title, count, defaultOpen) { function panel(key, className, title, count, defaultOpen) {
// count: a number or a short text such as "3 files".
var details = el('details', 'archify-model-panel ' + className); var details = el('details', 'archify-model-panel ' + className);
var saved = panelStates()[key]; var saved = panelStates()[key];
details.open = saved === undefined ? defaultOpen : saved; details.open = saved === undefined ? defaultOpen : saved;
@@ -446,13 +447,30 @@ function bridgeScript(options: PageOptions): string {
}); });
holder.appendChild(list); holder.appendChild(list);
}); });
// Registry elements as Archify info cards: the same cards a diagram shows below itself. // Registry: one collapsible header per registry file, its elements as cards inside.
if (model.registry.length) { if (model.registry.length) {
var registryPanel = panel('registry', 'archify-model-registry', 'Registry', model.registry.length, false); var files = [];
var byFile = {};
model.registry.forEach(function (element) {
if (!byFile[element.file]) { byFile[element.file] = []; files.push(element.file); }
byFile[element.file].push(element);
});
files.sort();
var registryPanel = panel('registry', 'archify-model-registry', 'Registry', files.length + (files.length === 1 ? ' file' : ' files'), true);
body.appendChild(registryPanel); body.appendChild(registryPanel);
files.forEach(function (file) {
var elements = byFile[file].slice().sort(function (a, b) { return a.line - b.line; });
var filePanel = panel('registry:' + file, 'archify-model-folder archify-model-registry-file', file.replace(/\.archify-registry$/, ''), elements.length + (elements.length === 1 ? ' element' : ' elements'), false);
var open = el('button', 'archify-model-used-link archify-model-open-file', 'Open');
open.type = 'button';
open.title = 'Open ' + file;
open.addEventListener('click', function (event) { event.preventDefault(); event.stopPropagation(); post({ type: 'openRegistry', file: file, line: 0 }); });
filePanel.firstChild.appendChild(open);
var cards = el('div', 'cards archify-model-cards'); var cards = el('div', 'cards archify-model-cards');
model.registry.forEach(function (element) { cards.appendChild(elementCard(element)); }); elements.forEach(function (element) { cards.appendChild(elementCard(element)); });
registryPanel.appendChild(cards); filePanel.appendChild(cards);
registryPanel.appendChild(filePanel);
});
} }
if (anchor && anchor.parentNode) anchor.parentNode.insertBefore(body, anchor.nextSibling); else document.body.appendChild(body); if (anchor && anchor.parentNode) anchor.parentNode.insertBefore(body, anchor.nextSibling); else document.body.appendChild(body);
} }
@@ -502,6 +520,10 @@ const MODEL_STYLE = `
.archify-model-folder > .archify-model-summary { padding: 0.55rem 0.75rem; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--m-muted); font-family: ui-monospace, Menlo, monospace; } .archify-model-folder > .archify-model-summary { padding: 0.55rem 0.75rem; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--m-muted); font-family: ui-monospace, Menlo, monospace; }
.archify-model-folder > .archify-model-summary:hover { color: var(--m-accent); } .archify-model-folder > .archify-model-summary:hover { color: var(--m-accent); }
.archify-model-folder > .archify-model-list { margin: 0 0.75rem; } .archify-model-folder > .archify-model-list { margin: 0 0.75rem; }
.archify-model-registry-file > .archify-model-summary { text-transform: none; letter-spacing: 0; font-size: 0.8125rem; }
.archify-model-registry-file > .archify-model-cards { margin: 0.25rem 0.75rem 0; }
.archify-model-open-file { margin-left: 0.5rem; text-transform: none; letter-spacing: 0; font-family: inherit; }
.archify-model-open-file:hover { color: var(--m-accent); }
.archify-model-diagrams > .archify-model-list { margin-top: 0.25rem; } .archify-model-diagrams > .archify-model-list { margin-top: 0.25rem; }
.archify-model-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0.5rem; } .archify-model-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0.5rem; }
.archify-model-open { display: flex; flex-direction: column; gap: 0.2rem; width: 100%; padding: 0.7rem 0.85rem; text-align: left; cursor: pointer; font: inherit; color: var(--m-text); background: var(--m-panel); border: 1px solid var(--m-border); border-radius: 0.6rem; transition: border-color 0.15s, transform 0.15s; } .archify-model-open { display: flex; flex-direction: column; gap: 0.2rem; width: 100%; padding: 0.7rem 0.85rem; text-align: left; cursor: pointer; font: inherit; color: var(--m-text); background: var(--m-panel); border: 1px solid var(--m-border); border-radius: 0.6rem; transition: border-color 0.15s, transform 0.15s; }