bc-atlas 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bca.example.json +0 -2
- package/CHANGELOG.md +77 -0
- package/README.md +6 -5
- package/THIRD_PARTY_NOTICES.md +23 -6
- package/package.json +10 -4
- package/src/app/service.js +16 -2
- package/src/app/snapshot.js +4 -1
- package/src/app/ui/app.css +93 -7
- package/src/app/ui/app.js +425 -79
- package/src/app/workspace.js +62 -19
- package/src/capabilities.js +10 -5
- package/src/commands/export.js +13 -5
- package/src/commands/report.js +8 -18
- package/src/d2.js +127 -81
- package/src/diagram/collapse.js +94 -0
- package/src/diagram/geometry.js +70 -0
- package/src/diagram/index.js +59 -0
- package/src/diagram/layout.js +191 -0
- package/src/diagram/metrics.js +4 -0
- package/src/diagram/pdf-font.js +133 -0
- package/src/diagram/pdf.js +205 -0
- package/src/diagram/png.js +36 -0
- package/src/diagram/svg.js +88 -0
- package/src/diagram/text.js +49 -0
- package/src/docs/export.js +75 -12
- package/src/docs/render/csv.js +6 -1
- package/src/docs/render/html.js +27 -13
- package/src/docs/task.js +70 -28
- package/src/knowledge/graph.js +213 -0
- package/src/knowledge/index.js +65 -0
- package/src/knowledge/ontology.js +105 -0
- package/src/knowledge/serialize.js +131 -0
- package/src/knowledge/vocabulary.js +210 -0
- package/src/output.js +12 -35
- package/vendor/README.md +9 -0
- package/vendor/fonts/LICENSE-LiberationSans.txt +101 -0
- package/vendor/fonts/LiberationSans-Bold.ttf +0 -0
- package/vendor/fonts/LiberationSans-Regular.ttf +0 -0
- package/src/svg.js +0 -67
package/.bca.example.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,83 @@ All notable changes to BC Atlas will be documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
|
|
5
5
|
follows [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.12.0 - 2026-10-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `bca export --view knowledge-graph` writes the project as RDF with the BC
|
|
12
|
+
Atlas OWL ontology, for triple stores, graph databases, and ontology
|
|
13
|
+
editors: Turtle (`.ttl`), RDF/XML (`.rdf`, `.owl`), N-Triples (`.nt`), or
|
|
14
|
+
JSON-LD (`.jsonld`). Apps, namespaces, objects with fields, keys, and
|
|
15
|
+
procedures, every relation as a property (`bca:calls`, `bca:writes`, ...)
|
|
16
|
+
and as a `bca:Relation` with its evidence, objects outside the workspace,
|
|
17
|
+
architecture components, and violations. Stable IRIs (`urn:bc-atlas:...`, or
|
|
18
|
+
`--base-iri`), and `SOURCE_DATE_EPOCH` for reproducible files. See
|
|
19
|
+
[Knowledge graph](./docs/knowledge-graph.md).
|
|
20
|
+
- Knowledge graph: `--no-evidence` writes only the direct relation triples,
|
|
21
|
+
without a `bca:Relation` per relation; the Base Application's graph drops
|
|
22
|
+
from 4.4 million triples (270 MB of Turtle) to 1.1 million (78 MB).
|
|
23
|
+
- The BC Atlas ontology is published at its IRI,
|
|
24
|
+
`https://schulzoli.github.io/BC-Atlas/ontology`: a page with every class
|
|
25
|
+
and property (each term's IRI opens its row), and the ontology as
|
|
26
|
+
`ontology.ttl`, `.owl`, `.rdf`, `.nt`, and `.jsonld` next to it, for
|
|
27
|
+
Protégé and other tools that resolve IRIs. Exports point to it with
|
|
28
|
+
`rdfs:seeAlso`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- App and VS Code panel: Diagrams and Docs work the same way. On the left, a
|
|
33
|
+
filterable list (the views; the overview, use cases, and scenarios) with
|
|
34
|
+
the settings below it; on the right, the selected item with its tools.
|
|
35
|
+
`J`/`K` and **‹**/**›** step through the list, **Enter** in the filter opens
|
|
36
|
+
the first match, and the address keeps the selection and the settings.
|
|
37
|
+
- Diagrams: views are listed with what they show, choices apply at once,
|
|
38
|
+
**PNG** saves the diagram besides **SVG** and **D2**, and a click on an
|
|
39
|
+
object opens its page.
|
|
40
|
+
- Diagrams are drawn by BC Atlas itself instead of D2: an ELK layered layout
|
|
41
|
+
with groups, right-angled routes (curves beyond 120 relations), labels, and
|
|
42
|
+
a legend, as SVG, PNG, or vector PDF. `--format png` and `--format pdf` no
|
|
43
|
+
longer need the `d2` executable, and every drawing still writes its D2
|
|
44
|
+
source next to it. Text is sized with the metrics of the bundled Liberation
|
|
45
|
+
Sans (Arial and Helvetica widths), so boxes fit in all three formats. Large
|
|
46
|
+
views are faster and far less tall (a 200-object project view: 5 instead of
|
|
47
|
+
19 seconds, 7499 × 6811 instead of 6112 × 25030 pixels).
|
|
48
|
+
- Diagrams page: hover an object to light up its relations and neighbors,
|
|
49
|
+
click it to open its page, find objects by name (**Enter** jumps to the next
|
|
50
|
+
match), and click a legend entry to hide that kind of relation.
|
|
51
|
+
- The npm package no longer installs `@terrastruct/d2` (58 MB); it uses
|
|
52
|
+
`elkjs` and `@resvg/resvg-wasm`. The VS Code extension shrinks from about 9
|
|
53
|
+
to 4.5 MB. The `layout` and `theme` settings of `.bca.json` (D2 options)
|
|
54
|
+
have no effect any more.
|
|
55
|
+
- Docs: the guides are part of the page instead of a page in a frame, one
|
|
56
|
+
overview, use case, or scenario at a time. Switch between user guide and
|
|
57
|
+
test case and between English and German; **Open test** opens the AL test
|
|
58
|
+
at its procedure; **Markdown**, **DITA**, **BPMN**, and **SVG** save the
|
|
59
|
+
page, **All as HTML** the whole documentation. Links between guides and
|
|
60
|
+
from a use case's process to its scenarios stay on the page.
|
|
61
|
+
- Large diagrams as an overview of their groups: every closed group is one
|
|
62
|
+
box with its object count and main object types, and the relations into,
|
|
63
|
+
out of, and between closed groups are bundled (one route per pair of groups
|
|
64
|
+
with the number of relations, per kind towards single objects). On the
|
|
65
|
+
Diagrams page, views with more than 120 objects or 250 relations start this
|
|
66
|
+
way; click a box to open its group, a group's name to close it, or use
|
|
67
|
+
**Open groups** and **Close groups**. Files of views with more than 1,500
|
|
68
|
+
objects are drawn as the overview instead of only writing the D2 source, so
|
|
69
|
+
the Base Application's project view (8,083 objects) now has a diagram.
|
|
70
|
+
- Diagrams page and the flows on the Docs page: on a dark theme the diagrams
|
|
71
|
+
turn dark too (saved files stay light).
|
|
72
|
+
- PDF diagrams with text outside Windows-1252 (Polish, Czech, Greek,
|
|
73
|
+
Cyrillic, ...) embed Liberation Sans, so the text shows and can be copied
|
|
74
|
+
and searched; other PDFs keep using Helvetica and stay small.
|
|
75
|
+
- The README's diagram images are rendered from the example SVGs by
|
|
76
|
+
`npm run examples`, and CI checks them with the other generated examples.
|
|
77
|
+
|
|
78
|
+
### Security
|
|
79
|
+
|
|
80
|
+
- The Azure DevOps test-case CSV (`--export ado-csv`) writes cells that start
|
|
81
|
+
with `=`, `+`, `-`, or `@` with a leading apostrophe, so Excel and Google
|
|
82
|
+
Sheets do not run them as formulas.
|
|
83
|
+
|
|
7
84
|
## 0.11.0 - 2026-10-29
|
|
8
85
|
|
|
9
86
|
### Changed
|
package/README.md
CHANGED
|
@@ -41,10 +41,10 @@ In VS Code, install the **BC Atlas** extension and open an AL project:
|
|
|
41
41
|
| **Get started** | `bca setup` | Guided setup in the terminal or through your AI agent (Claude Code, Copilot, Cursor): a starter `.bca.json`, features, Husky or Git hooks, GitHub Actions or Azure Pipelines, cron schedule, and sync. |
|
|
42
42
|
| **Analyze** | `bca check` | Health findings - cycles, forbidden dependencies, architecture violations, hot spots, diagnostics, and AL compiler and CodeCop/UICop/AppSourceCop findings - with a CI exit code, also as SARIF for code scanning. |
|
|
43
43
|
| | `bca architecture` | Find a target architecture (components, layers), list every dependency that blocks it with how to resolve it, and enforce it with a shrinking baseline. [Guide](./docs/clean-architecture.md) |
|
|
44
|
-
| **Export** | `bca export` | Everything the extension shows, as files: the BC Atlas pages as one offline HTML file to share (default), the Markdown report ([example](./examples/report/README.md)), the 3D explorer, one Markdown page per object, any of ten diagram views as D2, SVG, PNG, PDF, or JSON, with `--watch
|
|
44
|
+
| **Export** | `bca export` | Everything the extension shows, as files: the BC Atlas pages as one offline HTML file to share (default), the Markdown report ([example](./examples/report/README.md)), the 3D explorer, one Markdown page per object, any of ten diagram views as D2, SVG, PNG, PDF, or JSON, with `--watch`, and the project as a knowledge graph in RDF with an OWL ontology ([Turtle, RDF/XML, OWL, N-Triples, JSON-LD](./docs/knowledge-graph.md)). |
|
|
45
45
|
| | `bca docs` | User guides and test cases from AL UI tests (`[SCENARIO]`, `[GIVEN]`, `[WHEN]`, `[THEN]`) as Markdown, HTML, DITA 1.3, or Azure DevOps CSV, with real captions and translations, a flow diagram per scenario, and BPMN 2.0 processes per use case. [Example](./examples/docs/index.md) |
|
|
46
46
|
| **Integrate** | `bca run` | One entry point for Git hooks, schedules, and pipelines; verifies, stages, or publishes generated files. Also available as a [GitHub Action](./docs/automation-and-ci.md#github-actions). |
|
|
47
|
-
| | `bca update` | "BC Atlas 0.
|
|
47
|
+
| | `bca update` | "BC Atlas 0.13.0 is available": release notes, one-step update, and regenerated hooks, tasks, and pipelines. Dependabot pull requests on request. |
|
|
48
48
|
| | `bca mcp` | A Model Context Protocol server for AI agents; `bca help --format json` is the versioned command contract. |
|
|
49
49
|
|
|
50
50
|
Run `bca` for the same overview in your terminal and `bca help <command>` for
|
|
@@ -65,7 +65,7 @@ options and examples.
|
|
|
65
65
|
| `ui` | How do pages, actions, and source tables connect? | `bca export --view ui --focus "Sales Order" -o ui.svg` |
|
|
66
66
|
| `workflow` | What happens after an action or entry point? | `bca export --view workflow --entry action:Release -o flow.svg` |
|
|
67
67
|
|
|
68
|
-
In VS Code, the Diagrams page of the BC Atlas panel renders every view with zoom and pan.
|
|
68
|
+
In VS Code, the Diagrams page of the BC Atlas panel renders every view with zoom and pan; click an object to open it.
|
|
69
69
|
|
|
70
70
|
### Clean architecture
|
|
71
71
|
|
|
@@ -208,7 +208,7 @@ and update-check tools. `bca setup agent --agent claude|copilot|cursor` adds a
|
|
|
208
208
|
-> AL compiler: calls and diagnostics (src/al, when the toolchain is installed)
|
|
209
209
|
-> resolved model with evidence (resolver, call analysis, semantics)
|
|
210
210
|
-> view projection and insights (views, workflow, health)
|
|
211
|
-
->
|
|
211
|
+
-> SVG / PNG / PDF / D2 / JSON / Markdown / HTML (renderers)
|
|
212
212
|
-> the pages: one workspace, in VS Code (vscode/) or exported as one file
|
|
213
213
|
```
|
|
214
214
|
|
|
@@ -291,5 +291,6 @@ Release notes are in [CHANGELOG.md](./CHANGELOG.md).
|
|
|
291
291
|
## License
|
|
292
292
|
|
|
293
293
|
[MIT](./LICENSE). Dependencies retain their own licenses: `tree-sitter-al` and
|
|
294
|
-
`web-tree-sitter` are MIT,
|
|
294
|
+
`web-tree-sitter` are MIT, elkjs is EPL-2.0, resvg is MPL-2.0, and the
|
|
295
|
+
Liberation Sans font is OFL-1.1. See
|
|
295
296
|
[THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -13,14 +13,31 @@ project.
|
|
|
13
13
|
Copyright Tree-sitter contributors. Licensed under the MIT License.
|
|
14
14
|
Source: <https://github.com/tree-sitter/tree-sitter>
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## ELK (elkjs)
|
|
17
17
|
|
|
18
|
-
Copyright
|
|
19
|
-
|
|
18
|
+
Copyright Kiel University and others. Licensed under the Eclipse Public License
|
|
19
|
+
2.0 (or, at your option, GPL-3.0-or-later). Source: <https://github.com/kieler/elkjs>
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
`elkjs` lays out the diagram views (layered layout, routes, and labels).
|
|
22
|
+
|
|
23
|
+
## resvg (@resvg/resvg-wasm)
|
|
24
|
+
|
|
25
|
+
Copyright the resvg authors. Licensed under the Mozilla Public License 2.0.
|
|
26
|
+
Source: <https://github.com/yisibl/resvg-js>
|
|
27
|
+
|
|
28
|
+
The resvg WebAssembly package is an npm dependency that turns diagrams into
|
|
29
|
+
PNG files.
|
|
30
|
+
|
|
31
|
+
## Liberation Sans
|
|
32
|
+
|
|
33
|
+
Digitized data copyright (c) 2010 Google Corporation; copyright (c) 2012 Red
|
|
34
|
+
Hat, Inc. Licensed under the SIL Open Font License 1.1. Source:
|
|
35
|
+
<https://github.com/liberationfonts/liberation-fonts>
|
|
36
|
+
|
|
37
|
+
`vendor/fonts/LiberationSans-Regular.ttf` and `LiberationSans-Bold.ttf` are
|
|
38
|
+
redistributed (license in `vendor/fonts/LICENSE-LiberationSans.txt`): PNG files
|
|
39
|
+
are rendered with them, and their glyph widths size the boxes of every
|
|
40
|
+
diagram.
|
|
24
41
|
|
|
25
42
|
## 3d-force-graph and three.js
|
|
26
43
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bc-atlas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "BC Atlas maps Microsoft Dynamics 365 Business Central AL projects: architecture diagrams, health checks, reports, and documentation generated from source.",
|
|
5
5
|
"homepage": "https://github.com/SchulzOli/BC-Atlas#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"site:build": "vitepress build website && node ./scripts/site-assets.js",
|
|
39
39
|
"site:preview": "vitepress preview website",
|
|
40
40
|
"version:set": "node ./scripts/set-version.js",
|
|
41
|
-
"update:graph-vendor": "node ./scripts/build-graph-vendor.js"
|
|
41
|
+
"update:graph-vendor": "node ./scripts/build-graph-vendor.js",
|
|
42
|
+
"update:font-metrics": "node ./scripts/build-font-metrics.js"
|
|
42
43
|
},
|
|
43
44
|
"keywords": [
|
|
44
45
|
"al",
|
|
@@ -62,7 +63,8 @@
|
|
|
62
63
|
},
|
|
63
64
|
"dependencies": {
|
|
64
65
|
"@modelcontextprotocol/server": "^2.4.0",
|
|
65
|
-
"@
|
|
66
|
+
"@resvg/resvg-wasm": "^2.6.2",
|
|
67
|
+
"elkjs": "^0.12.1",
|
|
66
68
|
"fflate": "^0.8.3",
|
|
67
69
|
"graphology": "^0.26.0",
|
|
68
70
|
"graphology-communities-louvain": "^2.0.2",
|
|
@@ -71,10 +73,14 @@
|
|
|
71
73
|
"zod": "^4.6.7"
|
|
72
74
|
},
|
|
73
75
|
"devDependencies": {
|
|
74
|
-
"3d-force-graph": "^1.80.0",
|
|
75
76
|
"@modelcontextprotocol/client": "^2.4.0",
|
|
77
|
+
"3d-force-graph": "^1.80.0",
|
|
76
78
|
"bpmn-moddle": "^10.3.1",
|
|
77
79
|
"esbuild": "^0.28.2",
|
|
80
|
+
"jsonld": "^9.0.0",
|
|
81
|
+
"n3": "^2.13.12",
|
|
82
|
+
"opentype.js": "^2.0.0",
|
|
83
|
+
"rdfxml-streaming-parser": "^3.3.0",
|
|
78
84
|
"three": "^0.186.1",
|
|
79
85
|
"vitepress": "2.0.0-alpha.20"
|
|
80
86
|
}
|
package/src/app/service.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// call it directly. Every method takes one plain object and returns JSON or,
|
|
4
4
|
// for the explorer and the docs, an HTML page as a string.
|
|
5
5
|
|
|
6
|
+
import { DOCUMENT_MODES } from "../docs/export.js";
|
|
7
|
+
|
|
6
8
|
const HISTORY = ["commits", "prs", "releases"];
|
|
7
9
|
const GROUPINGS = ["auto", "coupling", "namespace", "folder", "module", "app"];
|
|
8
10
|
|
|
@@ -20,6 +22,13 @@ function architectureSettings(params = {}) {
|
|
|
20
22
|
return { propose: truthy(params.propose), groupBy, depth: positive(params.depth, "depth"), minObjects: positive(params.minObjects, "minObjects") };
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
// A guide or a test case, in a language; empty means the .bca.json setting.
|
|
26
|
+
function docsSettings({ mode, language } = {}) {
|
|
27
|
+
if (mode && !DOCUMENT_MODES.includes(mode)) throw new Error(`mode must be one of: ${DOCUMENT_MODES.join(", ")}`);
|
|
28
|
+
if (language && !/^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/u.test(language)) throw new Error("language must be a language tag such as de-DE");
|
|
29
|
+
return { mode: mode || undefined, language: language || undefined };
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
export function createService(workspace) {
|
|
24
33
|
const methods = {
|
|
25
34
|
overview: () => workspace.overview(),
|
|
@@ -46,8 +55,13 @@ export function createService(workspace) {
|
|
|
46
55
|
if (history && !HISTORY.includes(history)) throw new Error(`history must be one of: ${HISTORY.join(", ")}`);
|
|
47
56
|
return workspace.explorer({ propose: truthy(propose), history: history || false, last: positive(last, "last") });
|
|
48
57
|
},
|
|
49
|
-
|
|
50
|
-
const
|
|
58
|
+
docsBrowser: async (params = {}) => {
|
|
59
|
+
const browser = await workspace.docsBrowser(docsSettings(params));
|
|
60
|
+
if (!browser) throw new Error("no UI-test documentation found");
|
|
61
|
+
return browser;
|
|
62
|
+
},
|
|
63
|
+
docsHtml: async (params = {}) => {
|
|
64
|
+
const html = await workspace.docsHtml(docsSettings(params));
|
|
51
65
|
if (!html) throw new Error("no UI-test documentation found");
|
|
52
66
|
return html;
|
|
53
67
|
},
|
package/src/app/snapshot.js
CHANGED
|
@@ -39,11 +39,12 @@ function compactDetails(index) {
|
|
|
39
39
|
|
|
40
40
|
export async function createSnapshot(workspace, { log = () => {}, history = false, last, title } = {}) {
|
|
41
41
|
const overview = { ...await workspace.overview(), ...(title ? { title } : {}) };
|
|
42
|
-
const [health, architecture, components, index, docsHtml] = await Promise.all([
|
|
42
|
+
const [health, architecture, components, index, docs, docsHtml] = await Promise.all([
|
|
43
43
|
workspace.healthReport(),
|
|
44
44
|
workspace.architecture(),
|
|
45
45
|
workspace.components(),
|
|
46
46
|
workspace.objectIndex(),
|
|
47
|
+
workspace.docsBrowser(),
|
|
47
48
|
workspace.docsHtml()
|
|
48
49
|
]);
|
|
49
50
|
const { nodes, details } = compactDetails(index);
|
|
@@ -73,6 +74,8 @@ export async function createSnapshot(workspace, { log = () => {}, history = fals
|
|
|
73
74
|
details,
|
|
74
75
|
views,
|
|
75
76
|
diagrams,
|
|
77
|
+
// The Docs page in the settings of .bca.json; the whole page for download.
|
|
78
|
+
docs: docs ? { ...docs, languages: [docs.language] } : undefined,
|
|
76
79
|
explorerHtml,
|
|
77
80
|
docsHtml
|
|
78
81
|
};
|
package/src/app/ui/app.css
CHANGED
|
@@ -271,11 +271,91 @@ table.matrix td.self { background: var(--surface-2); }
|
|
|
271
271
|
.component-graph.focused [data-group], .component-graph.focused [data-groups] { opacity: 0.12; }
|
|
272
272
|
.component-graph.focused .on { opacity: 1; }
|
|
273
273
|
|
|
274
|
-
/* Diagrams
|
|
275
|
-
|
|
276
|
-
.
|
|
277
|
-
.
|
|
278
|
-
.
|
|
274
|
+
/* Workbench: Diagrams and Docs. A filterable list and its settings on the
|
|
275
|
+
left, the selected item with its tools on the right. */
|
|
276
|
+
#page > .workbench:only-child { grid-row: 1 / -1; }
|
|
277
|
+
.workbench { display: grid; grid-template-columns: 290px minmax(0, 1fr); gap: 16px; min-height: 0; height: 100%; }
|
|
278
|
+
.wb-side, .wb-main { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); min-height: 0; min-width: 0; }
|
|
279
|
+
.wb-side { display: flex; flex-direction: column; overflow: hidden; }
|
|
280
|
+
.wb-filter { margin: 12px 12px 6px; width: auto !important; }
|
|
281
|
+
.wb-list { flex: 1 1 auto; overflow: auto; padding: 0 8px 10px; min-height: 120px; }
|
|
282
|
+
.wb-group h3 { font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 14px 8px 4px; }
|
|
283
|
+
.wb-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 8px; padding: 6px 8px; border-radius: 8px; color: var(--text); }
|
|
284
|
+
.wb-item:hover { background: var(--hover); text-decoration: none; }
|
|
285
|
+
.wb-item[aria-current] { background: var(--selected); color: var(--selected-text); outline: 1px solid var(--vscode-contrastActiveBorder, transparent); outline-offset: -1px; }
|
|
286
|
+
.wb-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
287
|
+
.wb-item small { grid-column: 1 / -1; color: var(--muted); font-size: 12px; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
288
|
+
.wb-item[aria-current] small, .wb-item[aria-current] .count { color: inherit; opacity: 0.8; }
|
|
289
|
+
.wb-item .count { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; align-self: center; }
|
|
290
|
+
.wb-none { padding: 8px; }
|
|
291
|
+
.wb-settings { border-top: 1px solid var(--border); padding: 12px 14px 14px; max-height: 50%; overflow: auto; flex: none; }
|
|
292
|
+
.wb-form { display: grid; gap: 10px; }
|
|
293
|
+
.wb-form h3 { font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
|
|
294
|
+
.wb-form select, .wb-form input:not([type="checkbox"]) { width: 100%; }
|
|
295
|
+
.wb-form .field i { font-style: normal; font-weight: 400; }
|
|
296
|
+
.wb-form .segmented { display: flex; }
|
|
297
|
+
.wb-form .segmented button { flex: 1; }
|
|
298
|
+
.wb-main { display: flex; flex-direction: column; overflow: hidden; }
|
|
299
|
+
.wb-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 16px; border-bottom: 1px solid var(--border); }
|
|
300
|
+
.wb-heading { flex: 1 1 280px; min-width: 0; }
|
|
301
|
+
.wb-title { font-size: 16px; font-weight: 650; }
|
|
302
|
+
.wb-subtitle { margin: 2px 0 0; color: var(--muted); font-size: 12.5px; overflow-wrap: anywhere; }
|
|
303
|
+
.wb-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
|
304
|
+
.wb-tools, .wb-step { display: inline-flex; gap: 4px; }
|
|
305
|
+
.wb-actions .icon-button:disabled { opacity: 0.4; cursor: default; }
|
|
306
|
+
.wb-step .icon-button { font-size: 15px; line-height: 1; padding: 4px 10px; }
|
|
307
|
+
.wb-body { flex: 1 1 auto; min-height: 0; position: relative; display: flex; flex-direction: column; }
|
|
308
|
+
.wb-body.scroll { overflow: auto; display: block; }
|
|
309
|
+
.wb-body > .canvas { flex: 1; height: auto; border: 0; border-radius: 0 0 var(--radius) var(--radius); }
|
|
310
|
+
.wb-body > .empty, .wb-body > .error-box { margin: 18px; }
|
|
311
|
+
/* Interactive diagrams (src/diagram/svg.js): hover lights up an object's relations */
|
|
312
|
+
.bca-diagram .node, .bca-diagram .legend-kind, .bca-diagram .group text { cursor: pointer; }
|
|
313
|
+
.bca-diagram .node.collapsed:hover rect { stroke-width: 2.4; }
|
|
314
|
+
.bca-diagram .node, .bca-diagram .edge, .bca-diagram .group { transition: opacity 0.12s; }
|
|
315
|
+
.bca-diagram.focused .node:not(.on), .bca-diagram.focused .edge:not(.on) { opacity: 0.14; }
|
|
316
|
+
.bca-diagram.focused .edge.on path { stroke-width: 2.4; }
|
|
317
|
+
.bca-diagram .node.on rect { filter: drop-shadow(0 0 4px rgba(47, 111, 224, 0.55)); }
|
|
318
|
+
.bca-diagram.searching:not(.focused) .node:not(.match) { opacity: 0.25; }
|
|
319
|
+
.bca-diagram .node.match rect { stroke: #2f6fe0; stroke-width: 2.5; }
|
|
320
|
+
.bca-diagram .edge.off { display: none; }
|
|
321
|
+
.bca-diagram .legend-kind.off { opacity: 0.3; }
|
|
322
|
+
/* On a dark theme the diagrams turn dark too: lightness inverted, hues kept.
|
|
323
|
+
Saved files (SVG, PNG, PDF) stay light. */
|
|
324
|
+
:root[data-theme="dark"] .canvas:has(.bca-diagram) { background: #16181d; }
|
|
325
|
+
:root[data-theme="dark"] .bca-diagram,
|
|
326
|
+
:root[data-theme="dark"] .doc .diagram svg,
|
|
327
|
+
:root[data-theme="dark"] .doc .journey svg { filter: invert(0.88) hue-rotate(180deg) saturate(1.3); }
|
|
328
|
+
:root[data-theme="dark"] .doc .diagram, :root[data-theme="dark"] .doc .journey { background: #1d2026; }
|
|
329
|
+
:root[data-theme="dark"] .canvas-info { background: rgba(22, 24, 29, 0.85); color: var(--muted); }
|
|
330
|
+
.canvas-search { position: absolute; left: 12px; top: 12px; z-index: 2; width: 240px !important; background: var(--surface); box-shadow: var(--shadow); }
|
|
331
|
+
.canvas-search.none { border-color: var(--error); }
|
|
332
|
+
|
|
333
|
+
/* A guide or test case in the Docs page */
|
|
334
|
+
.doc { padding: 20px 28px 40px; max-width: 980px; font-size: 14.5px; line-height: 1.6; }
|
|
335
|
+
.doc h2 { font-size: 18px; margin: 26px 0 8px; }
|
|
336
|
+
.doc h2:first-child, .doc > p:first-child + h2 { margin-top: 0; }
|
|
337
|
+
.doc h3 { font-size: 15px; margin: 22px 0 6px; }
|
|
338
|
+
.doc p { margin: 8px 0; }
|
|
339
|
+
.doc ul, .doc ol { padding-left: 22px; margin: 6px 0; }
|
|
340
|
+
.doc ol.steps > li { margin: 8px 0; padding-left: 4px; }
|
|
341
|
+
.doc ol.steps > li::marker { color: var(--muted); font-weight: 600; }
|
|
342
|
+
.doc .short { font-size: 15.5px; }
|
|
343
|
+
.doc .ui { color: var(--link); font-weight: 600; }
|
|
344
|
+
.doc kbd { font: inherit; font-size: 0.95em; padding: 0 5px; border-bottom-width: 1px; background: var(--surface-2); }
|
|
345
|
+
.doc .note { background: var(--surface-2); border-left: 3px solid var(--accent); padding: 8px 12px; margin: 10px 0; border-radius: 0 6px 6px 0; }
|
|
346
|
+
.doc .info { display: block; color: var(--muted); font-size: 13px; }
|
|
347
|
+
.doc .result { display: block; margin-top: 4px; color: var(--muted); }
|
|
348
|
+
.doc table { border-collapse: collapse; width: 100%; margin: 8px 0; font-size: 13.5px; }
|
|
349
|
+
.doc th, .doc td { border: 1px solid var(--border); padding: 6px 9px; text-align: left; vertical-align: top; }
|
|
350
|
+
.doc th { background: var(--surface-2); font-weight: 600; }
|
|
351
|
+
.doc .chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
|
|
352
|
+
.doc .chips span { background: var(--accent-soft); color: var(--link); border-radius: 999px; padding: 1px 10px; font-size: 12px; font-weight: 600; }
|
|
353
|
+
.doc .diagram, .doc .journey { overflow-x: auto; margin: 12px 0; padding: 12px; background: var(--paper); border: 1px solid var(--border); border-radius: var(--radius); }
|
|
354
|
+
.doc .diagram svg, .doc .journey svg { max-width: 100%; height: auto; display: block; margin: 0 auto; }
|
|
355
|
+
.doc details { margin-top: 18px; font-size: 12.5px; color: var(--muted); }
|
|
356
|
+
.doc details summary { cursor: pointer; }
|
|
357
|
+
.doc article { border: 0; padding: 0; margin: 0; }
|
|
358
|
+
|
|
279
359
|
.canvas {
|
|
280
360
|
position: relative; height: calc(100vh - 170px); min-height: 420px; overflow: hidden; border-radius: var(--radius);
|
|
281
361
|
background: var(--paper); border: 1px solid var(--border); cursor: grab;
|
|
@@ -316,8 +396,14 @@ dialog p { color: var(--muted); margin: 0 0 16px; }
|
|
|
316
396
|
|
|
317
397
|
@media (max-width: 1100px) {
|
|
318
398
|
.grid.wide { grid-template-columns: 1fr; }
|
|
319
|
-
.
|
|
320
|
-
|
|
399
|
+
.workbench { grid-template-columns: 240px minmax(0, 1fr); }
|
|
400
|
+
}
|
|
401
|
+
@media (max-width: 760px) {
|
|
402
|
+
.workbench { grid-template-columns: 1fr; grid-template-rows: auto minmax(480px, 1fr); height: auto; }
|
|
403
|
+
.wb-list { max-height: 260px; }
|
|
404
|
+
/* On a phone the page scrolls, not the guide inside it. */
|
|
405
|
+
.wb-main, .wb-body.scroll { overflow: visible; }
|
|
406
|
+
.doc { padding: 16px; }
|
|
321
407
|
}
|
|
322
408
|
@media (max-width: 860px) {
|
|
323
409
|
#shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
|