Compare commits

..

27 Commits

Author SHA1 Message Date
Elio Struyf
cc21043053 2.0.1 2021-07-27 15:57:16 +02:00
Elio Struyf
b1816a0567 updated changelog 2021-07-27 15:57:04 +02:00
Elio Struyf
29b170a8bd Updated screenshot 2021-07-27 15:17:57 +02:00
Elio Struyf
3ed144f003 #42 - Table updates 2021-07-27 15:16:10 +02:00
Elio Struyf
613d7f2adb Update changelog 2021-07-27 15:08:07 +02:00
Elio Struyf
82260d7030 #43 - Fix for collapsible sections 2021-07-27 15:06:55 +02:00
Elio Struyf
dbd8b1c0ce Fix for onKeyDown enter 2021-07-24 18:08:09 +02:00
Elio Struyf
d2a4a281a3 2.0.0 2021-07-23 10:26:06 +02:00
Elio Struyf
37021e7a0a Fix 2021-07-23 10:26:03 +02:00
Elio Struyf
02c171d64c Merge branch 'dev' 2021-07-23 10:25:09 +02:00
Elio Struyf
a99f20b9f1 Updated image 2021-07-23 10:21:36 +02:00
Elio Struyf
14d66203d3 Updated dependency for the release 2021-07-23 10:01:25 +02:00
Elio Struyf
6e1b28c59e Removed material UI 2021-07-22 20:13:10 +02:00
Elio Struyf
53a1b19e07 Combine the title, description and artilce length details 2021-07-22 15:58:35 +02:00
Elio Struyf
87e735faa9 Update documentation + images + icons 2021-07-21 19:38:56 +02:00
Elio Struyf
b2f0d51aa2 Added table of contents 2021-07-21 18:16:11 +02:00
Elio Struyf
9a6403a6cd Updated images 2021-07-21 18:07:34 +02:00
Elio Struyf
021b3952ec updated changelog 2021-07-21 16:45:58 +02:00
Elio Struyf
8ddeab7a88 #40 - Implemented keyword checks 2021-07-21 16:45:37 +02:00
Elio Struyf
bea11bf7df Collapsible headers 2021-07-21 14:42:52 +02:00
Elio Struyf
323807c0e1 #41 - Better visualization of the article length 2021-07-21 12:23:46 +02:00
Elio Struyf
d55b122d33 mdx support + styling changes 2021-07-20 18:37:09 +02:00
Elio Struyf
a118b461a7 #41 - word count implementation + extra details 2021-07-20 18:37:01 +02:00
Elio Struyf
c572a821e9 Icon updates 2021-07-20 16:12:29 +02:00
Elio Struyf
a3f18bb143 Merge branch 'master' into dev 2021-07-20 15:18:31 +02:00
Elio Struyf
525a289a2c New panel updates 2021-07-20 15:17:48 +02:00
Elio Struyf
976a473d39 Added a new tag icon instead of the + 2020-12-11 10:10:14 +01:00
48 changed files with 1242 additions and 442 deletions

12
.vscode/launch.json vendored
View File

@@ -18,6 +18,18 @@
],
"preLaunchTask": "npm: build:ext"
},
{
"name": "Attach Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
]
},
{
"name": "Extension Tests",
"type": "extensionHost",

View File

@@ -1,8 +1,23 @@
# Change Log
## [2.0.1] - 2020-07-27
- [#42](https://github.com/estruyf/vscode-front-matter/issues/42): Small enhancement to the table layout
- [#43](https://github.com/estruyf/vscode-front-matter/issues/43): Fix for collapsible sections and taxonomy picker
## [2.0.0] - 2020-07-23
- Redesigned sidebar panel
- Sidebar background styling match the VSCode defined sidebar color
- Added support for `mdx` files
- Added support for `enter` press in the combobox
- [#41](https://github.com/estruyf/vscode-front-matter/issues/41): Word count implementation + extra details
- [#40](https://github.com/estruyf/vscode-front-matter/issues/40): Added checks for the keyword usage in title, description, slug, and content
## [1.18.0] - 2020-07-20
- Updated README
## [1.17.1] - 2020-06-28
- [#34](https://github.com/estruyf/vscode-front-matter/issues/34): Fix that last modification date does not update the publication date

101
README.md
View File

@@ -26,23 +26,72 @@ The extension will automatically verify if your title and description are SEO co
> If you see something missing in your article creation flow, please feel free to reach out.
## FrontMatter Panel (introduced in 1.10.0)
**Version 2**
In version `1.10.0` of this extension, the FrontMatter panel got introduced. This panel allows you to perform most of the extension actions by just a click on the button.
In version v2.0.0 we released the newly redesigned sidebar panel with improved SEO support. This extension makes it the only extension to manage your Markdown pages for your static sites in Visual Studio Code.
![FrontMatter Panel](./assets/frontmatter-panel.png)
<h2 id="table-of-contents">Table of Contents</h2>
<details open="open">
<summary>Table of Contents</summary>
<ol>
<li><a href="#the-panel">The panel</a></li>
<li><a href="#custom-actions">Custom actions</a></li>
<li><a href="#creating-articles-from-templates">Create articles from templates</a></li>
<li><a href="#syntax-highlighting-for-hugo-shortcodes">Syntax highlighting for Hugo Shortcodes</a></li>
<li><a href="#available-commands">Available commands</a></li>
<li><a href="#extension-settings">Extension settings</a></li>
<li><a href="#feedback--issues--ideas">Feedback / issues / ideas</a></li>
</ol>
</details>
## The panel
The Front Matter panel allows you to perform most of the extension actions by just a click on the button and it shows the SEO statuses of your title, description, and more.
Initially, this panel has been created to make it easier to add tags and categories to your articles as the current VSCode multi-select is not optimal to use.
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:
**SEO Status**
<p align="center">
<img src="./assets/v2.0.0/seo.png" alt="SEO article status" style="display: inline-block" />
</p>
**Actions**
<p align="center">
<img src="./assets/v2.0.0/actions.png" alt="Actions" style="display: inline-block" />
</p>
**Metadata: Keywords, Tags, Categories**
<p align="center">
<img src="./assets/v2.0.0/metadata.png" alt="Article metadata" style="display: inline-block" />
</p>
> **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.
**Other actions**
At the bottom of the panel you can find the following actions:
<p align="center">
<img src="./assets/v2.0.0/other-actions.png" alt="Other actions" style="display: inline-block" />
</p>
## Custom actions
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`](#frontmattercustomscripts) setting for your project.
Once a custom action has been configured, it will appear on the Front Matter panel.
![](./assets/custom-actions.png)
<p align="center">
<img src="./assets/v2.0.0/custom-action.png" alt="Custom action" style="display: inline-block" />
</p>
The current workspace-, file-path, and front matter data will be passed as an argument. In your script fetch these arguments as follows:
@@ -62,7 +111,9 @@ if (arguments && arguments.length > 0) {
The output of the script will be passed as a notification, and it allows you to copy the output.
![](./assets/custom-action-notification.png)
<p align="center">
<img src="./assets/custom-action-notification.png" alt="Custom action notification" style="display: inline-block" />
</p>
## Creating articles from templates
@@ -76,7 +127,9 @@ When adding files in the folder, you'll be able to run the `Front Matter: New ar
## Syntax highlighting for Hugo Shortcodes
![Shortcode syntax highlighting](./assets/syntax-highlighting.png)
<p align="center">
<img src="./assets/syntax-highlighting.png" alt="Shortcode syntax highlighting" style="display: inline-block" />
</p>
## Available commands:
@@ -84,11 +137,13 @@ When adding files in the folder, you'll be able to run the `Front Matter: New ar
Creates a new <tag | category> and allows you to include it into your post automatically
![Create tag or category](./assets/create-tag-category.gif)
<p align="center">
<img src="./assets/create-tag-category.gif" alt="Create tag or category" style="display: inline-block" />
</p>
**Front Matter: Insert <tags | categories>**
Inserts a selected <tags | categories> into the front matter of your article/post/... - When using this command, the FrontMatter panel opens and focuses on the specified type.
Inserts a selected <tags | categories> into the front matter of your article/post/... - When using this command, the Front Matter panel opens and focuses on the specified type.
> **Info**: This experience changed in version `1.11.0`.
@@ -119,6 +174,7 @@ Update the `lastmod` (last modified) property of the current article/post/... to
This command generates a clean slug for your article. It removes known stop words, punctuations, and special characters.
Example:
```
title: Just a sample page with a title
slug: sample-page-title
@@ -128,6 +184,13 @@ You can also specify a prefix and suffix, which can be added to the slug if you
> **Info**: At the moment, the extension only supports English stopwords.
### Usage
- Start by opening the command prompt:
- Windows: ⇧+ctrl+P
- Mac: ⇧+⌘+P
- Use one of the commands from above
## Where is the data stored?
The tags and categories are stored in the project VSCode user settings. You can find them back under: `.vscode/settings.json`.
@@ -139,7 +202,7 @@ The tags and categories are stored in the project VSCode user settings. You can
}
```
## Additional extension settings
## Extension settings
The extension has more settings that allow you to configure it to your needs further. Here is a list of settings that you can set:
@@ -161,6 +224,17 @@ Specifies the optimal description length for SEO (set to `-1` to turn it off). D
"frontMatter.taxonomy.seoDescriptionLength": 160
}
```
### `frontMatter.taxonomy.seoContentLength`
Specifies the optimal minimum length for your articles. Between 1,760 words 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off).
```json
{
"frontMatter.taxonomy.seoContentLength": 1760
}
```
### `frontMatter.taxonomy.seoDescriptionLength`
Specifies the name of the SEO description field for your page. Default is `description`.
@@ -237,13 +311,6 @@ Allows you to specify a title and script path (starting relative from the root o
> **Important**: When the command execution would fail when it cannot find the `node` command. You are able to specify your path to the node app. This is for instance required when using `nvm`.
## Usage
- Start by opening the command prompt:
- Windows: ⇧+ctrl+P
- Mac: ⇧+⌘+P
- Use one of the commands from above
## Feedback / issues / ideas
Please submit them via creating an issue in the project repository: [issue list](https://github.com/estruyf/vscode-front-matter/issues).

View File

@@ -21,6 +21,20 @@
}
}
.absolute {
position: absolute !important;
}
.collapsible__body,
.ext_settings {
padding: 1rem 1.25rem;
box-sizing: border-box;
}
#app, .frontmatter {
height: 100%;
}
.spinner,
.spinner:before,
.spinner:after {
@@ -58,6 +72,13 @@
left: 3.5em;
}
.frontmatter {
padding-top: 0;
padding-bottom: var(--input-margin-vertical);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.frontmatter h3 {
margin-bottom: 1rem;
@@ -69,22 +90,34 @@
margin-bottom: .5rem;
}
.seo__status__details {
margin-bottom: 2rem;
.article__tags h3,
.seo__status h3 {
display: flex;
align-items: center;
}
.section {
box-sizing: border-box;
position: relative;
}
.section h3 svg {
margin-right: 0.5rem;
}
.seo__status__details, .seo__status__keywords {
margin-bottom: 1rem;
}
.collapsible__body h4 {
text-align: center;
font-weight: bold;
}
.not-valid {
color: var(--vscode-errorForeground);
}
.article__actions {
margin-bottom: 2rem;
}
.article__action {
margin-bottom: 1rem;
}
.article__tags {
margin-bottom: 1rem;
}
@@ -118,9 +151,15 @@
.article__tags__input button {
position: absolute;
bottom: 1px;
top: 1px;
right: 1px;
width: 30px;
padding-bottom: 2px;
padding-top: 2px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.article__tags ul {
@@ -151,6 +190,12 @@
margin-top: 1rem;
}
.article__tags__items__item {
display: inline-flex;
margin-bottom: .5rem;
margin-right: .5rem;
}
.article__tags__items__item {
display: inline-block;
margin-bottom: .5rem;
@@ -199,18 +244,70 @@
filter: contrast(60%);
}
.ext_link_block {
margin-bottom: .5rem;
text-align: right;
.article__actions > * + * {
--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));
}
.seo__status__details ul > * + *,
.ext_settings > * + * {
--tw-space-y-reverse: 0;
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
}
.ext_link_block {
display: flex;
align-items: center;
}
.ext_link_block svg {
margin-right: .5rem;
}
.ext_link_block button,
.ext_link_block a {
color: var(--vscode-textLink-foreground);
align-items: center;
color: var(--vscode-button-secondaryForeground);
background-color: var(--vscode-button-secondaryBackground);
border: 0px;
border-radius: 0px;
box-sizing: border-box;
cursor: pointer;
display: inline-flex;
font-size: var(--vscode-font-size);
font-weight: var(--vscode-font-weight);
line-height: 26px;
padding: 0px 14px;
user-select: none;
text-decoration: none;
width: auto;
}
.ext_link_block a:hover,
.ext_link_block a:active,
.ext_link_block a:focus,
.ext_link_block a:visited {
color: var(--vscode-textLink-activeForeground);
color: var(--vscode-button-secondaryForeground);
}
.table__cell {
overflow: hidden;
}
.table__title {
text-transform: capitalize;
}
.table__cell__validation {
text-align: center;
}
.table__cell__validation .valid {
color: #46EC86;
}
.table__cell__validation .warning {
color: #E6AF2E;
}

View File

@@ -1,29 +1,26 @@
:root {
--container-paddding: 20px;
--container-padding: 20px;
--input-padding-vertical: 6px;
--input-padding-horizontal: 4px;
--input-margin-vertical: 4px;
--input-margin-horizontal: 0;
}
html, body {
height: 100%;
}
body {
padding: 0 var(--container-paddding);
color: var(--vscode-foreground);
font-size: var(--vscode-font-size);
font-weight: var(--vscode-font-weight);
font-family: var(--vscode-font-family);
background-color: var(--vscode-editor-background);
background-color: var(--vscode-sideBar-background);
}
ol,
ul {
padding-left: var(--container-paddding);
}
body > *,
form > * {
margin-block-start: var(--input-margin-vertical);
margin-block-end: var(--input-margin-vertical);
padding-left: var(--container-padding);
}
*:focus {
@@ -53,6 +50,7 @@ button {
outline-offset: 2px !important;
color: var(--vscode-button-foreground);
background: var(--vscode-button-background);
box-sizing: border-box;
}
button:hover {

BIN
assets/v2.0.0/actions.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
assets/v2.0.0/metadata.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/v2.0.0/seo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

675
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "vscode-front-matter",
"version": "1.18.0",
"version": "2.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -39,11 +39,14 @@
"regenerator-runtime": "^0.13.4"
}
},
"@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
"dev": true
"@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==",
"dev": true,
"requires": {
"lit-element": "^2.5.1"
}
},
"@iarna/toml": {
"version": "2.2.3",
@@ -51,123 +54,18 @@
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==",
"dev": true
},
"@material-ui/core": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.1.tgz",
"integrity": "sha512-aesI8lOaaw0DRIfNG+Anepf61NH5Q+cmkxJOZvI1oHkmD5cKubkZ0C7INqFKjfFpSFlFnqHkTasoM7ogFAvzOg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/styles": "^4.11.1",
"@material-ui/system": "^4.9.14",
"@material-ui/types": "^5.1.0",
"@material-ui/utils": "^4.10.2",
"@types/react-transition-group": "^4.2.0",
"clsx": "^1.0.4",
"hoist-non-react-statics": "^3.3.2",
"popper.js": "1.16.1-lts",
"prop-types": "^15.7.2",
"react-is": "^16.8.0",
"react-transition-group": "^4.4.0"
}
},
"@material-ui/icons": {
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz",
"integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4"
}
},
"@material-ui/lab": {
"version": "4.0.0-alpha.56",
"resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz",
"integrity": "sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.10.2",
"clsx": "^1.0.4",
"prop-types": "^15.7.2",
"react-is": "^16.8.0"
}
},
"@material-ui/styles": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.1.tgz",
"integrity": "sha512-GqzsFsVWT8jXa8OWAd1WD6WIaqtXr2mUPbRZ1EjkiM3Dlta4mCRaToDxkFVv6ZHfXlFjMJzdaIEvCpZOCvZTvg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@emotion/hash": "^0.8.0",
"@material-ui/types": "^5.1.0",
"@material-ui/utils": "^4.9.6",
"clsx": "^1.0.4",
"csstype": "^2.5.2",
"hoist-non-react-statics": "^3.3.2",
"jss": "^10.0.3",
"jss-plugin-camel-case": "^10.0.3",
"jss-plugin-default-unit": "^10.0.3",
"jss-plugin-global": "^10.0.3",
"jss-plugin-nested": "^10.0.3",
"jss-plugin-props-sort": "^10.0.3",
"jss-plugin-rule-value-function": "^10.0.3",
"jss-plugin-vendor-prefixer": "^10.0.3",
"prop-types": "^15.7.2"
},
"dependencies": {
"csstype": {
"version": "2.6.14",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz",
"integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==",
"dev": true
}
}
},
"@material-ui/system": {
"version": "4.9.14",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz",
"integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.9.6",
"csstype": "^2.5.2",
"prop-types": "^15.7.2"
},
"dependencies": {
"csstype": {
"version": "2.6.14",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz",
"integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==",
"dev": true
}
}
},
"@material-ui/types": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
"integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==",
"dev": true
},
"@material-ui/utils": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz",
"integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"prop-types": "^15.7.2",
"react-is": "^16.8.0"
}
},
"@types/anymatch": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
"dev": true
},
"@types/debug": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.6.tgz",
"integrity": "sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA==",
"dev": true
},
"@types/glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
@@ -196,6 +94,15 @@
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/mdast": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz",
"integrity": "sha512-YwR7OK8aPmaBvMMUi+pZXBNoW2unbVbfok4YRqGMJBe1dpDlzpRkJrYEYmvjxgs5JhuQmKfDexrN98u941Zasg==",
"dev": true,
"requires": {
"@types/unist": "*"
}
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -239,15 +146,6 @@
"@types/react": "*"
}
},
"@types/react-transition-group": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
"integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -277,6 +175,12 @@
}
}
},
"@types/unist": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
"dev": true
},
"@types/vscode": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.51.0.tgz",
@@ -324,6 +228,12 @@
}
}
},
"@vscode/codicons": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.20.tgz",
"integrity": "sha512-LlO6K7nzrIWDCZN1Zi6J6ibxrpMibSAct+zNjAwpkNkwup6cJLx5diYvsOJODMPWOuQlBO21qkxtdkSRzW6+Jw==",
"dev": true
},
"@webassemblyjs/ast": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@@ -1004,6 +914,24 @@
}
}
},
"character-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.0.tgz",
"integrity": "sha512-oHqMj3eAuJ77/P5PaIRcqk+C3hdfNwyCD2DAUcD5gyXkegAuF2USC40CEqPscDk4I8FRGMTojGJQkXDsN5QlJA==",
"dev": true
},
"character-entities-legacy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-2.0.0.tgz",
"integrity": "sha512-YwaEtEvWLpFa6Wh3uVLrvirA/ahr9fki/NUd/Bd4OR6EdJ8D22hovYQEOUCBfQfcqnC4IAMGMsHXY1eXgL4ZZA==",
"dev": true
},
"character-reference-invalid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.0.tgz",
"integrity": "sha512-pE3Z15lLRxDzWJy7bBHBopRwfI20sbrMVLQTC7xsPglCHf4Wv1e167OgYAFP78co2XlhojDyAqA+IAJse27//g==",
"dev": true
},
"chokidar": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
@@ -1086,12 +1014,6 @@
}
}
},
"clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
"dev": true
},
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -1286,16 +1208,6 @@
"nth-check": "~1.0.1"
}
},
"css-vendor": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.8.3",
"is-in-browser": "^1.0.2"
}
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
@@ -1320,6 +1232,15 @@
"integrity": "sha512-C14oTzTZy8DH1Eq8N78owrCWvf3+cnJw88BTK/N3DYWVxDJuJzPaNdplzYxDYuuXXGvqBcO4Vy5SOrwAooXSWw==",
"dev": true
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@@ -1426,16 +1347,6 @@
"utila": "~0.4"
}
},
"dom-helpers": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz",
"integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -2279,15 +2190,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"requires": {
"react-is": "^16.7.0"
}
},
"homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -2476,12 +2378,6 @@
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"dev": true
},
"hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==",
"dev": true
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -2510,15 +2406,6 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"indefinite-observable": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz",
"integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==",
"dev": true,
"requires": {
"symbol-observable": "1.2.0"
}
},
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@@ -2579,6 +2466,22 @@
}
}
},
"is-alphabetical": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.0.tgz",
"integrity": "sha512-5OV8Toyq3oh4eq6sbWTYzlGdnMT/DPI5I0zxUBxjiigQsZycpkKF3kskkao3JyYGuYDHvhgJF+DrjMQp9SX86w==",
"dev": true
},
"is-alphanumerical": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.0.tgz",
"integrity": "sha512-t+2GlJ+hO9yagJ+jU3+HSh80VKvz/3cG2cxbGGm4S0hjKuhWQXgPVUVOZz3tqZzMjhmphZ+1TIJTlRZRoe6GCQ==",
"dev": true,
"requires": {
"is-alphabetical": "^2.0.0",
"is-decimal": "^2.0.0"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -2636,6 +2539,12 @@
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-decimal": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.0.tgz",
"integrity": "sha512-QfrfjQV0LjoWQ1K1XSoEZkTAzSa14RKVMa5zg3SdAfzEmQzRM4+tbSFWb78creCeA9rNBzaZal92opi1TwPWZw==",
"dev": true
},
"is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@@ -2682,10 +2591,10 @@
"is-extglob": "^2.1.1"
}
},
"is-in-browser": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=",
"is-hexadecimal": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.0.tgz",
"integrity": "sha512-vGOtYkiaxwIiR0+Ng/zNId+ZZehGfINwTzdrDqc6iubbnQWhnPuYymOzOKUDqa2cSl59yHnEh2h6MvRLQsyNug==",
"dev": true
},
"is-number": {
@@ -2796,99 +2705,27 @@
}
}
},
"jss": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.5.0.tgz",
"integrity": "sha512-B6151NvG+thUg3murLNHRPLxTLwQ13ep4SH5brj4d8qKtogOx/jupnpfkPGSHPqvcwKJaCLctpj2lEk+5yGwMw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"csstype": "^3.0.2",
"indefinite-observable": "^2.0.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-camel-case": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.0.tgz",
"integrity": "sha512-GSjPL0adGAkuoqeYiXTgO7PlIrmjv5v8lA6TTBdfxbNYpxADOdGKJgIEkffhlyuIZHlPuuiFYTwUreLUmSn7rg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.5.0"
}
},
"jss-plugin-default-unit": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.0.tgz",
"integrity": "sha512-rsbTtZGCMrbcb9beiDd+TwL991NGmsAgVYH0hATrYJtue9e+LH/Gn4yFD1ENwE+3JzF3A+rPnM2JuD9L/SIIWw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.5.0"
}
},
"jss-plugin-global": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.0.tgz",
"integrity": "sha512-FZd9+JE/3D7HMefEG54fEC0XiQ9rhGtDHAT/ols24y8sKQ1D5KIw6OyXEmIdKFmACgxZV2ARQ5pAUypxkk2IFQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.5.0"
}
},
"jss-plugin-nested": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.0.tgz",
"integrity": "sha512-ejPlCLNlEGgx8jmMiDk/zarsCZk+DV0YqXfddpgzbO9Toamo0HweCFuwJ3ZO40UFOfqKwfpKMVH/3HUXgxkTMg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.5.0",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-props-sort": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.0.tgz",
"integrity": "sha512-kTLRvrOetFKz5vM88FAhLNeJIxfjhCepnvq65G7xsAQ/Wgy7HwO1BS/2wE5mx8iLaAWC6Rj5h16mhMk9sKdZxg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.5.0"
}
},
"jss-plugin-rule-value-function": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.0.tgz",
"integrity": "sha512-jXINGr8BSsB13JVuK274oEtk0LoooYSJqTBCGeBu2cG/VJ3+4FPs1gwLgsq24xTgKshtZ+WEQMVL34OprLidRA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.5.0",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-vendor-prefixer": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.0.tgz",
"integrity": "sha512-rux3gmfwDdOKCLDx0IQjTwTm03IfBa+Rm/hs747cOw5Q7O3RaTUIMPKjtVfc31Xr/XI9Abz2XEupk1/oMQ7zRA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.8",
"jss": "10.5.0"
}
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
"lit-element": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.5.1.tgz",
"integrity": "sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==",
"dev": true,
"requires": {
"lit-html": "^1.1.1"
}
},
"lit-html": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.4.1.tgz",
"integrity": "sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==",
"dev": true
},
"loader-runner": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
@@ -2985,6 +2822,30 @@
"safe-buffer": "^5.1.2"
}
},
"mdast-util-from-markdown": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.0.0.tgz",
"integrity": "sha512-uj2G60sb7z1PNOeElFwCC9b/Se/lFXuLhVKFOAY2EHz/VvgbupTQRNXPoZl7rGpXYL6BNZgcgaybrlSWbo7n/g==",
"dev": true,
"requires": {
"@types/mdast": "^3.0.0",
"@types/unist": "^2.0.0",
"mdast-util-to-string": "^3.0.0",
"micromark": "^3.0.0",
"micromark-util-decode-numeric-character-reference": "^1.0.0",
"micromark-util-normalize-identifier": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0",
"parse-entities": "^3.0.0",
"unist-util-stringify-position": "^3.0.0"
}
},
"mdast-util-to-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz",
"integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==",
"dev": true
},
"memory-fs": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
@@ -2995,6 +2856,222 @@
"readable-stream": "^2.0.1"
}
},
"micromark": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.1.tgz",
"integrity": "sha512-DqC0I0V6D3wy/XmJTK1Tn+JPlMk7phhtDQ7ZJIZgAzHTyeVXMDdOaHQ2WQ7i5z3Bh4JK66nLBmjtgMrWwe8eMw==",
"dev": true,
"requires": {
"@types/debug": "^4.0.0",
"debug": "^4.0.0",
"micromark-core-commonmark": "^1.0.0",
"micromark-factory-space": "^1.0.0",
"micromark-util-character": "^1.0.0",
"micromark-util-chunked": "^1.0.0",
"micromark-util-combine-extensions": "^1.0.0",
"micromark-util-decode-numeric-character-reference": "^1.0.0",
"micromark-util-encode": "^1.0.0",
"micromark-util-normalize-identifier": "^1.0.0",
"micromark-util-resolve-all": "^1.0.0",
"micromark-util-sanitize-uri": "^1.0.0",
"micromark-util-subtokenize": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0",
"parse-entities": "^3.0.0"
}
},
"micromark-core-commonmark": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.0.tgz",
"integrity": "sha512-y9g7zymcKRBHM/aNBekstvs/Grpf+y4OEBULUTYvGZcusnp+JeOxmilJY4GMpo2/xY7iHQL9fjz5pD9pSAud9A==",
"dev": true,
"requires": {
"micromark-factory-destination": "^1.0.0",
"micromark-factory-label": "^1.0.0",
"micromark-factory-space": "^1.0.0",
"micromark-factory-title": "^1.0.0",
"micromark-factory-whitespace": "^1.0.0",
"micromark-util-character": "^1.0.0",
"micromark-util-chunked": "^1.0.0",
"micromark-util-classify-character": "^1.0.0",
"micromark-util-html-tag-name": "^1.0.0",
"micromark-util-normalize-identifier": "^1.0.0",
"micromark-util-resolve-all": "^1.0.0",
"micromark-util-subtokenize": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0",
"parse-entities": "^3.0.0"
}
},
"micromark-factory-destination": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz",
"integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==",
"dev": true,
"requires": {
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-factory-label": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.0.tgz",
"integrity": "sha512-XWEucVZb+qBCe2jmlOnWr6sWSY6NHx+wtpgYFsm4G+dufOf6tTQRRo0bdO7XSlGPu5fyjpJenth6Ksnc5Mwfww==",
"dev": true,
"requires": {
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-factory-space": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz",
"integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==",
"dev": true,
"requires": {
"micromark-util-character": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-factory-title": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.0.tgz",
"integrity": "sha512-flvC7Gx0dWVWorXuBl09Cr3wB5FTuYec8pMGVySIp2ZlqTcIjN/lFohZcP0EG//krTptm34kozHk7aK/CleCfA==",
"dev": true,
"requires": {
"micromark-factory-space": "^1.0.0",
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-factory-whitespace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz",
"integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==",
"dev": true,
"requires": {
"micromark-factory-space": "^1.0.0",
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-character": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.0.0.tgz",
"integrity": "sha512-VdfDsHtUn/ocN2hGBkMunHHWcaN33llgwU0bmw2LA0tY1JvVkjHGvdiQSIk0pS3XeGCJLT6syS5i8y+1xbwDnQ==",
"dev": true,
"requires": {
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-chunked": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz",
"integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==",
"dev": true,
"requires": {
"micromark-util-symbol": "^1.0.0"
}
},
"micromark-util-classify-character": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz",
"integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==",
"dev": true,
"requires": {
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-combine-extensions": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz",
"integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==",
"dev": true,
"requires": {
"micromark-util-chunked": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-decode-numeric-character-reference": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz",
"integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==",
"dev": true,
"requires": {
"micromark-util-symbol": "^1.0.0"
}
},
"micromark-util-encode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.0.tgz",
"integrity": "sha512-cJpFVM768h6zkd8qJ1LNRrITfY4gwFt+tziPcIf71Ui8yFzY9wG3snZQqiWVq93PG4Sw6YOtcNiKJfVIs9qfGg==",
"dev": true
},
"micromark-util-html-tag-name": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.0.0.tgz",
"integrity": "sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g==",
"dev": true
},
"micromark-util-normalize-identifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz",
"integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==",
"dev": true,
"requires": {
"micromark-util-symbol": "^1.0.0"
}
},
"micromark-util-resolve-all": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz",
"integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==",
"dev": true,
"requires": {
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-sanitize-uri": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz",
"integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==",
"dev": true,
"requires": {
"micromark-util-character": "^1.0.0",
"micromark-util-encode": "^1.0.0",
"micromark-util-symbol": "^1.0.0"
}
},
"micromark-util-subtokenize": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.0.tgz",
"integrity": "sha512-EsnG2qscmcN5XhkqQBZni/4oQbLFjz9yk3ZM/P8a3YUjwV6+6On2wehr1ALx0MxK3+XXXLTzuBKHDFeDFYRdgQ==",
"dev": true,
"requires": {
"micromark-util-chunked": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0"
}
},
"micromark-util-symbol": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz",
"integrity": "sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ==",
"dev": true
},
"micromark-util-types": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.0.tgz",
"integrity": "sha512-psf1WAaP1B77WpW4mBGDkTr+3RsPuDAgsvlP47GJzbH1jmjH8xjOx7Z6kp84L8oqHmy5pYO3Ev46odosZV+3AA==",
"dev": true
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
@@ -3114,6 +3191,12 @@
"run-queue": "^1.0.3"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
@@ -3394,6 +3477,20 @@
"safe-buffer": "^5.1.1"
}
},
"parse-entities": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-3.0.0.tgz",
"integrity": "sha512-AJlcIFDNPEP33KyJLguv0xJc83BNvjxwpuUIcetyXUsLpVXAUCePJ5kIoYtEN2R1ac0cYaRu/vk9dVFkewHQhQ==",
"dev": true,
"requires": {
"character-entities": "^2.0.0",
"character-entities-legacy": "^2.0.0",
"character-reference-invalid": "^2.0.0",
"is-alphanumerical": "^2.0.0",
"is-decimal": "^2.0.0",
"is-hexadecimal": "^2.0.0"
}
},
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
@@ -3487,12 +3584,6 @@
"find-up": "^3.0.0"
}
},
"popper.js": {
"version": "1.16.1-lts",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
"integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==",
"dev": true
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -3663,18 +3754,6 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
"integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -4296,12 +4375,6 @@
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
"dev": true
},
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
"dev": true
},
"tapable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@@ -4371,12 +4444,6 @@
"setimmediate": "^1.0.4"
}
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true
},
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -4570,6 +4637,15 @@
"imurmurhash": "^0.1.4"
}
},
"unist-util-stringify-position": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz",
"integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==",
"dev": true,
"requires": {
"@types/unist": "^2.0.0"
}
},
"unset-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@@ -4979,6 +5055,11 @@
}
}
},
"wc-react": {
"version": "github:estruyf/wc-react#0989e37af55d3ee97392bf2747a818e4873243fa",
"from": "github:estruyf/wc-react",
"dev": true
},
"webpack": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz",

View File

@@ -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": "1.18.0",
"version": "2.0.1",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -142,6 +142,11 @@
"default": 160,
"description": "Specifies the optimal description length for SEO (set to `-1` to turn it off)."
},
"frontMatter.taxonomy.seoContentLengh": {
"type": "number",
"default": 1760,
"description": "Specifies the optimal minimum length for your articles. Between 1,760 words 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off)."
},
"frontMatter.taxonomy.seoDescriptionField": {
"type": "string",
"default": "description",
@@ -238,10 +243,8 @@
"clean": "rm -rf dist"
},
"devDependencies": {
"@bendera/vscode-webview-elements": "0.5.0",
"@iarna/toml": "2.2.3",
"@material-ui/core": "4.11.1",
"@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.56",
"@types/glob": "7.1.3",
"@types/js-yaml": "3.12.1",
"@types/mocha": "^5.2.6",
@@ -249,18 +252,22 @@
"@types/react": "17.0.0",
"@types/react-dom": "17.0.0",
"@types/vscode": "1.51.0",
"@vscode/codicons": "0.0.20",
"date-fns": "2.0.1",
"downshift": "6.0.6",
"glob": "7.1.6",
"gray-matter": "4.0.2",
"html-loader": "1.3.2",
"html-webpack-plugin": "4.5.0",
"mdast-util-from-markdown": "1.0.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"ts-loader": "8.0.3",
"tslint": "6.1.3",
"typescript": "4.0.2",
"wc-react": "github:estruyf/wc-react",
"webpack": "4.44.2",
"webpack-cli": "3.3.12"
}
},
"dependencies": {}
}

View File

@@ -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") {
if (editor && editor.document && (editor.document.languageId.toLowerCase() === "markdown" || editor.document.languageId.toLowerCase() === "mdx")) {
try {
const article = ArticleHelper.getFrontMatter(editor);

View File

@@ -18,6 +18,7 @@ export const SETTING_FRONTMATTER_TYPE = "taxonomy.frontMatterType";
export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength";
export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
export const SETTING_SEO_CONTENT_MIN_LENGTH = "taxonomy.seoContentLengh";
export const SETTING_SEO_DESCRIPTION_FIELD = "taxonomy.seoDescriptionField";
export const SETTING_TEMPLATES_FOLDER = "templates.folder";

View File

@@ -113,6 +113,6 @@ const debounceShowDraftTrigger = () => {
return (fnc: any, time: number) => {
const functionCall = (...args: any[]) => fnc.apply(args);
clearTimeout(timeout);
timeout = setTimeout(functionCall, time);
timeout = setTimeout(functionCall, time) as any;
};
};

View File

@@ -9,12 +9,13 @@ export class FilesHelper {
public static async getMdFiles(): Promise<vscode.Uri[] | null> {
const mdFiles = await vscode.workspace.findFiles('**/*.md', "**/node_modules/**,**/archetypes/**");
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.`);
return null;
}
const allMdFiles = mdFiles.concat(markdownFiles);
const allMdFiles = [...mdFiles, ...markdownFiles, ...mdxFiles];
return allMdFiles;
}
}

View File

@@ -11,6 +11,7 @@ export interface PanelSettings {
export interface SEO {
title: number;
description: number;
content: number;
descriptionField: string;
}

View File

@@ -6,6 +6,7 @@ export enum CommandToCode {
publish = 'publish',
updateTags = "update-tags",
updateCategories = "update-categories",
updateKeywords = "update-keywords",
addTagToSettings = "add-tag",
addCategoryToSettings = "add-category",
openSettings = "open-settings",

View File

@@ -1,4 +1,5 @@
export enum TagType {
tags = "Tags",
categories = "Categories"
categories = "Categories",
keywords = "Keywords"
}

View File

@@ -1,6 +1,14 @@
import * as React from 'react';
import { CommandToCode } from './CommandToCode';
import { Actions } from './components/Actions';
import { Collapsible } from './components/Collapsible';
import { BugIcon } from './components/Icons/BugIcon';
import { FileIcon } from './components/Icons/FileIcon';
import { FolderOpenedIcon } from './components/Icons/FolderOpenedIcon';
import { ListUnorderedIcon } from './components/Icons/ListUnorderedIcon';
import { SettingsIcon } from './components/Icons/SettingsIcon';
import { SymbolKeywordIcon } from './components/Icons/SymbolKeywordIcon';
import { TagIcon } from './components/Icons/TagIcon';
import { SeoStatus } from './components/SeoStatus';
import { Spinner } from './components/Spinner';
import { TagPicker } from './components/TagPicker';
@@ -42,48 +50,66 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
return (
<div className="frontmatter">
{
settings && settings.seo && <SeoStatus seo={settings.seo} data={metadata} />
}
{
settings && metadata && <Actions metadata={metadata} settings={settings} />
}
<div className={`ext_actions`}>
{
settings && settings.seo && <SeoStatus seo={settings.seo} data={metadata} />
}
{
settings && metadata && <Actions metadata={metadata} settings={settings} />
}
{
(settings && settings.tags && settings.tags.length > 0) && (
<TagPicker type={TagType.tags}
crntSelected={metadata.tags || []}
options={settings.tags}
freeform={settings.freeform}
focussed={focusElm === TagType.tags}
unsetFocus={unsetFocus} />
)
}
{
(settings && settings.categories && settings.categories.length > 0) && (
<TagPicker type={TagType.categories}
crntSelected={metadata.categories || []}
options={settings.categories}
freeform={settings.freeform}
focussed={focusElm === TagType.categories}
unsetFocus={unsetFocus} />
)
}
<div className="ext_link_block">
<a href="javascript:;" onClick={openSettings}>Open settings</a>
<Collapsible title="Metadata" className={`absolute`}>
{
<TagPicker type={TagType.keywords}
icon={<SymbolKeywordIcon />}
crntSelected={metadata.keywords || []}
options={[]}
freeform={true}
focussed={focusElm === TagType.keywords}
unsetFocus={unsetFocus}
disableConfigurable />
}
{
(settings && settings.tags && settings.tags.length > 0) && (
<TagPicker type={TagType.tags}
icon={<TagIcon />}
crntSelected={metadata.tags || []}
options={settings.tags}
freeform={settings.freeform}
focussed={focusElm === TagType.tags}
unsetFocus={unsetFocus} />
)
}
{
(settings && settings.categories && settings.categories.length > 0) && (
<TagPicker type={TagType.categories}
icon={<ListUnorderedIcon />}
crntSelected={metadata.categories || []}
options={settings.categories}
freeform={settings.freeform}
focussed={focusElm === TagType.categories}
unsetFocus={unsetFocus} />
)
}
</Collapsible>
</div>
<div className="ext_link_block">
<a href="javascript:;" onClick={openFile}>Reveal file in folder</a>
</div>
<div className={`ext_settings`}>
<div className="ext_link_block">
<button onClick={openSettings}><SettingsIcon /> Open settings</button>
</div>
<div className="ext_link_block">
<a href="javascript:;" onClick={openProject}>Reveal project folder</a>
</div>
<div className="ext_link_block">
<button onClick={openFile}><FileIcon /> Reveal file in folder</button>
</div>
<div className="ext_link_block">
<a href="https://github.com/estruyf/vscode-front-matter/issues" title="Open an issue on GitHub">Report an issue</a>
<div className="ext_link_block">
<button onClick={openProject}><FolderOpenedIcon /> Reveal project folder</button>
</div>
<div className="ext_link_block">
<a href="https://github.com/estruyf/vscode-front-matter/issues" title="Open an issue on GitHub"><BugIcon /> Report an issue</a>
</div>
</div>
</div>
);

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import { PanelSettings } from '../../models/PanelSettings';
import { Collapsible } from './Collapsible';
import { CustomScript } from './CustomScript';
import { DateAction } from './DateAction';
import { PublishAction } from './PublishAction';
@@ -18,22 +19,22 @@ export const Actions: React.FunctionComponent<IActionsProps> = (props: React.Pro
}
return (
<div className={`article__actions`}>
<h3>Actions</h3>
<Collapsible title="Actions">
<div className={`article__actions`}>
{ metadata && metadata.title && <SlugAction value={metadata.title} crntValue={metadata.slug} slugOpts={settings.slug} /> }
{ metadata && metadata.title && <SlugAction value={metadata.title} crntValue={metadata.slug} slugOpts={settings.slug} /> }
<DateAction />
<DateAction />
{ metadata && typeof metadata.draft !== undefined && <PublishAction draft={metadata.draft} />}
{ metadata && typeof metadata.draft !== undefined && <PublishAction draft={metadata.draft} />}
{
(settings && settings.scripts && settings.scripts.length > 0) && (
settings.scripts.map((value) => (
<CustomScript {...value} />
))
)
}
</div>
{
(settings && settings.scripts && settings.scripts.length > 0) && (
settings.scripts.map((value) => (
<CustomScript key={value.title.replace(/ /g, '')} {...value} />
))
)
}
</div>
</Collapsible>
);
};

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents';
export interface IArticleDetailsProps {
details: {
headings: number;
paragraphs: number;
wordCount: number;
}
}
export const ArticleDetails: React.FunctionComponent<IArticleDetailsProps> = ({details}: React.PropsWithChildren<IArticleDetailsProps>) => {
if (!details || (details.headings === undefined && details.paragraphs === undefined)) {
return null;
}
return (
<div className={`seo__status__details valid`}>
<h4>More details</h4>
<VsTable bordered>
<VsTableHeader slot="header">
<VsTableHeaderCell>Type</VsTableHeaderCell>
<VsTableHeaderCell>Total</VsTableHeaderCell>
</VsTableHeader>
<VsTableBody slot="body">
{
details?.headings !== undefined && (
<VsTableRow>
<VsTableCell>Headings</VsTableCell>
<VsTableCell>{details.headings}</VsTableCell>
</VsTableRow>
)
}
{
details?.paragraphs !== undefined && (
<VsTableRow>
<VsTableCell>Paragraphs</VsTableCell>
<VsTableCell>{details.paragraphs}</VsTableCell>
</VsTableRow>
)
}
</VsTableBody>
</VsTable>
</div>
);
};

View File

@@ -0,0 +1,32 @@
import * as React from 'react';
import { VsCollapsible } from './VscodeComponents';
export interface ICollapsibleProps {
title: string;
className?: string;
sendUpdate?: (open: boolean) => void;
}
export const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({children, title, sendUpdate, className}: React.PropsWithChildren<ICollapsibleProps>) => {
const [ isOpen, setIsOpen ] = React.useState(true);
// This is a work around for a lit-element issue of duplicate slot names
const triggerClick = (e: React.MouseEvent<HTMLElement>) => {
if ((e.target as any).tagName.toUpperCase() === 'VSCODE-COLLAPSIBLE') {
setIsOpen(prev => {
if (sendUpdate) {
sendUpdate(!prev);
}
return !prev;
});
}
}
return (
<VsCollapsible title={title} onClick={triggerClick} open={isOpen}>
<div className={`section collapsible__body ${className || ""}`} slot="body">
{children}
</div>
</VsCollapsible>
);
};

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
export interface IIconProps {
name: string;
}
export const Icon: React.FunctionComponent<IIconProps> = ({ name }: React.PropsWithChildren<IIconProps>) => {
return (<i className={`codicon codicon-${name}`}></i>);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IAddIconProps {}
export const AddIcon: React.FunctionComponent<IAddIconProps> = (props: React.PropsWithChildren<IAddIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M14 7v1H8v6H7V8H1V7h6V1h1v6h6z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IArchiveIconProps {}
export const ArchiveIcon: React.FunctionComponent<IArchiveIconProps> = (props: React.PropsWithChildren<IArchiveIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M14.5 1h-13l-.5.5v3l.5.5H2v8.5l.5.5h11l.5-.5V5h.5l.5-.5v-3l-.5-.5zm-1 3H2V2h12v2h-.5zM3 13V5h10v8H3zm8-6H5v1h6V7z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IBugIconProps {}
export const BugIcon: React.FunctionComponent<IBugIconProps> = (props: React.PropsWithChildren<IBugIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M10.877 4.5v-.582a2.918 2.918 0 1 0-5.836 0V4.5h-.833L2.545 2.829l-.593.59 1.611 1.619-.019.049a8.03 8.03 0 0 0-.503 2.831c0 .196.007.39.02.58l.003.045H1v.836h2.169l.006.034c.172.941.504 1.802.954 2.531l.034.055L2.2 13.962l.592.592 1.871-1.872.058.066c.868.992 2.002 1.589 3.238 1.589 1.218 0 2.336-.579 3.199-1.544l.057-.064 1.91 1.92.593-.591-1.996-2.006.035-.056c.467-.74.81-1.619.986-2.583l.006-.034h2.171v-.836h-2.065l.003-.044a8.43 8.43 0 0 0 .02-.58 8.02 8.02 0 0 0-.517-2.866l-.019-.05 1.57-1.57-.592-.59L11.662 4.5h-.785zm-5 0v-.582a2.082 2.082 0 1 1 4.164 0V4.5H5.878zm5.697.837l.02.053c.283.753.447 1.61.447 2.528 0 1.61-.503 3.034-1.274 4.037-.77 1.001-1.771 1.545-2.808 1.545-1.036 0-2.037-.544-2.807-1.545-.772-1.003-1.275-2.427-1.275-4.037 0-.918.164-1.775.448-2.528l.02-.053h7.229z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface ICheckIconProps {}
export const CheckIcon: React.FunctionComponent<ICheckIconProps> = (props: React.PropsWithChildren<ICheckIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IFileIconProps {}
export const FileIcon: React.FunctionComponent<IFileIconProps> = (props: React.PropsWithChildren<IFileIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M13.71 4.29l-3-3L10 1H4L3 2v12l1 1h9l1-1V5l-.29-.71zM13 14H4V2h5v4h4v8zm-3-9V2l3 3h-3z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IFolderOpenedIconProps {}
export const FolderOpenedIcon: React.FunctionComponent<IFolderOpenedIconProps> = (props: React.PropsWithChildren<IFolderOpenedIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M1.5 14h11l.48-.37 2.63-7-.48-.63H14V3.5l-.5-.5H7.71l-.86-.85L6.5 2h-5l-.5.5v11l.5.5zM2 3h4.29l.86.85.35.15H13v2H8.5l-.35.15-.86.85H3.5l-.47.34-1 3.08L2 3zm10.13 10H2.19l1.67-5H7.5l.35-.15.86-.85h5.79l-2.37 6z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IListUnorderedIconProps {}
export const ListUnorderedIcon: React.FunctionComponent<IListUnorderedIconProps> = (props: React.PropsWithChildren<IListUnorderedIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M2 3H1v1h1V3zm0 3H1v1h1V6zM1 9h1v1H1V9zm1 3H1v1h1v-1zm2-9h11v1H4V3zm11 3H4v1h11V6zM4 9h11v1H4V9zm11 3H4v1h11v-1z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface ISettingsIconProps {}
export const SettingsIcon: React.FunctionComponent<ISettingsIconProps> = (props: React.PropsWithChildren<ISettingsIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M3.5 2h-1v5h1V2zm6.1 5H6.4L6 6.45v-1L6.4 5h3.2l.4.5v1l-.4.5zm-5 3H1.4L1 9.5v-1l.4-.5h3.2l.4.5v1l-.4.5zm3.9-8h-1v2h1V2zm-1 6h1v6h-1V8zm-4 3h-1v3h1v-3zm7.9 0h3.19l.4-.5v-.95l-.4-.5H11.4l-.4.5v.95l.4.5zm2.1-9h-1v6h1V2zm-1 10h1v2h-1v-2z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface ISymbolKeywordIconProps {}
export const SymbolKeywordIcon: React.FunctionComponent<ISymbolKeywordIconProps> = (props: React.PropsWithChildren<ISymbolKeywordIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M15 4h-5V3h5v1zm-1 3h-2v1h2V7zm-4 0H1v1h9V7zm2 6H1v1h11v-1zm-5-3H1v1h6v-1zm8 0h-5v1h5v-1zM8 2v3H1V2h7zM7 3H2v1h5V3z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface ITagIconProps {}
export const TagIcon: React.FunctionComponent<ITagIconProps> = (props: React.PropsWithChildren<ITagIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M13.2 2H8.017l-.353.146L1 8.81v.707L6.183 14.7h.707l2.215-2.215A4.48 4.48 0 0 0 15.65 9c.027-.166.044-.332.051-.5a4.505 4.505 0 0 0-2-3.74V2.5l-.5-.5zm-.5 2.259A4.504 4.504 0 0 0 11.2 4a.5.5 0 1 0 0 1 3.5 3.5 0 0 1 1.5.338v2.138L8.775 11.4a.506.506 0 0 0-.217.217l-2.022 2.022-4.475-4.476L8.224 3H12.7v1.259zm1 1.792a3.5 3.5 0 0 1 1 2.449 3.438 3.438 0 0 1-.051.5 3.487 3.487 0 0 1-4.793 2.735l3.698-3.698.146-.354V6.051z"/></svg>
);
};

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
export interface IWarningIconProps {}
export const WarningIcon: React.FunctionComponent<IWarningIconProps> = (props: React.PropsWithChildren<IWarningIconProps>) => {
return (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M7.56 1h.88l6.54 12.26-.44.74H1.44L1 13.26 7.56 1zM8 2.28L2.28 13H13.7L8 2.28zM8.625 12v-1h-1.25v1h1.25zm-1.25-2V6h1.25v4h-1.25z"/></svg>
);
};

View File

@@ -1,21 +1,41 @@
import * as React from 'react';
import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents';
export interface ISeoDetailsProps {
allowedLength: number;
title: string;
value: string;
value: number;
valueTitle: string;
noValidation?: boolean;
}
export const SeoDetails: React.FunctionComponent<ISeoDetailsProps> = (props: React.PropsWithChildren<ISeoDetailsProps>) => {
const { allowedLength, title, value } = props;
const { allowedLength, title, value, valueTitle, noValidation } = props;
const validate = () => {
if (noValidation) {
return "";
}
return value <= allowedLength ? "valid" : "not-valid"
};
return (
<div className={`seo__status__details ${value.length <= allowedLength ? "valid" : "not-valid"}`}>
<h4><strong>{title}</strong></h4>
<ul>
<li><b>Length</b>: {value.length}</li>
<li><b>Recommended length</b>: {allowedLength}</li>
</ul>
<div className={`seo__status__details ${validate()}`}>
<h4>{title}</h4>
<VsTable bordered>
<VsTableHeader slot="header">
<VsTableHeaderCell className={validate()}>{valueTitle}</VsTableHeaderCell>
<VsTableHeaderCell>Recommended</VsTableHeaderCell>
</VsTableHeader>
<VsTableBody slot="body">
<VsTableRow>
<VsTableCell className={validate()}>{value}</VsTableCell>
<VsTableCell>{allowedLength}</VsTableCell>
</VsTableRow>
</VsTableBody>
</VsTable>
</div>
);
};

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
import { ValidInfo } from './ValidInfo';
import { VsTableCell, VsTableRow } from './VscodeComponents';
export interface ISeoFieldInfoProps {
title: string;
value: any;
recommendation: any;
isValid?: boolean;
}
export const SeoFieldInfo: React.FunctionComponent<ISeoFieldInfoProps> = ({ title, value, recommendation, isValid }: React.PropsWithChildren<ISeoFieldInfoProps>) => {
return (
<VsTableRow>
<VsTableCell className={`table__cell table__title`}>{title}</VsTableCell>
<VsTableCell className={`table__cell`}>{value}/{recommendation}</VsTableCell>
<VsTableCell className={`table__cell table__cell__validation`}>
{ isValid !== undefined ? <ValidInfo isValid={isValid} /> : <span>-</span> }
</VsTableCell>
</VsTableRow>
);
};

View File

@@ -0,0 +1,32 @@
import * as React from 'react';
import { ValidInfo } from './ValidInfo';
import { VsTableCell, VsTableRow } from './VscodeComponents';
export interface ISeoKeywordInfoProps {
keyword: string;
title: string;
description: string;
slug: string;
content: string;
}
export const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({keyword, title, description, slug, content}: React.PropsWithChildren<ISeoKeywordInfoProps>) => {
return (
<VsTableRow>
<VsTableCell className={`table__cell`}>{keyword}</VsTableCell>
<VsTableCell className={`table__cell table__cell__validation`}>
<ValidInfo isValid={!!title && title.toLowerCase().includes(keyword.toLowerCase())} />
</VsTableCell>
<VsTableCell className={`table__cell table__cell__validation`}>
<ValidInfo isValid={!!description && description.toLowerCase().includes(keyword.toLowerCase())} />
</VsTableCell>
<VsTableCell className={`table__cell table__cell__validation`}>
<ValidInfo isValid={!!slug && (slug.toLowerCase().includes(keyword.toLowerCase()) || slug.toLowerCase().includes(keyword.replace(/ /g, '-').toLowerCase()))} />
</VsTableCell>
<VsTableCell className={`table__cell table__cell__validation`}>
<ValidInfo isValid={!!content && content.toLowerCase().includes(keyword.toLowerCase())} />
</VsTableCell>
</VsTableRow>
);
};

View File

@@ -0,0 +1,46 @@
import * as React from 'react';
import { SeoKeywordInfo } from './SeoKeywordInfo';
import { VsTable, VsTableBody, VsTableCell, VsTableHeader, VsTableHeaderCell, VsTableRow } from './VscodeComponents';
export interface ISeoKeywordsProps {
keywords: string[] | null;
title: string;
description: string;
slug: string;
content: string;
}
export const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({keywords, ...data}: React.PropsWithChildren<ISeoKeywordsProps>) => {
if (!keywords || keywords.length === 0) {
return null;
}
return (
<div className={`seo__status__keywords`}>
<h4>Keywords</h4>
<VsTable bordered>
<VsTableHeader slot="header">
<VsTableHeaderCell className={`table__cell`}>Keyword</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Title</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Description</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Slug</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Content</VsTableHeaderCell>
</VsTableHeader>
<VsTableBody slot="body">
{
keywords.map((keyword, index) => {
return (
<SeoKeywordInfo key={index} keyword={keyword} {...data} />
);
})
}
</VsTableBody>
</VsTable>
</div>
);
};

View File

@@ -1,6 +1,12 @@
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';
export interface ISeoStatusProps {
seo: SEO;
@@ -9,7 +15,8 @@ export interface ISeoStatusProps {
export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React.PropsWithChildren<ISeoStatusProps>) => {
const { data, seo } = props;
const { title, description } = data;
const { title } = data;
const [ isOpen, setIsOpen ] = React.useState(true);
const { descriptionField } = seo;
@@ -17,11 +24,58 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
return null;
}
const renderContent = () => {
if (!isOpen) {
return null;
}
return (
<div>
<div className={`seo__status__details`}>
<h4>Recommendations</h4>
<VsTable bordered>
<VsTableHeader slot="header">
<VsTableHeaderCell className={`table__cell`}>Property</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Length</VsTableHeaderCell>
<VsTableHeaderCell className={`table__cell`}>Valid</VsTableHeaderCell>
</VsTableHeader>
<VsTableBody slot="body">
{
(title && seo.title > 0) && (
<SeoFieldInfo title={`title`} value={title.length} recommendation={`${seo.title} chars`} isValid={title.length <= seo.title} />
)
}
{
(data[descriptionField] && seo.description > 0) && (
<SeoFieldInfo title={descriptionField} value={data[descriptionField].length} recommendation={`${seo.description} chars`} isValid={data[descriptionField].length <= seo.description} />
)
}
{
(seo.content > 0 && data?.articleDetails?.wordCount) && (
<SeoFieldInfo title={`Article length`} value={data?.articleDetails?.wordCount} recommendation={`${seo.content} words`} />
)
}
</VsTableBody>
</VsTable>
</div>
<SeoKeywords keywords={data?.keywords}
title={title}
description={data[descriptionField]}
slug={data.slug}
content={data?.articleDetails?.content} />
<ArticleDetails details={data.articleDetails} />
</div>
);
};
return (
<div className="seo__status">
<h3>SEO Status</h3>
{ (title && seo.title > 0) && <SeoDetails title="Title" allowedLength={seo.title} value={title} /> }
{ (data[descriptionField] && seo.description > 0) && <SeoDetails title="Description" allowedLength={seo.description} value={data[descriptionField]} /> }
</div>
<Collapsible title="SEO Status" sendUpdate={(value) => setIsOpen(value)}>
{ renderContent() }
</Collapsible>
);
};

View File

@@ -1,26 +1,28 @@
import * as React from 'react';
import AddIcon from '@material-ui/icons/Add';
import DeleteIcon from '@material-ui/icons/Delete';
import { AddIcon } from './Icons/AddIcon';
import { ArchiveIcon } from './Icons/ArchiveIcon';
export interface ITagProps {
className: string;
value: string;
title: string;
disableConfigurable?: boolean;
onCreate?: (tags: string) => void;
onRemove: (tags: string) => void;
}
export const Tag: React.FunctionComponent<ITagProps> = (props: React.PropsWithChildren<ITagProps>) => {
const { value, className, title, onRemove, onCreate } = props;
const { value, className, title, onRemove, onCreate, disableConfigurable } = props;
return (
<div className={`article__tags__items__item`}>
{
onCreate &&
!disableConfigurable && onCreate &&
<button title={`Add ${value} to your settings`} className={`article__tags__items__item_add`} onClick={() => onCreate(value)}><AddIcon /></button>
}
<button title={title} className={`article__tags__items__item_delete ${className}`} onClick={() => onRemove(value)}>{value} <span><DeleteIcon /></span></button>
<button title={title} className={`article__tags__items__item_delete ${className}`} onClick={() => onRemove(value)}>{value} <span><ArchiveIcon /></span></button>
</div>
);
};

View File

@@ -5,18 +5,21 @@ import { CommandToCode } from '../CommandToCode';
import { TagType } from '../TagType';
import { MessageHelper } from '../helper/MessageHelper';
import Downshift from 'downshift';
import { AddIcon } from './Icons/AddIcon';
export interface ITagPickerProps {
type: string;
icon: JSX.Element;
crntSelected: string[];
options: string[];
freeform: boolean;
focussed: boolean;
unsetFocus: () => void;
disableConfigurable?: boolean;
}
export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React.PropsWithChildren<ITagPickerProps>) => {
const { type, crntSelected, options, freeform, focussed, unsetFocus } = props;
const { icon, type, crntSelected, options, freeform, focussed, unsetFocus, disableConfigurable } = props;
const [ selected, setSelected ] = React.useState<string[]>([]);
const [ inputValue, setInputValue ] = React.useState<string>("");
const prevSelected = usePrevious(crntSelected);
@@ -47,7 +50,16 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
* @param values
*/
const sendUpdate = (values: string[]) => {
const cmdType = type === TagType.tags ? CommandToCode.updateTags : CommandToCode.updateCategories;
let cmdType = CommandToCode.updateCategories;
if (type === TagType.tags) {
cmdType = CommandToCode.updateTags;
} else if (type === TagType.categories) {
cmdType = CommandToCode.updateCategories;
} else if (type === TagType.keywords) {
cmdType = CommandToCode.updateKeywords;
}
MessageHelper.sendMessage(cmdType, values);
};
@@ -114,8 +126,8 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
}, [crntSelected]);
return (
<div className={`article__tags`}>
<h3>{type}</h3>
<div className={`section article__tags`}>
<h3>{icon} {type}</h3>
<Downshift ref={dsRef}
onChange={(selected) => onSelect(selected || "")}
@@ -144,9 +156,12 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
{
freeform && (
<button title={`Add the unknown tag`}
<button className={`article__tags__input__button`}
title={`Add the unknown tag`}
disabled={!inputValue}
onClick={() => insertUnkownTag(closeMenu)}>+</button>
onClick={() => insertUnkownTag(closeMenu)}>
<AddIcon />
</button>
)
}
</div>
@@ -154,7 +169,7 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
<ul className={`article__tags__dropbox ${isOpen ? "open" : "closed" }`} {...getMenuProps()}>
{
isOpen ? options.filter((option) => filterList(option, inputValue)).map((item, index) => (
<li {...getItemProps({ key: item, index, item })} >
<li {...getItemProps({ key: item, index, item })}>
{ item }
</li>
)) : null
@@ -165,7 +180,7 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
}
</Downshift>
<Tags values={selected.sort((a: string, b: string) => a.toLowerCase() < b.toLowerCase() ? -1 : 1 )} onRemove={onRemove} onCreate={onCreate} options={options} />
<Tags values={selected.sort((a: string, b: string) => a.toLowerCase() < b.toLowerCase() ? -1 : 1 )} onRemove={onRemove} onCreate={onCreate} options={options} disableConfigurable={!!disableConfigurable} />
</div>
);
};

View File

@@ -5,12 +5,14 @@ export interface ITagsProps {
values: string[];
options: string[];
disableConfigurable?: boolean;
onCreate: (tags: string) => void;
onRemove: (tags: string) => void;
}
export const Tags: React.FunctionComponent<ITagsProps> = (props: React.PropsWithChildren<ITagsProps>) => {
const { values, options, onCreate, onRemove } = props;
const { values, options, onCreate, onRemove, disableConfigurable } = props;
const knownTags = values.filter(v => options.includes(v));
const unknownTags = values.filter(v => !options.includes(v));
@@ -24,7 +26,7 @@ export const Tags: React.FunctionComponent<ITagsProps> = (props: React.PropsWith
}
{
unknownTags.map(t => (
<Tag key={t.replace(/ /g, "_")} value={t} className={`article__tags__items__pill_notexists`} onRemove={onRemove} onCreate={onCreate} title={`Be aware, this tag "${t}" is not saved in your settings. Once removed, it will be gone forever.`} />
<Tag key={t.replace(/ /g, "_")} value={t} className={`article__tags__items__pill_notexists`} onRemove={onRemove} onCreate={onCreate} title={`Be aware, this tag "${t}" is not saved in your settings. Once removed, it will be gone forever.`} disableConfigurable={disableConfigurable} />
))
}
</div>

View File

@@ -0,0 +1,21 @@
import * as React from 'react';
import { CheckIcon } from './Icons/CheckIcon';
import { WarningIcon } from './Icons/WarningIcon';
export interface IValidInfoProps {
isValid: boolean;
}
export const ValidInfo: React.FunctionComponent<IValidInfoProps> = ({isValid}: React.PropsWithChildren<IValidInfoProps>) => {
return (
<>
{
isValid ? (
<span className="valid"><CheckIcon /></span>
) : (
<span className="warning"><WarningIcon /></span>
)
}
</>
);
};

View File

@@ -0,0 +1,9 @@
import {wrapWc} from 'wc-react';
export const VsTable = wrapWc(`vscode-table`);
export const VsTableHeader = wrapWc(`vscode-table-header`);
export const VsTableHeaderCell = wrapWc(`vscode-table-header-cell`);
export const VsTableBody = wrapWc(`vscode-table-body`);
export const VsTableRow = wrapWc(`vscode-table-row`);
export const VsTableCell = wrapWc(`vscode-table-cell`);
export const VsCollapsible = wrapWc(`vscode-collapsible`);

View File

@@ -2,6 +2,15 @@ import * as React from "react";
import { render } from "react-dom";
import { ViewPanel } from "./ViewPanel";
// require('@vscode/codicons/dist/codicon.css');
import '@bendera/vscode-webview-elements/dist/vscode-table';
import '@bendera/vscode-webview-elements/dist/vscode-table-header';
import '@bendera/vscode-webview-elements/dist/vscode-table-header-cell';
import '@bendera/vscode-webview-elements/dist/vscode-table-body';
import '@bendera/vscode-webview-elements/dist/vscode-table-row';
import '@bendera/vscode-webview-elements/dist/vscode-table-cell';
import '@bendera/vscode-webview-elements/dist/vscode-collapsible';
declare const acquireVsCodeApi: <T = unknown>() => {
getState: () => T;
setState: (data: T) => void;

View File

@@ -1,4 +1,4 @@
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings';
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD } 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";
@@ -11,6 +11,8 @@ import { TagType } from '../viewpanel/TagType';
import { TaxonomyType } from '../models';
import { exec } from 'child_process';
import * as path from 'path';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { Content } from 'mdast';
export class ExplorerView implements WebviewViewProvider, Disposable {
@@ -58,8 +60,6 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
*/
public async resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Promise<void> {
console.log(context);
this.panel = webviewView;
webviewView.webview.options = {
@@ -98,6 +98,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
case CommandToCode.updateCategories:
this.updateTags(TagType.categories, msg.data || []);
break;
case CommandToCode.updateKeywords:
this.updateTags(TagType.keywords, msg.data || []);
break;
case CommandToCode.addTagToSettings:
this.addTags(TagType.tags, msg.data);
break;
@@ -152,7 +155,10 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
* @param metadata
*/
public pushMetadata(metadata: any) {
this.postWebviewMessage({ command: Command.metadata, data: metadata });
this.postWebviewMessage({ command: Command.metadata, data: {
...metadata,
articleDetails: this.getArticleDetails()
} });
}
/**
@@ -221,6 +227,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
seo: {
title: config.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
description: config.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1,
content: config.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number || -1,
descriptionField: config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description"
},
slug: {
@@ -245,7 +252,10 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
}
const article = ArticleHelper.getFrontMatter(editor);
this.postWebviewMessage({ command: Command.metadata, data: article!.data });
this.postWebviewMessage({ command: Command.metadata, data: {
...article!.data,
articleDetails: this.getArticleDetails()
}});
}
/**
@@ -263,7 +273,10 @@ 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 });
this.postWebviewMessage({ command: Command.metadata, data: {
...article.data,
articleDetails: this.getArticleDetails()
}});
}
}
@@ -287,6 +300,59 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
}
}
/**
* Get article details
*/
private getArticleDetails() {
const editor = window.activeTextEditor;
if (!editor) {
return "";
}
const article = ArticleHelper.getFrontMatter(editor);
if (article && article.content) {
let content = article.content;
content = content.replace(/({{(.*?)}})/g, ''); // remove hugo shortcodes
const mdTree = fromMarkdown(content);
const headings = mdTree.children.filter(node => node.type === 'heading').length;
const paragraphs = mdTree.children.filter(node => node.type === 'paragraph').length;
const wordCount = this.wordCount(0, mdTree);
return {
headings,
paragraphs,
wordCount,
content: article.content
};
}
return null;
}
private counts(acc: any, node: any) {
// add 1 to an initial or existing value
acc[node.type] = (acc[node.type] || 0) + 1;
// find and add up the counts from all of this node's children
return (node.children || []).reduce(
(childAcc: any, childNode: any) => this.counts(childAcc, childNode),
acc
);
}
/**
* Get the word count for the current document
*/
private wordCount(count: number, node: Content | any) {
if (node.type === "text") {
return count + node.value.split(" ").length;
} else {
return (node.children || []).reduce((childCount: number, childNode: any) => this.wordCount(childCount, childNode), count);
}
}
/**
* Post data to the panel
* @param msg
@@ -314,6 +380,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
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 scriptUri = webView.asWebviewUri(Uri.joinPath(this.extPath, 'dist', 'viewpanel.js'));
const nonce = this.getNonce();
return `
@@ -326,7 +393,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
<link href="${styleVSCodeUri}" rel="stylesheet">
<link href="${stylesUri}" rel="stylesheet">
<title>FrontMatter</title>
<title>Front Matter</title>
</head>
<body>
<div id="app"></div>

View File

@@ -49,13 +49,15 @@ module.exports = [
extensions: ['.ts', '.js', '.tsx', '.jsx']
},
module: {
rules: [{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [{
loader: 'ts-loader'
}]
}]
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [{
loader: 'ts-loader'
}]
}
]
},
performance: {
maxEntrypointSize: 400000,