json-canvas-viewer 3.1.1 → 3.2.1
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 +47 -368
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types/baseModule.d.ts +11 -0
- package/dist/types/canvasViewer.d.ts +21 -14
- package/dist/types/controller.d.ts +33 -0
- package/dist/types/{extensions/controls → controls}/index.d.ts +12 -6
- package/dist/types/dataManager.d.ts +41 -22
- package/dist/types/debugPanel/index.d.ts +9 -0
- package/dist/types/declarations.d.ts +86 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/interactionHandler.d.ts +21 -9
- package/dist/types/{extensions/minimap → minimap}/index.d.ts +13 -7
- package/dist/types/mistouchPreventer/index.d.ts +22 -0
- package/dist/types/overlayManager.d.ts +28 -9
- package/dist/types/renderToString.d.ts +2 -0
- package/dist/types/renderer.d.ts +5 -4
- package/dist/types/shared.d.ts +1 -0
- package/dist/types/utilities.d.ts +25 -12
- package/package.json +31 -67
- package/dist/cjs/controls.js +0 -1
- package/dist/cjs/debugPanel.js +0 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/minimap.js +0 -1
- package/dist/cjs/mistouchPreventer.js +0 -1
- package/dist/es/controls.js +0 -1
- package/dist/es/debugPanel.js +0 -1
- package/dist/es/index.js +0 -1
- package/dist/es/minimap.js +0 -1
- package/dist/es/mistouchPreventer.js +0 -1
- package/dist/types/extensions/debugPanel/index.d.ts +0 -8
- package/dist/types/extensions/mistouchPreventer/index.d.ts +0 -14
- package/dist/types/interactor.d.ts +0 -30
- package/dist/utilities-9mYNj6lG.js +0 -1
- package/dist/utilities-C1DbXNeO.cjs +0 -1
package/README.md
CHANGED
|
@@ -1,75 +1,22 @@
|
|
|
1
1
|
# JSON Canvas Viewer
|
|
2
2
|
|
|
3
|
-

|
|
4
3
|
[](https://www.npmjs.com/package/json-canvas-viewer)
|
|
5
|
-
[](https://bundlephobia.com/package/json-canvas-viewer)
|
|
5
|
+
[](https://github.com/hesprs/json-canvas-viewer/actions)
|
|
6
|
+
[](https://www.codefactor.io/repository/github/hesprs/json-canvas-viewer)
|
|
7
|
+

|
|
8
|
+
[](https://snyk.io/test/npm/json-canvas-viewer)
|
|
9
|
+
[](https://github.com/hesprs/json-canvas-viewer/wiki)
|
|
10
|
+

|
|
6
11
|
|
|
7
|
-

|
|
8
13
|
|
|
9
14
|
A **TypeScript-based** viewer for **JSON Canvas** files. View and interact with your canvas files directly in the browser, or embed the viewer in front-end projects with ease. It is built without frameworks so it can be easily integrated into any framework.
|
|
10
15
|
|
|
11
|
-
This project is
|
|
12
|
-
|
|
13
|
-
For more about **JSON Canvas**, also known as **Obsidian Canvas**, please visit [jsoncanvas.org](https://jsoncanvas.org/).
|
|
14
|
-
|
|
15
|
-
## 📦 Installation
|
|
16
|
-
|
|
17
|
-
We recommend using your favorite package manager to install the package. **Note: This package requires `marked` as dependency, your package manager will automatically install it.**
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
# npm
|
|
21
|
-
npm install json-canvas-viewer
|
|
22
|
-
|
|
23
|
-
# pnpm
|
|
24
|
-
pnpm add json-canvas-viewer
|
|
25
|
-
|
|
26
|
-
# yarn
|
|
27
|
-
yarn add json-canvas-viewer
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
After installation, you can import the package as a module. It supports both ES module and CommonJS. JS, here we take ESM as an example:
|
|
31
|
-
|
|
32
|
-
```TypeScript
|
|
33
|
-
import canvasViewer from 'json-canvas-viewer';
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 🚀 Quick Start
|
|
37
|
-
|
|
38
|
-
Instantiate the viewer:
|
|
39
|
-
|
|
40
|
-
```HTML
|
|
41
|
-
<div id="myCanvasContainer" style="width:800px; height:600px;"></div>
|
|
42
|
-
<script type="module">
|
|
43
|
-
import canvasViewer from 'json-canvas-viewer';
|
|
44
|
-
import minimap from 'json-canvas-viewer/minimap'
|
|
45
|
-
|
|
46
|
-
const viewer = new canvasViewer(document.getElementById('myCanvasContainer'), {
|
|
47
|
-
extensions: [minimap], // use extensions
|
|
48
|
-
options: {
|
|
49
|
-
minimap: {
|
|
50
|
-
collapsed: true, // use options
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
hooks: {
|
|
54
|
-
onLoad: [(path) => console.log('Loading Canvas at ${path}!')] // register hooks
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
viewer.loadCanvas('example/introduction.canvas');
|
|
58
|
-
|
|
59
|
-
console.log(viewer.registry.api.dataManager.middleViewer()); // use APIs
|
|
60
|
-
|
|
61
|
-
viewer.dispose(); // dispose when not needed
|
|
62
|
-
</script>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
And the viewer should be right in your container, you can instantiate the viewer multiple times to render multiple canvases simultaneously.
|
|
66
|
-
|
|
67
|
-
**Public Methods & Registry**:
|
|
68
|
-
|
|
69
|
-
- `viewer.loadCanvas(path)` - Load a canvas file (by path), **please put all the related files (files embedded in the canvas) in the same folder as the canvas file, wherever they originally are**.
|
|
70
|
-
- `viewer.dispose()` - Clean up and remove viewer from DOM.
|
|
71
|
-
- `viewer.registry` - access `registry` (covered later).
|
|
16
|
+
This project is derived from [sofanati-nour/obsidian-canvas-web-renderer](https://github.com/sofanati-nour/obsidian-canvas-web-renderer), but is far more developed and optimized.
|
|
72
17
|
|
|
18
|
+
- **Documentation**: [project wiki](https://github.com/hesprs/json-canvas-viewer/wiki)
|
|
19
|
+
- **More about JSON Canvas**: [jsoncanvas.org](https://jsoncanvas.org/)
|
|
73
20
|
|
|
74
21
|
## 🐶 Features
|
|
75
22
|
|
|
@@ -86,329 +33,61 @@ And the viewer should be right in your container, you can instantiate the viewer
|
|
|
86
33
|
- Minimap for easy navigation (optional extension)
|
|
87
34
|
- Mistouch prevention (optional extension)
|
|
88
35
|
- Responsive design with mobile and touchpad adaptation
|
|
89
|
-
- Out-of-the-box extensibility and tree-shaking
|
|
90
36
|
- TypeScript native support
|
|
91
|
-
-
|
|
37
|
+
- 🧩 Out-of-the-box **extensibility** and tree-shaking
|
|
38
|
+
- 🔥 **More performant** than rendering canvases in Obsidian!
|
|
92
39
|
|
|
93
|
-
##
|
|
40
|
+
## 🚀 Quick Start
|
|
94
41
|
|
|
95
|
-
|
|
42
|
+
We recommend using your favourite package manager to install the package.
|
|
96
43
|
|
|
97
|
-
|
|
44
|
+
```sh
|
|
45
|
+
# npm
|
|
46
|
+
npm add json-canvas-viewer
|
|
98
47
|
|
|
99
|
-
|
|
100
|
-
|
|
48
|
+
# pnpm
|
|
49
|
+
pnpm add json-canvas-viewer
|
|
101
50
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
extensions: Array<Class<runtimeData, registry>>;
|
|
105
|
-
hooks: Record<string, Array<Function>>;
|
|
106
|
-
api: Record<string, Record<string, Function>>;
|
|
107
|
-
register: (userRegistry: userRegistry) => void;
|
|
108
|
-
}
|
|
109
|
-
interface Class<T> { new (...args: any[]): T }
|
|
110
|
-
interface Function { (...args: any[]): any }
|
|
51
|
+
# yarn
|
|
52
|
+
yarn add json-canvas-viewer
|
|
111
53
|
```
|
|
112
54
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
**Default Options and Values**:
|
|
55
|
+
Or include the following lines directly in your HTML file:
|
|
116
56
|
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
noShadow: false,
|
|
122
|
-
},
|
|
123
|
-
interactor: {
|
|
124
|
-
preventDefault: true,
|
|
125
|
-
proControlSchema: false,
|
|
126
|
-
zoomFactor: 0.002,
|
|
127
|
-
lockControlSchema: false,
|
|
128
|
-
},
|
|
129
|
-
}
|
|
130
|
-
}
|
|
57
|
+
```html
|
|
58
|
+
<script type="module">
|
|
59
|
+
import { JSONCanvasViewer } from 'https://unpkg.com/json-canvas-viewer/dist/index.js';
|
|
60
|
+
</script>
|
|
131
61
|
```
|
|
132
62
|
|
|
133
|
-
|
|
134
|
-
- `preventDefault` — Prevents default behavior of mouse events, the viewer may not work properly if set to false.
|
|
135
|
-
- `proControlSchema` — Uses control keybindings in professional software (`mouse wheel`: scroll vertically; `mouse wheel + shift`: scroll horizontally; `mouse wheel + ctrl`: zoom), rather than zooming with the mouse wheel. The canvas viewer automatically detects and adjusts the control schema by default, but you can explicitly configure it. This option doesn't affect mobile control.
|
|
136
|
-
- `zoomFactor` — The zoom factor, how fast the canvas zooms in or out.
|
|
137
|
-
- `lockControlSchema` — Locks the control schema.
|
|
138
|
-
|
|
139
|
-
**Default Hooks**:
|
|
63
|
+
This link ships the latest ESM version by default, to access CJS version or earlier versions, try using a different URL like:
|
|
140
64
|
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
hooks: {
|
|
144
|
-
onDispose: [],
|
|
145
|
-
onRender: [],
|
|
146
|
-
onResize: [(width: number, height: number) => {}],
|
|
147
|
-
onClick: [(id: string | null) => {}],
|
|
148
|
-
onToggleFullscreen: [],
|
|
149
|
-
onInteractionStart: [],
|
|
150
|
-
onInteractionEnd: [],
|
|
151
|
-
}
|
|
152
|
-
}
|
|
65
|
+
```html
|
|
66
|
+
<script src="https://unpkg.com/json-canvas-viewer@3.2.0/dist/index.cjs"></script>
|
|
153
67
|
```
|
|
154
68
|
|
|
155
|
-
|
|
156
|
-
- `onRender` — Called when the viewer is rendered every frame.
|
|
157
|
-
- `onResize` — Called when the viewer container is resized.
|
|
158
|
-
- `width` — The width of the resized viewer container.
|
|
159
|
-
- `height` — The height of the resized viewer container.
|
|
160
|
-
- `onClick` — Called when the canvas is clicked.
|
|
161
|
-
- `id` — The id of the node that is clicked, or `null` if no node is clicked.
|
|
162
|
-
- `onToggleFullscreen` — Called when the fullscreen mode is toggled.
|
|
163
|
-
- `onInteractionStart` — Called when the pointer enters a selected node.
|
|
164
|
-
- `onInteractionEnd` — Called when the pointer leaves a selected node.
|
|
69
|
+
The link above ships version 3.2.0 in CJS.
|
|
165
70
|
|
|
166
|
-
|
|
71
|
+
Then we can instantiate the viewer:
|
|
167
72
|
|
|
168
73
|
```TypeScript
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
middleViewer: () => {
|
|
183
|
-
x: number; // half of the container width
|
|
184
|
-
y: number; // half of the container height
|
|
185
|
-
width: number; // container width
|
|
186
|
-
height: number; // container height
|
|
187
|
-
},
|
|
188
|
-
findNodeAt: (mousePosition: Coordinates) => JSONCanvasNode | null,
|
|
189
|
-
applyStyles: (container: HTMLElement, styleString: string) => void, // add a <style> element containing styleString to container (used in extension development)
|
|
190
|
-
},
|
|
191
|
-
interactionHandler: {
|
|
192
|
-
stop: () => void, // Stop receiving interaction
|
|
193
|
-
start: () => void, // Start receiving interaction
|
|
194
|
-
},
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Extensions
|
|
200
|
-
|
|
201
|
-
The viewer is built with extensibility in mind, and it is easy to extend the viewer with custom extensions.
|
|
202
|
-
|
|
203
|
-
#### Official Extensions
|
|
204
|
-
|
|
205
|
-
**minimap**: Renders the minimap with an overview of the canvas.
|
|
206
|
-
- used with:
|
|
207
|
-
```TypeScript
|
|
208
|
-
import minimap from 'json-canvas-viewer/minimap';
|
|
209
|
-
new canvasViewer(container, { extensions: [minimap] });
|
|
210
|
-
```
|
|
211
|
-
- new options and API:
|
|
212
|
-
```TypeScript
|
|
213
|
-
{
|
|
214
|
-
options: {
|
|
215
|
-
minimap: {
|
|
216
|
-
collapsed: false,
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
api: {
|
|
220
|
-
minimap: {
|
|
221
|
-
toggleCollapse: () => void,
|
|
222
|
-
},
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**mistouchPreventer**: Prevents mistouch by freezing the canvas when clicking outside the viewer.
|
|
228
|
-
- used with:
|
|
229
|
-
```TypeScript
|
|
230
|
-
import mistouchPreventer from 'json-canvas-viewer/mistouchPreventer';
|
|
231
|
-
new canvasViewer(container, { extensions: [mistouchPreventer] });
|
|
232
|
-
```
|
|
233
|
-
- new options and API:
|
|
234
|
-
```TypeScript
|
|
235
|
-
{
|
|
236
|
-
options: {
|
|
237
|
-
mistouchPreventer: {
|
|
238
|
-
preventAtStart: true,
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
api: {
|
|
242
|
-
mistouchPreventer: {
|
|
243
|
-
startPrevention: () => void,
|
|
244
|
-
endPrevention: () => void,
|
|
245
|
-
},
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
**controls**: Shows a control bar with zoom, pan, scale slider, and fullscreen buttons.
|
|
251
|
-
- used with:
|
|
252
|
-
```TypeScript
|
|
253
|
-
import controls from 'json-canvas-viewer/controls';
|
|
254
|
-
new canvasViewer(container, { extensions: [controls] });
|
|
255
|
-
```
|
|
256
|
-
- new options and API:
|
|
257
|
-
```TypeScript
|
|
258
|
-
{
|
|
259
|
-
options: {
|
|
260
|
-
controls: {
|
|
261
|
-
collapsed: false,
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
api: {
|
|
265
|
-
controls: {
|
|
266
|
-
toggleCollapse: () => void,
|
|
267
|
-
},
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
**debugPanel**: Shows a debug panel with scale and offset.
|
|
273
|
-
- used with:
|
|
274
|
-
```TypeScript
|
|
275
|
-
import debugPanel from 'json-canvas-viewer/debugPanel';
|
|
276
|
-
new canvasViewer(container, { extensions: [debugPanel] });
|
|
277
|
-
```
|
|
278
|
-
- new API:
|
|
279
|
-
```TypeScript
|
|
280
|
-
{
|
|
281
|
-
api: {
|
|
282
|
-
debugPanel: {
|
|
283
|
-
update: () => void,
|
|
284
|
-
},
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
#### Develop an Extension
|
|
290
|
-
|
|
291
|
-
An extension, in essence, is a class that follows a fixed pattern. You can do almost anything with the viewer by using an extension. Actually, the viewer runs with only four core extensions: `renderer`, `interactionHandler`, `overlayManager` and `dataManager`. Here is the fixed pattern:
|
|
292
|
-
- receive `runtimeData` and `registry` as parameters in the constructor.
|
|
293
|
-
- calls `registry.register` first in the constructor to define default options, register hooks and provide API.
|
|
294
|
-
|
|
295
|
-
Here comes a minimal sample extension of a debug panel (JavaScript for simplicity):
|
|
296
|
-
|
|
297
|
-
```JavaScript
|
|
298
|
-
import style from './style.scss?inline';
|
|
299
|
-
|
|
300
|
-
export default class debugPanel {
|
|
301
|
-
constructor(data, registry) {
|
|
302
|
-
registry.register({
|
|
303
|
-
hooks: {
|
|
304
|
-
onRender: [this.update],
|
|
305
|
-
onDispose: [this.dispose],
|
|
306
|
-
},
|
|
307
|
-
api: {
|
|
308
|
-
debugPanel: {
|
|
309
|
-
update: this.update,
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
});
|
|
313
|
-
this.debugPanel = document.createElement('div');
|
|
314
|
-
this.debugPanel.className = 'debug-panel';
|
|
315
|
-
registry.api.dataManager.applyStyles(this.debugPanel, style);
|
|
316
|
-
this.data = data;
|
|
317
|
-
data.container.appendChild(this.debugPanel);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private update = () => {
|
|
321
|
-
this.debugPanel.innerHTML = `
|
|
322
|
-
<p>Scale: ${round(this.data.scale, 3)}</p>
|
|
323
|
-
<p>Offset: ${round(this.data.offsetX, 1)}, ${round(this.data.offsetY, 1)}</p>
|
|
324
|
-
`;
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
private round = (roundedNum: number, digits: number) => {
|
|
328
|
-
const factor = 10 ** digits;
|
|
329
|
-
return Math.round(roundedNum * factor) / factor;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private dispose = () => {
|
|
333
|
-
this.debugPanel.remove();
|
|
334
|
-
this.debugPanel = null;
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## 💻 Development
|
|
340
|
-
|
|
341
|
-
Built with `TypeScript`, `SCSS` and `HTML5 Canvas`.
|
|
342
|
-
|
|
343
|
-
**Project Structure**:
|
|
344
|
-
|
|
345
|
-
```
|
|
346
|
-
root
|
|
347
|
-
├── src/
|
|
348
|
-
│ ├── extensions/
|
|
349
|
-
│ │ ├── controls/ // Control panel
|
|
350
|
-
│ │ ├── minimap/ // Minimap extension
|
|
351
|
-
│ │ ├── mistouchPreventer/ // MistouchPrevention extension
|
|
352
|
-
│ │ └── debugPanel/ // Debug panel
|
|
353
|
-
│ ├── canvasViewer.ts // Main entry point
|
|
354
|
-
│ ├── interactor.ts // Handles pointer events for user pan/zoom
|
|
355
|
-
│ ├── dataManager.ts // Manages canvas data
|
|
356
|
-
│ ├── interactionHandler.ts // Handles interaction events (wrapper of interactor)
|
|
357
|
-
│ ├── overlayManager.ts // Renderer for interactive nodes
|
|
358
|
-
│ ├── renderer.ts // Renderer for non-interactive stuff
|
|
359
|
-
│ ├── declarations.d.ts // Public types
|
|
360
|
-
│ ├── utilities.ts // Utility functions
|
|
361
|
-
│ └── styles.scss // Main styles for the viewer
|
|
362
|
-
└── example/
|
|
363
|
-
├── index.html // Example/test entry point
|
|
364
|
-
└── Example Canvas/ // Example/test canvas file
|
|
74
|
+
import { Controls, JSONCanvasViewer, Minimap, MistouchPreventer } from 'json-canvas-viewer';
|
|
75
|
+
|
|
76
|
+
new JSONCanvasViewer(
|
|
77
|
+
{
|
|
78
|
+
container: document.body, // The element to attach the viewer to
|
|
79
|
+
canvasPath: './Example/introduction.canvas', // The path to the canvas to load
|
|
80
|
+
controlsCollapsed: true, // Other options, depending on the modules you passed in
|
|
81
|
+
mistouchPreventer: {
|
|
82
|
+
preventAtStart: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
[Controls, Minimap, MistouchPreventer], // The modules to load
|
|
86
|
+
);
|
|
365
87
|
```
|
|
366
88
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
The viewer expects JSON Canvas files in JSON format (a combination of official [JSON Canvas](https://jsoncanvas.org/spec/1.0/) spec and [Developer-Mike/obsidian-advanced-canvas](https://github.com/Developer-Mike/obsidian-advanced-canvas) spec):
|
|
370
|
-
|
|
371
|
-
```TypeScript
|
|
372
|
-
interface JSONCanvas {
|
|
373
|
-
nodes: Array<JSONCanvasNode>;
|
|
374
|
-
edges: Array<JSONCanvasEdge>;
|
|
375
|
-
metadata: {
|
|
376
|
-
version: string;
|
|
377
|
-
frontmatter: Record<string, string>;
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
interface JSONCanvasNode {
|
|
382
|
-
id: string;
|
|
383
|
-
type: 'group' | 'file' | 'text' | 'link';
|
|
384
|
-
x: number;
|
|
385
|
-
y: number;
|
|
386
|
-
width: number;
|
|
387
|
-
height: number;
|
|
388
|
-
label?: string;
|
|
389
|
-
background?: string;
|
|
390
|
-
backgroundStyle?: 'cover' | 'ratio' | 'repeat';
|
|
391
|
-
styleAttributes?: Record<string, string>;
|
|
392
|
-
color?: string;
|
|
393
|
-
text?: string;
|
|
394
|
-
file?: string;
|
|
395
|
-
subpath?: string;
|
|
396
|
-
url?: string;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
interface JSONCanvasEdge {
|
|
400
|
-
id: string;
|
|
401
|
-
fromNode: string;
|
|
402
|
-
toNode: string;
|
|
403
|
-
fromSide: 'right' | 'left' | 'top' | 'bottom';
|
|
404
|
-
toSide: 'right' | 'left' | 'top' | 'bottom';
|
|
405
|
-
toEnd?: 'arrow' | 'none';
|
|
406
|
-
label?: string;
|
|
407
|
-
styleAttributes?: Record<string, string>;
|
|
408
|
-
color?: string;
|
|
409
|
-
}
|
|
410
|
-
```
|
|
89
|
+
And the viewer should be right in the body, you can instantiate the viewer multiple times to render multiple canvases.
|
|
411
90
|
|
|
412
91
|
## 📝 Copyright & License
|
|
413
92
|
|
|
414
|
-
Copyright ©️ 2025 Hesprs (Hēsperus) | [MIT License](https://mit-license.org/)
|
|
93
|
+
Copyright ©️ 2025-2026 Hesprs (Hēsperus) | [MIT License](https://mit-license.org/)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("pointeract"),e=require("micromark");class i{constructor(t,e){this.container=t,Object.assign(this.options,e)}options={};dispose}function n(t){return"function"==typeof t}function s(t){const e=[];let i=t;for(;Object.getPrototypeOf(i).name;){const t=Object.getPrototypeOf(i);e.push(t),i=t}return e}function o(t){if(null==t)throw Error("Expected value to be not null or undefined");return t}function a(t,e){if(t.length>1)throw e();const i=t.at(0);if(void 0===i)throw e();return i}function r(t){return n(t)}function l(t){return"provide"in t&&"useClass"in t}function c(t){return"provide"in t&&"useFactory"in t}function h(t){return c(t)&&!0===t.async}function d(t){return"provide"in t&&"useExisting"in t}function p(t){return"provide"in t&&"multi"in t&&!0===t.multi}class u{description;options;constructor(t,e){this.description=t,this.options=e}toString(){return`InjectionToken "${String(this.description)}"`}}function m(t){return n(t)}function f(t){return n(t)?t.name:"symbol"==typeof t?t.description??String(t):t instanceof u?t.toString():t}function g(t){return r(t)?t:t.provide}const v=Symbol("injectable");class x{container;underConstruction=[];constructor(t){this.container=t}construct(t,e){if(h(t))throw new b(e);try{if(this.underConstruction.includes(t)){const e=[...this.underConstruction,t].map(g).map(f);throw new w(e)}return this.underConstruction.push(t),this.doConstruct(t)}finally{this.underConstruction.pop()}}async constructAsync(t){try{if(this.underConstruction.includes(t)){const e=[...this.underConstruction,t].map(g).map(f);throw new w(e)}if(this.underConstruction.push(t),h(t))return[await t.useFactory(this.container)];if(l(t)||r(t)){const e=r(t)?()=>[new t]:()=>[new t.useClass];return async function(t,e,i){for(;;)try{return await e()}catch(n){if(!(n instanceof t))throw n;await i(n)}}(b,async()=>e(),async t=>{await this.container.getAsync(t.token,{multi:!0,optional:!0})})}return this.doConstruct(t)}finally{this.underConstruction.pop()}}doConstruct(t){return r(t)?[new t]:l(t)?[new t.useClass]:function(t){return"provide"in t&&"useValue"in t}(t)?[t.useValue]:c(t)?[t.useFactory(this.container)]:d(t)?this.container.get(t.useExisting,{multi:!0}):function(){throw new Error("invalid state")}()}}class b extends Error{token;constructor(t){super(`Some providers for token ${f(t)} are async, please use injectAsync() or container.getAsync() instead`),this.token=t}}class w extends Error{constructor(t){super(`Detected circular dependency: ${t.join(" -> ")}. Please change your dependency graph or use lazy injection instead.`)}}class y{container;constructor(t){this.container=t}run(t){const e=C;try{return C=this,t(this.container)}finally{C=e}}async runAsync(t){const e=C;try{return C=this,await t(this.container)}finally{C=e}}}let C=new class{run(){throw new k}runAsync(){throw new k}};function M(t){return new y(t)}class k extends Error{constructor(){super("You can only invoke inject() or injectAsync() within an injection context")}}class z{providers=new Map;singletons=new Map;parent;factory;constructor(t){this.parent=t,this.factory=new x(this),this.bind({provide:z,useValue:this})}bindAll(...t){return t.forEach(t=>this.bind(t)),this}bind(t){const e=g(t);if(d(t)&&t.provide===t.useExisting)throw Error(`The provider for token ${f(e)} with "useExisting" cannot refer to itself.`);if(!d(t)&&this.singletons.has(e))throw Error(`Cannot bind a new provider for ${f(e)}, since the existing provider was already constructed.`);if(d(t)&&p(t)&&this.existingProviderAlreadyProvided(e,t.useExisting))return this;const i=this.providers.get(e)??[],n=p(t);if(n&&i.some(t=>!p(t)))throw Error(`Cannot bind ${f(e)} as multi-provider, since there is already a provider which is not a multi-provider.`);if(!n&&i.some(t=>p(t))&&!i.every(d))throw Error(`Cannot bind ${f(e)} as provider, since there are already provider(s) that are multi-providers.`);return this.providers.set(e,n?[...i,t]:[t]),m(e)&&(l(t)||r(t))&&function(t,e=2){const i=[];return t.some((n,s)=>{if(s+e>t.length)return!0;i.push(t.slice(s,s+e))}),i}([e,...s(e)]).forEach(([t,e])=>{const i={provide:e,useExisting:t,multi:!0},n=this.providers.get(e)??[];this.existingProviderAlreadyProvided(e,t)||this.providers.set(e,[...n,i])}),this}unbind(t){const e=g(t);return this.providers.delete(e),this.singletons.delete(e),this}unbindAll(){return this.providers.clear(),this.singletons.clear(),this}get(t,e){if(e?.lazy??!1)return()=>this.get(t,{...e,lazy:!1});this.autoBindIfNeeded(t);const i=e?.optional??!1;if(!this.providers.has(t)){if(this.parent)return this.parent.get(t,{...e,lazy:!1});if(i)return;throw Error(`No provider(s) found for ${f(t)}`)}const n=o(this.providers.get(t));this.singletons.has(t)||M(this).run(()=>{const e=n.flatMap(e=>this.factory.construct(e,t));this.singletons.set(t,e)});const s=o(this.singletons.get(t));return e?.multi??!1?s:a(s,()=>Error(`Requesting a single value for ${f(t)}, but multiple values were provided. Consider passing "{ multi: true }" to inject all values, or adjust your bindings accordingly.`))}getAsync(t,e){return e?.lazy??!1?()=>this.getAsync(t,{...e,lazy:!1}):async function(t){return await new Promise(e=>e(t()))}(async()=>{this.autoBindIfNeeded(t);const i=e?.optional??!1;if(!this.providers.has(t)){if(i)return;throw Error(`No provider(s) found for ${f(t)}`)}const n=o(this.providers.get(t));this.singletons.has(t)||await M(this).runAsync(async()=>{const e=await Promise.all(n.map(t=>this.factory.constructAsync(t)));this.singletons.set(t,e.flat())});const s=o(this.singletons.get(t));return e?.multi??!1?s:a(s,()=>new Error(`Requesting a single value for ${f(t)}, but multiple values were provided. Consider passing "{ multi: true }" to inject all values, or adjust your bindings accordingly.`))})}createChild(){return new z(this)}has(t){return this.providers.has(t)||(this.parent?.has(t)??!1)}autoBindIfNeeded(t){if(!this.singletons.has(t))if(m(t)&&t.hasOwnProperty(v)){const e=function(t){return t[v]}(t);e.filter(t=>!this.providers.has(t)).forEach(t=>{this.bind({provide:t,useClass:t,multi:!0})})}else if(!this.providers.has(t)&&function(t){return t instanceof u}(t)&&t.options?.factory){const e=t.options.async;e?e&&this.bind({provide:t,async:!0,useFactory:t.options.factory}):this.bind({provide:t,async:!1,useFactory:t.options.factory})}}existingProviderAlreadyProvided(t,e){return(this.providers.get(t)??[]).some(i=>d(i)&&i.provide===t&&i.useExisting===e)}}const _={round:function(t,e){const i=10**e;return Math.round(t*i)/i},resizeCanvasForDPR:function(t,e,i){const n=window.devicePixelRatio||1,s=t.getContext("2d");if(!s)throw new Error("[JSONCanvasViewer] This error is unexpected, probably caused uncontrollable runtime errors. Please contact the developer and show how to reproduce.");t.width=Math.round(e*n),t.height=Math.round(i*n),s.setTransform(1,0,0,1,0,0),s.scale(n,n)},applyStyles:function(t,e){const i=document.createElement("style");i.innerHTML=e,t.appendChild(i)},drawRoundRect:function(t,e,i,n,s,o){t.beginPath(),t.moveTo(e+o,i),t.lineTo(e+n-o,i),t.quadraticCurveTo(e+n,i,e+n,i+o),t.lineTo(e+n,i+s-o),t.quadraticCurveTo(e+n,i+s,e+n-o,i+s),t.lineTo(e+o,i+s),t.quadraticCurveTo(e,i+s,e,i+s-o),t.lineTo(e,i+o),t.quadraticCurveTo(e,i,e+o,i),t.closePath()},getAnchorCoord:function(t,e){const i=t.x+t.width/2,n=t.y+t.height/2;switch(e){case"top":return[i,t.y];case"bottom":return[i,t.y+t.height];case"left":return[t.x,n];case"right":return[t.x+t.width,n];default:return[i,n]}},getColor:function(t="0"){let e=null;if(1===t.length)switch(t){case"1":e="rgba(255, 120, 129, ?)";break;case"2":e="rgba(251, 187, 131, ?)";break;case"3":e="rgba(255, 232, 139, ?)";break;case"4":e="rgba(124, 211, 124, ?)";break;case"5":e="rgba(134, 223, 226, ?)";break;case"6":e="rgba(203, 158, 255, ?)";break;default:e="rgba(140, 140, 140, ?)"}else{const i=function(t){const e=t.replace("#","");return{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16)}}(t);e=`rgba(${i.r}, ${i.g}, ${i.b}, ?)`}return{border:e.replace("?","0.75"),background:e.replace("?","0.1"),active:e.replace("?","1")}},resolvePath:function(t){if(/^https?:\/\//.test(t))return t.substring(0,t.lastIndexOf("/")+1);{const e=t.lastIndexOf("/");return-1!==e?t.substring(0,e+1):"./"}},makeHook:function(){const t=(...e)=>{t.subs.forEach(t=>{t(...e)})};return t.subs=new Set,t.subscribe=e=>{t.subs.add(e)},t.unsubscribe=e=>{t.subs.delete(e)},t}};const D=800;class E extends i{spatialGrid=null;hooks={onToggleFullscreen:_.makeHook(),onCanvasFetched:_.makeHook()};data={canvasData:void 0,nodeMap:{},canvasBaseDir:void 0,nodeBounds:void 0,offsetX:0,offsetY:0,scale:1,container:document.createElement("div")};loadCanvas=async()=>{const t=this.options.canvasPath;try{this.data.canvasBaseDir=_.resolvePath(t),this.data.canvasData=Object.assign({nodes:[],edges:[]},await fetch(t).then(t=>t.json())),this.data.canvasData.nodes.forEach(t=>{if("file"===t.type&&!t.file.includes("http")){const e=t.file.split("/");t.file=e[e.length-1]}this.data.nodeMap[t.id]=t}),this.data.nodeBounds=this.calculateNodeBounds(),this.buildSpatialGrid(),this.hooks.onCanvasFetched()}catch(e){console.error("Failed to load canvas data:",e)}};findNodeAt=t=>{const{x:e,y:i}=this.C2W(this.C2C({x:t.x,y:t.y}));let n=[];if(this.spatialGrid){const t=`${Math.floor(e/D)},${Math.floor(i/D)}`;n=this.spatialGrid[t]||[]}else n=this.data.canvasData.nodes;for(const s of n)if(!(e<s.x||e>s.x+s.width||i<s.y||i>s.y+s.height||"non-interactive"===this.judgeInteract(s)))return s;return null};judgeInteract=t=>{switch(t?.type){case"text":case"link":return"select";case"file":return t.file.match(/\.(md|wav|mp3)$/i)?"select":"non-interactive";default:return"non-interactive"}};calculateNodeBounds(){let t=1/0,e=1/0,i=-1/0,n=-1/0;this.data.canvasData.nodes.forEach(s=>{t=Math.min(t,s.x),e=Math.min(e,s.y),i=Math.max(i,s.x+s.width),n=Math.max(n,s.y+s.height)});const s=i-t,o=n-e;return{minX:t,minY:e,maxX:i,maxY:n,width:s,height:o,centerX:t+s/2,centerY:e+o/2}}buildSpatialGrid(){const t=this.data.canvasData;if(!(t.nodes.length<50)){this.spatialGrid={};for(const e of t.nodes){const t=Math.floor(e.x/D),i=Math.floor((e.x+e.width)/D),n=Math.floor(e.y/D),s=Math.floor((e.y+e.height)/D);for(let o=t;o<=i;o++)for(let t=n;t<=s;t++){const i=`${o},${t}`;this.spatialGrid[i]||(this.spatialGrid[i]=[]),this.spatialGrid[i].push(e)}}}}zoom=(t,e)=>{const i=this.data.scale*t;this.zoomToScale(i,e)};zoomToScale=(t,e)=>{const i=Math.max(Math.min(t,20),.05),n=this.data.scale;if(i===n)return;const s=this.C2C(e);this.data.offsetX=e.x-s.x*i/n,this.data.offsetY=e.y-s.y*i/n,this.data.scale=i};pan=({x:t,y:e})=>{this.data.offsetX=this.data.offsetX+t,this.data.offsetY=this.data.offsetY+e};panToCoords=({x:t,y:e})=>{this.data.offsetX=t,this.data.offsetY=e};shiftFullscreen=(t="toggle")=>{document.fullscreenElement||"toggle"!==t&&"enter"!==t?!document.fullscreenElement||"toggle"!==t&&"exit"!==t||(document.exitFullscreen(),this.hooks.onToggleFullscreen(!1)):(this.data.container.requestFullscreen(),this.hooks.onToggleFullscreen(!0))};resetView=()=>{const t=this.data.nodeBounds,e=this.data.container;if(!t||!e)return;const i=t.width+200,n=t.height+200,s=e.clientWidth,o=e.clientHeight,a=s/i,r=o/n,l=Math.round(1e3*Math.min(a,r))/1e3,c={scale:l,offsetX:s/2-t.centerX*l,offsetY:o/2-t.centerY*l};this.data.offsetX=c.offsetX,this.data.offsetY=c.offsetY,this.data.scale=c.scale};C2C=({x:t,y:e})=>({x:t-this.data.offsetX,y:e-this.data.offsetY});C2W=({x:t,y:e})=>({x:t/this.data.scale,y:e/this.data.scale});middleViewer=()=>{const t=this.data.container;return{x:t.clientWidth/2,y:t.clientHeight/2,width:t.clientWidth,height:t.clientHeight}};dispose=()=>{this.data.container.remove()}}class B extends i{animationId=null;resizeAnimationId=null;DM;perFrame={lastScale:1,lastOffsets:{x:0,y:0}};lastResizeCenter={x:null,y:null};hooks={onResize:_.makeHook(),onRefresh:_.makeHook()};constructor(...t){super(...t),this.DM=this.container.get(E),this.DM.hooks.onCanvasFetched.subscribe(this.onFetched);const e=this.options.container;for(;e.firstElementChild;)e.firstElementChild.remove();e.innerHTML="";const i=this.options.noShadow||!1?e:e.attachShadow({mode:"open"});_.applyStyles(i,".full,.click-layer,.link-iframe,.audio{top:0;left:0;width:100%;height:100%;position:absolute}.flex-center,.overlay-container.markdown-content{display:flex;justify-content:center;align-items:center}.container{--contentTransition: color .2s, opacity .2s, text-shadow .2s, fill .2s;--containerTransition: background .2s, opacity .2s, box-shadow .2s, border .2s, filter .2s, backdrop-filter .2s;color:#fff;fill:#fff;stroke:#fff;position:relative;width:100%;height:100%;overflow:hidden;background-color:#141414}.container.numb,.container.numb *{pointer-events:none!important}.main-canvas{width:100%;height:100%;transform-origin:top left}.overlays{position:absolute;top:0;left:0;width:100%;height:100%;transform-origin:top left;will-change:transform}.parsed-content-wrapper{font-family:sans-serif;box-sizing:border-box;max-width:100%;max-height:100%;padding:10px 6px;pointer-events:none;overflow:hidden;scrollbar-gutter:stable both-edges;display:flex;flex-direction:column;gap:12px}@supports not (scrollbar-gutter: stable both-edges){.parsed-content-wrapper{padding:10px}}.overlay-container{position:absolute;box-sizing:border-box;border-radius:12px;overflow:hidden;-webkit-user-select:none;user-select:none;contain:strict;content-visibility:auto}.overlay-container:hover{box-shadow:0 2px 12px #00000080}.overlay-container{transition:var(--containerTransition)}.overlay-container .overlay-border{box-sizing:border-box;pointer-events:none;position:absolute;top:0;left:0;width:100%;height:100%;border-width:2px;border-style:solid;border-radius:12px;transition:var(--containerTransition)}.overlay-container img{width:100%;height:100%;object-fit:cover;pointer-events:none}.overlay-container.active .overlay-border{border:6px solid var(--active-color)}.overlay-container.markdown-content{position:absolute;padding:0 7px}.overlay-container.markdown-content.active .parsed-content-wrapper{overflow:auto;-webkit-user-select:text;user-select:text;pointer-events:auto}.overlay-container.markdown-content.rtl{direction:rtl;text-align:right}.link-iframe,.audio{border:none;background:transparent}.click-layer{background:transparent;pointer-events:auto}.active .click-layer{pointer-events:none}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background-color:transparent}::-webkit-scrollbar-thumb{border-radius:2px;background:#ffffff40}::-webkit-scrollbar-thumb:hover{background:#1e1e1ebf}p{font-size:16px;line-height:21px}.parsed-content-wrapper img{width:100%;border-radius:8px}h1{font-size:25px}h2{font-size:23px}h3{font-size:22px}h4{font-size:20px}h5{font-size:19px}h6{font-size:17px}p,h1,h2,h3,h4,h5,h6,ol,ul{margin:0}h1,h2{font-weight:800}h3,h4{font-weight:700}h5,h6{font-weight:600}code{background:#ffffff1a;padding:2px 4px;border-radius:8px}pre code{display:block;box-sizing:border-box;width:100%}pre:has(code),table{margin:6px 0}strong{color:#fe8e7c}em{color:#5affb2}a{text-decoration:none;color:#6dadd0;font-weight:800;font-style:italic;cursor:pointer;transition:var(--contentTransition)}a:hover{color:#86d3fd}hr{height:1px;width:100%;background-color:#fff3;border:none}li{margin:5px 0}ul{padding-left:16px}ol{padding-left:15px;padding-right:7.5px}table{border-collapse:collapse;border-radius:8px;overflow:hidden;width:100%}table th,table td{border:1px solid rgba(255,255,255,.2);padding:6px 10px;background:#ffffff0f;text-align:left}table th{background:#ffffff1f;font-weight:700}");const n=this.DM.data.container;n.classList.add("container"),i.appendChild(n)}onFetched=()=>{this.DM.resetView(),this.resizeObserver.observe(this.DM.data.container),this.animationId=requestAnimationFrame(this.draw)};draw=()=>{this.perFrame.lastScale===this.DM.data.scale&&this.perFrame.lastOffsets.x===this.DM.data.offsetX&&this.perFrame.lastOffsets.y===this.DM.data.offsetY||this.refresh(),this.animationId=requestAnimationFrame(this.draw)};refresh=()=>{this.perFrame.lastScale=this.DM.data.scale,this.perFrame.lastOffsets={x:this.DM.data.offsetX,y:this.DM.data.offsetY},this.hooks.onRefresh()};onResize=()=>{this.resizeAnimationId=requestAnimationFrame(()=>{const t=this.DM.middleViewer();this.lastResizeCenter.x&&this.lastResizeCenter.y&&(this.DM.data.offsetX=this.DM.data.offsetX+t.x-this.lastResizeCenter.x,this.DM.data.offsetY=this.DM.data.offsetY+t.y-this.lastResizeCenter.y),this.lastResizeCenter.x=t.x,this.lastResizeCenter.y=t.y,this.hooks.onResize(t.width,t.height),this.refresh()})};resizeObserver=new ResizeObserver(this.onResize);dispose=()=>{this.animationId&&cancelAnimationFrame(this.animationId),this.resizeAnimationId&&cancelAnimationFrame(this.resizeAnimationId),this.resizeObserver.disconnect()}}const P=new Error("[JSONCanvasViewer] Resource hasn't been set up or has been disposed.");class S extends i{_overlaysLayer=document.createElement("div");overlays={};selectedId=null;eventListeners={};DM;IH;parse;get overlaysLayer(){if(!this._overlaysLayer)throw P;return this._overlaysLayer}hooks={onInteractionStart:_.makeHook(),onInteractionEnd:_.makeHook()};constructor(...t){super(...t),this.parse=t=>e.micromark(t,this.options.micromark),this.DM=this.container.get(E),this.IH=this.container.get(I,{lazy:!0});const i=this.container.get(B);this.DM.hooks.onCanvasFetched.subscribe(this.onFetched),i.hooks.onRefresh.subscribe(this.updateOverlays),this._overlaysLayer=document.createElement("div"),this._overlaysLayer.className="overlays",this.DM.data.container.appendChild(this.overlaysLayer)}onFetched=()=>{this.IH().onClick.subscribe(this.select);const t=this.DM.data.canvasBaseDir,e=async e=>{switch(e.type){case"text":this.updateOverlay(e,e.text,"text");break;case"file":e.file.match(/\.md$/i)?this.loadMarkdownForNode(e):e.file.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)?this.updateOverlay(e,t+e.file,"image"):e.file.match(/\.(mp3|wav)$/i)&&this.updateOverlay(e,t+e.file,"audio");break;case"link":this.updateOverlay(e,e.url,"link")}};Object.values(this.DM.data.nodeMap).forEach(t=>{e(t)})};select=t=>{const e=this.selectedId?this.overlays[this.selectedId]:null,i=t?this.overlays[t]:null;e&&e.classList.remove("active"),i?(i.classList.add("active"),this.hooks.onInteractionStart()):this.hooks.onInteractionEnd(),this.selectedId=t};loadMarkdownForNode=async t=>{let e;this.updateOverlay(t,"Loading...","text");try{const i=await fetch(this.DM.data.canvasBaseDir+t.file),n=await i.text(),s=n.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);e=s?this.parse(s[2]):this.parse(n)}catch(i){console.error("[JSONCanvasViewer] Failed to load markdown:",i),e="Failed to load content."}this.updateOverlay(t,e,"text")};updateOverlays=()=>{const t=this.DM.data;this.overlaysLayer.style.transform=`translate(${t.offsetX}px, ${t.offsetY}px) scale(${t.scale})`};updateOverlay(t,e,i){let n=this.overlays[t.id];if(n){if("text"===i){n.getElementsByClassName("parsed-content-wrapper")[0].innerHTML=e}}else n=this.constructOverlay(t,e,i),this.overlaysLayer.appendChild(n),this.overlays[t.id]=n,n.style.left=`${t.x}px`,n.style.top=`${t.y}px`,n.style.width=`${t.width}px`,n.style.height=`${t.height}px`}constructOverlay(t,e,i){const n=_.getColor(t.color),s=document.createElement("div");switch(s.classList.add("overlay-container"),s.id=t.id,s.style.backgroundColor=n.background,s.style.setProperty("--active-color",n.active),i){case"text":{s.classList.add("markdown-content");const t=document.createElement("div");t.innerHTML=this.parse(e||""),t.classList.add("parsed-content-wrapper"),s.appendChild(t);break}case"link":{const t=document.createElement("iframe");t.src=e,t.sandbox="allow-scripts allow-same-origin",t.className="link-iframe",t.loading="lazy",s.appendChild(t);break}case"audio":{const t=document.createElement("audio");t.className="audio",t.src=e,t.controls=!0,s.appendChild(t);break}case"image":{const t=document.createElement("img");t.src=e,t.loading="lazy",s.appendChild(t)}}switch(i){case"link":case"audio":{const t=document.createElement("div");t.className="click-layer",s.appendChild(t)}}const o=document.createElement("div");o.className="overlay-border",o.style.borderColor=n.border,s.appendChild(o);const a=()=>{t.id===this.selectedId&&this.hooks.onInteractionStart()},r=()=>{t.id===this.selectedId&&this.hooks.onInteractionEnd()};return s.addEventListener("pointerenter",a),s.addEventListener("pointerleave",r),s.addEventListener("touchstart",a),s.addEventListener("touchend",r),this.eventListeners[t.id]=[a,r],s}dispose=()=>{for(;this.overlaysLayer.firstElementChild;){const t=this.overlaysLayer.firstElementChild;if(this.eventListeners[t.id]){const e=this.eventListeners[t.id][0],i=this.eventListeners[t.id][1];if(!e||!i)throw P;t.removeEventListener("pointerenter",e),t.removeEventListener("pointerleave",i),t.removeEventListener("touchstart",e),t.removeEventListener("touchend",i),this.eventListeners[t.id][0]=null,this.eventListeners[t.id][1]=null}t.remove()}this.overlaysLayer.remove(),this._overlaysLayer=null}}class I extends i{pointeract;DM;onClick=_.makeHook();constructor(...e){super(...e),this.DM=this.container.get(E);const i=Object.assign(this.options.pointeract||{},{coordinateOutput:"relative"});this.pointeract=new t.Pointeract(this.DM.data.container,[t.Click,t.Drag,t.WheelPanZoom,t.PreventDefault,t.MultitouchPanZoom],i),this.startInteraction=this.pointeract.start,this.stopInteraction=this.pointeract.stop;const n=this.container.get(S);n.hooks.onInteractionStart.subscribe(this.stopInteraction),n.hooks.onInteractionEnd.subscribe(this.startInteraction),this.DM.hooks.onCanvasFetched.subscribe(this.onFetched)}stopInteraction;startInteraction;onFetched=()=>{this.pointeract.on("pan",this.onPan),this.pointeract.on("drag",this.onPan),this.pointeract.on("zoom",this.onZoom),this.pointeract.on("trueClick",this.onTrueClick),this.pointeract.start()};onPan=t=>{this.DM.pan(t.detail)};onZoom=t=>{const e=t.detail;this.DM.zoom(e.factor,{x:e.x,y:e.y})};onTrueClick=t=>{const e=t.detail;if((i=t.detail.target)&&(i.closest(".controls")||i.closest("button")||i.closest("input")))return;var i;const n=this.DM.findNodeAt({x:e.x,y:e.y});this.onClick(n?n.id:null)};dispose=()=>{this.pointeract.off("pan",this.onPan),this.pointeract.off("zoom",this.onZoom),this.pointeract.off("trueClick",this.onTrueClick),this.pointeract.dispose()}}const L="#fff";class T extends i{_canvas;ctx;DM;zoomInOptimize={lastDrawnScale:0,lastDrawnViewport:{left:0,right:0,top:0,bottom:0},timeout:null,lastCallTime:0};get canvas(){if(null===this._canvas)throw P;return this._canvas}constructor(...t){super(...t);const e=this.container.get(B);e.hooks.onRefresh.subscribe(this.redraw),e.hooks.onResize.subscribe(this.optimizeDPR),this.DM=this.container.get(E),this._canvas=document.createElement("canvas"),this._canvas.className="main-canvas",this.ctx=this._canvas.getContext("2d"),this.DM.data.container.appendChild(this._canvas)}optimizeDPR=()=>{const t=this.DM.data.container;_.resizeCanvasForDPR(this.canvas,t.offsetWidth,t.offsetHeight)};redraw=()=>{this.zoomInOptimize.timeout&&(clearTimeout(this.zoomInOptimize.timeout),this.zoomInOptimize.timeout=null);const t=Date.now(),e=this.DM.data.offsetX,i=this.DM.data.offsetY,n=this.DM.data.scale,s=this.getCurrentViewport(e,i,n);if(this.isViewportInside(s,this.zoomInOptimize.lastDrawnViewport)&&n!==this.zoomInOptimize.lastDrawnScale){if(t-this.zoomInOptimize.lastCallTime<500)return this.zoomInOptimize.timeout=setTimeout(()=>{this.trueRedraw(e,i,n,s),this.zoomInOptimize.lastCallTime=t,this.zoomInOptimize.timeout=null},60),void this.fakeRedraw(s,n)}this.zoomInOptimize.lastCallTime=t,this.trueRedraw(e,i,n,s)};trueRedraw(t,e,i,n){this.zoomInOptimize.lastDrawnViewport=n,this.zoomInOptimize.lastDrawnScale=i,this.canvas.style.transform="",this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.save(),this.ctx.translate(t,e),this.ctx.scale(i,i);const s=this.DM.data.canvasData;s.nodes.forEach(t=>{switch(t.type){case"group":this.drawGroup(t,i);break;case"file":this.drawFileNode(t)}}),s.edges.forEach(t=>{this.drawEdge(t)}),this.ctx.restore()}fakeRedraw(t,e){const i=e/this.zoomInOptimize.lastDrawnScale,n=(this.zoomInOptimize.lastDrawnViewport.left-t.left)*e,s=(this.zoomInOptimize.lastDrawnViewport.top-t.top)*e;this.canvas.style.transform=`translate(${n}px, ${s}px) scale(${i})`}isViewportInside=(t,e)=>t.left>e.left&&t.top>e.top&&t.right<e.right&&t.bottom<e.bottom;getCurrentViewport=(t,e,i)=>{const n=-t/i,s=-e/i,o=this.DM.data.container;return{left:n,top:s,right:n+o.clientWidth/i,bottom:s+o.clientHeight/i}};drawLabelBar=(t,e,i,n,s)=>{const o=30*s,a=6*s,r=8*s,l=16*s,c=6*s;this.ctx.save(),this.ctx.translate(t,e),this.ctx.scale(1/s,1/s),this.ctx.font=`${l}px 'Inter', sans-serif`;const h=this.ctx.measureText(i).width+2*c;this.ctx.translate(0,-o-r),this.ctx.fillStyle=n,this.ctx.beginPath(),this.ctx.moveTo(a,0),this.ctx.lineTo(h-a,0),this.ctx.quadraticCurveTo(h,0,h,a),this.ctx.lineTo(h,o-a),this.ctx.quadraticCurveTo(h,o,h-a,o),this.ctx.lineTo(a,o),this.ctx.quadraticCurveTo(0,o,0,o-a),this.ctx.lineTo(0,a),this.ctx.quadraticCurveTo(0,0,a,0),this.ctx.closePath(),this.ctx.fill(),this.ctx.fillStyle=L,this.ctx.fillText(i,c,.65*o),this.ctx.restore()};drawNodeBackground=t=>{const e=_.getColor(t.color);this.ctx.globalAlpha=1,this.ctx.fillStyle=e.background,_.drawRoundRect(this.ctx,t.x+1,t.y+1,t.width-2,t.height-2,12),this.ctx.fill(),this.ctx.strokeStyle=e.border,this.ctx.lineWidth=2,_.drawRoundRect(this.ctx,t.x,t.y,t.width,t.height,12),this.ctx.stroke()};drawGroup=(t,e)=>{this.drawNodeBackground(t),t.label&&this.drawLabelBar(t.x,t.y,t.label,_.getColor(t.color).border,e)};drawFileNode=t=>{this.ctx.fillStyle=L,this.ctx.font="16px sans-serif",this.ctx.fillText(t.file,t.x+5,t.y-10)};drawEdge=t=>{const{fromNode:e,toNode:i}=this.getEdgeNodes(t),n=_.getAnchorCoord,[s,o]=n(e,t.fromSide),[a,r]=n(i,t.toSide);let[l,c,h,d]=[0,0,0,0];if(t.controlPoints?[l,c,h,d]=t.controlPoints:([l,c,h,d]=this.getControlPoints(s,o,a,r,t.fromSide,t.toSide),t.controlPoints=[l,c,h,d]),this.drawCurvedPath(s,o,a,r,l,c,h,d),this.drawArrowhead(a,r,h,d),t.label){const e=.5,i=(1-e)**3*s+3*(1-e)**2*e*l+3*(1-e)*e*e*h+e**3*a,n=(1-e)**3*o+3*(1-e)**2*e*c+3*(1-e)*e*e*d+e**3*r;this.ctx.font="18px sans-serif";const p=8,u=this.ctx.measureText(t.label).width+2*p,m=20;this.ctx.fillStyle="#222",this.ctx.beginPath(),_.drawRoundRect(this.ctx,i-u/2,n-m/2-2,u,m,4),this.ctx.fill(),this.ctx.fillStyle="#ccc",this.ctx.textAlign="center",this.ctx.textBaseline="middle",this.ctx.fillText(t.label,i,n-2),this.ctx.textAlign="left",this.ctx.textBaseline="alphabetic"}};getEdgeNodes=t=>({fromNode:this.DM.data.nodeMap[t.fromNode],toNode:this.DM.data.nodeMap[t.toNode]});getControlPoints=(t,e,i,n,s,o)=>{const a=i-t,r=n-e,l=Math.min(Math.abs(a),Math.abs(r))+.3*Math.max(Math.abs(a),Math.abs(r)),c=(h=.5*l,d=60,p=300,Math.max(d,Math.min(p,h)));var h,d,p;let u=t,m=e,f=i,g=n;switch(s){case"top":m=e-c;break;case"bottom":m=e+c;break;case"left":u=t-c;break;case"right":u=t+c}switch(o){case"top":g=n-c;break;case"bottom":g=n+c;break;case"left":f=i-c;break;case"right":f=i+c}return[u,m,f,g]};drawCurvedPath=(t,e,i,n,s,o,a,r)=>{this.ctx.beginPath(),this.ctx.moveTo(t,e),this.ctx.bezierCurveTo(s,o,a,r,i,n),this.ctx.strokeStyle="#ccc",this.ctx.lineWidth=2,this.ctx.stroke()};drawArrowhead=(t,e,i,n)=>{const s=t-i,o=e-n,a=Math.sqrt(s*s+o*o);if(0===a)return;const r=s/a,l=o/a,c=t-12*r-7*l,h=e-12*l+7*r,d=t-12*r+7*l,p=e-12*l-7*r;this.ctx.beginPath(),this.ctx.fillStyle="#ccc",this.ctx.moveTo(t,e),this.ctx.lineTo(c,h),this.ctx.lineTo(d,p),this.ctx.closePath(),this.ctx.fill()};dispose=()=>{this.zoomInOptimize.timeout&&(clearTimeout(this.zoomInOptimize.timeout),this.zoomInOptimize.timeout=null),this.canvas.remove(),this._canvas=null}}const O={Controller:B,DataManager:E,InteractionHandler:I,Renderer:T,OverlayManager:S};exports.BaseModule=i,exports.CanvasUtils=_,exports.Controls=class extends i{_controlsPanel=null;_toggleCollapseBtn=null;_toggleFullscreenBtn=null;_zoomOutBtn=null;_zoomSlider=null;_zoomInBtn=null;_resetViewBtn=null;DM;collapsed;get controlsPanel(){if(null===this._controlsPanel)throw P;return this._controlsPanel}get toggleCollapseBtn(){if(null===this._toggleCollapseBtn)throw P;return this._toggleCollapseBtn}get toggleFullscreenBtn(){if(null===this._toggleFullscreenBtn)throw P;return this._toggleFullscreenBtn}get zoomOutBtn(){if(null===this._zoomOutBtn)throw P;return this._zoomOutBtn}get zoomSlider(){if(null===this._zoomSlider)throw P;return this._zoomSlider}get zoomInBtn(){if(null===this._zoomInBtn)throw P;return this._zoomInBtn}get resetViewBtn(){if(null===this._resetViewBtn)throw P;return this._resetViewBtn}constructor(...t){super(...t),this.collapsed=this.options.controlsCollapsed||!1,this.DM=this.container.get(E),this.DM.hooks.onToggleFullscreen.subscribe(this.updateFullscreenBtn),this.container.get(B).hooks.onRefresh.subscribe(this.updateSlider),this._controlsPanel=document.createElement("div"),this._controlsPanel.className="controls",this._controlsPanel.classList.toggle("collapsed",this.collapsed),_.applyStyles(this._controlsPanel,".collapse-button{border-radius:8px;transition:transform .2s}.collapse-button:hover{background:#444c}button{cursor:pointer;font-size:18px;height:32px;border:none;transition:var(--containerTransition);text-align:center;background-color:#444;width:32px;padding:5px 0}button svg{width:100%;height:100%}.controls{position:absolute;top:10px;right:10px;display:flex;align-items:center;transition:transform .2s;border-radius:8px;gap:10px}.controls.collapsed{transform:translate(calc(100% - 32px))}.controls.collapsed .collapse-button{transform:rotate(180deg)}.controls .controls-content{display:flex;gap:1px;align-items:center;border-radius:8px;overflow:hidden;background:#333c}.controls button:hover{background:#555}.zoom-slider{width:100px;margin:0 10px}"),this._toggleCollapseBtn=document.createElement("button"),this._toggleCollapseBtn.className="collapse-button",this._toggleCollapseBtn.innerHTML='<svg viewBox="-3.6 -3.6 31.2 31.2" stroke-width=".4"><path d="M15.707 4.293a1 1 0 0 1 0 1.414L9.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414l-7-7a1 1 0 0 1 0-1.414l7-7a1 1 0 0 1 1.414 0Z" /></svg>',this._controlsPanel.appendChild(this._toggleCollapseBtn);const e=document.createElement("div");e.className="controls-content",this._toggleFullscreenBtn=document.createElement("button"),this._toggleFullscreenBtn.innerHTML='<svg viewBox="-5.28 -5.28 34.56 34.56" fill="none"><path d="M4 9V5.6c0-.56 0-.84.109-1.054a1 1 0 0 1 .437-.437C4.76 4 5.04 4 5.6 4H9M4 15v3.4c0 .56 0 .84.109 1.054a1 1 0 0 0 .437.437C4.76 20 5.04 20 5.6 20H9m6-16h3.4c.56 0 .84 0 1.054.109a1 1 0 0 1 .437.437C20 4.76 20 5.04 20 5.6V9m0 6v3.4c0 .56 0 .84-.109 1.054a1 1 0 0 1-.437.437C19.24 20 18.96 20 18.4 20H15" stroke-width="2.4" stroke-linecap="round"/></svg>',e.appendChild(this._toggleFullscreenBtn),this._zoomOutBtn=document.createElement("button"),this._zoomOutBtn.innerHTML='<svg viewBox="-1.2 -1.2 26.4 26.4"><path d="M6 12h12" stroke-width="2" stroke-linecap="round" /></svg>',e.appendChild(this._zoomOutBtn),this._zoomSlider=document.createElement("input"),this._zoomSlider.type="range",this._zoomSlider.className="zoom-slider",this._zoomSlider.min="-30",this._zoomSlider.max="30",this._zoomSlider.value="0",e.appendChild(this._zoomSlider),this._zoomInBtn=document.createElement("button"),this._zoomInBtn.innerHTML='<svg viewBox="-1.2 -1.2 26.4 26.4"><path d="M6 12h12m-6-6v12" stroke-width="2" stroke-linecap="round" /></svg>',e.appendChild(this._zoomInBtn),this._resetViewBtn=document.createElement("button"),this._resetViewBtn.innerHTML='<svg viewBox="-6 -6 30 30" stroke-width=".08"><path d="m14.955 7.986.116.01a1 1 0 0 1 .85 1.13 8 8 0 0 1-13.374 4.728l-.84.84c-.63.63-1.707.184-1.707-.707V10h3.987c.89 0 1.337 1.077.707 1.707l-.731.731a6 6 0 0 0 8.347-.264 6 6 0 0 0 1.63-3.33 1 1 0 0 1 1.131-.848zM11.514.813a8 8 0 0 1 1.942 1.336l.837-.837c.63-.63 1.707-.184 1.707.707V6h-3.981c-.89 0-1.337-1.077-.707-1.707l.728-.729a6 6 0 0 0-9.98 3.591 1 1 0 1 1-1.98-.281A8 8 0 0 1 11.514.813Z" /></svg>',e.appendChild(this._resetViewBtn),this._controlsPanel.appendChild(e),this.DM.data.container.appendChild(this._controlsPanel),this._toggleCollapseBtn.addEventListener("click",this.toggleCollapse),this._zoomInBtn.addEventListener("click",this.zoomIn),this._zoomOutBtn.addEventListener("click",this.zoomOut),this._zoomSlider.addEventListener("input",this.slide),this._resetViewBtn.addEventListener("click",this.DM.resetView),this._toggleFullscreenBtn.addEventListener("click",this.toggleFullscreen)}toggleCollapse=()=>{this.collapsed=!this.collapsed,this.controlsPanel.classList.toggle("collapsed",this.collapsed),this.collapsed||this.updateSlider()};zoomIn=()=>this.DM.zoom(1.1,this.DM.middleViewer());zoomOut=()=>this.DM.zoom(1/1.1,this.DM.middleViewer());slide=()=>this.DM.zoomToScale(1.1**Number(this.zoomSlider.value),this.DM.middleViewer());updateFullscreenBtn=t=>{this.toggleFullscreenBtn.innerHTML=t?'<svg viewBox="-5.28 -5.28 34.56 34.56" fill="none"><path d="M4 9V5.6c0-.56 0-.84.109-1.054a1 1 0 0 1 .437-.437C4.76 4 5.04 4 5.6 4H9M4 15v3.4c0 .56 0 .84.109 1.054a1 1 0 0 0 .437.437C4.76 20 5.04 20 5.6 20H9m6-16h3.4c.56 0 .84 0 1.054.109a1 1 0 0 1 .437.437C20 4.76 20 5.04 20 5.6V9m0 6v3.4c0 .56 0 .84-.109 1.054a1 1 0 0 1-.437.437C19.24 20 18.96 20 18.4 20H15" stroke-width="2.4" stroke-linecap="round"/></svg>':'<svg viewBox="-40.32 -40.32 176.64 176.64"><path d="M30 60H6a6 6 0 0 0 0 12h18v18a6 6 0 0 0 12 0V66a5.997 5.997 0 0 0-6-6Zm60 0H66a5.997 5.997 0 0 0-6 6v24a6 6 0 0 0 12 0V72h18a6 6 0 0 0 0-12ZM66 36h24a6 6 0 0 0 0-12H72V6a6 6 0 0 0-12 0v24a5.997 5.997 0 0 0 6 6ZM30 0a5.997 5.997 0 0 0-6 6v18H6a6 6 0 0 0 0 12h24a5.997 5.997 0 0 0 6-6V6a5.997 5.997 0 0 0-6-6Z"/></svg>'};toggleFullscreen=()=>this.DM.shiftFullscreen("toggle");updateSlider=()=>{this.collapsed||(this.zoomSlider.value=String(this.scaleToSlider(this.DM.data.scale)))};scaleToSlider=t=>Math.log(t)/Math.log(1.1);dispose=()=>{this.toggleCollapseBtn.removeEventListener("click",this.toggleCollapse),this.zoomInBtn.removeEventListener("click",this.zoomIn),this.zoomOutBtn.removeEventListener("click",this.zoomOut),this.zoomSlider.removeEventListener("input",this.slide),this.resetViewBtn.removeEventListener("click",this.DM.resetView),this.toggleFullscreenBtn.removeEventListener("click",this.toggleFullscreen),this.controlsPanel.remove(),this._controlsPanel=null,this._toggleCollapseBtn=null,this._zoomInBtn=null,this._zoomOutBtn=null,this._zoomSlider=null,this._resetViewBtn=null,this._toggleFullscreenBtn=null}},exports.DebugPanel=class extends i{_debugPanel=null;DM;get debugPanel(){if(!this._debugPanel)throw P;return this._debugPanel}constructor(...t){super(...t),this.DM=this.container.get(E),this.container.get(B).hooks.onRefresh.subscribe(this.update),this._debugPanel=document.createElement("div"),this._debugPanel.className="debug-panel";const e=this.DM.data.container;_.applyStyles(e,".debug-panel{position:absolute;bottom:12px;left:12px;background:#0006;border-radius:12px;padding:12px;-webkit-backdrop-filter:blur(8px) saturate(1.5);backdrop-filter:blur(8px) saturate(1.5);border:2px solid rgba(140,140,140,.75);color:#fff;font-size:calc(14px + .3vw);line-height:calc(17px + .3vw);pointer-events:none}"),e.appendChild(this._debugPanel)}update=()=>{const t=_.round,e=this.DM.data;this.debugPanel.innerHTML=`<p>Scale: ${t(e.scale,3)}</p><p>Offset: ${t(e.offsetX,1)}, ${t(e.offsetY,1)}</p>`};dispose=()=>{this.debugPanel.remove(),this._debugPanel=null}},exports.JSONCanvasViewer=class{options;allModules;container;constructor(t,e){this.container=new z,this.options=t;this.allModules=[E,B,S,I,T,...e||[]],this.allModules.forEach(t=>{this.container.bind({provide:t,useFactory:()=>new t(this.container,this.options)})}),this.allModules.forEach(t=>{this.container.get(t)}),this.container.get(E).loadCanvas()}dispose=()=>{const t=this.options.container;for(;t.firstChild;)t.firstChild.remove();this.allModules.reverse(),this.allModules.forEach(t=>{const e=this.container.get(t);e.dispose&&e.dispose()})}},exports.Minimap=class extends i{_minimapCtx=null;_viewportRectangle=null;_minimap=null;_minimapContainer=null;_toggleMinimapBtn=null;minimapCache={scale:1,centerX:0,centerY:0};DM;collapsed;get minimap(){if(null===this._minimap)throw P;return this._minimap}get minimapCtx(){if(null===this._minimapCtx)throw P;return this._minimapCtx}get viewportRectangle(){if(null===this._viewportRectangle)throw P;return this._viewportRectangle}get minimapContainer(){if(null===this._minimapContainer)throw P;return this._minimapContainer}get toggleMinimapBtn(){if(null===this._toggleMinimapBtn)throw P;return this._toggleMinimapBtn}constructor(...t){super(...t),this.collapsed=this.options.minimapCollapsed||!1,this.container.get(B).hooks.onRefresh.subscribe(this.updateViewportRectangle),this.DM=this.container.get(E),this.DM.hooks.onCanvasFetched.subscribe(this.drawMinimap),this._minimapContainer=document.createElement("div"),this._minimapContainer.className="minimap-container",_.applyStyles(this._minimapContainer,".collapse-button{border-radius:8px;transition:transform .2s}.collapse-button:hover{background:#444c}button{cursor:pointer;font-size:18px;height:32px;border:none;transition:var(--containerTransition);text-align:center;background-color:#444;width:32px;padding:5px 0}button svg{width:100%;height:100%}.minimap-container{position:absolute;bottom:10px;right:10px;display:flex;pointer-events:none;transition:transform .2s}.minimap-container.collapsed{transform:translate(calc(100% - 32px))}@media(max-width:768px){.minimap-container{transform:translateY(60px) translate(80px)}.minimap-container.collapsed{transform:translateY(60px) translate(calc(100% - 32px))}}.toggle-minimap{margin:auto 10px 0 0;pointer-events:auto}.collapsed .toggle-minimap{transform:rotate(180deg)}@media(max-width:768px){.toggle-minimap{transform:translateY(-60px)}.collapsed .toggle-minimap{transform:translateY(-60px) rotate(180deg)}}.minimap{position:relative;width:200px;height:150px;overflow:hidden;border-radius:12px;background:#202020;-webkit-backdrop-filter:blur(8px) saturate(1.5);backdrop-filter:blur(8px) saturate(1.5);border:2px solid rgba(140,140,140,.75);transform-origin:0 0}@media(max-width:768px){.minimap{transform:scale(.6)}}.minimap .minimap-canvas{width:100%;height:100%}.minimap .viewport-rectangle{position:absolute;top:0;left:0;pointer-events:none;border:2px solid #fff;border-radius:6px;box-sizing:border-box;background:transparent}"),this._toggleMinimapBtn=document.createElement("button"),this._toggleMinimapBtn.className="toggle-minimap collapse-button",this._toggleMinimapBtn.innerHTML='<svg viewBox="-3.6 -3.6 31.2 31.2" stroke-width=".4"><path d="M15.707 4.293a1 1 0 0 1 0 1.414L9.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414l-7-7a1 1 0 0 1 0-1.414l7-7a1 1 0 0 1 1.414 0Z" /></svg>',this._minimapContainer.appendChild(this._toggleMinimapBtn),this._minimap=document.createElement("div"),this._minimap.className="minimap";const e=document.createElement("canvas");e.className="minimap-canvas",e.width=200,e.height=150,this._minimap.appendChild(e),this._minimapCtx=e.getContext("2d"),this._viewportRectangle=document.createElement("div"),this._viewportRectangle.className="viewport-rectangle",this._minimap.appendChild(this._viewportRectangle),this._minimapContainer.appendChild(this._minimap),this.DM.data.container.appendChild(this._minimapContainer),this._minimapContainer.classList.toggle("collapsed",this.collapsed),this._toggleMinimapBtn.addEventListener("click",this.toggleCollapse),_.resizeCanvasForDPR(e,e.width,e.height)}toggleCollapse=()=>{this.collapsed=!this.collapsed,this.minimapContainer.classList.toggle("collapsed",this.collapsed),this.collapsed||this.updateViewportRectangle()};drawMinimap=()=>{const t=this.DM.data.nodeBounds;if(!t)return;const e=this.minimap.clientWidth,i=this.minimap.clientHeight,n=e/t.width,s=i/t.height;this.minimapCache.scale=.9*Math.min(n,s),this.minimapCache.centerX=e/2,this.minimapCache.centerY=i/2,this.minimapCtx.clearRect(0,0,e,i),this.minimapCtx.save(),this.minimapCtx.translate(this.minimapCache.centerX,this.minimapCache.centerY),this.minimapCtx.scale(this.minimapCache.scale,this.minimapCache.scale),this.minimapCtx.translate(-t.centerX,-t.centerY);const o=this.DM.data.canvasData;for(const a of o.edges)this.drawMinimapEdge(a);for(const a of o.nodes)this.drawMinimapNode(a);this.minimapCtx.restore()};drawMinimapNode=t=>{const e=_.getColor(t.color);this.minimapCtx.fillStyle=e.border,this.minimapCtx.globalAlpha=.3,_.drawRoundRect(this.minimapCtx,t.x,t.y,t.width,t.height,25),this.minimapCtx.fill(),this.minimapCtx.globalAlpha=1};drawMinimapEdge=t=>{const e=this.DM.data.nodeMap,i=e[t.fromNode],n=e[t.toNode];if(!i||!n)return;const[s,o]=_.getAnchorCoord(i,t.fromSide),[a,r]=_.getAnchorCoord(n,t.toSide);this.minimapCtx.beginPath(),this.minimapCtx.moveTo(s,o),this.minimapCtx.lineTo(a,r),this.minimapCtx.strokeStyle="#555",this.minimapCtx.lineWidth=10,this.minimapCtx.stroke()};updateViewportRectangle=()=>{if(this.collapsed)return;const t=this.DM.data.nodeBounds,e=this.DM.data.container,i=this.DM.data.scale;if(!t)return;const n=e.clientWidth/i,s=e.clientHeight/i,o=-this.DM.data.offsetX/i+e.clientWidth/(2*i),a=-this.DM.data.offsetY/i+e.clientHeight/(2*i),r=this.minimapCache.centerX+(o-n/2-t.centerX)*this.minimapCache.scale,l=this.minimapCache.centerY+(a-s/2-t.centerY)*this.minimapCache.scale,c=n*this.minimapCache.scale,h=s*this.minimapCache.scale;this.viewportRectangle.style.left=`${r}px`,this.viewportRectangle.style.top=`${l}px`,this.viewportRectangle.style.width=`${c}px`,this.viewportRectangle.style.height=`${h}px`};dispose=()=>{this.toggleMinimapBtn.removeEventListener("click",this.toggleCollapse),this.minimapCtx.clearRect(0,0,this.minimap.clientWidth,this.minimap.clientHeight),this.minimapContainer.remove(),this._minimapContainer=null,this._toggleMinimapBtn=null,this._viewportRectangle=null,this._minimap=null}},exports.MistouchPreventer=class extends i{_preventionContainer=null;preventMt=!1;DM;preventMistouch={record:!1,lastX:0,lastY:0,initialX:0,initialY:0};get preventionContainer(){if(null===this._preventionContainer)throw P;return this._preventionContainer}constructor(...t){super(...t);const e=Object.assign({preventAtStart:!0,labelText:"Click on to unlock."},this.options.mistouchPreventer||{}),i=document.createElement("div");i.className="prevention-banner",i.textContent=e.labelText,this.DM=this.container.get(E),this._preventionContainer=document.createElement("div"),this._preventionContainer.className="prevention-container hidden",_.applyStyles(this._preventionContainer,".full,.prevention-container{top:0;left:0;width:100%;height:100%;position:absolute}.flex-center,.prevention-container{display:flex;justify-content:center;align-items:center}.prevention-container{overflow:visible;transition:background .2s,opacity .2s,box-shadow .2s,border .2s,filter .2s,backdrop-filter .2s}.prevention-container.hidden{pointer-events:none;opacity:0}.prevention-container .prevention-banner{background:#0006;border-radius:12px;padding:12px;margin:12px;-webkit-backdrop-filter:blur(8px) saturate(1.5);backdrop-filter:blur(8px) saturate(1.5);border:2px solid rgba(140,140,140,.75);color:#fff;font-size:calc(14px + .3vw);line-height:calc(17px + .3vw);text-align:center}"),this._preventionContainer.appendChild(i),this.DM.data.container.appendChild(this._preventionContainer),e.preventAtStart&&this.startPrevention(),window.addEventListener("pointerdown",this.onPointerDown),window.addEventListener("pointermove",this.onPointerMove),window.addEventListener("pointerup",this.onPointerUp)}onPointerDown=t=>{const e=this.DM.data.container.getBoundingClientRect();t.clientX<e.left||t.clientX>e.right||t.clientY<e.top||t.clientY>e.bottom?this.preventMt||this.startPrevention():this.preventMt&&(this.preventMistouch.initialX=t.clientX,this.preventMistouch.initialY=t.clientY,this.preventMistouch.lastX=t.clientX,this.preventMistouch.lastY=t.clientY,this.preventMistouch.record=!0)};onPointerMove=t=>{this.preventMistouch.record&&(this.preventMistouch.lastX=t.clientX,this.preventMistouch.lastY=t.clientY)};onPointerUp=()=>{this.preventMistouch.record&&(this.preventMistouch.record=!1,Math.abs(this.preventMistouch.lastX-this.preventMistouch.initialX)+Math.abs(this.preventMistouch.lastY-this.preventMistouch.initialY)<5&&this.endPrevention())};startPrevention=()=>{this.preventionContainer.classList.remove("hidden"),this.DM.data.container.classList.add("numb"),this.preventMt=!0};endPrevention=()=>{this.preventMt=!1,this.preventionContainer.classList.add("hidden"),setTimeout(()=>this.DM.data.container.classList.remove("numb"),50)};dispose=()=>{window.removeEventListener("pointerdown",this.onPointerDown),window.removeEventListener("pointermove",this.onPointerMove),window.removeEventListener("pointerup",this.onPointerUp),this.preventionContainer.remove(),this._preventionContainer=null}},exports.developerSuite=O,exports.renderToString=async function(t,i){const n=t=>e.micromark(t,i),s=t=>function(t,e){switch(t.type){case"text":return e(t.text);case"file":return function(t,e){if(t.file.match(/\.md$/i))return async function(t,e){let i;try{const n=await fetch(t),s=await n.text(),o=s.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);i=e(o?o[2]:s)}catch{i="Failed to load content."}return i}(t.file,e);if(t.file.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i))return`<img src="${t.file}" alt="${t.file.split("/").pop()}">`;if(t.file.match(/\.(mp3|wav)$/i))return`<audio src="${t.file}" controls></audio>`}(t,e);case"link":return`<a href="${t.url}" target="_blank" rel="nofollow noreferrer">${t.url}</a>`;default:return""}}(t,n),o=Object.assign({nodes:[],edges:[]},await fetch(t).then(t=>t.json())).nodes,a=_.resolvePath(t);o.forEach(t=>{if("file"===t.type&&!t.file.includes("http")){const e=t.file.split("/");t.file=a+e.pop()}});let r="";return o.forEach(t=>{r+=s(t)}),r};
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|