umlimator 0.0.0-stage → 0.11.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/LICENSE +21 -0
- package/README.md +174 -2
- package/THIRD_PARTY_NOTICES.md +215 -0
- package/lib/umlimator-theme.css +64 -0
- package/lib/umlimator.d.ts +140 -0
- package/lib/umlimator.js +2870 -0
- package/package.json +13 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zintaen <zintaen@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,175 @@
|
|
|
1
|
-
#
|
|
1
|
+
# umlimator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/zintaen/umlimator/actions/workflows/check.yml)
|
|
4
|
+
|
|
5
|
+
Layered architecture maps you can click through, from one JSON model: an ES module that any page embeds, and a CLI
|
|
6
|
+
that writes a self-contained HTML page and still pictures (SVG, PNG) for documents that cannot run one.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
**Examples:** [live on GitHub Pages](https://zintaen.github.io/umlimator/), and [what each one shows](examples/README.md): a C4-style
|
|
11
|
+
drill-down, a data pipeline, an AI agent's skills, and host pages for embedding, live updates and editing.
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
- **Layers**: a view per level (system, containers, components, flows). A stacked card opens its inner view, which the
|
|
16
|
+
host may load only when asked.
|
|
17
|
+
- **Layout and routing**: columns in reading order, each ordered so the lines cross as little as they can, those that
|
|
18
|
+
skip columns included; each card level with the cards its lines come from, so lines run straight; orthogonal lines
|
|
19
|
+
that never run through a card, room for every label, containers kept apart, and cards with no lines in a row of their
|
|
20
|
+
own, under the view or at the bottom of their box. The same model gives the same picture.
|
|
21
|
+
- **Reading**: animated flow, neighbours lit on selection, numbered steps, tours, minimap, light and dark themes.
|
|
22
|
+
- **Keyboard and screen readers**: cards, then lines, in the Tab order (a card's tabs and chips too, for a host that
|
|
23
|
+
listens for click); arrows between cards; selections announced.
|
|
24
|
+
- **Editing** (opt-in): gestures emit `change` events; the host applies them and calls `update()`.
|
|
25
|
+
- **Export**: any view as a stand-alone SVG or PNG, in the browser or from the CLI.
|
|
26
|
+
- **A layout check**: crossings, shared lines, detours, labels on lines, text that overflows its card.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install umlimator
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or from GitHub, at a tag or any commit: `"umlimator": "github:zintaen/umlimator#v0.11.0"`. npm 12 refuses git
|
|
35
|
+
dependencies unless the project opts in, so that also needs `allow-git=root` in the `.npmrc` next to `package.json`.
|
|
36
|
+
|
|
37
|
+
## Embed
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { render } from 'umlimator'; // one ES module, no dependencies, no build step
|
|
41
|
+
import 'umlimator/theme.css'; // the default --um-* palette and fonts (or set the variables yourself)
|
|
42
|
+
|
|
43
|
+
const map = render(document.getElementById('map'), model, {
|
|
44
|
+
on: { select: e => showDetails(e.target), open: e => { e.preventDefault(); router.push(e.view); } },
|
|
45
|
+
});
|
|
46
|
+
map.update(nextModel); // redraws only what changed; keeps the view, camera and selection
|
|
47
|
+
const svg = map.toSVG({ scale: 2 }); // the current view as a stand-alone file
|
|
48
|
+
const png = await map.toPNG({ view: 'api' }); // any view, without showing it
|
|
49
|
+
map.destroy();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
In React, mount once and pass new models to `update`:
|
|
53
|
+
|
|
54
|
+
```jsx
|
|
55
|
+
function MapView({ model, onSelect }) {
|
|
56
|
+
const el = useRef(null), map = useRef(null), cb = useRef(onSelect);
|
|
57
|
+
cb.current = onSelect;
|
|
58
|
+
useEffect(() => { map.current = render(el.current, model, { on: { select: e => cb.current?.(e.target) } }); return () => map.current.destroy(); }, []);
|
|
59
|
+
useEffect(() => { map.current?.update(model); }, [model]);
|
|
60
|
+
return <div ref={el} style={{ height: 600 }} />;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `render` draws one view at a time into a shadow root (give the element a height), with pan, zoom and drill-down.
|
|
65
|
+
Events (`click`, `select`, `open`, `viewchange`, `change`) name what they are about by ids from the model.
|
|
66
|
+
- Colours are `--um-*` CSS variables, fonts are the host's (`--um-sans`, `--um-mono`), text is measured in that font,
|
|
67
|
+
and nothing is fetched.
|
|
68
|
+
- `layout(model, viewId)` returns a view's geometry without a DOM, in Node too.
|
|
69
|
+
- `editable: true` turns on editing gestures ([docs/editing.md](docs/editing.md); `examples/edit.html` is a working host).
|
|
70
|
+
- `mountPage` is the whole standalone viewer that the CLI writes.
|
|
71
|
+
|
|
72
|
+
The API, its types and the decisions behind it: [docs/module-api.md](docs/module-api.md) and `lib/umlimator.d.ts`.
|
|
73
|
+
|
|
74
|
+
## Model
|
|
75
|
+
|
|
76
|
+
A model has shared `nodes` and `edges`, and `views` that each show a subset, with optional `containers` (boxes),
|
|
77
|
+
`layout` (`[col, row]` per card), `render: "sequence"` for numbered run-order views and `tour` for a dot that rides a
|
|
78
|
+
path. The full schema is `engine/references/schema.md` (§ Map model); `examples/bookshop.json` uses most of it.
|
|
79
|
+
|
|
80
|
+
Card kinds: `frontend`, `backend`, `ai`, `database`, `queue`, `security`, `cloud`, `external` and `file` (skill-map
|
|
81
|
+
kinds such as `public`, `lane`, `script`, `hook` and `board` work too). Fields beyond the schema:
|
|
82
|
+
|
|
83
|
+
| On | Field | What it does |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| node | `badges` (`badge`) | tabs on the card, each a string or `{ text, tone }` |
|
|
86
|
+
| node | `icon` | a logo or line icon from `icons/manifest.json` |
|
|
87
|
+
| node | `noSub` | the name only; the description stays in the details panel |
|
|
88
|
+
| node | `opensView`, `opensCount` | the view the card opens, which may not be loaded yet, and the ⤢ number for it |
|
|
89
|
+
| node, edge | `edited` | a marker on the card or line |
|
|
90
|
+
| edge | `kind` | what the line means, drawn as its colour: `calls` (default), `feeds`, `triggers`, `state`, `guard` |
|
|
91
|
+
| edge | `weight` | 1, 2 or 3: how much the line stands for, drawn as its width |
|
|
92
|
+
| edge | `sublabel` | a second, smaller line under the label |
|
|
93
|
+
| edge | `fromSide`, `toSide` | the card sides the line uses |
|
|
94
|
+
| edge | `from`, `to` | may name a container of the view: the line ends on its box |
|
|
95
|
+
| view | `gap`, `rgap` | the space between columns and between rows |
|
|
96
|
+
| view | `keepPairs` | A→B and B→A drawn as two lines |
|
|
97
|
+
| view | `repair: false` | no port repair |
|
|
98
|
+
| view | `subAndChips` | cards 80 px tall that show their sub-line, then their chips |
|
|
99
|
+
| view | `kindsFromLabels` | a line whose edges name no `kind` takes one guessed from its label |
|
|
100
|
+
| container | `badges` | tabs on the box's border |
|
|
101
|
+
|
|
102
|
+
## Command line
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python3 umlimator.py render examples/bookshop.json --out page.html # the interactive page, then its layout check
|
|
106
|
+
python3 umlimator.py stills page.html --out stills/ # every view as SVG and PNG
|
|
107
|
+
python3 umlimator.py stills page.html --out stills/ --views overview,api --scale 1.5 --theme light
|
|
108
|
+
python3 umlimator.py check page.html --views 'pic-*' # the layout check on chosen views
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The page has breadcrumbs, a details panel per card, ⌘K search, deep links (`#view=<id>&node=<id>`) and export.
|
|
112
|
+
`stills` skips a view whose inputs have not changed since its last export (`--all` exports every view). Needs Python
|
|
113
|
+
3.9+ (standard library only), and Google Chrome or Chromium for `stills` and `check`.
|
|
114
|
+
|
|
115
|
+
## Map a repo with Claude Code
|
|
116
|
+
|
|
117
|
+
The `map-repo` skill reads a codebase, writes a C4-style model (context, containers, components), renders it to a page
|
|
118
|
+
and stills, and fixes what the layout check reports. Install it, then ask Claude Code to map the repository:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
/plugin marketplace add zintaen/umlimator
|
|
122
|
+
/plugin install umlimator@umlimator
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
On Claude Code 2.1.275 or later, `/plugin install umlimator --marketplace zintaen/umlimator` does both in one step. The
|
|
126
|
+
skill needs git, Python 3.9+ and Chrome; on first use it fetches the CLI of one pinned release into
|
|
127
|
+
`~/.cache/umlimator/cli`.
|
|
128
|
+
|
|
129
|
+
## Icons
|
|
130
|
+
|
|
131
|
+
A node's `icon` names an entry in `icons/manifest.json`. Line icons ship in `icons/` and take the card's colour. Brand
|
|
132
|
+
logos do not ship: they are their owners' trademarks. The manifest records each logo's official source and a sha256;
|
|
133
|
+
the first render that needs one downloads it into `~/.cache/umlimator/icons` (or `$UMLIMATOR_ICON_CACHE`) and uses it
|
|
134
|
+
only if the hash matches. A logo is never recoloured; follow each brand's guidelines for its use.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
python3 -I tools/fetch_icons.py icons "name:slug" # a brand logo: svgl, then devicon, then gilbarbara/logos
|
|
138
|
+
python3 -I tools/fetch_icons.py icons "name=https://..." # a brand's own official file
|
|
139
|
+
python3 -I tools/fetch_icons.py icons "name:lucide/<icon>" # a line icon
|
|
140
|
+
python3 umlimator.py pin-icons # records each new logo's sha256
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
node --test # layout() in Node: the bench views' geometry and the layout rules
|
|
147
|
+
python3 tests/embed.py # render() in headless Chrome: events, keyboard, colours, contrast, editing
|
|
148
|
+
python3 bench/bench.py --check-determinism --expect bench/expected.json --max-ms m50=2000
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The bench lays out fixed models (10 to 69 cards, and 50 modules with 100 lines) in headless Chrome and reports the
|
|
152
|
+
time per phase, the layout check's issues and a hash of the geometry. `--expect` fails on a view that no longer draws as
|
|
153
|
+
recorded in `bench/expected.json`; a change that moves lines on purpose refreshes it with `--write-expected`. CI runs
|
|
154
|
+
all of the above, and checks that the files copied from upstream (`engine/scripts`, `engine/references`,
|
|
155
|
+
`tools/fetch_icons.py`) are unmodified.
|
|
156
|
+
|
|
157
|
+
To release, bump `package.json`'s version in a PR and add that version's `## X.Y.Z: …` section to
|
|
158
|
+
`docs/module-api.md`. When the PR is merged and the checks pass on `main`, CI tags `vX.Y.Z`, publishes a GitHub
|
|
159
|
+
Release with that section as its notes, and publishes the package to npm (trusted publishing: no token is stored).
|
|
160
|
+
CI also publishes the examples to GitHub Pages from `main`.
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
umlimator.py CLI: render, stills, check, pin-icons
|
|
164
|
+
lib/ the module (umlimator.js), its types and the default theme
|
|
165
|
+
engine/ the page template the CLI inlines the module into; the upstream renderer and schema
|
|
166
|
+
docs/ the module's API and the editing design
|
|
167
|
+
tests/, bench/ tests, benchmark models and results
|
|
168
|
+
icons/ line icons and the logo manifest (sources and hashes only)
|
|
169
|
+
examples/ made-up systems: models, host pages and a Node script (live: zintaen.github.io/umlimator)
|
|
170
|
+
.claude-plugin/, plugins/ the Claude Code marketplace and its map-repo skill
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Licence
|
|
174
|
+
|
|
175
|
+
MIT (`LICENSE`). Third-party notices: `THIRD_PARTY_NOTICES.md`.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
umlimator contains, or builds on, the work below. Each entry says what is used and under which licence.
|
|
4
|
+
|
|
5
|
+
1. super-board `/visual` (MIT): the engine in `lib/` and `engine/`. Its own notices for BuilderIO/skills and archify follow,
|
|
6
|
+
kept as super-board wrote them, with one note added on archify's later licence change.
|
|
7
|
+
2. caru-ini/stack-diagram (MIT): `tools/fetch_icons.py` and the rules for brand logos.
|
|
8
|
+
3. Lucide (ISC, some icons MIT from Feather): the line icons in `icons/`.
|
|
9
|
+
4. Brand logos: not distributed with umlimator (see the last section).
|
|
10
|
+
|
|
11
|
+
## EricTechPro/super-board, `/visual` skill
|
|
12
|
+
|
|
13
|
+
`lib/umlimator.js` and `engine/assets/template.html` derive from super-board's `skills/visual/assets/template.html`
|
|
14
|
+
at commit `d0b7d5a` (29 Oct 2026), modified by umlimator (the changes are in its git history and `docs/module-api.md`);
|
|
15
|
+
`engine/scripts/visual.py` and `engine/references/` are copied from that commit unmodified. Source:
|
|
16
|
+
https://github.com/EricTechPro/super-board
|
|
17
|
+
|
|
18
|
+
MIT License
|
|
19
|
+
|
|
20
|
+
Copyright (c) 2026 Eric Tech
|
|
21
|
+
|
|
22
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
23
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
24
|
+
in the Software without restriction, including without limitation the rights
|
|
25
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
26
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
27
|
+
furnished to do so, subject to the following conditions:
|
|
28
|
+
|
|
29
|
+
The above copyright notice and this permission notice shall be included in all
|
|
30
|
+
copies or substantial portions of the Software.
|
|
31
|
+
|
|
32
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
33
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
34
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
35
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
36
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
37
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
38
|
+
SOFTWARE.
|
|
39
|
+
|
|
40
|
+
super-board's own third-party notice for `/visual`, as it ships it:
|
|
41
|
+
|
|
42
|
+
`/visual` adapts ideas from two MIT-licensed projects. No source file is copied
|
|
43
|
+
whole; the borrowed parts are named below so the attribution is precise.
|
|
44
|
+
|
|
45
|
+
## BuilderIO/skills (visual-recap, visual-plan)
|
|
46
|
+
|
|
47
|
+
Adapted from BuilderIO/skills (MIT): the per-mode section list, the recap and
|
|
48
|
+
plan quality bar, the grounding and secret-redaction rules, and the
|
|
49
|
+
"Key changes" + file-tree layout. `references/sections.md` carries them. The
|
|
50
|
+
hosted Agent-Native Plans app, MDX blocks, MCP connector, and `npx` tooling are
|
|
51
|
+
not used.
|
|
52
|
+
|
|
53
|
+
Source: https://github.com/BuilderIO/skills
|
|
54
|
+
|
|
55
|
+
MIT License
|
|
56
|
+
|
|
57
|
+
Copyright (c) 2026 Builder.io
|
|
58
|
+
|
|
59
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
60
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
61
|
+
in the Software without restriction, including without limitation the rights
|
|
62
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
63
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
64
|
+
furnished to do so, subject to the following conditions:
|
|
65
|
+
|
|
66
|
+
The above copyright notice and this permission notice shall be included in all
|
|
67
|
+
copies or substantial portions of the Software.
|
|
68
|
+
|
|
69
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
70
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
71
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
72
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
73
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
74
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
75
|
+
SOFTWARE.
|
|
76
|
+
|
|
77
|
+
## tt-a1i/archify
|
|
78
|
+
|
|
79
|
+
Diagram style adapted from Archify (MIT): the semantic node palette (dark and
|
|
80
|
+
light values), the opaque-mask-under-translucent-fill node construction, the
|
|
81
|
+
40px grid canvas, orthogonal rounded routes with arrowhead markers, and
|
|
82
|
+
per-kind icons (the cloud, shield, and external-link icon paths follow
|
|
83
|
+
Archify's semantic sigils). `assets/template.html` re-implements these as a small inline
|
|
84
|
+
renderer.
|
|
85
|
+
|
|
86
|
+
Viewer UX adapted from Archify's viewer (`viewer/viewer-camera.js`, `semantic-radar.js`,
|
|
87
|
+
`focus.js`, `node-finder.js`, `export.js`): camera zoom and pan with fit and 100% presets
|
|
88
|
+
and keyboard steps, reading depths that hide detail when zoomed out (map / read / full),
|
|
89
|
+
a minimap with a draggable viewport, a node focus panel with upstream and downstream reach
|
|
90
|
+
(Archify's "Semantic Passport"), dimming of unrelated nodes on focus, a node finder, SVG and
|
|
91
|
+
PNG export, and the floating control-pill and toolbar look. These are re-implemented from
|
|
92
|
+
scratch in `assets/template.html`; no Archify source file is copied. The edge router follows
|
|
93
|
+
the approach of Archify's `renderers/shared/route-quality.mjs` (`shortestOrthogonalGridRoute`)
|
|
94
|
+
and `renderers/architecture/routing.mjs`: one port per edge spread along the node side in the
|
|
95
|
+
order of the far ends, a sparse orthogonal visibility grid built from node edges plus clearance
|
|
96
|
+
and earlier routes plus separation, Dijkstra with a bend penalty over (cell, direction) states,
|
|
97
|
+
no collinear reuse of earlier routes, and labels reserved as obstacles for later routes.
|
|
98
|
+
Re-implemented, not copied. Drill-down views,
|
|
99
|
+
breadcrumbs, deep links, and the overlap check are /visual's own. Archify's JSON-IR CLI,
|
|
100
|
+
validators, viewer runtime, and embedded font are not bundled.
|
|
101
|
+
|
|
102
|
+
Source: https://github.com/tt-a1i/archify
|
|
103
|
+
|
|
104
|
+
Note added by umlimator (2 Nov 2026): archify moved to the Apache License 2.0 on 25 Oct 2026 (commit 9c306ee). Its
|
|
105
|
+
NOTICE keeps code and contributions received under MIT through commit 25cf370 under their original terms, and keeps
|
|
106
|
+
the historical MIT notice, reproduced below, in its LICENSE-MIT. archify itself originated from
|
|
107
|
+
Cocoon-AI/architecture-diagram-generator (MIT), which is why Cocoon AI appears in that notice. super-board adapted
|
|
108
|
+
ideas, not files, and umlimator copies none of archify's files.
|
|
109
|
+
|
|
110
|
+
MIT License
|
|
111
|
+
|
|
112
|
+
Copyright (c) 2026 tt-a1i (Archify)
|
|
113
|
+
Copyright (c) 2025 Cocoon AI
|
|
114
|
+
|
|
115
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
116
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
117
|
+
in the Software without restriction, including without limitation the rights
|
|
118
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
119
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
120
|
+
furnished to do so, subject to the following conditions:
|
|
121
|
+
|
|
122
|
+
The above copyright notice and this permission notice shall be included in all
|
|
123
|
+
copies or substantial portions of the Software.
|
|
124
|
+
|
|
125
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
126
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
127
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
128
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
129
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
130
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
131
|
+
SOFTWARE.
|
|
132
|
+
|
|
133
|
+
## caru-ini/stack-diagram
|
|
134
|
+
|
|
135
|
+
`tools/fetch_icons.py` is copied unmodified from caru-ini/stack-diagram (commit `30cbbb7`). The rules umlimator follows
|
|
136
|
+
for brand logos (official marks only, never recoloured, a white tile, a dark tile for a white mark, fetched rather than
|
|
137
|
+
bundled) are stack-diagram's. stack-diagram's bundled fonts (SIL OFL 1.1) are not used. Source:
|
|
138
|
+
https://github.com/caru-ini/stack-diagram
|
|
139
|
+
|
|
140
|
+
MIT License
|
|
141
|
+
|
|
142
|
+
Copyright (c) 2026 caru-ini
|
|
143
|
+
|
|
144
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
145
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
146
|
+
in the Software without restriction, including without limitation the rights
|
|
147
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
148
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
149
|
+
furnished to do so, subject to the following conditions:
|
|
150
|
+
|
|
151
|
+
The above copyright notice and this permission notice shall be included in all
|
|
152
|
+
copies or substantial portions of the Software.
|
|
153
|
+
|
|
154
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
155
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
156
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
157
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
158
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
159
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
160
|
+
SOFTWARE.
|
|
161
|
+
|
|
162
|
+
## Lucide icons
|
|
163
|
+
|
|
164
|
+
The line icons in `icons/` (every manifest entry whose source is `lucide`) are from Lucide, fetched from the
|
|
165
|
+
`lucide-static` package; `tools/fetch_icons.py` sets their stroke colour. Source: https://github.com/lucide-icons/lucide
|
|
166
|
+
|
|
167
|
+
ISC License
|
|
168
|
+
|
|
169
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
170
|
+
|
|
171
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
172
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
173
|
+
copyright notice and this permission notice appear in all copies.
|
|
174
|
+
|
|
175
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
176
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
177
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
178
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
179
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
180
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
181
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
182
|
+
|
|
183
|
+
Of the icons umlimator ships, clock, database, link, lock, server, terminal and trash-2 are derived from the Feather
|
|
184
|
+
project, under this licence:
|
|
185
|
+
|
|
186
|
+
The MIT License (MIT)
|
|
187
|
+
|
|
188
|
+
Copyright (c) 2013-present Cole Bemis
|
|
189
|
+
|
|
190
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
191
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
192
|
+
in the Software without restriction, including without limitation the rights
|
|
193
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
194
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
195
|
+
furnished to do so, subject to the following conditions:
|
|
196
|
+
|
|
197
|
+
The above copyright notice and this permission notice shall be included in all
|
|
198
|
+
copies or substantial portions of the Software.
|
|
199
|
+
|
|
200
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
201
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
202
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
203
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
204
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
205
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
206
|
+
SOFTWARE.
|
|
207
|
+
|
|
208
|
+
## Brand logos (not distributed)
|
|
209
|
+
|
|
210
|
+
umlimator does not ship any brand logo. `icons/manifest.json` records, for each one, where its official file is
|
|
211
|
+
published and a sha256; `umlimator.py render` downloads it into the user's cache the first time a model names it.
|
|
212
|
+
The logos are trademarks of their owners and keep their owners' terms: svgl (the project is MIT; the logos belong to
|
|
213
|
+
their owners), devicon (MIT), gilbarbara/logos (CC0), Microsoft's Azure and Entra architecture icons (allowed in
|
|
214
|
+
architecture diagrams, training material and documentation), and files published by the brands themselves. Their use
|
|
215
|
+
in a diagram does not imply endorsement.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* umlimator's default palette and fonts: light, and dark when the system asks or <html data-theme="dark">.
|
|
2
|
+
The canvas reads only these --um-* variables (it bakes in no colour and loads no font), so a host may use this file,
|
|
3
|
+
set the variables itself (e.g. --um-bg: var(--surface)), or switch light and dark by changing them on any ancestor
|
|
4
|
+
of the map. Fonts are the host's: --um-sans and --um-mono name families the page already has; nothing is fetched.
|
|
5
|
+
It meets WCAG AA in both themes (text 4.5:1 on its background, lines 3:1 on the stage): tests/contrast.html.
|
|
6
|
+
The tints (--um-<tone>-f) below are for page chrome: the map mixes its own from the tones and --um-panel in effect
|
|
7
|
+
where it is, so setting a tone on any ancestor of the map recolours its tint too. */
|
|
8
|
+
/* Clean flowchart (GitHub Primer-like): light first, dark when the system or the toggle asks. */
|
|
9
|
+
:root {
|
|
10
|
+
--um-stage-bg: #fbfcfd; --um-logo-tile: #ffffff; --um-logo-tile-dark: #1b1f23; --um-intake-mark: #1b1f23; --um-edited: #bf8700;
|
|
11
|
+
--um-bg: #ffffff; --um-bg-2: #f6f8fa; --um-panel: #ffffff; --um-panel-2: #f6f8fa; --um-border: #d8dee4; --um-border-2: #d1d9e0;
|
|
12
|
+
--um-text: #1f2328; --um-muted: #59636e; --um-dim: #818b98; --um-grid: rgba(31,35,40,.06); --um-mask: #ffffff; --um-arrow: #8c959f;
|
|
13
|
+
--um-shadow: 0 8px 24px rgba(31,35,40,.10); --um-dot: #e6eaef; --um-gridline: transparent; --um-chrome: rgba(255,255,255,.94);
|
|
14
|
+
--um-accent: #0969da; --um-accent-fg: #ffffff; --um-lane: rgba(31,35,40,.025);
|
|
15
|
+
--um-cyan: #0969da; --um-green: #1a7f37; --um-violet: #8250df; --um-amber: #9a6700; --um-rose: #cf222e; --um-orange: #bc4c00; --um-slate: #59636e; --um-fuchsia: #bf3989;
|
|
16
|
+
--um-k-public: #0969da; --um-k-lane: #0f766e; --um-k-ext: #8250df; --um-k-verb: #bf3989; --um-k-script: #6e7781; --um-k-hook: #cf222e; --um-k-board: #9a6700;
|
|
17
|
+
--um-w-calls: #424a53; --um-w-feeds: #0969da; --um-w-triggers: #8250df; --um-w-state: #868f99; --um-w-guard: #cf222e;
|
|
18
|
+
--um-add-bg: rgba(26,127,55,.10); --um-add-tx: #1a7f37; --um-del-bg: rgba(207,34,46,.08); --um-del-tx: #cf222e;
|
|
19
|
+
color-scheme: light;
|
|
20
|
+
--um-accent-f: color-mix(in srgb, var(--um-accent) 12%, var(--um-panel));
|
|
21
|
+
--um-cyan-f: color-mix(in srgb, var(--um-cyan) 9%, var(--um-panel));
|
|
22
|
+
--um-green-f: color-mix(in srgb, var(--um-green) 9%, var(--um-panel));
|
|
23
|
+
--um-violet-f: color-mix(in srgb, var(--um-violet) 9%, var(--um-panel));
|
|
24
|
+
--um-amber-f: color-mix(in srgb, var(--um-amber) 10%, var(--um-panel));
|
|
25
|
+
--um-rose-f: color-mix(in srgb, var(--um-rose) 8%, var(--um-panel));
|
|
26
|
+
--um-orange-f: color-mix(in srgb, var(--um-orange) 9%, var(--um-panel));
|
|
27
|
+
--um-slate-f: color-mix(in srgb, var(--um-slate) 8%, var(--um-panel));
|
|
28
|
+
--um-fuchsia-f: color-mix(in srgb, var(--um-fuchsia) 8%, var(--um-panel));
|
|
29
|
+
--um-k-public-f: color-mix(in srgb, var(--um-k-public) 8%, var(--um-panel));
|
|
30
|
+
--um-k-lane-f: color-mix(in srgb, var(--um-k-lane) 8%, var(--um-panel));
|
|
31
|
+
--um-k-ext-f: color-mix(in srgb, var(--um-k-ext) 7%, var(--um-panel));
|
|
32
|
+
--um-k-verb-f: color-mix(in srgb, var(--um-k-verb) 7%, var(--um-panel));
|
|
33
|
+
--um-k-script-f: color-mix(in srgb, var(--um-k-script) 5%, var(--um-panel));
|
|
34
|
+
--um-k-hook-f: color-mix(in srgb, var(--um-k-hook) 12%, var(--um-panel));
|
|
35
|
+
--um-k-board-f: color-mix(in srgb, var(--um-k-board) 9%, var(--um-panel));
|
|
36
|
+
--um-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
|
37
|
+
--um-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
38
|
+
}
|
|
39
|
+
@media (prefers-color-scheme: dark) {
|
|
40
|
+
:root:not([data-theme="light"]) {
|
|
41
|
+
--um-stage-bg: var(--um-bg-2); --um-edited: #d29922;
|
|
42
|
+
--um-bg: #0d1117; --um-bg-2: #010409; --um-panel: #0d1117; --um-panel-2: #151b23; --um-border: #262c36; --um-border-2: #3d444d;
|
|
43
|
+
--um-text: #f0f6fc; --um-muted: #9198a1; --um-dim: #656c76; --um-grid: rgba(240,246,252,.05); --um-mask: #0d1117; --um-arrow: #656c76;
|
|
44
|
+
--um-shadow: 0 10px 30px rgba(1,4,9,.55); --um-dot: #161b22; --um-gridline: transparent; --um-chrome: rgba(13,17,23,.92);
|
|
45
|
+
--um-accent: #4493f8; --um-accent-fg: #ffffff; --um-lane: rgba(240,246,252,.025);
|
|
46
|
+
--um-cyan: #4493f8; --um-green: #3fb950; --um-violet: #ab7df8; --um-amber: #d29922; --um-rose: #f85149; --um-orange: #db6d28; --um-slate: #9198a1; --um-fuchsia: #db61a2;
|
|
47
|
+
--um-k-public: #4493f8; --um-k-lane: #2dd4bf; --um-k-ext: #ab7df8; --um-k-verb: #f778ba; --um-k-script: #9198a1; --um-k-hook: #f85149; --um-k-board: #d29922;
|
|
48
|
+
--um-w-calls: #b7bdc8; --um-w-feeds: #4493f8; --um-w-triggers: #ab7df8; --um-w-state: #59626e; --um-w-guard: #f85149;
|
|
49
|
+
--um-add-bg: rgba(63,185,80,.13); --um-add-tx: #56d364; --um-del-bg: rgba(248,81,73,.12); --um-del-tx: #ff7b72;
|
|
50
|
+
color-scheme: dark;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
:root[data-theme="dark"] {
|
|
54
|
+
--um-stage-bg: var(--um-bg-2); --um-edited: #d29922;
|
|
55
|
+
--um-bg: #0d1117; --um-bg-2: #010409; --um-panel: #0d1117; --um-panel-2: #151b23; --um-border: #262c36; --um-border-2: #3d444d;
|
|
56
|
+
--um-text: #f0f6fc; --um-muted: #9198a1; --um-dim: #656c76; --um-grid: rgba(240,246,252,.05); --um-mask: #0d1117; --um-arrow: #656c76;
|
|
57
|
+
--um-shadow: 0 10px 30px rgba(1,4,9,.55); --um-dot: #161b22; --um-gridline: transparent; --um-chrome: rgba(13,17,23,.92);
|
|
58
|
+
--um-accent: #4493f8; --um-accent-fg: #ffffff; --um-lane: rgba(240,246,252,.025);
|
|
59
|
+
--um-cyan: #4493f8; --um-green: #3fb950; --um-violet: #ab7df8; --um-amber: #d29922; --um-rose: #f85149; --um-orange: #db6d28; --um-slate: #9198a1; --um-fuchsia: #db61a2;
|
|
60
|
+
--um-k-public: #4493f8; --um-k-lane: #2dd4bf; --um-k-ext: #ab7df8; --um-k-verb: #f778ba; --um-k-script: #9198a1; --um-k-hook: #f85149; --um-k-board: #d29922;
|
|
61
|
+
--um-w-calls: #b7bdc8; --um-w-feeds: #4493f8; --um-w-triggers: #ab7df8; --um-w-state: #59626e; --um-w-guard: #f85149;
|
|
62
|
+
--um-add-bg: rgba(63,185,80,.13); --um-add-tx: #56d364; --um-del-bg: rgba(248,81,73,.12); --um-del-tx: #ff7b72;
|
|
63
|
+
color-scheme: dark;
|
|
64
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Types for lib/umlimator.js (hand-written; the module is plain JavaScript). docs/module-api.md explains the API.
|
|
2
|
+
|
|
3
|
+
/** A map model: engine/references/schema.md § Map model, plus the README's umlimator fields. Never mutated. */
|
|
4
|
+
export interface MapModel {
|
|
5
|
+
title?: string;
|
|
6
|
+
root?: string;
|
|
7
|
+
nodes: MapNode[];
|
|
8
|
+
edges?: MapEdge[];
|
|
9
|
+
views: MapView[];
|
|
10
|
+
icons?: Record<string, { g?: string; src?: string; dark?: boolean }>;
|
|
11
|
+
authors?: Record<string, { name?: string; url?: string; avatar?: string }>;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
/** A badge: its text, and optionally a palette name for its colour (green, amber, rose, accent, violet ... : --um-<tone>). */
|
|
15
|
+
export type Badge = string | { text: string; tone?: string };
|
|
16
|
+
export interface MapNode {
|
|
17
|
+
id: string; label?: string; kind?: string; sublabel?: string; badge?: string; badges?: Badge[]; edited?: boolean; icon?: string; verbs?: string[];
|
|
18
|
+
/** The view this card opens. It may name a view the host has not loaded yet: the card still looks stacked, and
|
|
19
|
+
* double-click, Enter on the selected card and a click on its ⤢ badge emit `open` with that id; the map goes in once
|
|
20
|
+
* the view is in the model (the host loads it, calls update(), then setView(), or calls update() in its open handler). */
|
|
21
|
+
opensView?: string;
|
|
22
|
+
/** The number on the ⤢ badge of a card whose view is not loaded yet (else ⤢ alone). A loaded view's cards are counted. */
|
|
23
|
+
opensCount?: number;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** What a line means, drawn as its colour: calls (the default), feeds (hands work on), triggers (suggests, offers, fires),
|
|
27
|
+
* state (reads or writes a file, board or config: a thinner line, a muted label, no flow) and guard (a hook steps in).
|
|
28
|
+
* Colours are --um-w-<kind>. An edge's `style` (emphasis, dashed, security) wins over its kind's colour. */
|
|
29
|
+
export type WireKind = 'calls' | 'feeds' | 'triggers' | 'state' | 'guard';
|
|
30
|
+
/** from / to: a card's id, or a container's id in the view (the line ends on its box's border). */
|
|
31
|
+
export interface MapEdge {
|
|
32
|
+
id?: string; from: string; to: string; label?: string; edited?: boolean;
|
|
33
|
+
/** The line's kind (default calls). A line that stands for several edges (A→B and B→A, parallel edges) takes the first
|
|
34
|
+
* kind they name, in model order. Nothing is guessed from the label unless the view has `kindsFromLabels`. */
|
|
35
|
+
kind?: WireKind;
|
|
36
|
+
/** How much the line stands for, drawn as its width: 1 (the default), 2 or 3 (about 1.8, 3.2 and 4.5 px), the arrowhead and
|
|
37
|
+
* hit line growing with it. The host picks the bucket (say under 10, under 100, 100 or more relations); anything else is 1.
|
|
38
|
+
* A line that stands for several edges takes the largest. */
|
|
39
|
+
weight?: 1 | 2 | 3;
|
|
40
|
+
/** A second, smaller line under the label (a technology, say), placed with it and counted in the room left for labels.
|
|
41
|
+
* Shown only with a label. A line that stands for several edges shows each of their sublabels, joined by " / ". */
|
|
42
|
+
sublabel?: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
export interface MapView {
|
|
46
|
+
id: string; parent?: string | null; title?: string; nodeIds: string[];
|
|
47
|
+
edges?: (string | MapEdge)[]; layout?: Record<string, [number, number]>; repair?: boolean;
|
|
48
|
+
/** Cards 80 px tall that show their sub-line and then their chips (`verbs`) on a third line. Default: 64 px, and a card's
|
|
49
|
+
* chips take its sub-line's place. A script card (no kind) shows no sub-line either way. */
|
|
50
|
+
subAndChips?: boolean;
|
|
51
|
+
/** Guess a line's kind from the words of its label ("reads" is state, "triggers" is triggers ...) when its edges name
|
|
52
|
+
* none. Off in render() and layout(), which draw no legend; the standalone page (mountPage) always guesses. */
|
|
53
|
+
kindsFromLabels?: boolean;
|
|
54
|
+
containers?: { id: string; label?: string; nodeIds?: string[]; parent?: string; badges?: Badge[] }[]; // badges: tabs on the box's border
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** What a click or selection is about, named by ids from the model. An edge without an id is "<from>-><to>#<n>". */
|
|
59
|
+
export type Ref =
|
|
60
|
+
| { type: 'node'; id: string; view: string }
|
|
61
|
+
| { type: 'edge'; ids: string[]; view: string } // one drawn line can stand for several model edges
|
|
62
|
+
| { type: 'container'; id: string; view: string }
|
|
63
|
+
| { type: 'chip'; node: string; index: number; value: string; view: string }
|
|
64
|
+
| { type: 'badge'; node: string; index: number; value: string; view: string }
|
|
65
|
+
| { type: 'badge'; container: string; index: number; value: string; view: string }; // a container's badge
|
|
66
|
+
|
|
67
|
+
export interface Events {
|
|
68
|
+
click: { target: Ref; event: MouseEvent | KeyboardEvent; preventDefault(): void }; // preventDefault(): the selection stays as it was (no select); Enter on a focused tab or chip is a click (KeyboardEvent)
|
|
69
|
+
select: { target: Ref | null; targets: Ref[] }; // a chip or badge selects its card (a container's badge: its box); targets: every selected thing, the latest last
|
|
70
|
+
open: { target: Ref; view: string; preventDefault(): void }; // view: the one the card opens, which may not be in the model yet
|
|
71
|
+
viewchange: { view: string; from: string };
|
|
72
|
+
change: Change; // only with `editable`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Where the user dropped or placed something: a point in the view and, on a column view, its column and row. */
|
|
76
|
+
export interface Hint { x: number; y: number; col?: number; row?: number }
|
|
77
|
+
/** An edit the user asked for. Nothing changes until the host applies it and calls update(). */
|
|
78
|
+
export type Change = { id: string; view: string; hint?: Hint } & (
|
|
79
|
+
| { op: 'add'; kind: 'node'; label: string; container?: string }
|
|
80
|
+
| { op: 'add'; kind: 'edge'; from: string; to: string; label?: string }
|
|
81
|
+
| { op: 'rename'; target: Ref; label: string }
|
|
82
|
+
| { op: 'move'; target: Extract<Ref, { type: 'node' }>; to: string | null }
|
|
83
|
+
| { op: 'delete'; target: Ref }
|
|
84
|
+
| { op: 'reorder'; target: Ref; index: number });
|
|
85
|
+
|
|
86
|
+
export interface RenderOptions {
|
|
87
|
+
view?: string; // default: model.root, else the view with no parent
|
|
88
|
+
select?: Ref | null;
|
|
89
|
+
controls?: boolean; // up / zoom / fit / 1:1 / flow buttons (default true)
|
|
90
|
+
minimap?: boolean; // default false
|
|
91
|
+
flow?: boolean; // animated flow along lines (default: on unless prefers-reduced-motion)
|
|
92
|
+
/** Text widths: default the host's font (--um-sans, measured on a canvas); false: a font-independent estimate. */
|
|
93
|
+
measure?: false | ((text: string, px: number) => number);
|
|
94
|
+
/** Edit gestures that emit `change` events: all of them, or only those set true. Off by default. */
|
|
95
|
+
editable?: boolean | { add?: boolean; rename?: boolean; move?: boolean; delete?: boolean; reorder?: boolean };
|
|
96
|
+
/** Cards keep their places across update() (in this handle only, never in the model). Default true. */
|
|
97
|
+
stableLayout?: boolean;
|
|
98
|
+
on?: { [K in keyof Events]?: (e: Events[K]) => void };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ExportOptions { view?: string; scale?: number; fontCSS?: string }
|
|
102
|
+
|
|
103
|
+
export interface Handle {
|
|
104
|
+
update(model: MapModel): void;
|
|
105
|
+
setView(id: string): Promise<void> | void;
|
|
106
|
+
select(ref: Ref | null): void;
|
|
107
|
+
fit(): void;
|
|
108
|
+
/** A view (default: the one shown; another is drawn off screen) as a stand-alone SVG file: colours resolved from the
|
|
109
|
+
* host's CSS, drawn at rest (no selection, hover, flow or edit handles), `scale` × its natural size (default 1).
|
|
110
|
+
* `fontCSS`: the host's own @font-face rules (data: URLs) to carry into the file. No network, no headless browser.
|
|
111
|
+
* '' for an unknown or empty view, or a destroyed handle. */
|
|
112
|
+
toSVG(options?: ExportOptions): string;
|
|
113
|
+
/** The same picture as a PNG Blob, drawn on a canvas in the browser (default scale 2). null when there is nothing to draw. */
|
|
114
|
+
toPNG(options?: ExportOptions): Promise<Blob | null>;
|
|
115
|
+
/** Puts keyboard focus on the card or line `ref` names (a chip or badge: its card), by default the selected one, else on
|
|
116
|
+
* the map. The map's element delegates focus too: `host.focus()`, as a dialog does when it gives focus back, lands on
|
|
117
|
+
* the selected card or line. */
|
|
118
|
+
focus(ref?: Ref | null): void;
|
|
119
|
+
on<K extends keyof Events>(type: K, fn: (e: Events[K]) => void): () => void;
|
|
120
|
+
readonly view: string | null;
|
|
121
|
+
readonly selection: Ref | null; // the latest selected
|
|
122
|
+
readonly selected: Ref[]; // every selected thing (Shift / Cmd / Ctrl + click adds cards), the latest last
|
|
123
|
+
destroy(): void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface Geometry {
|
|
127
|
+
nodes: { id: string; x: number; y: number; w: number; h: number; [key: string]: unknown }[];
|
|
128
|
+
edges: { from: string; to: string; ids: string[]; pts: [number, number][]; weight: 1 | 2 | 3; wk: WireKind;
|
|
129
|
+
lbl: { x: number; y: number; w: number; h: number; text: string; sub?: string } | null; [key: string]: unknown }[];
|
|
130
|
+
bounds: { x: number; y: number; w: number; h: number };
|
|
131
|
+
[key: string]: unknown;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function render(container: HTMLElement, model: MapModel, options?: RenderOptions): Handle;
|
|
135
|
+
/** Undo for a host: it pushes each model it applies, and hands undo() / redo() results to handle.update(). */
|
|
136
|
+
export interface History { readonly model: MapModel; readonly canUndo: boolean; readonly canRedo: boolean; push(m: MapModel): MapModel; undo(): MapModel; redo(): MapModel }
|
|
137
|
+
export function history(model: MapModel, limit?: number): History;
|
|
138
|
+
export function layout(model: MapModel, viewId: string, options?: { repair?: boolean; measure?: (text: string, px: number) => number }): Geometry;
|
|
139
|
+
/** The whole standalone viewer (top bar, views list, details panel, search, export) that umlimator.py writes. */
|
|
140
|
+
export function mountPage(container: HTMLElement, model: MapModel, options?: { params?: URLSearchParams; instant?: boolean }): Handle;
|