json-canvas-viewer 3.0.4 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Hesprs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hesprs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
package/README.md CHANGED
@@ -1,211 +1,387 @@
1
- # JSON Canvas Viewer
2
-
3
- ![TypeScript](https://badgen.net/badge/-/Strict?icon=typescript&label=TypeScript&labelColor=blue&color=555555)
4
- [![npm](https://badgen.net/npm/v/json-canvas-viewer?icon=npm&labelColor=red&color=555555)](https://www.npmjs.com/package/json-canvas-viewer)
5
- [![publish size](https://badgen.net/packagephobia/publish/json-canvas-viewer?labelColor=green&color=555555)](https://packagephobia.now.sh/result?p=json-canvas-viewer)
6
-
7
- ![Canvas Viewer](example/preview.png)
8
-
9
- 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
-
11
- This project is inspired by [sofanati-nour/obsidian-canvas-web-renderer](https://github.com/sofanati-nour/obsidian-canvas-web-renderer), but is far more developed and optimized.
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
- The integrated version is a choice if your project *doesn't use Node.js*, which is built with `marked` and `json-canvas-viewer` inlined, so it can be deployed in vanilla JavaScript projects. Find the integrated version in [Release page](https://github.com/hesprs/JSON-Canvas-Viewer/releases).
31
-
32
- After installation, you can import the package as a module. It supports both ES module and Common JS, here we take ESM as example:
33
-
34
- ``` TypeScript
35
- // with Node.js
36
- import canvasViewer from 'json-canvas-viewer';
37
-
38
- // using integrated version
39
- import canvasViewer from 'path/to/canvasViewer.js';
40
- ```
41
-
42
- ## 🚀 Quick Start
43
-
44
- As a custom element (a simple way to embed, already defined in the code):
45
-
46
- ``` HTML
47
- <canvas-viewer
48
- src="example/introduction.canvas"
49
- extensions="minimap mistouchPrevention"
50
- options="minimapCollapsed"
51
- ></canvas-viewer>
52
- ```
53
-
54
- Or instantiate the viewer (more flexible, but requires more code):
55
-
56
- ``` HTML
57
- <div id="myCanvasContainer" style="width:800px; height:600px;"></div>
58
- <script>
59
- const viewer = new canvasViewer(
60
- document.getElementById('myCanvasContainer'),
61
- ['minimap', 'mistouchPrevention'],
62
- ['minimapCollapsed']
63
- );
64
- viewer.loadCanvas('example/introduction.canvas');
65
- viewer.addEventListener('interact', e => {
66
- // handle node interaction
67
- });
68
- // dispose when not needed
69
- viewer.dispose();
70
- </script>
71
- ```
72
-
73
- **Tip**: All emitted events are realized by `JavaScript CustomEvent`, so the event callback is stored in `event.detail`.
74
-
75
- If you are coding in TypeScript, and intend to retrieve Custom Event callback, please use the pattern below to make the type validator believe your code is type safe:
76
-
77
- ``` TypeScript
78
- const viewer: canvasViewer = new canvasViewer(...);
79
- viewer.loadCanvas(...);
80
- viewer.addEventListener('...', (e: Event) => {
81
- if (e instanceof CustomEvent) {
82
- // use e.detail safely here
83
- console.log(e.detail);
84
- };
85
- });
86
- ```
87
-
88
- ## 🐶 Features
89
-
90
- - View JSON Canvas files (`.canvas`) in a web browser
91
- - Full markdown syntax support (auto-parsed to HTML)
92
- - Embed into front-end projects using a container element or custom element
93
- - Interactive pan and zoom functionality
94
- - Support for different node types:
95
- - Text nodes
96
- - File nodes (including Markdown files)
97
- - Link nodes (embedded web content)
98
- - Group nodes with custom colors
99
- - Edge connections between nodes with labels
100
- - Minimap for easy navigation (optional extension)
101
- - Mistouch prevention (optional extension)
102
- - Responsive design with mobile and touchpad adaption
103
- - 🔥 **Much more performant** than rendering canvas in Obsidian!
104
-
105
- ## 🔌 API Reference
106
-
107
- ### Constructor
108
-
109
- ``` TypeScript
110
- new canvasViewer(container, extensions, options);
111
- ```
112
-
113
- - `container`: HTMLElement where the viewer will be rendered
114
- - `extensions`: (optional) Array (or space-separated string in case of custom element) of extension names to enable:
115
- - `minimap` - Adds navigation minimap
116
- - `mistouchPrevention` - Freezes canvas when clicking outside. **Warning: navigation methods (like `zoomIn()` or `resetView()`) cannot take effect if the canvas is frozen**.
117
- - `options`: (optional) Array (or space-separated string in case of custom element) of config options:
118
- - `controlsHidden` - Hides the control panel
119
- - `controlsCollapsed` - Starts with controls collapsed
120
- - `proControlSchema` - Uses control keybindings in professional softwares (`mouse wheel`: scroll vertically; `mouse wheel` + `shift`: scroll horizontally; `mouse wheel` + `ctrl`: zoom), rather than mouse wheel to zoom. The canvas viewer automatically detect and adjust control schema by default, but you can explicitly configure it. This option doesn't affect mobile control.
121
- - `noShadow` - Disables shadow DOM, DOM elements will be appended to the light DOM directly. The canvas viewer will still be functional, but the styles may be affected.
122
- - `noPreventionAtStart` (available when mistouchPrevention enabled) Starts without prevention
123
- - `minimapCollapsed` - (available when minimap enabled) Starts with minimap collapsed
124
-
125
- ### Methods
126
-
127
- - `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**.
128
- - `shiftFullscreen(option)` Toggle fullscreen mode ('toggle', 'enter', 'exit')
129
- - `resetView()` — Reset pan/zoom to fit canvas content
130
- - `zoomIn()` — Zoom in by a fixed step
131
- - `zoomOut()` — Zoom out by a fixed step
132
- - `setScale(scale)` — Set zoom level to a specific value (number, 0.05–20)
133
- - `panTo(x, y)` — Pan the view to a specific world coordinate
134
- - `dispose()` — Clean up and remove viewer from DOM
135
-
136
- ### Events
137
-
138
- Register with `viewer.addEventListener(event, callback)`.
139
-
140
- - `interact` — Fired when a node is interacted with (`callback(node id, type: 'select' | 'preview')`)
141
- - `loaded` — Fired when a canvas file is loaded (`callback(canvasData)`)
142
-
143
- ## 📂 Canvas File Structure
144
-
145
- The viewer expects JSON Canvas files in JSON format:
146
-
147
- ``` JSON
148
- {
149
- "nodes": [
150
- {
151
- "id": "unique-id",
152
- "type": "text|file|link|group",
153
- "x": 0,
154
- "y": 0,
155
- "width": 400,
156
- "height": 400,
157
- "text": "Content for text nodes",
158
- "file": "filename for file nodes",
159
- "url": "URL for link nodes",
160
- "color": "color-id for groups"
161
- }
162
- ],
163
- "edges": [
164
- {
165
- "id": "edge-id",
166
- "fromNode": "source-node-id",
167
- "toNode": "target-node-id",
168
- "fromSide": "top|bottom|left|right",
169
- "toSide": "top|bottom|left|right",
170
- "label": "Optional edge label"
171
- }
172
- ]
173
- }
174
- ```
175
-
176
- ## 💻 Development
177
-
178
- Built with `TypeScript`, `SCSS` and `HTML5 Canvas`.
179
-
180
- **Project Structure**:
181
-
182
- ``` TypeScript
183
- root
184
- ├── src/
185
- │ ├── canvasViewer.ts // Main class-based component
186
- │ ├── controls.ts // Controls panel
187
- │ ├── interactor.ts // Handles pointer events for user pan/zoom
188
- │ ├── minimap.ts // Minimap extension
189
- │ ├── mistouchPreventer.ts // MistouchPrevention extension
190
- │ ├── overlayManager.ts // Renderer for interactive nodes
191
- │ ├── previewModal.ts // Preview modal
192
- │ ├── renderer.ts // Renderer for non-interactive stuff + some shared exports
193
- │ ├── declarations.d.ts // Public types
194
- │ └── styles.scss // Styles for the viewer
195
- └── example/
196
- └── index.html // Example/test entry point
197
- ```
198
-
199
- **Development Standards**:
200
-
201
- - Strict type validation, no non-null assertion operator `!` allowed.
202
- - Meticulous resource disposal, no memory leak ever possible.
203
- - Modularized components, avoid monolithic class.
204
-
205
- **Extensibility**:
206
-
207
- You can extend `Marked` to support more markdown features. Please include `marked` as a dependency, and all [extensions based on Marked](https://marked.js.org/using_pro) are applied equally to the viewer.
208
-
209
- ## 📝 Copyright & License
210
-
211
- Copyright ©️ 2025 Hesprs (Hēsperus) | [MIT License](https://mit-license.org/)
1
+ # JSON Canvas Viewer
2
+
3
+ ![TypeScript](https://badgen.net/badge/-/Strict?icon=typescript&label=TypeScript&labelColor=blue&color=555555)
4
+ [![npm](https://badgen.net/npm/v/json-canvas-viewer?icon=npm&labelColor=red&color=555555)](https://www.npmjs.com/package/json-canvas-viewer)
5
+ [![publish size](https://badgen.net/packagephobia/publish/json-canvas-viewer?labelColor=green&color=555555)](https://packagephobia.now.sh/result?p=json-canvas-viewer)
6
+
7
+ ![Canvas Viewer](example/preview.png)
8
+
9
+ 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
+
11
+ This project is inspired by [sofanati-nour/obsidian-canvas-web-renderer](https://github.com/sofanati-nour/obsidian-canvas-web-renderer), but is far more developed and optimized.
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
+ const viewer = new canvasViewer(document.getElementById('myCanvasContainer'));
45
+ viewer.loadCanvas('example/introduction.canvas');
46
+ // dispose when not needed
47
+ viewer.dispose();
48
+ </script>
49
+ ```
50
+
51
+ And the viewer should be right in your container, you can instantiate the viewer multiple times to render multiple canvases simultaneously.
52
+
53
+ **Methods**:
54
+
55
+ - `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**.
56
+ - `dispose()` — Clean up and remove viewer from DOM.
57
+
58
+ ## 🐶 Features
59
+
60
+ - View JSON Canvas files (`.canvas`) in a web browser
61
+ - Full markdown syntax support (auto-parsed to HTML)
62
+ - Embed into websites easily
63
+ - Interactive pan and zoom functionality
64
+ - Support for different node types:
65
+ - Text nodes
66
+ - File nodes (including Markdown files)
67
+ - Link nodes (embedded web content)
68
+ - Group nodes with custom colors
69
+ - Edge connections between nodes with labels
70
+ - Minimap for easy navigation (optional extension)
71
+ - Mistouch prevention (optional extension)
72
+ - Responsive design with mobile and touchpad adaptation
73
+ - Out-of-the-box extensibility and tree-shaking
74
+ - TypeScript native support
75
+ - 🔥 **Much more performant** than rendering canvases in Obsidian!
76
+
77
+ ## 🧩 Extensibility
78
+
79
+ ### Registry
80
+
81
+ The viewer implements the `registry`, which is a powerful orchestrator to streamline the development and extension of the viewer. The registry is the core of the viewer.
82
+
83
+ ```TypeScript
84
+ new canvasViewer(container, registry);
85
+
86
+ interface registry {
87
+ options: Record<string, Record<string, any>>;
88
+ extensions: Array<Class<runtimeData, registry>>;
89
+ hooks: Record<string, Array<Function>>;
90
+ api: Record<string, Record<string, Function>>;
91
+ register: (userRegistry: userRegistry) => void;
92
+ }
93
+ interface Class<T> { new (...args: any[]): T }
94
+ interface Function { (...args: any[]): any }
95
+ ```
96
+
97
+ The second parameter of the constructor is an object which is then merged with the default registry. You can also manually register using the `register` method.
98
+
99
+ **Default Options and Values**:
100
+
101
+ ```TypeScript
102
+ {
103
+ main: {
104
+ noShadow: false,
105
+ },
106
+ interactor: {
107
+ preventDefault: true,
108
+ proControlSchema: false,
109
+ zoomFactor: 0.002,
110
+ lockControlSchema: false,
111
+ },
112
+ }
113
+ ```
114
+
115
+ - `noShadow` Disables shadow DOM, DOM elements will be appended to the light DOM directly. The canvas viewer will still be functional, but the styles may be affected.
116
+ - `preventDefault` Prevents default behavior of mouse events, the viewer may not work properly if set to false.
117
+ - `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.
118
+ - `zoomFactor` The zoom factor, how fast the canvas zooms in or out.
119
+ - `lockControlSchema` Locks the control schema.
120
+
121
+ **Default Hooks**:
122
+
123
+ ```TypeScript
124
+ {
125
+ onDispose: [],
126
+ onRender: [],
127
+ onResize: [(width: number, height: number) => {}],
128
+ onClick: [(id: string | null) => {}],
129
+ onZoom: [],
130
+ onToggleFullscreen: [],
131
+ onInteractionStart: [],
132
+ onInteractionEnd: [],
133
+ }
134
+ ```
135
+
136
+ - `onDispose` — Called when the viewer is disposed.
137
+ - `onRender` — Called when the viewer is rendered every frame.
138
+ - `onResize` — Called when the viewer container is resized.
139
+ - `width` — The width of the resized viewer container.
140
+ - `height` — The height of the resized viewer container.
141
+ - `onClick` — Called when the canvas is clicked.
142
+ - `id` — The id of the node that is clicked, or `null` if no node is clicked.
143
+ - `onZoom` Called when the zoom level changes.
144
+ - `onToggleFullscreen` — Called when the fullscreen mode is toggled.
145
+ - `onInteractionStart` Called when the pointer enters a selected node.
146
+ - `onInteractionEnd` — Called when the pointer leaves a selected node.
147
+
148
+ **Default API**:
149
+
150
+ ```TypeScript
151
+ {
152
+ main: {
153
+ loadCanvas: (path: string) => Promise<void>,
154
+ refresh: () => void, // Manually trigger a render
155
+ pan: (x: number, y: number) => void,
156
+ zoom: (factor: number, origin: Coordinates) => void,
157
+ zoomToScale: (newScale: number, origin: Coordinates) => void,
158
+ panToCoords: (x: number, y: number) => void,
159
+ shiftFullscreen: (option: 'toggle' | 'enter' | 'exit' = 'toggle') => void,
160
+ resetView: () => void, // Reset the scale and offset to default that the whole canvas is visible
161
+ },
162
+ dataManager: {
163
+ middleViewer: () => {
164
+ x: number; // half of the container width
165
+ y: number; // half of the container height
166
+ width: number; // container width
167
+ height: number; // container height
168
+ },
169
+ findNodeAtMousePosition: (mousePosition: Coordinates) => JSONCanvasNode | null,
170
+ },
171
+ interactionHandler: {
172
+ stop: () => void, // Stop receiving interaction
173
+ start: () => void, // Start receiving interaction
174
+ },
175
+ }
176
+ ```
177
+
178
+ ### Extensions
179
+
180
+ The viewer is built with extensibility in mind, and it is easy to extend the viewer with custom extensions.
181
+
182
+ #### Official Extensions
183
+
184
+ **minimap**: Renders the minimap with an overview of the canvas.
185
+ - used with:
186
+ ```TypeScript
187
+ import minimap from 'json-canvas-viewer/minimap';
188
+ new canvasViewer(container, { extensions: [minimap] });
189
+ ```
190
+ - new options and API:
191
+ ```TypeScript
192
+ {
193
+ options: {
194
+ minimap: {
195
+ collapsed: false,
196
+ },
197
+ },
198
+ api: {
199
+ minimap: {
200
+ toggleCollapse: () => void,
201
+ },
202
+ }
203
+ }
204
+ ```
205
+
206
+ **mistouchPreventer**: Prevents mistouch by freezing the canvas when clicking outside the viewer.
207
+ - used with:
208
+ ```TypeScript
209
+ import mistouchPreventer from 'json-canvas-viewer/mistouchPreventer';
210
+ new canvasViewer(container, { extensions: [mistouchPreventer] });
211
+ ```
212
+ - new options and API:
213
+ ```TypeScript
214
+ {
215
+ options: {
216
+ mistouchPreventer: {
217
+ preventAtStart: true,
218
+ },
219
+ },
220
+ api: {
221
+ mistouchPreventer: {
222
+ startPrevention: () => void,
223
+ endPrevention: () => void,
224
+ },
225
+ }
226
+ }
227
+ ```
228
+
229
+ **controls**: Shows a control bar with zoom, pan, scale slider, and fullscreen buttons.
230
+ - used with:
231
+ ```TypeScript
232
+ import controls from 'json-canvas-viewer/controls';
233
+ new canvasViewer(container, { extensions: [controls] });
234
+ ```
235
+ - new options and API:
236
+ ```TypeScript
237
+ {
238
+ options: {
239
+ controls: {
240
+ collapsed: false,
241
+ },
242
+ },
243
+ api: {
244
+ controls: {
245
+ toggleCollapse: () => void,
246
+ },
247
+ }
248
+ }
249
+ ```
250
+
251
+ **debugPanel**: Shows a debug panel with scale and offset.
252
+ - used with:
253
+ ```TypeScript
254
+ import debugPanel from 'json-canvas-viewer/debugPanel';
255
+ new canvasViewer(container, { extensions: [debugPanel] });
256
+ ```
257
+ - new API:
258
+ ```TypeScript
259
+ {
260
+ api: {
261
+ debugPanel: {
262
+ update: () => void,
263
+ },
264
+ }
265
+ }
266
+ ```
267
+
268
+ #### Develop an Extension
269
+
270
+ 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:
271
+ - receive `runtimeData` and `registry` as parameters in the constructor.
272
+ - calls `registry.register` first in the constructor to define default options, register hooks and provide API.
273
+
274
+ Here comes a minimal sample extension of a debug panel (JavaScript for simplicity):
275
+
276
+ ```JavaScript
277
+ import { round } from '../utilities';
278
+
279
+ export default class debugPanel {
280
+ constructor(data, registry) {
281
+ registry.register({
282
+ hooks: {
283
+ onRender: [this.update],
284
+ onDispose: [this.dispose],
285
+ },
286
+ api: {
287
+ debugPanel: {
288
+ update: this.update,
289
+ },
290
+ },
291
+ });
292
+ this.debugPanel = document.createElement('div');
293
+ this.debugPanel.className = 'debug-panel';
294
+ this.data = data;
295
+ data.container.appendChild(this.debugPanel);
296
+ }
297
+
298
+ private update = () => {
299
+ this.debugPanel.innerHTML = `
300
+ <p>Scale: ${round(this.data.scale, 3)}</p>
301
+ <p>Offset: ${round(this.data.offsetX, 1)}, ${round(this.data.offsetY, 1)}</p>
302
+ `;
303
+ };
304
+
305
+ private dispose = () => {
306
+ this.debugPanel.remove();
307
+ this.debugPanel = null;
308
+ };
309
+ }
310
+ ```
311
+
312
+ ## 📂 Canvas File Structure
313
+
314
+ The viewer expects JSON Canvas files in JSON format (a combination of official [JSON Canvas spec](https://jsoncanvas.org/spec/1.0/) and [Developer-Mike/obsidian-advanced-canvas spec](https://github.com/Developer-Mike/obsidian-advanced-canvas)):
315
+
316
+ ```TypeScript
317
+ interface JSONCanvas {
318
+ nodes: Array<JSONCanvasNode>;
319
+ edges: Array<JSONCanvasEdge>;
320
+ metadata: {
321
+ version: string;
322
+ frontmatter: Record<string, string>;
323
+ };
324
+ }
325
+
326
+ interface JSONCanvasNode {
327
+ id: string;
328
+ type: 'group' | 'file' | 'text' | 'link';
329
+ x: number;
330
+ y: number;
331
+ width: number;
332
+ height: number;
333
+ label?: string;
334
+ background?: string;
335
+ backgroundStyle?: 'cover' | 'ratio' | 'repeat';
336
+ styleAttributes?: Record<string, string>;
337
+ color?: string;
338
+ text?: string;
339
+ file?: string;
340
+ subpath?: string;
341
+ url?: string;
342
+ }
343
+
344
+ interface JSONCanvasEdge {
345
+ id: string;
346
+ fromNode: string;
347
+ toNode: string;
348
+ fromSide: 'right' | 'left' | 'top' | 'bottom';
349
+ toSide: 'right' | 'left' | 'top' | 'bottom';
350
+ toEnd?: 'arrow' | 'none';
351
+ label?: string;
352
+ styleAttributes?: Record<string, string>;
353
+ color?: string;
354
+ }
355
+ ```
356
+
357
+ ## 💻 Development
358
+
359
+ Built with `TypeScript`, `SCSS` and `HTML5 Canvas`.
360
+
361
+ **Project Structure**:
362
+
363
+ ```
364
+ root
365
+ ├── src/
366
+ │ ├── extensions/
367
+ │ │ ├── controls.ts // Control panel
368
+ │ │ ├── minimap.ts // Minimap extension
369
+ │ │ ├── mistouchPreventer.ts // MistouchPrevention extension
370
+ │ │ └── debugPanel.ts // Debug panel
371
+ │ ├── canvasViewer.ts // Main entry point
372
+ │ ├── interactor.ts // Handles pointer events for user pan/zoom
373
+ │ ├── dataManager.ts // Manages canvas data
374
+ │ ├── interactionHandler.ts // Handles interaction events (wrapper of interactor)
375
+ │ ├── overlayManager.ts // Renderer for interactive nodes
376
+ │ ├── renderer.ts // Renderer for non-interactive stuff
377
+ │ ├── declarations.d.ts // Public types
378
+ │ ├── utilities.ts // Utility functions
379
+ │ └── styles.scss // Styles for the viewer
380
+ └── example/
381
+ ├── index.html // Example/test entry point
382
+ └── Example Canvas/ // Example/test canvas file
383
+ ```
384
+
385
+ ## 📝 Copyright & License
386
+
387
+ Copyright ©️ 2025 Hesprs (Hēsperus) | [MIT License](https://mit-license.org/)