reladraw 0.3.0 → 0.5.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/NOTICE +37 -0
- package/README.md +1 -1
- package/SYNTAX.md +98 -9
- package/dist/ast.d.ts +33 -11
- package/dist/ast.js +25 -3
- package/dist/cli.js +18 -2
- package/dist/constants.d.ts +4 -4
- package/dist/constants.js +15 -8
- package/dist/grammar.d.ts +1 -1
- package/dist/grammar.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/model.d.ts +4 -3
- package/dist/parser.js +158 -32
- package/dist/render.d.ts +1 -22
- package/dist/render.js +20 -34
- package/dist/resolve.js +105 -39
- package/dist/themes.d.ts +46 -0
- package/dist/themes.js +190 -0
- package/package.json +1 -1
package/NOTICE
CHANGED
|
@@ -12,3 +12,40 @@ any other trade names, trademarks, service marks, or product names of the
|
|
|
12
12
|
project, except as required for reasonable and customary use in describing the
|
|
13
13
|
origin of the work (see section 6 of the License). Forks and derived works are
|
|
14
14
|
welcome and must be distributed under a different name.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
Color palettes
|
|
19
|
+
|
|
20
|
+
Several of reladraw's themes (src/themes.ts) are adapted from the color
|
|
21
|
+
palettes below. Each is distributed under the MIT License, whose text follows
|
|
22
|
+
the list of copyright notices and applies to each of them.
|
|
23
|
+
|
|
24
|
+
Solarized Copyright (c) 2011 Ethan Schoonover
|
|
25
|
+
https://github.com/altercation/solarized
|
|
26
|
+
Gruvbox Copyright (c) 2018 Pavel Pertsev
|
|
27
|
+
https://github.com/morhetz/gruvbox
|
|
28
|
+
Catppuccin Copyright (c) 2021 Catppuccin
|
|
29
|
+
https://github.com/catppuccin/catppuccin
|
|
30
|
+
Nord Copyright (c) 2016-present Sven Greb
|
|
31
|
+
https://github.com/nordtheme/nord
|
|
32
|
+
Dracula Copyright (c) 2023 Dracula Theme
|
|
33
|
+
https://github.com/dracula/dracula-theme
|
|
34
|
+
|
|
35
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
36
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
37
|
+
in the Software without restriction, including without limitation the rights
|
|
38
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
39
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
40
|
+
furnished to do so, subject to the following conditions:
|
|
41
|
+
|
|
42
|
+
The above copyright notice and this permission notice shall be included in
|
|
43
|
+
all copies or substantial portions of the Software.
|
|
44
|
+
|
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
46
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
47
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
48
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
49
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
50
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
51
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ It is [plain Markdown](.claude/skills/reladraw/SKILL.md) with the syntax referen
|
|
|
68
68
|
|
|
69
69
|
## Status
|
|
70
70
|
|
|
71
|
-
Version 0.
|
|
71
|
+
Version 0.5.0. Early, but it runs: a parser, resolver and SVG renderer in TypeScript with no runtime dependencies, and a command-line tool that takes a text file and writes a standalone SVG. The comparison at the top of this page is that pipeline run on [`examples/arch.reladraw`](examples/arch.reladraw). What is still visibly off there is typography, not placement.
|
|
72
72
|
|
|
73
73
|
```
|
|
74
74
|
npm install -g reladraw
|
package/SYNTAX.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Syntax reference — 0.
|
|
1
|
+
# Syntax reference — 0.5.0
|
|
2
2
|
|
|
3
3
|
What the language accepts. The parser, resolver and SVG renderer implement all of it; the sections at the end record what is defective, unchecked or undecided.
|
|
4
4
|
|
|
@@ -129,7 +129,7 @@ Bundling a text's properties into a style is how they come to mean something: `s
|
|
|
129
129
|
|
|
130
130
|
`color` takes a color written as the viewer will receive it — `#8b8b8b`, or any CSS color — or the one reserved word `muted`, which means the theme's secondary text color and so survives a change of theme.
|
|
131
131
|
|
|
132
|
-
`size: small | normal | large` sets how big the text is set
|
|
132
|
+
`size: small | normal | large` sets how big the text is set, and so does a plain number of pixels, `size: 22`. The names are the default and the preferred form: they follow the document's size if it is ever set differently, where `22` stays 22. A number says nothing about *why* one text is bigger than another, so bundle it into a style that does. `14px` is refused, since the number is already in pixels, and so is zero or less. Every kind of text has a default and `size` overrides it, exactly as `fill:` overrides the theme's color; only `shape: none` defaults to anything but `normal`, and it defaults to `small`.
|
|
133
133
|
|
|
134
134
|
`wrap: <n>` folds the text at word boundaries every `n` characters, on top of whatever ` / ` already breaks. It is how you make a block of text narrow and tall so it can sit snugly beside something, rather than wide and short so it cannot. The number is a count of characters and not a distance: it says how much fits on a line and nothing about where anything sits.
|
|
135
135
|
|
|
@@ -602,10 +602,12 @@ Nothing bounds bare text the way a border bounds a node, so a sentence-length on
|
|
|
602
602
|
## Decks
|
|
603
603
|
|
|
604
604
|
```
|
|
605
|
-
|
|
605
|
+
node drive "Drive 1" deck: "Drive 2" "Drive 3"
|
|
606
606
|
```
|
|
607
607
|
|
|
608
|
-
|
|
608
|
+
`deck:` draws offset copies behind a box, one per text, back to front, to say "there are several of these and they are the same." Only the front copy shows its contents. It goes on a box only; a picture or a `shape: none` node has no outline to repeat, and refuses it. A style may carry it.
|
|
609
|
+
|
|
610
|
+
**Changed after 0.3.0.** This was a statement of its own, `deck drive "Drive 2" "Drive 3"`, which created nothing and only said more about a node declared elsewhere. The statement is now an error that quotes the attribute to write instead.
|
|
609
611
|
|
|
610
612
|
## Attributes
|
|
611
613
|
|
|
@@ -613,11 +615,12 @@ Every attribute, and what takes one. The kinds here are what a node's **body** i
|
|
|
613
615
|
|
|
614
616
|
| attribute | `shape:` | `icon:` | `shape: none` | edge | says |
|
|
615
617
|
|---|---|---|---|---|---|
|
|
616
|
-
| `style` | ✓ | ✓ | ✓ | ✓ | the named
|
|
618
|
+
| `style` | ✓ | ✓ | ✓ | ✓ | the named bundles to take appearance from, one or a list |
|
|
617
619
|
| `gap` | ✓ | ✓ | ✓ | | the default distance to whatever it is placed against |
|
|
618
620
|
| `overlap` | ✓ | ✓ | ✓ | | `allow`, to opt out of non-overlap |
|
|
619
621
|
| `contents` | ✓ | | | | how the children are sized and where the block of them sits, in brackets |
|
|
620
622
|
| `badge` | ✓ | | | | the picture beside the text |
|
|
623
|
+
| `deck` | ✓ | | | | offset copies behind the box, one quoted text each |
|
|
621
624
|
| `shape` | ✓ | | ✓ | | the outline the node is drawn with, `none` included |
|
|
622
625
|
| `icon` | | ✓ | | | the picture the node is drawn as |
|
|
623
626
|
| `from` `to` | | | | ✓ | which side the line leaves and arrives on |
|
|
@@ -627,7 +630,7 @@ Every attribute, and what takes one. The kinds here are what a node's **body** i
|
|
|
627
630
|
| `line` | | | | ✓ | color |
|
|
628
631
|
| `url` | ✓ | ✓ | ✓ | ✓ | a destination to open when the thing is clicked |
|
|
629
632
|
|
|
630
|
-
The `diagram` statement has a vocabulary of its own — `
|
|
633
|
+
The `diagram` statement has a vocabulary of its own — `theme`, `background` and `text` — which is checked the same way. Writing `background:` on a node is an error that points at `fill:`, and `theme:` on one points at `diagram theme:`. A `default` takes a narrower list for each kind; see "Defaults".
|
|
631
634
|
|
|
632
635
|
**A word this table does not give the kind is an error.** The two ways of being wrong get different answers, because they have different remedies. A word that is an attribute nowhere is a misspelling, and the error lists what the kind does take. A word that is an attribute *somewhere else* is usually a real statement written on the wrong half of the diagram, so the error says where it belongs:
|
|
633
636
|
|
|
@@ -637,6 +640,8 @@ The `diagram` statement has a vocabulary of its own — `background`, and so far
|
|
|
637
640
|
|
|
638
641
|
Some of the gaps in the table are worth saying out loud, because none of them looks like a mistake while you are writing it. A picture and a bodiless node take no `fill:` or `border:` — there is no outline for either to reach. Neither takes `contents:` either, which says how a node's children sit, and neither may have any. `shape:` and `icon:` each appear only on the kind they make, and writing both is an error naming both. An edge takes no `gap:` or `overlap:` — those say where a node sits, and an edge is not placed, it joins two things that are.
|
|
639
642
|
|
|
643
|
+
**A key is written once per statement.** `fill: red fill: blue` is an error that quotes both values and asks you to keep one: two words on one line are equally explicit, so nothing says which was meant, and keeping either would drop the other in silence. It is almost always an edit that forgot to delete the old value. The same holds inside brackets — `(size: small, size: large)`, a placement's `(gap: wide, gap: tight)` — for `style:` written twice, and for a `style` statement that repeats one of its own keys. Two `text: (…)` brackets in one style are fine as long as they say different things. A node overriding its style is not a repeat; see "Styles".
|
|
644
|
+
|
|
640
645
|
**Changed 2026-09-09.** Until then a node or edge attribute the tool did not recognize was parsed, stored and never read: `wibble: red` on a node drew nothing and said nothing. This was the last place in the language where a key could silently do nothing, and the rule everywhere else — an unknown `diagram` key, an unknown placement modifier, a color naming a part the kind has not got — has always been that a key which silently does nothing looks like the tool being broken rather than like a typo. A file that rendered with a stray word in it will now stop with an error naming it.
|
|
641
646
|
|
|
642
647
|
## Destinations
|
|
@@ -673,6 +678,23 @@ node server.mirror "shared folder / mirror" style: backup
|
|
|
673
678
|
|
|
674
679
|
The appearance attributes are `fill`, `border`, `line`, `text`, `badge`, `icon` and `shape`. The first three each take a color written as the viewer will receive it — `#142814`, or any CSS color, or `none`; `text` takes the bracket described under "The text and its brackets".
|
|
675
680
|
|
|
681
|
+
**What a statement says itself beats what its style says**, wherever `style:` sits on the line. Here the server's border is red, not orange:
|
|
682
|
+
|
|
683
|
+
```
|
|
684
|
+
style backup border: #d2904e
|
|
685
|
+
node server "Server" border: red style: backup
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Whoever wrote `border: red` on the node meant it for that node, so it is an override rather than a contradiction, and nothing is refused. The rest of the style still applies.
|
|
689
|
+
|
|
690
|
+
**A thing may take several styles**, listed the way a placement lists its targets — commas and `and` both separate:
|
|
691
|
+
|
|
692
|
+
```
|
|
693
|
+
node pump "Pump 3" style: equipment, critical and alarm
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
They apply in the order written, and a later style wins where two set the same key; the statement's own words still win over all of them. Each style in the list is checked on its own, so one that gives the thing nothing is refused even when its neighbors do not. A style named twice in one list is refused, and so is `style:` written twice on one statement — the list is the way to say several.
|
|
697
|
+
|
|
676
698
|
### A color names the part it colors
|
|
677
699
|
|
|
678
700
|
A color attribute says which part of a thing it colors, and a part exists only on the kinds that have one:
|
|
@@ -740,12 +762,66 @@ diagram <attributes>
|
|
|
740
762
|
Settings that belong to the drawing as a whole rather than to anything in it. There is no name, because a file holds one diagram, and a second `diagram` statement is an error rather than a second opinion.
|
|
741
763
|
|
|
742
764
|
```
|
|
743
|
-
diagram background: #
|
|
765
|
+
diagram theme: nord background: #1e2229 text: (color: #e0e0e0)
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Three attributes:
|
|
769
|
+
|
|
770
|
+
- `theme` names the theme that supplies every color the file does not state. See "Themes" below.
|
|
771
|
+
- `background` takes a color the same way `fill` does, and it colors the page behind everything, including the strip an edge text knocks out of whatever it crosses. Say nothing and the theme's own background stands.
|
|
772
|
+
- `text: (color: …)` sets the color of every text, on nodes and edges alike. It takes a color and nothing else; a text size is about one kind of text and belongs on a `default`.
|
|
773
|
+
|
|
774
|
+
An unknown key is refused by name — `diagram has no "backround" — it takes theme, background, text` — rather than quietly ignored, the same as every other attribute. See "Attributes".
|
|
775
|
+
|
|
776
|
+
### Themes
|
|
777
|
+
|
|
778
|
+
```
|
|
779
|
+
diagram theme: solarized-dark
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
| theme | |
|
|
783
|
+
|---|---|
|
|
784
|
+
| `dark` | the default: the palette of the benchmark drawing |
|
|
785
|
+
| `light` | the same roles on a white page |
|
|
786
|
+
| `solarized-dark`, `solarized-light` | [Solarized](https://ethanschoonover.com/solarized/) |
|
|
787
|
+
| `gruvbox-dark`, `gruvbox-light` | [Gruvbox](https://github.com/morhetz/gruvbox) |
|
|
788
|
+
| `catppuccin-mocha`, `catppuccin-latte` | [Catppuccin](https://catppuccin.com) |
|
|
789
|
+
| `nord` | [Nord](https://www.nordtheme.com) |
|
|
790
|
+
| `dracula` | [Dracula](https://draculatheme.com) |
|
|
791
|
+
| `high-contrast-dark`, `high-contrast-light` | no fills to lean on, every line at full strength |
|
|
792
|
+
| `print` | a white page, black lines and no fills, for paper |
|
|
793
|
+
|
|
794
|
+
The borrowed palettes were made for code, where a color marks a keyword or a string. A diagram needs a page, a box, a border, text and a line, so each theme is its scheme read as a diagram rather than a copy of it. Their licenses are in `NOTICE`.
|
|
795
|
+
|
|
796
|
+
A theme only ever supplies a color, so changing it never moves anything. A color you write yourself — `fill: #14532d`, or a style's — stays that color under every theme, which means a hand-picked color can clash with a theme it was not chosen against. `muted` is the one word that follows the theme.
|
|
797
|
+
|
|
798
|
+
An unknown name is an error that lists the themes.
|
|
799
|
+
|
|
800
|
+
The command line can render a file in another theme without editing it — `reladraw arch.reladraw --theme light` — and the flag wins over the file's `theme:`. That is how one source becomes a light and a dark image for a README that shows whichever matches the reader's settings. The file's own `background:` and `text:` still win over the flag's theme, as a hand-written color wins over any theme.
|
|
801
|
+
|
|
802
|
+
### Defaults
|
|
803
|
+
|
|
804
|
+
```
|
|
805
|
+
default node border: #4a8a5c
|
|
806
|
+
default leaf fill: #2e5d3a
|
|
807
|
+
default container fill: #1a2620 border: #2a3a30
|
|
808
|
+
default edge line: #7a8a99 text: (color: #9aa5b1, size: small)
|
|
744
809
|
```
|
|
745
810
|
|
|
746
|
-
|
|
811
|
+
A default is a style that every thing of one kind wears without naming it. `node` covers every node; `leaf` a node with no children, and `container` a node with some — the same "has children" the theme's quieter backdrop colors follow, so setting a leaf's fill leaves containers looking like containers. `edge` covers every edge.
|
|
812
|
+
|
|
813
|
+
The more specific word wins, all the way down:
|
|
814
|
+
|
|
815
|
+
1. the theme
|
|
816
|
+
2. `diagram`
|
|
817
|
+
3. `default node`
|
|
818
|
+
4. `default leaf` or `default container`, or `default edge`
|
|
819
|
+
5. the thing's styles, a later one winning where two set the same key
|
|
820
|
+
6. what the thing's own line says
|
|
747
821
|
|
|
748
|
-
|
|
822
|
+
A default takes the words a style does, and may name a style of its own with `style:`, which it lays beneath its own words. Unlike a style, it is strict about kind: it names the kind it is for, so a word that kind has no use for can only be a mistake. `default edge fill: red` is refused and points at `line:`. `badge:` is refused on `default leaf` and `default node`, since a badge is a child and would make every leaf a container, and `icon:` is a leaf's alone, since a picture cannot hold children. A `url:` belongs to one thing, never to a kind.
|
|
823
|
+
|
|
824
|
+
Each kind is written at most once. `default leaf` twice is an error naming both lines, and `default leaf node` is refused with `default leaf` — a leaf is already a node.
|
|
749
825
|
|
|
750
826
|
## What the language refuses
|
|
751
827
|
|
|
@@ -810,6 +886,19 @@ That one was found by testing the lexer, not by rendering — and it could not h
|
|
|
810
886
|
|
|
811
887
|
Pre-1.0, so the minor number is where a breaking change goes. Every removal below is refused by name with the replacement quoted, rather than dropped in silence — an older file stops with an error saying what to write instead.
|
|
812
888
|
|
|
889
|
+
**0.5.0**
|
|
890
|
+
|
|
891
|
+
- Named themes: `diagram theme: nord`, one of thirteen, and `--theme` on the command line to render a file in another without editing it. The playground has a theme picker, which writes the `theme:` line into the source.
|
|
892
|
+
- `diagram text: (color: …)` sets every text's color at once.
|
|
893
|
+
- `default node | leaf | container | edge` sets what every thing of that kind looks like unless it says otherwise.
|
|
894
|
+
|
|
895
|
+
**0.4.0**
|
|
896
|
+
|
|
897
|
+
- A node with any children takes the theme's container colors, however the children are placed. A node with a `badge:` or a single child in a corner now draws as a container too; in 0.3.0 only a node with a title band did. Its text still sits where it did. Say `fill:` and `border:` to keep one looking like a leaf.
|
|
898
|
+
- A text's `size` takes a plain number of pixels beside `small`, `normal` and `large`: `(size: 22)`.
|
|
899
|
+
- **Breaking:** `deck` is an attribute of the node, not a statement. `deck drive "Drive 2" "Drive 3"` is written `deck: "Drive 2" "Drive 3"` on `node drive`, and the old statement is an error that quotes that line back. It is refused on a picture and on a `shape: none` node, where the statement used to draw nothing.
|
|
900
|
+
- **Breaking:** a key written twice on one statement is an error that quotes both values — `fill: red fill: blue`, `style:` twice, or the same property twice inside one set of brackets. In 0.3.0 one of the two was dropped in silence. A node's own word overriding its style's is unaffected.
|
|
901
|
+
- `style:` takes a list, `style: equipment, critical and alarm`, applied in order with a later style winning where two set the same key.
|
|
813
902
|
**0.3.0** — the vocabulary, reworked in one breaking version so there is one migration rather than five.
|
|
814
903
|
|
|
815
904
|
- `box` is `node` and `link` is `edge`. The string on either is its *text*; "label" is not a word the language has.
|
package/dist/ast.d.ts
CHANGED
|
@@ -272,14 +272,6 @@ export interface EdgeStmt {
|
|
|
272
272
|
attrs: Attrs;
|
|
273
273
|
line: number;
|
|
274
274
|
}
|
|
275
|
-
export interface DeckStmt {
|
|
276
|
-
kind: 'deck';
|
|
277
|
-
/** The container to draw with offset copies behind it. */
|
|
278
|
-
name: string;
|
|
279
|
-
/** One text per copy, back to front as written. */
|
|
280
|
-
texts: string[];
|
|
281
|
-
line: number;
|
|
282
|
-
}
|
|
283
275
|
/**
|
|
284
276
|
* `diagram background: #111111` — settings that belong to the drawing as a
|
|
285
277
|
* whole rather than to anything in it. It has no name because there is only
|
|
@@ -290,8 +282,38 @@ export interface DiagramStmt {
|
|
|
290
282
|
attrs: Attrs;
|
|
291
283
|
line: number;
|
|
292
284
|
}
|
|
293
|
-
/**
|
|
294
|
-
|
|
285
|
+
/**
|
|
286
|
+
* The attributes a `diagram` statement understands. `text` takes a bracket,
|
|
287
|
+
* `text: (color: …)`, and sets the text color of everything at once — the
|
|
288
|
+
* theme has one text color, shared by nodes and edges, and this is that.
|
|
289
|
+
*/
|
|
290
|
+
export declare const DIAGRAM_KEYS: readonly ["theme", "background", "text"];
|
|
291
|
+
/**
|
|
292
|
+
* `default leaf fill: #2e5d3a` — a style that applies to every thing of one
|
|
293
|
+
* kind without being named. The more specific wins: `node` covers every node,
|
|
294
|
+
* `leaf` and `container` beat it for the nodes they cover, and a thing's own
|
|
295
|
+
* styles and words beat any default.
|
|
296
|
+
*/
|
|
297
|
+
export interface DefaultStmt {
|
|
298
|
+
kind: 'default';
|
|
299
|
+
target: DefaultTarget;
|
|
300
|
+
attrs: Attrs;
|
|
301
|
+
line: number;
|
|
302
|
+
}
|
|
303
|
+
export declare const DEFAULT_TARGETS: readonly ["node", "leaf", "container", "edge"];
|
|
304
|
+
export type DefaultTarget = (typeof DEFAULT_TARGETS)[number];
|
|
305
|
+
/**
|
|
306
|
+
* What each default may say. A default carries a style's vocabulary, less the
|
|
307
|
+
* words that would contradict the kind it is written for:
|
|
308
|
+
*
|
|
309
|
+
* - A default names its kind, so it is strict where a style is permissive —
|
|
310
|
+
* `default edge fill:` can only be a mistake.
|
|
311
|
+
* - `badge:` gives a box a child, which makes it a container, so every leaf
|
|
312
|
+
* given one by `default leaf` or `default node` would stop being a leaf.
|
|
313
|
+
* - `icon:` draws a picture, and a picture cannot hold children, so it is a
|
|
314
|
+
* leaf's word and not a container's or every node's.
|
|
315
|
+
*/
|
|
316
|
+
export declare const DEFAULT_KEYS: Record<DefaultTarget, readonly string[]>;
|
|
295
317
|
/**
|
|
296
318
|
* The attributes whose value is a color rather than text. A color is written
|
|
297
319
|
* as the viewer will receive it and the renderer keeps no list of color words
|
|
@@ -373,7 +395,7 @@ export interface StyleStmt {
|
|
|
373
395
|
attrs: Attrs;
|
|
374
396
|
line: number;
|
|
375
397
|
}
|
|
376
|
-
export type Stmt = NodeStmt | EdgeStmt |
|
|
398
|
+
export type Stmt = NodeStmt | EdgeStmt | StyleStmt | DiagramStmt | DefaultStmt;
|
|
377
399
|
export interface Document {
|
|
378
400
|
statements: Stmt[];
|
|
379
401
|
}
|
package/dist/ast.js
CHANGED
|
@@ -222,8 +222,30 @@ export function describeAxis(axis) {
|
|
|
222
222
|
* `align` is how its lines range against each other once it is there.
|
|
223
223
|
*/
|
|
224
224
|
export const TEXT_KEYS = ['color', 'size', 'wrap', 'align', 'at'];
|
|
225
|
-
/**
|
|
226
|
-
|
|
225
|
+
/**
|
|
226
|
+
* The attributes a `diagram` statement understands. `text` takes a bracket,
|
|
227
|
+
* `text: (color: …)`, and sets the text color of everything at once — the
|
|
228
|
+
* theme has one text color, shared by nodes and edges, and this is that.
|
|
229
|
+
*/
|
|
230
|
+
export const DIAGRAM_KEYS = ['theme', 'background', 'text'];
|
|
231
|
+
export const DEFAULT_TARGETS = ['node', 'leaf', 'container', 'edge'];
|
|
232
|
+
/**
|
|
233
|
+
* What each default may say. A default carries a style's vocabulary, less the
|
|
234
|
+
* words that would contradict the kind it is written for:
|
|
235
|
+
*
|
|
236
|
+
* - A default names its kind, so it is strict where a style is permissive —
|
|
237
|
+
* `default edge fill:` can only be a mistake.
|
|
238
|
+
* - `badge:` gives a box a child, which makes it a container, so every leaf
|
|
239
|
+
* given one by `default leaf` or `default node` would stop being a leaf.
|
|
240
|
+
* - `icon:` draws a picture, and a picture cannot hold children, so it is a
|
|
241
|
+
* leaf's word and not a container's or every node's.
|
|
242
|
+
*/
|
|
243
|
+
export const DEFAULT_KEYS = {
|
|
244
|
+
node: ['style', 'shape', 'fill', 'border', 'text'],
|
|
245
|
+
leaf: ['style', 'shape', 'icon', 'fill', 'border', 'text'],
|
|
246
|
+
container: ['style', 'shape', 'badge', 'fill', 'border', 'text'],
|
|
247
|
+
edge: ['style', 'line', 'text'],
|
|
248
|
+
};
|
|
227
249
|
/**
|
|
228
250
|
* The attributes whose value is a color rather than text. A color is written
|
|
229
251
|
* as the viewer will receive it and the renderer keeps no list of color words
|
|
@@ -290,7 +312,7 @@ export const COLOR_PARTS = {
|
|
|
290
312
|
* an edge is not placed — it joins two things that are.
|
|
291
313
|
*/
|
|
292
314
|
export const ATTR_KEYS = {
|
|
293
|
-
shape: ['style', 'gap', 'overlap', 'contents', 'badge', 'shape', 'fill', 'border', 'text', 'url'],
|
|
315
|
+
shape: ['style', 'gap', 'overlap', 'contents', 'badge', 'deck', 'shape', 'fill', 'border', 'text', 'url'],
|
|
294
316
|
icon: ['style', 'gap', 'overlap', 'icon', 'text', 'url'],
|
|
295
317
|
none: ['style', 'gap', 'overlap', 'shape', 'text', 'url'],
|
|
296
318
|
edge: ['style', 'from', 'to', 'line', 'text', 'url'],
|
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,15 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { dirname, resolve as resolvePath } from 'node:path';
|
|
4
4
|
import { SourceError } from './errors.js';
|
|
5
5
|
import { compile } from './index.js';
|
|
6
|
+
import { THEME_NAMES, THEMES } from './themes.js';
|
|
6
7
|
const USAGE = `reladraw — render a diagram from stated placement
|
|
7
8
|
|
|
8
|
-
reladraw <input.reladraw> [-o <output.svg>]
|
|
9
|
+
reladraw <input.reladraw> [-o <output.svg>] [--theme <name>]
|
|
9
10
|
|
|
10
11
|
-o, --out where to write the SVG. Defaults to the input path with
|
|
11
12
|
its extension replaced by .svg. Use - for standard output.
|
|
13
|
+
--theme render in this theme, whatever the file's \`diagram theme:\`
|
|
14
|
+
says. One of ${THEME_NAMES.join(', ')}.
|
|
12
15
|
-h, --help print this.
|
|
13
16
|
`;
|
|
14
17
|
async function main(argv) {
|
|
@@ -18,6 +21,7 @@ async function main(argv) {
|
|
|
18
21
|
}
|
|
19
22
|
let input;
|
|
20
23
|
let out;
|
|
24
|
+
let theme;
|
|
21
25
|
for (let i = 0; i < argv.length; i += 1) {
|
|
22
26
|
const arg = argv[i];
|
|
23
27
|
if (arg === '-o' || arg === '--out') {
|
|
@@ -28,6 +32,18 @@ async function main(argv) {
|
|
|
28
32
|
}
|
|
29
33
|
i += 1;
|
|
30
34
|
}
|
|
35
|
+
else if (arg === '--theme') {
|
|
36
|
+
theme = argv[i + 1];
|
|
37
|
+
if (theme === undefined) {
|
|
38
|
+
process.stderr.write(`reladraw: --theme needs a name: ${THEME_NAMES.join(', ')}\n`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
if (THEMES[theme] === undefined) {
|
|
42
|
+
process.stderr.write(`reladraw: there is no theme called "${theme}" — the themes are ${THEME_NAMES.join(', ')}\n`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
i += 1;
|
|
46
|
+
}
|
|
31
47
|
else if (arg.startsWith('-') && arg !== '-') {
|
|
32
48
|
process.stderr.write(`reladraw: unknown option ${arg}\n`);
|
|
33
49
|
return 1;
|
|
@@ -47,7 +63,7 @@ async function main(argv) {
|
|
|
47
63
|
const source = await readFile(input, 'utf8');
|
|
48
64
|
let svg;
|
|
49
65
|
try {
|
|
50
|
-
svg = compile(source);
|
|
66
|
+
svg = compile(source, theme === undefined ? {} : { theme: THEMES[theme] });
|
|
51
67
|
}
|
|
52
68
|
catch (error) {
|
|
53
69
|
if (error instanceof SourceError) {
|
package/dist/constants.d.ts
CHANGED
|
@@ -98,10 +98,10 @@ export declare const ICON_LINES = 2;
|
|
|
98
98
|
export declare const ICON_GAP = 10;
|
|
99
99
|
export declare const DEFAULT_FONT_SIZE = 14;
|
|
100
100
|
/**
|
|
101
|
-
* The named text sizes, each a multiple of the document's own size.
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
101
|
+
* The named text sizes, each a multiple of the document's own size. A plain
|
|
102
|
+
* number of pixels is accepted too, as it is for a gap: nothing else in the
|
|
103
|
+
* diagram moving can make it wrong. The names stay the default because they
|
|
104
|
+
* follow the document's size when that is retuned, and a number does not.
|
|
105
105
|
*
|
|
106
106
|
* `small` is sampled rather than chosen. In
|
|
107
107
|
* `examples/reference/arch.png` the box and container texts run 25
|
package/dist/constants.js
CHANGED
|
@@ -110,10 +110,10 @@ export const ICON_LINES = 2;
|
|
|
110
110
|
export const ICON_GAP = 10;
|
|
111
111
|
export const DEFAULT_FONT_SIZE = 14;
|
|
112
112
|
/**
|
|
113
|
-
* The named text sizes, each a multiple of the document's own size.
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
113
|
+
* The named text sizes, each a multiple of the document's own size. A plain
|
|
114
|
+
* number of pixels is accepted too, as it is for a gap: nothing else in the
|
|
115
|
+
* diagram moving can make it wrong. The names stay the default because they
|
|
116
|
+
* follow the document's size when that is retuned, and a number does not.
|
|
117
117
|
*
|
|
118
118
|
* `small` is sampled rather than chosen. In
|
|
119
119
|
* `examples/reference/arch.png` the box and container texts run 25
|
|
@@ -143,10 +143,17 @@ const DEFAULT_TEXT_SIZE = { none: 'small' };
|
|
|
143
143
|
export function fontSizeFor(kind, textAttrs, fontSize, line) {
|
|
144
144
|
const named = textAttrs['size'] ?? DEFAULT_TEXT_SIZE[kind] ?? 'normal';
|
|
145
145
|
const scale = TEXT_SIZES[named];
|
|
146
|
-
if (scale
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
if (scale !== undefined)
|
|
147
|
+
return Math.round(fontSize * scale);
|
|
148
|
+
if (/^\d+(\.\d+)?$/.test(named) && Number(named) > 0)
|
|
149
|
+
return Number(named);
|
|
150
|
+
const unit = named.match(/^(\d+(?:\.\d+)?)px$/);
|
|
151
|
+
const hint = unit
|
|
152
|
+
? `; write "size: ${unit[1]}", a size's number is already in pixels`
|
|
153
|
+
: named.startsWith('-') || /^0+(\.0+)?$/.test(named)
|
|
154
|
+
? '; a text size has to be more than zero'
|
|
155
|
+
: '';
|
|
156
|
+
throw new SourceError(`size takes ${Object.keys(TEXT_SIZES).join(', ')} or a number of pixels, not "${named}"${hint}`, line);
|
|
150
157
|
}
|
|
151
158
|
export const DEFAULT_MARGIN = 40;
|
|
152
159
|
/**
|
package/dist/grammar.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface Span {
|
|
|
61
61
|
* it. A word missing here is a word that draws in the plain color, which is a
|
|
62
62
|
* dull page rather than a wrong one.
|
|
63
63
|
*/
|
|
64
|
-
export declare const STATEMENT_KEYWORDS: readonly ["node", "edge", "
|
|
64
|
+
export declare const STATEMENT_KEYWORDS: readonly ["node", "edge", "style", "diagram", "default"];
|
|
65
65
|
/**
|
|
66
66
|
* Every word that says something about where a thing goes. Assembled from the
|
|
67
67
|
* lists the parser itself reads, so a direction or a passage axis added there
|
package/dist/grammar.js
CHANGED
|
@@ -30,9 +30,9 @@ import { DIRECTIONS, POSITIONS, SIDES, PASSAGE_AXES } from './ast.js';
|
|
|
30
30
|
* it. A word missing here is a word that draws in the plain color, which is a
|
|
31
31
|
* dull page rather than a wrong one.
|
|
32
32
|
*/
|
|
33
|
-
export const STATEMENT_KEYWORDS = ['node', 'edge', '
|
|
33
|
+
export const STATEMENT_KEYWORDS = ['node', 'edge', 'style', 'diagram', 'default'];
|
|
34
34
|
/** Statements whose second word declares a name. `diagram` has none. */
|
|
35
|
-
const DECLARES_NAME = ['node', '
|
|
35
|
+
const DECLARES_NAME = ['node', 'style'];
|
|
36
36
|
/**
|
|
37
37
|
* Every word that says something about where a thing goes. Assembled from the
|
|
38
38
|
* lists the parser itself reads, so a direction or a passage axis added there
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export * from './measure.js';
|
|
|
6
6
|
export * from './model.js';
|
|
7
7
|
export { parse } from './parser.js';
|
|
8
8
|
export { resolve, type ResolveOptions } from './resolve.js';
|
|
9
|
-
export { render,
|
|
9
|
+
export { render, type RenderOptions } from './render.js';
|
|
10
|
+
export { DARK_THEME, DEFAULT_THEME, THEMES, THEME_NAMES, type Theme } from './themes.js';
|
|
10
11
|
import { type RenderOptions } from './render.js';
|
|
11
12
|
import { type ResolveOptions } from './resolve.js';
|
|
12
13
|
/** Source text in, SVG out. The whole pipeline in one call. */
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,8 @@ export * from './measure.js';
|
|
|
6
6
|
export * from './model.js';
|
|
7
7
|
export { parse } from './parser.js';
|
|
8
8
|
export { resolve } from './resolve.js';
|
|
9
|
-
export { render
|
|
9
|
+
export { render } from './render.js';
|
|
10
|
+
export { DARK_THEME, DEFAULT_THEME, THEMES, THEME_NAMES } from './themes.js';
|
|
10
11
|
import { parse } from './parser.js';
|
|
11
12
|
import { render } from './render.js';
|
|
12
13
|
import { resolve } from './resolve.js';
|
package/dist/model.d.ts
CHANGED
|
@@ -57,9 +57,10 @@ export interface LayoutNode {
|
|
|
57
57
|
/**
|
|
58
58
|
* Whether things stack beside this node's text — below it, or above it when
|
|
59
59
|
* the text is at the bottom. That is what a title band *is*, so it is what
|
|
60
|
-
* decides
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* decides its text's defaults, rather than whether the node has children: a
|
|
61
|
+
* node whose only child sits beside its text or in a corner has nothing
|
|
62
|
+
* stacked below the text, and its text sits as a leaf's does. Its *colors*
|
|
63
|
+
* follow having children, not this.
|
|
63
64
|
*/
|
|
64
65
|
banded: boolean;
|
|
65
66
|
/**
|