From de1a57e4c6429db42fcec03b1aebcc91e5dc76e2 Mon Sep 17 00:00:00 2001 From: Root At Skic Date: Mon, 19 Oct 2026 20:39:24 +0300 Subject: [PATCH] Model page: Registry panel lists registry files, elements inside each - one collapsible header per .archify-registry file with its element count and an Open button that opens the registry view; files start collapsed, the Registry panel open - view types moved to views/viewTypes.ts so the diagram page can open a registry without a circular import - 0.7.1 --- CHANGELOG.md | 4 ++++ README.md | 2 +- package-lock.json | 4 ++-- package.json | 10 +++++----- src/views/diagramView.ts | 16 ++++++++++------ src/views/registryView.ts | 5 ++--- src/views/viewTypes.ts | 6 ++++++ src/webview/page.ts | 32 +++++++++++++++++++++++++++----- 8 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 src/views/viewTypes.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2b212..fafc8a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 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 - **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. diff --git a/README.md b/README.md index 4a7060e..f7b1d01 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ online-shop/ 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. - **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. diff --git a/package-lock.json b/package-lock.json index cecf77a..65f4bc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "archify-vscode-ext", - "version": "0.7.0", + "version": "0.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "archify-vscode-ext", - "version": "0.7.0", + "version": "0.7.1", "license": "MIT", "dependencies": { "three": "^0.186.1", diff --git a/package.json b/package.json index 8063330..d4326ef 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "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.7.0", + "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.1", "publisher": "root-at-skic", "license": "MIT", "sponsor": { @@ -48,12 +48,12 @@ "commands": [ { "command": "archify.newDiagram", - "title": "Archify | New Diagram…", + "title": "Archify | New Diagram\u2026", "icon": "$(new-file)" }, { "command": "archify.newModel", - "title": "Archify | New Model…", + "title": "Archify | New Model\u2026", "icon": "$(new-folder)" }, { @@ -73,7 +73,7 @@ }, { "command": "archify.renderToHtml", - "title": "Archify | Render to HTML File…", + "title": "Archify | Render to HTML File\u2026", "icon": "$(file-code)" }, { diff --git a/src/views/diagramView.ts b/src/views/diagramView.ts index 2c8cebd..b520b20 100644 --- a/src/views/diagramView.ts +++ b/src/views/diagramView.ts @@ -14,9 +14,9 @@ import { handleWebviewMessage, WebviewMessage } from '../webview/messages'; import { makeNonce, messagePage, modelPage, prepareDiagramHtml } from '../webview/page'; import { viewer3dOption, viewerRoots } from '../webview/viewer3d'; -export const PREVIEW_VIEW_TYPE = 'archify.preview'; -/** Default editor for .archify files: the rendered diagram. */ -export const DIAGRAM_EDITOR_VIEW_TYPE = 'archify.diagram'; +import { DIAGRAM_EDITOR_VIEW_TYPE, PREVIEW_VIEW_TYPE, REGISTRY_VIEW_TYPE } from './viewTypes'; + +export { DIAGRAM_EDITOR_VIEW_TYPE, PREVIEW_VIEW_TYPE }; interface PreviewState { uri: string; @@ -59,7 +59,11 @@ class DiagramPreview implements vscode.Disposable { return; } 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; } 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. */ - private async openLinked(relativePath: string): Promise { + private async openLinked(relativePath: string, viewType: string): Promise { if (!this.root) return; const target = vscode.Uri.joinPath(this.root, ...relativePath.split('/')); try { @@ -105,7 +109,7 @@ class DiagramPreview implements vscode.Disposable { vscode.window.showWarningMessage(`Archify: ${relativePath} does not exist in this model.`); 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 { diff --git a/src/views/registryView.ts b/src/views/registryView.ts index 25d8779..d29e8ff 100644 --- a/src/views/registryView.ts +++ b/src/views/registryView.ts @@ -7,10 +7,9 @@ import { readText } from '../util/files'; import { log } from '../util/log'; import { handleWebviewMessage, WebviewMessage } from '../webview/messages'; 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 const REGISTRY_VIEW_TYPE = 'archify.registry'; +export { REGISTRY_VIEW_TYPE }; class RegistryDocument implements vscode.CustomDocument { constructor(readonly uri: vscode.Uri) {} diff --git a/src/views/viewTypes.ts b/src/views/viewTypes.ts new file mode 100644 index 0000000..0830573 --- /dev/null +++ b/src/views/viewTypes.ts @@ -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'; diff --git a/src/webview/page.ts b/src/webview/page.ts index 526d69f..95072ee 100644 --- a/src/webview/page.ts +++ b/src/webview/page.ts @@ -189,6 +189,7 @@ function bridgeScript(options: PageOptions): string { // Collapsible panels of the model page; open/closed is kept in the webview state. function panelStates() { return (vscode.getState() || {}).modelPanels || {}; } 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 saved = panelStates()[key]; details.open = saved === undefined ? defaultOpen : saved; @@ -446,13 +447,30 @@ function bridgeScript(options: PageOptions): string { }); 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) { - 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); - var cards = el('div', 'cards archify-model-cards'); - model.registry.forEach(function (element) { cards.appendChild(elementCard(element)); }); - registryPanel.appendChild(cards); + 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'); + elements.forEach(function (element) { cards.appendChild(elementCard(element)); }); + filePanel.appendChild(cards); + registryPanel.appendChild(filePanel); + }); } 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:hover { color: var(--m-accent); } .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-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; }