diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..5d875945 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [estruyf] +custom: ["https://www.buymeacoffee.com/zMeFRy9"] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6471eb9e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Release +on: + release: + types: + - published + workflow_dispatch: + +jobs: + build: + name: "Build and release" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 14 + registry-url: https://registry.npmjs.org/ + + - name: Install the dependencies + run: npm i + + - name: Install vsce + run: npm i -g vsce + + - name: Publish + run: vsce publish -p ${{ secrets.VSCE_PAT }} + diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e63578..a68cf937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [1.15.0] - 2020-05-04 + +- Added the ability to add your own custom scripts as panel actions. + ## [1.14.0] - 2020-03-19 - New links added to the Front Matter panel to reveal the file and folder. diff --git a/README.md b/README.md index 8ea99ecb..18c09a82 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,24 @@ To leverage most of the capabilities of the extension. SEO information and every > **Info**: By default, the tags/categories picker allows you to insert existing and none tags/categories. When you enter a none existing tag/category, the panel shows an add `+` icon in front of that button. This functionality allows you to store this tag/category in your settings. If you want to disable this feature, you can do that by setting the `frontMatter.panel.freeform` setting to `false`. +Since version `1.15.0`, the extension allows you to create your own custom actions, by running Node.js scripts from your project. In order to use this functionality, you will need to configure the [`frontMatter.custom.scripts`](#frontMatter.custom.scripts) setting for your project. + +Once a custom action has been configured, it will appear on the Front Matter panel. + +![](./assets/custom-actions.png) + +The current workspace- and file-path will be passed as an argument. In your script fetch these arguments as follows: + +```javascript +const arguments = process.argv; +const workspaceArg = arguments[2]; +const fileArg = arguments[3]; +``` + +The output of the script will be passed as a notification, and it allows you to copy the output. + +![](./assets/custom-action-notification.png) + ## Creating articles from templates By default, the extension looks for files stored in a `.templates` folder that should be located in your website project's root. @@ -170,6 +188,19 @@ Specifies the modified date field name to use in your Front Matter. Default valu } ``` +### `frontMatter.custom.scripts` + +Allows you to specify a title and script path (starting relative from the root of your project). These values will be used to create custom actions on the Front Matter panel. Default value: `[]`. + +```json +{ + "frontMatter.custom.scripts": [{ + "title": "Generate social image", + "script": "./scripts/social-img.js" + }] +} +``` + ## Usage - Start by opening the command prompt: diff --git a/assets/custom-action-notification.png b/assets/custom-action-notification.png new file mode 100644 index 00000000..65b6a193 Binary files /dev/null and b/assets/custom-action-notification.png differ diff --git a/assets/custom-actions.png b/assets/custom-actions.png new file mode 100644 index 00000000..d2594b88 Binary files /dev/null and b/assets/custom-actions.png differ diff --git a/package.json b/package.json index ec49ea34..f69cf909 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,11 @@ "type": "boolean", "default": true, "markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true." + }, + "frontMatter.custom.scripts": { + "type": "array", + "default": [], + "markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument." } } }, diff --git a/src/constants/settings.ts b/src/constants/settings.ts index c690f22f..61388d64 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -22,4 +22,6 @@ export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength"; export const SETTING_TEMPLATES_FOLDER = "templates.folder"; export const SETTING_TEMPLATES_PREFIX = "templates.prefix"; -export const SETTING_PANEL_FREEFORM = "panel.freeform"; \ No newline at end of file +export const SETTING_PANEL_FREEFORM = "panel.freeform"; + +export const SETTING_CUSTOM_SCRIPTS = "custom.scripts"; \ No newline at end of file diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 03d6061a..054b456e 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -5,6 +5,7 @@ export interface PanelSettings { tags: string[]; categories: string[]; freeform: boolean; + scripts: CustomScript[]; } export interface SEO { @@ -15,4 +16,9 @@ export interface SEO { export interface Slug { prefix: number; suffix: number; +} + +export interface CustomScript { + title: string; + script: string; } \ No newline at end of file diff --git a/src/viewpanel/CommandToCode.ts b/src/viewpanel/CommandToCode.ts index 16feb616..e3b1630f 100644 --- a/src/viewpanel/CommandToCode.ts +++ b/src/viewpanel/CommandToCode.ts @@ -10,5 +10,6 @@ export enum CommandToCode { addCategoryToSettings = "add-category", openSettings = "open-settings", openFile = "open-file", - openProject = "open-project" + openProject = "open-project", + runCustomScript = "custom-script" } \ No newline at end of file diff --git a/src/viewpanel/ViewPanel.tsx b/src/viewpanel/ViewPanel.tsx index 8b6bdccb..cbdd9bae 100644 --- a/src/viewpanel/ViewPanel.tsx +++ b/src/viewpanel/ViewPanel.tsx @@ -48,6 +48,7 @@ export const ViewPanel: React.FunctionComponent = (props: React { settings && metadata && } + { (settings && settings.tags && settings.tags.length > 0) && ( = (props: React.Pro { metadata && typeof metadata.draft !== undefined && } + + { + (settings && settings.scripts && settings.scripts.length > 0) && ( + settings.scripts.map((value) => ( + + )) + ) + } ); }; \ No newline at end of file diff --git a/src/viewpanel/components/CustomScript.tsx b/src/viewpanel/components/CustomScript.tsx new file mode 100644 index 00000000..bffa4f9d --- /dev/null +++ b/src/viewpanel/components/CustomScript.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { CommandToCode } from '../CommandToCode'; +import { MessageHelper } from '../helper/MessageHelper'; + +export interface ICustomScriptProps { + title: string; + script: string; +} + +export const CustomScript: React.FunctionComponent = ({title, script}: React.PropsWithChildren) => { + + const runCustomScript = () => { + MessageHelper.sendMessage(CommandToCode.runCustomScript, { title, script }); + }; + + return ( +
+ +
+ ); +}; \ No newline at end of file diff --git a/src/webview/ExplorerView.ts b/src/webview/ExplorerView.ts index 92df5cb0..19a2c247 100644 --- a/src/webview/ExplorerView.ts +++ b/src/webview/ExplorerView.ts @@ -1,6 +1,7 @@ +import { SETTING_CUSTOM_SCRIPTS } from './../constants/settings'; import * as os from 'os'; -import { PanelSettings } from './../models/PanelSettings'; -import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands } from "vscode"; +import { PanelSettings, CustomScript } from './../models/PanelSettings'; +import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode"; import { CONFIG_KEY, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants"; import { ArticleHelper, SettingsHelper } from "../helpers"; import { Command } from "../viewpanel/Command"; @@ -9,6 +10,7 @@ import { Article } from '../commands'; import { TagType } from '../viewpanel/TagType'; import { TaxonomyType } from '../models'; import { exec } from 'child_process'; +import * as path from 'path'; export class ExplorerView implements WebviewViewProvider, Disposable { @@ -108,6 +110,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable { case CommandToCode.openFile: commands.executeCommand('revealFileInOS'); break; + case CommandToCode.runCustomScript: + this.runCustomScript(msg); + break; case CommandToCode.openProject: const wsFolders = workspace.workspaceFolders; if (wsFolders && wsFolders.length > 0) { @@ -162,6 +167,36 @@ export class ExplorerView implements WebviewViewProvider, Disposable { } } + /** + * Run a custom script + * @param msg + */ + private runCustomScript(msg: { command: string, data: any}) { + const config = workspace.getConfiguration(CONFIG_KEY); + const scripts: CustomScript[] | undefined = config.get(SETTING_CUSTOM_SCRIPTS); + + + if (msg?.data?.title && msg?.data?.script && scripts && scripts.find((s: CustomScript) => s.title === msg?.data?.title)?.title) { + const editor = window.activeTextEditor; + const wsFolders = workspace.workspaceFolders; + if (wsFolders && wsFolders.length > 0) { + const wsPath = wsFolders[0].uri.fsPath; + exec(`node ${path.join(wsPath, msg.data.script)} "${wsPath}" "${editor?.document.uri.fsPath}"`, (error, stdout) => { + if (error) { + window.showErrorMessage(`${msg?.data?.title}: ${error.message}`); + return; + } + + window.showInformationMessage(`${msg?.data?.title}: ${stdout || "Executed your custom script."}`, 'Copy output').then(value => { + if (value === 'Copy output') { + vscodeEnv.clipboard.writeText(stdout); + } + }); + }); + } + } + } + /** * Retrieve the extension settings */ @@ -181,7 +216,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable { }, tags: config.get(SETTING_TAXONOMY_TAGS) || [], categories: config.get(SETTING_TAXONOMY_CATEGORIES) || [], - freeform: config.get(SETTING_PANEL_FREEFORM) + freeform: config.get(SETTING_PANEL_FREEFORM), + scripts: config.get(SETTING_CUSTOM_SCRIPTS) } as PanelSettings }); }