Feat/478 webview api (#482)
This commit is contained in:
parent
3c09e09b25
commit
04d220eda7
8 changed files with 408 additions and 577 deletions
|
@ -5,8 +5,7 @@
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/indent": ["error", 2, { "SwitchCase": 1 }],
|
"@typescript-eslint/indent": ["error", 2, { "SwitchCase": 1 }],
|
||||||
"@typescript-eslint/member-naming": 0,
|
"@typescript-eslint/member-naming": 0
|
||||||
"@typescript-eslint/interface-name-prefix": ["error", { "prefixWithI": "always" }]
|
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true
|
||||||
|
|
24
package.json
24
package.json
|
@ -170,17 +170,18 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@moxer/vscode-theme-generator": "1.19.0",
|
"@moxer/vscode-theme-generator": "1.19.0",
|
||||||
"@types/browserify": "12.0.36",
|
"@types/browserify": "12.0.36",
|
||||||
"@types/rimraf": "2.0.2",
|
"@types/fs-extra": "8.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "2.12.0",
|
"@types/rimraf": "3.0.0",
|
||||||
"@typescript-eslint/parser": "2.12.0",
|
"@typescript-eslint/eslint-plugin": "3.3.0",
|
||||||
"browserify": "16.2.2",
|
"@typescript-eslint/parser": "3.3.0",
|
||||||
"eslint": "6.7.2",
|
"browserify": "16.5.1",
|
||||||
"eslint-config-xo-space": "0.22.0",
|
"eslint": "7.2.0",
|
||||||
"eslint-config-xo-typescript": "0.23.0",
|
"eslint-config-xo-space": "0.25.0",
|
||||||
"fs-extra": "8.1.0",
|
"eslint-config-xo-typescript": "0.31.0",
|
||||||
|
"fs-extra": "9.0.1",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"typescript": "3.7.4",
|
"typescript": "3.9.5",
|
||||||
"vscode": "1.1.36"
|
"vscode": "1.1.37"
|
||||||
},
|
},
|
||||||
"__metadata": {
|
"__metadata": {
|
||||||
"id": "dffaf5a1-2219-434b-9d87-cb586fd59260",
|
"id": "dffaf5a1-2219-434b-9d87-cb586fd59260",
|
||||||
|
@ -188,8 +189,7 @@
|
||||||
"publisherId": "e41388a1-a892-4c1e-940b-1e7c1bf43c97"
|
"publisherId": "e41388a1-a892-4c1e-940b-1e7c1bf43c97"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sanity/client": "0.147.3",
|
"@sanity/client": "1.149.16",
|
||||||
"@types/fs-extra": "8.0.1",
|
|
||||||
"opencollective": "1.0.3"
|
"opencollective": "1.0.3"
|
||||||
},
|
},
|
||||||
"collective": {
|
"collective": {
|
||||||
|
|
|
@ -24,10 +24,10 @@ const getThemeColorCustomizationsConfig = (accentColor?: string): Record<string,
|
||||||
|
|
||||||
const updateColorCustomizationsConfig = async (config: any): Promise<boolean> => {
|
const updateColorCustomizationsConfig = async (config: any): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
workspace.getConfiguration().update('workbench.colorCustomizations', config, true);
|
await workspace.getConfiguration().update('workbench.colorCustomizations', config, true);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.showErrorMessage(error);
|
await window.showErrorMessage(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ export interface IExtensionManager {
|
||||||
init: () => Promise<void>;
|
init: () => Promise<void>;
|
||||||
getPackageJSON: () => Record<string, any>;
|
getPackageJSON: () => Record<string, any>;
|
||||||
getConfig: () => MaterialThemeConfig;
|
getConfig: () => MaterialThemeConfig;
|
||||||
getInstallationType: () => {};
|
getInstallationType: () => Record<string, unknown>;
|
||||||
updateConfig: (config: Partial<MaterialThemeConfig>) => Promise<void>;
|
updateConfig: (config: Partial<MaterialThemeConfig>) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,32 @@ class ExtensionManager implements IExtensionManager {
|
||||||
await workspace.fs.writeFile(this.configFileUri, Buffer.from(JSON.stringify(newConfig), 'utf-8'));
|
await workspace.fs.writeFile(this.configFileUri, Buffer.from(JSON.stringify(newConfig), 'utf-8'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async init(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const packageJSON = this.getPackageJSON();
|
||||||
|
const userConfig = await this.getUserConfig();
|
||||||
|
this.installationType = {
|
||||||
|
update: userConfig && this.isVersionUpdate(userConfig),
|
||||||
|
firstInstall: !userConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
const configBuffer = await workspace.fs.readFile(this.configFileUri);
|
||||||
|
const configContent = Buffer.from(configBuffer).toString('utf8');
|
||||||
|
|
||||||
|
this.configJSON = JSON.parse(configContent) as MaterialThemeConfig;
|
||||||
|
|
||||||
|
const userConfigUpdate = {...this.configJSON, changelog: {lastversion: packageJSON.version}};
|
||||||
|
await workspace.fs.writeFile(
|
||||||
|
this.userConfigFileUri,
|
||||||
|
Buffer.from(JSON.stringify(userConfigUpdate), 'utf-8')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.configJSON = {accentsProperties: {}, accents: {}};
|
||||||
|
await window
|
||||||
|
.showErrorMessage(`Material Theme: there was an error while loading the configuration. Please retry or open an issue: ${String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private isVersionUpdate(userConfig: MaterialThemeConfig): boolean {
|
private isVersionUpdate(userConfig: MaterialThemeConfig): boolean {
|
||||||
const splitVersion = (input: string): {major: number; minor: number; patch: number} => {
|
const splitVersion = (input: string): {major: number; minor: number; patch: number} => {
|
||||||
const [major, minor, patch] = input.split('.').map(i => parseInt(i, 10));
|
const [major, minor, patch] = input.split('.').map(i => parseInt(i, 10));
|
||||||
|
@ -77,32 +103,6 @@ class ExtensionManager implements IExtensionManager {
|
||||||
return JSON.parse(configContent) as MaterialThemeConfig;
|
return JSON.parse(configContent) as MaterialThemeConfig;
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const packageJSON = this.getPackageJSON();
|
|
||||||
const userConfig = await this.getUserConfig();
|
|
||||||
this.installationType = {
|
|
||||||
update: userConfig && this.isVersionUpdate(userConfig),
|
|
||||||
firstInstall: !userConfig
|
|
||||||
};
|
|
||||||
|
|
||||||
const configBuffer = await workspace.fs.readFile(this.configFileUri);
|
|
||||||
const configContent = Buffer.from(configBuffer).toString('utf8');
|
|
||||||
|
|
||||||
this.configJSON = JSON.parse(configContent) as MaterialThemeConfig;
|
|
||||||
|
|
||||||
const userConfigUpdate = {...this.configJSON, changelog: {lastversion: packageJSON.version}};
|
|
||||||
await workspace.fs.writeFile(
|
|
||||||
this.userConfigFileUri,
|
|
||||||
Buffer.from(JSON.stringify(userConfigUpdate), 'utf-8')
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this.configJSON = {accentsProperties: {}, accents: {}};
|
|
||||||
window
|
|
||||||
.showErrorMessage(`Material Theme: there was an error while loading the configuration. Please retry or open an issue: ${String(error)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extensionManager = new ExtensionManager();
|
export const extensionManager = new ExtensionManager();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {WebviewController} from './Webview';
|
import {WebviewController} from './Webview';
|
||||||
|
|
||||||
export class ReleaseNotesWebview extends WebviewController<{}> {
|
export class ReleaseNotesWebview extends WebviewController<Record<string, unknown>> {
|
||||||
get filename(): string {
|
get filename(): string {
|
||||||
return 'release-notes.html';
|
return 'release-notes.html';
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export class ReleaseNotesWebview extends WebviewController<{}> {
|
||||||
* This will be called by the WebviewController when init the view
|
* This will be called by the WebviewController when init the view
|
||||||
* passing as `window.bootstrap` to the view.
|
* passing as `window.bootstrap` to the view.
|
||||||
*/
|
*/
|
||||||
getBootstrap(): {} {
|
getBootstrap(): Record<string, unknown> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,10 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
|
||||||
async show(): Promise<void> {
|
async show(): Promise<void> {
|
||||||
const html = await this.getHtml();
|
const html = await this.getHtml();
|
||||||
|
|
||||||
const rootPath = Uri
|
|
||||||
.file(this.context.asAbsolutePath('./build'))
|
|
||||||
.with({scheme: 'vscode-resource'}).toString();
|
|
||||||
|
|
||||||
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
|
|
||||||
const fullHtml = html
|
|
||||||
.replace(/{{root}}/g, rootPath)
|
|
||||||
.replace('\'{{bootstrap}}\'', JSON.stringify(this.getBootstrap()));
|
|
||||||
|
|
||||||
// If panel already opened just reveal
|
// If panel already opened just reveal
|
||||||
if (this.panel !== undefined) {
|
if (this.panel !== undefined) {
|
||||||
|
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
|
||||||
|
const fullHtml = this.replaceInPanel(html);
|
||||||
this.panel.webview.html = fullHtml;
|
this.panel.webview.html = fullHtml;
|
||||||
return this.panel.reveal(ViewColumn.Active);
|
return this.panel.reveal(ViewColumn.Active);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +64,9 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
|
||||||
this.panel.webview.onDidReceiveMessage(this.onMessageReceived, this)
|
this.panel.webview.onDidReceiveMessage(this.onMessageReceived, this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
|
||||||
|
const fullHtml = this.replaceInPanel(html);
|
||||||
|
|
||||||
this.panel.webview.html = fullHtml;
|
this.panel.webview.html = fullHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +86,16 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private replaceInPanel(html: string): string {
|
||||||
|
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
|
||||||
|
const fullHtml = html
|
||||||
|
.replace(/{{root}}/g, this.panel.webview.asWebviewUri(Uri.file(this.context.asAbsolutePath('./build'))).toString())
|
||||||
|
.replace(/{{cspSource}}/g, this.panel.webview.cspSource)
|
||||||
|
.replace('\'{{bootstrap}}\'', JSON.stringify(this.getBootstrap()));
|
||||||
|
|
||||||
|
return fullHtml;
|
||||||
|
}
|
||||||
|
|
||||||
private async getHtml(): Promise<string> {
|
private async getHtml(): Promise<string> {
|
||||||
const doc = await Workspace
|
const doc = await Workspace
|
||||||
.openTextDocument(this.context.asAbsolutePath(path.join('build/ui', this.filename)));
|
.openTextDocument(this.context.asAbsolutePath(path.join('build/ui', this.filename)));
|
||||||
|
@ -127,7 +133,7 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
|
||||||
this.panel = undefined;
|
this.panel = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onViewStateChanged(event: WebviewPanelOnDidChangeViewStateEvent): Promise<boolean | void> {
|
private async onViewStateChanged(event: WebviewPanelOnDidChangeViewStateEvent): Promise<void> {
|
||||||
console.log('WebviewEditor.onViewStateChanged', event.webviewPanel.visible);
|
console.log('WebviewEditor.onViewStateChanged', event.webviewPanel.visible);
|
||||||
|
|
||||||
if (!this.invalidateOnVisible || !event.webviewPanel.visible) {
|
if (!this.invalidateOnVisible || !event.webviewPanel.visible) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ export interface IPostNormalized {
|
||||||
}
|
}
|
||||||
export interface ISettingsChangedMessage {
|
export interface ISettingsChangedMessage {
|
||||||
type: 'settingsChanged';
|
type: 'settingsChanged';
|
||||||
config: {};
|
config: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISaveSettingsMessage {
|
export interface ISaveSettingsMessage {
|
||||||
|
@ -36,18 +36,17 @@ export type Message = ISaveSettingsMessage | ISettingsChangedMessage;
|
||||||
export type Invalidates = 'all' | 'config' | undefined;
|
export type Invalidates = 'all' | 'config' | undefined;
|
||||||
|
|
||||||
export interface IBootstrap {
|
export interface IBootstrap {
|
||||||
config: {};
|
config: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISettingsBootstrap extends IBootstrap {
|
export interface ISettingsBootstrap extends IBootstrap {
|
||||||
scope: 'user' | 'workspace';
|
scope: 'user' | 'workspace';
|
||||||
scopes: Array<['user' | 'workspace', string]>;
|
scopes: Array<['user' | 'workspace', string]>;
|
||||||
defaults: {};
|
defaults: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
|
|
||||||
interface Window {
|
interface Window {
|
||||||
bootstrap: IBootstrap | ISettingsBootstrap | {};
|
bootstrap: IBootstrap | ISettingsBootstrap | Record<string, unknown>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue