Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6395e471f0 | ||
|
|
59f0351b79 | ||
|
|
1b16fa69d0 | ||
|
|
e8f67590bc | ||
|
|
b34a8feb63 | ||
|
|
fcbca1e48a | ||
|
|
4585790b45 | ||
|
|
445b64a2ec | ||
|
|
5d18d09b72 |
@@ -1,5 +1,14 @@
|
||||
# Change Log
|
||||
|
||||
## [2.5.1] - 2020-08-23
|
||||
|
||||
- Fix typo in the `package.json` file for the preview command
|
||||
|
||||
## [2.5.0] - 2020-08-19
|
||||
|
||||
- Moved the center layout button to the other actions section
|
||||
- [#60](https://github.com/estruyf/vscode-front-matter/issues/60): Added the ability to open a site preview in VS Code
|
||||
|
||||
## [2.4.1] - 2020-08-16
|
||||
|
||||
- Better editor highlighting functionality
|
||||
|
||||
67
README.md
@@ -1,6 +1,6 @@
|
||||
<h1 align="center">
|
||||
<a href="https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter">
|
||||
<img alt="Front Matter" src="./assets/front-matter.png">
|
||||
<img alt="Front Matter" src="./assets/frontmatter-128x128.png">
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
@@ -24,12 +24,22 @@ This VSCode extension simplifies working with your markdown articles' front matt
|
||||
|
||||
The extension will automatically verify if your title and description are SEO compliant. If this would not be the case, it will give you a warning.
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.5.0/site-preview.png" alt="Site preview" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> If you see something missing in your article creation flow, please feel free to reach out.
|
||||
|
||||
**Version 2**
|
||||
|
||||
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.
|
||||
|
||||
<p align="center" style="margin-top: 2rem;">
|
||||
<a href="https://www.producthunt.com/posts/front-matter?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-front-matter" target="_blank">
|
||||
<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.png?post_id=309033&theme=dark" alt="Front Matter - Managing your static sites straight from within VS Code | Product Hunt" style="width: 250px; height: 40px;" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 id="table-of-contents">Table of Contents</h2>
|
||||
|
||||
<details open="open">
|
||||
@@ -37,7 +47,8 @@ In version v2.0.0 we released the newly redesigned sidebar panel with improved S
|
||||
<ol>
|
||||
<li><a href="#markdown-features">Markdown features</a></li>
|
||||
<li><a href="#the-panel">The panel</a></li>
|
||||
<li><a href="#custom-actions">Custom actions</a></li>
|
||||
<li><a href="#site-preview">Site preview</a></li>
|
||||
<li><a href="#custom-actions">Custom actions/scripts</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>
|
||||
@@ -75,7 +86,7 @@ To leverage most of the capabilities of the extension. SEO information and every
|
||||
When you open the panel and the current file is not a Markdown file, it will contain the following sections:
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/baseview.png" alt="Base view" style="display: inline-block" />
|
||||
<img src="./assets/v2.5.0/baseview.png" alt="Base view" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> **Info**: both **Global Settings** and **Other Actions** sections are shown for the base view as when a Markdown file is openend.
|
||||
@@ -85,7 +96,7 @@ When you open the Front Matter panel on a Markdown file, you get to see the foll
|
||||
**Global Settings**
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/global-settings.png" alt="Global settings" style="display: inline-block" />
|
||||
<img src="./assets/v2.5.0/global-settings.png" alt="Global settings" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
**SEO Status**
|
||||
@@ -97,9 +108,11 @@ When you open the Front Matter panel on a Markdown file, you get to see the foll
|
||||
**Actions**
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/actions.png" alt="Actions" style="display: inline-block" />
|
||||
<img src="./assets/v2.5.0/actions.png" alt="Actions" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> **Info**: To gain the `open preview` button to show up, you will need to first set the `local preview URL`. You can do this within the `Global Settings` section or by updating the `frontMatter.preview.host` setting.
|
||||
|
||||
**Metadata: Keywords, Tags, Categories**
|
||||
|
||||
<p align="center">
|
||||
@@ -111,11 +124,23 @@ When you open the Front Matter panel on a Markdown file, you get to see the foll
|
||||
**Other actions**
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/other-actions.png" alt="Other actions" style="display: inline-block" />
|
||||
<img src="./assets/v2.5.0/other-actions.png" alt="Other actions" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
**Info**: The `Enable write settings` action allow you to make Markdown specific changes to optimize the writing of your articles. It will change settings like the `fontSize`, `lineHeight`, `wordWrap`, `lineNumbers` and more.
|
||||
|
||||
## Site preview
|
||||
|
||||
The Markdown preview is not consistently delivering the same result as the one you will see on your site. The Front Matter extension provides you a way to show the site result. For this, you will first have to set the `frontMatter.preview.host` setting. You can perform it from the `Global Settings` section in the panel or in your `settings.json` file to set the setting.
|
||||
|
||||
For example, with Hugo, the local server spins up on `http://localhost:1313`. When you set this URL as the value of the `frontMatter.preview.host` setting. You can click on the `open preview` button and the site preview will be shown.
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.5.0/site-preview.png" alt="Site preview" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> **Important**: Be sure to spin up your local server. This is an action the extension currently doesn't do for you. If you want, you create yourself a custom action in order to start it.
|
||||
|
||||
## 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.
|
||||
@@ -241,6 +266,10 @@ 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.
|
||||
|
||||
**Front Matter: Preview article**
|
||||
|
||||
Opens the site preview for the current article. More information about it can be found in the [site preview](#site-preview) section.
|
||||
|
||||
### Usage
|
||||
|
||||
- Start by opening the command prompt:
|
||||
@@ -404,12 +433,34 @@ Specify if you want to highlight the Front Matter in the Markdown file. Default:
|
||||
}
|
||||
```
|
||||
|
||||
### `frontMatter.preview.host`
|
||||
|
||||
Specify the host URL (example: http://localhost:1313) to be used when opening the preview.
|
||||
|
||||
```json
|
||||
{
|
||||
"frontMatter.preview.host": ""
|
||||
}
|
||||
```
|
||||
|
||||
### `frontMatter.preview.pathName`
|
||||
|
||||
Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM` (if not set via the slug). The date will be generated based on the article its date field value.
|
||||
|
||||
```json
|
||||
{
|
||||
"frontMatter.preview.pathName": ""
|
||||
}
|
||||
```
|
||||
|
||||
> **Important**: As the value will be formatted with the article's date, it will try to convert all characters you enter. In case you wan to skip some characters or all of them, you need to wrap that part between two single quotes. Example: `"'blog/'yyyy/MM"` will result in: `blog/2021/08`.
|
||||
|
||||
## Feedback / issues / ideas
|
||||
|
||||
Please submit them via creating an issue in the project repository: [issue list](https://github.com/estruyf/vscode-front-matter/issues).
|
||||
|
||||
<p align="center">
|
||||
<a href="#">
|
||||
<img src="https://estruyf-github.azurewebsites.net/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" />
|
||||
<a href="https://visitorbadge.io">
|
||||
<img src="https://estruyf-github.azurewebsites.net/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" height="25px" />
|
||||
</a>
|
||||
</p>
|
||||
BIN
assets/frontmatter-128x128.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
40
assets/frontmatter-dark.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 28 28" style="enable-background:new 0 0 28 28;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
|
||||
.st3{fill:none;}
|
||||
</style>
|
||||
<g class="st0">
|
||||
<path class="st1" d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z"/>
|
||||
<path class="st1" d="M10.9,11.4H9l-0.9-3c0-0.1,0-0.1,0-0.2C8,8.1,8,8,7.9,7.8v0.6v3H6.1V2.9H8c0.8,0,1.4,0.2,1.9,0.6
|
||||
c0.5,0.5,0.8,1.3,0.8,2.2c0,1-0.4,1.7-1.1,2.1L10.9,11.4z M8,6.8h0.1c0.2,0,0.4-0.1,0.5-0.3C8.7,6.3,8.8,6,8.8,5.7
|
||||
c0-0.6-0.3-1-0.8-1l0,0V6.8z"/>
|
||||
<path class="st1" d="M16.5,7.2c0,1.3-0.2,2.4-0.7,3.2c-0.5,0.8-1.1,1.2-1.8,1.2s-1.2-0.3-1.7-0.9c-0.6-0.8-0.9-2-0.9-3.5
|
||||
s0.3-2.7,0.9-3.5c0.5-0.6,1-0.9,1.7-0.9c0.8,0,1.4,0.4,1.9,1.2C16.2,4.7,16.5,5.8,16.5,7.2z M14.6,7.2c0-1.5-0.2-2.3-0.7-2.3
|
||||
c-0.2,0-0.4,0.2-0.5,0.6s-0.2,0.9-0.2,1.7c0,0.7,0.1,1.3,0.2,1.7c0.1,0.4,0.3,0.6,0.5,0.6s0.4-0.2,0.5-0.6
|
||||
C14.5,8.4,14.6,7.9,14.6,7.2z"/>
|
||||
<path class="st1" d="M17.2,11.4V2.9H19l0.9,3C20,6,20,6.2,20.1,6.5c0.1,0.2,0.1,0.5,0.2,0.8L20.5,8c-0.1-0.7-0.1-1.4-0.2-1.9
|
||||
s-0.1-1-0.1-1.3V2.9H22v8.5h-1.7l-0.9-3.1c-0.1-0.3-0.2-0.6-0.3-0.9S19,6.8,18.9,6.6c0,0.6,0.1,1.1,0.1,1.6c0,0.4,0,0.8,0,1.2v2.2
|
||||
h-1.8V11.4z"/>
|
||||
<path class="st1" d="M25.3,11.4h-1.8V4.9h-1v-2h3.9v2h-1.1V11.4z"/>
|
||||
</g>
|
||||
<rect class="st2" width="28" height="28"/>
|
||||
<g class="st0">
|
||||
<path class="st1" d="M2.9,17h0.9l0.6,3c0.1,0.4,0.2,0.8,0.2,1.2c0.1,0.4,0.1,0.8,0.2,1.2c0-0.1,0-0.1,0-0.1v-0.1L5,21.3l0.1-0.8
|
||||
L5.2,20l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3s0-0.2,0-0.2v-1v-0.9l0,0c0,0,0,0,0-0.1v0.2c0,0.2,0,0.3-0.1,0.5
|
||||
s0,0.2-0.1,0.3l-0.1,0.7v0.3l-0.6,3.3H4.5l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2l-0.3,5.2h-1L2.9,17z"/>
|
||||
<path class="st1" d="M9.3,17h0.8l1.6,7.5h-1L10.4,23H8.9l-0.3,1.5h-1L9.3,17z M10.3,22.2L10,21c-0.1-0.8-0.3-1.7-0.4-2.6
|
||||
c0,0.5-0.1,0.9-0.2,1.4c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1L10.3,22.2L10.3,22.2z"/>
|
||||
<path class="st1" d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z"/>
|
||||
<path class="st1" d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z"/>
|
||||
<path class="st1" d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z"/>
|
||||
<path class="st1" d="M22.3,17h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6
|
||||
l1.4,3.7h-1l-1.4-3.7v3.7h-1L22.3,17L22.3,17z M23.3,20.3c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6
|
||||
s-0.1-0.3-0.2-0.4s-0.2-0.2-0.3-0.2c-0.1,0-0.3-0.1-0.4-0.1h-0.2v2.5H23.3z"/>
|
||||
</g>
|
||||
<rect x="-33.5" y="14" class="st3" width="8.6" height="14"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
44
assets/frontmatter-mag.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 28 28" style="color:#ad0670" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st1 {
|
||||
fill: none;
|
||||
stroke: #ad0670;
|
||||
stroke-width: 2;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
|
||||
<g class="st0">
|
||||
<path fill="currentcolor" d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z" />
|
||||
<path fill="currentcolor" d="M10.9,11.4H9l-0.9-3c0-0.1,0-0.1,0-0.2C8,8.1,8,8,7.9,7.8l0,0.6v3H6.1V2.9H8c0.8,0,1.4,0.2,1.9,0.6
|
||||
c0.5,0.5,0.8,1.3,0.8,2.2c0,1-0.4,1.7-1.1,2.1L10.9,11.4z M8,6.8h0.1c0.2,0,0.4-0.1,0.5-0.3C8.7,6.3,8.8,6,8.8,5.7
|
||||
c0-0.6-0.3-1-0.8-1H8V6.8z" />
|
||||
<path fill="currentcolor" d="M16.5,7.2c0,1.3-0.2,2.4-0.7,3.2c-0.5,0.8-1.1,1.2-1.8,1.2c-0.7,0-1.2-0.3-1.7-0.9c-0.6-0.8-0.9-2-0.9-3.5
|
||||
c0-1.5,0.3-2.7,0.9-3.5c0.5-0.6,1-0.9,1.7-0.9c0.8,0,1.4,0.4,1.9,1.2C16.2,4.7,16.5,5.8,16.5,7.2z M14.6,7.2c0-1.5-0.2-2.3-0.7-2.3
|
||||
c-0.2,0-0.4,0.2-0.5,0.6c-0.1,0.4-0.2,0.9-0.2,1.7c0,0.7,0.1,1.3,0.2,1.7c0.1,0.4,0.3,0.6,0.5,0.6c0.2,0,0.4-0.2,0.5-0.6
|
||||
C14.5,8.4,14.6,7.9,14.6,7.2z" />
|
||||
<path fill="currentcolor" d="M17.2,11.4V2.9H19l0.9,3C20,6,20,6.2,20.1,6.5c0.1,0.2,0.1,0.5,0.2,0.8L20.5,8c-0.1-0.7-0.1-1.4-0.2-1.9s-0.1-1-0.1-1.3
|
||||
V2.9H22v8.5h-1.7l-0.9-3.1c-0.1-0.3-0.2-0.6-0.3-0.9s-0.1-0.6-0.2-0.8c0,0.6,0.1,1.1,0.1,1.6c0,0.4,0,0.8,0,1.2v2.2H17.2z" />
|
||||
<path fill="currentcolor" d="M25.3,11.4h-1.8V4.9h-1v-2h3.9v2h-1.1V11.4z" />
|
||||
</g>
|
||||
|
||||
<rect class="st1" width="28" height="28" />
|
||||
|
||||
<g class="st0">
|
||||
<path fill="currentcolor" d="M2.9,17h0.9L4.4,20c0.1,0.4,0.2,0.8,0.2,1.2c0.1,0.4,0.1,0.8,0.2,1.2c0-0.1,0-0.1,0-0.1c0,0,0-0.1,0-0.1L5,21.3l0.1-0.8
|
||||
L5.2,20l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3s0-0.2,0-0.2l0-1l0-0.9c0,0,0,0,0,0c0,0,0,0,0-0.1l0,0.2
|
||||
c0,0.2,0,0.3-0.1,0.5s0,0.2-0.1,0.3l-0.1,0.7l0,0.3l-0.6,3.3H4.5l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2
|
||||
l-0.3,5.2h-1L2.9,17z" />
|
||||
<path fill="currentcolor" d="M9.3,17h0.8l1.6,7.5h-1L10.4,23H8.9l-0.3,1.5h-1L9.3,17z M10.3,22.2L10,21c-0.1-0.8-0.3-1.7-0.4-2.6c0,0.5-0.1,0.9-0.2,1.4
|
||||
c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1H10.3z" />
|
||||
<path fill="currentcolor" d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z" />
|
||||
<path fill="currentcolor" d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z" />
|
||||
<path fill="currentcolor" d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z" />
|
||||
<path fill="currentcolor" d="M22.3,17h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6l1.4,3.7h-1
|
||||
l-1.4-3.7v3.7h-1V17z M23.3,20.3c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6s-0.1-0.3-0.2-0.4
|
||||
s-0.2-0.2-0.3-0.2c-0.1,0-0.3-0.1-0.4-0.1h-0.2V20.3z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
BIN
assets/frontmatter.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
@@ -268,6 +268,14 @@
|
||||
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.base__action label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.base__action input {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.seo__status__details ul > * + *,
|
||||
.ext_settings > * + * {
|
||||
--tw-space-y-reverse: 0;
|
||||
|
||||
BIN
assets/v2.5.0/actions.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/v2.5.0/baseview.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
assets/v2.5.0/global-settings.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/v2.5.0/other-actions.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/v2.5.0/site-preview.png
Normal file
|
After Width: | Height: | Size: 473 KiB |
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vscode-front-matter",
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
24
package.json
@@ -2,8 +2,8 @@
|
||||
"name": "vscode-front-matter",
|
||||
"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.4.1",
|
||||
"icon": "assets/frontmatter-128x128.png",
|
||||
"version": "2.5.1",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
@@ -58,6 +58,7 @@
|
||||
"onCommand:frontMatter.createContent",
|
||||
"onCommand:frontMatter.init",
|
||||
"onCommand:frontMatter.collapseSections",
|
||||
"onCommand:frontMatter.preview",
|
||||
"onView:frontMatter.explorer"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
@@ -110,6 +111,16 @@
|
||||
"default": true,
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true."
|
||||
},
|
||||
"frontMatter.preview.host": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview."
|
||||
},
|
||||
"frontMatter.preview.pathName": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value."
|
||||
},
|
||||
"frontMatter.taxonomy.dateField": {
|
||||
"type": "string",
|
||||
"default": "date",
|
||||
@@ -283,6 +294,11 @@
|
||||
"light": "assets/icons/close-light.svg",
|
||||
"dark": "assets/icons/close-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.preview",
|
||||
"title": "Preview article",
|
||||
"category": "Front matter"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -312,6 +328,10 @@
|
||||
"command": "frontMatter.createTemplate",
|
||||
"when": "!frontMatterCanInit"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.preview",
|
||||
"when": "frontMatterCanOpenPreview"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.collapseSections",
|
||||
"when": "false"
|
||||
|
||||
@@ -18,13 +18,13 @@ export class Article {
|
||||
* @param type
|
||||
*/
|
||||
public static async insert(type: TaxonomyType) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const article = Article.getCurrent();
|
||||
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export class Article {
|
||||
* @param article
|
||||
*/
|
||||
public static updateDate(article: matter.GrayMatterFile<string>, forceCreate: boolean = false) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const dateFormat = config.get(SETTING_DATE_FORMAT) as string;
|
||||
const dateField = config.get(SETTING_DATE_FIELD) as string || "date";
|
||||
const modField = config.get(SETTING_MODIFIED_FIELD) as string || "date";
|
||||
@@ -116,7 +116,7 @@ export class Article {
|
||||
* Sets the article lastmod date
|
||||
*/
|
||||
public static async setLastModifiedDate() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
@@ -148,7 +148,7 @@ export class Article {
|
||||
* Generate the slug based on the article title
|
||||
*/
|
||||
public static async generateSlug() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
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;
|
||||
@@ -231,7 +231,7 @@ export class Article {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
||||
if (txtChanges.length > 0 && editor && ArticleHelper.isMarkdownFile()) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const autoUpdate = config.get(SETTING_AUTO_UPDATE_DATE);
|
||||
|
||||
if (autoUpdate) {
|
||||
@@ -251,6 +251,23 @@ export class Article {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current article
|
||||
*/
|
||||
private static getCurrent(): matter.GrayMatterFile<string> | undefined {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
return article;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the article date and return it
|
||||
* @param article
|
||||
|
||||
@@ -6,6 +6,7 @@ import uniqBy = require("lodash.uniqby");
|
||||
import { Template } from "./Template";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { CONTEXT } from "../constants/context";
|
||||
import { SettingsHelper } from "../helpers";
|
||||
|
||||
export class Folders {
|
||||
|
||||
@@ -151,7 +152,7 @@ export class Folders {
|
||||
* @returns
|
||||
*/
|
||||
private static get() {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const folders: ContentFolder[] = config.get(SETTINGS_CONTENT_FOLDERS) as ContentFolder[];
|
||||
return folders;
|
||||
}
|
||||
@@ -161,7 +162,7 @@ export class Folders {
|
||||
* @param folders
|
||||
*/
|
||||
private static async update(folders: ContentFolder[]) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
await config.update(SETTINGS_CONTENT_FOLDERS, folders);
|
||||
}
|
||||
}
|
||||
137
src/commands/Preview.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME } from './../constants/settings';
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { join } from "path";
|
||||
import { commands, env, Uri, ViewColumn, window } from "vscode";
|
||||
import { SettingsHelper } from '../helpers';
|
||||
import { PreviewSettings } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
|
||||
|
||||
export class Preview {
|
||||
|
||||
/**
|
||||
* Init the preview
|
||||
*/
|
||||
public static async init() {
|
||||
const settings = Preview.getSettings();
|
||||
await commands.executeCommand('setContext', CONTEXT.canOpenPreview, !!settings.host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the markdown preview in the editor
|
||||
*/
|
||||
public static async open(extensionPath: string) {
|
||||
const settings = Preview.getSettings();
|
||||
|
||||
if (!settings.host) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
const article = editor ? ArticleHelper.getFrontMatter(editor) : null;
|
||||
let slug = article?.data ? article.data.slug : "";
|
||||
|
||||
if (settings.pathname) {
|
||||
const articleDate = ArticleHelper.getDate(article);
|
||||
try {
|
||||
slug = join(format(articleDate || new Date(), settings.pathname), slug);
|
||||
} catch (error) {
|
||||
slug = join(settings.pathname, slug);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the preview webview
|
||||
const webView = window.createWebviewPanel(
|
||||
'frontMatterPreview',
|
||||
'FrontMatter Preview',
|
||||
{
|
||||
viewColumn: ViewColumn.Beside,
|
||||
preserveFocus: true
|
||||
},
|
||||
{
|
||||
enableScripts: true
|
||||
}
|
||||
);
|
||||
|
||||
webView.iconPath = {
|
||||
dark: Uri.file(join(extensionPath, 'assets/frontmatter-dark.svg')),
|
||||
light: Uri.file(join(extensionPath, 'assets/frontmatter.svg'))
|
||||
}
|
||||
|
||||
const localhostUrl = await env.asExternalUri(
|
||||
Uri.parse(settings.host)
|
||||
);
|
||||
|
||||
const cspSource = webView.webview.cspSource;
|
||||
|
||||
webView.webview.html = `<!DOCTYPE html>
|
||||
<head>
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'none'; frame-src ${localhostUrl} ${cspSource} http: https:; img-src ${localhostUrl} ${cspSource} http: https:; script-src ${localhostUrl} ${cspSource} 'unsafe-inline'; style-src ${localhostUrl} ${cspSource} 'self' 'unsafe-inline' http: https:;"
|
||||
/>
|
||||
<style>
|
||||
html,body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: calc(100% - 30px);
|
||||
border: 0;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.slug {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--vscode-editor-background);
|
||||
border-bottom: 1px solid var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
input {
|
||||
color: var(--vscode-editor-foreground);
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: none;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="slug">
|
||||
<input type="text" value="${join(localhostUrl.toString(), slug)}" disabled />
|
||||
</div>
|
||||
<iframe src="${join(localhostUrl.toString(), slug)}" >
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all settings related to the preview command
|
||||
*/
|
||||
public static getSettings(): PreviewSettings {
|
||||
|
||||
const config = SettingsHelper.getConfig();
|
||||
const host = config.get<string>(SETTING_PREVIEW_HOST);
|
||||
const pathname = config.get<string>(SETTING_PREVIEW_PATHNAME);
|
||||
|
||||
return {
|
||||
host,
|
||||
pathname
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export class Settings {
|
||||
});
|
||||
|
||||
if (newOption) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const configSetting = type === TaxonomyType.Tag ? SETTING_TAXONOMY_TAGS : SETTING_TAXONOMY_CATEGORIES;
|
||||
let options = config.get(configSetting) as string[];
|
||||
if (!options) {
|
||||
@@ -72,7 +72,7 @@ export class Settings {
|
||||
* Export the tags/categories front matter to the user settings
|
||||
*/
|
||||
public static async export() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
|
||||
// Retrieve all the Markdown files
|
||||
const allMdFiles = await FilesHelper.getMdFiles();
|
||||
@@ -155,8 +155,6 @@ export class Settings {
|
||||
* Remap a tag or category to a new one
|
||||
*/
|
||||
public static async remap() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
|
||||
const taxType = await vscode.window.showQuickPick([
|
||||
"Tag",
|
||||
"Category"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants/settings';
|
||||
import * as vscode from 'vscode';
|
||||
import { CONFIG_KEY } from '../constants';
|
||||
import { ArticleHelper, SeoHelper } from '../helpers';
|
||||
import { ArticleHelper, SeoHelper, SettingsHelper } from '../helpers';
|
||||
import { ExplorerView } from '../webview/ExplorerView';
|
||||
|
||||
export class StatusListener {
|
||||
@@ -38,7 +37,7 @@ export class StatusListener {
|
||||
collection.clear();
|
||||
|
||||
// Retrieve the SEO config properties
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const titleLength = config.get(SETTING_SEO_TITLE_LENGTH) as number || -1;
|
||||
const descLength = config.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1;
|
||||
const fieldName = config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description";
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
||||
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 { ArticleHelper, SettingsHelper } from '../helpers';
|
||||
import { Article } from '.';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
@@ -93,7 +93,7 @@ export class Template {
|
||||
* Create from a template
|
||||
*/
|
||||
public static async create(folderPath: string) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const folder = config.get<string>(SETTING_TEMPLATES_FOLDER);
|
||||
const prefix = config.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
|
||||
@@ -188,7 +188,7 @@ export class Template {
|
||||
* Get the folder settings
|
||||
*/
|
||||
public static getSettings() {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const folder = config.get<string>(SETTING_TEMPLATES_FOLDER);
|
||||
return folder;
|
||||
}
|
||||
|
||||
@@ -22,4 +22,5 @@ export const COMMAND_NAME = {
|
||||
createContent: getCommandName("createContent"),
|
||||
createTemplate: getCommandName("createTemplate"),
|
||||
collapseSections: getCommandName("collapseSections"),
|
||||
preview: getCommandName("preview"),
|
||||
};
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
export const CONTEXT = {
|
||||
canInit: "frontMatterCanInit",
|
||||
canOpenPreview: "frontMatterCanOpenPreview",
|
||||
registeredFolders: 'frontMatter.registeredFolders'
|
||||
};
|
||||
@@ -27,6 +27,9 @@ export const SETTING_TEMPLATES_PREFIX = "templates.prefix";
|
||||
|
||||
export const SETTING_PANEL_FREEFORM = "panel.freeform";
|
||||
|
||||
export const SETTING_PREVIEW_HOST = "preview.host";
|
||||
export const SETTING_PREVIEW_PATHNAME = "preview.pathName";
|
||||
|
||||
export const SETTING_CUSTOM_SCRIPTS = "custom.scripts";
|
||||
|
||||
export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { Article, Settings, StatusListener } from './commands';
|
||||
import { Folders } from './commands/Folders';
|
||||
import { Preview } from './commands/Preview';
|
||||
import { Project } from './commands/Project';
|
||||
import { Template } from './commands/Template';
|
||||
import { COMMAND_NAME } from './constants/Extension';
|
||||
@@ -16,7 +17,7 @@ let collection: vscode.DiagnosticCollection;
|
||||
|
||||
const mdSelector: vscode.DocumentSelector = { language: 'markdown', scheme: 'file' };
|
||||
|
||||
export async function activate({ subscriptions, extensionUri }: vscode.ExtensionContext) {
|
||||
export async function activate({ subscriptions, extensionUri, extensionPath }: vscode.ExtensionContext) {
|
||||
collection = vscode.languages.createDiagnosticCollection('frontMatter');
|
||||
|
||||
const explorerSidebar = ExplorerView.getInstance(extensionUri);
|
||||
@@ -105,6 +106,7 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
// Things to do when configuration changes
|
||||
vscode.workspace.onDidChangeConfiguration(() => {
|
||||
Template.init();
|
||||
Preview.init();
|
||||
Folders.updateVsCodeCtx();
|
||||
});
|
||||
|
||||
@@ -128,6 +130,10 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
// Listener for setting changes
|
||||
subscriptions.push(vscode.workspace.onDidChangeConfiguration(MarkdownFoldingProvider.triggerHighlighting));
|
||||
|
||||
// Webview for preview
|
||||
Preview.init();
|
||||
subscriptions.push(vscode.commands.registerCommand('frontMatter.preview', () => Preview.open(extensionPath) ));
|
||||
|
||||
// Subscribe all commands
|
||||
subscriptions.push(
|
||||
insertTags,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as matter from "gray-matter";
|
||||
import * as fs from "fs";
|
||||
import { CONFIG_KEY, SETTING_INDENT_ARRAY, SETTING_REMOVE_QUOTES } from '../constants';
|
||||
import { CONFIG_KEY, SETTING_DATE_FIELD, SETTING_DATE_FORMAT, SETTING_INDENT_ARRAY, SETTING_REMOVE_QUOTES } from '../constants';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { TomlEngine, getFmLanguage, getFormatOpts } from './TomlEngine';
|
||||
import { SettingsHelper } from '.';
|
||||
import { parse } from 'date-fns';
|
||||
|
||||
export class ArticleHelper {
|
||||
|
||||
@@ -54,7 +56,7 @@ export class ArticleHelper {
|
||||
* @param article
|
||||
*/
|
||||
public static async update(editor: vscode.TextEditor, article: matter.GrayMatterFile<string>) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const removeQuotes = config.get(SETTING_REMOVE_QUOTES) as string[];
|
||||
|
||||
let newMarkdown = this.stringifyFrontMatter(article.content, article.data);
|
||||
@@ -80,7 +82,7 @@ export class ArticleHelper {
|
||||
* @param data
|
||||
*/
|
||||
public static stringifyFrontMatter(content: string, data: any) {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const indentArray = config.get(SETTING_INDENT_ARRAY) as boolean;
|
||||
|
||||
const language = getFmLanguage();
|
||||
@@ -104,4 +106,28 @@ export class ArticleHelper {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
return (editor && editor.document && (editor.document.languageId.toLowerCase() === "markdown" || editor.document.languageId.toLowerCase() === "mdx"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date from front matter
|
||||
*/
|
||||
public static getDate(article: matter.GrayMatterFile<string> | null) {
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = SettingsHelper.getConfig();
|
||||
const dateFormat = config.get(SETTING_DATE_FORMAT) as string;
|
||||
const dateField = config.get(SETTING_DATE_FIELD) as string || "date";
|
||||
|
||||
if (typeof article.data[dateField] !== "undefined") {
|
||||
if (dateFormat && typeof dateFormat === "string") {
|
||||
const date = parse(article.data[dateField], dateFormat, new Date());
|
||||
return date;
|
||||
} else {
|
||||
const date = new Date(article.data[dateField]);
|
||||
return date;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY } from '
|
||||
|
||||
export class SettingsHelper {
|
||||
|
||||
public static getConfig(): vscode.WorkspaceConfiguration {
|
||||
return vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the taxonomy settings
|
||||
*
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as toml from '@iarna/toml';
|
||||
import { CONFIG_KEY, SETTING_FRONTMATTER_TYPE } from '../constants';
|
||||
import { SETTING_FRONTMATTER_TYPE } from '../constants';
|
||||
import { SettingsHelper } from '.';
|
||||
|
||||
export const getFmLanguage = (): string => {
|
||||
const config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const language = config.get(SETTING_FRONTMATTER_TYPE) as string || "YAML";
|
||||
return language.toLowerCase();
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface PanelSettings {
|
||||
contentInfo: FolderInfo[] | null;
|
||||
writingSettingsEnabled: boolean;
|
||||
fmHighlighting: boolean;
|
||||
preview: PreviewSettings;
|
||||
}
|
||||
|
||||
export interface SEO {
|
||||
@@ -34,4 +35,9 @@ export interface CustomScript {
|
||||
title: string;
|
||||
script: string;
|
||||
nodeBin?: string;
|
||||
}
|
||||
|
||||
export interface PreviewSettings {
|
||||
host: string | undefined;
|
||||
pathname: string | undefined;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TextEditorDecorationType, workspace } from 'vscode';
|
||||
import { TextEditorDecorationType } from 'vscode';
|
||||
import { CancellationToken, FoldingContext, FoldingRange, FoldingRangeKind, FoldingRangeProvider, Range, TextDocument, window, Position } from 'vscode';
|
||||
import { CONFIG_KEY, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT } from '../constants';
|
||||
import { SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT } from '../constants';
|
||||
import { SettingsHelper } from '../helpers';
|
||||
import { FrontMatterDecorationProvider } from './FrontMatterDecorationProvider';
|
||||
|
||||
export class MarkdownFoldingProvider implements FoldingRangeProvider {
|
||||
@@ -43,7 +44,7 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider {
|
||||
}
|
||||
|
||||
public static triggerHighlighting() {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const fmHighlight = config.get<boolean>(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT);
|
||||
|
||||
if (MarkdownFoldingProvider.start !== null && MarkdownFoldingProvider.end !== null && MarkdownFoldingProvider.endLine !== null) {
|
||||
|
||||
@@ -20,4 +20,6 @@ export enum CommandToCode {
|
||||
createTemplate = "create-template",
|
||||
toggleCenterMode = "toggle-center-mode",
|
||||
toggleWritingSettings = "toggle-writing-settings",
|
||||
openPreview = "open-preview",
|
||||
updatePreviewUrl = "update-preview-url",
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { PanelSettings } from '../../models/PanelSettings';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import { CustomScript } from './CustomScript';
|
||||
import { DateAction } from './DateAction';
|
||||
import { Preview } from './Preview';
|
||||
import { PublishAction } from './PublishAction';
|
||||
import { SlugAction } from './SlugAction';
|
||||
|
||||
@@ -25,10 +23,10 @@ export const Actions: React.FunctionComponent<IActionsProps> = (props: React.Pro
|
||||
<Collapsible id={`actions`} title="Actions">
|
||||
<div className={`article__actions`}>
|
||||
|
||||
<ActionButton onClick={() => MessageHelper.sendMessage(CommandToCode.toggleCenterMode)} title={`Toggle center mode`} />
|
||||
|
||||
{ metadata && metadata.title && <SlugAction value={metadata.title} crntValue={metadata.slug} slugOpts={settings.slug} /> }
|
||||
|
||||
{ settings?.preview?.host && <Preview slug={metadata.slug} /> }
|
||||
|
||||
<DateAction />
|
||||
|
||||
{ metadata && typeof metadata.draft !== undefined && <PublishAction draft={metadata.draft} />}
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as React from 'react';
|
||||
import { PanelSettings } from '../../models';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { useDebounce } from '../hooks/useDebounce';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import { VsCheckbox } from './VscodeComponents';
|
||||
import { VsCheckbox, VsLabel } from './VscodeComponents';
|
||||
|
||||
export interface IGlobalSettingsProps {
|
||||
settings: PanelSettings | undefined;
|
||||
@@ -12,6 +13,9 @@ export interface IGlobalSettingsProps {
|
||||
|
||||
export const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings, isBase}: React.PropsWithChildren<IGlobalSettingsProps>) => {
|
||||
const { modifiedDateUpdate, fmHighlighting } = settings || {};
|
||||
const [ previewUrl, setPreviewUrl ] = React.useState<string>("");
|
||||
const [ isDirty, setIsDirty ] = React.useState<boolean>(false);
|
||||
const debouncePreviewUrl = useDebounce(previewUrl, 1000);
|
||||
|
||||
const onDateCheck = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.updateModifiedUpdating, !modifiedDateUpdate);
|
||||
@@ -21,15 +25,44 @@ export const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({s
|
||||
MessageHelper.sendMessage(CommandToCode.updateFmHighlight, !fmHighlighting);
|
||||
};
|
||||
|
||||
const previewChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setIsDirty(true);
|
||||
setPreviewUrl(e.currentTarget.value);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (settings?.preview.host) {
|
||||
setPreviewUrl(settings.preview.host);
|
||||
}
|
||||
}, [settings?.preview.host]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDirty) {
|
||||
console.log(debouncePreviewUrl);
|
||||
setIsDirty(false);
|
||||
MessageHelper.sendMessage(CommandToCode.updatePreviewUrl, debouncePreviewUrl);
|
||||
}
|
||||
}, [debouncePreviewUrl]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Collapsible id={`${isBase ? "base_" : ""}settings`} title="Global settings">
|
||||
<div className={`base__actions`}>
|
||||
<Collapsible id={`${isBase ? "base_" : ""}settings`} className={`base__actions`} title="Global settings">
|
||||
<div className={`base__action`}>
|
||||
<VsLabel>Modified date</VsLabel>
|
||||
<VsCheckbox label="Auto-update modified date" checked={modifiedDateUpdate} onClick={onDateCheck} />
|
||||
</div>
|
||||
<div className={`base__actions`}>
|
||||
<div className={`base__action`}>
|
||||
<VsLabel>Front Matter highlight</VsLabel>
|
||||
<VsCheckbox label="Highlight Front Matter" checked={fmHighlighting} onClick={onHighlightCheck} />
|
||||
</div>
|
||||
<div className={`base__action`}>
|
||||
<VsLabel>Local preview</VsLabel>
|
||||
<input
|
||||
type={`text`}
|
||||
placeholder="Example: http://localhost:1313"
|
||||
value={previewUrl}
|
||||
onChange={previewChange} />
|
||||
</div>
|
||||
</Collapsible>
|
||||
</>
|
||||
);
|
||||
|
||||
12
src/viewpanel/components/Icons/CenterIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ICenterIconProps {}
|
||||
|
||||
export const CenterIcon: React.FunctionComponent<ICenterIconProps> = (props: React.PropsWithChildren<ICenterIconProps>) => {
|
||||
return (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect x="1.2" y="0.6" stroke="currentcolor" strokeMiterlimit="10" width="13.6" height="14.8"/>
|
||||
<path stroke="currentcolor" d="M2.6,10V9h10.8v1H2.6z M2.6,6h10.8v1H2.6V6z M13.4,3v1H2.6V3H13.4z M2.6,12v1h10.8v-1H2.6z"/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import { BugIcon } from './Icons/BugIcon';
|
||||
import { CenterIcon } from './Icons/CenterIcon';
|
||||
import { FileIcon } from './Icons/FileIcon';
|
||||
import { FolderOpenedIcon } from './Icons/FolderOpenedIcon';
|
||||
import { SettingsIcon } from './Icons/SettingsIcon';
|
||||
@@ -44,6 +45,10 @@ export const OtherActions: React.FunctionComponent<IOtherActionsProps> = ({isFil
|
||||
<Collapsible id={`${isBase ? "base_" : ""}other_actions`} title="Other actions" className={`other_actions`}>
|
||||
<OtherActionButton className={settings?.writingSettingsEnabled ? "active" : ""} onClick={toggleWritingSettings} disabled={typeof settings?.writingSettingsEnabled === "undefined"}><WritingIcon /> <span>{settings?.writingSettingsEnabled ? "Writing settings enabled" : "Enable writing settings"}</span></OtherActionButton>
|
||||
|
||||
<OtherActionButton onClick={() => MessageHelper.sendMessage(CommandToCode.toggleCenterMode)}>
|
||||
<CenterIcon /> <span>Toggle center mode</span>
|
||||
</OtherActionButton>
|
||||
|
||||
<OtherActionButton onClick={createAsTemplate} disabled={!isFile}><TemplateIcon /> <span>Create as template</span></OtherActionButton>
|
||||
|
||||
<OtherActionButton onClick={openSettings}><SettingsIcon /> <span>Open settings</span></OtherActionButton>
|
||||
|
||||
23
src/viewpanel/components/Preview.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
|
||||
export interface IPreviewProps {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export const Preview: React.FunctionComponent<IPreviewProps> = ({slug}: React.PropsWithChildren<IPreviewProps>) => {
|
||||
|
||||
const open = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.openPreview);
|
||||
};
|
||||
|
||||
if (!slug) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionButton onClick={open} title={`Open preview`} />
|
||||
);
|
||||
};
|
||||
@@ -7,4 +7,5 @@ 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`);
|
||||
export const VsCheckbox = wrapWc(`vscode-checkbox`);
|
||||
export const VsCheckbox = wrapWc(`vscode-checkbox`);
|
||||
export const VsLabel = wrapWc(`vscode-label`);
|
||||
25
src/viewpanel/hooks/useDebounce.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useDebounce(value: string, delay: number) {
|
||||
// State and setters for debounced value
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
// Update debounced value after delay
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
|
||||
// Cancel the timeout if value changes (also on delay change or unmount)
|
||||
// This is how we prevent debounced value from updating if value is changed ...
|
||||
// .. within the delay period. Timeout gets cleared and restarted.
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
},
|
||||
[value, delay] // Only re-call effect if value or delay changes
|
||||
);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ 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';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-checkbox';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-label';
|
||||
|
||||
declare const acquireVsCodeApi: <T = unknown>() => {
|
||||
getState: () => T;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Template } from './../commands/Template';
|
||||
import { SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME } from './../constants/settings';
|
||||
import { SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST } 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";
|
||||
import { CONFIG_KEY, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants";
|
||||
import { SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants";
|
||||
import { ArticleHelper, SettingsHelper } from "../helpers";
|
||||
import { Command } from "../viewpanel/Command";
|
||||
import { CommandToCode } from '../viewpanel/CommandToCode';
|
||||
@@ -17,6 +17,7 @@ import { Content } from 'mdast';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { COMMAND_NAME } from '../constants/Extension';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { Preview } from '../commands/Preview';
|
||||
|
||||
|
||||
export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
@@ -161,6 +162,12 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
case CommandToCode.toggleCenterMode:
|
||||
await commands.executeCommand(`workbench.action.toggleCenteredLayout`);
|
||||
break;
|
||||
case CommandToCode.openPreview:
|
||||
await commands.executeCommand(COMMAND_NAME.preview);
|
||||
break;
|
||||
case CommandToCode.updatePreviewUrl:
|
||||
this.updatePreviewUrl(msg.data || "");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -222,7 +229,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* @param msg
|
||||
*/
|
||||
private runCustomScript(msg: { command: string, data: any}) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
const scripts: CustomScript[] | undefined = config.get(SETTING_CUSTOM_SCRIPTS);
|
||||
|
||||
if (msg?.data?.title && msg?.data?.script && scripts) {
|
||||
@@ -263,7 +270,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* Retrieve the extension settings
|
||||
*/
|
||||
private async getSettings() {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
|
||||
this.postWebviewMessage({
|
||||
command: Command.settings,
|
||||
@@ -288,6 +295,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
modifiedDateUpdate: config.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: config.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
} as PanelSettings
|
||||
});
|
||||
}
|
||||
@@ -331,7 +339,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
*/
|
||||
private async addTags(tagType: TagType, value: string) {
|
||||
if (value) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
let options = tagType === TagType.tags ? config.get<string[]>(SETTING_TAXONOMY_TAGS) : config.get<string[]>(SETTING_TAXONOMY_CATEGORIES);
|
||||
|
||||
if (!options) {
|
||||
@@ -435,11 +443,20 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
return fontSize && lineHeight && wordWrap && wordWrapColumn && lineNumbers && quickSuggestions !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the preview URL
|
||||
*/
|
||||
private async updatePreviewUrl(previewUrl: string) {
|
||||
const config = SettingsHelper.getConfig();
|
||||
await config.update(SETTING_PREVIEW_HOST, previewUrl);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the Front Matter highlighting
|
||||
*/
|
||||
private async updateFmHighlight(autoUpdate: boolean) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
await config.update(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, autoUpdate);
|
||||
this.getSettings();
|
||||
}
|
||||
@@ -448,7 +465,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* Toggle the modified auto-update setting
|
||||
*/
|
||||
private async updateModifiedUpdating(autoUpdate: boolean) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
const config = SettingsHelper.getConfig();
|
||||
await config.update(SETTING_AUTO_UPDATE_DATE, autoUpdate);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||