Files
foam/packages/foam-vscode/src/features/panels/dataviz.ts
meestahp d312eca3d1 [Graph-] Selection Panel (#1556)
* [Graph-] Added toggles to disable zoom & refocus movement on note selection
[Graph-] Added neighbour depth slider & refocus speed slider

* Refactored & updated the action of the refocusSpeedSlider into refocusDurationSlider to better reflect the underlying mechanism.

* Refactored the naming and action of the refocus & zoom checkboxes to avoid double negatives

* Removed refocus/Speed/Duration slider

* Added comment to graph.css detailing reasons for removal of custom style rules for UI controls

* Reverted(removed) null check added in dataviz.ts to keep focus on feature being implemented
2025-12-04 11:43:10 +01:00

207 lines
5.4 KiB
TypeScript

import * as vscode from 'vscode';
import { Foam } from '../../core/model/foam';
import { Logger } from '../../core/utils/log';
import { fromVsCodeUri } from '../../utils/vsc-utils';
import { isSome } from '../../core/utils';
import { getFoamVsCodeConfig } from '../../services/config';
export default async function activate(
context: vscode.ExtensionContext,
foamPromise: Promise<Foam>
) {
let panel: vscode.WebviewPanel | undefined = undefined;
vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration('foam.graph.style')) {
const style = getGraphStyle();
panel.webview.postMessage({
type: 'didUpdateStyle',
payload: style,
});
}
});
vscode.commands.registerCommand('foam-vscode.show-graph', async () => {
if (panel) {
panel.reveal();
} else {
const foam = await foamPromise;
panel = await createGraphPanel(foam, context);
const onFoamChanged = _ => {
updateGraph(panel, foam);
};
const noteUpdatedListener = foam.graph.onDidUpdate(onFoamChanged);
panel.onDidDispose(() => {
noteUpdatedListener.dispose();
panel = undefined;
});
vscode.window.onDidChangeActiveTextEditor(e => {
if (e?.document?.uri?.scheme !== 'untitled') {
const note = foam.workspace.get(fromVsCodeUri(e.document.uri));
if (isSome(note)) {
panel.webview.postMessage({
type: 'didSelectNote',
payload: note.uri.path,
});
}
}
});
}
});
const shouldOpenGraphOnStartup = getFoamVsCodeConfig('graph.onStartup');
if (shouldOpenGraphOnStartup) {
vscode.commands.executeCommand('foam-vscode.show-graph');
}
}
function updateGraph(panel: vscode.WebviewPanel, foam: Foam) {
const graph = generateGraphData(foam);
panel.webview.postMessage({
type: 'didUpdateGraphData',
payload: graph,
});
}
function generateGraphData(foam: Foam) {
const graph = {
nodeInfo: {},
edges: new Set(),
};
foam.workspace.list().forEach(n => {
const type = n.type === 'note' ? n.properties.type ?? 'note' : n.type;
const title = n.type === 'note' ? n.title : n.uri.getBasename();
graph.nodeInfo[n.uri.path] = {
id: n.uri.path,
type: type,
uri: n.uri,
title: cutTitle(title),
properties: n.properties,
tags: n.tags,
};
});
foam.graph.getAllConnections().forEach(c => {
graph.edges.add({
source: c.source.path,
target: c.target.path,
});
if (c.target.isPlaceholder()) {
graph.nodeInfo[c.target.path] = {
id: c.target.path,
type: 'placeholder',
uri: c.target,
title: c.target.path,
properties: {},
};
}
});
return {
nodeInfo: graph.nodeInfo,
links: Array.from(graph.edges),
};
}
function cutTitle(title: string): string {
const maxLen = vscode.workspace
.getConfiguration('foam.graph')
.get('titleMaxLength', 24);
if (maxLen > 0 && title.length > maxLen) {
return title.substring(0, maxLen).concat('...');
}
return title;
}
async function createGraphPanel(
foam: Foam,
context: vscode.ExtensionContext,
viewColumn?: vscode.ViewColumn
) {
const panel = vscode.window.createWebviewPanel(
'foam-graph',
'Foam Graph',
viewColumn ?? vscode.ViewColumn.Beside,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
panel.webview.html = await getWebviewContent(context, panel);
panel.webview.onDidReceiveMessage(
async message => {
switch (message.type) {
case 'webviewDidLoad': {
const styles = getGraphStyle();
panel.webview.postMessage({
type: 'didUpdateStyle',
payload: styles,
});
updateGraph(panel, foam);
break;
}
case 'webviewDidSelectNode': {
const noteUri = vscode.Uri.parse(message.payload);
const selectedNote = foam.workspace.get(fromVsCodeUri(noteUri));
if (isSome(selectedNote)) {
vscode.commands.executeCommand(
'vscode.open',
noteUri,
vscode.ViewColumn.One
);
}
break;
}
case 'error': {
Logger.error('An error occurred in the graph view', message.payload);
break;
}
}
},
undefined,
context.subscriptions
);
return panel;
}
async function getWebviewContent(
context: vscode.ExtensionContext,
panel: vscode.WebviewPanel
) {
const datavizUri = vscode.Uri.joinPath(
context.extensionUri,
'static',
'dataviz'
);
const getWebviewUri = (fileName: string) =>
panel.webview.asWebviewUri(vscode.Uri.joinPath(datavizUri, fileName));
const indexHtml = new TextDecoder('utf-8').decode(
await vscode.workspace.fs.readFile(
vscode.Uri.joinPath(datavizUri, 'index.html')
)
);
// Replace the script paths with the appropriate webview URI.
const filled = indexHtml.replace(
/data-replace (src|href)="[^"]+"/g,
match => {
const i = match.indexOf(' ');
const j = match.indexOf('=');
const uri = getWebviewUri(match.slice(j + 2, -1).trim());
return match.slice(i + 1, j) + '="' + uri.toString() + '"';
}
);
return filled;
}
function getGraphStyle(): object {
return vscode.workspace.getConfiguration('foam.graph').get('style');
}