@lapis-notes/graph 0.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/embed.d.ts +5 -0
  5. package/dist/embed.js +4 -0
  6. package/dist/graph-build-generation.d.ts +6 -0
  7. package/dist/graph-build-generation.js +13 -0
  8. package/dist/graph-controls-overlay.svelte +840 -0
  9. package/dist/graph-controls-overlay.svelte.d.ts +30 -0
  10. package/dist/graph-data-coordinator.d.ts +41 -0
  11. package/dist/graph-data-coordinator.js +265 -0
  12. package/dist/graph-data.d.ts +23 -0
  13. package/dist/graph-data.js +493 -0
  14. package/dist/graph-embed.svelte +92 -0
  15. package/dist/graph-embed.svelte.d.ts +12 -0
  16. package/dist/graph-file-preview.svelte +115 -0
  17. package/dist/graph-file-preview.svelte.d.ts +12 -0
  18. package/dist/graph-hover-intent.d.ts +22 -0
  19. package/dist/graph-hover-intent.js +85 -0
  20. package/dist/graph-load-alignment.d.ts +2 -0
  21. package/dist/graph-load-alignment.js +3 -0
  22. package/dist/graph-navigation.d.ts +7 -0
  23. package/dist/graph-navigation.js +78 -0
  24. package/dist/graph-node-activation.d.ts +4 -0
  25. package/dist/graph-node-activation.js +16 -0
  26. package/dist/graph-plugin.d.ts +38 -0
  27. package/dist/graph-plugin.js +177 -0
  28. package/dist/graph-problems.d.ts +15 -0
  29. package/dist/graph-problems.js +46 -0
  30. package/dist/graph-query-resolution.d.ts +9 -0
  31. package/dist/graph-query-resolution.js +44 -0
  32. package/dist/graph-renderer.d.ts +226 -0
  33. package/dist/graph-renderer.js +1350 -0
  34. package/dist/graph-settings.d.ts +20 -0
  35. package/dist/graph-settings.js +125 -0
  36. package/dist/graph-types.d.ts +74 -0
  37. package/dist/graph-types.js +1 -0
  38. package/dist/graph-view.d.ts +62 -0
  39. package/dist/graph-view.js +553 -0
  40. package/dist/graph-viewport-alignment.d.ts +6 -0
  41. package/dist/graph-viewport-alignment.js +9 -0
  42. package/dist/index.d.ts +9 -0
  43. package/dist/index.js +8 -0
  44. package/dist/mount-local-component.svelte.d.ts +7 -0
  45. package/dist/mount-local-component.svelte.js +12 -0
  46. package/dist/public-components.d.ts +2 -0
  47. package/dist/public-components.js +2 -0
  48. package/dist/styles.css +441 -0
  49. package/manifest.json +30 -0
  50. package/package.json +84 -0
  51. package/styles.css +1 -0
@@ -0,0 +1,553 @@
1
+ import { createVaultSearchFilterSyntax, ItemView, Menu, Notice, } from "@lapis-notes/api";
2
+ import { buildCanonicalLocalGraph, graphNodeIdForFile } from "./graph-data";
3
+ import { GraphBuildGeneration } from "./graph-build-generation";
4
+ import { graphLoadFocusNodeId } from "./graph-load-alignment";
5
+ import { openGraphTagSearch } from "./graph-node-activation";
6
+ import GraphControlsOverlay from "./graph-controls-overlay.svelte";
7
+ import { GraphRenderer } from "./graph-renderer";
8
+ import { DEFAULT_GRAPH_SETTINGS, mergeGraphSettings, patchGraphSettings, } from "./graph-settings";
9
+ import { resolveGraphNavigationLeaf } from "./graph-navigation";
10
+ import { mountLocalComponent, } from "./mount-local-component.svelte";
11
+ export const GraphViewType = "graph";
12
+ export const LocalGraphViewType = "graph-local";
13
+ function createDebounce(callback, waitMs) {
14
+ let timer = null;
15
+ const debounced = (() => {
16
+ if (timer) {
17
+ clearTimeout(timer);
18
+ }
19
+ timer = setTimeout(() => {
20
+ timer = null;
21
+ callback();
22
+ }, waitMs);
23
+ });
24
+ debounced.cancel = () => {
25
+ if (timer)
26
+ clearTimeout(timer);
27
+ timer = null;
28
+ };
29
+ return debounced;
30
+ }
31
+ class GraphViewBase extends ItemView {
32
+ plugin;
33
+ isLocal;
34
+ renderer = null;
35
+ overlay = null;
36
+ settings;
37
+ canonicalGraph = null;
38
+ coordinatorGraphVersion = -1;
39
+ unsubscribeCoordinator = null;
40
+ currentGraphPaths = new Set();
41
+ unregisterView = null;
42
+ buildGeneration = new GraphBuildGeneration();
43
+ deriveGeneration = new GraphBuildGeneration();
44
+ metadataFacetGeneration = 0;
45
+ filterSyntax = createVaultSearchFilterSyntax({
46
+ fileNames: [],
47
+ paths: [],
48
+ tags: [],
49
+ });
50
+ scheduleRebuild = createDebounce(() => {
51
+ void this.rebuild();
52
+ }, 120);
53
+ scheduleDerive = createDebounce(() => {
54
+ void this.deriveGraphFromSettings();
55
+ }, 90);
56
+ constructor(leaf, plugin, isLocal) {
57
+ super(leaf);
58
+ this.plugin = plugin;
59
+ this.isLocal = isLocal;
60
+ this.settings = this.plugin.getSettings();
61
+ }
62
+ onOpen() {
63
+ return Promise.resolve();
64
+ }
65
+ onClose() {
66
+ return Promise.resolve();
67
+ }
68
+ onload() {
69
+ this.settings = this.plugin.getSettings();
70
+ this.contentEl.empty();
71
+ this.contentEl.classList.add("graph-view");
72
+ this.contentEl.dataset.uiComponent = "graph";
73
+ this.contentEl.dataset.uiPart = this.isLocal ? "local-view" : "global-view";
74
+ this.contentEl.dataset.testid = this.isLocal
75
+ ? "local-graph-panel"
76
+ : "graph-panel";
77
+ this.overlay = mountLocalComponent(GraphControlsOverlay, {
78
+ target: this.contentEl,
79
+ props: {
80
+ app: this.app,
81
+ isLocal: this.isLocal,
82
+ settings: this.settings,
83
+ statsText: "",
84
+ statusText: "Loading graph…",
85
+ statusKind: "loading",
86
+ groupDiagnostics: {},
87
+ filterDiagnostic: null,
88
+ filterSyntax: this.filterSyntax,
89
+ preview: null,
90
+ isAnimating: false,
91
+ onFocusActiveFile: () => {
92
+ this.focusActiveFile();
93
+ },
94
+ onZoomIn: () => {
95
+ this.renderer?.zoomIn();
96
+ },
97
+ onZoomOut: () => {
98
+ this.renderer?.zoomOut();
99
+ },
100
+ onResetView: () => {
101
+ this.renderer?.resetView();
102
+ },
103
+ onRefreshGraph: () => {
104
+ if (this.isLocal) {
105
+ void this.rebuild();
106
+ }
107
+ else {
108
+ void this.plugin.refreshGlobalGraph(true);
109
+ }
110
+ },
111
+ onResetDefaults: () => {
112
+ this.resetLocalSettings();
113
+ },
114
+ onToggleAnimation: () => {
115
+ if (this.renderer?.isTimeLapseRunning()) {
116
+ this.renderer.stopTimeLapse();
117
+ }
118
+ else {
119
+ this.renderer?.startTimeLapse();
120
+ }
121
+ },
122
+ onSettingsPatch: (patch) => {
123
+ this.updateLocalSettings(patch);
124
+ },
125
+ onOpenPreviewFile: (preview) => {
126
+ void this.openNode(preview.node, false);
127
+ },
128
+ onDismissPreview: () => {
129
+ this.renderer?.dismissPreview();
130
+ },
131
+ },
132
+ });
133
+ const surfaceEl = this.getSurfaceEl();
134
+ this.renderer = new GraphRenderer(surfaceEl, {
135
+ onNodeClick: (node) => {
136
+ void this.openNode(node, false);
137
+ },
138
+ onNodeContextMenu: (node, event) => {
139
+ this.showNodeMenu(node, event);
140
+ },
141
+ onLayoutComplete: (summary) => {
142
+ this.app.telemetry.recordEvent("graph.layout.complete", {
143
+ animated: summary.animated,
144
+ "graph.layout.duration.ms": summary.durationMs,
145
+ "graph.node.count": summary.nodeCount,
146
+ "graph.link.count": summary.linkCount,
147
+ });
148
+ },
149
+ onTimeLapseStateChange: (state) => {
150
+ if (this.overlay)
151
+ this.overlay.props.isAnimating = state.running;
152
+ this.app.telemetry.recordEvent("graph.timelapse", {
153
+ running: state.running,
154
+ reason: state.reason,
155
+ });
156
+ },
157
+ onNodePreviewChange: (preview) => {
158
+ if (this.overlay)
159
+ this.overlay.props.preview = preview;
160
+ },
161
+ });
162
+ this.refreshFilterSyntaxFromVault();
163
+ void this.refreshMetadataFacets();
164
+ this.unregisterView = this.plugin.registerGraphView(this);
165
+ if (this.isLocal) {
166
+ this.registerEvent(this.app.workspace.on("file-open", () => {
167
+ this.scheduleRebuild();
168
+ }));
169
+ this.registerEvent(this.app.metadataCache.on("loaded", () => this.scheduleRebuild()));
170
+ this.registerEvent(this.app.metadataCache.on("index-changed", (change) => {
171
+ if (!change.reset && !change.domains.includes("metadata"))
172
+ return;
173
+ if (this.shouldRebuildForMetadataPaths(change.paths, change.reset ?? false)) {
174
+ this.scheduleRebuild();
175
+ }
176
+ }));
177
+ }
178
+ else {
179
+ this.unsubscribeCoordinator = this.plugin.subscribeToGlobalGraph((state) => this.applyCoordinatorState(state));
180
+ }
181
+ this.registerEvent(this.app.workspace.on("active-leaf-change", (leaf) => {
182
+ if (leaf === this.leaf) {
183
+ this.scheduleViewportRefreshWhenVisible();
184
+ }
185
+ if (this.isLocal) {
186
+ this.scheduleRebuild();
187
+ }
188
+ }));
189
+ this.registerEvent(this.app.metadataCache.on("loaded", () => {
190
+ this.refreshFilterSyntaxFromVault();
191
+ void this.refreshMetadataFacets();
192
+ }));
193
+ this.registerEvent(this.app.metadataCache.on("index-changed", (change) => {
194
+ if (!change.reset && !change.domains.includes("metadata"))
195
+ return;
196
+ this.refreshFilterSyntaxFromVault();
197
+ void this.refreshMetadataFacets();
198
+ }));
199
+ if (this.isLocal) {
200
+ requestAnimationFrame(() => {
201
+ void this.rebuild();
202
+ });
203
+ }
204
+ }
205
+ scheduleViewportRefreshWhenVisible() {
206
+ requestAnimationFrame(() => {
207
+ if (!this.renderer) {
208
+ return;
209
+ }
210
+ const surfaceEl = this.overlay?.target.querySelector("[data-graph-surface]");
211
+ if (!(surfaceEl instanceof HTMLDivElement)) {
212
+ return;
213
+ }
214
+ const { width, height } = surfaceEl.getBoundingClientRect();
215
+ if (width <= 0 || height <= 0) {
216
+ return;
217
+ }
218
+ this.renderer.refreshViewport();
219
+ });
220
+ }
221
+ onunload() {
222
+ this.scheduleRebuild.cancel();
223
+ this.scheduleDerive.cancel();
224
+ this.buildGeneration.invalidate();
225
+ this.deriveGeneration.invalidate();
226
+ this.metadataFacetGeneration += 1;
227
+ this.unregisterView?.();
228
+ this.unregisterView = null;
229
+ this.unsubscribeCoordinator?.();
230
+ this.unsubscribeCoordinator = null;
231
+ this.renderer?.destroy();
232
+ this.renderer = null;
233
+ this.overlay?.destroy();
234
+ this.overlay = null;
235
+ this.currentGraphPaths = new Set();
236
+ this.canonicalGraph = null;
237
+ }
238
+ onPaneMenu(menu) {
239
+ menu.addItem((item) => {
240
+ item
241
+ .setTitle("Refresh graph")
242
+ .setIcon("refresh-cw")
243
+ .onClick(() => {
244
+ if (this.isLocal) {
245
+ void this.rebuild();
246
+ }
247
+ else {
248
+ void this.plugin.refreshGlobalGraph(true);
249
+ }
250
+ });
251
+ });
252
+ menu.addItem((item) => {
253
+ item
254
+ .setTitle("Focus active file")
255
+ .setIcon("locate-fixed")
256
+ .onClick(() => {
257
+ this.focusActiveFile();
258
+ });
259
+ });
260
+ }
261
+ focusActiveFile() {
262
+ const activeFile = this.app.workspace.getActiveFile();
263
+ if (!activeFile) {
264
+ new Notice("No active file to focus in graph");
265
+ return;
266
+ }
267
+ this.renderer?.focusNode(graphNodeIdForFile(activeFile.path), {
268
+ zoom: true,
269
+ });
270
+ }
271
+ applyGraphSettings(settings) {
272
+ const previous = this.settings;
273
+ this.settings = mergeGraphSettings(settings);
274
+ if (this.overlay) {
275
+ this.overlay.props.settings = this.settings;
276
+ }
277
+ if (this.isLocal &&
278
+ previous.localGraph.depth !== this.settings.localGraph.depth) {
279
+ this.scheduleRebuild();
280
+ return;
281
+ }
282
+ if (this.canonicalGraph) {
283
+ this.scheduleDerive();
284
+ }
285
+ }
286
+ async rebuild() {
287
+ const generation = this.buildGeneration.next();
288
+ const diagnosticPath = this.isLocal
289
+ ? (this.app.workspace.getActiveFile()?.path ?? null)
290
+ : null;
291
+ if (this.overlay) {
292
+ this.overlay.props.statusText = "Loading graph…";
293
+ this.overlay.props.statusKind = "loading";
294
+ }
295
+ let graph;
296
+ try {
297
+ graph = await this.getGraphData(this.settings);
298
+ }
299
+ catch (error) {
300
+ if (!this.buildGeneration.isCurrent(generation) || !this.overlay)
301
+ return;
302
+ this.overlay.props.statusText = `Unable to load graph: ${error instanceof Error ? error.message : String(error)}`;
303
+ this.overlay.props.statusKind = "error";
304
+ this.plugin.reportGraphBuildFailure(this.isLocal ? "local" : "global", diagnosticPath, error);
305
+ return;
306
+ }
307
+ if (!this.buildGeneration.isCurrent(generation))
308
+ return;
309
+ this.plugin.clearGraphBuildFailure(this.isLocal ? "local" : "global");
310
+ this.canonicalGraph = graph;
311
+ const resolved = await this.plugin.resolveGraphSettings(graph, this.settings);
312
+ if (!this.buildGeneration.isCurrent(generation))
313
+ return;
314
+ if (this.overlay) {
315
+ this.overlay.props.groupDiagnostics = resolved.matches.groupDiagnostics;
316
+ this.overlay.props.filterDiagnostic = resolved.matches.filterDiagnostic;
317
+ }
318
+ this.renderGraph(resolved.graph);
319
+ if (this.overlay) {
320
+ this.overlay.props.settings = this.settings;
321
+ this.overlay.props.statsText = this.getStatsText(graph);
322
+ this.overlay.props.statusText = "";
323
+ this.overlay.props.statusKind = null;
324
+ }
325
+ }
326
+ applyCoordinatorState(state) {
327
+ if (!this.overlay)
328
+ return;
329
+ if (state.graph &&
330
+ (state.version !== this.coordinatorGraphVersion || !this.canonicalGraph)) {
331
+ this.coordinatorGraphVersion = state.version;
332
+ this.canonicalGraph = state.graph;
333
+ void this.deriveGraphFromSettings();
334
+ }
335
+ const progress = state.progress;
336
+ const progressText = progress?.total
337
+ ? ` (${Math.min(progress.processed, progress.total)} of ${progress.total})`
338
+ : "";
339
+ if (state.status === "loading" || state.status === "idle") {
340
+ this.overlay.props.statusText = `Loading graph${progressText}…`;
341
+ this.overlay.props.statusKind = "loading";
342
+ }
343
+ else if (state.status === "updating") {
344
+ this.overlay.props.statusText = `Updating graph${progressText}…`;
345
+ this.overlay.props.statusKind = "loading";
346
+ }
347
+ else if (state.status === "error") {
348
+ this.overlay.props.statusText = state.graph
349
+ ? "Unable to refresh graph; showing cached data"
350
+ : `Unable to load graph: ${state.error ?? "Unknown error"}`;
351
+ this.overlay.props.statusKind = "error";
352
+ }
353
+ else {
354
+ this.overlay.props.statusText = "";
355
+ this.overlay.props.statusKind = null;
356
+ }
357
+ }
358
+ async deriveGraphFromSettings() {
359
+ const canonical = this.canonicalGraph;
360
+ if (!canonical)
361
+ return;
362
+ const generation = this.deriveGeneration.next();
363
+ const resolved = await this.plugin.resolveGraphSettings(canonical, this.settings);
364
+ if (!this.deriveGeneration.isCurrent(generation))
365
+ return;
366
+ if (this.overlay) {
367
+ this.overlay.props.groupDiagnostics = resolved.matches.groupDiagnostics;
368
+ this.overlay.props.filterDiagnostic = resolved.matches.filterDiagnostic;
369
+ }
370
+ this.renderGraph(resolved.graph);
371
+ }
372
+ renderGraph(graph) {
373
+ this.currentGraphPaths = new Set(graph.nodes
374
+ .filter((node) => node.type === "note")
375
+ .map((node) => node.path)
376
+ .filter((path) => typeof path === "string"));
377
+ this.renderer?.setGraph(graph, this.settings);
378
+ const preferredCenterNodeId = graphLoadFocusNodeId(this.isLocal, graph);
379
+ if (preferredCenterNodeId) {
380
+ this.renderer?.focusNode(preferredCenterNodeId);
381
+ }
382
+ if (this.overlay) {
383
+ this.overlay.props.settings = this.settings;
384
+ this.overlay.props.statsText = this.getStatsText(graph);
385
+ }
386
+ }
387
+ shouldRebuildForMetadataPaths(changedPaths, reset) {
388
+ if (!this.isLocal) {
389
+ return true;
390
+ }
391
+ if (reset || changedPaths.length === 0)
392
+ return true;
393
+ const activeFile = this.app.workspace.getActiveFile();
394
+ if (!activeFile) {
395
+ return false;
396
+ }
397
+ return changedPaths.some((changedPath) => this.currentGraphPaths.has(changedPath) ||
398
+ this.app.metadataCache.isDirectlyAffectedByPathChange(activeFile.path, changedPath));
399
+ }
400
+ getSurfaceEl() {
401
+ const surfaceEl = this.overlay?.target.querySelector("[data-graph-surface]");
402
+ if (!(surfaceEl instanceof HTMLDivElement)) {
403
+ throw new Error("Graph surface root is missing");
404
+ }
405
+ return surfaceEl;
406
+ }
407
+ getStatsText(graph) {
408
+ const activeFile = this.app.workspace.getActiveFile();
409
+ const centerLabel = this.isLocal && activeFile
410
+ ? ` • ${activeFile.baseName ?? activeFile.path}`
411
+ : "";
412
+ return `${graph.nodes.length} nodes • ${graph.links.length} links${centerLabel}`;
413
+ }
414
+ refreshFilterSyntaxFromVault(tags = []) {
415
+ const files = this.app.vault.getFiles();
416
+ this.filterSyntax = createVaultSearchFilterSyntax({
417
+ fileNames: files.map((file) => file.name),
418
+ paths: files.flatMap((file) => file.parent?.path ? [file.parent.path] : []),
419
+ tags,
420
+ });
421
+ if (this.overlay)
422
+ this.overlay.props.filterSyntax = this.filterSyntax;
423
+ }
424
+ async refreshMetadataFacets() {
425
+ const generation = ++this.metadataFacetGeneration;
426
+ try {
427
+ const rows = await this.app.metadataCache.queryFacets({
428
+ kind: "tag",
429
+ limit: 100,
430
+ });
431
+ if (generation !== this.metadataFacetGeneration)
432
+ return;
433
+ const tags = rows.flatMap((row) => typeof row.value === "string"
434
+ ? [`#${row.value.replace(/^#/u, "")}`]
435
+ : []);
436
+ this.refreshFilterSyntaxFromVault(tags);
437
+ }
438
+ catch {
439
+ if (generation !== this.metadataFacetGeneration)
440
+ return;
441
+ this.refreshFilterSyntaxFromVault();
442
+ }
443
+ }
444
+ updateLocalSettings(patch) {
445
+ const nextSettings = patchGraphSettings(this.settings, patch);
446
+ void this.plugin.updateSettings(nextSettings);
447
+ }
448
+ resetLocalSettings() {
449
+ void this.plugin.updateSettings(mergeGraphSettings(DEFAULT_GRAPH_SETTINGS));
450
+ }
451
+ resolveNavigationLeaf(openInNewPane) {
452
+ return resolveGraphNavigationLeaf({
453
+ workspace: this.app.workspace,
454
+ graphLeaf: this.leaf,
455
+ isLocal: this.isLocal,
456
+ openInNewPane,
457
+ });
458
+ }
459
+ async openNode(node, openInNewPane) {
460
+ if (await openGraphTagSearch(this.app, node))
461
+ return;
462
+ if (!node.path || !node.exists) {
463
+ return;
464
+ }
465
+ const file = this.app.vault.getFileByPath(node.path);
466
+ if (!file) {
467
+ return;
468
+ }
469
+ const leaf = this.resolveNavigationLeaf(openInNewPane);
470
+ await leaf.openFile(file, { result: { history: true } });
471
+ if (leaf !== this.leaf) {
472
+ this.app.workspace.activeLeaf = leaf;
473
+ await this.app.workspace.revealLeaf(leaf);
474
+ }
475
+ }
476
+ showNodeMenu(node, event) {
477
+ const menu = new Menu();
478
+ if (node.type === "tag") {
479
+ menu
480
+ .addItem((item) => item
481
+ .setTitle("Search for tag")
482
+ .setIcon("search")
483
+ .onClick(() => {
484
+ void openGraphTagSearch(this.app, node);
485
+ }))
486
+ .showAtMouseEvent(event);
487
+ return;
488
+ }
489
+ if (!node.exists)
490
+ return;
491
+ menu.addItem((item) => {
492
+ item
493
+ .setTitle("Open")
494
+ .setIcon("file-text")
495
+ .onClick(() => {
496
+ void this.openNode(node, false);
497
+ });
498
+ });
499
+ menu.addItem((item) => {
500
+ item
501
+ .setTitle("Open in new pane")
502
+ .setIcon("panel-right-open")
503
+ .onClick(() => {
504
+ void this.openNode(node, true);
505
+ });
506
+ });
507
+ if (node.path) {
508
+ menu.addItem((item) => {
509
+ item
510
+ .setTitle("Copy path")
511
+ .setIcon("copy")
512
+ .onClick(() => {
513
+ void navigator.clipboard?.writeText(node.path ?? "");
514
+ });
515
+ });
516
+ }
517
+ menu.showAtMouseEvent(event);
518
+ }
519
+ }
520
+ export class GraphView extends GraphViewBase {
521
+ constructor(leaf, plugin) {
522
+ super(leaf, plugin, false);
523
+ }
524
+ getViewType() {
525
+ return GraphViewType;
526
+ }
527
+ getDisplayText() {
528
+ return "Graph";
529
+ }
530
+ getIcon() {
531
+ return "waypoints";
532
+ }
533
+ getGraphData(_settings) {
534
+ return Promise.resolve({ nodes: [], links: [], centerNodeId: null });
535
+ }
536
+ }
537
+ export class LocalGraphView extends GraphViewBase {
538
+ constructor(leaf, plugin) {
539
+ super(leaf, plugin, true);
540
+ }
541
+ getViewType() {
542
+ return LocalGraphViewType;
543
+ }
544
+ getDisplayText() {
545
+ return "Local graph";
546
+ }
547
+ getIcon() {
548
+ return "git-branch-plus";
549
+ }
550
+ getGraphData(settings) {
551
+ return buildCanonicalLocalGraph(this.app, settings, this.app.workspace.getActiveFile());
552
+ }
553
+ }
@@ -0,0 +1,6 @@
1
+ export type ViewportTransform = {
2
+ x: number;
3
+ y: number;
4
+ k: number;
5
+ };
6
+ export declare function adjustTransformForViewportResize(transform: ViewportTransform, prevWidth: number, prevHeight: number, nextWidth: number, nextHeight: number): ViewportTransform;
@@ -0,0 +1,9 @@
1
+ export function adjustTransformForViewportResize(transform, prevWidth, prevHeight, nextWidth, nextHeight) {
2
+ const worldX = (prevWidth / 2 - transform.x) / transform.k;
3
+ const worldY = (prevHeight / 2 - transform.y) / transform.k;
4
+ return {
5
+ k: transform.k,
6
+ x: nextWidth / 2 - worldX * transform.k,
7
+ y: nextHeight / 2 - worldY * transform.k,
8
+ };
9
+ }
@@ -0,0 +1,9 @@
1
+ import "./styles.css";
2
+ export { default as manifest } from "../../manifest.json";
3
+ export { GraphControlsOverlay, GraphEmbed } from "./public-components";
4
+ export { GraphPlugin } from "./graph-plugin";
5
+ export { default } from "./graph-plugin";
6
+ export { GraphRenderer } from "./graph-renderer";
7
+ export { DEFAULT_GRAPH_SETTINGS, mergeGraphSettings, moveGraphGroup, patchGraphSettings, } from "./graph-settings";
8
+ export { GraphView, GraphViewType, LocalGraphView, LocalGraphViewType, } from "./graph-view";
9
+ export type { GraphData, GraphGroupRule, GraphLink, GraphLinkType, GraphNode, GraphNodeType, GraphSearchAdapter, GraphSettings, GraphSettingsPatch, } from "./graph-types";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import "./styles.css";
2
+ export { default as manifest } from "../../manifest.json";
3
+ export { GraphControlsOverlay, GraphEmbed } from "./public-components";
4
+ export { GraphPlugin } from "./graph-plugin";
5
+ export { default } from "./graph-plugin";
6
+ export { GraphRenderer } from "./graph-renderer";
7
+ export { DEFAULT_GRAPH_SETTINGS, mergeGraphSettings, moveGraphGroup, patchGraphSettings, } from "./graph-settings";
8
+ export { GraphView, GraphViewType, LocalGraphView, LocalGraphViewType, } from "./graph-view";
@@ -0,0 +1,7 @@
1
+ import { type Component, type MountOptions } from "svelte";
2
+ export interface LocalMountComponent<T extends Record<string, unknown>> {
3
+ readonly props: T;
4
+ readonly target: Document | Element | ShadowRoot;
5
+ destroy(): void;
6
+ }
7
+ export declare function mountLocalComponent<T extends Record<string, unknown>>(component: Component<T, Record<string, unknown>, any>, options: MountOptions<T>): LocalMountComponent<T>;
@@ -0,0 +1,12 @@
1
+ import { mount, unmount } from "svelte";
2
+ export function mountLocalComponent(component, options) {
3
+ const props = $state(options.props ?? {});
4
+ const instance = mount(component, { ...options, props });
5
+ return {
6
+ props,
7
+ target: options.target,
8
+ destroy() {
9
+ void unmount(instance);
10
+ },
11
+ };
12
+ }
@@ -0,0 +1,2 @@
1
+ export { default as GraphControlsOverlay } from "./graph-controls-overlay.svelte";
2
+ export { default as GraphEmbed } from "./graph-embed.svelte";
@@ -0,0 +1,2 @@
1
+ export { default as GraphControlsOverlay } from "./graph-controls-overlay.svelte";
2
+ export { default as GraphEmbed } from "./graph-embed.svelte";