Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6395e471f0 | ||
|
|
59f0351b79 | ||
|
|
1b16fa69d0 | ||
|
|
e8f67590bc | ||
|
|
b34a8feb63 | ||
|
|
fcbca1e48a | ||
|
|
4585790b45 | ||
|
|
445b64a2ec | ||
|
|
5d18d09b72 | ||
|
|
fdfdfda6ce | ||
|
|
f016ae27ec | ||
|
|
c9d3eca431 | ||
|
|
486beaf650 | ||
|
|
6e82cf221e | ||
|
|
46b9591859 | ||
|
|
3e76da58f5 | ||
|
|
9d42bd2f97 | ||
|
|
75890ec6e8 | ||
|
|
fb0429e40f | ||
|
|
8db813a661 | ||
|
|
2655be9aae | ||
|
|
fc99756136 | ||
|
|
9159a98dbe |
22
CHANGELOG.md
@@ -1,5 +1,27 @@
|
||||
# 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
|
||||
|
||||
## [2.4.0] - 2020-08-16
|
||||
|
||||
- [#21](https://github.com/estruyf/vscode-front-matter/issues/21): Folding provider for Front Matter implemented
|
||||
- [#55](https://github.com/estruyf/vscode-front-matter/issues/55): Highlight Front Matter in Markdown files
|
||||
- [#56](https://github.com/estruyf/vscode-front-matter/issues/56): Action to collapse all Front Matter panel sections at once
|
||||
- [#57](https://github.com/estruyf/vscode-front-matter/issues/57): New action added to provide better writing settings (only for Markdown files)
|
||||
- [#58](https://github.com/estruyf/vscode-front-matter/issues/58): Sections remember their previous state (folded/unfolded)
|
||||
- [#59](https://github.com/estruyf/vscode-front-matter/issues/59): Center layout view toggle action added
|
||||
|
||||
## [2.3.0] - 2020-08-10
|
||||
|
||||
- Refactoring and showing other actions in the base view
|
||||
|
||||
98
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,19 +24,31 @@ 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">
|
||||
<summary>Table of Contents</summary>
|
||||
<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>
|
||||
@@ -45,6 +57,24 @@ In version v2.0.0 we released the newly redesigned sidebar panel with improved S
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
## Markdown features
|
||||
|
||||
The Front Matter extension tries to make it easy to manage your Markdown pages/content. Within a Markdown page, we allow you to fold the file's Front Matter to be less distracting when writing. Also, do we highlight the Front Matter content to create a visual difference between content and metadata.
|
||||
|
||||
### Front Matter folding
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/folding.png" alt="Front Matter folding" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
### Front Matter highlighting
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.4.0/fm-highlight.png" alt="Front Matter highlighting" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> **Info**: If you do not want this feature, you can disable it in the extension settings -> `Highlight Front Matter` or by setting the `frontMatter.content.fmHighlight` setting to `false`.
|
||||
|
||||
## 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.
|
||||
@@ -56,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.3.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.
|
||||
@@ -66,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.3.0/global-settings.png.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**
|
||||
@@ -78,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.0.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">
|
||||
@@ -92,9 +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.3.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.
|
||||
@@ -220,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:
|
||||
@@ -373,12 +423,44 @@ Specify if you want to automatically update the modification date of your markdo
|
||||
}
|
||||
```
|
||||
|
||||
### `frontMatter.content.fmHighlight`
|
||||
|
||||
Specify if you want to highlight the Front Matter in the Markdown file. Default: `true`.
|
||||
|
||||
```json
|
||||
{
|
||||
"frontMatter.content.fmHighlight": true
|
||||
}
|
||||
```
|
||||
|
||||
### `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 |
4
assets/icons/close-dark.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#F3EFF5">
|
||||
<path d="M9 9H4v1h5V9z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2V3zm1 2h4l1 1v4h2V3H6v2zm4 1H3v7h7V6z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
4
assets/icons/close-light.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentcolor">
|
||||
<path d="M9 9H4v1h5V9z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2V3zm1 2h4l1 1v4h2V3H6v2zm4 1H3v7h7V6z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 282 B |
@@ -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;
|
||||
@@ -278,10 +286,21 @@
|
||||
.ext_link_block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ext_link_block svg {
|
||||
margin-right: .5rem;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.ext_link_block button span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ext_link_block button,
|
||||
@@ -301,6 +320,16 @@
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ext_link_block button.active {
|
||||
color: var(--vscode-button-foreground);
|
||||
background: var(--vscode-button-background);
|
||||
}
|
||||
.ext_link_block button.active:hover {
|
||||
cursor: pointer;
|
||||
background: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.ext_link_block a:hover,
|
||||
|
||||
BIN
assets/v2.4.0/actions.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/v2.4.0/baseview.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
assets/v2.4.0/fm-highlight.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
assets/v2.4.0/folding.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/v2.4.0/global-settings.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/v2.4.0/other-actions.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
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 |
8
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vscode-front-matter",
|
||||
"version": "2.3.0",
|
||||
"version": "2.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -3565,9 +3565,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"pbkdf2": {
|
||||
|
||||
89
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.3.0",
|
||||
"icon": "assets/frontmatter-128x128.png",
|
||||
"version": "2.5.1",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
@@ -57,6 +57,8 @@
|
||||
"onCommand:frontMatter.unregisterFolder",
|
||||
"onCommand:frontMatter.createContent",
|
||||
"onCommand:frontMatter.init",
|
||||
"onCommand:frontMatter.collapseSections",
|
||||
"onCommand:frontMatter.preview",
|
||||
"onView:frontMatter.explorer"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
@@ -84,6 +86,41 @@
|
||||
"configuration": {
|
||||
"title": "Front Matter: Configuration",
|
||||
"properties": {
|
||||
"frontMatter.content.autoUpdateDate": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specify if you want to automatically update the modified date of your article/page."
|
||||
},
|
||||
"frontMatter.content.fmHighlight": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Specify if you want to highlight the Front Matter in the Markdown file."
|
||||
},
|
||||
"frontMatter.content.folders": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "This array of folders defines where the extension can easily create new content by running the create article command."
|
||||
},
|
||||
"frontMatter.custom.scripts": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument."
|
||||
},
|
||||
"frontMatter.panel.freeform": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true."
|
||||
},
|
||||
"frontMatter.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",
|
||||
@@ -94,11 +131,6 @@
|
||||
"default": "lastmod",
|
||||
"description": "Specifies the modified date field name to use in your Front Matter"
|
||||
},
|
||||
"frontMatter.content.autoUpdateDate": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specify if you want to automatically update the modified date of your article/page."
|
||||
},
|
||||
"frontMatter.taxonomy.tags": {
|
||||
"type": "array",
|
||||
"description": "Specifies the tags which can be used in the Front Matter"
|
||||
@@ -175,21 +207,6 @@
|
||||
"type": "string",
|
||||
"default": "yyyy-MM-dd",
|
||||
"description": "Specify the prefix you want to add for your new article filenames."
|
||||
},
|
||||
"frontMatter.panel.freeform": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true."
|
||||
},
|
||||
"frontMatter.custom.scripts": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument."
|
||||
},
|
||||
"frontMatter.content.folders": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "This array of folders defines where the extension can easily create new content by running the create article command."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -268,6 +285,20 @@
|
||||
"command": "frontMatter.createTemplate",
|
||||
"title": "Create a template from current file",
|
||||
"category": "Front matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.collapseSections",
|
||||
"title": "Collapse sections",
|
||||
"category": "Front matter",
|
||||
"icon": {
|
||||
"light": "assets/icons/close-light.svg",
|
||||
"dark": "assets/icons/close-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.preview",
|
||||
"title": "Preview article",
|
||||
"category": "Front matter"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -296,6 +327,20 @@
|
||||
{
|
||||
"command": "frontMatter.createTemplate",
|
||||
"when": "!frontMatterCanInit"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.preview",
|
||||
"when": "frontMatterCanOpenPreview"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.collapseSections",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"command": "frontMatter.collapseSections",
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -20,5 +20,7 @@ export const COMMAND_NAME = {
|
||||
registerFolder: getCommandName("registerFolder"),
|
||||
unregisterFolder: getCommandName("unregisterFolder"),
|
||||
createContent: getCommandName("createContent"),
|
||||
createTemplate: getCommandName("createTemplate")
|
||||
createTemplate: getCommandName("createTemplate"),
|
||||
collapseSections: getCommandName("collapseSections"),
|
||||
preview: getCommandName("preview"),
|
||||
};
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
export const CONTEXT = {
|
||||
canInit: "frontMatterCanInit",
|
||||
canOpenPreview: "frontMatterCanOpenPreview",
|
||||
registeredFolders: 'frontMatter.registeredFolders'
|
||||
};
|
||||
@@ -7,7 +7,6 @@ export const SETTING_TAXONOMY_CATEGORIES = "taxonomy.categories";
|
||||
export const SETTING_DATE_FORMAT = "taxonomy.dateFormat";
|
||||
export const SETTING_DATE_FIELD = "taxonomy.dateField";
|
||||
export const SETTING_MODIFIED_FIELD = "taxonomy.modifiedField";
|
||||
export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate";
|
||||
|
||||
export const SETTING_SLUG_PREFIX = "taxonomy.slugPrefix";
|
||||
export const SETTING_SLUG_SUFFIX = "taxonomy.slugSuffix";
|
||||
@@ -28,6 +27,11 @@ 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 SETTINGS_CONTENT_FOLDERS = "content.folders";
|
||||
export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate";
|
||||
export const SETTINGS_CONTENT_FOLDERS = "content.folders";
|
||||
export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight";
|
||||
@@ -1,10 +1,12 @@
|
||||
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';
|
||||
import { TaxonomyType } from './models';
|
||||
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
|
||||
import { TagType } from './viewpanel/TagType';
|
||||
import { ExplorerView } from './webview/ExplorerView';
|
||||
|
||||
@@ -13,7 +15,9 @@ let statusDebouncer: { (fnc: any, time: number): void; };
|
||||
let editDebounce: { (fnc: any, time: number): void; };
|
||||
let collection: vscode.DiagnosticCollection;
|
||||
|
||||
export async function activate({ subscriptions, extensionUri }: vscode.ExtensionContext) {
|
||||
const mdSelector: vscode.DocumentSelector = { language: 'markdown', scheme: 'file' };
|
||||
|
||||
export async function activate({ subscriptions, extensionUri, extensionPath }: vscode.ExtensionContext) {
|
||||
collection = vscode.languages.createDiagnosticCollection('frontMatter');
|
||||
|
||||
const explorerSidebar = ExplorerView.getInstance(extensionUri);
|
||||
@@ -23,6 +27,9 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
}
|
||||
});
|
||||
|
||||
// Folding the front matter of markdown files
|
||||
vscode.languages.registerFoldingRangeProvider(mdSelector, new MarkdownFoldingProvider());
|
||||
|
||||
let insertTags = vscode.commands.registerCommand(COMMAND_NAME.insertTags, async () => {
|
||||
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
|
||||
await vscode.commands.executeCommand('workbench.action.focusSideBar');
|
||||
@@ -68,7 +75,7 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
if (folderPath) {
|
||||
Template.create(folderPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let createTemplate = vscode.commands.registerCommand(COMMAND_NAME.createTemplate, Template.generate);
|
||||
|
||||
@@ -91,9 +98,15 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
Template.init();
|
||||
const projectInit = vscode.commands.registerCommand(COMMAND_NAME.init, Project.init);
|
||||
|
||||
// Collapse all sections in the webview
|
||||
const collapseAll = vscode.commands.registerCommand(COMMAND_NAME.collapseSections, () => {
|
||||
ExplorerView.getInstance()?.collapseAll();
|
||||
});
|
||||
|
||||
// Things to do when configuration changes
|
||||
vscode.workspace.onDidChangeConfiguration(() => {
|
||||
Template.init();
|
||||
Preview.init();
|
||||
Folders.updateVsCodeCtx();
|
||||
});
|
||||
|
||||
@@ -114,6 +127,13 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
editDebounce = debounceCallback();
|
||||
subscriptions.push(vscode.workspace.onDidChangeTextDocument(triggerFileChange));
|
||||
|
||||
// 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,
|
||||
@@ -132,7 +152,8 @@ export async function activate({ subscriptions, extensionUri }: vscode.Extension
|
||||
registerFolder,
|
||||
unregisterFolder,
|
||||
createContent,
|
||||
projectInit
|
||||
projectInit,
|
||||
collapseAll
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -9,6 +9,9 @@ export interface PanelSettings {
|
||||
isInitialized: boolean;
|
||||
modifiedDateUpdate: boolean;
|
||||
contentInfo: FolderInfo[] | null;
|
||||
writingSettingsEnabled: boolean;
|
||||
fmHighlighting: boolean;
|
||||
preview: PreviewSettings;
|
||||
}
|
||||
|
||||
export interface SEO {
|
||||
@@ -32,4 +35,9 @@ export interface CustomScript {
|
||||
title: string;
|
||||
script: string;
|
||||
nodeBin?: string;
|
||||
}
|
||||
|
||||
export interface PreviewSettings {
|
||||
host: string | undefined;
|
||||
pathname: string | undefined;
|
||||
}
|
||||
13
src/providers/FrontMatterDecorationProvider.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { TextEditorDecorationType, window, ColorThemeKind } from "vscode";
|
||||
|
||||
export class FrontMatterDecorationProvider {
|
||||
|
||||
get(): TextEditorDecorationType {
|
||||
const colorThemeKind = window.activeColorTheme.kind;
|
||||
|
||||
return window.createTextEditorDecorationType({
|
||||
backgroundColor: colorThemeKind === ColorThemeKind.Dark ? "#ffffff14" : "#00000014",
|
||||
isWholeLine: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
63
src/providers/MarkdownFoldingProvider.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { TextEditorDecorationType } from 'vscode';
|
||||
import { CancellationToken, FoldingContext, FoldingRange, FoldingRangeKind, FoldingRangeProvider, Range, TextDocument, window, Position } from 'vscode';
|
||||
import { SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT } from '../constants';
|
||||
import { SettingsHelper } from '../helpers';
|
||||
import { FrontMatterDecorationProvider } from './FrontMatterDecorationProvider';
|
||||
|
||||
export class MarkdownFoldingProvider implements FoldingRangeProvider {
|
||||
private static start: number | null = null;
|
||||
private static end: number | null = null;
|
||||
private static endLine: number | null = null;
|
||||
private static decType: TextEditorDecorationType | null = null;
|
||||
|
||||
public async provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken): Promise<FoldingRange[]> {
|
||||
const ranges: FoldingRange[] = [];
|
||||
|
||||
const lines = document.getText().split('\n');
|
||||
let start: number | null = null;
|
||||
let end: number | null = null;
|
||||
|
||||
MarkdownFoldingProvider.start = null;
|
||||
MarkdownFoldingProvider.end = null;
|
||||
MarkdownFoldingProvider.endLine = null;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.startsWith('---')) {
|
||||
if (i === 0 && start === null) {
|
||||
start = i;
|
||||
MarkdownFoldingProvider.start = start;
|
||||
} else if (start !== null && end === null) {
|
||||
end = i;
|
||||
MarkdownFoldingProvider.end = end;
|
||||
MarkdownFoldingProvider.endLine = line.length;
|
||||
|
||||
MarkdownFoldingProvider.triggerHighlighting();
|
||||
|
||||
ranges.push(new FoldingRange(start, end, FoldingRangeKind.Region));
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
public static triggerHighlighting() {
|
||||
const config = SettingsHelper.getConfig();
|
||||
const fmHighlight = config.get<boolean>(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT);
|
||||
|
||||
if (MarkdownFoldingProvider.start !== null && MarkdownFoldingProvider.end !== null && MarkdownFoldingProvider.endLine !== null) {
|
||||
const range = new Range(new Position(MarkdownFoldingProvider.start, 0), new Position(MarkdownFoldingProvider.end, MarkdownFoldingProvider.endLine));
|
||||
|
||||
if (MarkdownFoldingProvider.decType !== null) {
|
||||
MarkdownFoldingProvider.decType.dispose();
|
||||
}
|
||||
|
||||
if (fmHighlight) {
|
||||
MarkdownFoldingProvider.decType = new FrontMatterDecorationProvider().get();
|
||||
window.activeTextEditor?.setDecorations(MarkdownFoldingProvider.decType, [range]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ export enum Command {
|
||||
metadata = "metadata",
|
||||
settings = "settings",
|
||||
focusOnTags = "focusOnTags",
|
||||
focusOnCategories = "focusOnCategories"
|
||||
focusOnCategories = "focusOnCategories",
|
||||
closeSections = "closeSections",
|
||||
}
|
||||
@@ -16,5 +16,10 @@ export enum CommandToCode {
|
||||
initProject = "init-project",
|
||||
createContent = "create-content",
|
||||
updateModifiedUpdating = "update-modified-updates",
|
||||
updateFmHighlight = "update-fm-highlight",
|
||||
createTemplate = "create-template",
|
||||
toggleCenterMode = "toggle-center-mode",
|
||||
toggleWritingSettings = "toggle-writing-settings",
|
||||
openPreview = "open-preview",
|
||||
updatePreviewUrl = "update-preview-url",
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
settings && metadata && <Actions metadata={metadata} settings={settings} />
|
||||
}
|
||||
|
||||
<Collapsible title="Metadata" className={`inherit z-20`}>
|
||||
<Collapsible id={`tags`} title="Metadata" className={`inherit z-20`}>
|
||||
{
|
||||
<TagPicker type={TagType.keywords}
|
||||
icon={<SymbolKeywordIcon />}
|
||||
@@ -78,7 +78,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
}
|
||||
</Collapsible>
|
||||
|
||||
<OtherActions isFile={true} />
|
||||
<OtherActions settings={settings} isFile={true} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
16
src/viewpanel/components/ActionButton.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IActionButtonProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick: (e: React.SyntheticEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
export const ActionButton: React.FunctionComponent<IActionButtonProps> = ({className, onClick, disabled,title}: React.PropsWithChildren<IActionButtonProps>) => {
|
||||
return (
|
||||
<div className={`article__action`}>
|
||||
<button onClick={onClick} className={className || ""} disabled={disabled}>{title}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { PanelSettings } from '../../models/PanelSettings';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import { CustomScript } from './CustomScript';
|
||||
import { DateAction } from './DateAction';
|
||||
import { Preview } from './Preview';
|
||||
import { PublishAction } from './PublishAction';
|
||||
import { SlugAction } from './SlugAction';
|
||||
|
||||
@@ -19,10 +20,13 @@ export const Actions: React.FunctionComponent<IActionsProps> = (props: React.Pro
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapsible title="Actions">
|
||||
<Collapsible id={`actions`} title="Actions">
|
||||
<div className={`article__actions`}>
|
||||
|
||||
{ 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} />}
|
||||
|
||||
@@ -23,9 +23,9 @@ export const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings}: Re
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<div className={`ext_actions`}>
|
||||
<GlobalSettings settings={settings} />
|
||||
<GlobalSettings settings={settings} isBase />
|
||||
|
||||
<Collapsible title="Actions">
|
||||
<Collapsible id={`base_actions`} title="Actions">
|
||||
<div className={`base__actions`}>
|
||||
<button onClick={initProject} disabled={settings?.isInitialized}>Initialize project</button>
|
||||
<button onClick={createContent} disabled={!settings?.isInitialized}>Create new content</button>
|
||||
@@ -34,7 +34,7 @@ export const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings}: Re
|
||||
|
||||
{
|
||||
settings?.contentInfo && (
|
||||
<Collapsible title="Content information">
|
||||
<Collapsible id={`base_content`} title="Content information">
|
||||
<div className="base__information">
|
||||
{
|
||||
settings.contentInfo.map(folder => (
|
||||
@@ -48,7 +48,7 @@ export const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings}: Re
|
||||
)
|
||||
}
|
||||
|
||||
<OtherActions isFile={false} />
|
||||
<OtherActions settings={settings} isFile={false} isBase />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Command } from '../Command';
|
||||
import { VsCollapsible } from './VscodeComponents';
|
||||
|
||||
export interface ICollapsibleProps {
|
||||
id: string;
|
||||
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);
|
||||
export const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({id, children, title, sendUpdate, className}: React.PropsWithChildren<ICollapsibleProps>) => {
|
||||
const [ isOpen, setIsOpen ] = React.useState(false);
|
||||
const collapseKey = `collapse-${id}`;
|
||||
|
||||
const updateStorage = (value: boolean) => {
|
||||
window.localStorage.setItem(collapseKey, value.toString());
|
||||
}
|
||||
|
||||
// This is a work around for a lit-element issue of duplicate slot names
|
||||
const triggerClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
@@ -17,11 +25,29 @@ export const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({childre
|
||||
if (sendUpdate) {
|
||||
sendUpdate(!prev);
|
||||
}
|
||||
|
||||
updateStorage(!prev);
|
||||
return !prev;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const collapsed = window.localStorage.getItem(collapseKey);
|
||||
if (collapsed === null || collapsed === 'true') {
|
||||
setIsOpen(true);
|
||||
updateStorage(true);
|
||||
}
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
if (message.command === Command.closeSections) {
|
||||
setIsOpen(false);
|
||||
updateStorage(false);
|
||||
}
|
||||
});
|
||||
}, ['']);
|
||||
|
||||
return (
|
||||
<VsCollapsible title={title} onClick={triggerClick} open={isOpen}>
|
||||
<div className={`section collapsible__body ${className || ""}`} slot="body">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
|
||||
export interface ICustomScriptProps {
|
||||
title: string;
|
||||
@@ -14,8 +15,6 @@ export const CustomScript: React.FunctionComponent<ICustomScriptProps> = ({title
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`article__action`}>
|
||||
<button onClick={runCustomScript}>{title}</button>
|
||||
</div>
|
||||
<ActionButton onClick={runCustomScript} title={title} />
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
|
||||
export interface IDateActionProps {}
|
||||
|
||||
@@ -16,12 +17,8 @@ export const DateAction: React.FunctionComponent<IDateActionProps> = (props: Rea
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`article__action`}>
|
||||
<button onClick={setDate}>Set publish date</button>
|
||||
</div>
|
||||
<div className={`article__action`}>
|
||||
<button onClick={setLastMod}>Set modified date</button>
|
||||
</div>
|
||||
<ActionButton onClick={setDate} title={`Set publish date`} />
|
||||
<ActionButton onClick={setLastMod} title={`Set modified date`} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -2,25 +2,66 @@ 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;
|
||||
isBase?: boolean;
|
||||
}
|
||||
|
||||
export const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings}: React.PropsWithChildren<IGlobalSettingsProps>) => {
|
||||
const { modifiedDateUpdate } = settings || {};
|
||||
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 onCheck = () => {
|
||||
const onDateCheck = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.updateModifiedUpdating, !modifiedDateUpdate);
|
||||
};
|
||||
|
||||
const onHighlightCheck = () => {
|
||||
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 title="Global settings">
|
||||
<div className={`base__actions`}>
|
||||
<VsCheckbox label="Auto-update modified date" checked={modifiedDateUpdate} onClick={onCheck} />
|
||||
<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__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>
|
||||
);
|
||||
};
|
||||
9
src/viewpanel/components/Icons/WritingIcon.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IWritingIconProps {}
|
||||
|
||||
export const WritingIcon: React.FunctionComponent<IWritingIconProps> = (props: React.PropsWithChildren<IWritingIconProps>) => {
|
||||
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.223 10.933c.326.192.699.29 1.077.282a2.159 2.159 0 0 0 1.754-.842 3.291 3.291 0 0 0 .654-2.113 2.886 2.886 0 0 0-.576-1.877 1.99 1.99 0 0 0-1.634-.733 2.294 2.294 0 0 0-1.523.567V3.475h-.991V11.1h.995v-.344c.076.066.158.125.244.177zM7.85 6.7c.186-.079.388-.113.59-.1a1.08 1.08 0 0 1 .896.428c.257.363.382.802.357 1.245a2.485 2.485 0 0 1-.4 1.484 1.133 1.133 0 0 1-.96.508 1.224 1.224 0 0 1-.976-.417A1.522 1.522 0 0 1 6.975 8.8v-.6a1.722 1.722 0 0 1 .393-1.145c.13-.154.296-.276.482-.355zM3.289 5.675a3.03 3.03 0 0 0-.937.162 2.59 2.59 0 0 0-.8.4l-.1.077v1.2l.423-.359a2.1 2.1 0 0 1 1.366-.572.758.758 0 0 1 .661.282c.15.232.23.503.231.779L2.9 7.825a2.6 2.6 0 0 0-1.378.575 1.65 1.65 0 0 0-.022 2.336 1.737 1.737 0 0 0 1.253.454 1.96 1.96 0 0 0 1.107-.332c.102-.068.197-.145.286-.229v.444h.941V7.715a2.193 2.193 0 0 0-.469-1.5 1.687 1.687 0 0 0-1.329-.54zm.857 3.041c.02.418-.12.829-.391 1.148a1.221 1.221 0 0 1-.955.422.832.832 0 0 1-.608-.2.833.833 0 0 1 0-1.091c.281-.174.6-.277.93-.3l1.02-.148.004.169zm8.313 2.317c.307.13.64.193.973.182.495.012.983-.114 1.41-.365l.123-.075.013-.007V9.615l-.446.32c-.316.224-.696.34-1.084.329A1.3 1.3 0 0 1 12.4 9.8a1.975 1.975 0 0 1-.4-1.312 2.01 2.01 0 0 1 .453-1.381A1.432 1.432 0 0 1 13.6 6.6a1.8 1.8 0 0 1 .971.279l.43.265V5.97l-.17-.073a2.9 2.9 0 0 0-1.17-.247 2.52 2.52 0 0 0-1.929.817 2.9 2.9 0 0 0-.747 2.049c-.028.707.21 1.4.67 1.939.222.249.497.446.804.578z"/></svg>
|
||||
);
|
||||
};
|
||||
15
src/viewpanel/components/OtherActionButton.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IOtherActionButtonProps {
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick: (e: React.SyntheticEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
export const OtherActionButton: React.FunctionComponent<IOtherActionButtonProps> = ({ className, disabled, onClick, children}: React.PropsWithChildren<IOtherActionButtonProps>) => {
|
||||
return (
|
||||
<div className={`ext_link_block`}>
|
||||
<button onClick={onClick} className={className || ""} disabled={disabled}>{children}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { PanelSettings } from '../../models';
|
||||
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';
|
||||
import { TemplateIcon } from './Icons/TemplateIcon';
|
||||
import { WritingIcon } from './Icons/WritingIcon';
|
||||
import { OtherActionButton } from './OtherActionButton';
|
||||
|
||||
export interface IOtherActionsProps {
|
||||
isFile: boolean;
|
||||
settings: PanelSettings | undefined;
|
||||
isBase?: boolean;
|
||||
}
|
||||
|
||||
export const OtherActions: React.FunctionComponent<IOtherActionsProps> = ({isFile}: React.PropsWithChildren<IOtherActionsProps>) => {
|
||||
export const OtherActions: React.FunctionComponent<IOtherActionsProps> = ({isFile, settings, isBase}: React.PropsWithChildren<IOtherActionsProps>) => {
|
||||
|
||||
const openSettings = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.openSettings);
|
||||
@@ -30,27 +36,29 @@ export const OtherActions: React.FunctionComponent<IOtherActionsProps> = ({isFil
|
||||
MessageHelper.sendMessage(CommandToCode.createTemplate);
|
||||
};
|
||||
|
||||
const toggleWritingSettings = () => {
|
||||
MessageHelper.sendMessage(CommandToCode.toggleWritingSettings);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Collapsible title="Other actions" className={`other_actions`}>
|
||||
<div className="ext_link_block">
|
||||
<button onClick={createAsTemplate} disabled={!isFile}><TemplateIcon /> Create as template</button>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<OtherActionButton onClick={openFile} disabled={!isFile}><FileIcon /> <span>Reveal file in folder</span></OtherActionButton>
|
||||
|
||||
<OtherActionButton onClick={openProject}><FolderOpenedIcon /> <span>Reveal project folder</span></OtherActionButton>
|
||||
|
||||
<div className="ext_link_block">
|
||||
<button onClick={openSettings}><SettingsIcon /> Open settings</button>
|
||||
</div>
|
||||
|
||||
<div className="ext_link_block">
|
||||
<button onClick={openFile} disabled={!isFile}><FileIcon /> Reveal file in folder</button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/issues" title="Open an issue on GitHub"><BugIcon /> <span>Report an issue</span></a>
|
||||
</div>
|
||||
</Collapsible>
|
||||
</>
|
||||
|
||||
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`} />
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
|
||||
export interface IPublishActionProps {
|
||||
draft: boolean;
|
||||
@@ -16,8 +17,6 @@ export const PublishAction: React.FunctionComponent<IPublishActionProps> = (prop
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`article__action`}>
|
||||
<button onClick={publish} className={`${draft ? "" : "secondary"}`}>{draft ? "Publish" : "Revert to draft"}</button>
|
||||
</div>
|
||||
<ActionButton onClick={publish} className={`${draft ? "" : "secondary"}`} title={draft ? "Publish" : "Revert to draft"} />
|
||||
);
|
||||
};
|
||||
@@ -88,7 +88,7 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
}, [title, data[descriptionField], data?.articleDetails?.wordCount]);
|
||||
|
||||
return (
|
||||
<Collapsible title="SEO Status" sendUpdate={(value) => setIsOpen(value)}>
|
||||
<Collapsible id={`seo`} title="SEO Status" sendUpdate={(value) => setIsOpen(value)}>
|
||||
{ renderContent() }
|
||||
</Collapsible>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SlugHelper } from '../../helpers/SlugHelper';
|
||||
import { Slug } from '../../models/PanelSettings';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { ActionButton } from './ActionButton';
|
||||
|
||||
export interface ISlugActionProps {
|
||||
value: string;
|
||||
@@ -21,8 +22,6 @@ export const SlugAction: React.FunctionComponent<ISlugActionProps> = (props: Rea
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`article__action`}>
|
||||
<button onClick={optimize} disabled={crntValue === slug}>Optimize slug</button>
|
||||
</div>
|
||||
<ActionButton onClick={optimize} disabled={crntValue === slug} title={`Optimize slug`} />
|
||||
);
|
||||
};
|
||||
@@ -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 { 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 {
|
||||
@@ -152,6 +153,21 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
case CommandToCode.updateModifiedUpdating:
|
||||
this.updateModifiedUpdating(msg.data || false);
|
||||
break;
|
||||
case CommandToCode.toggleWritingSettings:
|
||||
this.toggleWritingSettings();
|
||||
break;
|
||||
case CommandToCode.updateFmHighlight:
|
||||
this.updateFmHighlight((msg.data !== null && msg.data !== undefined) ? msg.data : false);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -201,12 +217,19 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger all sections to close
|
||||
*/
|
||||
public collapseAll() {
|
||||
this.postWebviewMessage({ command: Command.closeSections });
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a custom script
|
||||
* @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) {
|
||||
@@ -247,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,
|
||||
@@ -269,7 +292,10 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
scripts: config.get(SETTING_CUSTOM_SCRIPTS),
|
||||
isInitialized: await Template.isInitialized(),
|
||||
contentInfo: await Folders.getInfo() || null,
|
||||
modifiedDateUpdate: config.get(SETTING_AUTO_UPDATE_DATE) || false
|
||||
modifiedDateUpdate: config.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: config.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
} as PanelSettings
|
||||
});
|
||||
}
|
||||
@@ -313,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) {
|
||||
@@ -383,8 +409,63 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the writing settings
|
||||
*/
|
||||
private async toggleWritingSettings() {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
const enabled = this.isWritingSettingsEnabled();
|
||||
|
||||
await config.update("editor.fontSize", enabled ? undefined : 14, false, true);
|
||||
await config.update("editor.lineHeight", enabled ? undefined : 26, false, true);
|
||||
await config.update("editor.wordWrap", enabled ? undefined : "wordWrapColumn", false, true);
|
||||
await config.update("editor.wordWrapColumn", enabled ? undefined : 64, false, true);
|
||||
await config.update("editor.lineNumbers", enabled ? undefined : "off", false, true);
|
||||
await config.update("editor.quickSuggestions", enabled ? undefined : false, false, true);
|
||||
await config.update("editor.minimap.enabled", enabled ? undefined : false, false, true);
|
||||
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the writing settings are enabled
|
||||
*/
|
||||
private isWritingSettingsEnabled() {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
|
||||
const fontSize = config.get("editor.fontSize");
|
||||
const lineHeight = config.get("editor.lineHeight");
|
||||
const wordWrap = config.get("editor.wordWrap");
|
||||
const wordWrapColumn = config.get("editor.wordWrapColumn");
|
||||
const lineNumbers = config.get("editor.lineNumbers");
|
||||
const quickSuggestions = config.get<boolean>("editor.quickSuggestions");
|
||||
|
||||
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 = SettingsHelper.getConfig();
|
||||
await config.update(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, autoUpdate);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -397,7 +478,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
this.panel!.webview.postMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate a unique nonce
|
||||
*/
|
||||
private getNonce() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||