mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
310 lines
8.5 KiB
TypeScript
310 lines
8.5 KiB
TypeScript
import {
|
|
ArticleListener,
|
|
ExtensionListener,
|
|
MediaListener,
|
|
ScriptListener,
|
|
TaxonomyListener,
|
|
DataListener,
|
|
SettingsListener,
|
|
FieldsListener,
|
|
LocalizationListener
|
|
} from './../listeners/panel';
|
|
import { SETTING_EXPERIMENTAL, SETTING_EXTENSIBILITY_SCRIPTS, TelemetryEvent } from '../constants';
|
|
import {
|
|
CancellationToken,
|
|
Disposable,
|
|
Uri,
|
|
Webview,
|
|
WebviewView,
|
|
WebviewViewProvider,
|
|
WebviewViewResolveContext,
|
|
window
|
|
} from 'vscode';
|
|
import { ArticleHelper, Logger, Settings } from '../helpers';
|
|
import { Command } from '../panelWebView/Command';
|
|
import { TagType } from '../panelWebView/TagType';
|
|
import { WebviewHelper } from '@estruyf/vscode';
|
|
import { Extension } from '../helpers/Extension';
|
|
import { Telemetry } from '../helpers/Telemetry';
|
|
import { GitListener, ModeListener } from '../listeners/general';
|
|
import { Folders } from '../commands';
|
|
import { basename } from 'path';
|
|
import { ignoreMsgCommand } from '../utils';
|
|
|
|
export class PanelProvider implements WebviewViewProvider, Disposable {
|
|
public static readonly viewType = 'frontMatter.explorer';
|
|
private static instance: PanelProvider;
|
|
|
|
private panel: WebviewView | null = null;
|
|
private disposable: Disposable | null = null;
|
|
|
|
private constructor(private readonly extPath: Uri) {}
|
|
|
|
/**
|
|
* Creates the singleton instance for the panel
|
|
* @param extPath
|
|
*/
|
|
public static getInstance(extPath?: Uri): PanelProvider {
|
|
if (!PanelProvider.instance) {
|
|
PanelProvider.instance = new PanelProvider(extPath as Uri);
|
|
}
|
|
|
|
return PanelProvider.instance;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the visibility of the webview
|
|
*/
|
|
get visible() {
|
|
return this.panel ? this.panel.visible : false;
|
|
}
|
|
|
|
/**
|
|
* Webview panel dispose
|
|
*/
|
|
public dispose() {
|
|
if (this.disposable) {
|
|
this.disposable.dispose();
|
|
}
|
|
}
|
|
|
|
public getWebview() {
|
|
return this.panel?.webview;
|
|
}
|
|
|
|
/**
|
|
* Default resolve webview panel
|
|
* @param webviewView
|
|
* @param context
|
|
* @param token
|
|
*/
|
|
public async resolveWebviewView(
|
|
webviewView: WebviewView,
|
|
context: WebviewViewResolveContext,
|
|
token: CancellationToken
|
|
): Promise<void> {
|
|
this.panel = webviewView;
|
|
|
|
webviewView.webview.options = {
|
|
enableScripts: true,
|
|
enableCommandUris: true
|
|
};
|
|
|
|
webviewView.webview.html = this.getWebviewContent(webviewView.webview);
|
|
|
|
this.disposable = Disposable.from(
|
|
webviewView.onDidDispose(() => {
|
|
webviewView.webview.html = '';
|
|
}, this)
|
|
);
|
|
|
|
this.updateCurrentFile();
|
|
|
|
webviewView.webview.onDidReceiveMessage(async (msg) => {
|
|
if (!ignoreMsgCommand(msg.command)) {
|
|
Logger.info(`Receiving message from panel: ${msg.command}`);
|
|
}
|
|
|
|
LocalizationListener.process(msg);
|
|
FieldsListener.process(msg);
|
|
ArticleListener.process(msg);
|
|
DataListener.process(msg);
|
|
ExtensionListener.process(msg);
|
|
MediaListener.process(msg);
|
|
ScriptListener.process(msg);
|
|
SettingsListener.process(msg);
|
|
TaxonomyListener.process(msg);
|
|
ModeListener.process(msg);
|
|
GitListener.process(msg);
|
|
});
|
|
|
|
webviewView.onDidChangeVisibility(() => {
|
|
if (this.visible) {
|
|
Telemetry.send(TelemetryEvent.openPanelWebview);
|
|
DataListener.getFileData();
|
|
}
|
|
});
|
|
|
|
window.onDidChangeActiveTextEditor(() => {
|
|
this.sendMessage({ command: Command.loading, payload: true });
|
|
|
|
if (this.visible) {
|
|
DataListener.getFileData();
|
|
}
|
|
}, this);
|
|
|
|
Settings.attachListener('panel-listener', () => {
|
|
SettingsListener.getSettings();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Post data to the panel
|
|
* @param msg
|
|
*/
|
|
public sendMessage(msg: { command: Command; payload?: any }) {
|
|
this.panel?.webview?.postMessage(msg);
|
|
}
|
|
|
|
/**
|
|
* Allows the webview panel to focus on tags or categories input
|
|
* @param tagType
|
|
*/
|
|
public triggerInputFocus(tagType: TagType) {
|
|
if (tagType === TagType.tags) {
|
|
this.sendMessage({ command: Command.focusOnTags });
|
|
} else {
|
|
this.sendMessage({ command: Command.focusOnCategories });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger all sections to close
|
|
*/
|
|
public collapseAll() {
|
|
this.sendMessage({ command: Command.closeSections });
|
|
}
|
|
|
|
/**
|
|
* On view change, update the current file that is loaded to show the correct data
|
|
*/
|
|
public async updateCurrentFile() {
|
|
const crntPanel = PanelProvider.getInstance();
|
|
if (!crntPanel.visible) {
|
|
return;
|
|
}
|
|
|
|
const filePath = ArticleHelper.getActiveFile();
|
|
if (filePath) {
|
|
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
|
DataListener.pushMetadata(article?.data);
|
|
|
|
const fileName = basename(filePath);
|
|
crntPanel.updateTitle(fileName);
|
|
} else {
|
|
crntPanel.updateTitle(undefined);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the title of the panel
|
|
* @param title
|
|
* @returns
|
|
*/
|
|
private updateTitle(title: string | undefined) {
|
|
if (!this.panel) {
|
|
return;
|
|
}
|
|
|
|
if (!title) {
|
|
this.panel.title = 'General';
|
|
return;
|
|
}
|
|
|
|
this.panel.title = title;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the webview HTML contents
|
|
* @param webView
|
|
*/
|
|
private getWebviewContent(webView: Webview): string {
|
|
const ext = Extension.getInstance();
|
|
const dashboardFile = 'panelWebView.js';
|
|
const localPort = `9001`;
|
|
const localServerUrl = `localhost:${localPort}`;
|
|
const extensionPath = ext.extensionPath;
|
|
|
|
const styleVSCodeUri = webView.asWebviewUri(
|
|
Uri.joinPath(this.extPath, 'assets/media', 'vscode.css')
|
|
);
|
|
const styleResetUri = webView.asWebviewUri(
|
|
Uri.joinPath(this.extPath, 'assets/media', 'reset.css')
|
|
);
|
|
const stylesUri = webView.asWebviewUri(
|
|
Uri.joinPath(this.extPath, 'assets/media', 'styles.css')
|
|
);
|
|
|
|
const nonce = WebviewHelper.getNonce();
|
|
|
|
const version = ext.getVersion();
|
|
const isBeta = ext.isBetaVersion();
|
|
|
|
let scriptUri = '';
|
|
const isProd = Extension.getInstance().isProductionMode;
|
|
if (isProd) {
|
|
scriptUri = webView
|
|
.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile))
|
|
.toString();
|
|
} else {
|
|
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
|
|
}
|
|
|
|
// Get experimental setting
|
|
const experimental = Settings.get(SETTING_EXPERIMENTAL);
|
|
const extensibilityScripts = Settings.get<string[]>(SETTING_EXTENSIBILITY_SCRIPTS) || [];
|
|
|
|
const scriptsToLoad: string[] = [];
|
|
if (experimental) {
|
|
for (const script of extensibilityScripts) {
|
|
if (script.startsWith('https://')) {
|
|
scriptsToLoad.push(script);
|
|
} else {
|
|
const absScriptPath = Folders.getAbsFilePath(script);
|
|
const scriptUri = webView.asWebviewUri(Uri.file(absScriptPath));
|
|
scriptsToLoad.push(scriptUri.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
const csp = [
|
|
`default-src 'none';`,
|
|
`img-src ${`vscode-file://vscode-app`} ${
|
|
webView.cspSource
|
|
} https://api.visitorbadge.io 'self' 'unsafe-inline' https://*`,
|
|
`script-src 'unsafe-eval' https://* ${
|
|
isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`
|
|
}`,
|
|
`style-src ${webView.cspSource} 'self' 'unsafe-inline' https://*`,
|
|
`font-src ${webView.cspSource}`,
|
|
`connect-src https://o1022172.ingest.sentry.io https://* ${
|
|
isProd
|
|
? ``
|
|
: `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`
|
|
}`
|
|
];
|
|
|
|
return `
|
|
<!DOCTYPE html>
|
|
<html lang="en-US">
|
|
<head>
|
|
<meta http-equiv="Content-Security-Policy" content="${csp.join('; ')}">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link href="${styleResetUri}" rel="stylesheet">
|
|
<link href="${styleVSCodeUri}" rel="stylesheet">
|
|
<link href="${stylesUri}" rel="stylesheet">
|
|
|
|
<title>Front Matter Panel</title>
|
|
</head>
|
|
<body>
|
|
<div id="app" data-isProd="${isProd}" data-environment="${
|
|
isBeta ? 'BETA' : 'main'
|
|
}" data-version="${
|
|
version.usedVersion
|
|
}" data-is-crash-disabled="${!Telemetry.isVscodeEnabled()}"></div>
|
|
|
|
${(scriptsToLoad || [])
|
|
.map((script) => {
|
|
return `<script type="module" src="${script}" nonce="${nonce}"></script>`;
|
|
})
|
|
.join('')}
|
|
|
|
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
|
|
|
|
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`panel-${version.installedVersion}`}" alt="Daily usage" />
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
}
|