lispgram 0.10.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/LAYOUT_LANGUAGE.md +390 -0
- package/LICENSE +21 -0
- package/LISPGRAM_GRAMMAR.md +568 -0
- package/README.md +326 -0
- package/dist/index.js +5648 -0
- package/dist/interaction.js +9 -0
- package/dist/layout.js +6 -0
- package/dist/surface.js +12 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# lispgram
|
|
2
|
+
|
|
3
|
+
`lispgram` is a small JavaScript package that gives you:
|
|
4
|
+
|
|
5
|
+
- a user-facing lispy surface language
|
|
6
|
+
- a compiler from that surface language into NCF
|
|
7
|
+
- a lightweight SVG visualizer backed by the constraint layout engine that can render either Lispgram source or raw NCF
|
|
8
|
+
|
|
9
|
+
The public compiler now has three explicit syntaxes:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Lispgram surface human-friendly input
|
|
13
|
+
Semantic core official tiny fact language: node, arrow, contains
|
|
14
|
+
NCF visualization-oriented graph syntax
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
It also exposes optional interpretations of the semantic core. The first is the normalized tree interpretation, which derives a single-owner containment tree using a generated root and least-common-ancestor ownership for arrows.
|
|
18
|
+
|
|
19
|
+
NCF remains the rendering representation. Author-facing source compiles through the semantic core into:
|
|
20
|
+
|
|
21
|
+
```lisp
|
|
22
|
+
(cy
|
|
23
|
+
(nodes ...)
|
|
24
|
+
(edges ...))
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Public npm surface
|
|
28
|
+
|
|
29
|
+
The published package intentionally exposes only the author-facing API:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import lispgram from "lispgram";
|
|
33
|
+
import { parseDocument, compileLispgram, toNCF, toNCFDoc, toLayerSexp } from "lispgram/surface";
|
|
34
|
+
import { render, initialize, createDiagramSvg } from "lispgram/layout";
|
|
35
|
+
import { diagram, registerDiagram } from "lispgram/interaction";
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package export map is limited to:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
lispgram
|
|
42
|
+
lispgram/surface
|
|
43
|
+
lispgram/layout
|
|
44
|
+
lispgram/interaction
|
|
45
|
+
lispgram/package.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Internal layer modules are not public API and are not exported as package subpaths. The npm package still exposes normal runtime functionality through the public surface, layout, and interaction entry points, including layer translation output for devtools via `toLayerSexp()`.
|
|
49
|
+
|
|
50
|
+
JavaScript packages cannot fully prevent reverse engineering of code that runs on the client or is installed locally. The package boundary therefore focuses on minimizing the supported import surface, excluding examples/tests/devtools and maintainer docs from npm, and shipping only the grammar/layout authoring docs needed by package consumers.
|
|
51
|
+
|
|
52
|
+
## Supported surface forms
|
|
53
|
+
|
|
54
|
+
```lispgram
|
|
55
|
+
; initialize a node
|
|
56
|
+
(P)
|
|
57
|
+
|
|
58
|
+
; initialize a node with a label
|
|
59
|
+
(P{hi})
|
|
60
|
+
|
|
61
|
+
; initialize a node with label plus fields
|
|
62
|
+
(P{Person { color blue rank 2 }})
|
|
63
|
+
|
|
64
|
+
; fully explicit data field map
|
|
65
|
+
(P{{ label Person color blue rank 2 }})
|
|
66
|
+
|
|
67
|
+
; parent/child relationships
|
|
68
|
+
(X Y Z)
|
|
69
|
+
(X Y (Z P))
|
|
70
|
+
(X (Y (Z P)))
|
|
71
|
+
|
|
72
|
+
; arrows
|
|
73
|
+
(-> e A B)
|
|
74
|
+
(-> e{hi} A B)
|
|
75
|
+
(-> e{maps { color purple weight 2 }} A B)
|
|
76
|
+
|
|
77
|
+
; fan-out
|
|
78
|
+
(-> e A [B C D])
|
|
79
|
+
|
|
80
|
+
; fan-in
|
|
81
|
+
(-> e [A B C] D)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## What this starter implements
|
|
85
|
+
|
|
86
|
+
- nodes and arrows with optional `{label}` suffixes and richer `{label { key value ... }}` / `{{ key value ... }}` field maps
|
|
87
|
+
- parent/child trees compiled to `(@parent X)`
|
|
88
|
+
- arrows compiled as NCF arrow carrier nodes with:
|
|
89
|
+
- `(@class "arrow")`
|
|
90
|
+
- `(@class "arrowFrom")`
|
|
91
|
+
- `(@class "arrowTo")`
|
|
92
|
+
- fan-in / fan-out expansion into multiple arrows that keep the same visible label
|
|
93
|
+
- an official semantic core grammar with only `node`, `arrow`, and `contains`
|
|
94
|
+
- semantic core parsing/emitting for external rule or constraint systems
|
|
95
|
+
- a normalized tree interpretation for derived ownership/layout
|
|
96
|
+
- containment of arrows as well as object nodes in the semantic core
|
|
97
|
+
- raw NCF passthrough
|
|
98
|
+
- browser auto-initialization for `<pre class="lispgram">...</pre>` blocks
|
|
99
|
+
- heuristic SVG routing for parallel, anti-parallel, and higher-order arrows
|
|
100
|
+
- compound/container labels rendered only in the header band
|
|
101
|
+
|
|
102
|
+
## Install
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install lispgram
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Browser usage
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<pre class="lispgram">
|
|
112
|
+
(Y X)
|
|
113
|
+
(-> f A B)
|
|
114
|
+
(-> g B A)
|
|
115
|
+
(-> h X Y)
|
|
116
|
+
</pre>
|
|
117
|
+
|
|
118
|
+
<script type="module">
|
|
119
|
+
import lispgram from "lispgram";
|
|
120
|
+
lispgram.initialize({
|
|
121
|
+
startOnLoad: true,
|
|
122
|
+
showArrowGlyphs: true,
|
|
123
|
+
clickToFocus: true
|
|
124
|
+
});
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Library usage
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
import lispgram from "lispgram";
|
|
132
|
+
|
|
133
|
+
const source = `
|
|
134
|
+
(Y X)
|
|
135
|
+
(-> f A B)
|
|
136
|
+
(-> g B A)
|
|
137
|
+
(-> h X Y)
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
const ncfString = lispgram.toNCF(source);
|
|
141
|
+
const ncfDoc = lispgram.toNCFDoc(source);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Render into a DOM node:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { render } from "lispgram";
|
|
148
|
+
|
|
149
|
+
render(document.querySelector("#app"), `
|
|
150
|
+
(P A B)
|
|
151
|
+
(-> left A B)
|
|
152
|
+
(-> right B A)
|
|
153
|
+
`);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## Interactive diagrams
|
|
158
|
+
|
|
159
|
+
Interactivity is attached from JavaScript, not from Lispgram syntax. Give a render a stable `diagramId`, or set `data-lispgram-diagram` on an auto-rendered `<pre>`, then select semantic elements by their normalized `data` fields.
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { render, diagram } from "lispgram";
|
|
163
|
+
|
|
164
|
+
render(document.querySelector("#app"), `
|
|
165
|
+
(lg-core
|
|
166
|
+
(node (A (@data clickable true info "Node A")))
|
|
167
|
+
(node B)
|
|
168
|
+
(arrow (f (@data clickable true info "A to B")) A B))
|
|
169
|
+
`, { diagramId: "P" });
|
|
170
|
+
|
|
171
|
+
diagram("P")
|
|
172
|
+
.select((elem) => elem.data.clickable === true)
|
|
173
|
+
.addonclick((elem) => alert(elem.data.info));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Each selected `elem` describes the semantic cell rather than the raw SVG node:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
{
|
|
180
|
+
id: "f",
|
|
181
|
+
type: "arrow", // or "node"
|
|
182
|
+
kind: "arrow",
|
|
183
|
+
data: { info: "A to B" },
|
|
184
|
+
source: "A",
|
|
185
|
+
target: "B",
|
|
186
|
+
parent: null,
|
|
187
|
+
elements: [/* generated SVG path/label/glyph elements */]
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
You can also select with a small object matcher:
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
diagram("P")
|
|
195
|
+
.select({ type: "arrow", data: { clickable: true } })
|
|
196
|
+
.addOnClick((elem) => console.log(elem.source, elem.target));
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
For auto-rendered diagrams:
|
|
200
|
+
|
|
201
|
+
```html
|
|
202
|
+
<pre class="lispgram" data-lispgram-diagram="P">
|
|
203
|
+
(A{{ clickable true info "Node A" }} B)
|
|
204
|
+
</pre>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Interaction behavior is available through `lispgram/interaction` and through the default click-to-focus SVG renderer.
|
|
208
|
+
|
|
209
|
+
## API
|
|
210
|
+
|
|
211
|
+
The public API is intentionally small but includes the normal runtime features needed by the standalone devtool. Internal solver and layer implementation modules are not exported by the package.
|
|
212
|
+
|
|
213
|
+
### Surface entry point
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
import { parseDocument, compileLispgram, toNCFDoc, toNCF, sourceToSemanticCore, toLayerSexp } from "lispgram/surface";
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`parseDocument(source)` parses Lispgram surface syntax. `compileLispgram(source)` returns the parsed AST, surface facts, semantic core, and normalized NCF document. `toNCFDoc(source)` accepts Lispgram surface, wrapped semantic core, or raw NCF and returns the normalized document object. `toNCF(source)` emits textual NCF. `sourceToSemanticCore(source)` returns the tiny semantic-core fact model for Lispgram or wrapped semantic-core input. `toLayerSexp(source)` returns author/devtool layer text including Surface AST, Surface Facts, Semantic Core, Normalized Tree, NCF Model, and NCF Text when those layers are available for the input form.
|
|
220
|
+
|
|
221
|
+
### Layout entry point
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
import { render, renderElement, initialize, createDiagramSvg } from "lispgram/layout";
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`render(target, source, options?)` renders Lispgram or NCF into a DOM target. `renderElement(sourceElement, options?)` renders one source element. `initialize(options?)` auto-renders matching elements such as `<pre class="lispgram">`. `createDiagramSvg(doc, options?)` renders an already-normalized NCF document into an SVG element.
|
|
228
|
+
|
|
229
|
+
### Interaction entry point
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
import { diagram, registerDiagram, unregisterDiagram, listDiagrams } from "lispgram/interaction";
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`diagram(id?)` returns an interactive diagram controller. Controllers support `.select(predicateOrMatcher)`, `.selectAll()`, `.get(id)`, `.on(eventName, selector, handler)`, `.addOnClick(selector, handler)`, and `.clearInteractions()`. `registerDiagram()` registers an already-rendered document/SVG pair; `render()` and `initialize()` call it automatically.
|
|
236
|
+
|
|
237
|
+
### Root entry point
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
import lispgram, { render, toNCF } from "lispgram";
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The root entry point re-exports the public surface, layout, and interaction APIs only. It does not export internal layer emitters, solver internals, or devtool diagnostic helpers.
|
|
244
|
+
|
|
245
|
+
## Notes
|
|
246
|
+
|
|
247
|
+
This package is intentionally small and readable. It is a good base for:
|
|
248
|
+
|
|
249
|
+
- improving the layout engine
|
|
250
|
+
- extending the surface language
|
|
251
|
+
- integrating external rule/constraint systems over the semantic core
|
|
252
|
+
- adding richer styling directives
|
|
253
|
+
- expanding support for higher-order arrows and editor UX
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
## Dev visualizer
|
|
257
|
+
|
|
258
|
+
The repository includes a browser-based dev tool with Lispgram + raw NCF input, compiled/normalized NCF output, orthogonal routing, wiring overlay, arrow-node glyphs, click focus, zoom, and fit-to-view.
|
|
259
|
+
|
|
260
|
+
Run it from the repo with:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npm run dev:visualizer
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Then open:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
http://localhost:4173/examples/devtool.html
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The dev tool accepts Lispgram surface syntax, wrapped semantic core `(lg-core ...)`, or raw NCF. When the input is Lispgram, it compiles it through the semantic core first and shows both the semantic core and normalized NCF in the side panel.
|
|
273
|
+
|
|
274
|
+
The repo devtool can use source-only helpers during development, while the standalone devtool uses the public `toLayerSexp()` API from the npm package.
|
|
275
|
+
|
|
276
|
+
## Development
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm test
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
## Focus behavior
|
|
284
|
+
|
|
285
|
+
When `clickToFocus` is enabled, clicking a node, compound/container, or arrow focuses that element within its own diagram. The focused element, its incident arrows, and immediate source/target nodes or arrows stay prominent while unrelated elements dim. This includes arrow-between-arrow routes, where the source and target arrows are treated as immediate endpoints. `clickToFocus` defaults to `true`; set `data-lispgram-click-focus="false"` or pass `clickToFocus: false` to disable it.
|
|
286
|
+
|
|
287
|
+
Click hit-testing uses a transparent interaction layer above the rendered diagram. Ordinary nodes are given priority over arrows, arrows over container interiors, and deeper containers over their ancestors. This keeps large ancestor containers clickable without letting them steal clicks from nested containers such as `BuildSystem`, `ExternalDependencies`, or `GuileBinding`.
|
|
288
|
+
|
|
289
|
+
## Demo
|
|
290
|
+
|
|
291
|
+
go to repo root directory
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
python -m http.server
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
navigate to examples/basic.html in browser
|
|
298
|
+
|
|
299
|
+
## Browser script Drop-in use
|
|
300
|
+
|
|
301
|
+
```html
|
|
302
|
+
|
|
303
|
+
<pre class="lispgram">
|
|
304
|
+
(lispgram
|
|
305
|
+
(A)
|
|
306
|
+
(-> e X Y)
|
|
307
|
+
)
|
|
308
|
+
</pre>
|
|
309
|
+
<script type="module">
|
|
310
|
+
import lispgram from "https://unpkg.com/lispgram@latest/dist/index.js";
|
|
311
|
+
lispgram.initialize({
|
|
312
|
+
startOnLoad: true,
|
|
313
|
+
showArrowGlyphs: true,
|
|
314
|
+
clickToFocus: true,
|
|
315
|
+
arrowGlyphRadius: 5.75
|
|
316
|
+
});
|
|
317
|
+
</script>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The standalone devtool at `examples/devtool-standalone.html` is static-site friendly: it dynamically imports `https://unpkg.com/lispgram@latest/dist/index.js` and uses public APIs, including `toLayerSexp()`, so you can host that single HTML file independently after publishing the package to npm.
|
|
321
|
+
|
|
322
|
+
## Constraint layout foundation
|
|
323
|
+
|
|
324
|
+
This version uses the constraint-layout engine in `src/layers/08-constraint-layout` as the main SVG layout backend. The default small-core inference now projects arrows through containment, so arrows from leaves in one subtree to leaves in another subtree can compact their ancestor containers into readable columns instead of long vertical stacks. The small Lispgram core remains the preferred authoring surface: tree forms and ordinary arrows are inferred into containment scopes, self-loops, parallel/anti-parallel lanes, arrow-to-arrow route strata, and fallback rank constraints. The engine models nodes and containers as boxes, islands as first-class layout views, relative constraints as compiler output, and arrows as addressable routes with anchors. Boundary attachments place external source subtrees outside the selected side and then draw rounded orthogonal routes through side-ports. Template `:container` declarations are active visual shells for the template core members. This enables autodetected self loops, arrows between arrows, route dependency strata, curved cone legs, distinct route-to-node anchors, and categorical templates such as product, coproduct, equalizer, pullback, pushout, exponential, and Hasse-style ranked diagrams.
|
|
325
|
+
|
|
326
|
+
Open `examples/constraint-layout-foundation.html` from a source checkout for a browser demo. See `LAYOUT_LANGUAGE.md` for the `$layout` surface syntax. The repository also contains maintainer-oriented layout-engine notes for contributors; those internal docs are not part of the minimal npm runtime surface.
|