forked from iarv/vscode-front-matter
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b1dc55da7 | ||
|
|
bfbc81c90f | ||
|
|
cefbf74582 | ||
|
|
0780842365 | ||
|
|
1f94a87993 | ||
|
|
157228edb5 | ||
|
|
10268fc60f | ||
|
|
e51911ed83 | ||
|
|
e1429bc666 |
@@ -1,5 +1,14 @@
|
||||
# Change Log
|
||||
|
||||
## [2.2.0] - 2020-08-06
|
||||
|
||||
- [#28](https://github.com/estruyf/vscode-front-matter/issues/28): Align the file its name with the article slug
|
||||
- [#47](https://github.com/estruyf/vscode-front-matter/issues/47): Fix when table shows only value `0`
|
||||
- [#48](https://github.com/estruyf/vscode-front-matter/issues/48): Added new folder registration message + notification helper
|
||||
- [#49](https://github.com/estruyf/vscode-front-matter/issues/49): New initialize project command
|
||||
- [#50](https://github.com/estruyf/vscode-front-matter/issues/50): Fix in the table rendering of rows
|
||||
- [#51](https://github.com/estruyf/vscode-front-matter/issues/51): Panel actions base view enhanced to show project actions and information
|
||||
|
||||
## [2.1.0] - 2020-08-04
|
||||
|
||||
- [#44](https://github.com/estruyf/vscode-front-matter/issues/45): Added article creation command
|
||||
|
||||
14
README.md
14
README.md
@@ -53,7 +53,13 @@ Initially, this panel has been created to make it easier to add tags and categor
|
||||
|
||||
To leverage most of the capabilities of the extension. SEO information and everyday actions like slug optimization, updating the date, and publish/drafting the article.
|
||||
|
||||
The panel consists of the following sections:
|
||||
When the panel opens on a none markdown file, it will contain the following sections:
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.2.0/baseview.png" alt="Base view" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
When you open the Front Matter panel on a Markdown file, you get to see the following sections:
|
||||
|
||||
**SEO Status**
|
||||
|
||||
@@ -133,6 +139,10 @@ When adding files in the folder, you'll be able to run the `Front Matter: New ar
|
||||
|
||||
## Available commands
|
||||
|
||||
**Front Matter: Initialize project**
|
||||
|
||||
This command will initialize the project with a template folder and an article template. It makes it easier to get you started with the extension and creating your content.
|
||||
|
||||
**Front Matter: Create content**
|
||||
|
||||
With this command, you can easily create content in your project within the registered folders and provided templates.
|
||||
@@ -198,7 +208,7 @@ title: Just a sample page with a title
|
||||
slug: sample-page-title
|
||||
```
|
||||
|
||||
You can also specify a prefix and suffix, which can be added to the slug if you want. Use the following settings to do this: `frontMatter.taxonomy.slugPrefix` and `frontMatter.taxonomy.slugSuffix`. By default, both options are not provided and will not add anything to the slug.
|
||||
You can also specify a prefix and suffix, which can be added to the slug if you want. Use the following settings to do this: `frontMatter.taxonomy.slugPrefix` and `frontMatter.taxonomy.slugSuffix`. By default, both options are not provided and will not add anything to the slug. Another setting is to allow you to sync the filename with the generated slug. The setting you need to turn on enable for this is `frontMatter.taxonomy.alignFilename`.
|
||||
|
||||
> **Info**: At the moment, the extension only supports English stopwords.
|
||||
|
||||
|
||||
@@ -248,7 +248,9 @@
|
||||
filter: contrast(60%);
|
||||
}
|
||||
|
||||
.article__actions > * + * {
|
||||
.article__actions > * + *,
|
||||
.base__actions > * + *,
|
||||
.base__information > * + * {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
||||
|
||||
BIN
assets/v2.2.0/baseview.png
Normal file
BIN
assets/v2.2.0/baseview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vscode-front-matter",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -40,9 +40,9 @@
|
||||
}
|
||||
},
|
||||
"@bendera/vscode-webview-elements": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@bendera/vscode-webview-elements/-/vscode-webview-elements-0.5.0.tgz",
|
||||
"integrity": "sha512-mlZi8RG+tsqr1bDbA7H82spyWzZyj/tsyHb9eta7kE0xRhvx7ON6w6DG4PONew1rVJv1knTKOAW4iQKVBYQhVQ==",
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@bendera/vscode-webview-elements/-/vscode-webview-elements-0.6.2.tgz",
|
||||
"integrity": "sha512-smtr+KvCKV2MwjVrmyvrhonpaXVpxCjTMXUQOwDwWSAQ42x5pnlpjCGElz2dljc5VHS1Mh1ovPSQ/P3jAm7vMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lit-element": "^2.5.1"
|
||||
|
||||
60
package.json
60
package.json
@@ -3,7 +3,7 @@
|
||||
"displayName": "Front Matter",
|
||||
"description": "Simplifies working with front matter of your articles. Useful extension when you are using a static site generator like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...",
|
||||
"icon": "assets/front-matter.png",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
@@ -56,6 +56,7 @@
|
||||
"onCommand:frontMatter.registerFolder",
|
||||
"onCommand:frontMatter.unregisterFolder",
|
||||
"onCommand:frontMatter.createContent",
|
||||
"onCommand:frontMatter.init",
|
||||
"onView:frontMatter.explorer"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
@@ -113,6 +114,11 @@
|
||||
"type": "string",
|
||||
"markdownDescription": "Specify a suffix for the slug"
|
||||
},
|
||||
"frontMatter.taxonomy.alignFilename": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Align the filename with the new slug when it gets generated."
|
||||
},
|
||||
"frontMatter.taxonomy.indentArrays": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
@@ -185,55 +191,73 @@
|
||||
"commands": [
|
||||
{
|
||||
"command": "frontMatter.insertTags",
|
||||
"title": "Front Matter: Insert tags"
|
||||
"title": "Insert tags",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.insertCategories",
|
||||
"title": "Front Matter: Insert categories"
|
||||
"title": "Insert categories",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.createTag",
|
||||
"title": "Front Matter: Create tag"
|
||||
"title": "Create tag",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.createCategory",
|
||||
"title": "Front Matter: Create category"
|
||||
"title": "Create category",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.exportTaxonomy",
|
||||
"title": "Front Matter: Export all tags & categories to your settings"
|
||||
"title": "Export all tags & categories to your settings",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.remap",
|
||||
"title": "Front Matter: Remap or remove tag/category in all articles"
|
||||
"title": "Remap or remove tag/category in all articles",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.setDate",
|
||||
"title": "Front Matter: Set current date"
|
||||
"title": "Set current date",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.setLastModifiedDate",
|
||||
"title": "Front Matter: Set lastmod date"
|
||||
"title": "Set lastmod date",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.generateSlug",
|
||||
"title": "Front Matter: Generate slug based on article title"
|
||||
"title": "Generate slug based on article title",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.createFromTemplate",
|
||||
"title": "Front Matter: New article from template"
|
||||
"title": "New article from template",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.registerFolder",
|
||||
"title": "Front Matter: Register folder"
|
||||
"title": "Register folder",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.unregisterFolder",
|
||||
"title": "Front Matter: Unregister folder"
|
||||
"title": "Unregister folder",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.createContent",
|
||||
"title": "Front Matter: Create content"
|
||||
"title": "Create content",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.init",
|
||||
"title": "Initialize project",
|
||||
"category": "Front matter"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -253,6 +277,12 @@
|
||||
"when": "explorerResourceIsFolder && resourcePath in frontMatter.registeredFolders",
|
||||
"group": "Front Matter@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "frontMatter.init",
|
||||
"when": "frontMatterCanInit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"grammars": [
|
||||
@@ -273,7 +303,7 @@
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bendera/vscode-webview-elements": "0.5.0",
|
||||
"@bendera/vscode-webview-elements": "0.6.2",
|
||||
"@iarna/toml": "2.2.3",
|
||||
"@types/glob": "7.1.3",
|
||||
"@types/js-yaml": "3.12.1",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { SETTING_MODIFIED_FIELD } from './../constants/settings';
|
||||
import { SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX } from './../constants/settings';
|
||||
import * as vscode from 'vscode';
|
||||
import { TaxonomyType } from "../models";
|
||||
import { CONFIG_KEY, SETTING_DATE_FORMAT, EXTENSION_NAME, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_DATE_FIELD } from "../constants/settings";
|
||||
import { CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_DATE_FIELD } from "../constants/settings";
|
||||
import { format } from "date-fns";
|
||||
import { ArticleHelper, SettingsHelper, SlugHelper } from '../helpers';
|
||||
import matter = require('gray-matter');
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { extname, basename } from 'path';
|
||||
|
||||
|
||||
export class Article {
|
||||
@@ -53,7 +55,7 @@ export class Article {
|
||||
}
|
||||
|
||||
if (options.length === 0) {
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
|
||||
Notifications.info(`No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ export class Article {
|
||||
try {
|
||||
ArticleHelper.update(editor, article);
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(`${EXTENSION_NAME}: Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
|
||||
Notifications.error(`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
@@ -135,7 +137,7 @@ export class Article {
|
||||
|
||||
ArticleHelper.update(editor, article);
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(`${EXTENSION_NAME}: Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
|
||||
Notifications.error(`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
@@ -143,11 +145,14 @@ export class Article {
|
||||
/**
|
||||
* Generate the slug based on the article title
|
||||
*/
|
||||
public static generateSlug() {
|
||||
public static async generateSlug() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const prefix = config.get(SETTING_SLUG_PREFIX) as string;
|
||||
const suffix = config.get(SETTING_SLUG_SUFFIX) as string;
|
||||
const updateFileName = config.get(SETTING_SLUG_UPDATE_FILE_NAME) as string;
|
||||
const filePrefix = config.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -158,10 +163,42 @@ export class Article {
|
||||
}
|
||||
|
||||
const articleTitle: string = article.data["title"];
|
||||
const slug = SlugHelper.createSlug(articleTitle);
|
||||
let slug = SlugHelper.createSlug(articleTitle);
|
||||
if (slug) {
|
||||
article.data["slug"] = `${prefix}${slug}${suffix}`;
|
||||
slug = `${prefix}${slug}${suffix}`;
|
||||
article.data["slug"] = slug;
|
||||
ArticleHelper.update(editor, article);
|
||||
|
||||
// Check if the file name should be updated by the slug
|
||||
// This is required for systems like Jekyll
|
||||
if (updateFileName) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
const ext = extname(editor.document.fileName);
|
||||
const fileName = basename(editor.document.fileName);
|
||||
|
||||
let slugName = slug.startsWith("/") ? slug.substring(1) : slug;
|
||||
slugName = slugName.endsWith("/") ? slugName.substring(0, slugName.length - 1) : slugName;
|
||||
|
||||
let newFileName = `${slugName}${ext}`;
|
||||
if (filePrefix && typeof filePrefix === "string") {
|
||||
newFileName = `${format(new Date(), filePrefix)}-${newFileName}`;
|
||||
}
|
||||
|
||||
const newPath = editor.document.uri.fsPath.replace(fileName, newFileName);
|
||||
|
||||
try {
|
||||
await editor.document.save();
|
||||
|
||||
await vscode.workspace.fs.rename(editor.document.uri, vscode.Uri.file(newPath), {
|
||||
overwrite: false
|
||||
});
|
||||
} catch (e) {
|
||||
Notifications.error(`Failed to rename file.`);
|
||||
console.log(e?.message || e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { commands, Uri, workspace, window } from "vscode";
|
||||
import { CONFIG_KEY, EXTENSION_NAME, SETTINGS_CONTENT_FOLDERS } from "../constants";
|
||||
import { CONFIG_KEY, SETTINGS_CONTENT_FOLDERS } from "../constants";
|
||||
import { basename } from "path";
|
||||
import { ContentFolder } from "../models";
|
||||
import { ContentFolder, FolderInfo } from "../models";
|
||||
import uniqBy = require("lodash.uniqby");
|
||||
import { Template } from "./Template";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { CONTEXT } from "../constants/context";
|
||||
|
||||
export class Folders {
|
||||
|
||||
@@ -15,7 +17,7 @@ export class Folders {
|
||||
const folders = Folders.get();
|
||||
|
||||
if (!folders || folders.length === 0) {
|
||||
window.showWarningMessage(`${EXTENSION_NAME}: There are no known content locations defined in this project.`);
|
||||
Notifications.warning(`There are no known content locations defined in this project.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,7 +26,7 @@ export class Folders {
|
||||
});
|
||||
|
||||
if (!selectedFolder) {
|
||||
window.showWarningMessage(`${EXTENSION_NAME}: You didn't select a place where you wanted to create your content.`);
|
||||
Notifications.warning(`You didn't select a place where you wanted to create your content.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +52,8 @@ export class Folders {
|
||||
const exists = folders.find(f => f.paths.includes(folder.fsPath) || f.paths.includes(wslPath));
|
||||
|
||||
if (exists) {
|
||||
return window.showInformationMessage(`Folder is already registered`);
|
||||
Notifications.warning(`Folder is already registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
const folderName = await window.showInputBox({
|
||||
@@ -67,6 +70,8 @@ export class Folders {
|
||||
|
||||
folders = uniqBy(folders, f => f.fsPath);
|
||||
await Folders.update(folders);
|
||||
|
||||
Notifications.info(`Folder registered`);
|
||||
}
|
||||
|
||||
Folders.updateVsCodeCtx();
|
||||
@@ -95,7 +100,7 @@ export class Folders {
|
||||
for (const folder of folders) {
|
||||
allFolders = [...allFolders, ...folder.paths]
|
||||
}
|
||||
commands.executeCommand('setContext', 'frontMatter.registeredFolders', allFolders);
|
||||
commands.executeCommand('setContext', CONTEXT.registeredFolders, allFolders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,6 +118,34 @@ export class Folders {
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered folders information
|
||||
*/
|
||||
public static async getInfo(): Promise<FolderInfo[] | null> {
|
||||
const folders = Folders.get();
|
||||
if (folders && folders.length > 0) {
|
||||
let folderInfo: FolderInfo[] = [];
|
||||
|
||||
for (const folder of folders) {
|
||||
try {
|
||||
const files = await workspace.fs.readDirectory(Uri.file(folder.fsPath));
|
||||
if (files) {
|
||||
folderInfo.push({
|
||||
title: folder.title,
|
||||
files: files.length
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// Skip the current folder
|
||||
}
|
||||
}
|
||||
|
||||
return folderInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder settings
|
||||
* @returns
|
||||
|
||||
49
src/commands/Project.ts
Normal file
49
src/commands/Project.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { workspace, Uri } from "vscode";
|
||||
import { CONFIG_KEY, SETTING_TEMPLATES_FOLDER } from "../constants";
|
||||
import { join } from "path";
|
||||
import * as fs from "fs";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
|
||||
export class Project {
|
||||
|
||||
private static content = `---
|
||||
title: "{{name}}"
|
||||
slug: "/{{kebabCase name}}/"
|
||||
description:
|
||||
author:
|
||||
date: 2019-08-22T15:20:28.000Z
|
||||
lastmod: 2019-08-22T15:20:28.000Z
|
||||
draft: true
|
||||
tags: []
|
||||
categories: []
|
||||
---
|
||||
`;
|
||||
|
||||
/**
|
||||
* Initialize a new "Project" instance.
|
||||
*/
|
||||
public static async init() {
|
||||
try {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const folder = config.get<string>(SETTING_TEMPLATES_FOLDER);
|
||||
|
||||
const workspaceFolders = workspace.workspaceFolders;
|
||||
|
||||
if (!folder || !workspaceFolders || workspaceFolders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceFolder = workspaceFolders[0];
|
||||
const templatePath = Uri.file(join(workspaceFolder.uri.fsPath, folder));
|
||||
const article = Uri.file(join(templatePath.fsPath, "article.md"));
|
||||
|
||||
await workspace.fs.createDirectory(templatePath);
|
||||
|
||||
fs.writeFileSync(article.fsPath, Project.content, { encoding: "utf-8" });
|
||||
|
||||
Notifications.info("Project initialized successfully.");
|
||||
} catch (err) {
|
||||
Notifications.error(`Sorry, something went wrong - ${err?.message || err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { CONFIG_KEY, SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, EXTENSI
|
||||
import { ArticleHelper, SettingsHelper, FilesHelper } from '../helpers';
|
||||
import { TomlEngine, getFmLanguage, getFormatOpts } from '../helpers/TomlEngine';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
|
||||
export class Settings {
|
||||
|
||||
@@ -29,7 +30,7 @@ export class Settings {
|
||||
}
|
||||
|
||||
if (options.find(o => o === newOption)) {
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: The provided ${type === TaxonomyType.Tag ? "tag" : "category"} already exists.`);
|
||||
Notifications.info(`The provided ${type === TaxonomyType.Tag ? "tag" : "category"} already exists.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,7 +146,7 @@ export class Settings {
|
||||
await config.update(SETTING_TAXONOMY_CATEGORIES, crntCategories);
|
||||
|
||||
// Done
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: Export completed. Tags: ${crntTags.length} - Categories: ${crntCategories.length}.`);
|
||||
Notifications.info(`Export completed. Tags: ${crntTags.length} - Categories: ${crntCategories.length}.`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ export class Settings {
|
||||
let options = SettingsHelper.getTaxonomy(type);
|
||||
|
||||
if (!options || options.length === 0) {
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
|
||||
Notifications.info(`No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,7 +274,7 @@ export class Settings {
|
||||
}
|
||||
await SettingsHelper.update(type, options);
|
||||
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: ${newOptionValue ? "Remapping" : "Deleation"} of the ${selectedOption} ${type === TaxonomyType.Tag ? "tag" : "category"} completed.`);
|
||||
Notifications.info(`${newOptionValue ? "Remapping" : "Deleation"} of the ${selectedOption} ${type === TaxonomyType.Tag ? "tag" : "category"} completed.`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export class StatusListener {
|
||||
const publishMsg = "to publish";
|
||||
|
||||
let editor = vscode.window.activeTextEditor;
|
||||
if (editor && editor.document && (editor.document.languageId.toLowerCase() === "markdown" || editor.document.languageId.toLowerCase() === "mdx")) {
|
||||
if (editor && ArticleHelper.isMarkdownDile()) {
|
||||
try {
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
|
||||
|
||||
@@ -1,14 +1,47 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { CONFIG_KEY, EXTENSION_NAME, SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants';
|
||||
import { CONFIG_KEY, SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants';
|
||||
import { format } from 'date-fns';
|
||||
import sanitize from '../helpers/Sanitize';
|
||||
import { ArticleHelper } from '../helpers';
|
||||
import { Article } from '.';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
|
||||
export class Template {
|
||||
|
||||
/**
|
||||
* Check if the template folder is available
|
||||
*/
|
||||
public static async init() {
|
||||
const isInitialized = await Template.isInitialized();
|
||||
await vscode.commands.executeCommand('setContext', CONTEXT.canInit, !isInitialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is already initialized
|
||||
*/
|
||||
public static async isInitialized() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const folder = config.get<string>(SETTING_TEMPLATES_FOLDER);
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
|
||||
if (!folder || !workspaceFolders || workspaceFolders.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const workspaceFolder = workspaceFolders[0];
|
||||
const templatePath = vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, folder));
|
||||
|
||||
try {
|
||||
await vscode.workspace.fs.stat(templatePath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from a template
|
||||
*/
|
||||
@@ -18,18 +51,18 @@ export class Template {
|
||||
const prefix = config.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
|
||||
if (!folderPath) {
|
||||
this.showNoTemplates(`Incorrect project folder path retrieved.`);
|
||||
Notifications.warning(`Incorrect project folder path retrieved.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!folder) {
|
||||
this.showNoTemplates(`No templates found.`);
|
||||
Notifications.warning(`No templates found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const templates = await vscode.workspace.findFiles(`${folder}/**/*`, "**/node_modules/**,**/archetypes/**");
|
||||
if (!templates || templates.length === 0) {
|
||||
this.showNoTemplates(`No templates found.`);
|
||||
Notifications.warning(`No templates found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +70,7 @@ export class Template {
|
||||
placeHolder: `Select the article template to use`
|
||||
});
|
||||
if (!selectedTemplate) {
|
||||
this.showNoTemplates(`No template selected.`);
|
||||
Notifications.warning(`No template selected.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,14 +79,14 @@ export class Template {
|
||||
placeHolder: `Article title`
|
||||
});
|
||||
if (!titleValue) {
|
||||
this.showNoTemplates(`You did not specify an article title.`);
|
||||
Notifications.warning(`You did not specify an article title.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the template read
|
||||
const template = templates.find(t => t.fsPath.endsWith(selectedTemplate));
|
||||
if (!template) {
|
||||
this.showNoTemplates(`Article template could not be found.`);
|
||||
Notifications.warning(`Article template could not be found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,7 +99,7 @@ export class Template {
|
||||
|
||||
const newFilePath = path.join(folderPath, newFileName);
|
||||
if (fs.existsSync(newFilePath)) {
|
||||
this.showNoTemplates(`File already exists, please remove it before creating a new one with the same title.`);
|
||||
Notifications.warning(`File already exists, please remove it before creating a new one with the same title.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +109,7 @@ export class Template {
|
||||
// Update the properties inside the template
|
||||
let frontMatter = ArticleHelper.getFrontMatterByPath(newFilePath);
|
||||
if (!frontMatter) {
|
||||
this.showNoTemplates(`Something failed when retrieving the newly created file.`);
|
||||
Notifications.warning(`Something failed when retrieving the newly created file.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,13 +134,6 @@ export class Template {
|
||||
vscode.window.showTextDocument(txtDoc);
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: Your new article has been created.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a warning message when no templates are found
|
||||
*/
|
||||
private static showNoTemplates(value: string) {
|
||||
vscode.window.showWarningMessage(`${EXTENSION_NAME}: ${value}`);
|
||||
Notifications.info(`Your new article has been created.`);
|
||||
}
|
||||
}
|
||||
23
src/constants/Extension.ts
Normal file
23
src/constants/Extension.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
const extensionName = "frontMatter";
|
||||
|
||||
export const getCommandName = (command: string) => {
|
||||
return `${extensionName}.${command}`;
|
||||
};
|
||||
|
||||
export const COMMAND_NAME = {
|
||||
init: getCommandName("init"),
|
||||
insertTags: getCommandName("insertTags"),
|
||||
insertCategories: getCommandName("insertCategories"),
|
||||
createTag: getCommandName("createTag"),
|
||||
createCategory: getCommandName("createCategory"),
|
||||
exportTaxonomy: getCommandName("exportTaxonomy"),
|
||||
remap: getCommandName("remap"),
|
||||
setDate: getCommandName("setDate"),
|
||||
setLastModifiedDate: getCommandName("setLastModifiedDate"),
|
||||
generateSlug: getCommandName("generateSlug"),
|
||||
createFromTemplate: getCommandName("createFromTemplate"),
|
||||
toggleDraft: getCommandName("toggleDraft"),
|
||||
registerFolder: getCommandName("registerFolder"),
|
||||
unregisterFolder: getCommandName("unregisterFolder"),
|
||||
createContent: getCommandName("createContent")
|
||||
};
|
||||
6
src/constants/context.ts
Normal file
6
src/constants/context.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
export const CONTEXT = {
|
||||
canInit: "frontMatterCanInit",
|
||||
registeredFolders: 'frontMatter.registeredFolders'
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export const SETTING_MODIFIED_FIELD = "taxonomy.modifiedField";
|
||||
|
||||
export const SETTING_SLUG_PREFIX = "taxonomy.slugPrefix";
|
||||
export const SETTING_SLUG_SUFFIX = "taxonomy.slugSuffix";
|
||||
export const SETTING_SLUG_UPDATE_FILE_NAME = "taxonomy.alignFilename";
|
||||
|
||||
export const SETTING_INDENT_ARRAY = "taxonomy.indentArrays";
|
||||
export const SETTING_REMOVE_QUOTES = "taxonomy.noPropertyValueQuotes";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { Article, Settings, StatusListener } from './commands';
|
||||
import { Folders } from './commands/Folders';
|
||||
import { Project } from './commands/Project';
|
||||
import { Template } from './commands/Template';
|
||||
import { COMMAND_NAME } from './constants/Extension';
|
||||
import { TaxonomyType } from './models';
|
||||
import { TagType } from './viewpanel/TagType';
|
||||
import { ExplorerView } from './webview/ExplorerView';
|
||||
@@ -20,68 +22,78 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
}
|
||||
});
|
||||
|
||||
let insertTags = vscode.commands.registerCommand('frontMatter.insertTags', async () => {
|
||||
let insertTags = vscode.commands.registerCommand(COMMAND_NAME.insertTags, async () => {
|
||||
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
|
||||
await vscode.commands.executeCommand('workbench.action.focusSideBar');
|
||||
explorerSidebar.triggerInputFocus(TagType.tags);
|
||||
});
|
||||
|
||||
let insertCategories = vscode.commands.registerCommand('frontMatter.insertCategories', async () => {
|
||||
let insertCategories = vscode.commands.registerCommand(COMMAND_NAME.insertCategories, async () => {
|
||||
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
|
||||
await vscode.commands.executeCommand('workbench.action.focusSideBar');
|
||||
explorerSidebar.triggerInputFocus(TagType.categories);
|
||||
});
|
||||
|
||||
let createTag = vscode.commands.registerCommand('frontMatter.createTag', () => {
|
||||
let createTag = vscode.commands.registerCommand(COMMAND_NAME.createTag, () => {
|
||||
Settings.create(TaxonomyType.Tag);
|
||||
});
|
||||
|
||||
let createCategory = vscode.commands.registerCommand('frontMatter.createCategory', () => {
|
||||
let createCategory = vscode.commands.registerCommand(COMMAND_NAME.createCategory, () => {
|
||||
Settings.create(TaxonomyType.Category);
|
||||
});
|
||||
|
||||
let exportTaxonomy = vscode.commands.registerCommand('frontMatter.exportTaxonomy', () => {
|
||||
let exportTaxonomy = vscode.commands.registerCommand(COMMAND_NAME.exportTaxonomy, () => {
|
||||
Settings.export();
|
||||
});
|
||||
|
||||
let remap = vscode.commands.registerCommand('frontMatter.remap', () => {
|
||||
let remap = vscode.commands.registerCommand(COMMAND_NAME.remap, () => {
|
||||
Settings.remap();
|
||||
});
|
||||
|
||||
let setDate = vscode.commands.registerCommand('frontMatter.setDate', () => {
|
||||
let setDate = vscode.commands.registerCommand(COMMAND_NAME.setDate, () => {
|
||||
Article.setDate();
|
||||
});
|
||||
|
||||
let setLastModifiedDate = vscode.commands.registerCommand('frontMatter.setLastModifiedDate', () => {
|
||||
let setLastModifiedDate = vscode.commands.registerCommand(COMMAND_NAME.setLastModifiedDate, () => {
|
||||
Article.setLastModifiedDate();
|
||||
});
|
||||
|
||||
let generateSlug = vscode.commands.registerCommand('frontMatter.generateSlug', () => {
|
||||
let generateSlug = vscode.commands.registerCommand(COMMAND_NAME.generateSlug, () => {
|
||||
Article.generateSlug();
|
||||
});
|
||||
|
||||
let createFromTemplate = vscode.commands.registerCommand('frontMatter.createFromTemplate', (folder: vscode.Uri) => {
|
||||
let createFromTemplate = vscode.commands.registerCommand(COMMAND_NAME.createFromTemplate, (folder: vscode.Uri) => {
|
||||
const folderPath = Folders.getFolderPath(folder);
|
||||
if (folderPath) {
|
||||
Template.create(folderPath);
|
||||
}
|
||||
});
|
||||
|
||||
const toggleDraftCommand = 'frontMatter.toggleDraft';
|
||||
const toggleDraftCommand = COMMAND_NAME.toggleDraft;
|
||||
const toggleDraft = vscode.commands.registerCommand(toggleDraftCommand, async () => {
|
||||
await Article.toggleDraft();
|
||||
triggerShowDraftStatus();
|
||||
});
|
||||
|
||||
// Register project folders
|
||||
const registerFolder = vscode.commands.registerCommand(`frontMatter.registerFolder`, Folders.register);
|
||||
const registerFolder = vscode.commands.registerCommand(COMMAND_NAME.registerFolder, Folders.register);
|
||||
|
||||
const unregisterFolder = vscode.commands.registerCommand(`frontMatter.unregisterFolder`, Folders.unregister);
|
||||
const unregisterFolder = vscode.commands.registerCommand(COMMAND_NAME.unregisterFolder, Folders.unregister);
|
||||
|
||||
const createContent = vscode.commands.registerCommand(`frontMatter.createContent`, Folders.create);
|
||||
const createContent = vscode.commands.registerCommand(COMMAND_NAME.createContent, Folders.create);
|
||||
|
||||
Folders.updateVsCodeCtx();
|
||||
|
||||
// Initialize command
|
||||
Template.init();
|
||||
const projectInit = vscode.commands.registerCommand(COMMAND_NAME.init, Project.init);
|
||||
|
||||
// Things to do when configuration changes
|
||||
vscode.workspace.onDidChangeConfiguration(() => {
|
||||
Template.init();
|
||||
Folders.updateVsCodeCtx();
|
||||
});
|
||||
|
||||
// Create the status bar
|
||||
frontMatterStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
|
||||
frontMatterStatusBar.command = toggleDraftCommand;
|
||||
@@ -94,21 +106,24 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
triggerShowDraftStatus();
|
||||
|
||||
// Subscribe all commands
|
||||
subscriptions.push(insertTags);
|
||||
subscriptions.push(explorerView);
|
||||
subscriptions.push(insertCategories);
|
||||
subscriptions.push(createTag);
|
||||
subscriptions.push(createCategory);
|
||||
subscriptions.push(exportTaxonomy);
|
||||
subscriptions.push(remap);
|
||||
subscriptions.push(setDate);
|
||||
subscriptions.push(setLastModifiedDate);
|
||||
subscriptions.push(generateSlug);
|
||||
subscriptions.push(createFromTemplate);
|
||||
subscriptions.push(toggleDraft);
|
||||
subscriptions.push(registerFolder);
|
||||
subscriptions.push(unregisterFolder);
|
||||
subscriptions.push(createContent);
|
||||
subscriptions.push(
|
||||
insertTags,
|
||||
explorerView,
|
||||
insertCategories,
|
||||
createTag,
|
||||
createCategory,
|
||||
exportTaxonomy,
|
||||
remap,
|
||||
setDate,
|
||||
setLastModifiedDate,
|
||||
generateSlug,
|
||||
createFromTemplate,
|
||||
toggleDraft,
|
||||
registerFolder,
|
||||
unregisterFolder,
|
||||
createContent,
|
||||
projectInit
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
@@ -96,4 +96,12 @@ export class ArticleHelper {
|
||||
indent: spaces || 2
|
||||
} as DumpOptions as any));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current file is a markdown file
|
||||
*/
|
||||
public static isMarkdownDile() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
return (editor && editor.document && (editor.document.languageId.toLowerCase() === "markdown" || editor.document.languageId.toLowerCase() === "mdx"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { EXTENSION_NAME } from '../constants';
|
||||
import { Notifications } from './Notifications';
|
||||
|
||||
export class FilesHelper {
|
||||
|
||||
@@ -11,7 +12,7 @@ export class FilesHelper {
|
||||
const markdownFiles = await vscode.workspace.findFiles('**/*.markdown', "**/node_modules/**,**/archetypes/**");
|
||||
const mdxFiles = await vscode.workspace.findFiles('**/*.mdx', "**/node_modules/**,**/archetypes/**");
|
||||
if (!mdFiles && !markdownFiles) {
|
||||
vscode.window.showInformationMessage(`${EXTENSION_NAME}: No MD files found.`);
|
||||
Notifications.info(`No MD files found.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
18
src/helpers/Notifications.ts
Normal file
18
src/helpers/Notifications.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { window } from "vscode";
|
||||
import { EXTENSION_NAME } from "../constants";
|
||||
|
||||
|
||||
export class Notifications {
|
||||
|
||||
public static info(message: string) {
|
||||
window.showInformationMessage(`${EXTENSION_NAME}: ${message}`);
|
||||
}
|
||||
|
||||
public static warning(message: string) {
|
||||
window.showWarningMessage(`${EXTENSION_NAME}: ${message}`);
|
||||
}
|
||||
|
||||
public static error(message: string) {
|
||||
window.showErrorMessage(`${EXTENSION_NAME}: ${message}`);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ export interface PanelSettings {
|
||||
categories: string[];
|
||||
freeform: boolean;
|
||||
scripts: CustomScript[];
|
||||
isInitialized: boolean;
|
||||
contentInfo: FolderInfo[] | null;
|
||||
}
|
||||
|
||||
export interface SEO {
|
||||
@@ -20,6 +22,11 @@ export interface Slug {
|
||||
suffix: number;
|
||||
}
|
||||
|
||||
export interface FolderInfo {
|
||||
title: string;
|
||||
files: number;
|
||||
}
|
||||
|
||||
export interface CustomScript {
|
||||
title: string;
|
||||
script: string;
|
||||
|
||||
@@ -12,5 +12,7 @@ export enum CommandToCode {
|
||||
openSettings = "open-settings",
|
||||
openFile = "open-file",
|
||||
openProject = "open-project",
|
||||
runCustomScript = "custom-script"
|
||||
runCustomScript = "custom-script",
|
||||
initProject = "init-project",
|
||||
createContent = "create-content",
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { CommandToCode } from './CommandToCode';
|
||||
import { Actions } from './components/Actions';
|
||||
import { BaseView } from './components/BaseView';
|
||||
import { Collapsible } from './components/Collapsible';
|
||||
import { BugIcon } from './components/Icons/BugIcon';
|
||||
import { FileIcon } from './components/Icons/FileIcon';
|
||||
@@ -30,9 +31,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
|
||||
if (!metadata || Object.keys(metadata).length === 0) {
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<p>Current view/file is not supported by FrontMatter.</p>
|
||||
</div>
|
||||
<BaseView settings={settings} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
49
src/viewpanel/components/BaseView.tsx
Normal file
49
src/viewpanel/components/BaseView.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { PanelSettings } from '../../models';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { Collapsible } from './Collapsible';
|
||||
|
||||
export interface IBaseViewProps {
|
||||
settings: PanelSettings | undefined;
|
||||
}
|
||||
|
||||
export const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings}: React.PropsWithChildren<IBaseViewProps>) => {
|
||||
|
||||
const initProject = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.initProject);
|
||||
};
|
||||
|
||||
const createContent = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.createContent);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<div className={`ext_actions`}>
|
||||
<Collapsible title="Actions">
|
||||
<div className={`base__actions`}>
|
||||
<button onClick={initProject} disabled={settings?.isInitialized}>Initialize project</button>
|
||||
<button onClick={createContent} disabled={!settings?.isInitialized}>Create new content</button>
|
||||
</div>
|
||||
</Collapsible>
|
||||
|
||||
{
|
||||
settings?.contentInfo && (
|
||||
<Collapsible title="Content information">
|
||||
<div className="base__information">
|
||||
{
|
||||
settings.contentInfo.map(folder => (
|
||||
<div key={folder.title}>
|
||||
{folder.title}: {folder.files} file{folder.files > 1 ? 's' : ''}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,11 +2,9 @@ import * as React from 'react';
|
||||
import { SEO } from '../../models/PanelSettings';
|
||||
import { ArticleDetails } from './ArticleDetails';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import { SeoDetails } from './SeoDetails';
|
||||
import { SeoFieldInfo } from './SeoFieldInfo';
|
||||
import { SeoKeywords } from './SeoKeywords';
|
||||
import { ValidInfo } from './ValidInfo';
|
||||
import { VsTable, VsTableBody, VsTableCell, VsTableHeader, VsTableHeaderCell, VsTableRow } from './VscodeComponents';
|
||||
import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell } from './VscodeComponents';
|
||||
|
||||
export interface ISeoStatusProps {
|
||||
seo: SEO;
|
||||
@@ -17,6 +15,7 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
const { data, seo } = props;
|
||||
const { title } = data;
|
||||
const [ isOpen, setIsOpen ] = React.useState(true);
|
||||
const tableRef = React.useRef<HTMLElement>();
|
||||
|
||||
const { descriptionField } = seo;
|
||||
|
||||
@@ -34,7 +33,7 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
<div className={`seo__status__details`}>
|
||||
<h4>Recommendations</h4>
|
||||
|
||||
<VsTable bordered>
|
||||
<VsTable ref={tableRef} bordered zebra>
|
||||
<VsTableHeader slot="header">
|
||||
<VsTableHeaderCell className={`table__cell`}>Property</VsTableHeaderCell>
|
||||
<VsTableHeaderCell className={`table__cell`}>Length</VsTableHeaderCell>
|
||||
@@ -54,7 +53,7 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
}
|
||||
|
||||
{
|
||||
(seo.content > 0 && data?.articleDetails?.wordCount) && (
|
||||
(seo.content > 0 && data?.articleDetails?.wordCount > 0) && (
|
||||
<SeoFieldInfo title={`Article length`} value={data?.articleDetails?.wordCount} recommendation={`${seo.content} words`} />
|
||||
)
|
||||
}
|
||||
@@ -73,6 +72,21 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
);
|
||||
};
|
||||
|
||||
// Workaround for lit components not updating render
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
let height = 0;
|
||||
|
||||
tableRef.current?.childNodes.forEach((elm: any) => {
|
||||
height += elm.clientHeight;
|
||||
});
|
||||
|
||||
if (height > 0 && tableRef.current) {
|
||||
tableRef.current.style.height = `${height}px`;
|
||||
}
|
||||
}, 10);
|
||||
}, [title, data[descriptionField], data?.articleDetails?.wordCount]);
|
||||
|
||||
return (
|
||||
<Collapsible title="SEO Status" sendUpdate={(value) => setIsOpen(value)}>
|
||||
{ renderContent() }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings';
|
||||
import { Template } from './../commands/Template';
|
||||
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME } from './../constants/settings';
|
||||
import * as os from 'os';
|
||||
import { PanelSettings, CustomScript } from './../models/PanelSettings';
|
||||
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
|
||||
@@ -13,6 +14,9 @@ import { exec } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { Content } from 'mdast';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { COMMAND_NAME } from '../constants/Extension';
|
||||
import { Folders } from '../commands/Folders';
|
||||
|
||||
|
||||
export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
@@ -74,7 +78,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
webviewView.onDidDispose(() => { webviewView.webview.html = ""; }, this),
|
||||
);
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(msg => {
|
||||
webviewView.webview.onDidReceiveMessage(async (msg) => {
|
||||
switch(msg.command) {
|
||||
case CommandToCode.getData:
|
||||
this.getSettings();
|
||||
@@ -135,6 +139,13 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CommandToCode.initProject:
|
||||
await commands.executeCommand(COMMAND_NAME.init);
|
||||
this.getSettings();
|
||||
break;
|
||||
case CommandToCode.createContent:
|
||||
await commands.executeCommand(COMMAND_NAME.createContent);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -161,10 +172,15 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* @param metadata
|
||||
*/
|
||||
public pushMetadata(metadata: any) {
|
||||
const articleDetails = this.getArticleDetails();
|
||||
|
||||
if (articleDetails) {
|
||||
metadata.articleDetails = articleDetails;
|
||||
}
|
||||
|
||||
this.postWebviewMessage({ command: Command.metadata, data: {
|
||||
...metadata,
|
||||
articleDetails: this.getArticleDetails()
|
||||
} });
|
||||
...metadata
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +222,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
|
||||
exec(`${customScript.nodeBin || "node"} ${path.join(wsPath, msg.data.script)} "${wsPath}" "${editor?.document.uri.fsPath}" ${articleData}`, (error, stdout) => {
|
||||
if (error) {
|
||||
window.showErrorMessage(`${msg?.data?.title}: ${error.message}`);
|
||||
Notifications.error(`${msg?.data?.title}: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,7 +240,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
/**
|
||||
* Retrieve the extension settings
|
||||
*/
|
||||
private getSettings() {
|
||||
private async getSettings() {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
|
||||
this.postWebviewMessage({
|
||||
@@ -238,12 +254,15 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
},
|
||||
slug: {
|
||||
prefix: config.get(SETTING_SLUG_PREFIX) || "",
|
||||
suffix: config.get(SETTING_SLUG_SUFFIX) || ""
|
||||
suffix: config.get(SETTING_SLUG_SUFFIX) || "",
|
||||
updateFileName: !!config.get<boolean>(SETTING_SLUG_UPDATE_FILE_NAME),
|
||||
},
|
||||
tags: config.get(SETTING_TAXONOMY_TAGS) || [],
|
||||
categories: config.get(SETTING_TAXONOMY_CATEGORIES) || [],
|
||||
freeform: config.get(SETTING_PANEL_FREEFORM),
|
||||
scripts: config.get(SETTING_CUSTOM_SCRIPTS)
|
||||
scripts: config.get(SETTING_CUSTOM_SCRIPTS),
|
||||
isInitialized: await Template.isInitialized(),
|
||||
contentInfo: await Folders.getInfo() || null
|
||||
} as PanelSettings
|
||||
});
|
||||
}
|
||||
@@ -258,10 +277,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
this.postWebviewMessage({ command: Command.metadata, data: {
|
||||
...article!.data,
|
||||
articleDetails: this.getArticleDetails()
|
||||
}});
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,10 +295,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
if (article && article.data) {
|
||||
article.data[tagType.toLowerCase()] = values || [];
|
||||
ArticleHelper.update(editor, article);
|
||||
this.postWebviewMessage({ command: Command.metadata, data: {
|
||||
...article.data,
|
||||
articleDetails: this.getArticleDetails()
|
||||
}});
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +325,11 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
private getArticleDetails() {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ArticleHelper.isMarkdownDile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
@@ -393,7 +410,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webView.cspSource} 'self' 'unsafe-inline'; script-src 'nonce-${nonce}'; style-src ${webView.cspSource} 'self' 'unsafe-inline'; font-src ${webView.cspSource}">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'; script-src 'nonce-${nonce}'; style-src ${webView.cspSource} 'self' 'unsafe-inline'; font-src ${webView.cspSource}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="${styleResetUri}" rel="stylesheet">
|
||||
<link href="${styleVSCodeUri}" rel="stylesheet">
|
||||
@@ -404,6 +421,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759" alt="Daily usage" />
|
||||
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user