umlimator 0.13.0 → 0.15.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/README.md +54 -20
- package/lib/umlimator-element.d.ts +4 -2
- package/lib/umlimator-element.js +27 -12
- package/lib/umlimator-markdown-it.js +5 -5
- package/lib/umlimator-theme.css +6 -2
- package/lib/umlimator.d.ts +99 -11
- package/lib/umlimator.js +1281 -428
- package/lib/umlimator.schema.json +73 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ drill-down, a data pipeline, an AI agent's skills, and host pages for embedding,
|
|
|
22
22
|
straight; orthogonal lines that never run through a card, room for every label, containers kept apart, and cards with
|
|
23
23
|
no lines in a row of their own, under the view or at the bottom of their box. The same model gives the same picture.
|
|
24
24
|
- **Reading**: animated flow, neighbours lit on selection, numbered steps, tours, minimap, light and dark themes.
|
|
25
|
+
- **Card shapes**: three notations. `box` (the default) draws every card as a rounded box, as before. `c4` draws people,
|
|
26
|
+
browser apps, phones, databases, queues and files as the C4 model's tools do. `uml` draws UML 2.5's actors, nodes,
|
|
27
|
+
artifacts, packages and data stores, with UML's component icon on a box. A card's own `shape` wins in any notation.
|
|
28
|
+
The notation comes from the model, a view, `render()`, the page's toggle or the link; switching it draws the cards
|
|
29
|
+
again and lays nothing out ([`examples/notations.html`](examples/notations.html)).
|
|
25
30
|
- **Exploring**: every path between two cards, or everything a card reaches, lit on the map; a view in plain sentences
|
|
26
31
|
(`describe()`); a tour along any path the host names.
|
|
27
32
|
- **Live data**: marks over the map without touching the model (`overlay()`): flow as fast as the traffic, errors that
|
|
@@ -38,8 +43,9 @@ drill-down, a data pipeline, an AI agent's skills, and host pages for embedding,
|
|
|
38
43
|
- **The page's pieces, embedded**: the standalone page's views list, details panel, legend and hint line, each one a
|
|
39
44
|
`render()` option, around the map in the host's own page.
|
|
40
45
|
- **Editing** (opt-in): gestures emit `change` events; the host applies them and calls `update()`.
|
|
41
|
-
- **Export**: any view as a stand-alone SVG or PNG, in the browser or from the CLI; the whole map as one PDF
|
|
42
|
-
with
|
|
46
|
+
- **Export**: any view as a stand-alone SVG or PNG, in the browser or from the CLI; the whole map as one PDF to hand in
|
|
47
|
+
for a review: a cover with the contents, each view with its legend and tables of its cards, lines and boxes, then the
|
|
48
|
+
rules (`umlimator.py pdf`).
|
|
43
49
|
- **In docs**: `<umlimator-map src="model.json">` in any page, and an `umlimator` fenced block in VitePress, Docusaurus,
|
|
44
50
|
MkDocs or any Markdown that allows HTML.
|
|
45
51
|
- **A layout check**: crossings, shared lines, detours, labels on lines, text that overflows its card.
|
|
@@ -59,7 +65,7 @@ drill-down, a data pipeline, an AI agent's skills, and host pages for embedding,
|
|
|
59
65
|
npm install umlimator
|
|
60
66
|
```
|
|
61
67
|
|
|
62
|
-
Or from GitHub, at a tag or any commit: `"umlimator": "github:zintaen/umlimator#v0.
|
|
68
|
+
Or from GitHub, at a tag or any commit: `"umlimator": "github:zintaen/umlimator#v0.15.0"`. npm 12 refuses git
|
|
63
69
|
dependencies unless the project opts in, so that also needs `allow-git=root` in the `.npmrc` next to `package.json`.
|
|
64
70
|
|
|
65
71
|
## Embed
|
|
@@ -94,7 +100,8 @@ function MapView({ model, onSelect }) {
|
|
|
94
100
|
- `map.expand(id)` unfolds a stacked card in place and `map.collapse(id)` folds it; `layout(model, view, { expand })`
|
|
95
101
|
draws the same in Node.
|
|
96
102
|
- Colours are `--um-*` CSS variables, fonts are the host's (`--um-sans`, `--um-mono`), text is measured in that font,
|
|
97
|
-
and nothing is fetched.
|
|
103
|
+
and nothing is fetched. `--um-tint` sets how deep the cards' and the boxes' tints are: 1 by default, 0 for none, 2 for
|
|
104
|
+
twice as deep (a tint stops at 18% of its colour, so text on it keeps 4.5:1).
|
|
98
105
|
- `layout(model, viewId)` returns a view's geometry without a DOM, in Node too; `check(model, viewId)` runs the layout
|
|
99
106
|
check on it, and `validate(model)` finds dangling or repeated ids before anything is drawn. `rules(model)` lists every
|
|
100
107
|
rule the model breaks, view by view.
|
|
@@ -103,6 +110,9 @@ function MapView({ model, onSelect }) {
|
|
|
103
110
|
- `map.lens(id)` puts one of the model's lenses on the map. `critical(model, view, { from, to, by })` finds the slowest
|
|
104
111
|
or costliest path, `whatIf(model, view, { remove })` what loses its way when cards go down, and
|
|
105
112
|
`sequenceFromTrace(model, spans, { view, match })` keeps a trace as a numbered view; all three in Node too.
|
|
113
|
+
- `shapes: 'box' | 'c4' | 'uml'` draws every view in that notation, over each view's own (and the model's).
|
|
114
|
+
`map.shapes` reads the notation shown and sets another (`null`: each view's own again); the cards are drawn again from
|
|
115
|
+
the geometry they have, with the camera, selection and marks kept, and nothing is laid out.
|
|
106
116
|
- `editable: true` turns on editing gestures ([docs/editing.md](docs/editing.md); `examples/edit.html` is a working host).
|
|
107
117
|
- `diff(before, after)` returns `after` with each card and line marked added, changed or removed, what changed in words,
|
|
108
118
|
and the views that show it; `drift(model, observed)` marks the lines traffic ran on that nobody drew, and the drawn ones
|
|
@@ -141,9 +151,11 @@ the map is drawn, and stay where scripts don't run ([`examples/element.html`](ex
|
|
|
141
151
|
|
|
142
152
|
- **The model**: `src`, a URL resolved as an `<img>`'s is (a `data:` URL too), or the model in a child
|
|
143
153
|
`<script type="application/json">`.
|
|
144
|
-
- **Attributes**: `view`, `minimap`, `controls="false"`, `
|
|
154
|
+
- **Attributes**: `view`, `minimap`, `controls="false"`, `wheel="ctrl"` (the wheel zooms only with Ctrl or ⌘, and scrolls
|
|
155
|
+
the page otherwise, as a map in a document should), `height` (a number is pixels; 480 by default, or size it in CSS)
|
|
145
156
|
and `theme` (`light` or `dark`: theme.css's palette for that map, whatever the page's). Changing one draws the map
|
|
146
|
-
again, and removing the element destroys it.
|
|
157
|
+
again, and removing the element destroys it. `shapes` (`box`, `c4` or `uml`) is the notation; changing it draws the
|
|
158
|
+
cards again in the map drawn, with nothing loaded or laid out again.
|
|
147
159
|
- **Events**: `umlimator:select`, `umlimator:open` (cancel it to stay) and `umlimator:viewchange`, the map's event as
|
|
148
160
|
`detail`; `load` once it is drawn, `error` when it can't be. `el.map` is the `render()` handle.
|
|
149
161
|
- **Colours**: theme.css, as above, or your own `--um-*` variables. The element fetches nothing but its model.
|
|
@@ -188,9 +200,11 @@ kinds such as `public`, `lane`, `script`, `hook` and `board` work too). Fields b
|
|
|
188
200
|
| node | `noSub` | the name only; the description stays in the details panel |
|
|
189
201
|
| node | `opensView`, `opensCount` | the view the card opens, which may not be loaded yet, and the ⤢ number for it |
|
|
190
202
|
| node | `owner` | who owns the card (a team): rules and lenses pick by it, and a lens can ring cards by owner |
|
|
203
|
+
| node | `shape` | the card's own shape in every notation: `box`, `person`, `browser`, `mobile`, `cylinder`, `pipe`, `document`, `hexagon`, `robot`, `terminal`, `window`, `folder` or `node` |
|
|
191
204
|
| node, edge | `edited` | a marker on the card or line |
|
|
192
205
|
| edge | `kind` | what the line means, drawn as its colour: `calls` (default), `feeds`, `triggers`, `state`, `guard` |
|
|
193
206
|
| edge | `weight` | 1, 2 or 3: how much the line stands for, drawn as its width |
|
|
207
|
+
| edge | `step` | a number (2, or 2.1) or text of up to 3 characters (2a), in a circle on the line in any view (a pill for 2 or 3 characters): the order things happen, shown as written in the line's name and title. A numbered view numbers its lines in run order instead |
|
|
194
208
|
| edge | `sublabel` | a second, smaller line under the label |
|
|
195
209
|
| edge | `fromSide`, `toSide` | the card sides the line uses |
|
|
196
210
|
| edge | `from`, `to` | may name a container of the view: the line ends on its box |
|
|
@@ -202,7 +216,9 @@ kinds such as `public`, `lane`, `script`, `hook` and `board` work too). Fields b
|
|
|
202
216
|
| view | `repair: false` | no port repair |
|
|
203
217
|
| view | `subAndChips` | cards 80 px tall that show their sub-line, then their chips |
|
|
204
218
|
| view | `kindsFromLabels` | a line whose edges name no `kind` takes one guessed from its label |
|
|
219
|
+
| view, model | `shapes` | the notation the cards are drawn in: `box` (default), `c4` or `uml`; a view's wins over the model's ([what each draws](docs/module-api.md#card-shapes-box-c4-and-uml)) |
|
|
205
220
|
| container | `badges` | tabs on the box's border |
|
|
221
|
+
| container | `tone` | a palette name, as a badge's: the region behind the box tinted in it, its border dashed and its title in the tone (in `c4`, every box is dashed) |
|
|
206
222
|
| model | `rules` | what must hold: no line `from` some cards `to` others (picked by id, kind, box or owner), or `noCycles` between boxes |
|
|
207
223
|
| model | `stories` | walk-throughs: steps that each set the view, camera, selection, highlight, tour and caption |
|
|
208
224
|
| model | `lenses` | ways to look at the map: what `pick` picks (by id, kind, box, owner or `line` kind) lit, the rest dimmed; `color: "owner"` rings cards by owner |
|
|
@@ -223,7 +239,7 @@ python3 umlimator.py record bike-share.html --story ride --out ride.png # bike
|
|
|
223
239
|
python3 umlimator.py import workspace.dsl --out model.json # a diagram you have already, as a model
|
|
224
240
|
docker compose config --format json > compose.json && python3 umlimator.py import compose.json --out model.json
|
|
225
241
|
kubectl get deploy,sts,svc,ing -A -o json > cluster.json && python3 umlimator.py import cluster.json --out model.json
|
|
226
|
-
python3 umlimator.py pdf page.html --out map.pdf # every view in one PDF, with its
|
|
242
|
+
python3 umlimator.py pdf page.html --out map.pdf # every view in one PDF: a cover, each view with its tables, the rules
|
|
227
243
|
python3 umlimator.py trace model.json trace.json --view ride-end --match match.json --out model.json # an OTLP trace kept as a numbered view
|
|
228
244
|
python3 umlimator.py mcp model.json # an MCP server over stdio, for an AI assistant (below)
|
|
229
245
|
```
|
|
@@ -237,18 +253,28 @@ views), C4-PlantUML (its element, relationship and boundary macros), Mermaid (`f
|
|
|
237
253
|
decides. Groups, boundaries, subgraphs, networks and namespaces become boxes; Structurizr's views stay views, a card
|
|
238
254
|
opening the view below it; `examples/clinic.json` is `examples/clinic.dsl`, imported. What each element becomes, and
|
|
239
255
|
what is left out: [docs/module-api.md § Import](docs/module-api.md#import-90). Whatever the file holds that `import`
|
|
240
|
-
doesn't read is listed on stderr with its line (in JSON, its path).
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
256
|
+
doesn't read is listed on stderr with its line (in JSON, its path). A Structurizr, C4-PlantUML or Mermaid C4 source is
|
|
257
|
+
drawn in the `c4` notation: its people as people (`person`, or `user` outside the system), its phone apps as phones. A
|
|
258
|
+
Structurizr workspace's `styles` give its elements their shapes (`shape Person`, `Cylinder`, `WebBrowser`, ...), in
|
|
259
|
+
Structurizr's own order; a shape the map has no counterpart for is listed in `notImported`.
|
|
260
|
+
|
|
261
|
+
`pdf` prints the map through Chrome's own print, as a document to hand in for a review
|
|
262
|
+
([bike-share's](https://zintaen.github.io/umlimator/examples/bike-share.pdf), as CI builds it). A cover has the title,
|
|
263
|
+
the date, the model's file, a summary line and the contents with each view's page. Each view starts a page of its own,
|
|
264
|
+
under its path ("Bike share › What runs"): its picture in vectors, as large as the page allows, the legend of what it
|
|
265
|
+
draws, and tables of its cards, lines and boxes, which go on over the next pages with their header row when they are
|
|
266
|
+
long. The rules follow as a table, when the model has any, each holding or broken in the views printed. Every page ends in
|
|
267
|
+
the title and "page N of M". It takes `--views` and `--theme`, as `stills` does, and `--paper a4|letter` (A4 is the
|
|
268
|
+
default; both across).
|
|
246
269
|
|
|
247
270
|
The page has breadcrumbs, a details panel per card (with the slowest path from it and what-if), ⌘K search, deep links
|
|
248
|
-
(`#view=<id>&node=<id>`, `#story=<id>&step=<n>` for a story's step,
|
|
271
|
+
(`#view=<id>&node=<id>`, `#story=<id>&step=<n>` for a story's step, `#lens=<id>` and `#shapes=c4`), stories, lenses, a
|
|
272
|
+
card shapes toggle next to ◐ (box, c4, uml; the page keeps the reader's choice), and export. For
|
|
249
273
|
scripts and tests, it keeps its map's handle on `window.umlimatorMap`: the `Handle` that `render()` returns (`setView()`,
|
|
250
274
|
`highlight()`, `tour()`, `play()` ...).
|
|
251
|
-
`stills` skips a view whose inputs have not changed since its last export (`--all` exports every view)
|
|
275
|
+
`stills` skips a view whose inputs have not changed since its last export (`--all` exports every view), and exits 1,
|
|
276
|
+
saying why, when a view can't be exported. In `stills`, `check` and `pdf`, `--views` takes view ids, and a trailing `*`
|
|
277
|
+
is a prefix; a name that matches no view is an error. A command that fails leaves its previous output as it was. Needs Python
|
|
252
278
|
3.9+ (standard library only), Google Chrome or Chromium for `stills`, `check`, `record` and `pdf`, and Node 18+ for
|
|
253
279
|
`diff`, `drift`, `trace` and `mcp`.
|
|
254
280
|
|
|
@@ -261,17 +287,19 @@ ffmpeg -f apng -i ride.png -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -pix_fmt yuv4
|
|
|
261
287
|
```
|
|
262
288
|
|
|
263
289
|
On pull requests, the diff Action posts what they change in your maps: one comment with the summary and a sketch, and
|
|
264
|
-
the pictures as the run's artifact.
|
|
290
|
+
the pictures as the run's artifact. The comment names the commit it describes, only the run for the pull request's
|
|
291
|
+
latest commit writes it, and it says so when the diff fails.
|
|
265
292
|
|
|
266
293
|
```yaml
|
|
267
294
|
on: pull_request
|
|
268
295
|
permissions: { contents: read, pull-requests: write }
|
|
296
|
+
concurrency: { group: "maps-${{ github.event.pull_request.number }}", cancel-in-progress: true } # one run per pull request
|
|
269
297
|
jobs:
|
|
270
298
|
maps:
|
|
271
299
|
runs-on: ubuntu-latest
|
|
272
300
|
steps:
|
|
273
301
|
- uses: actions/checkout@v4
|
|
274
|
-
- uses: zintaen/umlimator@v0.
|
|
302
|
+
- uses: zintaen/umlimator@v0.15.0
|
|
275
303
|
with:
|
|
276
304
|
models: "docs/*.json" # optional: where the models are (default: every .json that is a map model)
|
|
277
305
|
```
|
|
@@ -296,12 +324,18 @@ claude mcp add umlimator -- python3 /path/to/umlimator.py mcp /path/to/model.jso
|
|
|
296
324
|
| `show_view(view)` | shows a view on the page |
|
|
297
325
|
| `highlight(refs, view?)` | lights cards and lines (their ids, or the Refs `paths` and `reach` give) and dims the rest |
|
|
298
326
|
| `tour(cards, view?)` | a dot rides the lines through these cards |
|
|
299
|
-
| `play_story(id)`, `clear()` | plays one of the model's stories; takes the highlight,
|
|
327
|
+
| `play_story(id)`, `clear()` | plays one of the model's stories; takes the assistant's highlight, tour and story off |
|
|
328
|
+
| `page_state()` | what each open page shows (its map's `state`: view, selection, layers, story, camera), and whether the model renders |
|
|
300
329
|
|
|
301
330
|
- The read tools run the module's own functions in Node on the model file, read again at each call, so edits show up.
|
|
302
331
|
- The page tools drive the map's page, which the server serves at `http://127.0.0.1:7391` (`--port N`, or another port
|
|
303
|
-
when that one is taken). Open it in a browser.
|
|
304
|
-
|
|
332
|
+
when that one is taken). Open it in a browser.
|
|
333
|
+
- A tool answers "shown" once a page has shown it, or says why none did: the page's own error, or a tab in the
|
|
334
|
+
background that didn't finish in time. With no page open, it gives the URL.
|
|
335
|
+
- A page that opens later shows what the assistant built: the view, with the highlight on it, and a tour or story
|
|
336
|
+
still under way.
|
|
337
|
+
- The assistant's highlight is a layer of its own (`mcp`): off on another view, back on its own.
|
|
338
|
+
- A model that stops rendering keeps the last page that did, and the tools and the page say why.
|
|
305
339
|
- The server listens on 127.0.0.1 only and fetches nothing, so a logo not in the cache yet keeps its card's sigil.
|
|
306
340
|
|
|
307
341
|
## Map a repo with Claude Code
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Types for lib/umlimator-element.js, `umlimator/element` (#92). Its header says what each attribute and event does.
|
|
2
2
|
import type { Events, Handle } from './umlimator.js';
|
|
3
3
|
|
|
4
|
-
/** <umlimator-map src view minimap controls theme height>: the model from `src` (a URL) or a child
|
|
4
|
+
/** <umlimator-map src view minimap controls wheel theme height>: the model from `src` (a URL) or a child
|
|
5
5
|
* <script type="application/json">, drawn by render(); its other children show until the map is drawn. */
|
|
6
6
|
export class UmlimatorMap extends HTMLElement {
|
|
7
|
-
/** render()'s handle once the map is drawn (each `load` event), else null. */
|
|
7
|
+
/** render()'s handle once the map is drawn (each `load` event), else null. Its `state` is the map's MapState. */
|
|
8
8
|
readonly map: Handle | null;
|
|
9
|
+
/** True while a draw waits for its model (0.14.0); `map` is then still the map drawn last. `load` or `error` ends it. */
|
|
10
|
+
readonly loading: boolean;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
declare global {
|
package/lib/umlimator-element.js
CHANGED
|
@@ -6,45 +6,57 @@
|
|
|
6
6
|
|
|
7
7
|
The model comes from `src`, a URL (a data: URL too), or from a child <script type="application/json">, and render()
|
|
8
8
|
draws it in the element's shadow root. The element's other children (a still, say) show until the map is drawn, and stay
|
|
9
|
-
where scripts don't run. Changing src, view, minimap
|
|
10
|
-
removing the element destroys the map.
|
|
9
|
+
where scripts don't run. Changing src, view, minimap, controls (controls="false" hides them) or wheel draws it again,
|
|
10
|
+
and removing the element destroys the map. wheel="ctrl": the wheel zooms only with Ctrl or ⌘, and scrolls the page
|
|
11
|
+
otherwise, as a map in a document should. height: a number is pixels (default 480px; CSS may size it instead). theme:
|
|
11
12
|
"light" or "dark", that map's palette from lib/umlimator-theme.css, whatever the page's; without it the map follows the
|
|
12
13
|
page, a docs site's dark mode too (VitePress's html.dark, Docusaurus's html[data-theme="dark"]). The colours come from
|
|
13
14
|
that file or the host's own --um-* variables; the element fetches nothing but its model.
|
|
14
15
|
Events: the map's select, open and viewchange as umlimator:select, umlimator:open and umlimator:viewchange, the map's
|
|
15
|
-
event as `detail` (preventDefault() on umlimator:open keeps the view); load each time the map is drawn, error
|
|
16
|
-
can't be
|
|
17
|
-
|
|
16
|
+
event as `detail` (preventDefault() on umlimator:open keeps the view); load each time the map is drawn, error (an
|
|
17
|
+
ErrorEvent, whose message says why) when it can't be, a model with no view to draw too (its children show again).
|
|
18
|
+
shapes: "box", "c4" or "uml", the notation every view is drawn in (0.15.0; without it each view's own); changing it
|
|
19
|
+
draws the cards again in the map drawn, with nothing laid out again.
|
|
20
|
+
`el.map` is render()'s handle once drawn (toSVG(), highlight(), setView(), state ...), else null; `el.loading` is true
|
|
21
|
+
while a draw waits for its model, when el.map is still the map drawn last. An attribute set to the value it has draws
|
|
22
|
+
nothing again. */
|
|
18
23
|
import { render } from './umlimator.js';
|
|
19
24
|
|
|
20
25
|
const Base = globalThis.HTMLElement || class {}; // importable where there is no DOM (a docs site's server render)
|
|
21
26
|
|
|
22
27
|
export class UmlimatorMap extends Base {
|
|
23
|
-
static observedAttributes = ['src', 'view', 'minimap', 'controls', 'height'];
|
|
28
|
+
static observedAttributes = ['src', 'view', 'minimap', 'controls', 'wheel', 'height', 'shapes'];
|
|
24
29
|
map = null;
|
|
25
|
-
#drawn = 0; #due = false; #src = null; #model = null;
|
|
30
|
+
#drawn = 0; #due = false; #src = null; #model = null; #loading = false;
|
|
26
31
|
|
|
27
32
|
constructor() {
|
|
28
33
|
super();
|
|
29
34
|
this.attachShadow({ mode: 'open' }).innerHTML = '<style>:host { display: block; height: 480px; overflow: hidden; } ::slotted(img) { display: block; max-width: 100%; max-height: 100%; }</style>'
|
|
30
35
|
+ '<slot></slot><div hidden style="height: 100%"></div>';
|
|
31
36
|
}
|
|
37
|
+
get loading() { return this.#loading; } // a draw under way, waiting for its model (0.14.0)
|
|
32
38
|
connectedCallback() { this.#soon(); }
|
|
33
|
-
disconnectedCallback() { this.#drop(); }
|
|
39
|
+
disconnectedCallback() { this.#loading = false; this.#drop(); }
|
|
34
40
|
attributeChangedCallback(name, was, now) {
|
|
41
|
+
if (was === now) return; // set to the value it has: nothing to draw again (0.14.0)
|
|
35
42
|
if (name === 'height') this.style.height = now == null ? '' : /^\d+(\.\d+)?$/.test(now) ? now + 'px' : now;
|
|
43
|
+
else if (name === 'shapes' && this.map && !this.#loading) this.map.shapes = now || null; // the map drawn, in another notation: no new draw
|
|
36
44
|
else this.#soon();
|
|
37
45
|
}
|
|
38
46
|
#soon() { // attributes set together draw once
|
|
47
|
+
this.#loading = true;
|
|
39
48
|
if (this.#due) return;
|
|
40
49
|
this.#due = true;
|
|
41
|
-
queueMicrotask(() => { this.#due = false; if (this.isConnected) this.#draw(); });
|
|
50
|
+
queueMicrotask(() => { this.#due = false; if (this.isConnected) this.#draw(); else this.#loading = false; });
|
|
42
51
|
}
|
|
43
52
|
#drop() {
|
|
44
53
|
this.map?.destroy(); this.map = null;
|
|
45
54
|
const [, slot, box] = this.shadowRoot.children; slot.hidden = false; box.hidden = true;
|
|
46
55
|
}
|
|
47
|
-
#fail(err) {
|
|
56
|
+
#fail(err) { // the children show again, and the error event says why
|
|
57
|
+
this.#loading = false; this.#drop(); console.warn('<umlimator-map>:', err);
|
|
58
|
+
this.dispatchEvent(new ErrorEvent('error', { message: String((err && err.message) || err), error: err }));
|
|
59
|
+
}
|
|
48
60
|
async #draw() {
|
|
49
61
|
const n = ++this.#drawn, src = this.getAttribute('src'), get = a => this.getAttribute(a); // a later draw wins
|
|
50
62
|
let model = src && src === this.#src ? this.#model : null;
|
|
@@ -59,7 +71,8 @@ export class UmlimatorMap extends Base {
|
|
|
59
71
|
model = JSON.parse(json.textContent);
|
|
60
72
|
}
|
|
61
73
|
} catch (err) { if (n === this.#drawn) this.#fail(err); return; }
|
|
62
|
-
if (n !== this.#drawn
|
|
74
|
+
if (n !== this.#drawn) return; // a later draw has the element
|
|
75
|
+
if (!this.isConnected) { this.#loading = false; return; }
|
|
63
76
|
if (src) { this.#src = src; this.#model = model; }
|
|
64
77
|
this.#drop();
|
|
65
78
|
const [, slot, box] = this.shadowRoot.children; slot.hidden = true; box.hidden = false;
|
|
@@ -68,8 +81,10 @@ export class UmlimatorMap extends Base {
|
|
|
68
81
|
if (!this.dispatchEvent(new CustomEvent('umlimator:' + t, { detail: e, bubbles: true, composed: true, cancelable: t === 'open' }))) e.preventDefault();
|
|
69
82
|
}]));
|
|
70
83
|
try {
|
|
71
|
-
this.map = render(box, model, { view: get('view') || undefined, minimap: this.hasAttribute('minimap') && get('minimap') !== 'false', controls: get('controls') !== 'false', on });
|
|
84
|
+
this.map = render(box, model, { view: get('view') || undefined, minimap: this.hasAttribute('minimap') && get('minimap') !== 'false', controls: get('controls') !== 'false', wheel: get('wheel') === 'ctrl' ? 'ctrl' : 'zoom', shapes: get('shapes') || undefined, on });
|
|
72
85
|
} catch (err) { return this.#fail(err); }
|
|
86
|
+
if (this.map.view == null) return this.#fail(new Error(`${src || 'the inline model'}: the model has no view to draw`)); // drawn, with nothing to see: an error, not a load (0.14.0)
|
|
87
|
+
this.#loading = false;
|
|
73
88
|
this.dispatchEvent(new Event('load'));
|
|
74
89
|
}
|
|
75
90
|
}
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
view: api
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The block's `key: value` lines are the element's attributes (src, view, minimap, controls, theme, height); after
|
|
10
|
-
or instead of src, it may hold a model in JSON. An inline model goes into src as a data: URL, since VitePress
|
|
11
|
-
the page as a Vue template, which drops a <script> child. A block the element can't use fails the build,
|
|
12
|
-
seen. */
|
|
13
|
-
const KEYS = ['src', 'view', 'minimap', 'controls', 'theme', 'height'];
|
|
9
|
+
The block's `key: value` lines are the element's attributes (src, view, minimap, controls, wheel, theme, height); after
|
|
10
|
+
them, or instead of src, it may hold a model in JSON. An inline model goes into src as a data: URL, since VitePress
|
|
11
|
+
compiles the page as a Vue template, which drops a <script> child. A block the element can't use fails the build,
|
|
12
|
+
where it is seen. */
|
|
13
|
+
const KEYS = ['src', 'view', 'minimap', 'controls', 'wheel', 'theme', 'height'];
|
|
14
14
|
|
|
15
15
|
/** A block's text as the element's attributes. */
|
|
16
16
|
export function attrs(text) {
|
package/lib/umlimator-theme.css
CHANGED
|
@@ -39,13 +39,17 @@
|
|
|
39
39
|
--um-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
|
40
40
|
--um-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
41
41
|
}
|
|
42
|
+
/* How deep the map's tints are, in either theme: a card's kind colour, and a box's tone (containers[].tone). 1 is as
|
|
43
|
+
drawn here, 0 is none (plain panels), 2 twice as deep. The map stops a tint at 18% of its colour, where a card's
|
|
44
|
+
sub-line would fall below 4.5:1. Set it on the map or any ancestor; on :root only, so a map's own setting wins. */
|
|
45
|
+
:root { --um-tint: 1; }
|
|
42
46
|
@media (prefers-color-scheme: dark) {
|
|
43
47
|
:root:not([data-theme="light"]) {
|
|
44
48
|
--um-stage-bg: var(--um-bg-2); --um-edited: #d29922;
|
|
45
49
|
--um-bg: #0d1117; --um-bg-2: #010409; --um-panel: #0d1117; --um-panel-2: #151b23; --um-border: #262c36; --um-border-2: #3d444d;
|
|
46
50
|
--um-text: #f0f6fc; --um-muted: #9198a1; --um-dim: #656c76; --um-grid: rgba(240,246,252,.05); --um-mask: #0d1117; --um-arrow: #656c76;
|
|
47
51
|
--um-shadow: 0 10px 30px rgba(1,4,9,.55); --um-dot: #161b22; --um-gridline: transparent; --um-chrome: rgba(13,17,23,.92);
|
|
48
|
-
--um-accent: #4493f8; --um-accent-fg: #
|
|
52
|
+
--um-accent: #4493f8; --um-accent-fg: #0d1117; --um-lane: rgba(240,246,252,.025);
|
|
49
53
|
--um-cyan: #4493f8; --um-green: #3fb950; --um-violet: #ab7df8; --um-amber: #d29922; --um-rose: #f85149; --um-orange: #db6d28; --um-slate: #9198a1; --um-fuchsia: #db61a2;
|
|
50
54
|
--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;
|
|
51
55
|
--um-w-calls: #b7bdc8; --um-w-feeds: #4493f8; --um-w-triggers: #ab7df8; --um-w-state: #59626e; --um-w-guard: #f85149;
|
|
@@ -59,7 +63,7 @@
|
|
|
59
63
|
--um-bg: #0d1117; --um-bg-2: #010409; --um-panel: #0d1117; --um-panel-2: #151b23; --um-border: #262c36; --um-border-2: #3d444d;
|
|
60
64
|
--um-text: #f0f6fc; --um-muted: #9198a1; --um-dim: #656c76; --um-grid: rgba(240,246,252,.05); --um-mask: #0d1117; --um-arrow: #656c76;
|
|
61
65
|
--um-shadow: 0 10px 30px rgba(1,4,9,.55); --um-dot: #161b22; --um-gridline: transparent; --um-chrome: rgba(13,17,23,.92);
|
|
62
|
-
--um-accent: #4493f8; --um-accent-fg: #
|
|
66
|
+
--um-accent: #4493f8; --um-accent-fg: #0d1117; --um-lane: rgba(240,246,252,.025);
|
|
63
67
|
--um-cyan: #4493f8; --um-green: #3fb950; --um-violet: #ab7df8; --um-amber: #d29922; --um-rose: #f85149; --um-orange: #db6d28; --um-slate: #9198a1; --um-fuchsia: #db61a2;
|
|
64
68
|
--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;
|
|
65
69
|
--um-w-calls: #b7bdc8; --um-w-feeds: #4493f8; --um-w-triggers: #ab7df8; --um-w-state: #59626e; --um-w-guard: #f85149;
|
package/lib/umlimator.d.ts
CHANGED
|
@@ -15,8 +15,16 @@ export interface MapModel {
|
|
|
15
15
|
stories?: Story[];
|
|
16
16
|
/** Saved ways to look at the map (#94): handle.lens(id), the standalone page's Lenses menu and #lens=<id>. */
|
|
17
17
|
lenses?: Lens[];
|
|
18
|
+
/** The notation every view's cards are drawn in (0.15.0; a view's own `shapes` wins). Default 'box'. */
|
|
19
|
+
shapes?: Notation;
|
|
18
20
|
[key: string]: unknown;
|
|
19
21
|
}
|
|
22
|
+
/** How cards are drawn (0.15.0): box (every card a rounded box, the default), c4 (people, browser apps, phones, databases,
|
|
23
|
+
* queues and files as the C4 model's tools draw them) or uml (UML 2.5's actor, node, artifact, package and data store,
|
|
24
|
+
* and the component icon on a box). Each shape keeps to its card's box: the layout is the same in every notation. */
|
|
25
|
+
export type Notation = 'box' | 'c4' | 'uml';
|
|
26
|
+
/** A card's shape (0.15.0). */
|
|
27
|
+
export type Shape = 'box' | 'person' | 'browser' | 'mobile' | 'cylinder' | 'pipe' | 'document' | 'hexagon' | 'robot' | 'terminal' | 'window' | 'folder' | 'node';
|
|
20
28
|
/** A badge: its text, and optionally a palette name for its colour (green, amber, rose, accent, violet ... : --um-<tone>). */
|
|
21
29
|
export type Badge = string | { text: string; tone?: string };
|
|
22
30
|
export interface MapNode {
|
|
@@ -29,6 +37,8 @@ export interface MapNode {
|
|
|
29
37
|
opensCount?: number;
|
|
30
38
|
/** Who owns the card (a team, say): selectors pick by it, and a lens with color 'owner' rings the card in its owner's tone. */
|
|
31
39
|
owner?: string;
|
|
40
|
+
/** The card's own shape, in every notation (0.15.0); without it, the notation draws the card by its kind. */
|
|
41
|
+
shape?: Shape;
|
|
32
42
|
[key: string]: unknown;
|
|
33
43
|
}
|
|
34
44
|
/** What a line means, drawn as its colour: calls (the default), feeds (hands work on), triggers (suggests, offers, fires),
|
|
@@ -45,6 +55,13 @@ export interface MapEdge {
|
|
|
45
55
|
* hit line growing with it. The host picks the bucket (say under 10, under 100, 100 or more relations); anything else is 1.
|
|
46
56
|
* A line that stands for several edges takes the largest. */
|
|
47
57
|
weight?: 1 | 2 | 3;
|
|
58
|
+
/** Its place in the order things happen, drawn in a circle on the line before its label, as a numbered view draws its
|
|
59
|
+
* steps, in any view: 2, 2.1 for a step within step 2, or text of up to 3 characters such as "2a" (a pill for 2 or 3
|
|
60
|
+
* characters). The line's name and title say it as written ("Step 2a: …"); in order, 2 < 2a < 2b < 3. A numbered view
|
|
61
|
+
* (`render: 'sequence'`) numbers its lines in run order instead. A line that stands for several edges shows the first
|
|
62
|
+
* one's step (a view's `keepPairs` draws each edge as its own line). A step on a model edge shows in every view that
|
|
63
|
+
* draws the edge; a view's own edge numbers it in that view only. */
|
|
64
|
+
step?: number | string;
|
|
48
65
|
/** A second, smaller line under the label (a technology, say), placed with it and counted in the room left for labels.
|
|
49
66
|
* Shown only with a label. A line that stands for several edges shows each of their sublabels, joined by " / ". */
|
|
50
67
|
sublabel?: string;
|
|
@@ -71,7 +88,11 @@ export interface MapView {
|
|
|
71
88
|
/** Guess a line's kind from the words of its label ("reads" is state, "triggers" is triggers ...) when its edges name
|
|
72
89
|
* none. Off in render() and layout(), which draw no legend; the standalone page (mountPage) always guesses. */
|
|
73
90
|
kindsFromLabels?: boolean;
|
|
74
|
-
|
|
91
|
+
/** Boxes: badges are tabs on the box's border; tone, a palette name as a badge's (--um-<tone>), tints the region behind the
|
|
92
|
+
* box (as deep as --um-tint makes the cards), with its border dashed and its title in the tone. */
|
|
93
|
+
containers?: { id: string; label?: string; nodeIds?: string[]; parent?: string; badges?: Badge[]; tone?: string }[];
|
|
94
|
+
/** The notation this view's cards are drawn in (0.15.0): it wins over the model's. */
|
|
95
|
+
shapes?: Notation;
|
|
75
96
|
[key: string]: unknown;
|
|
76
97
|
}
|
|
77
98
|
|
|
@@ -92,14 +113,48 @@ export interface Events {
|
|
|
92
113
|
change: Change; // only with `editable`
|
|
93
114
|
/** E or Alt + click on a stacked card, or the ⤡ tab of the box it unfolded into (#71): before the card unfolds in place
|
|
94
115
|
* (expanded: true) or folds back (false). view: the card's inner view, which may not be in the model yet; the card
|
|
95
|
-
* unfolds once it is
|
|
116
|
+
* unfolds once it is: the update() that brings it unfolds the card (here or later), or expand(). preventDefault(): no change. */
|
|
96
117
|
expand: { target: Extract<Ref, { type: 'node' }>; view: string; expanded: boolean; preventDefault(): void };
|
|
97
|
-
|
|
118
|
+
/** A story changed: it shows a step (0-based), it was paused or resumed (playing), or it ended (step null, on destroy()
|
|
119
|
+
* too). playing (0.14.0): it goes on by itself. */
|
|
120
|
+
story: { story: string; step: number | null; steps: number; playing: boolean };
|
|
121
|
+
/** The map's state changed (0.14.0): at most once a frame, with the keys that changed since the last state event. The
|
|
122
|
+
* camera is the exception: a pan, zoom, glide or drag is one event, once it comes to rest, and until then the event's
|
|
123
|
+
* camera is where it last rested (handle.state reads it live). The last event, on destroy(), has destroyed: true. */
|
|
124
|
+
state: { state: MapState; changed: (keyof MapState)[] };
|
|
125
|
+
/** The lens on the map changed: lens(), or an update() whose model lacks the lens shown (lens: null). */
|
|
126
|
+
lens: { lens: string | null };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The map's state, for hosts and agents (0.14.0): handle.state, the state event, the standalone page's
|
|
130
|
+
* window.umlimatorMap.state and <umlimator-map>'s el.map.state. Fresh on every read, frozen, and plain JSON. */
|
|
131
|
+
export interface MapState {
|
|
132
|
+
view: string | null; // shown now (null: the model has no views)
|
|
133
|
+
navigating: string | null; // the view a move under way is going to
|
|
134
|
+
selection: Ref[]; // as `selected`
|
|
135
|
+
expanded: string[]; // as `expanded`
|
|
136
|
+
lens: string | null;
|
|
137
|
+
overlays: string[]; // the layers on the map, in stack order ('lens', 'highlight', 'replay', 'story', 'tour', a host's)
|
|
138
|
+
story: { id: string; step: number; steps: number; playing: boolean } | null; // playing: the player's own (it goes on by itself)
|
|
139
|
+
tour: boolean; // a tour's dot is on the map, riding or held (by paused, or by its story's pause); a path lit at rest: false
|
|
140
|
+
replay: { playing: boolean } | null; // a replay under way: playing, or paused by its own pause()
|
|
141
|
+
/** The standalone page's ❚❚ is on (0.15.0): it holds every clock (a tour's or a replay's dot, a story's step, the flow)
|
|
142
|
+
* and leaves each one's own state as it is, so story.playing or replay.playing true under it means "would play, held". */
|
|
143
|
+
paused: boolean;
|
|
144
|
+
camera: { x: number; y: number; zoom: number }; // the centre in map units, and the zoom (1: actual size); a state event's: at rest
|
|
145
|
+
flow: boolean;
|
|
146
|
+
shapes: Notation; // the notation the view shown is drawn in (0.15.0)
|
|
147
|
+
motion: 'full' | 'reduced' | 'instant';
|
|
148
|
+
gesture: 'idle' | 'dragging' | 'connecting' | 'naming'; // an edit in progress (editable maps)
|
|
149
|
+
destroyed: boolean;
|
|
98
150
|
}
|
|
99
151
|
|
|
100
152
|
/** Where the user dropped or placed something: a point in the view and, on a column view, its column and row. */
|
|
101
153
|
export interface Hint { x: number; y: number; col?: number; row?: number }
|
|
102
|
-
/** An edit the user asked for. Nothing changes until the host applies it and calls update().
|
|
154
|
+
/** An edit the user asked for. Nothing changes until the host applies it and calls update(). The places it hints (a card
|
|
155
|
+
* moved, reordered or added) are used only if that update's model shows the edit: the card in its new box, a new card
|
|
156
|
+
* with its name, or, for a place alone, the card's `layout` entry written or the model unchanged. Any other update drops
|
|
157
|
+
* them. An update back to the model before an applied move, or after it (undo, redo), puts the cards back as they were. */
|
|
103
158
|
export type Change = { id: string; view: string; hint?: Hint } & (
|
|
104
159
|
| { op: 'add'; kind: 'node'; label: string; container?: string }
|
|
105
160
|
| { op: 'add'; kind: 'edge'; from: string; to: string; label?: string }
|
|
@@ -112,8 +167,13 @@ export interface RenderOptions {
|
|
|
112
167
|
view?: string; // default: model.root, else the view with no parent
|
|
113
168
|
select?: Ref | null;
|
|
114
169
|
controls?: boolean; // up / zoom / fit / 1:1 / flow buttons (default true)
|
|
170
|
+
/** 'zoom' (the default): the wheel zooms the map, and a trackpad pans it. 'ctrl': only Ctrl or ⌘ with the wheel (and a
|
|
171
|
+
* trackpad's pinch) zooms, and the wheel otherwise scrolls the page, as a map in a document should. */
|
|
172
|
+
wheel?: 'zoom' | 'ctrl';
|
|
115
173
|
minimap?: boolean; // default false
|
|
116
174
|
flow?: boolean; // animated flow along lines (default: on unless prefers-reduced-motion)
|
|
175
|
+
/** The notation every view is drawn in (0.15.0), over each view's and the model's own. */
|
|
176
|
+
shapes?: Notation;
|
|
117
177
|
/** Text widths: default the host's font (--um-sans, measured on a canvas); false: a font-independent estimate. */
|
|
118
178
|
measure?: false | ((text: string, px: number) => number);
|
|
119
179
|
/** Edit gestures that emit `change` events: all of them, or only those set true. Off by default. */
|
|
@@ -148,6 +208,9 @@ export interface Handle {
|
|
|
148
208
|
* where it was; none of it with the option `ease: false` or under prefers-reduced-motion, and an export at any point
|
|
149
209
|
* draws where things end up. */
|
|
150
210
|
update(model: MapModel): void;
|
|
211
|
+
/** Shows a view, zooming in or out as a drill-down does. A request during a move takes the move to its own view (the
|
|
212
|
+
* latest wins). Resolves true once the view shows; false for a view the model lacks (with a console warning), when a
|
|
213
|
+
* later request supersedes it, or on destroy(). */
|
|
151
214
|
setView(id: string): Promise<void> | void;
|
|
152
215
|
select(ref: Ref | null): void;
|
|
153
216
|
fit(): void;
|
|
@@ -175,13 +238,32 @@ export interface Handle {
|
|
|
175
238
|
readonly view: string | null;
|
|
176
239
|
readonly selection: Ref | null; // the latest selected
|
|
177
240
|
readonly selected: Ref[]; // every selected thing (Shift / Cmd / Ctrl + click adds cards), the latest last
|
|
241
|
+
/** The map's state as one snapshot (0.14.0): fresh, frozen and JSON-serializable on every read. */
|
|
242
|
+
readonly state: MapState;
|
|
243
|
+
/** The view a move is going to, while one is under way (setView(), a drill-down, up); else null. */
|
|
244
|
+
readonly navigating: string | null;
|
|
245
|
+
/** True once destroy() has run: calls then do nothing, and every promise has settled. */
|
|
246
|
+
readonly destroyed: boolean;
|
|
247
|
+
/** The animated flow along lines (the ≋ button and F), to read or set. */
|
|
248
|
+
flow: boolean;
|
|
249
|
+
/** The notation the view shown is drawn in (0.15.0). Set it to draw every view in one (null: each view's own): the
|
|
250
|
+
* cards are drawn again from the geometry they have, with the camera, selection, lens and marks; nothing is laid out. */
|
|
251
|
+
get shapes(): Notation;
|
|
252
|
+
set shapes(notation: Notation | null);
|
|
253
|
+
/** How this map moves: 'instant' (drawn without animation, as the page's #check, #still and #shot are), 'reduced' (the
|
|
254
|
+
* reader's setting), else 'full'. */
|
|
255
|
+
readonly motion: 'full' | 'reduced' | 'instant';
|
|
256
|
+
/** The player of the story shown, whoever started it (play(), the standalone page's Stories menu or a link), else null. */
|
|
257
|
+
readonly story: StoryPlayer | null;
|
|
178
258
|
/** Sets (or, with null, removes) a named layer of marks, drawn over the picture without touching the model. Layers stack
|
|
179
259
|
* in the order first set: each property comes from the latest layer that sets it, and a card or line is as lit as its
|
|
180
260
|
* lowest level. They survive update() and setView(); ids the view doesn't show are ignored. toSVG() and toPNG() draw them. */
|
|
181
261
|
overlay(name: string, layer: Overlay | null): void;
|
|
182
262
|
/** The 'highlight' layer: these cards and lines lit, the rest dimmed (in place of the selection's dimming), or null to clear.
|
|
183
|
-
* A Ref's level (0 to 1), else its distance (as reach() gives it), fades it. A chip's or badge's Ref lights its card.
|
|
184
|
-
|
|
263
|
+
* A Ref's level (0 to 1), else its distance (as reach() gives it), fades it. A chip's or badge's Ref lights its card.
|
|
264
|
+
* layer (0.14.0): draw them as a layer of that name instead, so two owners never clear each other's (the MCP page's
|
|
265
|
+
* assistant uses 'mcp'); null clears that layer only. */
|
|
266
|
+
highlight(refs: Lit[] | null, options?: { layer?: string }): void;
|
|
185
267
|
/** Puts one of the model's lenses on the map (#94), or none with null: the 'lens' layer lights what it picks and dims the
|
|
186
268
|
* rest, on every view shown, and the view is not laid out again. With color 'owner', each lit card is ringed in its
|
|
187
269
|
* owner's tone, and a legend over the controls names them. It stays across update() and setView(); a model without the
|
|
@@ -189,8 +271,9 @@ export interface Handle {
|
|
|
189
271
|
lens(id: string | null): boolean;
|
|
190
272
|
/** The tour dot rides the line between each pair of cards in turn (either way), through each card to its next line; a card
|
|
191
273
|
* it reaches flashes, and a pair with no line between them is skipped. Resolves true at the end, false when cut short (a
|
|
192
|
-
* new tour or replay, tour(null), a new picture of the view, destroy()). With reduced motion
|
|
193
|
-
*
|
|
274
|
+
* new tour or replay, tour(null), Esc on the map, a new picture of the view, destroy()). With reduced motion no dot rides:
|
|
275
|
+
* it resolves at once, and the path is lit at rest (its cards ringed and numbered in turn, its lines glowing: the 'tour'
|
|
276
|
+
* layer) and said, until the next tour or replay, tour(null), Esc or a new picture. speed: map units a second (default 240). */
|
|
194
277
|
tour(path: string[] | null, options?: { speed?: number }): Promise<boolean>;
|
|
195
278
|
/** Plays a trace: each call from one card to another (a span and its parent's) runs along their line at the trace's relative
|
|
196
279
|
* time; the line glows while the call lasts and keeps its duration as a label (the 'replay' layer, until stop() or the next
|
|
@@ -200,8 +283,11 @@ export interface Handle {
|
|
|
200
283
|
/** Tells one of the model's stories (#69): each step in turn sets the view, camera, selection, highlight and caption
|
|
201
284
|
* (drawn in a bar on the map and said to screen readers), then plays its tour. auto (default true): the next step
|
|
202
285
|
* follows once a step's duration is up and its tour has ended. step: where to start (0-based). The bar and the keys
|
|
203
|
-
* (← → PageUp PageDown, Esc) step through and stop it.
|
|
204
|
-
|
|
286
|
+
* (← → PageUp PageDown, Esc) step through and stop it. The reader taking the map (a press, a key, a view or a selection
|
|
287
|
+
* the story didn't make) pauses it, and a pause holds the step's tour too, until resume(). A step folds back the cards
|
|
288
|
+
* unfolded on its view first, and its highlight is its own 'story' layer, which dims alone while the step shows.
|
|
289
|
+
* null when the model has no story `id` with steps, and the story shown, if any, plays on; play(null) stops it. */
|
|
290
|
+
play(id: string | null, options?: { step?: number; auto?: boolean }): StoryPlayer | null;
|
|
205
291
|
destroy(): void;
|
|
206
292
|
}
|
|
207
293
|
|
|
@@ -215,9 +301,11 @@ export interface StoryStep {
|
|
|
215
301
|
camera?: 'fit' | { x: number; y: number; zoom?: number } | { card: string; zoom?: number }; duration?: number;
|
|
216
302
|
}
|
|
217
303
|
/** A story being told. step: the one shown (0-based). next() past the last step ends it (done: true); stop() closes it
|
|
218
|
-
* (done: false).
|
|
304
|
+
* (done: false). state (0.14.0): entering a step (its view, camera, selection and highlight on their way), playing or paused
|
|
305
|
+
* on it, then ended or stopped. */
|
|
219
306
|
export interface StoryPlayer {
|
|
220
307
|
readonly story: string; readonly step: number; readonly steps: number; readonly playing: boolean; readonly done: Promise<boolean>;
|
|
308
|
+
readonly state: 'entering' | 'playing' | 'paused' | 'ended' | 'stopped';
|
|
221
309
|
next(): void; prev(): void; pause(): void; resume(): void; stop(): void;
|
|
222
310
|
}
|
|
223
311
|
|