Compare commits
358 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71072d9520 | ||
|
|
b64dd8f88a | ||
|
|
173c89d86f | ||
|
|
f5f558d5bc | ||
|
|
c9c38ef10b | ||
|
|
c30f401c4f | ||
|
|
9b92050af8 | ||
|
|
31a41e2a66 | ||
|
|
baa56bc246 | ||
|
|
f53e81e0cb | ||
|
|
f454266846 | ||
|
|
0ba3c22795 | ||
|
|
ff38cf361c | ||
|
|
57e93b91c5 | ||
|
|
c1161b95ed | ||
|
|
32dc63b62a | ||
|
|
0c1198c802 | ||
|
|
ed4b78cfdc | ||
|
|
65f77baf2b | ||
|
|
eabdf00d3d | ||
|
|
c084a15e08 | ||
|
|
e577ba591e | ||
|
|
b17c7f888a | ||
|
|
0ed41b7d7e | ||
|
|
2e1faaa34f | ||
|
|
63f02f4f0e | ||
|
|
489fc5ec9e | ||
|
|
4c8ecdb344 | ||
|
|
8d705ff6c5 | ||
|
|
cfe68e65e8 | ||
|
|
0e179f5fd7 | ||
|
|
6cabd6283b | ||
|
|
6135e38fce | ||
|
|
935b2230af | ||
|
|
6dcd89e9cd | ||
|
|
2775b2051f | ||
|
|
5ebb2d7370 | ||
|
|
c9488e6661 | ||
|
|
442261e655 | ||
|
|
1aa2d41c95 | ||
|
|
e5a2194c23 | ||
|
|
cf6f051ee8 | ||
|
|
bebde4de68 | ||
|
|
174c4b7734 | ||
|
|
1f7519ee60 | ||
|
|
b6482546a5 | ||
|
|
0decd84f7f | ||
|
|
a1dbda0b23 | ||
|
|
427245f211 | ||
|
|
4678189eab | ||
|
|
15d89e34cf | ||
|
|
cbb0d8f72b | ||
|
|
131150f5a6 | ||
|
|
a31bca73e7 | ||
|
|
1d5f940c94 | ||
|
|
70ea6a5a16 | ||
|
|
849af69ce2 | ||
|
|
754570a9ec | ||
|
|
f7f6f26997 | ||
|
|
946d84a7a9 | ||
|
|
781ab6ac40 | ||
|
|
df86d02e8b | ||
|
|
19e468c908 | ||
|
|
5a81ea19b8 | ||
|
|
64a38e56b9 | ||
|
|
fca0528a7e | ||
|
|
936916acf8 | ||
|
|
61e9fc0308 | ||
|
|
2356623d7a | ||
|
|
ee70acebb6 | ||
|
|
09c48db957 | ||
|
|
d7658852b0 | ||
|
|
0a0efba37b | ||
|
|
a16c0c6355 | ||
|
|
8dcbe67152 | ||
|
|
2c20621071 | ||
|
|
48c4c0b8e4 | ||
|
|
2900777ffb | ||
|
|
0ccd428852 | ||
|
|
368ade6b44 | ||
|
|
5f6b6e3b4a | ||
|
|
43554a4303 | ||
|
|
2b0007c21a | ||
|
|
5ab0bdaa69 | ||
|
|
1c74df0266 | ||
|
|
469a1aaaf8 | ||
|
|
c5523f7aaf | ||
|
|
61b46bc5ac | ||
|
|
d7a0f71552 | ||
|
|
7d6d60039e | ||
|
|
42cc53cefc | ||
|
|
e68daa8ac2 | ||
|
|
333cc1f9df | ||
|
|
00bb8c6385 | ||
|
|
1deb969c20 | ||
|
|
928afceca7 | ||
|
|
179b31f67c | ||
|
|
7d4fe9ca0f | ||
|
|
3e33383eb1 | ||
|
|
66324fd292 | ||
|
|
8b1fbcabaa | ||
|
|
90519488c1 | ||
|
|
1012e10ddc | ||
|
|
2dd129d9bd | ||
|
|
6af5458082 | ||
|
|
9744cf0117 | ||
|
|
01921c799c | ||
|
|
b1674b4b84 | ||
|
|
9f7f803e25 | ||
|
|
b83c565e29 | ||
|
|
dee30923ff | ||
|
|
9a91be8025 | ||
|
|
46a9d6e602 | ||
|
|
511fd48081 | ||
|
|
0039fc1555 | ||
|
|
98044187cd | ||
|
|
a6dcc1ea79 | ||
|
|
32a686227e | ||
|
|
faa74132e5 | ||
|
|
3a847f7e42 | ||
|
|
66c978891e | ||
|
|
2f31230e07 | ||
|
|
c4225c0011 | ||
|
|
4edc7a0280 | ||
|
|
a60fe5204b | ||
|
|
bb980b4afe | ||
|
|
504658d87a | ||
|
|
feff69d969 | ||
|
|
a34d77242a | ||
|
|
1b24c1277d | ||
|
|
e6750205be | ||
|
|
1da8bf3f8b | ||
|
|
90c60b6a40 | ||
|
|
ee79f89c7f | ||
|
|
0668d48fd5 | ||
|
|
d046f73d16 | ||
|
|
f144d713d1 | ||
|
|
d31c403bdc | ||
|
|
35a0327387 | ||
|
|
9b39649bde | ||
|
|
3d857463f0 | ||
|
|
0428642a2f | ||
|
|
5182a9ae1a | ||
|
|
ab3686b3b5 | ||
|
|
c5b7b7845d | ||
|
|
2f13c335ed | ||
|
|
f219ac721f | ||
|
|
0149885289 | ||
|
|
cb80a10de2 | ||
|
|
092eb0fd2a | ||
|
|
24f79d9d3f | ||
|
|
d667b19716 | ||
|
|
4d38a0881a | ||
|
|
1478d0bd53 | ||
|
|
00a9e59bc2 | ||
|
|
2b2843234e | ||
|
|
71449aa5cc | ||
|
|
87f293a3f6 | ||
|
|
6da3ddb5dd | ||
|
|
87e80ccfe9 | ||
|
|
af9865d91b | ||
|
|
e576b6e8a4 | ||
|
|
c9f4c8f94e | ||
|
|
6ef63da973 | ||
|
|
2d85b0a554 | ||
|
|
41cbcc4c46 | ||
|
|
6c94880497 | ||
|
|
1d75c6b6b8 | ||
|
|
338bc022f9 | ||
|
|
2451730311 | ||
|
|
22cdaabe28 | ||
|
|
5a69240178 | ||
|
|
b81ef077f6 | ||
|
|
cb72b53653 | ||
|
|
050a513b48 | ||
|
|
481d1f56e4 | ||
|
|
229238703a | ||
|
|
074de212ce | ||
|
|
98e4490318 | ||
|
|
c79dc3c41f | ||
|
|
b45abe88ac | ||
|
|
2bac596e3d | ||
|
|
15a6ea8d8d | ||
|
|
72d6f8263a | ||
|
|
7549b3d989 | ||
|
|
ca628b37de | ||
|
|
db120b5c45 | ||
|
|
2973f5d27b | ||
|
|
075c7ad350 | ||
|
|
23625ed8fa | ||
|
|
9bd06909c8 | ||
|
|
1b45c2af13 | ||
|
|
b4bbf6c6fb | ||
|
|
a5b8d9668d | ||
|
|
490210da00 | ||
|
|
6878d440b1 | ||
|
|
81fa0a7d0f | ||
|
|
b7d4e547a1 | ||
|
|
2b2e256b8f | ||
|
|
7a7b31c2ae | ||
|
|
dea5eb1053 | ||
|
|
df3144ea73 | ||
|
|
a170cc3ad9 | ||
|
|
35b3813022 | ||
|
|
f0472fe89b | ||
|
|
7bdb2fa025 | ||
|
|
5ba7ad449a | ||
|
|
145441d55e | ||
|
|
62ff3419c1 | ||
|
|
34aee134d6 | ||
|
|
5899120d87 | ||
|
|
816a2fefe7 | ||
|
|
1f64e59917 | ||
|
|
5e54334fb9 | ||
|
|
d354af306f | ||
|
|
045cece0ce | ||
|
|
506012200f | ||
|
|
4ad2f0d495 | ||
|
|
b00e3cfd4b | ||
|
|
bb05489872 | ||
|
|
ede4d417bd | ||
|
|
928072fa27 | ||
|
|
6a313fcc8a | ||
|
|
725f7f4915 | ||
|
|
b9cb0ea16d | ||
|
|
61b80795a4 | ||
|
|
8daaa23774 | ||
|
|
8a0d308ceb | ||
|
|
e6a7a9aae7 | ||
|
|
f3943bd846 | ||
|
|
d2ae94df34 | ||
|
|
f799613b1a | ||
|
|
3f057a01d8 | ||
|
|
3ad5136735 | ||
|
|
e201ce6f83 | ||
|
|
383a3a7d4c | ||
|
|
8261f1de1b | ||
|
|
5b38e6fa56 | ||
|
|
717f34bc85 | ||
|
|
47fb2a90a9 | ||
|
|
85a7221895 | ||
|
|
9618a89528 | ||
|
|
14f0af2754 | ||
|
|
ebe248670d | ||
|
|
511960c4a9 | ||
|
|
31fd1f93ce | ||
|
|
6625b69170 | ||
|
|
9e8533fbb8 | ||
|
|
9c9cbb7dcb | ||
|
|
079a13e161 | ||
|
|
69c1e587d0 | ||
|
|
3996252531 | ||
|
|
4fddda65e6 | ||
|
|
5916344092 | ||
|
|
b96722dd69 | ||
|
|
263ccab311 | ||
|
|
3571af82c7 | ||
|
|
c60520c0ff | ||
|
|
b473431eae | ||
|
|
cbf434f741 | ||
|
|
04c401207f | ||
|
|
7291e6aac6 | ||
|
|
a7aab96f0e | ||
|
|
f500749644 | ||
|
|
47e59bc54c | ||
|
|
8902e25021 | ||
|
|
33093e1eb4 | ||
|
|
d36178c44f | ||
|
|
15b09ccc75 | ||
|
|
dffa6c87a0 | ||
|
|
c4a1caee09 | ||
|
|
1d9f07b86d | ||
|
|
a794a95bb8 | ||
|
|
40a56f6057 | ||
|
|
82353f7b64 | ||
|
|
82a22da90a | ||
|
|
380e40ea05 | ||
|
|
2bedb23341 | ||
|
|
1110b76364 | ||
|
|
d19e632f80 | ||
|
|
4e040b5f7a | ||
|
|
7a2a0934c2 | ||
|
|
d3eb7b223c | ||
|
|
f74eec954f | ||
|
|
864c4e7aa6 | ||
|
|
5667906caf | ||
|
|
2fe7c524e7 | ||
|
|
5cc83526ad | ||
|
|
76b5e99a08 | ||
|
|
7d5505d421 | ||
|
|
d97a11f8b5 | ||
|
|
0590cec684 | ||
|
|
b4cdc4feb9 | ||
|
|
986fd95524 | ||
|
|
f51fec5fb9 | ||
|
|
8198ce2af3 | ||
|
|
defffc4c8e | ||
|
|
8f47cbfb0b | ||
|
|
0be91c17d0 | ||
|
|
fae7ab8417 | ||
|
|
df239f2cc0 | ||
|
|
70099dc97f | ||
|
|
c11be0e3ec | ||
|
|
f8b7870180 | ||
|
|
c58a5c62d9 | ||
|
|
ce92444bf2 | ||
|
|
b2709ebffd | ||
|
|
2b20cf9d24 | ||
|
|
f4a499ad0f | ||
|
|
4494b158c0 | ||
|
|
3416e55264 | ||
|
|
9b53e31cd5 | ||
|
|
f49b93b042 | ||
|
|
a005930c14 | ||
|
|
9dea1ee6ed | ||
|
|
1ea0999d17 | ||
|
|
2e6a466ba5 | ||
|
|
fbcd430dc6 | ||
|
|
2bd910db47 | ||
|
|
8158c9a483 | ||
|
|
4622fbe757 | ||
|
|
97a635c2de | ||
|
|
e8c67c75fd | ||
|
|
6151ecb4c1 | ||
|
|
1d8c192c07 | ||
|
|
014911b7a9 | ||
|
|
7b2d7b8aa5 | ||
|
|
caceed2d4c | ||
|
|
476ec6c2fd | ||
|
|
5374edfa01 | ||
|
|
6a0cac9dfb | ||
|
|
b525a6a211 | ||
|
|
c295761560 | ||
|
|
6154164b4b | ||
|
|
05ce2d3537 | ||
|
|
544f24bcba | ||
|
|
1c354ed976 | ||
|
|
9b9bf1bfbe | ||
|
|
29d5f02d10 | ||
|
|
9bc2fbc141 | ||
|
|
cac009b773 | ||
|
|
bf1639cac7 | ||
|
|
5f28e145c4 | ||
|
|
8199ab964e | ||
|
|
14c050e34b | ||
|
|
b96411d1f9 | ||
|
|
87c469a6c7 | ||
|
|
c29aef03f0 | ||
|
|
9eaf94de7a | ||
|
|
f5e7526fae | ||
|
|
579e4925c8 | ||
|
|
4a4c558d9d | ||
|
|
9bb34850e2 | ||
|
|
c6e37532bc | ||
|
|
221f962beb | ||
|
|
17566279e2 | ||
|
|
4e7488414d | ||
|
|
c206817431 |
1
.frontmatter/content/mediaDb.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
.github/FUNDING.yml
vendored
@@ -1,4 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [estruyf]
|
||||
open_collective: frontmatter
|
||||
custom: ["https://www.buymeacoffee.com/zMeFRy9"]
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/feedback_request.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Feedback
|
||||
about: Tell more on what you think
|
||||
title: 'Feedback: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
7
.github/workflows/release-beta.yml
vendored
@@ -11,9 +11,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install the dependencies
|
||||
@@ -25,3 +25,6 @@ jobs:
|
||||
- name: Publish
|
||||
run: npx vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
|
||||
|
||||
- name: Publish to open-vsx.org
|
||||
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}
|
||||
|
||||
|
||||
6
.github/workflows/release.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install the dependencies
|
||||
@@ -26,3 +26,5 @@ jobs:
|
||||
- name: Publish
|
||||
run: npx vsce publish -p ${{ secrets.VSCE_PAT }}
|
||||
|
||||
- name: Publish to open-vsx.org
|
||||
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}
|
||||
@@ -17,4 +17,9 @@ postcss.config.js
|
||||
.templates
|
||||
.github
|
||||
scripts
|
||||
.all-contributorsrc
|
||||
.all-contributorsrc
|
||||
assets/v2.*
|
||||
assets/v3.*
|
||||
assets/v4.*
|
||||
assets/sponsors
|
||||
dist/*.html
|
||||
236
CHANGELOG.md
@@ -1,5 +1,241 @@
|
||||
# Change Log
|
||||
|
||||
## [6.1.1] - 2022-03-02
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#275](https://github.com/estruyf/vscode-front-matter/issues/275): Fix for rendering the panel when content contains an invalid markdown syntax tree
|
||||
|
||||
## [6.1.0] - 2022-02-28 - [Release notes](https://beta.frontmatter.codes/updates/v6.1.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#176](https://github.com/estruyf/vscode-front-matter/issues/176): New `block` field type that allows you to you to define a group of fields which can be used to create a list of data
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- Updated the activity bar icon for better visibility
|
||||
- Storing the panel collapse section states
|
||||
- [#241](https://github.com/estruyf/vscode-front-matter/issues/241): Added taxonomy limit field property which allows you to limit the number of selections
|
||||
- [#242](https://github.com/estruyf/vscode-front-matter/issues/242): Keep comments at the root of the front matter
|
||||
- [#248](https://github.com/estruyf/vscode-front-matter/issues/248): Added support for front matter highlighting to all file types specified in `frontMatter.content.supportedFileTypes`
|
||||
- [#255](https://github.com/estruyf/vscode-front-matter/issues/255): Added support for default values on block fields / data creation
|
||||
- [#257](https://github.com/estruyf/vscode-front-matter/issues/257): Allow preview images to be used in multi-dimensional fields
|
||||
- [#271](https://github.com/estruyf/vscode-front-matter/issues/271): Added image size placeholders for media snippets
|
||||
|
||||
### ⚡️ Optimizations
|
||||
|
||||
- Show the data item its details when clicking on the record
|
||||
- Refactoring of the explorer view panel listeners
|
||||
- Added `{{now}}` placeholder to the publishing date for content creation
|
||||
- [#243](https://github.com/estruyf/vscode-front-matter/issues/243): Refactoring front matter parsing
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#247](https://github.com/estruyf/vscode-front-matter/issues/247): Fix the front matter highlighting in markdown documents
|
||||
- [#261](https://github.com/estruyf/vscode-front-matter/issues/261): Fix to allow that tag and category fields can be renamed
|
||||
- [#264](https://github.com/estruyf/vscode-front-matter/issues/264): Fix for Windows paths on content folder registration
|
||||
- [#268](https://github.com/estruyf/vscode-front-matter/issues/268): Fix for panel which only shows loading indicator
|
||||
|
||||
## [6.0.0] - 2022-01-25 - [Release Notes](https://beta.frontmatter.codes/updates/v6.0.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#193](https://github.com/estruyf/vscode-front-matter/issues/193): Support added for editing data files.
|
||||
- [#197](https://github.com/estruyf/vscode-front-matter/issues/197): Support for multi-dimensional content type fields on content creation and editing.
|
||||
- [#225](https://github.com/estruyf/vscode-front-matter/issues/225): Placeholder support for front matter field values (template and content type).
|
||||
- [#226](https://github.com/estruyf/vscode-front-matter/issues/226): Ability to specify the local server start command and trigger it from the UI.
|
||||
- [#227](https://github.com/estruyf/vscode-front-matter/issues/227): Specify the file types to support with the new `frontMatter.content.supportedFileTypes` setting.
|
||||
- [#228](https://github.com/estruyf/vscode-front-matter/issues/228): Show bulk button actions in panel and dashboard view.
|
||||
- [#231](https://github.com/estruyf/vscode-front-matter/issues/231): Once you authenticate via GitHub as a supporter, the support links will be hidden from the UI.
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- Added default field value for content type fields
|
||||
- HMR support for panel webview development
|
||||
- Added reveal media file action
|
||||
- [#187](https://github.com/estruyf/vscode-front-matter/issues/187): Svelte support with the [#227](https://github.com/estruyf/vscode-front-matter/issues/227) features has been added.
|
||||
- [#198](https://github.com/estruyf/vscode-front-matter/issues/198): Additional media sort options (alt, caption, and size).
|
||||
- [#230](https://github.com/estruyf/vscode-front-matter/issues/230): JSON front matter support added.
|
||||
- [#233](https://github.com/estruyf/vscode-front-matter/issues/233): Partial update when a page is updated.
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#234](https://github.com/estruyf/vscode-front-matter/issues/234): Fix for multi-word keywords
|
||||
- [#235](https://github.com/estruyf/vscode-front-matter/issues/235): Fix for reselecting the previously removed value from a choice field
|
||||
|
||||
## [5.10.0] - 2022-01-10
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#218](https://github.com/estruyf/vscode-front-matter/issues/218): Add support for creating `mdx` files from templates and content types. This introduced a new setting: `frontMatter.content.defaultFileType`.
|
||||
- [#220](https://github.com/estruyf/vscode-front-matter/issues/220): Add support DateTime updates in `mdx` files when the `mdx extension` is not installed.
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#221](https://github.com/estruyf/vscode-front-matter/issues/221): Automatic DateTime switch from on text change to on save to prevent multiple updates.
|
||||
|
||||
## [5.9.0] - 2022-01-01 - 🎇🎆
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- Fixing the spinner which overlaps the global navigation bar
|
||||
- Quick actions added for media files (edit, delete, insert markdown, insert snippet)
|
||||
- [#199](https://github.com/estruyf/vscode-front-matter/issues/199): Search media files in the currently selected folder
|
||||
- [#211](https://github.com/estruyf/vscode-front-matter/issues/211): Replace text selection on media inserts
|
||||
- [#212](https://github.com/estruyf/vscode-front-matter/issues/212): Create folder watchers for content folders. When new content gets created, the dashboard updates.
|
||||
- [#213](https://github.com/estruyf/vscode-front-matter/issues/213): New media folder overview design
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#210](https://github.com/estruyf/vscode-front-matter/issues/210): Fix for adding media files with uppercase file extensions
|
||||
- [#214](https://github.com/estruyf/vscode-front-matter/issues/214): Fix for opening markdown file after creating it for the specified content type
|
||||
|
||||
## [5.8.0] - 2021-12-21 - 🎄
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- Refactoring of the WebView logic to new message handlers
|
||||
- Optimized the `getMedia` call from the webview
|
||||
- Keep the dashboard its context when switching tabs
|
||||
- [#205](https://github.com/estruyf/vscode-front-matter/issues/205): Define a logging level setting
|
||||
- [#206](https://github.com/estruyf/vscode-front-matter/issues/206): Add front matter issues to the diagnostic tab
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#207](https://github.com/estruyf/vscode-front-matter/issues/207): Fix the quick picks for content creation
|
||||
- [#208](https://github.com/estruyf/vscode-front-matter/issues/208): Fix for the collapse sections action so that it is not available everywhere, but only on the Front Matter panel
|
||||
|
||||
## [5.7.0] - 2021-12-07 - [Release Notes](https://beta.frontmatter.codes/updates/v5.7.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#188](https://github.com/estruyf/vscode-front-matter/issues/188): Support for `.markdown` files added to the dashboard
|
||||
- [#190](https://github.com/estruyf/vscode-front-matter/issues/190): Diagnostic output for the extension
|
||||
- [#194](https://github.com/estruyf/vscode-front-matter/issues/194): WYSIWYG controls added for markdown files + configuration to enable/disable the functionality
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#191](https://github.com/estruyf/vscode-front-matter/issues/191): Fix beta settings page
|
||||
- [#200](https://github.com/estruyf/vscode-front-matter/issues/200): Fix last modified date sorting for media files
|
||||
- [#201](https://github.com/estruyf/vscode-front-matter/issues/201): Fix overflow issue with the media filename
|
||||
- [#202](https://github.com/estruyf/vscode-front-matter/issues/202): Fix checkbox label color for light themes
|
||||
|
||||
## [5.6.0] - 2021-11-23
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- Updated camera icon from VS Code to media icon
|
||||
- Updated the media card actions to show it within a menu. This will give a better experience with custom scripts.
|
||||
- [#97](https://github.com/estruyf/vscode-front-matter/issues/97): Custom Script support for media files and folders
|
||||
- [#178](https://github.com/estruyf/vscode-front-matter/issues/178): Sorting added to the media dashboard
|
||||
- [#179](https://github.com/estruyf/vscode-front-matter/issues/179): Updated the `open dashboard` icon to make it easier to spot it
|
||||
- [#180](https://github.com/estruyf/vscode-front-matter/issues/180): Added `{filename}` as placeholder for media snippets
|
||||
- [#181](https://github.com/estruyf/vscode-front-matter/issues/181): Support for custom taxonomy fields added
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#183](https://github.com/estruyf/vscode-front-matter/issues/183): Fix type error on the `frontMatter.content.sorting` setting
|
||||
|
||||
## [5.5.0] - 2021-11-15
|
||||
|
||||
As from this version onwards, the extension will be published to [open-vsx.org](https://open-vsx.org/).
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#173](https://github.com/estruyf/vscode-front-matter/issues/173): Allow to specify your own sorting for the content dashboard
|
||||
- [#174](https://github.com/estruyf/vscode-front-matter/issues/174): Added option to exclude sub-directories from page/markdown content retrieval
|
||||
|
||||
## [5.4.0] - 2021-11-05
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#166](https://github.com/estruyf/vscode-front-matter/issues/166): Added preview button to the panel base view
|
||||
- [#167](https://github.com/estruyf/vscode-front-matter/issues/167): Allow to set the preview path per content type
|
||||
|
||||
## [5.3.1] - 2021-10-29
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#163](https://github.com/estruyf/vscode-front-matter/issues/163): Setting workspace state instead of global state for the media view
|
||||
|
||||
## [5.3.0] - 2021-10-28 - [Release Notes](https://beta.frontmatter.codes/updates/v5.3.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#158](https://github.com/estruyf/vscode-front-matter/issues/158): Add support for non-boolean draft/publish status fields
|
||||
- [#159](https://github.com/estruyf/vscode-front-matter/issues/159): Enhancements to SEO checks: Slug check, keyword details, more article information
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- Value check when generating slug from title
|
||||
- Fix for date time formatting with `DD` and `YYYY` tokens
|
||||
- Fix in tag space replacing when object is passed
|
||||
|
||||
## [5.2.0] - 2021-10-19
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#151](https://github.com/estruyf/vscode-front-matter/issues/151): Detect which site-generator or framework is used
|
||||
- [#152](https://github.com/estruyf/vscode-front-matter/issues/152): Automatically set setting based on the used site-generator or framework
|
||||
- [#154](https://github.com/estruyf/vscode-front-matter/issues/154): Bulk script support added
|
||||
- [#155](https://github.com/estruyf/vscode-front-matter/issues/155): Fallback image added for the images shown in the editor panel
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#153](https://github.com/estruyf/vscode-front-matter/issues/153): Support old date formatting for date-fns
|
||||
- [#156](https://github.com/estruyf/vscode-front-matter/issues/156): Fix for uploading media files into a new folder
|
||||
|
||||
## [5.1.1] - 2021-10-14
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#149](https://github.com/estruyf/vscode-front-matter/issues/149): Fix panel rendering when incorrect type for keywords is provided
|
||||
|
||||
## [5.1.0] - 2021-10-13
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#141](https://github.com/estruyf/vscode-front-matter/issues/141): Allow content creation for page bundles or single files
|
||||
- [#145](https://github.com/estruyf/vscode-front-matter/issues/145): Moved folder registration settings to `frontmatter.json` file
|
||||
- [#147](https://github.com/estruyf/vscode-front-matter/issues/147): Error boundary added for metadata fields
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- Rendered more hooks than during the previous render in `FileList`
|
||||
- [#142](https://github.com/estruyf/vscode-front-matter/issues/142): Fix for unknown tags where it throws an error
|
||||
- [#143](https://github.com/estruyf/vscode-front-matter/issues/143): Fix for duplicate values in the file list
|
||||
- [#144](https://github.com/estruyf/vscode-front-matter/issues/144): Fix for `toISOString` does not exist on object
|
||||
- [#146](https://github.com/estruyf/vscode-front-matter/issues/146): Date parsing logic added with fallbacks
|
||||
|
||||
## [5.0.0] - 2021-10-07 - [Release Notes](https://beta.frontmatter.codes/updates/v5.0.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#113](https://github.com/estruyf/vscode-front-matter/issues/113): Integrating a local DB for media metadata (caption, alt)
|
||||
- [#132](https://github.com/estruyf/vscode-front-matter/issues/132): Major changes to the media dashboard which allows you to navigate through all folders
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#110](https://github.com/estruyf/vscode-front-matter/issues/110): Add support for workspaces with multiple folders
|
||||
- [#117](https://github.com/estruyf/vscode-front-matter/issues/117): Allow to specify a singleline of text in the metadata fields
|
||||
- [#119](https://github.com/estruyf/vscode-front-matter/issues/119): Multi-select support for choice fields
|
||||
- [#121](https://github.com/estruyf/vscode-front-matter/issues/121): Choice fields support ID/title objects as well as a regular string
|
||||
- [#122](https://github.com/estruyf/vscode-front-matter/issues/122): Update the filenames of your media
|
||||
- [#124](https://github.com/estruyf/vscode-front-matter/issues/124): Add new `isPreviewImage` property to the content type field to specify custom preview images
|
||||
- [#126](https://github.com/estruyf/vscode-front-matter/issues/126): Create new content from the available content types
|
||||
- [#127](https://github.com/estruyf/vscode-front-matter/issues/127): Title bar action added to open the dashboard
|
||||
- [#128](https://github.com/estruyf/vscode-front-matter/issues/128): Support for multi-select on image fields added
|
||||
- [#131](https://github.com/estruyf/vscode-front-matter/issues/131): Folder creation support added on media dashboard
|
||||
- [#134](https://github.com/estruyf/vscode-front-matter/issues/134): On startup, the extension checks if local settings can be promoted
|
||||
- [#135](https://github.com/estruyf/vscode-front-matter/issues/135): `Hidden` property added for field configuration
|
||||
- [#137](https://github.com/estruyf/vscode-front-matter/issues/137): Ask to move the `.templates` folder to the new `.frontmatter` folder
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#120](https://github.com/estruyf/vscode-front-matter/issues/120): Choice and number field not updating when set manually in front matter of the file
|
||||
- [#133](https://github.com/estruyf/vscode-front-matter/issues/133): Fix for overriding default content type settings
|
||||
|
||||
## [4.0.1] - 2021-09-24
|
||||
|
||||
- [#114](https://github.com/estruyf/vscode-front-matter/issues/114): Fix for categories/tags provided as string in YAML
|
||||
|
||||
128
README.beta.md
@@ -28,26 +28,50 @@
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
## What is Front Matter?
|
||||
## ❓ What is Front Matter?
|
||||
|
||||
Front Matter BETA is an essential Visual Studio Code extension that simplifies working and managing your markdown articles. We created the extension to support many static-site generators like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more.
|
||||
Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice. Jump right into editing and creating content with Front Matter and be able to preview it straight in VS Code.
|
||||
|
||||
The extension brings Content Management System (CMS) capabilities straight within Visual Studio Code. For example, you can keep a list of the used tags, categories, create content, and so much more.
|
||||
The extension supports various static-site generators and frameworks like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more.
|
||||
|
||||
Our main extension features are:
|
||||
A couple of our extension highlights that hopefully get you interested in giving Front Matter a try:
|
||||
|
||||
- Page dashboard where you can get an overview of all your markdown pages. You can use it to search, filter, sort your contents.
|
||||
- Site preview within Visual Studio Code
|
||||
- Content, data, and media management
|
||||
- Search, filter, sort, etc. all your content
|
||||
- Create new content
|
||||
- Supporting tools to edit content and media
|
||||
- Preview your site/content straight in Visual Studio Code
|
||||
- SEO checks for title, description, and keywords
|
||||
- Support for custom actions/scripts
|
||||
- and many more
|
||||
- Extensibility
|
||||
- As we know, we cannot support all use cases. We provide a way to extend the functionality of the extension to your needs
|
||||
- and many more features ...
|
||||
|
||||
> Missing something? Let us know by opening an issue on the [GitHub repository](https://github.com/estruyf/vscode-front-matter/issues/new/choose)
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v4.0.0/preview.png" alt="Site preview" style="display: inline-block" />
|
||||
<img src="./assets/v6.0.0/content-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 6**
|
||||
|
||||
In this version, we introduced the new data files/folders dashboard. You can find more information about the release in our [v6.0.0 release notes](https://frontmatter.codes/updates/v6.0.0).
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v6.0.0/data-dashboard.png" alt="Data dashboard" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> Data files/folders are pieces of content that do not belong to any markdown content, but live on their own. Most of the time, these data files are used to store additional information about your project/blog/website that will be used to render the content.
|
||||
|
||||
**Version 5**
|
||||
|
||||
The new media dashboard redesign got introduced + support for setting metadata on media files [v5.0.0 release notes](https://frontmatter.codes/updates/v5.0.0).
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v5.9.0/media-dashboard.png" alt="Data dashboard" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
**Version 4**
|
||||
|
||||
Support for Team level settings, content-types, and image support. Get to know more at: [v4.0.0 release notes](https://frontmatter.codes/updates/v4_0_0).
|
||||
@@ -66,7 +90,7 @@ In version v2 we released the re-designed sidebar panel with improved SEO suppor
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Installation
|
||||
## ⚙️ Installation
|
||||
|
||||
You can get the extension via:
|
||||
|
||||
@@ -76,41 +100,91 @@ You can get the extension via:
|
||||
|
||||
> **Info**: The docs can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
|
||||
### Beta version
|
||||
### 🧪 Beta version
|
||||
|
||||
If you have the courage to test out the beta features, we made available a beta version as well. You can install this via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
- Uninstall the main Front Matter version
|
||||
- Install the beta version
|
||||
- VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The BETA docs can be found on [beta.frontmatter.codes](https://beta.frontmatter.codes).
|
||||
|
||||
## Documentation
|
||||
## 📖 Documentation
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://beta.frontmatter.codes" title="Documentation @ beta.frontmatter.codes">
|
||||
Check out the extension documentation at beta.frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
All documentation can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
|
||||
Documentation repository: [GitHub - Front Matter DOCs](https://github.com/FrontMatter/web-documentation-nextjs)
|
||||
|
||||
## 💪 Contributing
|
||||
|
||||
Pull requests are welcome. Please open an issue first to discuss what you would like to change, or which problem you would like to fix. This makes it easier for us to follow-up and plan for future releases.
|
||||
|
||||
You can always help us improve the extension in varous ways like:
|
||||
|
||||
- Testing out the extension and providing feedback
|
||||
- Reporting issues and bugs
|
||||
- Suggesting new features
|
||||
- Fixing an issue
|
||||
- Updating documentation
|
||||
- UI improvements
|
||||
- Tutorials
|
||||
- etc.
|
||||
|
||||
Eager to start contributing? Great 🤩, you can contribute to the following projects:
|
||||
|
||||
- [Extension](https://github.com/estruyf/vscode-front-matter)
|
||||
- [Documentation](https://github.com/FrontMatter/web-documentation-nextjs)
|
||||
- [Sample Projects](https://github.com/FrontMatter/project-samples)
|
||||
|
||||
## 👀 Show the work you are using Front Matter
|
||||
|
||||
Are you using Front Matter and are you interested in showing for which websites you use it? You can show your work by opening a [showcase issue](https://github.com/estruyf/vscode-front-matter/issues/new?assignees=&labels=&template=showcase.md&title=Showcase%3A+).
|
||||
|
||||
You can open showcase issues for the following things:
|
||||
|
||||
- Show the website for which you use Front Matter;
|
||||
- Share an article/video/webcast/... that explains how you use Front Matter;
|
||||
- Got something else to share? Open an issue and we can see where it fits on our website.
|
||||
|
||||
## 👉 Contributors 🤘
|
||||
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
<p align="center">
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🖤 Backers & Sponsors 👇 🤘
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/apowell656" title="Andre Powell">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1969515" />
|
||||
</a>
|
||||
<a href="https://github.com/timschps" title="Tim Schaeps">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/13098307" />
|
||||
</a>
|
||||
<a href="https://github.com/zivbk1" title="Bryan Klein">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/6154767" />
|
||||
</a>
|
||||
<a href="https://github.com/flikteoh" title="FlikTeoh">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1472065" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## 🖤 Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🔑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
129
README.md
@@ -26,26 +26,50 @@
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
## What is Front Matter?
|
||||
## ❓ What is Front Matter?
|
||||
|
||||
Front Matter is an essential Visual Studio Code extension that simplifies working and managing your markdown articles. We created the extension to support many static-site generators like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more.
|
||||
Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice. Jump right into editing and creating content with Front Matter and be able to preview it straight in VS Code.
|
||||
|
||||
The extension brings Content Management System (CMS) capabilities straight within Visual Studio Code. For example, you can keep a list of the used tags, categories, create content, and so much more.
|
||||
The extension supports various static-site generators and frameworks like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more.
|
||||
|
||||
Our main extension features are:
|
||||
A couple of our extension highlights that hopefully get you interested in giving Front Matter a try:
|
||||
|
||||
- Page dashboard where you can get an overview of all your markdown pages. You can use it to search, filter, sort your contents.
|
||||
- Site preview within Visual Studio Code
|
||||
- Content, data, and media management
|
||||
- Search, filter, sort, etc. all your content
|
||||
- Create new content
|
||||
- Supporting tools to edit content and media
|
||||
- Preview your site/content straight in Visual Studio Code
|
||||
- SEO checks for title, description, and keywords
|
||||
- Support for custom actions/scripts
|
||||
- and many more
|
||||
- Extensibility
|
||||
- As we know, we cannot support all use cases. We provide a way to extend the functionality of the extension to your needs
|
||||
- and many more features ...
|
||||
|
||||
> Missing something? Let us know by opening an issue on the [GitHub repository](https://github.com/estruyf/vscode-front-matter/issues/new/choose)
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v4.0.0/preview.png" alt="Site preview" style="display: inline-block" />
|
||||
<img src="./assets/v6.0.0/content-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 6**
|
||||
|
||||
In this version, we introduced the new data files/folders dashboard. You can find more information about the release in our [v6.0.0 release notes](https://frontmatter.codes/updates/v6.0.0).
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v6.0.0/data-dashboard.png" alt="Data dashboard" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> Data files/folders are pieces of content that do not belong to any markdown content, but live on their own. Most of the time, these data files are used to store additional information about your project/blog/website that will be used to render the content.
|
||||
|
||||
**Version 5**
|
||||
|
||||
The new media dashboard redesign got introduced + support for setting metadata on media files [v5.0.0 release notes](https://frontmatter.codes/updates/v5.0.0).
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v5.9.0/media-dashboard.png" alt="Data dashboard" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
**Version 4**
|
||||
|
||||
Support for Team level settings, content-types, and image support. Get to know more at: [v4.0.0 release notes](https://frontmatter.codes/updates/v4_0_0).
|
||||
@@ -64,7 +88,7 @@ In version v2 we released the re-designed sidebar panel with improved SEO suppor
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Installation
|
||||
## ⚙️ Installation
|
||||
|
||||
You can get the extension via:
|
||||
|
||||
@@ -74,41 +98,92 @@ You can get the extension via:
|
||||
|
||||
> **Info**: The docs can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
|
||||
### Beta version
|
||||
### 🧪 Beta version
|
||||
|
||||
If you have the courage to test out the beta features, we made available a beta version as well. You can install this via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
- Uninstall the main Front Matter version
|
||||
- Install the beta version
|
||||
- VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The BETA docs can be found on [beta.frontmatter.codes](https://beta.frontmatter.codes).
|
||||
|
||||
## Documentation
|
||||
## 📖 Documentation
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://frontmatter.codes" title="Documentation @ frontmatter.codes">
|
||||
Check out the extension documentation at frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
All documentation can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
|
||||
Documentation repository: [GitHub - Front Matter DOCs](https://github.com/FrontMatter/web-documentation-nextjs)
|
||||
|
||||
## 💪 Contributing
|
||||
|
||||
Pull requests are welcome. Please open an issue first to discuss what you would like to change, or which problem you would like to fix. This makes it easier for us to follow-up and plan for future releases.
|
||||
|
||||
You can always help us improve the extension in varous ways like:
|
||||
|
||||
- Testing out the extension and providing feedback
|
||||
- Reporting issues and bugs
|
||||
- Suggesting new features
|
||||
- Fixing an issue
|
||||
- Updating documentation
|
||||
- UI improvements
|
||||
- Tutorials
|
||||
- etc.
|
||||
|
||||
Eager to start contributing? Great 🤩, you can contribute to the following projects:
|
||||
|
||||
- [Extension](https://github.com/estruyf/vscode-front-matter)
|
||||
- [Documentation](https://github.com/FrontMatter/web-documentation-nextjs)
|
||||
- [Sample Projects](https://github.com/FrontMatter/project-samples)
|
||||
|
||||
## 👀 Show the work you are using Front Matter
|
||||
|
||||
Are you using Front Matter and are you interested in showing for which websites you use it? You can show your work by opening a [showcase issue](https://github.com/estruyf/vscode-front-matter/issues/new?assignees=&labels=&template=showcase.md&title=Showcase%3A+).
|
||||
|
||||
You can open showcase issues for the following things:
|
||||
|
||||
- Show the website for which you use Front Matter;
|
||||
- Share an article/video/webcast/... that explains how you use Front Matter;
|
||||
- Got something else to share? Open an issue and we can see where it fits on our website.
|
||||
|
||||
## 👉 Contributors 🤘
|
||||
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
<p align="center">
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🖤 Backers & Sponsors 👇 🤘
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/apowell656" title="Andre Powell">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1969515" />
|
||||
</a>
|
||||
<a href="https://github.com/timschps" title="Tim Schaeps">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/13098307" />
|
||||
</a>
|
||||
<a href="https://github.com/zivbk1" title="Bryan Klein">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/6154767" />
|
||||
</a>
|
||||
<a href="https://github.com/flikteoh" title="FlikTeoh">
|
||||
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1472065" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## 🖤 Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🔑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,40 +1 @@
|
||||
<?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>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#ffffff;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#ffffff"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#01aeb7"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#01aeb7" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#01aeb7"/></svg>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#AD0670;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#AD0670"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -1,44 +1 @@
|
||||
<?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>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#AD0670;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#AD0670"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
assets/frontmatter-short-min.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
16
assets/frontmatter-short-min.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#02AEB7;stroke-width:50;stroke-miterlimit:10;}
|
||||
.st1{fill:#02AEB7;}
|
||||
</style>
|
||||
<rect x="25" y="25" class="st0" width="1200" height="1200"/>
|
||||
<path class="st1" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path class="st1" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 5.7 KiB |
1
assets/frontmatter-teal-min.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#02aeb7;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#02aeb7"/></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
@@ -1,39 +1 @@
|
||||
<?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="enable-background:new 0 0 28 28;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
|
||||
.st2{fill:none;}
|
||||
</style>
|
||||
<g class="st0">
|
||||
<path d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z"/>
|
||||
<path 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 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 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 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 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 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 d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z"/>
|
||||
<path d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z"/>
|
||||
<path d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z"/>
|
||||
<path 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>
|
||||
<rect x="-33.5" y="14" class="st2" width="8.6" height="14"/>
|
||||
</svg>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#000000;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#000000"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#000000"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#000000"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#000000"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#000000"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#000000"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#000000"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#000000"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#000000"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#000000"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#000000"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
9
assets/icons/blockquote-dark.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M6 15h15" />
|
||||
<path d="M21 19h-15" />
|
||||
<path d="M15 11h6" />
|
||||
<path d="M21 7h-6" />
|
||||
<path d="M9 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
|
||||
<path d="M3 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 449 B |
9
assets/icons/blockquote-light.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M6 15h15" />
|
||||
<path d="M21 19h-15" />
|
||||
<path d="M15 11h6" />
|
||||
<path d="M21 7h-6" />
|
||||
<path d="M9 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
|
||||
<path d="M3 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 449 B |
5
assets/icons/bold-dark.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z" />
|
||||
<path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 329 B |
5
assets/icons/bold-light.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z" />
|
||||
<path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 329 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#F3EFF5">
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#C5C5C5">
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 277 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentcolor">
|
||||
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#424242">
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 277 B |
6
assets/icons/code-dark.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<polyline points="7 8 3 12 7 16" />
|
||||
<polyline points="17 8 21 12 17 16" />
|
||||
<line x1="14" y1="4" x2="10" y2="20" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 358 B |
6
assets/icons/code-light.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<polyline points="7 8 3 12 7 16" />
|
||||
<polyline points="17 8 21 12 17 16" />
|
||||
<line x1="14" y1="4" x2="10" y2="20" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 358 B |
7
assets/icons/codeblock-dark.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M9 12h6" />
|
||||
<path d="M12 9v6" />
|
||||
<path d="M6 19a2 2 0 0 1 -2 -2v-4l-1 -1l1 -1v-4a2 2 0 0 1 2 -2" />
|
||||
<path d="M18 19a2 2 0 0 0 2 -2v-4l1 -1l-1 -1v-4a2 2 0 0 0 -2 -2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 422 B |
7
assets/icons/codeblock-light.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M9 12h6" />
|
||||
<path d="M12 9v6" />
|
||||
<path d="M6 19a2 2 0 0 1 -2 -2v-4l-1 -1l1 -1v-4a2 2 0 0 1 2 -2" />
|
||||
<path d="M18 19a2 2 0 0 0 2 -2v-4l1 -1l-1 -1v-4a2 2 0 0 0 -2 -2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 422 B |
1
assets/icons/frontmatter-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#C5C5C5"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#C5C5C5" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#C5C5C5"/></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
1
assets/icons/frontmatter-light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#424242"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#424242"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#424242"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#424242"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#424242"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#424242" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#424242"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#424242"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#424242"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#424242"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#424242"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#424242"/></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
12
assets/icons/frontmatter-short-dark.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
|
||||
<rect x="25" y="25" fill="none" stroke="#ffffff" stroke-width="50" stroke-miterlimit="10" width="1200" height="1200"/>
|
||||
<path fill="#ffffff" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path fill="#ffffff" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
12
assets/icons/frontmatter-short-light.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
|
||||
<rect x="25" y="25" fill="none" stroke="#424242" stroke-width="50" stroke-miterlimit="10" width="1200" height="1200"/>
|
||||
<path fill="#424242" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path fill="#424242" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
assets/icons/frontmatter-small-dark.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
|
||||
<path fill="#C5C5C5" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path fill="#C5C5C5" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
11
assets/icons/frontmatter-small-light.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
|
||||
<path fill="#424242" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path fill="#424242" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
11
assets/icons/frontmatter-small-teal.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
|
||||
<path fill="#02aeb7" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
|
||||
<path fill="#02aeb7" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
|
||||
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
|
||||
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
|
||||
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
|
||||
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
10
assets/icons/heading-dark.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 12h10" />
|
||||
<path d="M7 4v16" />
|
||||
<path d="M17 4v16" />
|
||||
<path d="M15 20h4" />
|
||||
<path d="M15 4h4" />
|
||||
<path d="M5 20h4" />
|
||||
<path d="M5 4h4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
10
assets/icons/heading-light.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 12h10" />
|
||||
<path d="M7 4v16" />
|
||||
<path d="M17 4v16" />
|
||||
<path d="M15 20h4" />
|
||||
<path d="M15 4h4" />
|
||||
<path d="M5 20h4" />
|
||||
<path d="M5 4h4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
6
assets/icons/italic-dark.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="11" y1="5" x2="17" y2="5" />
|
||||
<line x1="7" y1="19" x2="13" y2="19" />
|
||||
<line x1="14" y1="5" x2="10" y2="19" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 362 B |
6
assets/icons/italic-light.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="11" y1="5" x2="17" y2="5" />
|
||||
<line x1="7" y1="19" x2="13" y2="19" />
|
||||
<line x1="14" y1="5" x2="10" y2="19" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 362 B |
3
assets/icons/media-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#C5C5C5" width="24" height="24">
|
||||
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 269 B |
3
assets/icons/media-light.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#424242" width="24" height="24">
|
||||
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 269 B |
7
assets/icons/options-dark.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="8" y1="12" x2="8" y2="12.01" />
|
||||
<line x1="12" y1="12" x2="12" y2="12.01" />
|
||||
<line x1="16" y1="12" x2="16" y2="12.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
7
assets/icons/options-light.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="8" y1="12" x2="8" y2="12.01" />
|
||||
<line x1="12" y1="12" x2="12" y2="12.01" />
|
||||
<line x1="16" y1="12" x2="16" y2="12.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
8
assets/icons/ordered-list-dark.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list-numbers" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M11 6h9" />
|
||||
<path d="M11 12h9" />
|
||||
<path d="M12 18h8" />
|
||||
<path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" />
|
||||
<path d="M6 10v-6l-2 2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 451 B |
8
assets/icons/ordered-list-light.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list-numbers" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M11 6h9" />
|
||||
<path d="M11 12h9" />
|
||||
<path d="M12 18h8" />
|
||||
<path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" />
|
||||
<path d="M6 10v-6l-2 2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 451 B |
5
assets/icons/strikethrough-dark.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 5v9a5 5 0 0 0 10 0v-9" />
|
||||
<path d="M4 12h16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
5
assets/icons/strikethrough-light.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M7 5v9a5 5 0 0 0 10 0v-9" />
|
||||
<path d="M4 12h16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
9
assets/icons/unordered-list-dark.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="9" y1="6" x2="20" y2="6" />
|
||||
<line x1="9" y1="12" x2="20" y2="12" />
|
||||
<line x1="9" y1="18" x2="20" y2="18" />
|
||||
<line x1="5" y1="6" x2="5" y2="6.01" />
|
||||
<line x1="5" y1="12" x2="5" y2="12.01" />
|
||||
<line x1="5" y1="18" x2="5" y2="18.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 533 B |
9
assets/icons/unordered-list-light.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="9" y1="6" x2="20" y2="6" />
|
||||
<line x1="9" y1="12" x2="20" y2="12" />
|
||||
<line x1="9" y1="18" x2="20" y2="18" />
|
||||
<line x1="5" y1="6" x2="5" y2="6.01" />
|
||||
<line x1="5" y1="12" x2="5" y2="12.01" />
|
||||
<line x1="5" y1="18" x2="5" y2="18.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 533 B |
@@ -30,7 +30,7 @@
|
||||
}
|
||||
|
||||
.inherit {
|
||||
position: inherit !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.z-10 { z-index: 10 !important; }
|
||||
@@ -143,6 +143,7 @@
|
||||
}
|
||||
|
||||
.article__tags {
|
||||
position: relative;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@@ -165,6 +166,10 @@
|
||||
border: 1px solid var(--vscode-inputValidation-infoBorder);
|
||||
}
|
||||
|
||||
.article__tags__input input:disabled {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.article__tags__input.freeform {
|
||||
position: relative;
|
||||
}
|
||||
@@ -356,8 +361,18 @@
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.table__cell__seo_details {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.table__cell__validation {
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table__cell__validation div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.table__cell__validation .valid {
|
||||
@@ -368,6 +383,15 @@
|
||||
color: #E6AF2E;
|
||||
}
|
||||
|
||||
.table__cell__validation div span + span {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.seo__status__note {
|
||||
font-size: 10px;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
/* Fields */
|
||||
.field__toggle {
|
||||
position: relative;
|
||||
@@ -427,16 +451,57 @@ input:checked + .field__toggle__slider:before {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.vscode-dark .metadata_field__box {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px dashed rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.vscode-light .metadata_field__box {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border: 1px dashed rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.metadata_field__box {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px dashed rgba(255, 255, 255, 0.2);
|
||||
margin-bottom: .5rem;
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.metadata_field__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.metadata_field__label.metadata_field__label_parent {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.metadata_field__label svg {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.metadata_field__error {
|
||||
color: var(--vscode-errorForeground);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metadata_field__error button {
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.metadata_field__error button:hover {
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.metadata_field__input, .metadata_field__input:focus,
|
||||
.metadata_field__textarea, .metadata_field__textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -451,17 +516,92 @@ input:checked + .field__toggle__slider:before {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.metadata_field__choice {
|
||||
.metadata_field__choice__toggle {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
border: 1px solid var(--vscode-inputValidation-infoBorder) !important;
|
||||
outline: none !important;
|
||||
width: 100%;
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
color: var(--vscode-input-foreground);
|
||||
background-color: var(--vscode-input-background);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.metadata_field__choice__toggle:hover,
|
||||
.metadata_field__choice__toggle:focus,
|
||||
.metadata_field__choice__toggle:active,
|
||||
.metadata_field__choice__toggle:disabled {
|
||||
background-color: var(--vscode-input-background);
|
||||
}
|
||||
|
||||
.metadata_field__choice::placeholde {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
.metadata_field__choice__toggle span {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.metadata_field__choice__toggle svg.icon {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
margin-left: .25rem;
|
||||
|
||||
position: absolute;
|
||||
right: .25rem;
|
||||
}
|
||||
|
||||
.metadata_field__choice_list {
|
||||
width: 90%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
|
||||
color: var(--vscode-dropdown-foreground);
|
||||
background-color: var(--vscode-dropdown-background);
|
||||
}
|
||||
|
||||
.metadata_field__choice_list.open {
|
||||
border: 1px solid rgba(0, 0, 0, .9);
|
||||
}
|
||||
|
||||
.metadata_field__choice_list li {
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.metadata_field__choice_list li:active {
|
||||
color: var(--vscode-button-foreground);
|
||||
background-color: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
.metadata_field__choice_list li[aria-selected="true"] {
|
||||
color: var(--vscode-button-foreground);
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.metadata_field__choice_list li[aria-disabled="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.metadata_field__choice_list__item {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.metadata_field__choice__button {
|
||||
margin-top: .5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.metadata_field__choice__button_icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.metadata_field__datetime {
|
||||
@@ -481,12 +621,64 @@ input:checked + .field__toggle__slider:before {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.metadata_field__datetime > button:hover {
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.metadata_field__multiple_images {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.metadata_field__preview_image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-height: 16rem;
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__button {
|
||||
background-color: transparent;
|
||||
border: 1px dashed var(--vscode-button-background);
|
||||
padding: 1.5rem;
|
||||
filter: brightness(85%);
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__button:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
filter: brightness(100%);
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__button svg {
|
||||
color: var(--vscode-foreground);
|
||||
display: block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__button span {
|
||||
color: var(--vscode-foreground);
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.vscode-light .metadata_field__preview_image__preview {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.vscode-dark .metadata_field__preview_image__preview {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__preview {
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.metadata_field__preview_image__remove {
|
||||
background-color: var(--vscode-inputValidation-errorBackground);
|
||||
color: var(--vscode-inputValidation-errorForeground);
|
||||
|
||||
BIN
assets/v5.9.0/media-dashboard.png
Normal file
|
After Width: | Height: | Size: 3.5 MiB |
BIN
assets/v6.0.0/content-preview.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/v6.0.0/data-dashboard.png
Normal file
|
After Width: | Height: | Size: 196 KiB |
23167
package-lock.json
generated
1020
package.json
@@ -2,6 +2,7 @@ const tailwindcss = require('tailwindcss');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('postcss-nested'),
|
||||
tailwindcss('./tailwind.config.js'),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX } from './../constants/settings';
|
||||
import { isValidFile } from './../helpers/isValidFile';
|
||||
import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX, CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTINGS_CONTENT_PLACEHOLDERS, TelemetryEvent } from './../constants';
|
||||
import * as vscode from 'vscode';
|
||||
import { TaxonomyType } from "../models";
|
||||
import { CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX } from "../constants/settings";
|
||||
import { Field, TaxonomyType } from "../models";
|
||||
import { format } from "date-fns";
|
||||
import { ArticleHelper, Settings, SlugHelper } from '../helpers';
|
||||
import matter = require('gray-matter');
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { extname, basename } from 'path';
|
||||
import { extname, basename, parse, dirname } from 'path';
|
||||
import { COMMAND_NAME, DefaultFields } from '../constants';
|
||||
import { DashboardData } from '../models/DashboardData';
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { MediaListener } from '../listeners/panel';
|
||||
|
||||
|
||||
export class Article {
|
||||
|
||||
private static prevContent = "";
|
||||
|
||||
/**
|
||||
* Insert taxonomy
|
||||
*
|
||||
@@ -103,7 +103,7 @@ export class Article {
|
||||
* Update the date in the front matter
|
||||
* @param article
|
||||
*/
|
||||
public static updateDate(article: matter.GrayMatterFile<string>, forceCreate: boolean = false) {
|
||||
public static updateDate(article: ParsedFrontMatter, forceCreate: boolean = false) {
|
||||
article.data = ArticleHelper.updateDates(article.data);
|
||||
return article;
|
||||
}
|
||||
@@ -117,7 +117,37 @@ export class Article {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const updatedArticle = this.setLastModifiedDateInner(editor.document);
|
||||
|
||||
if (typeof updatedArticle === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
ArticleHelper.update(
|
||||
editor,
|
||||
updatedArticle as ParsedFrontMatter
|
||||
);
|
||||
}
|
||||
|
||||
public static async setLastModifiedDateOnSave(
|
||||
document: vscode.TextDocument
|
||||
): Promise<vscode.TextEdit[]> {
|
||||
const updatedArticle = this.setLastModifiedDateInner(document);
|
||||
|
||||
if (typeof updatedArticle === "undefined") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const update = ArticleHelper.generateUpdate(document, updatedArticle);
|
||||
|
||||
return [update];
|
||||
}
|
||||
|
||||
private static setLastModifiedDateInner(
|
||||
document: vscode.TextDocument
|
||||
): ParsedFrontMatter | undefined {
|
||||
const article = ArticleHelper.getFrontMatterFromDocument(document);
|
||||
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
@@ -126,8 +156,7 @@ export class Article {
|
||||
const dateField = Settings.get(SETTING_MODIFIED_FIELD) as string || DefaultFields.LastModified;
|
||||
try {
|
||||
cloneArticle.data[dateField] = Article.formatDate(new Date());
|
||||
|
||||
ArticleHelper.update(editor, cloneArticle);
|
||||
return cloneArticle;
|
||||
} catch (e: any) {
|
||||
Notifications.error(`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
|
||||
}
|
||||
@@ -137,6 +166,8 @@ export class Article {
|
||||
* Generate the slug based on the article title
|
||||
*/
|
||||
public static async generateSlug() {
|
||||
Telemetry.send(TelemetryEvent.generateSlug);
|
||||
|
||||
const prefix = Settings.get(SETTING_SLUG_PREFIX) as string;
|
||||
const suffix = Settings.get(SETTING_SLUG_SUFFIX) as string;
|
||||
const updateFileName = Settings.get(SETTING_SLUG_UPDATE_FILE_NAME) as string;
|
||||
@@ -152,11 +183,34 @@ export class Article {
|
||||
return;
|
||||
}
|
||||
|
||||
const articleTitle: string = article.data["title"];
|
||||
let slug = SlugHelper.createSlug(articleTitle);
|
||||
const contentType = ArticleHelper.getContentType(article.data);
|
||||
const titleField = "title";
|
||||
const articleTitle: string = article.data[titleField];
|
||||
|
||||
const slug = SlugHelper.createSlug(articleTitle);
|
||||
if (slug) {
|
||||
slug = `${prefix}${slug}${suffix}`;
|
||||
article.data["slug"] = slug;
|
||||
let slugFieldValue = `${prefix}${slug}${suffix}`;
|
||||
article.data["slug"] = slugFieldValue;
|
||||
|
||||
if (contentType) {
|
||||
// Update the fields containing the slug placeholder
|
||||
let fieldsToUpdate: Field[] = contentType.fields.filter(f => f.default === "{{slug}}");
|
||||
for (const field of fieldsToUpdate) {
|
||||
article.data[field.name] = slug;
|
||||
}
|
||||
|
||||
// Update the fields containing a custom placeholder that depends on slug
|
||||
const placeholders = Settings.get<{id: string, value: string}[]>(SETTINGS_CONTENT_PLACEHOLDERS);
|
||||
const customPlaceholders = placeholders?.filter(p => p.value.includes("{{slug}}"));
|
||||
for (const customPlaceholder of (customPlaceholders || [])) {
|
||||
const customPlaceholderFields = contentType.fields.filter(f => f.default === `{{${customPlaceholder.id}}}`);
|
||||
for (const pField of customPlaceholderFields) {
|
||||
article.data[pField.name] = customPlaceholder.value;
|
||||
article.data[pField.name] = ArticleHelper.processKnownPlaceholders(article.data[pField.name], articleTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleHelper.update(editor, article);
|
||||
|
||||
// Check if the file name should be updated by the slug
|
||||
@@ -172,7 +226,7 @@ export class Article {
|
||||
|
||||
let newFileName = `${slugName}${ext}`;
|
||||
if (filePrefix && typeof filePrefix === "string") {
|
||||
newFileName = `${format(new Date(), filePrefix)}-${newFileName}`;
|
||||
newFileName = `${format(new Date(), DateHelper.formatUpdate(filePrefix) as string)}-${newFileName}`;
|
||||
}
|
||||
|
||||
const newPath = editor.document.uri.fsPath.replace(fileName, newFileName);
|
||||
@@ -183,7 +237,7 @@ export class Article {
|
||||
await vscode.workspace.fs.rename(editor.document.uri, vscode.Uri.file(newPath), {
|
||||
overwrite: false
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
Notifications.error(`Failed to rename file: ${e?.message || e}`);
|
||||
}
|
||||
}
|
||||
@@ -191,6 +245,31 @@ export class Article {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the slug from the front matter
|
||||
*/
|
||||
public static getSlug() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = parseWinPath(editor.document.fileName);
|
||||
|
||||
if (!isValidFile(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedFile = parse(file);
|
||||
|
||||
if (parsedFile.name.toLowerCase() !== "index") {
|
||||
return parsedFile.name;
|
||||
}
|
||||
|
||||
const folderName = basename(dirname(file));
|
||||
return folderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the page its draft mode
|
||||
*/
|
||||
@@ -211,42 +290,29 @@ export class Article {
|
||||
|
||||
/**
|
||||
* Article auto updater
|
||||
* @param fileChanges
|
||||
* @param event
|
||||
*/
|
||||
public static async autoUpdate(fileChanges: vscode.TextDocumentChangeEvent) {
|
||||
const txtChanges = fileChanges.contentChanges.map(c => c.text);
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
public static async autoUpdate(event: vscode.TextDocumentWillSaveEvent) {
|
||||
const document = event.document;
|
||||
if (document && ArticleHelper.isMarkdownFile(document)) {
|
||||
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
|
||||
|
||||
if (txtChanges.length > 0 && editor && ArticleHelper.isMarkdownFile()) {
|
||||
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
|
||||
|
||||
if (autoUpdate) {
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (article.content === Article.prevContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
Article.prevContent = article.content;
|
||||
|
||||
Article.setLastModifiedDate();
|
||||
if (autoUpdate) {
|
||||
event.waitUntil(Article.setLastModifiedDateOnSave(document));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date to the defined format
|
||||
*/
|
||||
public static formatDate(dateValue: Date) {
|
||||
public static formatDate(dateValue: Date): string {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
|
||||
if (dateFormat && typeof dateFormat === "string") {
|
||||
return format(dateValue, dateFormat);
|
||||
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
|
||||
} else {
|
||||
return dateValue.toISOString();
|
||||
return typeof dateValue.toISOString === 'function' ? dateValue.toISOString() : dateValue?.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,13 +337,13 @@ export class Article {
|
||||
} as DashboardData);
|
||||
|
||||
// Let the editor panel know you are selecting an image
|
||||
ExplorerView.getInstance().getMediaSelection();
|
||||
MediaListener.getMediaSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current article
|
||||
*/
|
||||
private static getCurrent(): matter.GrayMatterFile<string> | undefined {
|
||||
private static getCurrent(): ParsedFrontMatter | undefined {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
@@ -298,7 +364,7 @@ export class Article {
|
||||
* @param field
|
||||
* @param forceCreate
|
||||
*/
|
||||
private static articleDate(article: matter.GrayMatterFile<string>, field: string, forceCreate: boolean) {
|
||||
private static articleDate(article: ParsedFrontMatter, field: string, forceCreate: boolean) {
|
||||
if (typeof article.data[field] !== "undefined" || forceCreate) {
|
||||
article.data[field] = Article.formatDate(new Date());
|
||||
}
|
||||
|
||||
75
src/commands/Backers.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { commands, ExtensionContext } from 'vscode';
|
||||
import { CONTEXT } from '../constants';
|
||||
import { Extension } from '../helpers';
|
||||
import { Credentials } from "../services/Credentials";
|
||||
import fetch from "node-fetch";
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { Dashboard } from './Dashboard';
|
||||
import { SettingsListener } from '../listeners/panel';
|
||||
|
||||
export class Backers {
|
||||
private static creds: Credentials | null = null;
|
||||
|
||||
public static async init(context: ExtensionContext) {
|
||||
Backers.creds = new Credentials();
|
||||
await Backers.creds.initialize(context, Backers.tryUsernameCheck);
|
||||
|
||||
Backers.tryUsernameCheck();
|
||||
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand('frontMatter.authenticate', async () => {
|
||||
Backers.tryUsernameCheck();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public static async tryUsernameCheck() {
|
||||
try {
|
||||
const username = await Backers.getUsername();
|
||||
Backers.validate(username || "");
|
||||
} catch (e) {
|
||||
Backers.validate("");
|
||||
}
|
||||
}
|
||||
|
||||
public static async getUsername() {
|
||||
const octokit = await Backers.creds?.getOctokit();
|
||||
const user = await octokit?.users.getAuthenticated();
|
||||
|
||||
if (user?.data?.login) {
|
||||
return user?.data?.login;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public static async validate(username: string) {
|
||||
const ext = Extension.getInstance();
|
||||
|
||||
if (!username) {
|
||||
ext.setState(CONTEXT.backer, undefined, 'global');
|
||||
}
|
||||
|
||||
const isBeta = ext.isBetaVersion();
|
||||
|
||||
const response = await fetch(`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/backers?backer=${username}`);
|
||||
|
||||
if (response.ok) {
|
||||
const prevData = await ext.getState<boolean>(CONTEXT.backer, 'global');
|
||||
await ext.setState(CONTEXT.backer, true, 'global');
|
||||
|
||||
if (!prevData) {
|
||||
const explorerView = ExplorerView.getInstance();
|
||||
if (explorerView.visible) {
|
||||
SettingsListener.getSettings();
|
||||
}
|
||||
|
||||
if (Dashboard.isOpen) {
|
||||
Dashboard.reload();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ext.setState(CONTEXT.backer, false, 'global');
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/commands/Content.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { commands, QuickPickItem, window } from 'vscode';
|
||||
import { COMMAND_NAME } from '../constants';
|
||||
|
||||
export class Content {
|
||||
|
||||
public static async create() {
|
||||
|
||||
const options: QuickPickItem[] = [{
|
||||
label: "Create content by content type",
|
||||
description: "Select if you want to create new content by the available content type(s)"
|
||||
}, {
|
||||
label: "Create content by template",
|
||||
description: "Select if you want to create new content by the available template(s)"
|
||||
} as QuickPickItem];
|
||||
|
||||
const selectedOption = await window.showQuickPick(options, {
|
||||
placeHolder: `Select how you want to create your new content`,
|
||||
canPickMany: false
|
||||
});
|
||||
|
||||
if (selectedOption) {
|
||||
if (selectedOption.label === options[0].label) {
|
||||
commands.executeCommand(COMMAND_NAME.createByContentType);
|
||||
} else {
|
||||
commands.executeCommand(COMMAND_NAME.createByTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,26 @@
|
||||
import { SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET } from './../constants/settings';
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { basename, dirname, extname, join } from "path";
|
||||
import { existsSync, statSync, unlinkSync, writeFileSync } from "fs";
|
||||
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env, Position } from "vscode";
|
||||
import { Settings as SettingsHelper } from '../helpers';
|
||||
import { TaxonomyType } from '../models';
|
||||
import { Folders } from './Folders';
|
||||
import { SETTINGS_DASHBOARD_OPENONSTART, CONTEXT } from '../constants';
|
||||
import { join } from "path";
|
||||
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from "vscode";
|
||||
import { Logger, Settings as SettingsHelper } from '../helpers';
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
|
||||
import { Page } from '../dashboardWebView/models/Page';
|
||||
import { openFileInEditor } from '../helpers/openFileInEditor';
|
||||
import { COMMAND_NAME, EXTENSION_STATE_PAGES_VIEW } from '../constants/Extension';
|
||||
import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Settings } from '../dashboardWebView/models/Settings';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { parseJSON } from 'date-fns';
|
||||
import { ViewType } from '../dashboardWebView/state';
|
||||
import { EditorHelper, WebviewHelper } from '@estruyf/vscode';
|
||||
import { MediaInfo, MediaPaths } from './../models/MediaPaths';
|
||||
import { decodeBase64Image } from '../helpers/decodeBase64Image';
|
||||
import { DefaultFields } from '../constants';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { DashboardData } from '../models/DashboardData';
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
|
||||
import { MediaLibrary } from '../helpers/MediaLibrary';
|
||||
import { DashboardListener, MediaListener, SettingsListener, TelemetryListener, DataListener, PagesListener, ExtensionListener } from '../listeners/dashboard';
|
||||
import { MediaListener as PanelMediaListener } from '../listeners/panel'
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
private static isDisposed: boolean = true;
|
||||
private static media: MediaInfo[] = [];
|
||||
private static timers: { [folder: string]: any } = {};
|
||||
private static _viewData: DashboardData | undefined;
|
||||
private static isDisposed: boolean = true;
|
||||
|
||||
public static get viewData(): DashboardData | undefined {
|
||||
return Dashboard._viewData;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Init the dashboard
|
||||
*/
|
||||
public static async init() {
|
||||
@@ -50,13 +34,17 @@ export class Dashboard {
|
||||
* Open or reveal the dashboard
|
||||
*/
|
||||
public static async open(data?: DashboardData) {
|
||||
MediaLibrary.getInstance();
|
||||
|
||||
Dashboard._viewData = data;
|
||||
|
||||
if (Dashboard.isOpen) {
|
||||
Dashboard.reveal();
|
||||
Dashboard.reveal(!!data);
|
||||
} else {
|
||||
Dashboard.create();
|
||||
}
|
||||
|
||||
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +57,33 @@ export class Dashboard {
|
||||
/**
|
||||
* Reveal the dashboard if it is open
|
||||
*/
|
||||
public static reveal() {
|
||||
public static reveal(hasData: boolean = false) {
|
||||
if (Dashboard.webview) {
|
||||
Dashboard.webview.reveal();
|
||||
|
||||
if (hasData) {
|
||||
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard.viewData });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static close() {
|
||||
Dashboard.webview?.dispose();
|
||||
}
|
||||
|
||||
public static reload() {
|
||||
if (Dashboard.isOpen) {
|
||||
Dashboard.webview?.dispose();
|
||||
|
||||
setTimeout(() => {
|
||||
Dashboard.open();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
public static resetViewData() {
|
||||
Dashboard._viewData = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the dashboard webview
|
||||
@@ -87,399 +97,68 @@ export class Dashboard {
|
||||
'FrontMatter Dashboard',
|
||||
ViewColumn.One,
|
||||
{
|
||||
enableScripts: true
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true
|
||||
}
|
||||
);
|
||||
|
||||
Dashboard.isDisposed = false;
|
||||
|
||||
Dashboard.webview.iconPath = {
|
||||
dark: Uri.file(join(extensionUri.fsPath, 'assets/frontmatter-dark.svg')),
|
||||
light: Uri.file(join(extensionUri.fsPath, 'assets/frontmatter.svg'))
|
||||
dark: Uri.file(join(extensionUri.fsPath, 'assets/icons/frontmatter-short-dark.svg')),
|
||||
light: Uri.file(join(extensionUri.fsPath, 'assets/icons/frontmatter-short-light.svg'))
|
||||
};
|
||||
|
||||
Dashboard.webview.webview.html = Dashboard.getWebviewContent(Dashboard.webview.webview, extensionUri);
|
||||
|
||||
Dashboard.webview.onDidChangeViewState(() => {
|
||||
Dashboard.webview.onDidChangeViewState(async () => {
|
||||
if (!this.webview?.visible) {
|
||||
Dashboard._viewData = undefined;
|
||||
const panel = ExplorerView.getInstance(extensionUri);
|
||||
panel.getMediaSelection();
|
||||
PanelMediaListener.getMediaSelection();
|
||||
|
||||
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: null });
|
||||
}
|
||||
|
||||
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, this.webview?.visible);
|
||||
});
|
||||
|
||||
Dashboard.webview.onDidDispose(() => {
|
||||
Dashboard.webview.onDidDispose(async () => {
|
||||
Dashboard.isDisposed = true;
|
||||
Dashboard._viewData = undefined;
|
||||
const panel = ExplorerView.getInstance(extensionUri);
|
||||
panel.getMediaSelection();
|
||||
PanelMediaListener.getMediaSelection();
|
||||
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false);
|
||||
});
|
||||
|
||||
SettingsHelper.onConfigChange((global?: any) => {
|
||||
Dashboard.getSettings();
|
||||
SettingsListener.getSettings();
|
||||
});
|
||||
|
||||
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
|
||||
switch(msg.command) {
|
||||
case DashboardMessage.getViewType:
|
||||
if (Dashboard._viewData) {
|
||||
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard._viewData });
|
||||
}
|
||||
break;
|
||||
case DashboardMessage.getData:
|
||||
Dashboard.getSettings();
|
||||
Dashboard.getPages();
|
||||
break;
|
||||
case DashboardMessage.openFile:
|
||||
openFileInEditor(msg.data);
|
||||
break;
|
||||
case DashboardMessage.createContent:
|
||||
await commands.executeCommand(COMMAND_NAME.createContent);
|
||||
break;
|
||||
case DashboardMessage.updateSetting:
|
||||
Dashboard.updateSetting(msg.data);
|
||||
break;
|
||||
case DashboardMessage.initializeProject:
|
||||
await commands.executeCommand(COMMAND_NAME.init, Dashboard.getSettings);
|
||||
break;
|
||||
case DashboardMessage.reload:
|
||||
if (!Dashboard.isDisposed) {
|
||||
Dashboard.webview?.dispose();
|
||||
setTimeout(() => {
|
||||
Dashboard.open();
|
||||
}, 100);
|
||||
}
|
||||
break;
|
||||
case DashboardMessage.setPageViewType:
|
||||
Extension.getInstance().setState(EXTENSION_STATE_PAGES_VIEW, msg.data);
|
||||
break;
|
||||
case DashboardMessage.getMedia:
|
||||
Dashboard.getMedia(msg?.data?.page, msg?.data?.folder)
|
||||
break;
|
||||
case DashboardMessage.copyToClipboard:
|
||||
env.clipboard.writeText(msg.data);
|
||||
break;
|
||||
case DashboardMessage.refreshMedia:
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(0, msg?.data?.folder);
|
||||
break;
|
||||
case DashboardMessage.uploadMedia:
|
||||
Dashboard.saveFile(msg?.data);
|
||||
break;
|
||||
case DashboardMessage.deleteMedia:
|
||||
Dashboard.deleteFile(msg?.data);
|
||||
break;
|
||||
case DashboardMessage.insertPreviewImage:
|
||||
Dashboard.insertImage(msg?.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an image into the front matter or contents
|
||||
* @param data
|
||||
*/
|
||||
private static async insertImage(data: any) {
|
||||
if (data?.file && data?.image) {
|
||||
if (!data?.position) {
|
||||
await commands.executeCommand(`workbench.view.extension.frontmatter-explorer`);
|
||||
}
|
||||
|
||||
await EditorHelper.showFile(data.file);
|
||||
Dashboard._viewData = undefined;
|
||||
Logger.info(`Receiving message from webview: ${msg.command}`);
|
||||
|
||||
const extensionUri = Extension.getInstance().extensionPath;
|
||||
const panel = ExplorerView.getInstance(extensionUri);
|
||||
|
||||
if (data?.position) {
|
||||
const editor = window.activeTextEditor;
|
||||
const line = data.position.line;
|
||||
const character = data.position.character;
|
||||
if (line) {
|
||||
await editor?.edit(builder => builder.insert(new Position(line, character), data.snippet || ``));
|
||||
}
|
||||
panel.getMediaSelection();
|
||||
} else {
|
||||
panel.getMediaSelection();
|
||||
panel.updateMetadata({field: data.fieldName, value: data.image});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the settings for the dashboard
|
||||
*/
|
||||
private static async getSettings() {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.settings,
|
||||
data: {
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS),
|
||||
folders: Folders.get(),
|
||||
initialized: await Template.isInitialized(),
|
||||
tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag),
|
||||
categories: SettingsHelper.getTaxonomy(TaxonomyType.Category),
|
||||
openOnStart: SettingsHelper.get(SETTINGS_DASHBOARD_OPENONSTART),
|
||||
versionInfo: ext.getVersion(),
|
||||
pageViewType: await ext.getState<ViewType | undefined>(EXTENSION_STATE_PAGES_VIEW),
|
||||
mediaSnippet: SettingsHelper.get<string[]>(SETTINGS_DASHBOARD_MEDIA_SNIPPET) || [],
|
||||
} as Settings
|
||||
DashboardListener.process(msg);
|
||||
ExtensionListener.process(msg);
|
||||
MediaListener.process(msg);
|
||||
PagesListener.process(msg);
|
||||
SettingsListener.process(msg);
|
||||
DataListener.process(msg);
|
||||
TelemetryListener.process(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a setting from the dashboard
|
||||
* Return the webview
|
||||
* @returns The webview
|
||||
*/
|
||||
private static async updateSetting(data: { name: string, value: any }) {
|
||||
await SettingsHelper.update(data.name, data.value);
|
||||
Dashboard.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all media files
|
||||
*/
|
||||
private static async getMedia(page: number = 0, folder: string = '') {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
|
||||
|
||||
if (Dashboard.media.length === 0) {
|
||||
const contentFolder = Folders.get();
|
||||
let allMedia: MediaInfo[] = [];
|
||||
|
||||
if (staticFolder) {
|
||||
const files = await workspace.findFiles(`${staticFolder || ""}/**/*`);
|
||||
const media = Dashboard.filterMedia(files);
|
||||
|
||||
allMedia = [...media];
|
||||
}
|
||||
|
||||
if (contentFolder && wsFolder) {
|
||||
for (let i = 0; i < contentFolder.length; i++) {
|
||||
const folder = contentFolder[i];
|
||||
const relFolderPath = folder.path.substring(wsFolder.fsPath.length + 1);
|
||||
const files = await workspace.findFiles(`${relFolderPath}/**/*`);
|
||||
const media = Dashboard.filterMedia(files);
|
||||
|
||||
allMedia = [...allMedia, ...media];
|
||||
}
|
||||
}
|
||||
|
||||
allMedia = allMedia.sort((a, b) => {
|
||||
if (b.fsPath < a.fsPath) {
|
||||
return -1;
|
||||
}
|
||||
if (b.fsPath > a.fsPath) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
Dashboard.media = Object.assign([], allMedia);
|
||||
}
|
||||
|
||||
// Filter the media
|
||||
let files: MediaInfo[] = Dashboard.media;
|
||||
if (folder) {
|
||||
files = files.filter(f => f.fsPath.includes(folder));
|
||||
}
|
||||
|
||||
// Retrieve the total after filtering and before the slicing happens
|
||||
const total = files.length;
|
||||
|
||||
// Get media set
|
||||
files = files.slice(page * 16, ((page + 1) * 16));
|
||||
files = files.map((file) => {
|
||||
try {
|
||||
return {
|
||||
...file,
|
||||
stats: statSync(file.fsPath)
|
||||
};
|
||||
} catch (e) {
|
||||
return {...file, stats: undefined};
|
||||
}
|
||||
}).filter(f => f.stats !== undefined);
|
||||
|
||||
const folders = [...new Set(Dashboard.media.map((file) => {
|
||||
let relFolderPath = wsFolder ? file.fsPath.substring(wsFolder.fsPath.length + 1) : file.fsPath;
|
||||
if (staticFolder && relFolderPath.startsWith(staticFolder)) {
|
||||
relFolderPath = relFolderPath.substring(staticFolder.length);
|
||||
}
|
||||
if (relFolderPath?.startsWith('/')) {
|
||||
relFolderPath = relFolderPath.substring(1);
|
||||
}
|
||||
return dirname(relFolderPath);
|
||||
}))];
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.media,
|
||||
data: {
|
||||
media: files,
|
||||
total: total,
|
||||
folders
|
||||
} as MediaPaths
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the markdown pages
|
||||
*/
|
||||
private static async getPages() {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
const descriptionField = SettingsHelper.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description;
|
||||
const dateField = SettingsHelper.get(SETTING_DATE_FIELD) as string || DefaultFields.PublishingDate;
|
||||
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
|
||||
|
||||
const folderInfo = await Folders.getInfo();
|
||||
const pages: Page[] = [];
|
||||
|
||||
if (folderInfo) {
|
||||
for (const folder of folderInfo) {
|
||||
for (const file of folder.lastModified) {
|
||||
if (file.fileName.endsWith(`.md`) || file.fileName.endsWith(`.mdx`)) {
|
||||
try {
|
||||
const article = ArticleHelper.getFrontMatterByPath(file.filePath);
|
||||
|
||||
if (article?.data.title) {
|
||||
const page: Page = {
|
||||
...article.data,
|
||||
// FrontMatter properties
|
||||
fmFolder: folder.title,
|
||||
fmModified: file.mtime,
|
||||
fmFilePath: file.filePath,
|
||||
fmFileName: file.fileName,
|
||||
fmDraft: article?.data.draft ? "Draft" : "Published",
|
||||
fmYear: article?.data[dateField] ? parseJSON(article?.data[dateField]).getFullYear() : null,
|
||||
// Make sure these are always set
|
||||
title: article?.data.title,
|
||||
slug: article?.data.slug,
|
||||
date: article?.data[dateField] || "",
|
||||
draft: article?.data.draft,
|
||||
description: article?.data[descriptionField] || "",
|
||||
};
|
||||
|
||||
if (article?.data.preview && wsFolder) {
|
||||
const staticPath = join(wsFolder.fsPath, staticFolder || "", article?.data.preview);
|
||||
const contentFolderPath = join(dirname(file.filePath), article?.data.preview);
|
||||
|
||||
let previewUri = null;
|
||||
if (existsSync(staticPath)) {
|
||||
previewUri = Uri.file(staticPath);
|
||||
} else if (existsSync(contentFolderPath)) {
|
||||
previewUri = Uri.file(contentFolderPath);
|
||||
}
|
||||
|
||||
if (previewUri) {
|
||||
const preview = Dashboard.webview?.webview.asWebviewUri(previewUri);
|
||||
page.preview = preview?.toString() || "";
|
||||
} else {
|
||||
page.preview = "";
|
||||
}
|
||||
}
|
||||
|
||||
pages.push(page);
|
||||
}
|
||||
} catch (error: any) {
|
||||
Notifications.error(`File error: ${file.filePath} - ${error?.message || error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.pages,
|
||||
data: pages
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the media files
|
||||
*/
|
||||
private static filterMedia(files: Uri[]) {
|
||||
return files.filter(file => {
|
||||
const ext = extname(file.fsPath);
|
||||
return ['.jpg', '.jpeg', '.png', '.gif', '.svg'].includes(ext);
|
||||
}).map((file) => ({
|
||||
fsPath: file.fsPath,
|
||||
vsPath: Dashboard.webview?.webview.asWebviewUri(file).toString(),
|
||||
stats: undefined
|
||||
} as MediaInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the dropped file in the current folder
|
||||
* @param fileData
|
||||
*/
|
||||
private static async saveFile({fileName, contents, folder}: { fileName: string; contents: string; folder: string | null }) {
|
||||
if (fileName && contents) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
|
||||
const wsPath = wsFolder ? wsFolder.fsPath : "";
|
||||
let absFolderPath = join(wsPath, staticFolder || "", folder || "");
|
||||
|
||||
if (!existsSync(absFolderPath)) {
|
||||
absFolderPath = join(wsPath, folder || "");
|
||||
}
|
||||
|
||||
if (!existsSync(absFolderPath)) {
|
||||
Notifications.error(`We couldn't find your selected folder.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const staticPath = join(absFolderPath, fileName);
|
||||
const imgData = decodeBase64Image(contents);
|
||||
|
||||
if (imgData) {
|
||||
writeFileSync(staticPath, imgData.data);
|
||||
Notifications.info(`File ${fileName} uploaded to: ${staticFolder}/${folder}`);
|
||||
|
||||
const folderPath = `${staticFolder}/${folder}`;
|
||||
if (Dashboard.timers[folderPath]) {
|
||||
clearTimeout(Dashboard.timers[folderPath]);
|
||||
delete Dashboard.timers[folderPath];
|
||||
}
|
||||
|
||||
Dashboard.timers[folderPath] = setTimeout(() => {
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(0, folder || "");
|
||||
delete Dashboard.timers[folderPath];
|
||||
}, 500);
|
||||
} else {
|
||||
Notifications.error(`Something went wrong uploading ${fileName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the selected file
|
||||
* @param fileData
|
||||
*/
|
||||
private static async deleteFile({ file, page, folder }: { file: string; page: number; folder: string | null; }) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
unlinkSync(file);
|
||||
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(page || 0, folder || "");
|
||||
} catch(err) {
|
||||
Notifications.error(`Something went wrong deleting ${basename(file)}`);
|
||||
}
|
||||
public static getWebview() {
|
||||
return Dashboard.webview?.webview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the dashboard
|
||||
* @param msg
|
||||
*/
|
||||
private static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
|
||||
public static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
|
||||
if (Dashboard.isDisposed) {
|
||||
return;
|
||||
}
|
||||
@@ -494,27 +173,49 @@ export class Dashboard {
|
||||
* @param webView
|
||||
*/
|
||||
private static getWebviewContent(webView: Webview, extensionPath: Uri): string {
|
||||
const scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', 'pages.js'));
|
||||
const dashboardFile = "dashboardWebView.js";
|
||||
const localPort = `9000`;
|
||||
const localServerUrl = `localhost:${localPort}`;
|
||||
|
||||
let scriptUri = "";
|
||||
const isProd = Extension.getInstance().isProductionMode;
|
||||
if (isProd) {
|
||||
scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile)).toString();
|
||||
} else {
|
||||
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
|
||||
}
|
||||
|
||||
const nonce = WebviewHelper.getNonce();
|
||||
|
||||
const version = Extension.getInstance().getVersion();
|
||||
const ext = Extension.getInstance();
|
||||
const version = ext.getVersion();
|
||||
const isBeta = ext.isBetaVersion();
|
||||
|
||||
const csp = [
|
||||
`default-src 'none';`,
|
||||
`img-src ${`vscode-file://vscode-app`} ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'`,
|
||||
`script-src ${isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`} 'unsafe-eval'`,
|
||||
`style-src ${webView.cspSource} 'self' 'unsafe-inline'`,
|
||||
`font-src ${webView.cspSource}`,
|
||||
`connect-src https://o1022172.ingest.sentry.io ${isProd ? `` : `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`}`
|
||||
];
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="width:100%;height:100%;margin:0;padding:0;">
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${`vscode-file://vscode-app`} ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'; script-src 'nonce-${nonce}'; style-src ${webView.cspSource} 'self' 'unsafe-inline'; font-src ${webView.cspSource}">
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="${csp.join('; ')}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Front Matter Dashboard</title>
|
||||
</head>
|
||||
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden" class="bg-gray-100 text-vulcan-500 dark:bg-vulcan-500 dark:text-whisper-500">
|
||||
<div id="app" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
|
||||
<div id="app" data-isProd="${isProd}" data-environment="${isBeta ? "BETA" : "main"}" data-version="${version.usedVersion}" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
|
||||
|
||||
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`dashboard-${version.installedVersion}`}" alt="Daily usage" />
|
||||
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
<script ${isProd ? `nonce="${nonce}"` : ""} src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
63
src/commands/Diagnostics.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Folders } from "./Folders";
|
||||
import { ViewColumn, workspace } from "vscode";
|
||||
import ContentProvider from "../providers/ContentProvider";
|
||||
import { join } from "path";
|
||||
import { ContentFolder } from "../models";
|
||||
|
||||
|
||||
export class Diagnostics {
|
||||
|
||||
public static async show() {
|
||||
const folders = Folders.get();
|
||||
const projectName = Folders.getProjectFolderName();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
const folderData = [];
|
||||
for (const folder of folders) {
|
||||
folderData.push(await Diagnostics.processFolder(folder, projectName));
|
||||
}
|
||||
|
||||
const all = await Diagnostics.allProjectFiles();
|
||||
|
||||
const logging = `# Project name
|
||||
|
||||
${projectName}
|
||||
|
||||
# Folders
|
||||
|
||||
${folders.map(f => `- ${f.title}: "${f.path}"`).join("\n")}
|
||||
|
||||
# Workspace folder
|
||||
|
||||
${wsFolder ? wsFolder.fsPath : "No workspace folder"}
|
||||
|
||||
# Total files
|
||||
|
||||
${all}
|
||||
|
||||
# Folders to search files
|
||||
|
||||
${folderData.join("\n")}
|
||||
`;
|
||||
|
||||
ContentProvider.show(logging, `${projectName} diagnostics`, "markdown", ViewColumn.One);
|
||||
}
|
||||
|
||||
private static async allProjectFiles() {
|
||||
const allFiles = await workspace.findFiles(`**/*.*`);
|
||||
return `Total files found: ${allFiles.length}`;
|
||||
}
|
||||
|
||||
private static async processFolder(folder: ContentFolder, projectName: string) {
|
||||
let projectStart = folder.path.split(projectName).pop();
|
||||
projectStart = projectStart || "";
|
||||
projectStart = projectStart?.replace(/\\/g, '/');
|
||||
projectStart = projectStart?.startsWith('/') ? projectStart.substr(1) : projectStart;
|
||||
|
||||
const mdFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md'));
|
||||
const mdxFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx'));
|
||||
const markdownFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown'));
|
||||
|
||||
return `- Project start length: ${projectStart.length} | Search in: "${join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.*')}" | mdFiles: ${mdFiles.length} | mdxFiles: ${mdxFiles.length} | markdownFiles: ${markdownFiles.length}`;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,89 @@
|
||||
import { SETTINGS_CONTENT_PAGE_FOLDERS } from './../constants/settings';
|
||||
import { Questions } from './../helpers/Questions';
|
||||
import { SETTINGS_CONTENT_PAGE_FOLDERS, SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_CONTENT_SUPPORTED_FILETYPES, TelemetryEvent } from './../constants';
|
||||
import { commands, Uri, workspace, window } from "vscode";
|
||||
import { basename, join } from "path";
|
||||
import { ContentFolder, FileInfo, FolderInfo } from "../models";
|
||||
import uniqBy = require("lodash.uniqby");
|
||||
import { Template } from "./Template";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { CONTEXT } from "../constants/context";
|
||||
import { Settings } from "../helpers";
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { format } from 'date-fns';
|
||||
import { Dashboard } from './Dashboard';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { MediaHelpers } from '../helpers/MediaHelpers';
|
||||
import { MediaListener, PagesListener } from '../listeners/dashboard';
|
||||
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
|
||||
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
|
||||
|
||||
export class Folders {
|
||||
|
||||
/**
|
||||
* Add a media folder
|
||||
* @returns
|
||||
*/
|
||||
public static async addMediaFolder(data?: {selectedFolder?: string}) {
|
||||
let wsFolder = Folders.getWorkspaceFolder();
|
||||
const staticFolder = Settings.get<string>(SETTINGS_CONTENT_STATIC_FOLDER);
|
||||
|
||||
let startPath = "";
|
||||
|
||||
if (data?.selectedFolder) {
|
||||
startPath = data.selectedFolder.replace(parseWinPath(wsFolder?.fsPath || ""), "");
|
||||
} else if (staticFolder) {
|
||||
startPath = `/${staticFolder}`;
|
||||
}
|
||||
|
||||
if (startPath && !startPath.endsWith("/")) {
|
||||
startPath += "/";
|
||||
}
|
||||
|
||||
const folderName = await window.showInputBox({
|
||||
prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`,
|
||||
value: startPath,
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: `${format(new Date(), `yyyy/MM`)}`
|
||||
});
|
||||
|
||||
if (!folderName) {
|
||||
Notifications.warning(`No folder name was specified.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = folderName.split("/").filter(f => f);
|
||||
let parentFolders: string[] = [];
|
||||
|
||||
for (const folder of folders) {
|
||||
const folderPath = join(parseWinPath(wsFolder?.fsPath || ""), parentFolders.join("/"), folder);
|
||||
|
||||
parentFolders.push(folder);
|
||||
|
||||
if (!existsSync(folderPath)) {
|
||||
mkdirSync(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (Dashboard.isOpen) {
|
||||
MediaHelpers.resetMedia();
|
||||
MediaListener.sendMediaFiles(0, folderName);
|
||||
}
|
||||
|
||||
Telemetry.send(TelemetryEvent.addMediaFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content in a registered folder
|
||||
* @returns
|
||||
*/
|
||||
public static async create() {
|
||||
const folders = Folders.get();
|
||||
|
||||
if (!folders || folders.length === 0) {
|
||||
Notifications.warning(`There are no known content locations defined in this project.`);
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedFolder: string | undefined;
|
||||
if (folders.length > 1) {
|
||||
selectedFolder = await window.showQuickPick(folders.map(f => f.title), {
|
||||
placeHolder: `Select where you want to create your content`
|
||||
});
|
||||
} else {
|
||||
selectedFolder = folders[0].title;
|
||||
}
|
||||
|
||||
const selectedFolder = await Questions.SelectContentFolder();
|
||||
if (!selectedFolder) {
|
||||
Notifications.warning(`You didn't select a place where you wanted to create your content.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = Folders.get();
|
||||
const location = folders.find(f => f.title === selectedFolder);
|
||||
if (location) {
|
||||
const folderPath = Folders.getFolderPath(Uri.file(location.path));
|
||||
@@ -79,6 +125,8 @@ export class Folders {
|
||||
await Folders.update(folders);
|
||||
|
||||
Notifications.info(`Folder registered`);
|
||||
|
||||
Telemetry.send(TelemetryEvent.registerFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +139,8 @@ export class Folders {
|
||||
let folders = Folders.get();
|
||||
folders = folders.filter(f => f.path !== folder.fsPath);
|
||||
await Folders.update(folders);
|
||||
|
||||
Telemetry.send(TelemetryEvent.unregisterFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +165,33 @@ export class Folders {
|
||||
*/
|
||||
public static getWorkspaceFolder(): Uri | undefined {
|
||||
const folders = workspace.workspaceFolders;
|
||||
if (folders && folders.length > 0) {
|
||||
|
||||
if (folders && folders.length === 1) {
|
||||
return folders[0].uri;
|
||||
} else if (folders && folders.length > 1) {
|
||||
let projectFolder = undefined;
|
||||
|
||||
for (const folder of folders) {
|
||||
if (!projectFolder && existsSync(join(folder.uri.fsPath, Settings.globalFile))) {
|
||||
projectFolder = folder.uri;
|
||||
}
|
||||
}
|
||||
|
||||
if (!projectFolder) {
|
||||
window.showWorkspaceFolderPick({
|
||||
placeHolder: `Please select the main workspace folder for Front Matter to use.`
|
||||
}).then(selectedFolder => {
|
||||
if (selectedFolder) {
|
||||
Settings.createGlobalFile(selectedFolder.uri);
|
||||
// Full reload to make sure the whole extension is reloaded correctly
|
||||
commands.executeCommand(`workbench.action.reloadWindow`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return projectFolder;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -127,7 +201,6 @@ export class Folders {
|
||||
public static getProjectFolderName(): string {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
// const projectFolder = wsFolder?.fsPath.split('\\').join('/').split('/').pop();
|
||||
return basename(wsFolder.fsPath);
|
||||
}
|
||||
return "";
|
||||
@@ -137,6 +210,7 @@ export class Folders {
|
||||
* Get the registered folders information
|
||||
*/
|
||||
public static async getInfo(limit?: number): Promise<FolderInfo[] | null> {
|
||||
const supportedFiles = Settings.get<string[]>(SETTINGS_CONTENT_SUPPORTED_FILETYPES);
|
||||
const folders = Folders.get();
|
||||
if (folders && folders.length > 0) {
|
||||
let folderInfo: FolderInfo[] = [];
|
||||
@@ -145,19 +219,28 @@ export class Folders {
|
||||
try {
|
||||
const projectName = Folders.getProjectFolderName();
|
||||
let projectStart = folder.path.split(projectName).pop();
|
||||
|
||||
if (projectStart) {
|
||||
projectStart = projectStart.replace(/\\/g, '/');
|
||||
projectStart = projectStart.startsWith('/') ? projectStart.substr(1) : projectStart;
|
||||
const mdFiles = await workspace.findFiles(join(projectStart, '**/*.md'));
|
||||
const mdxFiles = await workspace.findFiles(join(projectStart, '**/*.mdx'));
|
||||
let files = [...mdFiles, ...mdxFiles];
|
||||
|
||||
let files: Uri[] = [];
|
||||
|
||||
for (const fileType of (supportedFiles || DEFAULT_FILE_TYPES)) {
|
||||
const filePath = join(projectStart, folder.excludeSubdir ? '/' : '**', `*${fileType.startsWith('.') ? '' : '.'}${fileType}`);
|
||||
const foundFiles = await workspace.findFiles(filePath, '**/node_modules/**');
|
||||
files = [...files, ...foundFiles];
|
||||
}
|
||||
|
||||
if (files) {
|
||||
let fileStats: FileInfo[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const fileName = basename(file.fsPath);
|
||||
|
||||
const stats = await workspace.fs.stat(file);
|
||||
|
||||
fileStats.push({
|
||||
filePath: file.fsPath,
|
||||
fileName,
|
||||
@@ -201,7 +284,7 @@ export class Folders {
|
||||
const folders: ContentFolder[] = Settings.get(SETTINGS_CONTENT_PAGE_FOLDERS) as ContentFolder[];
|
||||
|
||||
return folders.map(folder => ({
|
||||
title: folder.title,
|
||||
...folder,
|
||||
path: Folders.absWsFolder(folder, wsFolder)
|
||||
}));
|
||||
}
|
||||
@@ -212,7 +295,29 @@ export class Folders {
|
||||
*/
|
||||
private static async update(folders: ContentFolder[]) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
await Settings.update(SETTINGS_CONTENT_PAGE_FOLDERS, folders.map(folder => ({ title: folder.title, path: Folders.relWsFolder(folder, wsFolder) })));
|
||||
|
||||
let folderDetails = folders.map(folder => ({
|
||||
...folder,
|
||||
path: Folders.relWsFolder(folder, wsFolder)
|
||||
}));
|
||||
|
||||
await Settings.update(SETTINGS_CONTENT_PAGE_FOLDERS, folderDetails, true);
|
||||
|
||||
// Reinitialize the folder listeners
|
||||
PagesListener.startWatchers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the absolute file path
|
||||
* @param filePath
|
||||
* @returns
|
||||
*/
|
||||
public static getAbsFilePath(filePath: string): string {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = filePath.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ""));
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +328,7 @@ export class Folders {
|
||||
*/
|
||||
private static absWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, wsFolder?.fsPath || "");
|
||||
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ""));
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
@@ -236,8 +341,8 @@ export class Folders {
|
||||
*/
|
||||
private static relWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = folder.path.replace(wsFolder?.fsPath || "", WORKSPACE_PLACEHOLDER);
|
||||
let absPath = parseWinPath(folder.path).replace(parseWinPath(wsFolder?.fsPath || ""), WORKSPACE_PLACEHOLDER);
|
||||
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME } from './../constants/settings';
|
||||
import { Telemetry } from './../helpers/Telemetry';
|
||||
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME, CONTEXT, TelemetryEvent } from './../constants';
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { join } from "path";
|
||||
import { commands, env, Uri, ViewColumn, window } from "vscode";
|
||||
import { Settings } from '../helpers';
|
||||
import { PreviewSettings } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
import { Article } from '.';
|
||||
import { urlJoin } from 'url-join-ts';
|
||||
|
||||
|
||||
export class Preview {
|
||||
@@ -32,12 +35,25 @@ export class Preview {
|
||||
const article = editor ? ArticleHelper.getFrontMatter(editor) : null;
|
||||
let slug = article?.data ? article.data.slug : "";
|
||||
|
||||
if (settings.pathname) {
|
||||
let pathname = settings.pathname;
|
||||
if (article?.data) {
|
||||
const contentType = ArticleHelper.getContentType(article.data);
|
||||
if (contentType && contentType.previewPath) {
|
||||
pathname = contentType.previewPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
slug = Article.getSlug();
|
||||
}
|
||||
|
||||
if (pathname) {
|
||||
const articleDate = ArticleHelper.getDate(article);
|
||||
|
||||
try {
|
||||
slug = join(format(articleDate || new Date(), settings.pathname), slug);
|
||||
slug = join(format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string), slug);
|
||||
} catch (error) {
|
||||
slug = join(settings.pathname, slug);
|
||||
slug = join(pathname, slug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,11 +129,13 @@ export class Preview {
|
||||
</head>
|
||||
<body>
|
||||
<div class="slug">
|
||||
<input type="text" value="${join(localhostUrl.toString(), slug)}" disabled />
|
||||
<input type="text" value="${urlJoin(localhostUrl.toString(), slug || '')}" disabled />
|
||||
</div>
|
||||
<iframe src="${join(localhostUrl.toString(), slug)}" >
|
||||
<iframe src="${urlJoin(localhostUrl.toString(), slug || '')}" >
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
Telemetry.send(TelemetryEvent.openPreview);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,4 +150,4 @@ export class Preview {
|
||||
pathname
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Telemetry } from './../helpers/Telemetry';
|
||||
import { workspace, Uri } from "vscode";
|
||||
import { CONFIG_KEY, SETTING_TEMPLATES_FOLDER } from "../constants";
|
||||
import { join } from "path";
|
||||
import * as fs from "fs";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { Template } from "./Template";
|
||||
import { Folders } from "./Folders";
|
||||
import { Settings } from "../helpers";
|
||||
import { SETTINGS_CONTENT_DEFAULT_FILETYPE, TelemetryEvent } from "../constants";
|
||||
|
||||
export class Project {
|
||||
|
||||
private static content = `---
|
||||
title: "{{name}}"
|
||||
slug: "/{{kebabCase name}}/"
|
||||
title:
|
||||
slug:
|
||||
description:
|
||||
author:
|
||||
date: 2019-08-22T15:20:28.000Z
|
||||
@@ -28,6 +29,7 @@ categories: []
|
||||
public static async init(sampleTemplate: boolean = true) {
|
||||
try {
|
||||
Settings.createTeamSettings();
|
||||
const fileType = Settings.get<string>(SETTINGS_CONTENT_DEFAULT_FILETYPE);
|
||||
|
||||
const folder = Template.getSettings();
|
||||
const templatePath = Project.templatePath();
|
||||
@@ -36,7 +38,7 @@ categories: []
|
||||
return;
|
||||
}
|
||||
|
||||
const article = Uri.file(join(templatePath.fsPath, "article.md"));
|
||||
const article = Uri.file(join(templatePath.fsPath, `article.${fileType}`));
|
||||
|
||||
if (!fs.existsSync(templatePath.fsPath)) {
|
||||
await workspace.fs.createDirectory(templatePath);
|
||||
@@ -46,6 +48,8 @@ categories: []
|
||||
fs.writeFileSync(article.fsPath, Project.content, { encoding: "utf-8" });
|
||||
Notifications.info("Project initialized successfully.");
|
||||
}
|
||||
|
||||
Telemetry.send(TelemetryEvent.initialization)
|
||||
} catch (err: any) {
|
||||
Notifications.error(`Sorry, something went wrong - ${err?.message || err}`);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as matter from 'gray-matter';
|
||||
import * as fs from 'fs';
|
||||
import { TaxonomyType } from "../models";
|
||||
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, EXTENSION_NAME } from '../constants';
|
||||
import { ArticleHelper, Settings as SettingsHelper, FilesHelper } from '../helpers';
|
||||
import { TomlEngine, getFmLanguage, getFormatOpts } from '../helpers/TomlEngine';
|
||||
import { FrontMatterParser } from '../parsers';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
|
||||
@@ -90,10 +89,6 @@ export class Settings {
|
||||
const progressNr = allMdFiles.length/100;
|
||||
progress.report({ increment: 0});
|
||||
|
||||
// Get language options
|
||||
const language = getFmLanguage();
|
||||
const langOpts = getFormatOpts(language);
|
||||
|
||||
let i = 0;
|
||||
for (const file of allMdFiles) {
|
||||
progress.report({ increment: (++i/progressNr) });
|
||||
@@ -102,10 +97,7 @@ export class Settings {
|
||||
const txtData = mdFile.getText();
|
||||
if (txtData) {
|
||||
try {
|
||||
const article = matter(txtData, {
|
||||
...TomlEngine,
|
||||
...langOpts
|
||||
});
|
||||
const article = FrontMatterParser.fromFile(txtData);
|
||||
if (article && article.data) {
|
||||
const { data } = article;
|
||||
const mdTags = data["tags"];
|
||||
@@ -218,13 +210,8 @@ export class Settings {
|
||||
progress.report({ increment: (++i/progressNr) });
|
||||
const mdFile = fs.readFileSync(file.path, { encoding: "utf8" });
|
||||
if (mdFile) {
|
||||
const language = getFmLanguage();
|
||||
const langOpts = getFormatOpts(language);
|
||||
try {
|
||||
const article = matter(mdFile, {
|
||||
...TomlEngine,
|
||||
...langOpts
|
||||
});
|
||||
const article = FrontMatterParser.fromFile(mdFile);
|
||||
if (article && article.data) {
|
||||
const { data } = article;
|
||||
let taxonomies: string[] = data[matterProp];
|
||||
@@ -239,9 +226,7 @@ export class Settings {
|
||||
data[matterProp] = [...new Set(taxonomies)].sort();
|
||||
const spaces = vscode.window.activeTextEditor?.options?.tabSize;
|
||||
// Update the file
|
||||
fs.writeFileSync(file.path, matter.stringify(article.content, article.data, {
|
||||
...TomlEngine,
|
||||
...langOpts,
|
||||
fs.writeFileSync(file.path, FrontMatterParser.toFile(article.content, article.data, {
|
||||
indent: spaces || 2
|
||||
} as DumpOptions as any), { encoding: "utf8" });
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants/settings';
|
||||
import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants';
|
||||
import * as vscode from 'vscode';
|
||||
import { ArticleHelper, SeoHelper, Settings } from '../helpers';
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { DefaultFields } from '../constants';
|
||||
import { ContentType } from '../helpers/ContentType';
|
||||
import { DataListener } from '../listeners/panel';
|
||||
|
||||
export class StatusListener {
|
||||
|
||||
@@ -15,6 +17,11 @@ export class StatusListener {
|
||||
public static async verify(frontMatterSB: vscode.StatusBarItem, collection: vscode.DiagnosticCollection) {
|
||||
const draftMsg = "in draft";
|
||||
const publishMsg = "to publish";
|
||||
|
||||
const draft = ContentType.getDraftField();
|
||||
if (!draft || draft.type !== "boolean") {
|
||||
frontMatterSB.hide();
|
||||
}
|
||||
|
||||
let editor = vscode.window.activeTextEditor;
|
||||
if (editor && ArticleHelper.isMarkdownFile()) {
|
||||
@@ -52,7 +59,7 @@ export class StatusListener {
|
||||
|
||||
const panel = ExplorerView.getInstance();
|
||||
if (panel && panel.visible) {
|
||||
panel.pushMetadata(article!.data);
|
||||
DataListener.pushMetadata(article!.data);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -62,7 +69,7 @@ export class StatusListener {
|
||||
} else {
|
||||
const panel = ExplorerView.getInstance();
|
||||
if (panel && panel.visible) {
|
||||
panel.pushMetadata(null);
|
||||
DataListener.pushMetadata(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { Questions } from './../helpers/Questions';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants';
|
||||
import { format } from 'date-fns';
|
||||
import sanitize from '../helpers/Sanitize';
|
||||
import { SETTINGS_CONTENT_DEFAULT_FILETYPE, SETTING_TEMPLATES_FOLDER, TelemetryEvent } from '../constants';
|
||||
import { ArticleHelper, Settings } from '../helpers';
|
||||
import { Article } from '.';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
import { CONTEXT } from '../constants';
|
||||
import { Project } from './Project';
|
||||
import { Folders } from './Folders';
|
||||
import { ContentType } from '../helpers/ContentType';
|
||||
import { ContentType as IContentType } from '../models';
|
||||
import { PagesListener } from '../listeners/dashboard';
|
||||
import { extname } from 'path';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
|
||||
export class Template {
|
||||
|
||||
@@ -48,6 +52,7 @@ export class Template {
|
||||
public static async generate() {
|
||||
const folder = Template.getSettings();
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const fileType = Settings.get<string>(SETTINGS_CONTENT_DEFAULT_FILETYPE);
|
||||
|
||||
if (folder && editor && ArticleHelper.isMarkdownFile()) {
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
@@ -67,7 +72,7 @@ export class Template {
|
||||
["yes", "no"],
|
||||
{
|
||||
canPickMany: false,
|
||||
placeHolder: `Do you want to keep the article its contents for the template?`,
|
||||
placeHolder: `Do you want to keep the contents for the template?`,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -81,7 +86,7 @@ export class Template {
|
||||
if (templatePath) {
|
||||
let fileContents = ArticleHelper.stringifyFrontMatter(keepContents === "no" ? "" : clonedArticle.content, clonedArticle.data);
|
||||
|
||||
const templateFile = path.join(templatePath.fsPath, `${titleValue}.md`);
|
||||
const templateFile = path.join(templatePath.fsPath, `${titleValue}.${fileType}`);
|
||||
fs.writeFileSync(templateFile, fileContents, { encoding: "utf-8" });
|
||||
|
||||
Notifications.info(`Template created and is now available in your ${folder} folder.`);
|
||||
@@ -94,7 +99,7 @@ export class Template {
|
||||
*/
|
||||
public static async create(folderPath: string) {
|
||||
const folder = Settings.get<string>(SETTING_TEMPLATES_FOLDER);
|
||||
const prefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
const contentTypes = ContentType.getAll();
|
||||
|
||||
if (!folderPath) {
|
||||
Notifications.warning(`Incorrect project folder path retrieved.`);
|
||||
@@ -113,39 +118,34 @@ export class Template {
|
||||
}
|
||||
|
||||
const selectedTemplate = await vscode.window.showQuickPick(templates.map(t => path.basename(t.fsPath)), {
|
||||
placeHolder: `Select the article template to use`
|
||||
placeHolder: `Select the content template to use`
|
||||
});
|
||||
if (!selectedTemplate) {
|
||||
Notifications.warning(`No template selected.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const titleValue = await vscode.window.showInputBox({
|
||||
prompt: `What would you like to use as a title for the new article?`,
|
||||
placeHolder: `Article title`
|
||||
});
|
||||
const titleValue = await Questions.ContentTitle();
|
||||
if (!titleValue) {
|
||||
Notifications.warning(`You did not specify an article title.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the template read
|
||||
const template = templates.find(t => t.fsPath.endsWith(selectedTemplate));
|
||||
if (!template) {
|
||||
Notifications.warning(`Article template could not be found.`);
|
||||
Notifications.warning(`Content template could not be found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileExt = path.parse(selectedTemplate).ext;
|
||||
const sanitizedName = sanitize(titleValue.toLowerCase().replace(/ /g, "-"));
|
||||
let newFileName = `${sanitizedName}${fileExt}`;
|
||||
if (prefix && typeof prefix === "string") {
|
||||
newFileName = `${format(new Date(), prefix)}-${newFileName}`;
|
||||
const templateData = ArticleHelper.getFrontMatterByPath(template.fsPath);
|
||||
let contentType: IContentType | undefined;
|
||||
if (templateData && templateData.data && templateData.data.type) {
|
||||
contentType = contentTypes?.find(t => t.name === templateData.data.type);
|
||||
}
|
||||
|
||||
const newFilePath = path.join(folderPath, newFileName);
|
||||
if (fs.existsSync(newFilePath)) {
|
||||
Notifications.warning(`File already exists, please remove it before creating a new one with the same title.`);
|
||||
const fileExtension = extname(template.fsPath).replace(".", "");
|
||||
let newFilePath: string | undefined = ArticleHelper.createContent(contentType, folderPath, titleValue, fileExtension);
|
||||
if (!newFilePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,13 +160,7 @@ export class Template {
|
||||
}
|
||||
|
||||
if (frontMatter.data) {
|
||||
const fmData = frontMatter.data;
|
||||
if (typeof fmData.title !== "undefined") {
|
||||
fmData.title = titleValue;
|
||||
}
|
||||
if (typeof fmData.slug !== "undefined") {
|
||||
fmData.slug = sanitizedName;
|
||||
}
|
||||
frontMatter.data = ArticleHelper.updatePlaceholders(frontMatter.data, titleValue);
|
||||
|
||||
frontMatter = Article.updateDate(frontMatter);
|
||||
|
||||
@@ -180,7 +174,12 @@ export class Template {
|
||||
vscode.window.showTextDocument(txtDoc);
|
||||
}
|
||||
|
||||
Notifications.info(`Your new article has been created.`);
|
||||
Notifications.info(`Your new content has been created.`);
|
||||
|
||||
Telemetry.send(TelemetryEvent.createContentFromTemplate);
|
||||
|
||||
// Trigger a refresh for the dashboard
|
||||
PagesListener.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
230
src/commands/Wysiwyg.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { commands, window, Selection, QuickPickItem } from "vscode";
|
||||
import { COMMAND_NAME, CONTEXT, SETTINGS_CONTENT_WYSIWYG } from "../constants";
|
||||
import { Settings } from "../helpers";
|
||||
|
||||
enum MarkupType {
|
||||
bold = 1,
|
||||
italic,
|
||||
strikethrough,
|
||||
code,
|
||||
codeblock,
|
||||
blockquote,
|
||||
heading,
|
||||
unorderedList,
|
||||
orderedList,
|
||||
taskList
|
||||
}
|
||||
|
||||
export class Wysiwyg {
|
||||
|
||||
/**
|
||||
* Registers the markup commands for the WYSIWYG controls
|
||||
* @param subscriptions
|
||||
* @returns
|
||||
*/
|
||||
public static async registerCommands(subscriptions: any) {
|
||||
|
||||
const wysiwygEnabled = Settings.get(SETTINGS_CONTENT_WYSIWYG);
|
||||
|
||||
if (!wysiwygEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await commands.executeCommand('setContext', CONTEXT.wysiwyg, true);
|
||||
|
||||
// Surrounding markup
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.bold, () => this.addMarkup(MarkupType.bold)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.italic, () => this.addMarkup(MarkupType.italic)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.strikethrough, () => this.addMarkup(MarkupType.strikethrough)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.code, () => this.addMarkup(MarkupType.code)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.codeblock, () => this.addMarkup(MarkupType.codeblock)));
|
||||
|
||||
// Prefix markup
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.heading, () => this.addMarkup(MarkupType.heading)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.blockquote, () => this.addMarkup(MarkupType.blockquote)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.unorderedlist, () => this.addMarkup(MarkupType.unorderedList)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.orderedlist, () => this.addMarkup(MarkupType.orderedList)));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.taskList, () => this.addMarkup(MarkupType.taskList)));
|
||||
|
||||
// Options
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.options, async () => {
|
||||
const qpItems: QuickPickItem[] = [
|
||||
{ label: "$(list-unordered) Unordered list", detail: "Add an unordered list", alwaysShow: true, },
|
||||
{ label: "$(list-ordered) Ordered list", detail: "Add an ordered list", alwaysShow: true },
|
||||
{ label: "$(tasklist) Task list", detail: "Add a task list", alwaysShow: true },
|
||||
{ label: "$(code) Code", detail: "Add inline code snippet", alwaysShow: true },
|
||||
{ label: "$(symbol-namespace) Code block", detail: "Add a code block", alwaysShow: true },
|
||||
]
|
||||
|
||||
const option = await window.showQuickPick([ ...qpItems ], {
|
||||
placeHolder: "Which type of markup would you like to insert?",
|
||||
canPickMany: false,
|
||||
ignoreFocusOut: false,
|
||||
});
|
||||
|
||||
if (option) {
|
||||
if (option.label === qpItems[0].label) {
|
||||
await this.addMarkup(MarkupType.unorderedList);
|
||||
} else if (option.label === qpItems[1].label) {
|
||||
await this.addMarkup(MarkupType.orderedList);
|
||||
} else if (option.label === qpItems[2].label) {
|
||||
await this.addMarkup(MarkupType.taskList);
|
||||
} else if (option.label === qpItems[3].label) {
|
||||
await this.addMarkup(MarkupType.code);
|
||||
} else if (option.label === qpItems[4].label) {
|
||||
await this.addMarkup(MarkupType.codeblock);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the markup to the content
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
private static async addMarkup(type: MarkupType) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selection = editor.selection;
|
||||
const hasTextSelection = !selection.isEmpty;
|
||||
|
||||
const markers = this.getMarkers(type);
|
||||
if (!markers) {
|
||||
return;
|
||||
}
|
||||
|
||||
const crntSelection = selection.active;
|
||||
|
||||
if (hasTextSelection) {
|
||||
// Replace the selection and surround with the markup
|
||||
const selectionText = editor.document.getText(selection);
|
||||
const txt = await this.insertText(markers, type, selectionText);
|
||||
|
||||
editor.edit(builder => {
|
||||
builder.replace(selection, txt);
|
||||
});
|
||||
} else {
|
||||
const txt = await this.insertText(markers, type);
|
||||
|
||||
// Insert the markers where cursor is located.
|
||||
const markerLength = this.isMarkupWrapping(type) ? txt.length + 1 : markers.length;
|
||||
let newPosition = crntSelection.with(crntSelection.line, crntSelection.character + markerLength);
|
||||
|
||||
await editor.edit(builder => {
|
||||
builder.insert(newPosition, txt);
|
||||
});
|
||||
|
||||
if (type === MarkupType.codeblock) {
|
||||
newPosition = crntSelection.with(crntSelection.line + 1, 0);
|
||||
}
|
||||
|
||||
editor.selection = new Selection(newPosition, newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the text will be wrapped
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
private static isMarkupWrapping(type: MarkupType) {
|
||||
return (
|
||||
type === MarkupType.blockquote ||
|
||||
type === MarkupType.heading ||
|
||||
type === MarkupType.unorderedList ||
|
||||
type === MarkupType.orderedList ||
|
||||
type === MarkupType.taskList
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert text at the current cursor position
|
||||
*/
|
||||
private static async insertText(marker: string | undefined, type: MarkupType, text: string | null = null) {
|
||||
const crntText = text || this.lineBreak(type);
|
||||
|
||||
if (this.isMarkupWrapping(type)) {
|
||||
if (type === MarkupType.heading) {
|
||||
const headingLvl = await window.showQuickPick([
|
||||
"Heading 1",
|
||||
"Heading 2",
|
||||
"Heading 3",
|
||||
"Heading 4",
|
||||
"Heading 5",
|
||||
"Heading 6"
|
||||
], {
|
||||
canPickMany: false,
|
||||
placeHolder: "Which heading level do you want to insert?",
|
||||
ignoreFocusOut: false
|
||||
});
|
||||
|
||||
if (headingLvl) {
|
||||
const headingNr = parseInt(headingLvl.replace("Heading ", ""));
|
||||
return `${Array(headingNr + 1).join(marker)} ${crntText}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === MarkupType.unorderedList || type === MarkupType.taskList) {
|
||||
const lines = crntText.split("\n").map(line => `${marker} ${line}`);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
if (type === MarkupType.orderedList) {
|
||||
const lines = crntText.split("\n").map((line, idx) => `${idx+1}. ${line}`);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
return `${marker} ${crntText}`;
|
||||
} else {
|
||||
return `${marker}${crntText}${marker}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if linebreak needs to be added
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
private static lineBreak(type: MarkupType) {
|
||||
if (type === MarkupType.codeblock) {
|
||||
return `\n\n`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the type of markers
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
private static getMarkers(type: MarkupType) {
|
||||
switch(type) {
|
||||
case MarkupType.bold:
|
||||
return `**`;
|
||||
case MarkupType.italic:
|
||||
return `*`;
|
||||
case MarkupType.strikethrough:
|
||||
return `~~`;
|
||||
case MarkupType.code:
|
||||
return "`";
|
||||
case MarkupType.codeblock:
|
||||
return "```";
|
||||
case MarkupType.blockquote:
|
||||
return ">";
|
||||
case MarkupType.heading:
|
||||
return "#";
|
||||
case MarkupType.unorderedList:
|
||||
return "-";
|
||||
case MarkupType.orderedList:
|
||||
return "1.";
|
||||
case MarkupType.taskList:
|
||||
return "- [ ]";
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/components/icons/CompressIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ICompressIconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CompressIcon: React.FunctionComponent<ICompressIconProps> = ({className}: React.PropsWithChildren<ICompressIconProps>) => {
|
||||
return (
|
||||
<svg className={className || ""} aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
41
src/components/uniforms-frontmatter/AutoField.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as invariant from 'invariant';
|
||||
import { createAutoField } from 'uniforms';
|
||||
import { PreviewImageField } from '../../panelWebView/components/Fields/PreviewImageField';
|
||||
export { AutoFieldProps } from 'uniforms';
|
||||
|
||||
import BoolField from './BoolField';
|
||||
import DateField from './DateField';
|
||||
import ListField from './ListField';
|
||||
import NestField from './NestField';
|
||||
import NumField from './NumField';
|
||||
import RadioField from './RadioField';
|
||||
import SelectField from './SelectField';
|
||||
import TextField from './TextField';
|
||||
|
||||
const AutoField = createAutoField(props => {
|
||||
|
||||
if (props.allowedValues) {
|
||||
return props.checkboxes && props.fieldType !== Array
|
||||
? RadioField
|
||||
: SelectField;
|
||||
}
|
||||
|
||||
switch (props.fieldType) {
|
||||
case Array:
|
||||
return ListField;
|
||||
case Boolean:
|
||||
return BoolField;
|
||||
case Date:
|
||||
return DateField;
|
||||
case Number:
|
||||
return NumField;
|
||||
case Object:
|
||||
return NestField;
|
||||
case String:
|
||||
return TextField;
|
||||
}
|
||||
|
||||
return invariant(false, 'Unsupported field type: %s', props.fieldType);
|
||||
});
|
||||
|
||||
export default AutoField;
|
||||
29
src/components/uniforms-frontmatter/AutoFields.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ComponentType, createElement, Fragment } from 'react';
|
||||
import { useForm } from 'uniforms';
|
||||
|
||||
import AutoField from './AutoField';
|
||||
|
||||
export type AutoFieldsProps = {
|
||||
autoField?: ComponentType<{ name: string }>;
|
||||
element?: ComponentType | string;
|
||||
fields?: string[];
|
||||
omitFields?: string[];
|
||||
};
|
||||
|
||||
export default function AutoFields({
|
||||
autoField = AutoField,
|
||||
element = Fragment,
|
||||
fields,
|
||||
omitFields = [],
|
||||
...props
|
||||
}: AutoFieldsProps) {
|
||||
const { schema } = useForm();
|
||||
|
||||
return createElement(
|
||||
element,
|
||||
props,
|
||||
(fields ?? schema.getSubfields())
|
||||
.filter(field => !omitFields.includes(field))
|
||||
.map(field => createElement(autoField, { key: field, name: field })),
|
||||
);
|
||||
}
|
||||
13
src/components/uniforms-frontmatter/AutoForm.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { AutoForm } from 'uniforms';
|
||||
|
||||
import ValidatedQuickForm from './ValidatedQuickForm';
|
||||
|
||||
function Auto(parent: any) {
|
||||
class _ extends AutoForm.Auto(parent) {
|
||||
static Auto = Auto;
|
||||
}
|
||||
|
||||
return _ as unknown as AutoForm;
|
||||
}
|
||||
|
||||
export default Auto(ValidatedQuickForm);
|
||||
13
src/components/uniforms-frontmatter/BaseForm.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { BaseForm } from 'uniforms';
|
||||
|
||||
function Unstyled(parent: any) {
|
||||
class _ extends parent {
|
||||
static Unstyled = Unstyled;
|
||||
|
||||
static displayName = `Unstyled${parent.displayName}`;
|
||||
}
|
||||
|
||||
return _ as unknown as typeof BaseForm;
|
||||
}
|
||||
|
||||
export default Unstyled(BaseForm);
|
||||
52
src/components/uniforms-frontmatter/BoolField.css
Normal file
@@ -0,0 +1,52 @@
|
||||
.field__toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.field__toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.field__toggle__slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--frontmatter-toggle-secondaryBackground, var(--vscode-button-secondaryBackground));
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.field__toggle__slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .field__toggle__slider {
|
||||
background-color: var(--frontmatter-toggle-background, var(--vscode-button-background));
|
||||
}
|
||||
|
||||
input:focus + .field__toggle__slider {
|
||||
box-shadow: 0 0 1px var(--frontmatter-toggle-background, var(--vscode-button-background));
|
||||
}
|
||||
|
||||
input:checked + .field__toggle__slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
44
src/components/uniforms-frontmatter/BoolField.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import * as React from 'react';
|
||||
import { Ref } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
import './BoolField.css';
|
||||
import { LabelField } from './LabelField';
|
||||
|
||||
export type BoolFieldProps = HTMLFieldProps<
|
||||
boolean,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement> }
|
||||
>;
|
||||
|
||||
function Bool({
|
||||
disabled,
|
||||
id,
|
||||
inputRef,
|
||||
label,
|
||||
name,
|
||||
onChange,
|
||||
readOnly,
|
||||
value,
|
||||
...props
|
||||
}: BoolFieldProps) {
|
||||
return (
|
||||
<div {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={id} required={props.required} />
|
||||
|
||||
<label className="field__toggle">
|
||||
<input
|
||||
checked={value || false}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
name={name}
|
||||
onChange={() => !disabled && !readOnly && onChange(!value)}
|
||||
ref={inputRef}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span className="field__toggle__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<BoolFieldProps>(Bool, { kind: 'leaf' });
|
||||
57
src/components/uniforms-frontmatter/DateField.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import * as React from 'react';
|
||||
import { Ref } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
|
||||
/* istanbul ignore next */
|
||||
const DateConstructor = (typeof global === 'object' ? global : window).Date;
|
||||
const dateFormat = (value?: Date) => value?.toISOString().slice(0, -8);
|
||||
|
||||
export type DateFieldProps = HTMLFieldProps<
|
||||
Date,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement>; max?: Date; min?: Date }
|
||||
>;
|
||||
|
||||
function Date({
|
||||
disabled,
|
||||
id,
|
||||
inputRef,
|
||||
label,
|
||||
max,
|
||||
min,
|
||||
name,
|
||||
onChange,
|
||||
placeholder,
|
||||
readOnly,
|
||||
value,
|
||||
...props
|
||||
}: DateFieldProps) {
|
||||
return (
|
||||
<div {...filterDOMProps(props)}>
|
||||
{label && <label htmlFor={id}>{label}</label>}
|
||||
|
||||
<input
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
max={dateFormat(max)}
|
||||
min={dateFormat(min)}
|
||||
name={name}
|
||||
onChange={event => {
|
||||
const date = new DateConstructor(event.target.valueAsNumber);
|
||||
if (date.getFullYear() < 10000) {
|
||||
onChange(date);
|
||||
} else if (isNaN(event.target.valueAsNumber)) {
|
||||
onChange(undefined);
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
readOnly={readOnly}
|
||||
ref={inputRef}
|
||||
type="datetime-local"
|
||||
value={dateFormat(value) ?? ''}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<DateFieldProps>(Date, { kind: 'leaf' });
|
||||
19
src/components/uniforms-frontmatter/ErrorField.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { HTMLProps } from 'react';
|
||||
import { Override, connectField, filterDOMProps } from 'uniforms';
|
||||
|
||||
export type ErrorFieldProps = Override<
|
||||
Omit<HTMLProps<HTMLDivElement>, 'onChange'>,
|
||||
{ error?: any; errorMessage?: string }
|
||||
>;
|
||||
|
||||
function Error({ children, error, errorMessage, ...props }: ErrorFieldProps) {
|
||||
return !error ? null : (
|
||||
<div {...filterDOMProps(props)}>{children || errorMessage}</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<ErrorFieldProps>(Error, {
|
||||
initialValue: false,
|
||||
kind: 'leaf',
|
||||
});
|
||||
18
src/components/uniforms-frontmatter/ErrorsField.css
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
.autoform-error {
|
||||
background-color: var(--frontmatter-error-background, var(--vscode-inputValidation-errorBackground));
|
||||
border: 1px solid var(--frontmatter-error-border, var(--vscode-inputValidation-errorBorder));
|
||||
border-radius: 2px;
|
||||
margin: 20px 0px;
|
||||
padding: 10px;
|
||||
color: var(--frontmatter-error-foreground, var(--vscode-editor-foreground));
|
||||
|
||||
ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
23
src/components/uniforms-frontmatter/ErrorsField.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { HTMLProps } from 'react';
|
||||
import { filterDOMProps, useForm } from 'uniforms';
|
||||
import './ErrorsField.css';
|
||||
|
||||
export type ErrorsFieldProps = HTMLProps<HTMLDivElement>;
|
||||
|
||||
export default function ErrorsField(props: ErrorsFieldProps) {
|
||||
const { error, schema } = useForm();
|
||||
return !error && !props.children ? null : (
|
||||
<div className='autoform-error'>
|
||||
<div {...filterDOMProps(props)}>
|
||||
{props.children}
|
||||
|
||||
<ul>
|
||||
{schema.getErrorMessages(error).map((message, index) => (
|
||||
<li key={index}>{message}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
src/components/uniforms-frontmatter/HiddenField.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import { HTMLProps, Ref, useEffect } from 'react';
|
||||
import { Override, filterDOMProps, useField } from 'uniforms';
|
||||
|
||||
export type HiddenFieldProps = Override<
|
||||
HTMLProps<HTMLInputElement>,
|
||||
{
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
name: string;
|
||||
noDOM?: boolean;
|
||||
value?: any;
|
||||
}
|
||||
>;
|
||||
|
||||
export default function HiddenField({ value, ...rawProps }: HiddenFieldProps) {
|
||||
const props = useField(rawProps.name, rawProps, { initialValue: false })[0];
|
||||
|
||||
useEffect(() => {
|
||||
if (value !== undefined && value !== props.value) {
|
||||
props.onChange(value);
|
||||
}
|
||||
});
|
||||
|
||||
return props.noDOM ? null : (
|
||||
<input
|
||||
disabled={props.disabled}
|
||||
name={props.name}
|
||||
readOnly={props.readOnly}
|
||||
ref={props.inputRef}
|
||||
type="hidden"
|
||||
value={value ?? props.value ?? ''}
|
||||
{...filterDOMProps(props)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
14
src/components/uniforms-frontmatter/LabelField.css
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
.autoform__label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
|
||||
.autoform__label__required {
|
||||
color: var(--vscode-inputValidation-errorBorder);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
20
src/components/uniforms-frontmatter/LabelField.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import './LabelField.css';
|
||||
|
||||
export interface ILabelFieldProps {
|
||||
id: string;
|
||||
label: string | ReactNode;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export const LabelField: React.FunctionComponent<ILabelFieldProps> = ({ label, id, required }: React.PropsWithChildren<ILabelFieldProps>) => {
|
||||
return (
|
||||
label ? (
|
||||
<label className="autoform__label" htmlFor={id}>
|
||||
{label}
|
||||
{required && <span title='Required field' className='autoform__label__required'>*</span>}
|
||||
</label>
|
||||
) : null
|
||||
);
|
||||
};
|
||||
21
src/components/uniforms-frontmatter/ListAddField.css
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
.autoform__list_add_field {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
border: 1px dashed var(--frontmatter-field-border, var(--vscode-editor-foreground));
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: .5rem;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--frontmatter-field-borderActive, var(--vscode-button-background));
|
||||
color: var(--frontmatter-field-borderActive, var(--vscode-button-background));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
63
src/components/uniforms-frontmatter/ListAddField.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { PlusIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
HTMLFieldProps,
|
||||
connectField,
|
||||
filterDOMProps,
|
||||
joinName,
|
||||
useField,
|
||||
} from 'uniforms';
|
||||
import './ListAddField.css';
|
||||
|
||||
export type ListAddFieldProps = HTMLFieldProps<
|
||||
unknown,
|
||||
HTMLSpanElement,
|
||||
{ initialCount?: number }
|
||||
>;
|
||||
|
||||
function ListAdd({
|
||||
disabled,
|
||||
initialCount,
|
||||
name,
|
||||
readOnly,
|
||||
value,
|
||||
...props
|
||||
}: ListAddFieldProps) {
|
||||
const nameParts = joinName(null, name);
|
||||
const parentName = joinName(nameParts.slice(0, -1));
|
||||
const parent = useField<
|
||||
{ initialCount?: number; maxCount?: number },
|
||||
unknown[]
|
||||
>(parentName, { initialCount }, { absoluteName: true })[0];
|
||||
|
||||
const limitNotReached =
|
||||
!disabled && !(parent.maxCount! <= parent.value!.length);
|
||||
|
||||
function onAction(event: React.KeyboardEvent | React.MouseEvent) {
|
||||
if (
|
||||
limitNotReached &&
|
||||
!readOnly &&
|
||||
(!('key' in event) || event.key === 'Enter')
|
||||
) {
|
||||
parent.onChange(parent.value!.concat([Object.assign({}, value)]));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className='autoform__list_add_field'
|
||||
{...filterDOMProps(props as any)}
|
||||
onClick={onAction}
|
||||
onKeyDown={onAction}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<PlusIcon />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<ListAddFieldProps>(ListAdd, {
|
||||
initialValue: false,
|
||||
kind: 'leaf',
|
||||
});
|
||||
27
src/components/uniforms-frontmatter/ListDelField.css
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
.autoform__list_del_field {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: .5rem;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
background: var(--frontmatter-list-border, var(--vscode-editor-foreground));
|
||||
width: 100%;
|
||||
margin-right: .5rem;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
}
|
||||
63
src/components/uniforms-frontmatter/ListDelField.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { TrashIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
HTMLFieldProps,
|
||||
connectField,
|
||||
filterDOMProps,
|
||||
joinName,
|
||||
useField,
|
||||
} from 'uniforms';
|
||||
import './ListDelField.css';
|
||||
|
||||
export type ListDelFieldProps = HTMLFieldProps<unknown, HTMLSpanElement>;
|
||||
|
||||
function ListDel({ disabled, name, readOnly, ...props }: ListDelFieldProps) {
|
||||
const nameParts = joinName(null, name);
|
||||
const nameIndex = +nameParts[nameParts.length - 1];
|
||||
const parentName = joinName(nameParts.slice(0, -1));
|
||||
const parent = useField<{ minCount?: number }, unknown[]>(
|
||||
parentName,
|
||||
{},
|
||||
{ absoluteName: true },
|
||||
)[0];
|
||||
|
||||
const limitNotReached =
|
||||
!disabled && !(parent.minCount! >= parent.value!.length);
|
||||
|
||||
function onAction(
|
||||
event:
|
||||
| React.KeyboardEvent<HTMLSpanElement>
|
||||
| React.MouseEvent<HTMLSpanElement, MouseEvent>,
|
||||
) {
|
||||
if (
|
||||
limitNotReached &&
|
||||
!readOnly &&
|
||||
(!('key' in event) || event.key === 'Enter')
|
||||
) {
|
||||
const value = parent.value!.slice();
|
||||
value.splice(nameIndex, 1);
|
||||
parent.onChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className='autoform__list_del_field'
|
||||
{...filterDOMProps(props)}
|
||||
onClick={onAction}
|
||||
onKeyDown={onAction}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className='line'></div>
|
||||
<TrashIcon />
|
||||
</span>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<ListDelFieldProps>(ListDel, {
|
||||
initialValue: false,
|
||||
kind: 'leaf',
|
||||
});
|
||||
8
src/components/uniforms-frontmatter/ListField.css
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
.autoform__list_field {
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 1rem;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--frontmatter-list-border, rgba(255, 255, 255, 0.2));
|
||||
}
|
||||
46
src/components/uniforms-frontmatter/ListField.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import * as React from 'react';
|
||||
import { Children, cloneElement, isValidElement } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
|
||||
import ListAddField from './ListAddField';
|
||||
import ListItemField from './ListItemField';
|
||||
|
||||
import './ListField.css';
|
||||
import { LabelField } from './LabelField';
|
||||
|
||||
export type ListFieldProps = HTMLFieldProps<
|
||||
unknown[],
|
||||
HTMLDivElement,
|
||||
{ initialCount?: number; itemProps?: object }
|
||||
>;
|
||||
|
||||
function List({
|
||||
children = <ListItemField name="$" />,
|
||||
initialCount,
|
||||
itemProps,
|
||||
label,
|
||||
value,
|
||||
...props
|
||||
}: ListFieldProps) {
|
||||
return (
|
||||
<div className="autoform__list_field" {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={props.id} required={props.required} />
|
||||
|
||||
{value?.map((item, itemIndex) =>
|
||||
Children.map(children, (child, childIndex) =>
|
||||
isValidElement(child)
|
||||
? cloneElement(child, {
|
||||
key: `${itemIndex}-${childIndex}`,
|
||||
name: (child.props.name || "").replace('$', '' + itemIndex),
|
||||
...itemProps,
|
||||
})
|
||||
: child,
|
||||
),
|
||||
)}
|
||||
|
||||
<ListAddField initialCount={initialCount} name="$" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<ListFieldProps>(List);
|
||||
23
src/components/uniforms-frontmatter/ListItemField.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { connectField } from 'uniforms';
|
||||
|
||||
import AutoField from './AutoField';
|
||||
import ListDelField from './ListDelField';
|
||||
|
||||
export type ListItemFieldProps = { children?: ReactNode; value?: unknown };
|
||||
|
||||
function ListItem({
|
||||
children = <AutoField label={null} name="" />,
|
||||
}: ListItemFieldProps) {
|
||||
return (
|
||||
<div>
|
||||
<ListDelField name="" />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<ListItemFieldProps>(ListItem, {
|
||||
initialValue: false,
|
||||
});
|
||||
41
src/components/uniforms-frontmatter/LongTextField.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as React from 'react';
|
||||
import { Ref } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
|
||||
export type LongTextFieldProps = HTMLFieldProps<
|
||||
string,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLTextAreaElement> }
|
||||
>;
|
||||
|
||||
function LongText({
|
||||
disabled,
|
||||
id,
|
||||
inputRef,
|
||||
label,
|
||||
name,
|
||||
onChange,
|
||||
placeholder,
|
||||
readOnly,
|
||||
value,
|
||||
...props
|
||||
}: LongTextFieldProps) {
|
||||
return (
|
||||
<div {...filterDOMProps(props)}>
|
||||
{label && <label htmlFor={id}>{label}</label>}
|
||||
|
||||
<textarea
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
name={name}
|
||||
onChange={event => onChange(event.target.value)}
|
||||
placeholder={placeholder}
|
||||
readOnly={readOnly}
|
||||
ref={inputRef}
|
||||
value={value ?? ''}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<LongTextFieldProps>(LongText, { kind: 'leaf' });
|
||||
32
src/components/uniforms-frontmatter/NestField.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
|
||||
import AutoField from './AutoField';
|
||||
import { LabelField } from './LabelField';
|
||||
|
||||
export type NestFieldProps = HTMLFieldProps<
|
||||
object,
|
||||
HTMLDivElement,
|
||||
{ itemProps?: object }
|
||||
>;
|
||||
|
||||
function Nest({
|
||||
children,
|
||||
fields,
|
||||
itemProps,
|
||||
label,
|
||||
...props
|
||||
}: NestFieldProps) {
|
||||
return (
|
||||
<div {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={props.id} required={props.required} />
|
||||
|
||||
{children ||
|
||||
fields.map(field => (
|
||||
<AutoField key={field} name={field} {...itemProps} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<NestFieldProps>(Nest);
|
||||
54
src/components/uniforms-frontmatter/NumField.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import * as React from 'react';
|
||||
import { Ref } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
import { LabelField } from './LabelField';
|
||||
|
||||
export type NumFieldProps = HTMLFieldProps<
|
||||
number,
|
||||
HTMLDivElement,
|
||||
{ decimal?: boolean; inputRef?: Ref<HTMLInputElement> }
|
||||
>;
|
||||
|
||||
function Num({
|
||||
decimal,
|
||||
disabled,
|
||||
id,
|
||||
inputRef,
|
||||
label,
|
||||
max,
|
||||
min,
|
||||
name,
|
||||
onChange,
|
||||
placeholder,
|
||||
readOnly,
|
||||
step,
|
||||
value,
|
||||
...props
|
||||
}: NumFieldProps) {
|
||||
return (
|
||||
<div {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={id} required={props.required} />
|
||||
|
||||
<input
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
max={max}
|
||||
min={min}
|
||||
name={name}
|
||||
onChange={event => {
|
||||
const parse = decimal ? parseFloat : parseInt;
|
||||
const value = parse(event.target.value);
|
||||
onChange(isNaN(value) ? undefined : value);
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
readOnly={readOnly}
|
||||
ref={inputRef}
|
||||
step={step || (decimal ? 0.01 : 1)}
|
||||
type="number"
|
||||
value={value ?? ''}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<NumFieldProps>(Num, { kind: 'leaf' });
|
||||
28
src/components/uniforms-frontmatter/QuickForm.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { QuickForm } from 'uniforms';
|
||||
|
||||
import AutoField from './AutoField';
|
||||
import BaseForm from './BaseForm';
|
||||
import ErrorsField from './ErrorsField';
|
||||
import SubmitField from './SubmitField';
|
||||
|
||||
function Quick(parent: any) {
|
||||
class _ extends QuickForm.Quick(parent) {
|
||||
static Quick = Quick;
|
||||
|
||||
getAutoField() {
|
||||
return AutoField;
|
||||
}
|
||||
|
||||
getErrorsField() {
|
||||
return ErrorsField;
|
||||
}
|
||||
|
||||
getSubmitField() {
|
||||
return SubmitField;
|
||||
}
|
||||
}
|
||||
|
||||
return _ as unknown as QuickForm;
|
||||
}
|
||||
|
||||
export default Quick(BaseForm);
|
||||
62
src/components/uniforms-frontmatter/RadioField.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import omit = require('lodash.omit');
|
||||
import * as React from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
import { LabelField } from './LabelField';
|
||||
|
||||
const base64: typeof btoa =
|
||||
typeof btoa === 'undefined'
|
||||
? /* istanbul ignore next */ x => Buffer.from(x).toString('base64')
|
||||
: btoa;
|
||||
const escape = (x: string) => base64(encodeURIComponent(x)).replace(/=+$/, '');
|
||||
|
||||
export type RadioFieldProps = HTMLFieldProps<
|
||||
string,
|
||||
HTMLDivElement,
|
||||
{
|
||||
allowedValues?: string[];
|
||||
checkboxes?: boolean;
|
||||
transform?: (value: string) => string;
|
||||
}
|
||||
>;
|
||||
|
||||
function Radio({
|
||||
allowedValues,
|
||||
disabled,
|
||||
id,
|
||||
label,
|
||||
name,
|
||||
onChange,
|
||||
readOnly,
|
||||
transform,
|
||||
value,
|
||||
...props
|
||||
}: RadioFieldProps) {
|
||||
return (
|
||||
<div {...omit(filterDOMProps(props), ['checkboxes'])}>
|
||||
<LabelField label={label} id={id} required={props.required} />
|
||||
|
||||
{allowedValues?.map(item => (
|
||||
<div key={item}>
|
||||
<input
|
||||
checked={item === value}
|
||||
disabled={disabled}
|
||||
id={`${id}-${escape(item)}`}
|
||||
name={name}
|
||||
onChange={() => {
|
||||
if (!readOnly) {
|
||||
onChange(item);
|
||||
}
|
||||
}}
|
||||
type="radio"
|
||||
/>
|
||||
|
||||
<label htmlFor={`${id}-${escape(item)}`}>
|
||||
{transform ? transform(item) : item}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<RadioFieldProps>(Radio, { kind: 'leaf' });
|
||||
5
src/components/uniforms-frontmatter/SelectField.css
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
.autoform__select_field {
|
||||
color: var(--frontmatter-select-foreground, var(--vscode-editor-foreground));
|
||||
}
|
||||
110
src/components/uniforms-frontmatter/SelectField.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import xor = require('lodash.xor');
|
||||
import * as React from 'react';
|
||||
import { Ref } from 'react';
|
||||
import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
|
||||
import { LabelField } from './LabelField';
|
||||
import './SelectField.css';
|
||||
|
||||
const base64: typeof btoa =
|
||||
typeof btoa === 'undefined'
|
||||
? /* istanbul ignore next */ x => Buffer.from(x).toString('base64')
|
||||
: btoa;
|
||||
const escape = (x: string) => base64(encodeURIComponent(x)).replace(/=+$/, '');
|
||||
|
||||
export type SelectFieldProps = HTMLFieldProps<
|
||||
string | string[],
|
||||
HTMLDivElement,
|
||||
{
|
||||
allowedValues?: string[];
|
||||
checkboxes?: boolean;
|
||||
disableItem?: (value: string) => boolean;
|
||||
inputRef?: Ref<HTMLSelectElement>;
|
||||
transform?: (value: string) => string;
|
||||
}
|
||||
>;
|
||||
|
||||
function Select({
|
||||
allowedValues,
|
||||
checkboxes,
|
||||
disabled,
|
||||
fieldType,
|
||||
id,
|
||||
inputRef,
|
||||
label,
|
||||
name,
|
||||
onChange,
|
||||
placeholder,
|
||||
readOnly,
|
||||
required,
|
||||
disableItem,
|
||||
transform,
|
||||
value,
|
||||
...props
|
||||
}: SelectFieldProps) {
|
||||
const multiple = fieldType === Array;
|
||||
return (
|
||||
<div className='autoform__select_field' {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={id} required={required} />
|
||||
|
||||
{checkboxes ? (
|
||||
allowedValues!.map(item => (
|
||||
<div key={item}>
|
||||
<input
|
||||
checked={
|
||||
fieldType === Array ? value!.includes(item) : value === item
|
||||
}
|
||||
disabled={disableItem?.(item) ?? disabled}
|
||||
id={`${id}-${escape(item)}`}
|
||||
name={name}
|
||||
onChange={() => {
|
||||
if (!readOnly) {
|
||||
onChange(fieldType === Array ? xor([item], value) : item);
|
||||
}
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
|
||||
<label htmlFor={`${id}-${escape(item)}`}>
|
||||
{transform ? transform(item) : item}
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<select
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
multiple={multiple}
|
||||
name={name}
|
||||
onChange={event => {
|
||||
if (!readOnly) {
|
||||
const item = event.target.value;
|
||||
if (multiple) {
|
||||
const clear = event.target.selectedIndex === -1;
|
||||
onChange(clear ? [] : xor([item], value));
|
||||
} else {
|
||||
onChange(item !== '' ? item : undefined);
|
||||
}
|
||||
}
|
||||
}}
|
||||
ref={inputRef}
|
||||
value={value ?? ''}
|
||||
style={{ width: "100%", padding: "0.5rem" }}
|
||||
>
|
||||
{(!!placeholder || !required || value === undefined) && !multiple && (
|
||||
<option value="" disabled={required} hidden={required}>
|
||||
{placeholder || label}
|
||||
</option>
|
||||
)}
|
||||
|
||||
{allowedValues?.map(value => (
|
||||
<option disabled={disableItem?.(value)} key={value} value={value}>
|
||||
{transform ? transform(value) : value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectField<SelectFieldProps>(Select, { kind: 'leaf' });
|
||||
29
src/components/uniforms-frontmatter/SubmitField.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { HTMLProps, Ref } from 'react';
|
||||
import * as React from 'react';
|
||||
import { Override, filterDOMProps, useForm } from 'uniforms';
|
||||
|
||||
export type SubmitFieldProps = Override<
|
||||
HTMLProps<HTMLInputElement>,
|
||||
{ inputRef?: Ref<HTMLInputElement>; value?: string }
|
||||
>;
|
||||
|
||||
export default function SubmitField({
|
||||
disabled,
|
||||
inputRef,
|
||||
readOnly,
|
||||
value,
|
||||
...props
|
||||
}: SubmitFieldProps) {
|
||||
const { error, state } = useForm();
|
||||
|
||||
return (
|
||||
<input
|
||||
disabled={disabled === undefined ? !!(error || state.disabled) : disabled}
|
||||
readOnly={readOnly}
|
||||
ref={inputRef}
|
||||
type="submit"
|
||||
{...(value ? { value } : {})}
|
||||
{...filterDOMProps(props)}
|
||||
/>
|
||||
);
|
||||
}
|
||||