Compare commits

...

71 Commits

Author SHA1 Message Date
Elio Struyf
13e58d26a1 Merge pull request #854 from estruyf/beta
Release 10.4.0
2024-09-25 08:49:50 +02:00
Elio Struyf
634196b056 Update release date 2024-09-25 08:21:52 +02:00
Elio Struyf
8b95468c78 datetime type fields not respecting empty default value #853 2024-09-23 10:02:48 +02:00
Elio Struyf
dc23aba128 Updated lib 2024-09-20 09:12:21 +02:00
Elio Struyf
a778be9737 Add manifest 2024-09-19 15:08:49 +02:00
Elio Struyf
b9508df4f8 webpack update + loading prod files 2024-09-19 12:57:39 +02:00
Elio Struyf
0110b7365c #844 - Exclude hidden files and verify if valid 2024-09-19 08:48:00 +02:00
Elio Struyf
6588b90e7d Added collapsible panes 2024-09-18 14:05:12 +02:00
Elio Struyf
47dba5f510 #841 - Remove dash on folder notation 2024-09-18 09:20:54 +02:00
Elio Struyf
121a84659f Show non-empty fields in metadata panel #849 2024-09-18 09:18:58 +02:00
Elio Struyf
620966c08e Update vscrui version 2024-09-16 17:50:29 +02:00
Elio Struyf
06718c3577 #837 - Update panels component 2024-09-16 17:31:22 +02:00
Elio Struyf
178207fd82 #837 - Update dropdowns 2024-09-16 16:06:39 +02:00
Elio Struyf
657e9054f6 Added release notes link 2024-09-16 14:34:24 +02:00
Elio Struyf
36a8002cea Merge branch 'beta' of github.com:estruyf/vscode-front-matter into beta 2024-09-16 12:13:39 +02:00
Elio Struyf
07f124dcf5 Add empty view heading for creating new data file #834 2024-09-16 12:13:30 +02:00
Elio Struyf
ff1d4487f4 #848 - change the default GH Copilot model + optimized prompts 2024-09-13 21:59:57 +02:00
Elio Struyf
66151083c0 #844 - new {{filePrefix.index}} placeholder 2024-09-13 14:08:20 +02:00
Elio Struyf
83abff67ac #837 - Adopt vscrui components 2024-09-13 13:43:34 +02:00
Elio Struyf
431a83b882 #841 - enable placeholders in file prefixes 2024-09-09 14:42:35 +02:00
Elio Struyf
d240e8fdc8 #846 - Add GH Copilot action for title field 2024-09-09 12:35:24 +02:00
Elio Struyf
e95e9a8fc7 #842 - Allow the slug to be set to an empty value 2024-09-06 14:12:12 +02:00
Elio Struyf
d8e3338abe Include hidden files 2024-09-05 15:35:50 +02:00
Elio Struyf
6f6b97e6ca Update beta script 2024-09-05 14:23:55 +02:00
Elio Struyf
3f8665cadf #845 - Fix empty values for number fields 2024-09-05 14:05:37 +02:00
Elio Struyf
8cc68be4da Merge branch 'main' into beta 2024-08-15 19:53:50 +02:00
Elio Struyf
27f2b57c24 #833 - Fix localization key/values 2024-08-15 19:52:44 +02:00
Elio Struyf
9b1be1a6c1 feat: Add support for Asciidoc files #833 2024-08-14 16:26:29 +02:00
Elio Struyf
d0b7af5c86 Enhancement: Ability to create new data files for a folder #834 2024-08-14 14:06:52 +02:00
Elio Struyf
f13058c59b Prep changelog 2024-08-14 11:01:35 +02:00
Elio Struyf
cf28e5fc85 10.4.0 2024-08-14 11:00:47 +02:00
Elio Struyf
cf787ab0f6 Merge branch 'beta' 2024-08-14 10:56:55 +02:00
Elio Struyf
c7424a6d73 Update sponsor image 2024-08-14 10:56:19 +02:00
Elio Struyf
2d607bdb5b Merge pull request #839 from estruyf/beta
PR for 10.3.0 release
2024-08-13 17:44:42 +02:00
Elio Struyf
823d99aff2 Fix pkg file 2024-08-13 17:42:00 +02:00
Elio Struyf
9005a94355 Merge branches 'beta' and 'main' of github.com:estruyf/vscode-front-matter into beta 2024-08-13 17:38:32 +02:00
Elio Struyf
dff2fb0149 Update date of 10.3.0 release 2024-08-13 17:33:05 +02:00
Elio Struyf
7d3653589b Update changelog 2024-08-13 13:39:32 +02:00
Elio Struyf
31460026ee Hide media scripts when empty 2024-08-13 09:21:32 +02:00
Elio Struyf
1a97a11c1c 10.2.1 2024-08-08 14:18:38 +02:00
Elio Struyf
430760eca8 #820 - Update API URLs 2024-08-08 14:18:27 +02:00
Elio Struyf
800acde914 Update dev branch 2024-08-08 12:25:47 +02:00
Elio Struyf
06ff07bec8 JSON schema update 2024-08-07 14:22:52 +02:00
Elio Struyf
27a4d9bc71 #820 - Update API URL 2024-08-07 14:22:43 +02:00
Elio Struyf
45f2794631 #823 - added support for v1.92.0 2024-08-05 10:38:46 +02:00
Elio Struyf
42f6557bd6 Update beta logo 2024-07-24 15:29:26 +02:00
Elio Struyf
78d42ac09b Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2024-07-24 14:16:01 +02:00
Elio Struyf
73e00a7a94 #820 - Update endpoints 2024-07-24 14:15:50 +02:00
Elio Struyf
36e0ef0171 Substring update 2024-07-21 20:41:10 +02:00
Elio Struyf
380bc804fd Update beta script 2024-07-21 20:37:43 +02:00
Elio Struyf
458aadcbef Add os and vscode info to diagnostic logging 2024-07-21 20:15:28 +02:00
Elio Struyf
adb541805a Update node modules ignore 2024-07-21 15:25:08 +02:00
Elio Struyf
e7ca5488de Fix for finding folders with wildcards in the path #832 2024-07-21 15:21:39 +02:00
Elio Struyf
f583e0e91a #820 - Update endpoints 2024-07-19 16:29:10 +02:00
Elio Struyf
00bbb3879f Telemetry removal 2024-07-18 17:08:16 +02:00
Elio Struyf
1d7436d051 chore: Update website and API hosts #820 2024-07-18 16:47:22 +02:00
Elio Struyf
86de4fa767 feat: Add select all action #831 2024-07-18 16:35:17 +02:00
Elio Struyf
ced7e41fe6 feat: Add support for DefaultFields.Keywords in ContentTypeValidator #467 2024-07-09 21:26:52 +02:00
Elio Struyf
b81e92ef9e Fix for fieldCollection 2024-07-09 21:10:05 +02:00
Elio Struyf
ec3c1eec58 feat: Add URI handler for command links from documentation #821 2024-07-09 17:15:20 +02:00
Elio Struyf
c173fe973c feat: Add support for fmContentType metadata field #467. 2024-07-09 13:31:19 +02:00
Elio Struyf
47e8caeede Issue: slug placeholder not working if there is no title field #830 2024-07-03 14:35:25 +02:00
Elio Struyf
5b3223abb6 Enhancement: Allow UI extensibility out of experimental mode #829 2024-07-03 11:59:12 +02:00
Elio Struyf
2cb6c89d87 Fix localization 2024-07-01 10:36:34 +02:00
Elio Struyf
4197de2b2e #824 - Added support in single image field 2024-06-28 17:42:19 +02:00
Elio Struyf
fe7a296cc1 Style fix 2024-06-28 13:22:55 +02:00
Elio Struyf
b03d972d31 #824 - Field actions 2024-06-28 09:23:26 +02:00
Elio Struyf
0e6e776f70 #827 - Fix team settings file recreation 2024-06-27 21:35:20 +02:00
Elio Struyf
bd1fc32f1c #823 - Copilot setting 2024-06-27 14:39:22 +02:00
Elio Struyf
38d48b9fa7 Update changelog 2024-06-26 11:37:54 +02:00
Elio Struyf
c92a5cac00 Merge branch 'copilot-integration' into dev 2024-06-26 11:36:26 +02:00
126 changed files with 2207 additions and 1431 deletions

View File

@@ -42,5 +42,6 @@ runs:
- uses: actions/upload-artifact@v4
with:
include-hidden-files: true
name: ${{ inputs.PACKAGE_NAME }}
path: .

View File

@@ -2,7 +2,7 @@ name: BETA Release
on:
push:
branches:
- dev
- beta
workflow_dispatch:
env:

View File

@@ -1,16 +1,57 @@
# Change Log
## [10.3.0] - 2024-xx-xx
## [10.4.0] - 2024-09-25 - [Release notes](https://beta.frontmatter.codes/updates/v10.4.0)
### ✨ New features
- [#844](https://github.com/estruyf/vscode-front-matter/issues/844): New `{{filePrefix.index}}` placeholder to add the index number of the file in the folder
### 🎨 Enhancements
- [#819](https://github.com/estruyf/vscode-front-matter/issues/819): Added new extensibility support for media scripts
- [#822](https://github.com/estruyf/vscode-front-matter/issues/822): Added docs to the panel & dashboard views
- [#833](https://github.com/estruyf/vscode-front-matter/issues/833): Added support for Asciidoc files
- [#834](https://github.com/estruyf/vscode-front-matter/issues/834): Added the ability to create new data files for a data folder
- [#841](https://github.com/estruyf/vscode-front-matter/issues/841): Enable placeholders for file prefixes
- [#846](https://github.com/estruyf/vscode-front-matter/issues/846): Added GitHub Copilot action for title field
- [#848](https://github.com/estruyf/vscode-front-matter/issues/848): Set the default GitHub Copilot model to `gpt-4o-mini`
### 🐞 Fixes
- [#842](https://github.com/estruyf/vscode-front-matter/issues/842): Allow to set the `frontMatter.taxonomy.slugTemplate` setting to an empty string
- [#845](https://github.com/estruyf/vscode-front-matter/issues/845): Fix empty values for number fields
- [#849](https://github.com/estruyf/vscode-front-matter/issues/849): Show fields which are not empty in the metadata panel
- [#853](https://github.com/estruyf/vscode-front-matter/issues/853): Allow empty values in date fields
### 🚧 Work in progress
- [#837](https://github.com/estruyf/vscode-front-matter/issues/837): Replacing the VSCode Webview UI Toolkit with [vscrui](https://github.com/estruyf/vscrui) due to the deprecation of the VSCode Webview UI Toolkit library
## [10.3.0] - 2024-08-13 - [Release notes](https://beta.frontmatter.codes/updates/v10.3.0)
### ✨ New features
- [#823](https://github.com/estruyf/vscode-front-matter/issues/823): Integrated GitHub Copilot support for titles, descriptions, and taxonomy field suggestions
- [#824](https://github.com/estruyf/vscode-front-matter/issues/824): Added the ability to link custom actions to fields
### 🎨 Enhancements
- [#467](https://github.com/estruyf/vscode-front-matter/issues/467): New `fmContentType` metadata field to link content type (fallback to the `type` field)
- [#819](https://github.com/estruyf/vscode-front-matter/issues/819): Added new extensibility support for media scripts
- [#820](https://github.com/estruyf/vscode-front-matter/issues/820): Moving the website and API to different hosts
- [#821](https://github.com/estruyf/vscode-front-matter/issues/821): Added URI handler to support command links from the documentation
- [#822](https://github.com/estruyf/vscode-front-matter/issues/822): Added docs to the panel & dashboard views
- [#829](https://github.com/estruyf/vscode-front-matter/issues/829): UI extensibility is now generally available
- [#831](https://github.com/estruyf/vscode-front-matter/issues/831): Added "select all" action bar button to the content and media dashboards
### 🐞 Fixes
- [#827](https://github.com/estruyf/vscode-front-matter/issues/827): Fix for `frontmatter.json` file which gets created when already present in a sub-folder
- [#830](https://github.com/estruyf/vscode-front-matter/issues/830): Fix for using the SEO title field setting to change the title field reference
- [#832](https://github.com/estruyf/vscode-front-matter/issues/832): Fix for finding folders with wildcards in the path
## [10.2.1] - 2024-08-08
- [#820](https://github.com/estruyf/vscode-front-matter/issues/820): Update API links to the new API URL
## [10.2.0] - 2024-06-12 - [Release notes](https://beta.frontmatter.codes/updates/v10.2.0)
### ✨ New features

View File

@@ -182,7 +182,7 @@ You can open showcase issues for the following things:
## 💚 Backers & Sponsors 👇 🤘
<p align="center">
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
<img src="https://api.frontmatter.codes/img-sponsors" alt="Front Matter sponsors" />
</p>
<br />
@@ -203,18 +203,7 @@ You can open showcase issues for the following things:
## 📊 Telemetry
The Front Matter CMS extension collects telemetry data to help us build a better understand which features from the CMS are used. The extension respects the `telemetry.enableTelemetry` setting which you can learn more about in the [Visual Studio Code FAQ](https://aka.ms/vscode-remote/telemetry), or you can only disable it for the extension by configuring the `frontMatter.telemetry.disable` setting.
We only collect the following data:
- Type of event
- Extension title (main or beta)
- Extension version
No user-specific data is collected, you can check the telemetry implementation in the following files:
- [Telemetry class](https://github.com/estruyf/vscode-front-matter/blob/59528a3db01be8d34dc40638e6cf827090e31986/src/helpers/Telemetry.ts)
- [Metrics API](https://github.com/FrontMatter/web-documentation-nextjs/blob/main/pages/api/metrics.ts)
The Front Matter CMS extension only uses telemetry on application crashes. The extension respects the `telemetry.enableTelemetry` setting which you can learn more about in the [Visual Studio Code FAQ](https://aka.ms/vscode-remote/telemetry).
For crash reports in the webviews, we make use of Sentry to help us understand what went wrong. This data is only used to fix issues and improve the extension. You can find more information about the Sentry implementation in the following files:

View File

@@ -180,7 +180,7 @@ You can open showcase issues for the following things:
## 💚 Backers & Sponsors 👇 🤘
<p align="center">
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
<img src="https://api.frontmatter.codes/img-sponsors" alt="Front Matter sponsors" />
</p>
<br />
@@ -209,18 +209,7 @@ You can open showcase issues for the following things:
## 📊 Telemetry
The Front Matter CMS extension collects telemetry data to help us build a better understand which features from the CMS are used. The extension respects the `telemetry.enableTelemetry` setting which you can learn more about in the [Visual Studio Code FAQ](https://aka.ms/vscode-remote/telemetry), or you can only disable it for the extension by configuring the `frontMatter.telemetry.disable` setting.
We only collect the following data:
- Type of event
- Extension title (main or beta)
- Extension version
No user-specific data is collected, you can check the telemetry implementation in the following files:
- [Telemetry class](https://github.com/estruyf/vscode-front-matter/blob/59528a3db01be8d34dc40638e6cf827090e31986/src/helpers/Telemetry.ts)
- [Metrics API](https://github.com/FrontMatter/web-documentation-nextjs/blob/main/pages/api/metrics.ts)
The Front Matter CMS extension only uses telemetry on application crashes. The extension respects the `telemetry.enableTelemetry` setting which you can learn more about in the [Visual Studio Code FAQ](https://aka.ms/vscode-remote/telemetry).
For crash reports in the webviews, we make use of Sentry to help us understand what went wrong. This data is only used to fix issues and improve the extension. You can find more information about the Sentry implementation in the following files:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,45 +1,54 @@
<?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{fill:#FFE45E;}
.st1{fill:none;stroke:#FFE45E;stroke-width:2;stroke-miterlimit:10;}
.st2{font-family:'MyriadPro-Bold';}
.st3{font-size:8px;}
</style>
<g>
<g>
<path class="st0" d="M4.1,10.2H2.4V2.1h3.1V4H4.1v1.2h1.2V7H4.1V10.2z"/>
<path class="st0" d="M10.7,10.2H8.9L8,7.3c0-0.1,0-0.1,0-0.2C7.9,7.1,7.9,7,7.8,6.8v0.6v2.9H6.1V2.1h1.8c0.8,0,1.3,0.2,1.8,0.6
c0.5,0.5,0.8,1.2,0.8,2.1c0,1-0.4,1.6-1.1,2L10.7,10.2z M7.9,5.8L7.9,5.8c0.3,0,0.5-0.1,0.6-0.3S8.7,5,8.7,4.8c0-0.6-0.3-1-0.8-1
l0,0V5.8z"/>
<path class="st0" d="M16.1,6.2c0,1.2-0.2,2.3-0.7,3.1s-1.1,1.2-1.7,1.2s-1.2-0.3-1.6-0.9c-0.6-0.8-0.9-1.9-0.9-3.4
s0.3-2.6,0.9-3.4C12.6,2.3,13,2,13.7,2c0.8,0,1.3,0.4,1.8,1.2C15.8,3.8,16.1,4.8,16.1,6.2z M14.3,6.2c0-1.4-0.2-2.2-0.7-2.2
c-0.2,0-0.4,0.2-0.5,0.6c-0.1,0.4-0.2,0.9-0.2,1.6c0,0.7,0.1,1.2,0.2,1.6c0.1,0.4,0.3,0.6,0.5,0.6c0.2,0,0.4-0.2,0.5-0.6
C14.2,7.3,14.3,6.9,14.3,6.2z"/>
<path class="st0" d="M16.8,10.2V2.1h1.7l0.9,2.9c0.1,0.1,0.1,0.3,0.2,0.6c0.1,0.2,0.1,0.5,0.2,0.8L20,7c-0.1-0.7-0.1-1.3-0.2-1.8
s-0.1-1-0.1-1.2V2.1h1.7v8.2h-1.6l-0.9-3c-0.1-0.3-0.2-0.6-0.3-0.9c-0.1-0.3-0.1-0.6-0.2-0.8c0,0.6,0.1,1.1,0.1,1.5
c0,0.4,0,0.8,0,1.2v2.1h-1.7V10.2z"/>
<path class="st0" d="M24.6,10.2h-1.7V4h-1V2.1h3.7V4h-1.1V10.2z"/>
</g>
</g>
<rect class="st1" width="28" height="28"/>
<g>
<g>
<path class="st0" d="M3.1,11.6H4l0.6,3c0.1,0.4,0.2,0.8,0.2,1.2C4.9,16.2,4.9,16.6,5,17c0-0.1,0-0.1,0-0.1v-0.1l0.2-0.9l0.1-0.8
l0.1-0.5l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3c0-0.1,0-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
c-0.1,0.2,0,0.2-0.1,0.3L6,15.7V16l-0.6,3.3H4.7l-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-1
L3.1,11.6z"/>
<path class="st0" d="M9.4,11.6h0.8l1.6,7.5h-1l-0.3-1.5H9l-0.3,1.5h-1L9.4,11.6z M10.4,16.8l-0.3-1.2C10,14.8,9.8,13.9,9.7,13
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.4,16.8L10.4,16.8z"/>
<path class="st0" d="M11.6,11.6h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V11.6z"/>
<path class="st0" d="M14.9,11.6h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V11.6z"/>
<path class="st0" d="M18.8,11.6h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V11.6z"/>
<path class="st0" d="M22.3,11.6h1.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,11.6L22.3,11.6z M23.3,14.9c0.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
c-0.1-0.2-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.2-0.3-0.2s-0.3-0.1-0.4-0.1h-0.2v2.5H23.3z"/>
</g>
</g>
<text transform="matrix(1 0 0 1 5.4457 25.9479)" class="st0 st2 st3">BETA</text>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 28 28">
<defs>
<style>
.cls-1 {
stroke-width: 0px;
}
.cls-1, .cls-2, .cls-3, .cls-4 {
fill: #c91980;
}
.cls-2 {
font-family: Futura-MediumItalic, Futura;
font-size: 8px;
font-style: italic;
}
.cls-2, .cls-3, .cls-4, .cls-5 {
isolation: isolate;
}
.cls-2, .cls-4 {
font-weight: 500;
}
.cls-3 {
font-family: Futura-CondensedExtraBold, Futura;
font-size: 10.6px;
font-weight: 700;
}
.cls-4 {
font-family: Futura-CondensedMedium, Futura;
font-size: 10.1px;
}
</style>
</defs>
<g class="cls-5">
<text class="cls-4" transform="translate(2.1 18.9) scale(1 1)"><tspan x="0" y="0">MATTER</tspan></text>
</g>
<g class="cls-5">
<text class="cls-2" transform="translate(1.9 26)"><tspan x="0" y="0">BETA</tspan></text>
</g>
<rect class="cls-1" x="2.4" width="3" height="1"/>
<rect class="cls-1" x="6.9" width="3" height="1"/>
<rect class="cls-1" x="11.4" width="3" height="1"/>
<rect class="cls-1" x="2.4" y="27" width="3" height="1"/>
<rect class="cls-1" x="6.9" y="27" width="3" height="1"/>
<rect class="cls-1" x="11.4" y="27" width="3" height="1"/>
<g class="cls-5">
<text class="cls-3" transform="translate(2.1 10.1) scale(.8 1)"><tspan x="0" y="0">FRONT</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -75,7 +75,7 @@
}
.frontmatter h3 {
margin-bottom: 1rem;
/* margin-bottom: 1rem; */
}
.frontmatter p,
@@ -224,6 +224,7 @@
text-decoration: none;
width: 100%;
white-space: nowrap;
border-radius: 0.25rem;
}
.ext_link_block button.active {

View File

@@ -140,8 +140,12 @@
"dashboard.dataView.dataView.noDataFiles": "No data files found",
"dashboard.dataView.dataView.getStarted.link": "Read more to get started using data files",
"dashboard.dataView.dataView.update.message": "Updated your data entries",
"dashboard.dataView.dataView.createNew": "Create new data file",
"dashboard.dataView.dataView.selectDataFolder": "Select data folder",
"dashboard.dataView.dataView.closeSelectedDataFile": "Close data file",
"dashboard.dataView.emptyView.heading": "Select your date type first",
"dashboard.dataView.emptyView.heading.create": "Start by creating a new data file",
"dashboard.dataView.sortableItem.editButton.title": "Edit \"{0}\"",
"dashboard.dataView.sortableItem.deleteButton.title": "Delete \"{0}\"",
@@ -154,6 +158,7 @@
"dashboard.filters.languageFilter.all": "All",
"dashboard.header.actionsBar.itemsSelected": "{0} selected",
"dashboard.header.actionsBar.selectAll": "Select all",
"dashboard.header.actionsBar.alertDelete.title": "Delete selected files",
"dashboard.header.actionsBar.alertDelete.description": "Are you sure you want to delete the selected files?",
@@ -438,6 +443,9 @@
"panel.fields.wrapperField.unknown": "Unkown field type: {0}",
"panel.fields.fieldCustomAction.button.title": "Custom action",
"panel.fields.fieldCustomAction.executing": "Executing field action...",
"panel.actions.title": "Actions",
"panel.articleDetails.title": "More details",
@@ -779,6 +787,9 @@
"listeners.panel.dataListener.aiSuggestTaxonomy.noData.error": "No article data",
"listeners.panel.dataListener.getDataFileEntries.noDataFiles.error": "Couldn't find data file entries",
"listeners.panel.dataListener.pushMetadata.frontMatter.error": "Something went wrong while parsing your front matter. Please check the contents of your file.",
"listeners.panel.dataListener.createDataFile.inputTitle": "What is the name of the data file?",
"listeners.panel.dataListener.createDataFile.error": "No data file id or path defined.",
"listeners.panel.dataListener.createDataFile.noFileName": "No filename provided.",
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "No active editor",

328
package-lock.json generated
View File

@@ -1,16 +1,13 @@
{
"name": "vscode-front-matter-beta",
"version": "10.3.0",
"version": "10.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-front-matter-beta",
"version": "10.3.0",
"version": "10.4.0",
"license": "MIT",
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.0.6"
},
"devDependencies": {
"@actions/core": "^1.10.0",
"@estruyf/vscode": "^1.1.0",
@@ -18,6 +15,7 @@
"@heroicons/react": "^2.1.1",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@sentry/react": "^6.19.7",
"@sentry/tracing": "^6.19.7",
"@tailwindcss/forms": "^0.5.3",
@@ -34,11 +32,10 @@
"@types/react-datepicker": "^4.8.0",
"@types/react-dom": "17.0.0",
"@types/vscode": "^1.90.0",
"@types/webpack-bundle-analyzer": "^4.7.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vscode-elements/elements": "^1.2.0",
"@vscode/l10n": "^0.0.14",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@webpack-cli/serve": "^1.7.0",
"ajv": "^8.12.0",
"array-move": "^4.0.0",
@@ -100,12 +97,14 @@
"uniforms-bridge-json-schema": "^3.10.2",
"uniforms-unstyled": "^3.10.2",
"url-join-ts": "^1.0.5",
"vscrui": "^0.1.0-beta.1094721",
"wc-react": "github:estruyf/wc-react",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.7.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-ignore-dynamic-require": "^1.0.0",
"webpack-manifest-plugin": "^5.0.0",
"yaml": "^2.2.1",
"yawn-yaml": "^1.5.0"
},
@@ -392,6 +391,7 @@
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
"integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -536,6 +536,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
"integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
"dev": true,
"dependencies": {
"@floating-ui/utils": "^0.2.1"
}
@@ -544,6 +545,7 @@
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
"integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
"dev": true,
"dependencies": {
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.0"
@@ -553,6 +555,7 @@
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
"integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
"dev": true,
"dependencies": {
"@floating-ui/dom": "^1.6.1"
},
@@ -564,7 +567,8 @@
"node_modules/@floating-ui/utils": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==",
"dev": true
},
"node_modules/@headlessui/react": {
"version": "1.7.18",
@@ -754,61 +758,6 @@
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
"dev": true
},
"node_modules/@lit-labs/ssr-dom-shim": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz",
"integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==",
"dev": true
},
"node_modules/@lit/reactive-element": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
"integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==",
"dev": true,
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.2.0"
}
},
"node_modules/@microsoft/fast-element": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.12.0.tgz",
"integrity": "sha512-gQutuDHPKNxUEcQ4pypZT4Wmrbapus+P9s3bR/SEOLsMbNqNoXigGImITygI5zhb+aA5rzflM6O8YWkmRbGkPA==",
"dev": true
},
"node_modules/@microsoft/fast-foundation": {
"version": "2.49.5",
"resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.49.5.tgz",
"integrity": "sha512-3PpG1BNmZ5kUM1goYU3SsxjsM2H7Rk0ZmpDJ7mnRhWDgKiM5SzJ02KvALRUqDrJQoeDnkW0Q2Q+r9SkEd68Gpg==",
"dev": true,
"dependencies": {
"@microsoft/fast-element": "^1.12.0",
"@microsoft/fast-web-utilities": "^5.4.1",
"tabbable": "^5.2.0",
"tslib": "^1.13.0"
}
},
"node_modules/@microsoft/fast-react-wrapper": {
"version": "0.3.23",
"resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.3.23.tgz",
"integrity": "sha512-iuL+J2AFKJ1mwUBxSp+bqzt4X93kQwj1jpVgHgw2VRzCOTl7wzta6X+lvRIVg4eoyLfmeVSMkB+q3PD87T/MyQ==",
"dev": true,
"dependencies": {
"@microsoft/fast-element": "^1.12.0",
"@microsoft/fast-foundation": "^2.49.5"
},
"peerDependencies": {
"react": ">=16.9.0"
}
},
"node_modules/@microsoft/fast-web-utilities": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz",
"integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==",
"dev": true,
"dependencies": {
"exenv-es6": "^1.1.1"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1006,6 +955,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
}
@@ -1014,6 +964,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
"integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
@@ -1037,6 +988,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
"integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
@@ -1063,6 +1015,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1080,6 +1033,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1097,6 +1051,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
"integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1114,6 +1069,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
@@ -1141,6 +1097,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz",
"integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
@@ -1170,6 +1127,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
"integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1187,6 +1145,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
"integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
@@ -1212,6 +1171,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "1.0.1"
@@ -1230,6 +1190,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz",
"integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
@@ -1270,6 +1231,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz",
"integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@floating-ui/react-dom": "^2.0.0",
@@ -1302,6 +1264,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
@@ -1325,6 +1288,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
@@ -1349,6 +1313,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "1.0.2"
@@ -1372,6 +1337,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz",
"integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
@@ -1403,6 +1369,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
@@ -1421,6 +1388,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1438,6 +1406,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
@@ -1456,6 +1425,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
@@ -1474,6 +1444,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@@ -1491,6 +1462,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
"integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/rect": "1.0.1"
@@ -1509,6 +1481,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz",
"integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "1.0.1"
@@ -1527,6 +1500,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
"integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.13.10"
}
@@ -1945,7 +1919,7 @@
"version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
"devOptional": true
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.11",
@@ -1972,7 +1946,7 @@
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz",
"integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -1994,7 +1968,7 @@
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz",
"integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==",
"devOptional": true,
"dev": true,
"dependencies": {
"@types/react": "*"
}
@@ -2062,12 +2036,6 @@
"integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==",
"dev": true
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
},
"node_modules/@types/uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.4.tgz",
@@ -2109,6 +2077,26 @@
"source-map": "^0.6.0"
}
},
"node_modules/@types/webpack-bundle-analyzer": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz",
"integrity": "sha512-c5i2ThslSNSG8W891BRvOd/RoCjI2zwph8maD22b1adtSns20j+0azDDMCK06DiVrzTgnwiDl5Ntmu1YRJw8Sg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"tapable": "^2.2.0",
"webpack": "^5"
}
},
"node_modules/@types/webpack-bundle-analyzer/node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@types/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.3.tgz",
@@ -2332,42 +2320,12 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/@vscode-elements/elements": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@vscode-elements/elements/-/elements-1.2.0.tgz",
"integrity": "sha512-aCsf9iEnx+PE2rRfAySjvFTSgqP4NUvHG0nOc5AxFB1FXHyG/ayYA2TN9XpT7zuO024tRAu+XoKREbRC7uAmLA==",
"dev": true,
"dependencies": {
"lit": "^3.1.2"
}
},
"node_modules/@vscode/l10n": {
"version": "0.0.14",
"resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.14.tgz",
"integrity": "sha512-/yrv59IEnmh655z1oeDnGcvMYwnEzNzHLgeYcQCkhYX0xBvYWrAuefoiLcPBUkMpJsb46bqQ6Yv4pwTTQ4d3Qg==",
"dev": true
},
"node_modules/@vscode/webview-ui-toolkit": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.4.0.tgz",
"integrity": "sha512-modXVHQkZLsxgmd5yoP3ptRC/G8NBDD+ob+ngPiWNQdlrH6H1xR/qgOBD85bfU3BhOB5sZzFWBwwhp9/SfoHww==",
"dev": true,
"dependencies": {
"@microsoft/fast-element": "^1.12.0",
"@microsoft/fast-foundation": "^2.49.4",
"@microsoft/fast-react-wrapper": "^0.3.22",
"tslib": "^2.6.2"
},
"peerDependencies": {
"react": ">=16.9.0"
}
},
"node_modules/@vscode/webview-ui-toolkit/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@@ -2790,6 +2748,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz",
"integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==",
"dev": true,
"dependencies": {
"tslib": "^2.0.0"
},
@@ -2800,7 +2759,8 @@
"node_modules/aria-hidden/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.1",
@@ -3715,7 +3675,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"devOptional": true
"dev": true
},
"node_modules/date-fns": {
"version": "2.23.0",
@@ -3894,7 +3854,8 @@
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"dev": true
},
"node_modules/didyoumean": {
"version": "1.2.2",
@@ -4566,12 +4527,6 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/exenv-es6": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz",
"integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==",
"dev": true
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
@@ -5046,6 +5001,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -5918,6 +5874,7 @@
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dev": true,
"dependencies": {
"loose-envify": "^1.0.0"
}
@@ -6455,7 +6412,8 @@
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"node_modules/js-yaml": {
"version": "4.1.0",
@@ -6604,37 +6562,6 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/lit": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lit/-/lit-3.1.2.tgz",
"integrity": "sha512-VZx5iAyMtX7CV4K8iTLdCkMaYZ7ipjJZ0JcSdJ0zIdGxxyurjIn7yuuSxNBD7QmjvcNJwr0JS4cAdAtsy7gZ6w==",
"dev": true,
"dependencies": {
"@lit/reactive-element": "^2.0.4",
"lit-element": "^4.0.4",
"lit-html": "^3.1.2"
}
},
"node_modules/lit-element": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.4.tgz",
"integrity": "sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==",
"dev": true,
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.2.0",
"@lit/reactive-element": "^2.0.4",
"lit-html": "^3.1.2"
}
},
"node_modules/lit-html": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.2.tgz",
"integrity": "sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==",
"dev": true,
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
},
"node_modules/load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
@@ -6751,6 +6678,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -8152,6 +8080,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -8695,9 +8624,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"funding": [
{
@@ -8716,7 +8645,7 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -9877,6 +9806,7 @@
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
"integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -9907,6 +9837,7 @@
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz",
"integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -10030,6 +9961,7 @@
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
"integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
"dev": true,
"dependencies": {
"react-remove-scroll-bar": "^2.3.3",
"react-style-singleton": "^2.2.1",
@@ -10054,6 +9986,7 @@
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz",
"integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==",
"dev": true,
"dependencies": {
"react-style-singleton": "^2.2.1",
"tslib": "^2.0.0"
@@ -10074,12 +10007,14 @@
"node_modules/react-remove-scroll-bar/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/react-remove-scroll/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/react-router": {
"version": "6.22.0",
@@ -10133,6 +10068,7 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
"integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
"dev": true,
"dependencies": {
"get-nonce": "^1.0.0",
"invariant": "^2.2.4",
@@ -10154,7 +10090,8 @@
"node_modules/react-style-singleton/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/read-cache": {
"version": "1.0.0",
@@ -10261,7 +10198,8 @@
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.1",
@@ -10686,6 +10624,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -11098,6 +11037,12 @@
"websocket-driver": "^0.7.4"
}
},
"node_modules/source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
"dev": true
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -11108,9 +11053,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -11495,12 +11440,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tabbable": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
"integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==",
"dev": true
},
"node_modules/tailwind-merge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz",
@@ -12228,6 +12167,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz",
"integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==",
"dev": true,
"dependencies": {
"tslib": "^2.0.0"
},
@@ -12247,12 +12187,14 @@
"node_modules/use-callback-ref/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
"dev": true,
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^2.0.0"
@@ -12273,7 +12215,8 @@
"node_modules/use-sidecar/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/util": {
"version": "0.12.5",
@@ -12395,6 +12338,20 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/vscrui": {
"version": "0.1.0-beta.1094721",
"resolved": "https://registry.npmjs.org/vscrui/-/vscrui-0.1.0-beta.1094721.tgz",
"integrity": "sha512-EhniC+YCRAcwmjIxf5vr6JaOH+rkdhd6wc4MplPqw/MWC7rTpgTErpUTKnNd3aH5MDyvINTONYEBrPSe8d1skA==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/estruyf"
},
"peerDependencies": {
"@types/react": "*",
"react": "^17 || ^18"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
@@ -12486,9 +12443,9 @@
}
},
"node_modules/webpack-bundle-analyzer": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz",
"integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==",
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
"integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
"dev": true,
"dependencies": {
"@discoveryjs/json-ext": "0.5.7",
@@ -12499,7 +12456,6 @@
"escape-string-regexp": "^4.0.0",
"gzip-size": "^6.0.0",
"html-escaper": "^2.0.2",
"is-plain-object": "^5.0.0",
"opener": "^1.5.2",
"picocolors": "^1.0.0",
"sirv": "^2.0.3",
@@ -12724,6 +12680,44 @@
"integrity": "sha512-WeGFPgwDochKPwizAu5XsHcPq3aaQLl2E+n1piD/VPGNUo5HIwrtURWNMfrPDfkHVOx+flkAihXbUiILAv5x4Q==",
"dev": true
},
"node_modules/webpack-manifest-plugin": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz",
"integrity": "sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==",
"dev": true,
"dependencies": {
"tapable": "^2.0.0",
"webpack-sources": "^2.2.0"
},
"engines": {
"node": ">=12.22.0"
},
"peerDependencies": {
"webpack": "^5.47.0"
}
},
"node_modules/webpack-manifest-plugin/node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/webpack-manifest-plugin/node_modules/webpack-sources": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz",
"integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==",
"dev": true,
"dependencies": {
"source-list-map": "^2.0.1",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/webpack-merge": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",

View File

@@ -3,7 +3,7 @@
"displayName": "Front Matter CMS",
"description": "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 like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...",
"icon": "assets/frontmatter-teal-128x128.png",
"version": "10.3.0",
"version": "10.4.0",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -577,6 +577,7 @@
"default": [],
"markdownDescription": "%setting.frontMatter.custom.scripts.markdownDescription%",
"items": {
"$id": "#customscript",
"type": "object",
"properties": {
"id": {
@@ -869,6 +870,20 @@
"type": "boolean",
"description": "%setting.frontMatter.data.folders.items.properties.singleEntry.description%",
"default": false
},
"enableFileCreation": {
"type": "boolean",
"description": "%setting.frontMatter.data.folders.items.properties.enableFileCreation.description%",
"default": false
},
"fileType": {
"type": "string",
"default": "json",
"enum": [
"json",
"yaml"
],
"description": "%setting.frontMatter.data.folders.items.properties.fileType.description%"
}
},
"additionalProperties": false,
@@ -1243,9 +1258,16 @@
"fileType": {
"type": "string",
"default": "",
"enum": [
"md",
"mdx"
"oneOf": [
{
"enum": [
"md",
"mdx"
]
},
{
"type": "string"
}
],
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fileType.description%"
},
@@ -1564,6 +1586,13 @@
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description%"
}
}
},
"actions": {
"type": "array",
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.actions.description%",
"items": {
"$ref": "#customscript"
}
}
},
"additionalProperties": false,
@@ -1986,7 +2015,11 @@
"scope": "Taxonomy"
},
"frontMatter.taxonomy.slugTemplate": {
"type": "string",
"type": [
"string",
"null"
],
"default": null,
"markdownDescription": "%setting.frontMatter.taxonomy.slugTemplate.markdownDescription%",
"scope": "Taxonomy"
},
@@ -1998,11 +2031,6 @@
},
"scope": "Taxonomy"
},
"frontMatter.telemetry.disable": {
"type": "boolean",
"default": false,
"markdownDescription": "%setting.frontMatter.telemetry.disable.markdownDescription%"
},
"frontMatter.templates.enabled": {
"type": "boolean",
"default": false,
@@ -2017,7 +2045,7 @@
},
"frontMatter.templates.prefix": {
"type": "string",
"default": "yyyy-MM-dd",
"default": "{{date|yyyy-MM-dd}}",
"markdownDescription": "%setting.frontMatter.templates.prefix.markdownDescription%",
"scope": "Templates"
},
@@ -2034,6 +2062,11 @@
"info",
"verbose"
]
},
"frontMatter.copilot.family": {
"type": "string",
"default": "gpt-4o-mini",
"markdownDescription": "%setting.frontMatter.copilot.family.markdownDescription%"
}
}
},
@@ -2682,23 +2715,23 @@
"group": "navigation@0",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.collapseSections",
"group": "navigation@1",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.mode.switch",
"group": "navigation@2",
"group": "navigation@1",
"when": "view == frontMatter.explorer && frontMatter:has:modes == true"
},
{
"command": "frontMatter.project.switch",
"group": "navigation@3",
"group": "navigation@2",
"when": "view == frontMatter.explorer && frontMatter:project:switch:enabled"
},
{
"command": "frontMatter.settings.refresh",
"group": "navigation@3",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.collapseSections",
"group": "navigation@4",
"when": "view == frontMatter.explorer"
},
@@ -2803,6 +2836,7 @@
"@heroicons/react": "^2.1.1",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@sentry/react": "^6.19.7",
"@sentry/tracing": "^6.19.7",
"@tailwindcss/forms": "^0.5.3",
@@ -2819,11 +2853,10 @@
"@types/react-datepicker": "^4.8.0",
"@types/react-dom": "17.0.0",
"@types/vscode": "^1.90.0",
"@types/webpack-bundle-analyzer": "^4.7.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vscode-elements/elements": "^1.2.0",
"@vscode/l10n": "^0.0.14",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@webpack-cli/serve": "^1.7.0",
"ajv": "^8.12.0",
"array-move": "^4.0.0",
@@ -2885,19 +2918,18 @@
"uniforms-bridge-json-schema": "^3.10.2",
"uniforms-unstyled": "^3.10.2",
"url-join-ts": "^1.0.5",
"vscrui": "^0.1.0-beta.1094721",
"wc-react": "github:estruyf/wc-react",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.7.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-ignore-dynamic-require": "^1.0.0",
"webpack-manifest-plugin": "^5.0.0",
"yaml": "^2.2.1",
"yawn-yaml": "^1.5.0"
},
"vsce": {
"dependencies": false
},
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.0.6"
}
}

View File

@@ -145,6 +145,8 @@
"setting.frontMatter.data.folders.items.properties.path.description": "Path to the folder to load files.",
"setting.frontMatter.data.folders.items.properties.type.description": "If you are using data types, you can specify your type ID.",
"setting.frontMatter.data.folders.items.properties.singleEntry.description": "If you want to use a single entry for your data files in the folder.",
"setting.frontMatter.data.folders.items.properties.enableFileCreation.description": "Enable the creation of new data files in the folder.",
"setting.frontMatter.data.folders.items.properties.fileType.description": "Defines the file type for when the file creation is enabled. JSON is the default.",
"setting.frontMatter.data.types.markdownDescription": "Specify the data types. These types can be used in for your data files. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.types) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.types%22%5D)",
"setting.frontMatter.data.types.items.properties.id.description": "Your unique ID you want to use for your data type.",
"setting.frontMatter.file.preserveCasing.markdownDescription": "Specify if you want to preserve the casing of your file names from the title. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.file.preservecasing) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.file.preservecasing%22%5D)",
@@ -231,6 +233,7 @@
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.operator.description": "The operator to use",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.value.description": "The value to compare",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "Specify if the comparison is case sensitive. Default: true",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.actions.description": "Specify the field custom actions",
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "Specify if you want to create a folder when creating new content.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "Defines a custom preview path for the content type.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.",
@@ -279,5 +282,6 @@
"setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "Specify if the content type is sub content.",
"setting.frontMatter.git.disableOnBranches.markdownDescription": "Specify the branches on which you want to disable the Git actions. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.disableonbranches%22%5D)",
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.requirescommitmessage%22%5D)"
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.requirescommitmessage%22%5D)",
"setting.frontMatter.copilot.family.markdownDescription": "Specify the LLM family of the Copilot you want to use. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.copilot.family) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.copilot.family%22%5D)"
}

View File

@@ -5,13 +5,15 @@ const core = require('@actions/core');
const packageJson = require('../package.json');
const version = packageJson.version.split('.');
packageJson.version = `${version[0]}.${version[1]}.${process.argv[process.argv.length-1].substr(0, 7)}`;
packageJson.version = `${version[0]}.${version[1]}.${process.argv[
process.argv.length - 1
].substring(0, 9)}`;
packageJson.preview = true;
packageJson.name = "vscode-front-matter-beta";
packageJson.name = 'vscode-front-matter-beta';
packageJson.displayName = `${packageJson.displayName} (BETA)`;
packageJson.description = `BETA Version of Front Matter. ${packageJson.description}`;
packageJson.icon = "assets/frontmatter-beta.png";
packageJson.homepage = "https://beta.frontmatter.codes";
packageJson.icon = 'assets/frontmatter-beta.png';
packageJson.homepage = 'https://beta.frontmatter.codes';
console.log(packageJson.version);
@@ -20,19 +22,24 @@ core.summary.addHeading(`Version info`).addRaw(`Version: ${packageJson.version}`
const scripts = packageJson.scripts;
for (const key in scripts) {
if (key.startsWith(`prod:`)) {
scripts[key] = scripts[key].replace("production", "development");
scripts[key] = scripts[key].replace('production', 'development');
}
}
console.log(JSON.stringify(packageJson.scripts, null, 2));
fs.writeFileSync(path.join(path.resolve('.'), 'package.json'), JSON.stringify(packageJson, null, 2));
fs.writeFileSync(
path.join(path.resolve('.'), 'package.json'),
JSON.stringify(packageJson, null, 2)
);
let readme = fs.readFileSync(path.join(__dirname, '../README.beta.md'), 'utf8');
fs.writeFileSync(path.join(__dirname, '../README.md'), readme);
// Update the .vscodeignore file
const ignoreFilePath = path.join(path.resolve('.'), '.vscodeignore');
let vscodeignore = fs.readFileSync(ignoreFilePath, 'utf8');
vscodeignore = vscodeignore.replace(`**/*.map`, '');
fs.writeFileSync(ignoreFilePath, vscodeignore);
if (fs.existsSync(ignoreFilePath)) {
let vscodeignore = fs.readFileSync(ignoreFilePath, 'utf8');
vscodeignore = vscodeignore.replace(`**/*.map`, '');
fs.writeFileSync(ignoreFilePath, vscodeignore);
}

View File

@@ -20,7 +20,6 @@ import {
SETTING_SLUG_PREFIX,
SETTING_SLUG_SUFFIX,
SETTING_CONTENT_PLACEHOLDERS,
TelemetryEvent,
SETTING_SLUG_TEMPLATE
} from './../constants';
import { CustomPlaceholder, Field } from '../models';
@@ -39,13 +38,13 @@ import { COMMAND_NAME, DefaultFields } from '../constants';
import { DashboardData, SnippetInfo, SnippetRange } from '../models/DashboardData';
import { DateHelper } from '../helpers/DateHelper';
import { parseWinPath } from '../helpers/parseWinPath';
import { Telemetry } from '../helpers/Telemetry';
import { ParsedFrontMatter } from '../parsers';
import { MediaListener } from '../listeners/panel';
import { NavigationType } from '../dashboardWebView/models';
import { SNIPPET } from '../constants/Snippet';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { getTitleField } from '../utils';
export class Article {
/**
@@ -176,7 +175,7 @@ export class Article {
if (article?.data) {
const slug = SlugHelper.createSlug(title, article?.data, slugTemplate);
if (slug) {
if (typeof slug === 'string') {
return {
slug,
slugWithPrefixAndSuffix: `${prefix}${slug}${suffix}`
@@ -191,8 +190,6 @@ export class Article {
* Generate the slug based on the article title
*/
public static async updateSlug() {
Telemetry.send(TelemetryEvent.generateSlug);
const updateFileName = Settings.get(SETTING_SLUG_UPDATE_FILE_NAME) as string;
const editor = window.activeTextEditor;
@@ -205,19 +202,27 @@ export class Article {
return;
}
const titleField = getTitleField();
const articleTitle: string = article.data[titleField];
const articleDate = await ArticleHelper.getDate(article);
let filePrefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
const contentType = await ArticleHelper.getContentType(article);
filePrefix = await ArticleHelper.getFilePrefix(
filePrefix,
editor.document.uri.fsPath,
contentType
contentType,
articleTitle,
articleDate
);
const titleField = 'title';
const articleTitle: string = article.data[titleField];
const slugInfo = Article.generateSlug(articleTitle, article, contentType.slugTemplate);
if (slugInfo && slugInfo.slug && slugInfo.slugWithPrefixAndSuffix) {
if (
slugInfo &&
typeof slugInfo.slug === 'string' &&
typeof slugInfo.slugWithPrefixAndSuffix === 'string'
) {
article.data['slug'] = slugInfo.slugWithPrefixAndSuffix;
if (contentType) {
@@ -267,7 +272,11 @@ export class Article {
let newFileName = `${slugName}${ext}`;
if (filePrefix && typeof filePrefix === 'string') {
newFileName = `${filePrefix}-${newFileName}`;
if (filePrefix.endsWith('/')) {
newFileName = `${filePrefix}${newFileName}`;
} else {
newFileName = `${filePrefix}-${newFileName}`;
}
}
const newPath = editor.document.uri.fsPath.replace(fileName, newFileName);
@@ -307,17 +316,18 @@ export class Article {
const parsedFile = parse(file);
const titleField = getTitleField();
const slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE);
if (slugTemplate) {
if (slugTemplate === '{{title}}') {
const article = ArticleHelper.getFrontMatter(editor);
if (article?.data?.title) {
return article.data.title.toLowerCase().replace(/\s/g, '-');
if (article?.data && article.data[titleField]) {
return article.data[titleField].toLowerCase().replace(/\s/g, '-');
}
} else {
const article = ArticleHelper.getFrontMatter(editor);
if (article?.data) {
return SlugHelper.createSlug(article.data.title, article.data, slugTemplate);
return SlugHelper.createSlug(article.data[titleField], article.data, slugTemplate);
}
}
}
@@ -490,11 +500,12 @@ export class Article {
const article = ArticleHelper.getFrontMatter(editor);
const contentType = article ? await ArticleHelper.getContentType(article) : undefined;
const tileField = getTitleField();
await commands.executeCommand(COMMAND_NAME.dashboard, {
type: NavigationType.Snippets,
data: {
fileTitle: article?.data.title || '',
fileTitle: article?.data[tileField] || '',
filePath: editor.document.uri.fsPath,
fieldName: basename(editor.document.uri.fsPath),
contentType,

View File

@@ -1,5 +1,5 @@
import { authentication, commands, ExtensionContext } from 'vscode';
import { COMMAND_NAME, CONTEXT } from '../constants';
import { COMMAND_NAME, CONTEXT, WEBSITE_LINKS } from '../constants';
import { Extension, Logger } from '../helpers';
import { Dashboard } from './Dashboard';
import { SettingsListener } from '../listeners/panel';
@@ -22,9 +22,8 @@ export class Backers {
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
if (githubAuth && githubAuth.accessToken) {
try {
const isBeta = ext.isBetaVersion();
const response = await fetch(
`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/v2/backers`,
`${WEBSITE_LINKS.api.baseUrl}${WEBSITE_LINKS.api.endpoints.backers}`,
{
method: 'POST',
headers: {

View File

@@ -1,5 +1,4 @@
import { Telemetry } from './../helpers/Telemetry';
import { TelemetryEvent, PreviewCommands, GeneralCommands } from './../constants';
import { PreviewCommands, GeneralCommands } from './../constants';
import { join } from 'path';
import { commands, Uri, ViewColumn, window } from 'vscode';
import { Extension } from '../helpers';
@@ -7,6 +6,7 @@ import { WebviewHelper } from '@estruyf/vscode';
import { getLocalizationFile } from '../utils/getLocalizationFile';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { getWebviewJsFiles } from '../utils';
export class Chatbot {
/**
@@ -57,7 +57,7 @@ export class Chatbot {
}
});
const dashboardFile = 'dashboardWebView.js';
const webviewFile = 'dashboard.main.js';
const localPort = `9000`;
const localServerUrl = `localhost:${localPort}`;
@@ -67,7 +67,6 @@ export class Chatbot {
const isProd = ext.isProductionMode;
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
const extensionUri = ext.extensionPath;
const csp = [
`default-src 'none';`,
@@ -83,13 +82,11 @@ export class Chatbot {
}`
];
let scriptUri = '';
let scriptUris = [];
if (isProd) {
scriptUri = webView.webview
.asWebviewUri(Uri.joinPath(extensionUri, 'dist', dashboardFile))
.toString();
scriptUris = await getWebviewJsFiles('dashboard', webView.webview);
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUris.push(`http://${localServerUrl}/${webviewFile}`);
}
// By default, the chatbot is seen as experimental
@@ -112,11 +109,13 @@ export class Chatbot {
experimental ? `data-experimental="${experimental}"` : ''
} style="width:100%;height:100%;margin:0;padding:0;"></div>
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
${scriptUris
.map((uri) => `<script ${isProd ? `nonce="${nonce}"` : ''} src="${uri}"></script>`)
.join('\n')}
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`chatbot-${version.installedVersion}`}" alt="Daily usage" />
</body>
</html>
`;
Telemetry.send(TelemetryEvent.openChatbot);
}
}

View File

@@ -3,9 +3,7 @@ import {
CONTEXT,
ExtensionState,
SETTING_EXPERIMENTAL,
SETTING_EXTENSIBILITY_SCRIPTS,
COMMAND_NAME,
TelemetryEvent
COMMAND_NAME
} from '../constants';
import { join } from 'path';
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from 'vscode';
@@ -18,7 +16,6 @@ import {
DashboardListener,
MediaListener,
SettingsListener,
TelemetryListener,
DataListener,
PagesListener,
ExtensionListener,
@@ -29,12 +26,11 @@ import {
} from '../listeners/dashboard';
import { MediaListener as PanelMediaListener } from '../listeners/panel';
import { GitListener, ModeListener } from '../listeners/general';
import { Folders } from './Folders';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
import { NavigationType } from '../dashboardWebView/models';
import { ignoreMsgCommand } from '../utils';
import { getExtensibilityScripts, getWebviewJsFiles, ignoreMsgCommand } from '../utils';
export class Dashboard {
private static webview: WebviewPanel | null = null;
@@ -67,7 +63,6 @@ export class Dashboard {
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboard, (data?: DashboardData) => {
Telemetry.send(TelemetryEvent.openContentDashboard);
if (!data) {
Dashboard.open({ type: NavigationType.Contents });
} else {
@@ -78,35 +73,30 @@ export class Dashboard {
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboardMedia, () => {
Telemetry.send(TelemetryEvent.openMediaDashboard);
Dashboard.open({ type: NavigationType.Media });
})
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboardSnippets, () => {
Telemetry.send(TelemetryEvent.openSnippetsDashboard);
Dashboard.open({ type: NavigationType.Snippets });
})
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboardData, () => {
Telemetry.send(TelemetryEvent.openDataDashboard);
Dashboard.open({ type: NavigationType.Data });
})
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboardTaxonomy, () => {
Telemetry.send(TelemetryEvent.openTaxonomyDashboard);
Dashboard.open({ type: NavigationType.Taxonomy });
})
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.dashboardClose, () => {
Telemetry.send(TelemetryEvent.closeDashboard);
Dashboard.close();
})
);
@@ -241,7 +231,6 @@ export class Dashboard {
PagesListener.process(msg);
SettingsListener.process(msg);
DataListener.process(msg);
TelemetryListener.process(msg);
SnippetListener.process(msg);
ModeListener.process(msg);
GitListener.process(msg);
@@ -285,18 +274,17 @@ export class Dashboard {
* @param webView
*/
private static async getWebviewContent(webView: Webview, extensionPath: Uri): Promise<string> {
const dashboardFile = 'dashboardWebView.js';
const webviewFile = 'dashboard.main.js';
const localPort = `9000`;
const localServerUrl = `localhost:${localPort}`;
let scriptUri = '';
const isProd = Extension.getInstance().isProductionMode;
let scriptUris = [];
if (isProd) {
scriptUri = webView
.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile))
.toString();
scriptUris = await getWebviewJsFiles('dashboard', webView);
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUris.push(`http://${localServerUrl}/${webviewFile}`);
}
const nonce = WebviewHelper.getNonce();
@@ -307,20 +295,8 @@ export class Dashboard {
// Get experimental setting
const experimental = SettingsHelper.get(SETTING_EXPERIMENTAL);
const extensibilityScripts = SettingsHelper.get<string[]>(SETTING_EXTENSIBILITY_SCRIPTS) || [];
const scriptsToLoad: string[] = [];
if (experimental) {
for (const script of extensibilityScripts) {
if (script.startsWith('https://')) {
scriptsToLoad.push(script);
} else {
const absScriptPath = Folders.getAbsFilePath(script);
const scriptUri = webView.asWebviewUri(Uri.file(absScriptPath));
scriptsToLoad.push(scriptUri.toString());
}
}
}
const scriptsToLoad: string[] = getExtensibilityScripts(webView);
const csp = [
`default-src 'none';`,
@@ -374,7 +350,9 @@ export class Dashboard {
})
.join('')}
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
${scriptUris
.map((uri) => `<script ${isProd ? `nonce="${nonce}"` : ''} src="${uri}"></script>`)
.join('\n')}
<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" />
</body>

View File

@@ -1,5 +1,5 @@
import { Folders } from './Folders';
import { ViewColumn, commands, workspace } from 'vscode';
import { ViewColumn, commands, version, workspace } from 'vscode';
import ContentProvider from '../providers/ContentProvider';
import { join } from 'path';
import { ContentFolder } from '../models';
@@ -37,6 +37,8 @@ export class Diagnostics {
Beta: \`${Extension.getInstance().isBetaVersion()}\`
Version: \`${Extension.getInstance().version}\`
OS: \`${process.platform}\`
VSCode version: \`${version}\`
## Project name

View File

@@ -6,8 +6,7 @@ import {
SETTING_CONTENT_PAGE_FOLDERS,
SETTING_CONTENT_STATIC_FOLDER,
SETTING_CONTENT_SUPPORTED_FILETYPES,
SETTING_DATE_FORMAT,
TelemetryEvent
SETTING_DATE_FORMAT
} from './../constants';
import { commands, Uri, workspace, window } from 'vscode';
import { basename, dirname, join, relative, sep } from 'path';
@@ -30,10 +29,9 @@ import { parseWinPath } from '../helpers/parseWinPath';
import { MediaHelpers } from '../helpers/MediaHelpers';
import { MediaListener, PagesListener, SettingsListener } from '../listeners/dashboard';
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
import { Telemetry } from '../helpers/Telemetry';
import { glob } from 'glob';
import { mkdirAsync } from '../utils/mkdirAsync';
import { existsAsync, isWindows } from '../utils';
import { existsAsync, isWindows, lstatAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { Preview } from './Preview';
@@ -107,8 +105,6 @@ export class Folders {
MediaHelpers.resetMedia();
MediaListener.sendMediaFiles(0, folderPath);
}
Telemetry.send(TelemetryEvent.addMediaFolder);
}
/**
@@ -183,8 +179,6 @@ export class Folders {
Notifications.info(l10n.t(LocalizationKey.commandsFoldersCreateSuccess));
Telemetry.send(TelemetryEvent.registerFolder);
SettingsListener.getSettings(true);
}
}
@@ -198,8 +192,6 @@ export class Folders {
let folders = await Folders.get();
folders = folders.filter((f) => f.path !== folder.fsPath);
await Folders.update(folders);
Telemetry.send(TelemetryEvent.unregisterFolder);
}
}
@@ -565,6 +557,22 @@ export class Folders {
return parseWinPath(absPath);
}
/**
* Converts a given file path to a workspace-relative path.
*
* @param path - The file path to convert.
* @returns The workspace-relative path.
*/
public static wsPath(path: string) {
const wsFolder = Folders.getWorkspaceFolder();
let absPath = parseWinPath(path).replace(
parseWinPath(wsFolder?.fsPath || ''),
WORKSPACE_PLACEHOLDER
);
absPath = isWindows() ? absPath.split('\\').join('/') : absPath;
return absPath;
}
/**
* Generate relative folder path
* @param folder
@@ -835,9 +843,26 @@ export class Folders {
try {
pattern = isWindows() ? parseWinPath(pattern) : pattern;
const files = await glob(pattern, { ignore: '**/node_modules/**', dot: true });
const allFolders = (files || []).map((file) => dirname(file));
const uniqueFolders = [...new Set(allFolders)];
const folders = await glob(pattern, {
ignore: 'node_modules/**',
dot: true
});
const onlyFolders = [];
for (const folder of folders) {
try {
const stats = await lstatAsync(folder);
if (stats.isDirectory()) {
onlyFolders.push(folder);
} else {
onlyFolders.push(dirname(folder));
}
} catch (e) {
continue;
}
}
const uniqueFolders = [...new Set(onlyFolders)];
Logger.verbose(`Folders:findFolders:end - ${uniqueFolders.length}`);
return uniqueFolders;
} catch (e) {
@@ -856,7 +881,7 @@ export class Folders {
try {
pattern = isWindows() ? parseWinPath(pattern) : pattern;
const files = await glob(pattern, { ignore: '**/node_modules/**', dot: true });
const files = await glob(pattern, { ignore: 'node_modules/**', dot: true });
const allFiles = (files || []).map((file) => Uri.file(file));
Logger.verbose(`Folders:findFiles:end - ${allFiles.length}`);
return allFiles;

View File

@@ -2,7 +2,6 @@ import {
SETTING_PREVIEW_HOST,
SETTING_PREVIEW_PATHNAME,
CONTEXT,
TelemetryEvent,
PreviewCommands,
SETTING_EXPERIMENTAL,
SETTING_DATE_FORMAT,
@@ -20,7 +19,6 @@ import {
processFmPlaceholders,
processPathPlaceholders,
Settings,
Telemetry,
processDateTimePlaceholders
} from '../helpers';
import { ContentFolder, ContentType, PreviewSettings } from '../models';
@@ -31,7 +29,7 @@ import { ParsedFrontMatter } from '../parsers';
import { getLocalizationFile } from '../utils/getLocalizationFile';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { joinUrl } from '../utils';
import { getTitleField, getWebviewJsFiles, joinUrl } from '../utils';
import { i18n } from './i18n';
export class Preview {
@@ -77,11 +75,13 @@ export class Preview {
return;
}
const titleField = getTitleField();
// Create the preview webview
const webView = window.createWebviewPanel(
'frontMatterPreview',
article?.data?.title
? l10n.t(LocalizationKey.commandsPreviewPanelTitle, article?.data.title)
article?.data && article?.data[titleField]
? l10n.t(LocalizationKey.commandsPreviewPanelTitle, article?.data[titleField])
: 'Front Matter Preview',
{
viewColumn: ViewColumn.Beside,
@@ -134,7 +134,7 @@ export class Preview {
}
});
const dashboardFile = 'dashboardWebView.js';
const webviewFile = 'dashboard.main.js';
const localPort = `9000`;
const localServerUrl = `localhost:${localPort}`;
@@ -144,7 +144,6 @@ export class Preview {
const isProd = ext.isProductionMode;
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
const extensionUri = ext.extensionPath;
const csp = [
`default-src 'none';`,
@@ -161,13 +160,11 @@ export class Preview {
`frame-src ${localhostUrl} ${cspSource} http: https:;`
];
let scriptUri = '';
let scriptUris = [];
if (isProd) {
scriptUri = webView.webview
.asWebviewUri(Uri.joinPath(extensionUri, 'dist', dashboardFile))
.toString();
scriptUris = await getWebviewJsFiles('dashboard', webView.webview);
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUris.push(`http://${localServerUrl}/${webviewFile}`);
}
// Get experimental setting
@@ -193,12 +190,14 @@ export class Preview {
experimental ? `data-experimental="${experimental}"` : ''
} style="width:100%;height:100%;margin:0;padding:0;"></div>
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
${scriptUris
.map((uri) => `<script ${isProd ? `nonce="${nonce}"` : ''} src="${uri}"></script>`)
.join('\n')}
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`preview-${version.installedVersion}`}" alt="Daily usage" />
</body>
</html>
`;
Telemetry.send(TelemetryEvent.openPreview);
}
/**
@@ -312,7 +311,7 @@ export class Preview {
try {
const articleDate = await ArticleHelper.getDate(article);
pathname = processDateTimePlaceholders(pathname, dateFormat, articleDate);
pathname = processDateTimePlaceholders(pathname, articleDate);
slug = join(pathname, slug);
} catch (error) {
slug = join(pathname, slug);

View File

@@ -1,5 +1,4 @@
import { DEFAULT_CONTENT_TYPE } from './../constants/ContentType';
import { Telemetry } from './../helpers/Telemetry';
import { workspace, Uri, commands, window } from 'vscode';
import { join } from 'path';
import { Notifications } from '../helpers/Notifications';
@@ -16,8 +15,7 @@ import {
import {
COMMAND_NAME,
SETTING_CONTENT_DEFAULT_FILETYPE,
SETTING_TAXONOMY_CONTENT_TYPES,
TelemetryEvent
SETTING_TAXONOMY_CONTENT_TYPES
} from '../constants';
import { SettingsListener } from '../listeners/dashboard';
import { existsAsync, writeFileAsync } from '../utils';
@@ -96,8 +94,6 @@ categories: []
// Initialize the taxonomy database
TaxonomyHelper.initDb();
Telemetry.send(TelemetryEvent.initialization);
// Check if you can find the framework
const wsFolder = Folders.getWorkspaceFolder();
const framework = await FrameworkDetector.get(wsFolder?.fsPath || '');

View File

@@ -3,15 +3,12 @@ import {
CONTEXT,
EXTENSION_NAME,
NOTIFICATION_TYPE,
SETTING_SEO_DESCRIPTION_FIELD,
SETTING_SEO_DESCRIPTION_LENGTH,
SETTING_SEO_TITLE_FIELD,
SETTING_SEO_TITLE_LENGTH
} from './../constants';
import * as vscode from 'vscode';
import { ArticleHelper, Notifications, SeoHelper, Settings } from '../helpers';
import { PanelProvider } from '../panelWebView/PanelProvider';
import { DefaultFields } from '../constants';
import { ContentType } from '../helpers/ContentType';
import { DataListener } from '../listeners/panel';
import { commands } from 'vscode';
@@ -20,6 +17,7 @@ import { Preview } from './Preview';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { i18n } from './i18n';
import { getDescriptionField, getTitleField } from '../utils';
export class StatusListener {
/**
@@ -56,12 +54,10 @@ export class StatusListener {
collection.clear();
// Retrieve the SEO config properties
const titleLength = (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1;
const descLength = (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1;
const titleField =
(Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const descriptionField =
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
const titleLength = Settings.get<number>(SETTING_SEO_TITLE_LENGTH) || -1;
const descLength = Settings.get<number>(SETTING_SEO_DESCRIPTION_LENGTH) || -1;
const titleField = getTitleField();
const descriptionField = getDescriptionField();
if (editor && article.data[titleField] && titleLength > -1) {
SeoHelper.checkLength(editor, collection, article, titleField, titleLength);

33
src/commands/Taxonomy.ts Normal file
View File

@@ -0,0 +1,33 @@
import { commands } from 'vscode';
import { COMMAND_NAME } from '../constants';
import { TagType } from '../panelWebView/TagType';
import { PanelProvider } from '../panelWebView/PanelProvider';
export class Taxonomy {
/**
* Registers the commands for the Article class.
*
* @param subscriptions - The array of subscriptions to register the commands with.
*/
public static async registerCommands(subscriptions: unknown[]) {
const explorerSidebar = PanelProvider.getInstance();
if (explorerSidebar) {
subscriptions.push(
commands.registerCommand(COMMAND_NAME.insertTags, async () => {
await commands.executeCommand('workbench.view.extension.frontmatter-explorer');
await commands.executeCommand('workbench.action.focusSideBar');
explorerSidebar.triggerInputFocus(TagType.tags);
})
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.insertCategories, async () => {
await commands.executeCommand('workbench.view.extension.frontmatter-explorer');
await commands.executeCommand('workbench.action.focusSideBar');
explorerSidebar.triggerInputFocus(TagType.categories);
})
);
}
}
}

View File

@@ -3,9 +3,9 @@ import * as vscode from 'vscode';
import * as path from 'path';
import {
COMMAND_NAME,
DefaultFields,
SETTING_CONTENT_DEFAULT_FILETYPE,
SETTING_TEMPLATES_FOLDER,
TelemetryEvent
SETTING_TEMPLATES_FOLDER
} from '../constants';
import { ArticleHelper, Extension, Settings } from '../helpers';
import { Article, Folders } from '.';
@@ -15,7 +15,6 @@ 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';
import { writeFileAsync, copyFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
@@ -163,8 +162,14 @@ export class Template {
const templateData = await ArticleHelper.getFrontMatterByPath(template.fsPath);
let contentType: IContentType | undefined;
if (templateData && templateData.data && templateData.data.type) {
contentType = contentTypes?.find((t) => t.name === templateData.data.type);
if (templateData && templateData.data) {
if (templateData.data[DefaultFields.ContentType]) {
contentType = contentTypes?.find(
(t) => t.name === templateData.data[DefaultFields.ContentType]
);
} else if (templateData.data[DefaultFields.Type]) {
contentType = contentTypes?.find((t) => t.name === templateData.data[DefaultFields.Type]);
}
}
const fileExtension = extname(template.fsPath).replace('.', '');
@@ -217,8 +222,6 @@ export class Template {
Notifications.info(l10n.t(LocalizationKey.commandsTemplateCreateSuccess));
Telemetry.send(TelemetryEvent.createContentFromTemplate);
// Trigger a refresh for the dashboard
PagesListener.refresh();
}

View File

@@ -1,8 +1,7 @@
import { commands, window, Selection, QuickPickItem, TextEditor } from 'vscode';
import { COMMAND_NAME, CONTEXT, SETTING_CONTENT_WYSIWYG } from '../constants';
import { Settings } from '../helpers';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { LocalizationKey, localize } from '../localization';
enum MarkupType {
bold = 1,
@@ -18,6 +17,8 @@ enum MarkupType {
hyperlink
}
type DocType = 'markdown' | 'asciidoc';
export class Wysiwyg {
/**
* Registers the markup commands for the WYSIWYG controls
@@ -83,45 +84,45 @@ export class Wysiwyg {
commands.registerCommand(COMMAND_NAME.options, async () => {
const qpItems: QuickPickItem[] = [
{
label: `$(list-unordered) ${LocalizationKey.commandsWysiwygCommandUnorderedListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandUnorderedListDetail,
label: `$(list-unordered) ${localize(LocalizationKey.commandsWysiwygCommandUnorderedListLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandUnorderedListDetail),
alwaysShow: true
},
{
label: `$(list-ordered) ${LocalizationKey.commandsWysiwygCommandOrderedListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandOrderedListDetail,
label: `$(list-ordered) ${localize(LocalizationKey.commandsWysiwygCommandOrderedListLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandOrderedListDetail),
alwaysShow: true
},
{
label: `$(tasklist) ${LocalizationKey.commandsWysiwygCommandTaskListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandTaskListDetail,
label: `$(tasklist) ${localize(LocalizationKey.commandsWysiwygCommandTaskListLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandTaskListDetail),
alwaysShow: true
},
{
label: `$(code) ${LocalizationKey.commandsWysiwygCommandCodeLabel}`,
detail: LocalizationKey.commandsWysiwygCommandCodeDetail,
label: `$(code) ${localize(LocalizationKey.commandsWysiwygCommandCodeLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandCodeDetail),
alwaysShow: true
},
{
label: `$(symbol-namespace) ${LocalizationKey.commandsWysiwygCommandCodeblockLabel}`,
detail: LocalizationKey.commandsWysiwygCommandCodeblockDetail,
label: `$(symbol-namespace) ${localize(LocalizationKey.commandsWysiwygCommandCodeblockLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandCodeblockDetail),
alwaysShow: true
},
{
label: `$(quote) ${LocalizationKey.commandsWysiwygCommandBlockquoteLabel}`,
detail: LocalizationKey.commandsWysiwygCommandBlockquoteDetail,
label: `$(quote) ${localize(LocalizationKey.commandsWysiwygCommandBlockquoteLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandBlockquoteDetail),
alwaysShow: true
},
{
label: `$(symbol-text) ${LocalizationKey.commandsWysiwygCommandStrikethroughLabel}`,
detail: LocalizationKey.commandsWysiwygCommandStrikethroughDetail,
label: `$(symbol-text) ${localize(LocalizationKey.commandsWysiwygCommandStrikethroughLabel)}`,
detail: localize(LocalizationKey.commandsWysiwygCommandStrikethroughDetail),
alwaysShow: true
}
];
const option = await window.showQuickPick([...qpItems], {
title: l10n.t(LocalizationKey.commandsWysiwygQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygQuickPickPlaceholder),
title: localize(LocalizationKey.commandsWysiwygQuickPickTitle),
placeHolder: localize(LocalizationKey.commandsWysiwygQuickPickPlaceholder),
canPickMany: false,
ignoreFocusOut: true
});
@@ -147,6 +148,15 @@ export class Wysiwyg {
);
}
/**
* Retrieves the document type based on the file extension.
* @param filePath - The path of the file.
* @returns The document type ('asciidoc' or 'markdown').
*/
public static getDocType(filePath: string): DocType {
return filePath.endsWith('.adoc') ? 'asciidoc' : 'markdown';
}
/**
* Add the markup to the content
* @param type
@@ -161,11 +171,12 @@ export class Wysiwyg {
const selection = editor.selection;
const hasTextSelection = !selection.isEmpty;
const docType: DocType = Wysiwyg.getDocType(editor.document.fileName);
if (type === MarkupType.hyperlink) {
return this.addHyperlink(editor, selection);
return this.addHyperlink(editor, selection, docType);
}
const markers = this.getMarkers(type);
const markers = this.getMarkers(type, docType);
if (!markers) {
return;
}
@@ -175,13 +186,13 @@ export class Wysiwyg {
if (hasTextSelection) {
// Replace the selection and surround with the markup
const selectionText = editor.document.getText(selection);
const txt = await this.insertText(markers, type, selectionText);
const txt = await this.insertText(markers, type, selectionText, docType);
editor.edit((builder) => {
builder.replace(selection, txt);
});
} else {
const txt = await this.insertText(markers, type);
const txt = await this.insertText(markers, type, null, docType);
// Insert the markers where cursor is located.
const markerLength = this.isMarkupWrapping(type) ? txt.length + 1 : markers.length;
@@ -198,6 +209,10 @@ export class Wysiwyg {
newPosition = crntSelection.with(crntSelection.line + 1, 0);
}
if (type === MarkupType.blockquote && docType === 'asciidoc') {
newPosition = crntSelection.with(crntSelection.line + 1, 0);
}
editor.selection = new Selection(newPosition, newPosition);
}
}
@@ -206,28 +221,39 @@ export class Wysiwyg {
* Add a hyperlink to the content
* @returns void
*/
private static async addHyperlink(editor: TextEditor, selection: Selection) {
private static async addHyperlink(
editor: TextEditor,
selection: Selection,
docType: DocType = 'markdown'
) {
const hasTextSelection = !selection.isEmpty;
const linkText = hasTextSelection ? editor.document.getText(selection) : '';
const link = await window.showInputBox({
title: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
prompt: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
title: localize(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputTitle),
placeHolder: localize(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
prompt: localize(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
value: linkText,
ignoreFocusOut: true
});
const text = await window.showInputBox({
title: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputTitle),
prompt: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
title: localize(LocalizationKey.commandsWysiwygAddHyperlinkTextInputTitle),
prompt: localize(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
placeHolder: localize(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
value: linkText,
ignoreFocusOut: true
});
if (link) {
const txt = `[${text || link}](${link})`;
let txt = `[${text || link}](${link})`;
if (docType === 'asciidoc') {
txt = !link.startsWith('http') ? `link:${link}` : link;
if (text) {
txt = `${txt}[${text}]`;
}
}
if (hasTextSelection) {
editor.edit((builder) => {
@@ -255,14 +281,23 @@ export class Wysiwyg {
* @param type
* @returns
*/
private static isMarkupWrapping(type: MarkupType) {
return (
type === MarkupType.blockquote ||
type === MarkupType.heading ||
type === MarkupType.unorderedList ||
type === MarkupType.orderedList ||
type === MarkupType.taskList
);
private static isMarkupWrapping(type: MarkupType, docType: DocType = 'markdown') {
if (docType === 'markdown') {
return (
type === MarkupType.blockquote ||
type === MarkupType.heading ||
type === MarkupType.unorderedList ||
type === MarkupType.orderedList ||
type === MarkupType.taskList
);
} else if (docType === 'asciidoc') {
return (
type === MarkupType.heading ||
type === MarkupType.unorderedList ||
type === MarkupType.orderedList ||
type === MarkupType.taskList
);
}
}
/**
@@ -271,17 +306,18 @@ export class Wysiwyg {
private static async insertText(
marker: string | undefined,
type: MarkupType,
text: string | null = null
text: string | null = null,
docType: DocType = 'markdown'
) {
const crntText = text || this.lineBreak(type);
const crntText = text || this.lineBreak(type, docType);
if (this.isMarkupWrapping(type)) {
if (this.isMarkupWrapping(type, docType)) {
if (type === MarkupType.heading) {
const headingLvl = await window.showQuickPick(
['Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6'],
{
title: l10n.t(LocalizationKey.commandsWysiwygInsertTextHeadingInputTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygInsertTextHeadingInputPlaceholder),
title: localize(LocalizationKey.commandsWysiwygInsertTextHeadingInputTitle),
placeHolder: localize(LocalizationKey.commandsWysiwygInsertTextHeadingInputPlaceholder),
canPickMany: false,
ignoreFocusOut: true
}
@@ -298,9 +334,12 @@ export class Wysiwyg {
return lines.join('\n');
}
if (type === MarkupType.orderedList) {
if (type === MarkupType.orderedList && docType === 'markdown') {
const lines = crntText.split('\n').map((line, idx) => `${idx + 1}. ${line}`);
return lines.join('\n');
} else if (type === MarkupType.orderedList && docType === 'asciidoc') {
const lines = crntText.split('\n').map((line) => `${marker} ${line}`);
return lines.join('\n');
}
return `${marker} ${crntText}`;
@@ -314,9 +353,11 @@ export class Wysiwyg {
* @param type
* @returns
*/
private static lineBreak(type: MarkupType) {
private static lineBreak(type: MarkupType, docType: DocType = 'markdown') {
if (type === MarkupType.codeblock) {
return `\n\n`;
} else if (type === MarkupType.blockquote && docType === 'asciidoc') {
return `\n\n`;
}
return '';
}
@@ -326,30 +367,57 @@ export class Wysiwyg {
* @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;
private static getMarkers(type: MarkupType, docType: DocType = 'markdown') {
if (docType === 'markdown') {
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;
}
} else if (docType === 'asciidoc') {
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 '.';
case MarkupType.taskList:
return '* [ ]';
default:
return;
}
}
}
}

View File

@@ -12,7 +12,7 @@ import {
import { COMMAND_NAME, SETTING_CONTENT_I18N } from '../constants';
import { ContentFolder, Field, I18nConfig, ContentType as IContentType } from '../models';
import { join, parse } from 'path';
import { existsAsync } from '../utils';
import { existsAsync, getDescriptionField, getTitleField } from '../utils';
import { Folders } from '.';
import { ParsedFrontMatter } from '../parsers';
import { PagesListener } from '../listeners/dashboard';
@@ -412,8 +412,11 @@ export class i18n {
},
async () => {
try {
const title = article.data.title || '';
const description = article.data.description || '';
const titleField = getTitleField();
const descriptionField = getDescriptionField();
const title = article.data[titleField] || '';
const description = article.data[descriptionField] || '';
const content = article.content || '';
const text = [title, description, content];
@@ -428,8 +431,8 @@ export class i18n {
return;
}
article.data.title = article.data.title ? translations[0] : '';
article.data.description = article.data.description ? translations[1] : '';
article.data[titleField] = article.data[titleField] ? translations[0] : '';
article.data[descriptionField] = article.data[descriptionField] ? translations[1] : '';
article.content = article.content ? translations[2] : '';
} catch (error) {
Notifications.error(`${(error as Error).message}`);

View File

@@ -10,5 +10,7 @@ export * from './Preview';
export * from './Project';
export * from './Settings';
export * from './StatusListener';
export * from './Taxonomy';
export * from './Template';
export * from './Wysiwyg';
export * from './i18n';

View File

@@ -3,5 +3,8 @@ export const DefaultFields = {
LastModified: `lastmod`,
Description: `description`,
Title: `title`,
Slug: `slug`
Slug: `slug`,
Type: `type`,
ContentType: `fmContentType`,
Keywords: `keywords`
};

View File

@@ -1,10 +1,10 @@
const extensionName = 'frontMatter';
export const EXTENSION_COMMAND_PREFIX = 'frontMatter';
export const EXTENSION_ID = 'eliostruyf.vscode-front-matter';
export const EXTENSION_BETA_ID = 'eliostruyf.vscode-front-matter-beta';
export const getCommandName = (command: string) => {
return `${extensionName}.${command}`;
return `${EXTENSION_COMMAND_PREFIX}.${command}`;
};
export const COMMAND_NAME = {

View File

@@ -14,8 +14,20 @@ export const DOCS_SUBMODULES = 'https://frontmatter.codes/docs/git-integration#g
export const WEBSITE_LINKS = {
root: 'https://frontmatter.codes',
api: {
metrics: 'https://frontmatter.codes/api/metrics',
ai: 'https://frontmatter.codes/api/ai'
baseUrl: 'https://api.frontmatter.codes',
endpoints: {
ai: {
description: '/ai/description',
taxonomy: '/ai/taxonomy',
title: '/ai/title'
},
chat: {
init: '/ai-init',
message: '/ai-chat',
feedback: '/ai-feedback'
},
backers: '/v2/backers'
}
},
docs: {
dataDashboard: 'https://frontmatter.codes/docs/dashboard/datafiles-view',

View File

@@ -1,56 +0,0 @@
export const TelemetryEvent = {
activate: 'activate',
initialization: 'initialization',
registerFolder: 'registerFolder',
unregisterFolder: 'unregisterFolder',
promoteSettings: 'promoteSettings',
// Commands
openContentDashboard: 'openContentDashboard',
openMediaDashboard: 'openMediaDashboard',
openDataDashboard: 'openDataDashboard',
openSnippetsDashboard: 'openSnippetsDashboard',
openTaxonomyDashboard: 'openTaxonomyDashboard',
closeDashboard: 'closeDashboard',
// Other actions
generateSlug: 'generateSlug',
createContentFromTemplate: 'createContentFromTemplate',
createContentFromContentType: 'createContentFromContentType',
addMediaFolder: 'addMediaFolder',
openPreview: 'openPreview',
uploadMedia: 'uploadMedia',
refreshMedia: 'refreshMedia',
deleteMedia: 'deleteMedia',
insertContentSnippet: 'insertContentSnippet',
insertMediaToContent: 'insertMediaToContent',
insertFileToContent: 'insertFileToContent',
updateMediaMetadata: 'updateMediaMetadata',
openPanelWebview: 'openPanelWebview',
// Chatbot
openChatbot: 'openChatbot',
// Content types
generateContentType: 'generateContentType',
addMissingFields: 'addMissingFields',
setContentType: 'setContentType',
// Custom scripts
runCustomScript: 'runCustomScript',
runMediaScript: 'runMediaScript',
// Webviews
webviewWelcomeScreen: 'webviewWelcomeScreen',
webviewMediaView: 'webviewMediaView',
webviewDataView: 'webviewDataView',
webviewContentsView: 'webviewContentsView',
webviewSnippetsView: 'webviewSnippetsView',
webviewTaxonomyDashboard: 'webviewTaxonomyDashboard',
webviewSettings: 'webviewSettings',
webviewUnknown: 'webviewUnknown',
// Git
gitSync: 'gitSync',
gitFetch: 'gitFetch'
};

View File

@@ -1,4 +1,5 @@
export * from './ContentType';
export * from './DefaultFeatureFlags';
export * from './DefaultFieldValues';
export * from './DefaultFields';
export * from './DefaultFileTypes';
@@ -17,7 +18,6 @@ export * from './SentryIgnore';
export * from './Snippet';
export * from './SsgScripts';
export * from './StaticFolderPlaceholder';
export * from './TelemetryEvent';
export * from './Templates';
export * from './charCode';
export * from './charMap';

View File

@@ -45,8 +45,6 @@ export const SETTING_TEMPLATES_FOLDER = 'templates.folder';
export const SETTING_TEMPLATES_PREFIX = 'templates.prefix';
export const SETTING_TEMPLATES_ENABLED = 'templates.enabled';
export const SETTING_TELEMETRY_DISABLE = 'telemetry.disable';
export const SETTING_PANEL_FREEFORM = 'panel.freeform';
export const SETTING_PANEL_ACTIONS_DISABLED = 'panel.actions.disabled';
@@ -115,6 +113,8 @@ export const SETTING_SNIPPETS_WRAPPER = 'snippets.wrapper.enabled';
export const SETTING_WEBSITE_URL = 'website.host';
export const SETTING_COPILOT_FAMILY = 'copilot.family';
export const SETTING_LOGGING = 'logging';
/**

View File

@@ -50,6 +50,7 @@ export enum DashboardMessage {
// Data dashboard
getDataEntries = 'getDataEntries',
putDataEntries = 'putDataEntries',
createDataFile = 'createDataFile',
// Snippets dashboard
insertSnippet = 'insertSnippet',
@@ -74,7 +75,6 @@ export enum DashboardMessage {
setState = 'setState',
getState = 'getState',
runCustomScript = 'runCustomScript',
sendTelemetry = 'sendTelemetry',
showNotification = 'showNotification',
setTitle = 'setTitle',

View File

@@ -10,7 +10,7 @@ import { AiInitResponse } from './models/AiInitResponse';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { messageHandler } from '@estruyf/vscode/dist/client';
import { GeneralCommands } from '../../../constants';
import { GeneralCommands, WEBSITE_LINKS } from '../../../constants';
export interface IChatbotProps { }
@@ -36,7 +36,7 @@ export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.Props
setLocaleReady(true);
});
const initResponse = await fetch(`${aiUrl}/api/ai-init`);
const initResponse = await fetch(`${aiUrl}${WEBSITE_LINKS.api.endpoints.chat.init}`);
if (!initResponse.ok) {
return;
@@ -70,7 +70,7 @@ export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.Props
return;
}
const response = await fetch(`${aiUrl}/api/ai-chat`, {
const response = await fetch(`${aiUrl}${WEBSITE_LINKS.api.endpoints.chat.message}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -3,6 +3,7 @@ import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
import { HandThumbDownIcon as ThumbDownSolidIcon, HandThumbUpIcon as ThumbUpSolidIcon } from '@heroicons/react/24/solid';
import { useCallback } from 'react';
import { useSettingsContext } from '../../providers/SettingsProvider';
import { WEBSITE_LINKS } from '../../../constants';
export interface IFeedbackProps {
answerId: number;
@@ -28,7 +29,7 @@ export const Feedback: React.FunctionComponent<IFeedbackProps> = ({
}, []);
const callVote = useCallback(async (vote: boolean) => {
await fetch(`${aiUrl}/api/ai-feedback`, {
await fetch(`${aiUrl}${WEBSITE_LINKS.api.endpoints.chat.feedback}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import useSelectedItems from '../../hooks/useSelectedItems';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import { Checkbox as VSCodeCheckbox } from 'vscrui';
import { useMemo } from 'react';
export interface IItemSelectionProps {
@@ -24,11 +24,8 @@ export const ItemSelection: React.FunctionComponent<IItemSelectionProps> = ({
return (
<div className={`${cssNames} group-hover:block`}>
<VSCodeCheckbox
style={{
boxShadow: show ? "" : "0 0 3px var(--frontmatter-border-preserve)"
}}
onClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.stopPropagation();
className={show ? "" : " shadow-[0_0_3px_var(--frontmatter-border-preserve)]"}
onChange={() => {
onMultiSelect(filePath);
}}
checked={selectedFiles.includes(filePath)} />

View File

@@ -10,7 +10,7 @@ import usePages from '../../hooks/usePages';
import { useCallback, useEffect, useState } from 'react';
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { GeneralCommands, TelemetryEvent } from '../../../constants';
import { GeneralCommands } from '../../../constants';
import { PageLayout } from '../Layout/PageLayout';
import { FilesProvider } from '../../providers/FilesProvider';
import { Alert } from '../Modals/Alert';
@@ -67,10 +67,6 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({
}, [JSON.stringify(pageItems)]);
useEffect(() => {
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewContentsView
});
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsContents));
}, []);

View File

@@ -38,7 +38,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
date: pageData.date,
title: pageData.title,
description: pageData.description,
type: pageData.type,
type: pageData.fmContentType,
pageData
});

View File

@@ -6,7 +6,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { groupBy } from '../../../helpers/GroupBy';
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
import { GroupOption } from '../../constants/GroupOption';
import { GroupingSelector, PageAtom, ViewSelector } from '../../state';
import { GroupingSelector, PageAtom, PagedItems, ViewSelector } from '../../state';
import { Item } from './Item';
import { List } from './List';
import usePagination from '../../hooks/usePagination';
@@ -34,6 +34,7 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
const page = useRecoilValue(PageAtom);
const { pageSetNr } = usePagination(settings?.dashboardState.contents.pagination);
const view = useRecoilValue(ViewSelector);
const [, setPagedItems] = useRecoilState(PagedItems);
const pagedPages = useMemo(() => {
if (pageSetNr) {
@@ -100,6 +101,10 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
return { groupKeys, groupedPages };
}, [pages, grouping, settings?.draftField]);
React.useEffect(() => {
setPagedItems(pagedPages.map((page) => page.fmFilePath));
}, [pagedPages]);
React.useEffect(() => {
messageHandler.request<string[]>(DashboardMessage.getPinnedItems).then((items) => {
setIsReady(true);

View File

@@ -5,7 +5,7 @@ import { SettingsSelector } from '../../state';
import { DataForm } from './DataForm';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { DataFile } from '../../../models/DataFile';
import { Messenger } from '@estruyf/vscode/dist/client';
import { messageHandler, Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { SponsorMsg } from '../Layout/SponsorMsg';
import { EventData } from '@estruyf/vscode';
@@ -15,15 +15,18 @@ import { arrayMoveImmutable } from 'array-move';
import { EmptyView } from './EmptyView';
import { Container } from './SortableContainer';
import { SortableItem } from './SortableItem';
import { ChevronRightIcon, CircleStackIcon } from '@heroicons/react/24/outline';
import { ChevronRightIcon, CircleStackIcon, EyeIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { DataType } from '../../../models/DataType';
import { GeneralCommands, TelemetryEvent, WEBSITE_LINKS } from '../../../constants';
import { GeneralCommands, WEBSITE_LINKS } from '../../../constants';
import { NavigationItem } from '../Layout';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { LocalizationKey, localize } from '../../../localization';
import { DropdownMenu, DropdownMenuContent } from '../../../components/shadcn/Dropdown';
import { MenuButton, MenuItem } from '../Menu';
import { Transition } from '@headlessui/react';
import { DataFolder } from '../../../models';
import { ActionsBarItem } from '../Header/ActionsBarItem';
import { Spinner } from '../Common/Spinner';
import { openFile } from '../../utils/MessageHandlers';
export interface IDataViewProps { }
@@ -33,6 +36,7 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
const [selectedData, setSelectedData] = useState<DataFile | null>(null);
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
const [dataEntries, setDataEntries] = useState<any | any[] | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const settings = useRecoilValue(SettingsSelector);
const setSchema = (dataFile: DataFile) => {
@@ -71,6 +75,7 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
return;
}
debugger
const dataClone: any[] = Object.assign([], dataEntries);
if (selectedIndex !== null && selectedIndex !== undefined) {
dataClone[selectedIndex] = data;
@@ -110,11 +115,23 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
entries: data
});
Messenger.send(DashboardMessage.showNotification, l10n.t(LocalizationKey.dashboardDataViewDataViewUpdateMessage));
Messenger.send(DashboardMessage.showNotification, localize(LocalizationKey.dashboardDataViewDataViewUpdateMessage));
},
[selectedData]
);
const createDataFile = (folder: DataFolder) => {
setLoading(true);
messageHandler.request<DataFile>(DashboardMessage.createDataFile, folder).then(dataFile => {
if (dataFile) {
setSchema(dataFile);
}
setLoading(false);
}).catch((_: any) => {
setLoading(false);
});
}
const dataEntry = useMemo(() => {
if (selectedData?.singleEntry) {
return dataEntries || {};
@@ -125,14 +142,42 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
: null;
}, [selectedData, , dataEntries, selectedIndex]);
// Retrieve the data files, check if they have a schema or ID, if not, they shouldn't be shown
const dataFiles = useMemo(() => {
return (settings?.dataFiles || [])
.map((dataFile: DataFile) => {
if (!dataFile.schema && !dataFile.id) {
return null;
}
const clonedFile = Object.assign({}, dataFile);
if (clonedFile.type) {
const dataType = settings?.dataTypes?.find(
(dataType: DataType) => dataType.id === clonedFile.type
);
if (!dataType) {
return null;
}
clonedFile.schema = Object.assign({}, dataType.schema);
}
return clonedFile;
})
.filter((d) => d !== null) as DataFile[];
}, [settings?.dataFiles]);
const fileCreationFolders = useMemo(() => {
return (settings?.dataFolders || [])
.filter((folder) => folder.enableFileCreation);
}, [settings?.dataFolders]);
const hasOnlyDataCreationFolders = useMemo(() => (!dataFiles || dataFiles.length === 0) && (fileCreationFolders && fileCreationFolders.length > 0), [dataFiles, fileCreationFolders]);
useEffect(() => {
Messenger.listen(messageListener);
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsData));
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewDataView
});
Messenger.send(DashboardMessage.setTitle, localize(LocalizationKey.dashboardHeaderTabsData));
Messenger.send(GeneralCommands.toVSCode.logging.info, {
message: 'Data view loaded',
@@ -144,49 +189,29 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
};
}, []);
// Retrieve the data files, check if they have a schema or ID, if not, they shouldn't be shown
const dataFiles = (settings?.dataFiles || [])
.map((dataFile: DataFile) => {
if (!dataFile.schema && !dataFile.id) {
return null;
}
const clonedFile = Object.assign({}, dataFile);
if (clonedFile.type) {
const dataType = settings?.dataTypes?.find(
(dataType: DataType) => dataType.id === clonedFile.type
);
if (!dataType) {
return null;
}
clonedFile.schema = Object.assign({}, dataType.schema);
}
return clonedFile;
})
.filter((d) => d !== null) as DataFile[];
console.log('DataView render', settings?.dataFolders);
return (
<div className="flex flex-col h-full overflow-auto inset-y-0">
<Header settings={settings} />
{dataFiles && dataFiles.length > 0 ? (
{(dataFiles && dataFiles.length > 0) || (fileCreationFolders && fileCreationFolders.length > 0) ? (
<div className={`relative w-full flex-grow mx-auto overflow-hidden`}>
{
!selectedData && (
!selectedData && (dataFiles && dataFiles.length > 0) && (
<div className={`flex w-64 flex-col absolute inset-y-0`}>
<aside
className={`flex flex-col flex-grow overflow-y-auto border-r py-6 px-4 overflow-auto border-[var(--frontmatter-border)]`}
>
<h2 className={`text-lg text-[var(--frontmatter-text)]`}>
{l10n.t(LocalizationKey.dashboardDataViewDataViewSelect)}
{localize(LocalizationKey.dashboardDataViewDataViewSelect)}
</h2>
<nav className={`flex-1 py-4 -mx-4`}>
<div
className={`divide-y border-t border-b divide-[var(--frontmatter-border)] border-[var(--frontmatter-border)]`}
>
{dataFiles &&
dataFiles.length > 0 &&
dataFiles.map((dataFile, idx) => (
@@ -212,30 +237,82 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
enterFrom="opacity-0"
enterTo="opacity-100">
<div className={`w-full px-4 py-2 border-b border-[var(--frontmatter-border)]`}>
{selectedData && (
<DropdownMenu>
<MenuButton
label={l10n.t(LocalizationKey.dashboardDataViewDataViewSelect)}
title={selectedData.title}
/>
<div className={`flex justify-between`}>
<div className={`flex gap-4`}>
{
selectedData && (
<DropdownMenu>
<MenuButton
label={localize(LocalizationKey.dashboardDataViewDataViewSelect)}
title={selectedData.title}
/>
<DropdownMenuContent>
{dataFiles.map((dataFile) => (
<MenuItem
key={dataFile.id}
title={dataFile.title}
value={dataFile}
isCurrent={selectedData.id === dataFile.id}
onClick={() => setSchema(dataFile)}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
)}
<DropdownMenuContent>
{dataFiles.map((dataFile) => (
<MenuItem
key={dataFile.id}
title={dataFile.title}
value={dataFile}
isCurrent={selectedData.id === dataFile.id}
onClick={() => setSchema(dataFile)}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}
{
fileCreationFolders && fileCreationFolders.length > 0 && (
<DropdownMenu>
<MenuButton
label={localize(LocalizationKey.dashboardDataViewDataViewCreateNew)}
title={localize(LocalizationKey.dashboardDataViewDataViewSelectDataFolder)}
/>
<DropdownMenuContent>
{fileCreationFolders.map((folder) => (
<MenuItem
key={folder.id}
title={folder.id}
value={folder}
onClick={() => createDataFile(folder)}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}
</div>
{
selectedData && (
<div className={`flex gap-2`}>
<ActionsBarItem
className='flex items-center'
onClick={() => openFile(selectedData.file)}
title={localize(LocalizationKey.commonView)}
>
<EyeIcon className="w-4 h-4 mr-1" aria-hidden="true" />
<span>{localize(LocalizationKey.commonView)}</span>
</ActionsBarItem>
<ActionsBarItem
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
onClick={() => setSelectedData(null)}
title={localize(LocalizationKey.dashboardDataViewDataViewCloseSelectedDataFile)}
>
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
<span>{localize(LocalizationKey.dashboardDataViewDataViewCloseSelectedDataFile)}</span>
</ActionsBarItem>
</div>
)
}
</div>
</div>
</Transition>
<section className={`flex min-w-0 h-full ease transition-[padding] ${selectedData ? "" : "pl-64"}`}>
<section className={`flex min-w-0 h-full ease transition-[padding] ${selectedData ? "" : hasOnlyDataCreationFolders ? "" : "pl-64"}`}>
{selectedData ? (
<>
{!selectedData.singleEntry && (
@@ -243,7 +320,7 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
className={`w-1/3 py-6 px-4 flex-1 border-r overflow-auto border-[var(--frontmatter-border)]`}
>
<h2 className={`text-lg text-[var(--frontmatter-text)]`}>
{l10n.t(LocalizationKey.dashboardDataViewDataViewTitle, selectedData?.title?.toLowerCase() || '')}
{localize(LocalizationKey.dashboardDataViewDataViewTitle, selectedData?.title?.toLowerCase() || '')}
</h2>
<div className="py-4">
@@ -263,13 +340,13 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
))}
</Container>
<Button className="mt-4" onClick={() => setSelectedIndex(null)}>
{l10n.t(LocalizationKey.dashboardDataViewDataViewAdd)}
{localize(LocalizationKey.dashboardDataViewDataViewAdd)}
</Button>
</>
) : (
<div className={`flex flex-col items-center justify-center`}>
<p className={`text-[var(--frontmatter-text)]`}>
{l10n.t(LocalizationKey.dashboardDataViewDataViewEmpty, selectedData.title.toLowerCase())}
{localize(LocalizationKey.dashboardDataViewDataViewEmpty, selectedData.title.toLowerCase())}
</p>
</div>
)}
@@ -281,7 +358,7 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
} py-6 px-4 overflow-auto`}
>
<h2 className={`text-lg text-[var(--frontmatter-text)]`}>
{l10n.t(LocalizationKey.dashboardDataViewDataViewCreateOrModify, selectedData.title.toLowerCase())}
{localize(LocalizationKey.dashboardDataViewDataViewCreateOrModify, selectedData.title.toLowerCase())}
</h2>
{selectedData ? (
<DataForm
@@ -291,12 +368,14 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
onClear={() => setSelectedIndex(null)}
/>
) : (
<p>{l10n.t(LocalizationKey.dashboardDataViewDataViewGetStarted)}</p>
<p>{localize(LocalizationKey.dashboardDataViewDataViewGetStarted)}</p>
)}
</div>
</>
) : (
<EmptyView />
<EmptyView
folders={fileCreationFolders}
onCreate={createDataFile} />
)}
</section>
</div>
@@ -304,14 +383,14 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
<div className="w-full h-full flex items-center justify-center">
<div className={`flex flex-col items-center text-[var(--frontmatter-text)]`}>
<CircleStackIcon className="w-32 h-32" />
<p className="text-3xl mt-2">{l10n.t(LocalizationKey.dashboardDataViewDataViewNoDataFiles)}</p>
<p className="text-3xl mt-2">{localize(LocalizationKey.dashboardDataViewDataViewNoDataFiles)}</p>
<p className="text-xl mt-4">
<a
className={`text-[var(--frontmatter-link)] hover:text-[var(--frontmatter-link-hover)]`}
href={WEBSITE_LINKS.docs.dataDashboard}
title={l10n.t(LocalizationKey.dashboardDataViewDataViewGetStartedLink)}
title={localize(LocalizationKey.dashboardDataViewDataViewGetStartedLink)}
>
{l10n.t(LocalizationKey.dashboardDataViewDataViewGetStartedLink)}
{localize(LocalizationKey.dashboardDataViewDataViewGetStartedLink)}
</a>
</p>
</div>
@@ -319,6 +398,8 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
)
}
{loading && <Spinner />}
<SponsorMsg
beta={settings?.beta}
version={settings?.versionInfo}

View File

@@ -1,20 +1,56 @@
import { ExclamationCircleIcon } from '@heroicons/react/24/outline';
import * as React from 'react';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { LocalizationKey, localize } from '../../../localization';
import { DataFolder } from '../../../models';
import { DropdownMenu, DropdownMenuContent } from '../../../components/shadcn/Dropdown';
import { MenuButton, MenuItem } from '../Menu';
export interface IEmptyViewProps { }
export interface IEmptyViewProps {
folders: DataFolder[];
onCreate: (folder: DataFolder) => void;
}
export const EmptyView: React.FunctionComponent<IEmptyViewProps> = (
props: React.PropsWithChildren<IEmptyViewProps>
{ folders, onCreate }: React.PropsWithChildren<IEmptyViewProps>
) => {
return (
<div className="flex flex-col items-center justify-center w-full">
<div className="flex flex-col items-center justify-center w-full space-y-2">
<ExclamationCircleIcon className={`w-1/12 opacity-90 text-[var(--frontmatter-secondary-text)]`} />
<h2 className={`text-xl text-[var(--frontmatter-secondary-text)]`}>
{l10n.t(LocalizationKey.dashboardDataViewEmptyViewHeading)}
{
(folders && folders.length > 0) ?
localize(LocalizationKey.dashboardDataViewEmptyViewHeadingCreate) :
l10n.t(LocalizationKey.dashboardDataViewEmptyViewHeading)
}
</h2>
{
onCreate && folders && folders.length > 0 && (
<div className=''>
<DropdownMenu>
<MenuButton
label={localize(LocalizationKey.dashboardDataViewDataViewCreateNew)}
title={localize(LocalizationKey.dashboardDataViewDataViewSelectDataFolder)}
className={`text-lg`}
labelClass={`font-normal text-[var(--frontmatter-secondary-text)]`}
/>
<DropdownMenuContent>
{folders.map((folder) => (
<MenuItem
key={folder.id}
title={folder.id}
value={folder}
onClick={() => onCreate(folder)}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}
</div>
);
};

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { NavigationType, Page } from '../../models';
import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon, LanguageIcon } from '@heroicons/react/24/outline';
import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon, LanguageIcon, CheckIcon } from '@heroicons/react/24/outline';
import { useRecoilState, useRecoilValue } from 'recoil';
import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
import { MultiSelectedItemsAtom, PagedItems, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
import { ActionsBarItem } from './ActionsBarItem';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
@@ -29,6 +29,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const settings = useRecoilValue(SettingsSelector);
const { files } = useFilesContext();
const pagedItems = useRecoilValue(PagedItems);
const viewFile = React.useCallback(() => {
if (selectedFiles.length === 1) {
@@ -66,6 +67,10 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
}
}, [selectedFiles]);
const selectAllItems = React.useCallback(() => {
setSelectedFiles([...pagedItems]);
}, [pagedItems]);
const languageActions = React.useMemo(() => {
const actions: React.ReactNode[] = [];
@@ -192,6 +197,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
<ActionsBarItem
disabled={selectedFiles.length === 0 || selectedFiles.length > 1}
onClick={viewFile}
title={l10n.t(LocalizationKey.commonView)}
>
<EyeIcon className="w-4 h-4 mr-2" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.commonView)}</span>
@@ -205,6 +211,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
messageHandler.send(DashboardMessage.rename, selectedFiles[0]);
setSelectedFiles([]);
}}
title={l10n.t(LocalizationKey.commonRename)}
>
<RenameIcon className="w-4 h-4 mr-2" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.commonRename)}</span>
@@ -221,6 +228,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
path: selectedFiles[0],
action: 'edit'
})}
title={l10n.t(LocalizationKey.commonEdit)}
>
<PencilIcon className="w-4 h-4 mr-2" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.commonEdit)}</span>
@@ -237,25 +245,39 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
className='hover:text-[var(--vscode-statusBarItem-errorBackground)]'
disabled={selectedFiles.length === 0}
onClick={() => setShowAlert(true)}
title={l10n.t(LocalizationKey.commonDelete)}
>
<TrashIcon className="w-4 h-4 mr-2" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.commonDelete)}</span>
</ActionsBarItem>
</div>
{
selectedFiles.length > 0 && (
<button
type="button"
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
onClick={() => setSelectedFiles([])}
>
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
</button>
)
}
</div>
<div className='flex gap-4'>
{
selectedFiles.length > 0 && (
<ActionsBarItem
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
onClick={() => setSelectedFiles([])}
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}
>
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
</ActionsBarItem>
)
}
<ActionsBarItem
disabled={selectedFiles.length === pagedItems.length}
onClick={selectAllItems}
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)}
>
<div className='w-4 h-4 inline-flex items-center justify-center border border-[var(--vscode-sideBar-foreground)] group-hover:border-[var(--vscode-statusBarItem-warningBackground)] rounded mr-1'>
<CheckIcon className="w-3 h-3" aria-hidden="true" />
</div>
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)}</span>
</ActionsBarItem>
</div>
</div >
{showAlert && (
<Alert
@@ -266,7 +288,8 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
dismiss={() => setShowAlert(false)}
trigger={onDeleteConfirm}
/>
)}
)
}
</>
);
};

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { cn } from '../../../utils/cn';
export interface IActionsBarItemProps {
title?: string;
className?: string;
disabled?: boolean;
onClick?: () => void;
@@ -11,11 +12,13 @@ export const ActionsBarItem: React.FunctionComponent<IActionsBarItemProps> = ({
children,
className,
disabled,
onClick
onClick,
title
}: React.PropsWithChildren<IActionsBarItemProps>) => {
return (
<button
type="button"
title={title || ''}
className={cn(`flex items-center text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] disabled:opacity-50 disabled:hover:text-[var(--vscode-tab-inactiveForeground)]`, className)}
onClick={onClick}
disabled={disabled}

View File

@@ -5,8 +5,7 @@ import { DashboardMessage } from '../../DashboardMessage';
import { SETTING_DASHBOARD_OPENONSTART } from '../../../constants';
import * as l10n from "@vscode/l10n"
import { LocalizationKey } from '../../../localization';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import { Checkbox as VSCodeCheckbox } from 'vscrui';
export interface IStartupProps {
settings: Settings | null;
@@ -31,7 +30,7 @@ export const Startup: React.FunctionComponent<IStartupProps> = ({
return (
<VSCodeCheckbox
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.checked)}
onChange={onChange}
checked={isChecked}>
{l10n.t(LocalizationKey.dashboardHeaderStartupLabel)}
</VSCodeCheckbox>

View File

@@ -33,6 +33,9 @@ export const CustomActions: React.FunctionComponent<ICustomActionsProps> = ({
));
}, [scripts]);
if (!customActions.length) {
return null;
}
if (showTrigger) {
return (

View File

@@ -1,10 +1,11 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { ArrowUpTrayIcon } from '@heroicons/react/24/outline';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import {
LoadingAtom,
MediaFoldersAtom,
PagedItems,
SelectedMediaFolderAtom,
SettingsSelector,
ViewDataSelector
@@ -20,7 +21,7 @@ import { DashboardMessage } from '../../DashboardMessage';
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
import { FolderItem } from './FolderItem';
import useMedia from '../../hooks/useMedia';
import { GeneralCommands, STATIC_FOLDER_PLACEHOLDER, TelemetryEvent } from '../../../constants';
import { STATIC_FOLDER_PLACEHOLDER, } from '../../../constants';
import { PageLayout } from '../Layout/PageLayout';
import { parseWinPath } from '../../../helpers/parseWinPath';
import { basename, extname, join } from 'path';
@@ -41,6 +42,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
const folders = useRecoilValue(MediaFoldersAtom);
const loading = useRecoilValue(LoadingAtom);
const [, setPagedItems] = useRecoilState(PagedItems);
const currentStaticFolder = useMemo(() => {
if (settings?.staticFolder) {
@@ -126,6 +128,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
});
}
setPagedItems(mediaFiles.map((m) => m.fsPath));
return mediaFiles;
}, [media, viewData, currentStaticFolder, settings?.staticFolder]);
@@ -151,15 +154,6 @@ export const Media: React.FunctionComponent<IMediaProps> = (
useEffect(() => {
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsMedia));
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewMediaView
});
Messenger.send(GeneralCommands.toVSCode.logging.info, {
message: `Media view loaded`,
location: 'DASHBOARD'
});
}, []);
const { getRootProps, isDragActive } = useDropzone({

View File

@@ -1,26 +1,33 @@
import { ChevronDownIcon } from '@heroicons/react/24/solid';
import * as React from 'react';
import { DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
import { cn } from '../../../utils/cn';
export interface IMenuButtonProps {
label: string | JSX.Element;
title: string;
disabled?: boolean;
className?: string;
labelClass?: string;
buttonClass?: string;
}
export const MenuButton: React.FunctionComponent<IMenuButtonProps> = ({
label,
title,
disabled
disabled,
className,
labelClass,
buttonClass,
}: React.PropsWithChildren<IMenuButtonProps>) => {
return (
<div className={`group flex items-center shrink-0 ${disabled ? 'opacity-50' : ''}`}>
<div className={`mr-2 font-medium flex items-center text-[var(--vscode-tab-inactiveForeground)]`}>
<div className={cn(`group flex items-center shrink-0 ${disabled ? 'opacity-50' : ''} ${className || ""}`)}>
<div className={cn(`mr-2 font-medium flex items-center text-[var(--vscode-tab-inactiveForeground)] ${labelClass || ""}`)}>
{label}:
</div>
<DropdownMenuTrigger
className='text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)] flex items-center focus:outline-none'
className={cn(`text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)] flex items-center focus:outline-none ${buttonClass || ""}`)}
disabled={disabled}>
<span>{title}</span>
<ChevronDownIcon className={`-mr-1 ml-1 h-4 w-4`} aria-hidden="true" />

View File

@@ -5,7 +5,7 @@ import { Startup } from '../Header/Startup';
import { useRecoilValue } from 'recoil';
import { SettingsSelector } from '../../state';
import { SettingsInput } from './SettingsInput';
import { VSCodeButton } from '@vscode/webview-ui-toolkit/react';
import { Button as VSCodeButton } from 'vscrui';
import { DOCS_SUBMODULES, FrameworkDetectors, GIT_CONFIG, SETTING_FRAMEWORK_START, SETTING_GIT_COMMIT_MSG, SETTING_GIT_ENABLED, SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL } from '../../../constants';
import { messageHandler } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';

View File

@@ -4,7 +4,7 @@ import { messageHandler } from '@estruyf/vscode/dist/client';
import { LocalizationKey } from '../../../localization';
import { GeneralCommands, ExtensionState } from '../../../constants';
import { SettingsInput } from './SettingsInput';
import { VSCodeButton } from '@vscode/webview-ui-toolkit/react';
import { Button as VSCodeButton } from 'vscrui';
export interface IIntegrationsViewProps { }

View File

@@ -1,5 +1,5 @@
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import * as React from 'react';
import { Checkbox as VSCodeCheckbox } from 'vscrui';
export interface ISettingsCheckboxProps {
label: string;
@@ -27,7 +27,7 @@ export const SettingsCheckbox: React.FunctionComponent<ISettingsCheckboxProps> =
return (
<VSCodeCheckbox
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateValue(e.target.checked)}
onChange={updateValue}
checked={isEnabled}>
{label}
</VSCodeCheckbox>

View File

@@ -1,5 +1,5 @@
import { VSCodeTextField } from '@vscode/webview-ui-toolkit/react';
import * as React from 'react';
import { TextField as VSCodeTextField } from 'vscrui';
export interface ISettingsInputProps {
label: string;
@@ -21,13 +21,10 @@ export const SettingsInput: React.FunctionComponent<ISettingsInputProps> = ({
return (
<VSCodeTextField
className='w-full p-0 m-0 bg-inherit border-0 focus:border-0 outline-none focus:outline-none shadow-none'
style={{
boxShadow: 'none'
}}
className='w-full p-0 m-0 bg-inherit'
value={value || fallback || ""}
placeholder={placeholder}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => onChange(name, e.target.value)}>
onChange={(value: string) => onChange(name, value)}>
{label}
</VSCodeTextField>
);

View File

@@ -8,14 +8,14 @@ import { AstroContentTypes } from '../Configuration/Astro/AstroContentTypes';
import { ContentFolders } from '../Configuration/Common/ContentFolders';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { COMMAND_NAME, TelemetryEvent } from '../../../constants';
import { COMMAND_NAME } from '../../../constants';
import { ArrowPathIcon } from '@heroicons/react/24/outline';
import { VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react';
import { CommonSettings } from './CommonSettings';
import { IntegrationsView } from './IntegrationsView';
import { useEffect } from 'react';
import { Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { ITab, IView, Panels as VSCodePanels } from 'vscrui';
export interface ISettingsViewProps { }
@@ -23,12 +23,76 @@ export const SettingsView: React.FunctionComponent<ISettingsViewProps> = (_: Rea
const [loading, setLoading] = React.useState<boolean>(false);
const settings = useRecoilValue(SettingsSelector);
const tabs: ITab[] = React.useMemo(() => {
let temp = [
{ id: "view-1", label: l10n.t(LocalizationKey.settingsViewCommon) },
{ id: "view-2", label: l10n.t(LocalizationKey.settingsViewContentFolders) }
];
if (settings?.crntFramework === 'astro') {
temp.push({ id: "view-3", label: l10n.t(LocalizationKey.settingsViewAstro) });
}
return [
...temp,
{ id: "view-4", label: l10n.t(LocalizationKey.settingsViewIntegration) }
];
}, [settings]);
const views: IView[] = React.useMemo(() => {
if (!settings) {
return [];
}
let temp = [
{
id: "view-1",
content: <CommonSettings />
},
{
id: "view-2",
content: (
<div className='py-4'>
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsContentFolders)}</h2>
<ContentFolders
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)} />
</div>
)
}
];
if (settings?.crntFramework === 'astro') {
temp.push({
id: "view-3",
content: (
<div className='py-4'>
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsContentTypes)}</h2>
<AstroContentTypes
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)}
setStatus={_ => null} />
</div>
)
});
}
temp.push({
id: "view-4",
content: (
<IntegrationsView />
)
});
return [
...temp
];
}, [settings]);
useEffect(() => {
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.commonSettings));
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewSettings
});
}, []);
return (
@@ -56,51 +120,11 @@ export const SettingsView: React.FunctionComponent<ISettingsViewProps> = (_: Rea
</a>
</div>
<VSCodePanels className={`mt-4`}>
<VSCodePanelTab id="view-1">{l10n.t(LocalizationKey.settingsViewCommon)}</VSCodePanelTab>
<VSCodePanelTab id="view-2">{l10n.t(LocalizationKey.settingsViewContentFolders)}</VSCodePanelTab>
{
settings?.crntFramework === 'astro' && (
<VSCodePanelTab id="view-3">{l10n.t(LocalizationKey.settingsViewAstro)}</VSCodePanelTab>
)
}
<VSCodePanelTab id="view-4">{l10n.t(LocalizationKey.settingsViewIntegration)}</VSCodePanelTab>
<VSCodePanelView id="view-1">
<CommonSettings />
</VSCodePanelView>
<VSCodePanelView id="view-2">
<div className='py-4'>
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsContentFolders)}</h2>
<ContentFolders
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)} />
</div>
</VSCodePanelView>
{
settings?.crntFramework === 'astro' && (
<VSCodePanelView id="view-3">
<div className='py-4'>
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsContentTypes)}</h2>
<AstroContentTypes
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)}
setStatus={_ => null} />
</div>
</VSCodePanelView>
)
}
<VSCodePanelView id="view-4">
<IntegrationsView />
</VSCodePanelView>
</VSCodePanels>
<VSCodePanels
className={`mt-4`}
tabs={tabs}
views={views}
/>
</div>
)
}

View File

@@ -5,7 +5,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { FeatureFlag } from '../../../components/features/FeatureFlag';
import { FEATURE_FLAG, GeneralCommands, WEBSITE_LINKS } from '../../../constants';
import { TelemetryEvent } from '../../../constants/TelemetryEvent';
import { SnippetParser } from '../../../helpers/SnippetParser';
import { DashboardMessage } from '../../DashboardMessage';
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
@@ -84,10 +83,6 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
useEffect(() => {
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsSnippets));
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewSnippetsView
});
Messenger.send(GeneralCommands.toVSCode.logging.info, {
message: `Snippets view loaded`,
location: 'DASHBOARD'

View File

@@ -17,7 +17,7 @@ import { TemplateItem } from './TemplateItem';
import { Spinner } from '../Common/Spinner';
import { AstroContentTypes } from '../Configuration/Astro/AstroContentTypes';
import { ContentFolders } from '../Configuration/Common/ContentFolders';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import { Checkbox as VSCodeCheckbox } from 'vscrui';
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator } from '../../../components/shadcn/Dropdown';
export interface IStepsToGetStartedProps {
@@ -239,7 +239,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
description: (
<div className='mt-1'>
<VSCodeCheckbox
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateSetting(SETTING_GIT_ENABLED, e.target.checked)}
onChange={(value) => updateSetting(SETTING_GIT_ENABLED, value)}
checked={isGitEnabled}>
{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedGitDescription)}
</VSCodeCheckbox>

View File

@@ -6,17 +6,17 @@ import { getTaxonomyField } from '../../../helpers/getTaxonomyField';
import { Sorting } from '../../../helpers/Sorting';
import { ArrowLeftIcon, EyeIcon } from '@heroicons/react/24/outline';
import { Button } from '../Common/Button';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import { FilterInput } from './FilterInput';
import { useDebounce } from '../../../hooks/useDebounce';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { sortPages } from '../../../utils/sortPages';
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
import { messageHandler } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { ExtensionState } from '../../../constants';
import { LinkButton } from '../Common/LinkButton';
import { openFile } from '../../utils';
import { Checkbox as VSCodeCheckbox } from 'vscrui';
export interface ITaxonomyTaggingProps {
taxonomy: string | null;
@@ -122,8 +122,12 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
}, [pageMappings, untaggedPages]);
const checkIfChecked = React.useCallback((page: Page) => {
return (!untaggedPages.find((p: Page) => p.fmFilePath === page.fmFilePath) && !pageMappings.untagged.find((p: Page) => p.fmFilePath === page.fmFilePath)) || pageMappings.tagged.find((p: Page) => p.fmFilePath === page.fmFilePath);
}, [untaggedPages, pageMappings.tagged]);
const isUntagged = untaggedPages.some((p) => p.fmFilePath === page.fmFilePath);
const isTagged = pageMappings.tagged.some((p) => p.fmFilePath === page.fmFilePath);
const isInUntagged = pageMappings.untagged.some((p) => p.fmFilePath === page.fmFilePath);
return (!isUntagged && !isInUntagged) || isTagged;
}, [untaggedPages, pageMappings.tagged, pageMappings.untagged]);
const onFileView = (filePath: string) => {
openFile(filePath);
@@ -193,7 +197,7 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
<td className={`pl-6 py-2 w-[25px]`}>
<VSCodeCheckbox
title={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingCheckbox, value)}
onClick={() => onCheckboxClick(page)}
onChange={() => onCheckboxClick(page)}
checked={checkIfChecked(page)}>
<span className='sr-only'>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingCheckbox, value)}

View File

@@ -3,7 +3,7 @@ import { ChevronRightIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline
import * as React from 'react';
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { GeneralCommands, TelemetryEvent } from '../../../constants';
import { GeneralCommands } from '../../../constants';
import { TaxonomyData } from '../../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { Page, PageMappings } from '../../models';
@@ -55,10 +55,6 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
useEffect(() => {
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsTaxonomies));
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewTaxonomyDashboard
});
Messenger.send(GeneralCommands.toVSCode.logging.info, {
message: 'Taxonomy view loaded',
location: 'DASHBOARD'

View File

@@ -5,7 +5,6 @@ import { LocalizationKey } from '../../../localization';
import { useEffect } from 'react';
import { Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { TelemetryEvent } from '../../../constants';
export interface IUnknownViewProps { }
@@ -14,10 +13,6 @@ export const UnknownView: React.FunctionComponent<IUnknownViewProps> = (
) => {
useEffect(() => {
Messenger.send(DashboardMessage.setTitle, "Unknown View");
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewUnknown
});
}, []);
return (

View File

@@ -1,6 +1,6 @@
import { BookOpenIcon, HeartIcon, StarIcon } from '@heroicons/react/24/outline';
import * as React from 'react';
import { DOCUMENTATION_LINK, GITHUB_LINK, GeneralCommands, REVIEW_LINK, SPONSOR_LINK, TelemetryEvent } from '../../../constants';
import { DOCUMENTATION_LINK, GITHUB_LINK, GeneralCommands, REVIEW_LINK, SPONSOR_LINK } from '../../../constants';
import { Messenger } from '@estruyf/vscode/dist/client';
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
import { GitHubIcon } from '../../../panelWebView/components/Icons/GitHubIcon';
@@ -25,9 +25,6 @@ export const WelcomeScreen: React.FunctionComponent<IWelcomeScreenProps> = ({
React.useEffect(() => {
setView(NavigationType.Welcome);
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewWelcomeScreen
});
Messenger.send(GeneralCommands.toVSCode.logging.info, {
message: 'Welcome screen loaded',

View File

@@ -93,7 +93,7 @@ if (elm) {
render(
<I10nProvider>
<SettingsProvider
aiUrl={WEBSITE_LINKS.root}
aiUrl={WEBSITE_LINKS.api.baseUrl}
experimental={experimental === 'true'}
version={version || ""}>
<Chatbot />

View File

@@ -5,6 +5,7 @@ import {
ContentType,
CustomScript,
CustomTaxonomy,
DataFolder,
DraftField,
FilterType,
Framework,
@@ -43,6 +44,7 @@ export interface Settings {
dashboardState: DashboardState;
scripts: CustomScript[];
dataFiles: DataFile[] | undefined;
dataFolders: DataFolder[];
dataTypes: DataType[] | undefined;
isBacker: boolean | undefined;
snippets: Snippets | undefined;

View File

@@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const PagedItems = atom<string[]>({
key: 'PagedItems',
default: []
});

View File

@@ -16,6 +16,7 @@ export * from './MediaTotalAtom';
export * from './ModeAtom';
export * from './MultiSelectedItemsAtom';
export * from './PageAtom';
export * from './PagedItems';
export * from './PinnedItems';
export * from './SearchAtom';
export * from './SearchReadyAtom';

View File

@@ -1,8 +1,7 @@
import { GitListener } from './listeners/general/GitListener';
import * as vscode from 'vscode';
import { COMMAND_NAME, CONTEXT, EXTENSION_NAME, TelemetryEvent } from './constants';
import { COMMAND_NAME, CONTEXT, EXTENSION_NAME } from './constants';
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
import { TagType } from './panelWebView/TagType';
import { PanelProvider } from './panelWebView/PanelProvider';
import {
DashboardSettings,
@@ -31,11 +30,13 @@ import {
Article,
Settings,
StatusListener,
Chatbot
Chatbot,
Taxonomy
} from './commands';
import { join } from 'path';
import { Terminal } from './services';
import { i18n } from './commands/i18n';
import { UriHandler } from './providers/UriHandler';
let pageUpdateDebouncer: { (fnc: any, time: number): void };
let editDebounce: { (fnc: any, time: number): void };
@@ -54,6 +55,7 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.executeCommand('setContext', CONTEXT.isDevelopment, true);
}
// Sponsor check
Backers.init(context).then(() => {});
// Make sure the EN language file is loaded
@@ -79,9 +81,6 @@ export async function activate(context: vscode.ExtensionContext) {
SettingsHelper.checkToPromote();
// Sends the activation event
Telemetry.send(TelemetryEvent.activate);
// Start listening to the folders for content changes.
// This will make sure the dashboard is up to date
PagesListener.startWatchers();
@@ -118,32 +117,16 @@ export async function activate(context: vscode.ExtensionContext) {
// Folding the front matter of markdown files
MarkdownFoldingProvider.register();
const insertTags = vscode.commands.registerCommand(COMMAND_NAME.insertTags, async () => {
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
await vscode.commands.executeCommand('workbench.action.focusSideBar');
explorerSidebar.triggerInputFocus(TagType.tags);
});
const insertCategories = vscode.commands.registerCommand(
COMMAND_NAME.insertCategories,
async () => {
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
await vscode.commands.executeCommand('workbench.action.focusSideBar');
explorerSidebar.triggerInputFocus(TagType.categories);
}
);
// Register the taxonomy commands
Taxonomy.registerCommands(subscriptions);
// Register all the article commands
Article.registerCommands(subscriptions);
/**
* Template creation
*/
// Template creation
Template.registerCommands();
/**
* Content creation
*/
// Content creation
ContentType.registerCommands();
Content.registerCommands();
Folders.registerCommands();
@@ -251,8 +234,11 @@ export async function activate(context: vscode.ExtensionContext) {
// Cache commands
Cache.registerCommands();
// Register the URI handler
UriHandler.register();
// Subscribe all commands
subscriptions.push(insertTags, PanelView, insertCategories, collapseAll, fmStatusBarItem);
subscriptions.push(PanelView, collapseAll, fmStatusBarItem);
console.log(`𝖥𝗋𝗈𝗇𝗍 𝖬𝖺𝗍𝗍𝖾𝗋 𝖢𝖬𝖲 𝖺𝖼𝗍𝗂𝗏𝖺𝗍𝖾𝖽! 𝖱𝖾𝖺𝖽𝗒 𝗍𝗈 𝗌𝗍𝖺𝗋𝗍 𝗐𝗋𝗂𝗍𝗂𝗇𝗀... 👩‍💻🧑‍💻👨‍💻`);
}

View File

@@ -31,6 +31,9 @@ import {
isValidFile,
parseWinPath,
processArticlePlaceholdersFromPath,
processDateTimePlaceholders,
processFilePrefixPlaceholders,
processI18nPlaceholders,
processTimePlaceholders
} from '.';
import { format, parse } from 'date-fns';
@@ -39,8 +42,7 @@ import { Article } from '../commands';
import { dirname, join, parse as parseFile } from 'path';
import { EditorHelper } from '@estruyf/vscode';
import sanitize from '../helpers/Sanitize';
import { Field, ContentType as IContentType } from '../models';
import { DateHelper } from './DateHelper';
import { ContentFolder, Field, ContentType as IContentType } from '../models';
import { DiagnosticSeverity, Position, window, Range } from 'vscode';
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
import { fromMarkdown } from 'mdast-util-from-markdown';
@@ -48,7 +50,7 @@ import { Link, Parent } from 'mdast-util-from-markdown/lib';
import { Content } from 'mdast';
import { CustomScript } from './CustomScript';
import { Folders } from '../commands/Folders';
import { existsAsync } from '../utils';
import { existsAsync, getTitleField } from '../utils';
import { mkdirAsync } from '../utils/mkdirAsync';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
@@ -460,8 +462,10 @@ export class ArticleHelper {
let contentType: IContentType | undefined = undefined;
// Get content type by type name in the front matter
if (article.data.type) {
contentType = contentTypes.find((ct) => ct.name === article.data.type);
if (article.data[DefaultFields.ContentType]) {
contentType = contentTypes.find((ct) => ct.name === article.data[DefaultFields.ContentType]);
} else if (article.data[DefaultFields.Type]) {
contentType = contentTypes.find((ct) => ct.name === article.data[DefaultFields.Type]);
} else if (!contentType && article.path) {
const pageFolder = await Folders.getPageFolderByFilePath(article.path);
if (pageFolder && pageFolder.contentTypes?.length === 1) {
@@ -496,7 +500,7 @@ export class ArticleHelper {
const dateFields = contentType.fields.filter((field) => field.type === 'datetime');
for (const dateField of dateFields) {
if (typeof article?.data[dateField.name] !== 'undefined') {
if (article?.data[dateField.name]) {
article.data[dateField.name] = Article.formatDate(new Date(), dateField.dateFormat);
}
}
@@ -532,7 +536,13 @@ export class ArticleHelper {
const fileType = Settings.get<string>(SETTING_CONTENT_DEFAULT_FILETYPE);
let prefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
prefix = await ArticleHelper.getFilePrefix(prefix, folderPath, contentType);
prefix = await ArticleHelper.getFilePrefix(
prefix,
folderPath,
contentType,
titleValue,
new Date()
);
// Name of the file or folder to create
let sanitizedName = ArticleHelper.sanitize(titleValue);
@@ -541,7 +551,11 @@ export class ArticleHelper {
// Create a folder with the `index.md` file
if (contentType?.pageBundle) {
if (prefix && typeof prefix === 'string') {
sanitizedName = `${prefix}-${sanitizedName}`;
if (prefix.endsWith('/')) {
sanitizedName = `${prefix}${sanitizedName}`;
} else {
sanitizedName = `${prefix}-${sanitizedName}`;
}
}
const newFolder = join(folderPath, sanitizedName);
@@ -594,12 +608,19 @@ export class ArticleHelper {
public static async getFilePrefix(
prefix: string | null | undefined,
filePath?: string,
contentType?: IContentType
contentType?: IContentType,
title?: string,
articleDate?: Date
): Promise<string | undefined> {
if (!prefix) {
prefix = undefined;
}
// Replace the default date format
if (prefix === 'yyyy-MM-dd') {
prefix = '{{date|yyyy-MM-dd}}';
}
// Retrieve the file prefix from the folder
if (filePath) {
const filePrefixOnFolder = await Folders.getFilePrefixBeFilePath(filePath);
@@ -613,9 +634,27 @@ export class ArticleHelper {
prefix = contentType.filePrefix;
}
// Process the prefix date formatting
if (prefix && typeof prefix === 'string') {
prefix = `${format(new Date(), DateHelper.formatUpdate(prefix) as string)}`;
prefix = await ArticleHelper.processCustomPlaceholders(prefix, title, filePath, true);
prefix = await processFilePrefixPlaceholders(prefix, filePath);
let selectedFolder: ContentFolder | undefined | null = null;
if (filePath) {
// Get the folder of the article by the file path
selectedFolder = await Folders.getPageFolderByFilePath(filePath);
if (!selectedFolder && contentType) {
selectedFolder = await Folders.getFolderByContentType(contentType, filePath);
}
if (selectedFolder) {
prefix = processI18nPlaceholders(prefix, selectedFolder);
}
}
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
prefix = processTimePlaceholders(prefix, dateFormat);
prefix = processDateTimePlaceholders(prefix, articleDate);
}
return prefix;
@@ -635,11 +674,12 @@ export class ArticleHelper {
) {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
const fmData = Object.assign({}, data);
const titleField = getTitleField();
for (const fieldName of Object.keys(fmData)) {
const fieldValue = fmData[fieldName];
if (fieldName === 'title' && (fieldValue === null || fieldValue === '')) {
if (fieldName === titleField && (fieldValue === null || fieldValue === '')) {
fmData[fieldName] = title;
}
@@ -664,7 +704,8 @@ export class ArticleHelper {
public static async processCustomPlaceholders(
value: string,
title: string | undefined,
filePath: string | undefined
filePath: string | undefined,
skipFileCheck = false
) {
if (value && typeof value === 'string') {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
@@ -708,7 +749,7 @@ export class ArticleHelper {
let updatedValue = placeHolderValue;
// Check if the file already exists, during creation it might not exist yet
if (filePath && (await existsAsync(filePath))) {
if (filePath && (await existsAsync(filePath)) && !skipFileCheck) {
updatedValue = await processArticlePlaceholdersFromPath(placeHolderValue, filePath);
}

View File

@@ -12,14 +12,14 @@ import {
import {
COMMAND_NAME,
DefaultFieldValues,
DefaultFields,
EXTENSION_NAME,
FEATURE_FLAG,
SETTING_CONTENT_DRAFT_FIELD,
SETTING_DATE_FORMAT,
SETTING_FRAMEWORK_ID,
SETTING_TAXONOMY_CONTENT_TYPES,
SETTING_TAXONOMY_FIELD_GROUPS,
TelemetryEvent
SETTING_TAXONOMY_FIELD_GROUPS
} from '../constants';
import {
ContentType as IContentType,
@@ -37,7 +37,7 @@ import { DEFAULT_CONTENT_TYPE_NAME } from '../constants/ContentType';
import { Telemetry } from './Telemetry';
import { basename } from 'path';
import { ParsedFrontMatter } from '../parsers';
import { encodeEmoji, existsAsync, fieldWhenClause, writeFileAsync } from '../utils';
import { encodeEmoji, existsAsync, fieldWhenClause, getTitleField, writeFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
@@ -185,7 +185,7 @@ export class ContentType {
fields.splice(fieldIdx, 1, ...fieldGroup.fields);
}
} else if (field && field.type === 'fields') {
fields = field.fields || [];
field.fields = field.fields || [];
}
}
}
@@ -203,8 +203,6 @@ export class ContentType {
return;
}
Telemetry.send(TelemetryEvent.generateContentType);
const content = ArticleHelper.getCurrent();
const editor = window.activeTextEditor;
@@ -284,7 +282,7 @@ export class ContentType {
// Update the type field in the page
if (!overrideBool && editor) {
content.data['type'] = contentTypeName;
content.data[DefaultFields.ContentType] = contentTypeName;
ArticleHelper.update(editor, content);
}
@@ -332,8 +330,6 @@ export class ContentType {
return;
}
Telemetry.send(TelemetryEvent.addMissingFields);
const article = ArticleHelper.getCurrent();
if (!article || !article.data) {
@@ -377,8 +373,6 @@ export class ContentType {
return;
}
Telemetry.send(TelemetryEvent.setContentType);
const content = ArticleHelper.getCurrent();
const contentTypes = ContentType.getAll() || [];
@@ -402,7 +396,7 @@ export class ContentType {
return;
}
content.data.type = ctAnswer;
content.data[DefaultFields.ContentType] = ctAnswer;
const editor = window.activeTextEditor;
ArticleHelper.update(editor!, content);
@@ -950,8 +944,11 @@ export class ContentType {
}
titleValue = titleValue.trim();
const titleFieldName = getTitleField();
// Check if the title needs to encode the emoji's used in it
const titleField = contentType.fields.find((f) => f.name === 'title');
const titleField = contentType.fields.find((f) => f.name === titleFieldName);
if (titleField && titleField.encodeEmoji) {
titleValue = encodeEmoji(titleValue);
}
@@ -991,12 +988,8 @@ export class ContentType {
contentType
);
let isTypeSet = false;
if (data.type) {
isTypeSet = true;
} else {
data.type = contentType.name;
}
// Set the content type
data[DefaultFields.ContentType] = contentType.name;
const article: ParsedFrontMatter = {
content: '',
@@ -1006,12 +999,11 @@ export class ContentType {
data = await ArticleHelper.updateDates(article);
if (isTypeSet) {
delete data.type;
}
if (contentType.name !== DEFAULT_CONTENT_TYPE_NAME) {
data['type'] = contentType.name;
data[DefaultFields.ContentType] = contentType.name;
} else {
// Default content type, remove the content type field
delete data[DefaultFields.ContentType];
}
const content = ArticleHelper.stringifyFrontMatter(templateData?.content || ``, data);
@@ -1035,8 +1027,6 @@ export class ContentType {
Notifications.info(l10n.t(LocalizationKey.helpersContentTypeCreateSuccess));
Telemetry.send(TelemetryEvent.createContentFromContentType);
// Trigger a refresh for the dashboard
PagesListener.refresh();
}
@@ -1058,6 +1048,7 @@ export class ContentType {
isRoot: boolean = true
): Promise<any> {
if (obj.fields) {
const titleField = getTitleField();
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
for (const field of obj.fields) {
@@ -1066,7 +1057,7 @@ export class ContentType {
continue;
}
if (field.name === 'title') {
if (field.name === titleField) {
if (field.default) {
data[field.name] = processArticlePlaceholdersFromData(
field.default as string,

View File

@@ -2,7 +2,7 @@ import { Settings } from './SettingsHelper';
import { CommandType, EnvironmentType } from './../models/PanelSettings';
import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings';
import { window, env as vscodeEnv, ProgressLocation, Uri, commands } from 'vscode';
import { ArticleHelper, Logger, MediaHelpers, Telemetry } from '.';
import { ArticleHelper, Logger, MediaHelpers } from '.';
import { Folders, WORKSPACE_PLACEHOLDER } from '../commands/Folders';
import { exec, execSync } from 'child_process';
import * as os from 'os';
@@ -12,7 +12,6 @@ import ContentProvider from '../providers/ContentProvider';
import { Dashboard } from '../commands/Dashboard';
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { ParsedFrontMatter } from '../parsers';
import { TelemetryEvent } from '../constants/TelemetryEvent';
import { SETTING_CUSTOM_SCRIPTS } from '../constants';
import { existsAsync } from '../utils';
import * as l10n from '@vscode/l10n';
@@ -40,12 +39,8 @@ export class CustomScript {
const wsPath = wsFolder.fsPath;
if (script.type === ScriptType.MediaFile || script.type === ScriptType.MediaFolder) {
Telemetry.send(TelemetryEvent.runMediaScript);
await CustomScript.runMediaScript(wsPath, path, script);
} else {
Telemetry.send(TelemetryEvent.runCustomScript);
if (script.bulk) {
// Run script on all files
await CustomScript.bulkRun(wsPath, script);
@@ -67,11 +62,11 @@ export class CustomScript {
* @param path
* @returns
*/
private static async singleRun(
public static async singleRun(
wsPath: string,
script: ICustomScript,
path: string | null = null
): Promise<void> {
): Promise<any> {
let articlePath: string | null = path;
let article: ParsedFrontMatter | null | undefined = null;
@@ -99,7 +94,7 @@ export class CustomScript {
articlePath as string,
script
);
await CustomScript.showOutput(output, script, articlePath);
return await CustomScript.showOutput(output, script, articlePath);
}
);
} else {
@@ -133,7 +128,7 @@ export class CustomScript {
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
cancellable: false
},
async (progress, token) => {
async (_, __) => {
for await (const folder of folders) {
if (folder.lastModified.length > 0) {
for await (const file of folder.lastModified) {
@@ -266,15 +261,21 @@ export class CustomScript {
output: string | null,
script: ICustomScript,
articlePath?: string | null
): Promise<void> {
): Promise<any> {
if (output) {
try {
const data: {
frontmatter?: { [key: string]: any };
fmAction?: 'open' | 'copyMediaMetadata' | 'copyMediaMetadataAndDelete' | 'deleteMedia';
fmAction?:
| 'open'
| 'copyMediaMetadata'
| 'copyMediaMetadataAndDelete'
| 'deleteMedia'
| 'fieldAction';
fmPath?: string;
fmSourcePath?: string;
fmDestinationPath?: string;
fmFieldValue?: any;
} = JSON.parse(output);
if (data.frontmatter) {
@@ -326,6 +327,8 @@ export class CustomScript {
await MediaHelpers.deleteFile(data.fmSourcePath);
} else if (data.fmAction === 'deleteMedia' && data.fmPath) {
await MediaHelpers.deleteFile(data.fmPath);
} else if (data.fmAction === 'fieldAction') {
return data.fmFieldValue || undefined;
}
} else {
Logger.error(`No frontmatter found.`);

View File

@@ -58,6 +58,7 @@ import { parseWinPath } from './parseWinPath';
import { TaxonomyHelper } from './TaxonomyHelper';
import { ContentType } from './ContentType';
import { Logger } from './Logger';
import { DataListener } from '../listeners/dashboard';
export class DashboardSettings {
private static cachedSettings: ISettings | undefined = undefined;
@@ -158,6 +159,7 @@ export class DashboardSettings {
}
},
dataFiles: await this.getDataFiles(),
dataFolders: Settings.get<DataFolder[]>(SETTING_DATA_FOLDERS) || [],
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
snippets: Settings.get<Snippets>(SETTING_CONTENT_SNIPPETS),
snippetsWrapper: Settings.get<boolean>(SETTING_SNIPPETS_WRAPPER),
@@ -217,16 +219,7 @@ export class DashboardSettings {
const dataFiles = [...dataJsonFiles, ...dataYmlFiles, ...dataYamlFiles];
for (let dataFile of dataFiles) {
clonedFiles.push({
id: basename(dataFile.fsPath),
title: basename(dataFile.fsPath),
file: dataFile.fsPath,
fileType: dataFile.fsPath.endsWith('.json') ? 'json' : 'yaml',
labelField: folder.labelField,
schema: folder.schema,
type: folder.type,
singleEntry: typeof folder.singleEntry === 'boolean' ? folder.singleEntry : false
} as DataFile);
clonedFiles.push(DataListener.createDataFileObject(dataFile.fsPath, folder));
}
}
}

View File

@@ -32,6 +32,7 @@ import { lookup } from 'mime-types';
import { existsAsync, readdirAsync, unlinkAsync, writeFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { Wysiwyg } from '../commands';
export class MediaHelpers {
private static media: MediaInfo[] = [];
@@ -439,12 +440,23 @@ export class MediaHelpers {
const caption = isFile ? `${data.title || ''}` : `${data.alt || data.caption || ''}`;
const snippet =
data.snippet ||
`${isFile ? '' : '!'}[${caption}](${FrameworkDetector.relAssetPathUpdate(
relPath,
editor.document.fileName
).replace(/ /g, '%20')})`;
const docType = Wysiwyg.getDocType(filePath);
let snippet = data.snippet || '';
if (!data.Snippet) {
if (docType === 'markdown') {
snippet = `${isFile ? '' : '!'}[${caption}](${FrameworkDetector.relAssetPathUpdate(
relPath,
editor.document.fileName
).replace(/ /g, '%20')})`;
} else if (docType === 'asciidoc') {
snippet = `${isFile ? 'link:' : 'image:'}${FrameworkDetector.relAssetPathUpdate(
relPath,
editor.document.fileName
).replace(/ /g, '%20')}${caption ? `[${caption}]` : ''}`;
}
}
if (selection !== undefined) {
builder.replace(selection, snippet);
} else {

View File

@@ -10,7 +10,6 @@ import { Preview } from '../commands/Preview';
import { Project } from '../commands/Project';
import {
CONTEXT,
DefaultFields,
SETTING_CONTENT_DRAFT_FIELD,
SETTING_CONTENT_FRONTMATTER_HIGHLIGHT,
SETTING_DATA_TYPES,
@@ -22,7 +21,6 @@ import {
SETTING_DATE_FORMAT,
SETTING_PANEL_FREEFORM,
SETTING_SEO_CONTENT_MIN_LENGTH,
SETTING_SEO_DESCRIPTION_FIELD,
SETTING_SEO_DESCRIPTION_LENGTH,
SETTING_SEO_SLUG_LENGTH,
SETTING_SEO_TITLE_LENGTH,
@@ -30,8 +28,7 @@ import {
SETTING_SLUG_SUFFIX,
SETTING_SLUG_UPDATE_FILE_NAME,
SETTING_TAXONOMY_CUSTOM,
SETTING_TAXONOMY_FIELD_GROUPS,
SETTING_SEO_TITLE_FIELD
SETTING_TAXONOMY_FIELD_GROUPS
} from '../constants';
import { GitListener } from '../listeners/general';
import {
@@ -46,6 +43,7 @@ import {
} from '../models';
import { Folders } from '../commands';
import { Copilot } from '../services/Copilot';
import { getDescriptionField, getTitleField } from '../utils';
export class PanelSettings {
public static async get(): Promise<IPanelSettings> {
@@ -61,9 +59,8 @@ export class PanelSettings {
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
description: (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1,
content: (Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number) || -1,
titleField: (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title,
descriptionField:
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description
titleField: getTitleField(),
descriptionField: getDescriptionField()
},
slug: {
prefix: Settings.get(SETTING_SLUG_PREFIX) || '',

View File

@@ -93,46 +93,12 @@ export class Questions {
}
}
if (title && aiTitles && aiTitles.length > 0) {
const options: QuickPickItem[] = [
{
label: `✏️ ${l10n.t(
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
)}`,
kind: QuickPickItemKind.Separator
},
{
label: title
},
{
label: `🤖 ${l10n.t(
isCopilotInstalled
? LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator
: LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
)}`,
kind: QuickPickItemKind.Separator
},
...aiTitles.map((d: string) => ({
label: d
}))
];
const selectedTitle = await window.showQuickPick(options, {
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder),
ignoreFocusOut: true
});
if (selectedTitle) {
title = selectedTitle.label;
} else if (!selectedTitle) {
// Reset the title, so the user can enter their own title
title = undefined;
}
} else if (!title && showWarning) {
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
return;
}
title = await this.pickTitleSuggestions(
title,
aiTitles || [],
isCopilotInstalled,
showWarning
);
}
if (!title) {
@@ -152,6 +118,56 @@ export class Questions {
return title;
}
public static async pickTitleSuggestions(
title: string | undefined,
aiTitles: string[],
isCopilotInstalled: boolean,
showWarning: boolean = true
): Promise<string | undefined> {
if (title && aiTitles && aiTitles.length > 0) {
const options: QuickPickItem[] = [
{
label: `✏️ ${l10n.t(
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
)}`,
kind: QuickPickItemKind.Separator
},
{
label: title
},
{
label: `🤖 ${l10n.t(
isCopilotInstalled
? LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator
: LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
)}`,
kind: QuickPickItemKind.Separator
},
...aiTitles.map((d: string) => ({
label: d
}))
];
const selectedTitle = await window.showQuickPick(options, {
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder),
ignoreFocusOut: true
});
if (selectedTitle) {
title = selectedTitle.label;
} else if (!selectedTitle) {
// Reset the title, so the user can enter their own title
title = undefined;
}
} else if (!title && showWarning) {
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
return;
}
return title;
}
/**
* Select the folder for your content creation
* @param showWarning
@@ -241,7 +257,7 @@ export class Questions {
if (options.length === 0) {
Notifications.error(
LocalizationKey.helpersQuestionsSelectContentTypeQuickPickErrorNoContentTypes
l10n.t(LocalizationKey.helpersQuestionsSelectContentTypeQuickPickErrorNoContentTypes)
);
return;
}

View File

@@ -1,5 +1,4 @@
import { parseWinPath } from './parseWinPath';
import { Telemetry } from './Telemetry';
import { Notifications } from './Notifications';
import {
commands,
@@ -18,7 +17,6 @@ import {
CONTEXT,
ExtensionState,
SETTING_TAXONOMY_CUSTOM,
TelemetryEvent,
COMMAND_NAME,
SETTING_TAXONOMY_CONTENT_TYPES,
SETTING_CONTENT_PAGE_FOLDERS,
@@ -516,7 +514,9 @@ ${JSON.stringify(value, null, 2)}`,
}frontmatter.codes/frontmatter.schema.json`
};
if (wsFolder) {
const projectFile = await Settings.projectConfigPath();
if (wsFolder && !projectFile) {
const configPath = join(wsFolder.fsPath, Settings.globalFile);
if (!(await existsAsync(configPath))) {
await writeFileAsync(configPath, JSON.stringify(initialConfig, null, 2), 'utf8');
@@ -600,8 +600,6 @@ ${JSON.stringify(value, null, 2)}`,
}
Notifications.info(l10n.t(LocalizationKey.helpersSettingsHelperPromoteSuccess));
Telemetry.send(TelemetryEvent.promoteSettings);
}
/**

View File

@@ -17,11 +17,11 @@ export class SlugHelper {
return null;
}
if (!slugTemplate) {
slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE) || undefined;
if (slugTemplate === undefined || slugTemplate === null) {
slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE);
}
if (slugTemplate) {
if (typeof slugTemplate === 'string') {
if (slugTemplate.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
slugTemplate = slugTemplate.replace(regex, articleTitle.toLowerCase().replace(/\s/g, '-'));

View File

@@ -1,32 +1,6 @@
import { workspace } from 'vscode';
import { Extension, Settings } from '.';
import {
EXTENSION_BETA_ID,
EXTENSION_ID,
SETTING_TELEMETRY_DISABLE,
WEBSITE_LINKS
} from '../constants';
export class Telemetry {
private static instance: Telemetry;
private extTitle: string;
private extVersion: string;
private events: any[] = [];
private timeout: NodeJS.Timeout | undefined;
private constructor() {
const extension = Extension.getInstance();
this.extTitle = extension.isBetaVersion() ? EXTENSION_BETA_ID : EXTENSION_ID;
this.extVersion = extension.version;
}
public static getInstance(): Telemetry {
if (!Telemetry.instance) {
Telemetry.instance = new Telemetry();
}
return Telemetry.instance;
}
public static isVscodeEnabled(): boolean {
const config = workspace.getConfiguration('telemetry');
const isVscodeEnable = config.get<'off' | undefined>('enableTelemetry');
@@ -39,56 +13,6 @@ export class Telemetry {
*/
public static isEnabled(): boolean {
const isVscodeEnable = Telemetry.isVscodeEnabled();
const isDisabled = Settings.get<boolean>(SETTING_TELEMETRY_DISABLE);
return isDisabled || isVscodeEnable ? false : true;
}
/**
* Send metrics to our own database
* @param eventName
* @param properties
* @returns
*/
public static send(eventName: string, properties?: any) {
if (!Telemetry.isEnabled()) {
return;
}
const instance = Telemetry.getInstance();
instance.events.push({
name: eventName,
extName: instance.extTitle,
version: instance.extVersion,
properties
});
instance.debounceMetrics();
}
/**
* Debounce the metrics by 1 second
*/
private async debounceMetrics() {
const instance = Telemetry.getInstance();
// Check if timeout was defined
if (instance.timeout) {
clearTimeout(instance.timeout);
}
// Set a new timeout
instance.timeout = setTimeout(async () => {
await fetch(WEBSITE_LINKS.api.metrics, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.events)
});
// Reset the events
this.events = [];
}, 1000) as any as NodeJS.Timeout;
return isVscodeEnable ? false : true;
}
}

View File

@@ -33,6 +33,7 @@ export * from './openFileInEditor';
export * from './parseWinPath';
export * from './processArticlePlaceholders';
export * from './processDateTimePlaceholders';
export * from './processFilePrefixPlaceholders';
export * from './processFmPlaceholders';
export * from './processI18nPlaceholders';
export * from './processPathPlaceholders';

View File

@@ -3,9 +3,14 @@ import { Logger } from './Logger';
import { Notifications } from './Notifications';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { Folders, WORKSPACE_PLACEHOLDER } from '../commands';
export const openFileInEditor = async (filePath: string) => {
if (filePath) {
if (filePath.startsWith(WORKSPACE_PLACEHOLDER)) {
filePath = Folders.getAbsFilePath(filePath);
}
try {
const doc = await workspace.openTextDocument(Uri.file(filePath));
await window.showTextDocument(doc, 1, false);

View File

@@ -1,4 +1,5 @@
import { ContentType } from '../models';
import { getTitleField } from '../utils';
import { ArticleHelper } from './ArticleHelper';
import { SlugHelper } from './SlugHelper';
@@ -7,16 +8,17 @@ export const processArticlePlaceholdersFromData = (
data: { [key: string]: any },
contentType: ContentType
): string => {
if (value.includes('{{title}}') && data.title) {
const titleField = getTitleField();
if (value.includes('{{title}}') && data[titleField]) {
const regex = new RegExp('{{title}}', 'g');
value = value.replace(regex, data.title || '');
value = value.replace(regex, data[titleField] || '');
}
if (value.includes('{{slug}}')) {
const regex = new RegExp('{{slug}}', 'g');
value = value.replace(
regex,
SlugHelper.createSlug(data.title || '', data, contentType.slugTemplate) || ''
SlugHelper.createSlug(data[titleField] || '', data, contentType.slugTemplate) || ''
);
}
@@ -32,9 +34,11 @@ export const processArticlePlaceholdersFromPath = async (
return value;
}
const titleField = getTitleField();
if (value.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
value = value.replace(regex, article.data.title || '');
value = value.replace(regex, article.data[titleField] || '');
}
if (value.includes('{{slug}}') && filePath) {
@@ -43,8 +47,11 @@ export const processArticlePlaceholdersFromPath = async (
const regex = new RegExp('{{slug}}', 'g');
value = value.replace(
regex,
SlugHelper.createSlug(article.data.title || '', article.data, contentType.slugTemplate) ||
''
SlugHelper.createSlug(
article.data[titleField] || '',
article.data,
contentType.slugTemplate
) || ''
);
}
}

View File

@@ -8,11 +8,7 @@ import { DateHelper } from './DateHelper';
* @param articleDate
* @returns
*/
export const processDateTimePlaceholders = (
value: string,
dateFormat?: string,
articleDate?: Date
) => {
export const processDateTimePlaceholders = (value: string, articleDate?: Date) => {
if (value && typeof value === 'string') {
if (value.includes(`{{date|`)) {
const regex = /{{date\|[^}]*}}/g;

View File

@@ -0,0 +1,62 @@
import { FileType, Uri, workspace } from 'vscode';
import { parse } from 'path';
import { isValidFile } from './isValidFile';
/**
* Processes file prefix placeholders in a given string value.
*
* This function replaces placeholders in the format `{{filePrefix.index}}` or `{{filePrefix.index|zeros:4}}`
* with the appropriate index number based on the number of files in the directory of the given file path.
*
* @param value - The string containing the placeholders to be replaced.
* @param folderPath - The path of the file whose directory will be used to determine the index number.
* @returns A promise that resolves to the string with the placeholders replaced by the index number.
*/
export const processFilePrefixPlaceholders = async (value: string, folderPath?: string) => {
// Example: {{filePrefix.index}} or {{filePrefix.index|chars:4,zeros:true}}
if (value && value.includes('{{filePrefix.index') && folderPath) {
const dirContent = await workspace.fs.readDirectory(Uri.file(folderPath));
const files = dirContent.filter(
([filePath, type]) =>
type === FileType.File &&
!filePath.startsWith('.') &&
isValidFile(filePath) &&
!filePath.includes('_index.')
);
let chars = 3;
let idxValue = files.length + 1;
if (value.includes('{{filePrefix.index}}')) {
const regex = new RegExp('{{filePrefix.index}}', 'g');
const placeholderValue = idxValue.toString().padStart(chars, '0');
value = value.replace(regex, placeholderValue);
}
// Example: {{filePrefix.index|zeros:4}}
else if (value.includes('{{filePrefix.index')) {
const regex = /{{filePrefix.index[^}]*}}/g;
const matches = value.match(regex);
if (matches) {
for (const match of matches) {
const placeholderParts = match.split('|');
if (placeholderParts.length > 1) {
let options = placeholderParts[1].trim().replace('}}', '').split(',');
for (const option of options) {
if (option.startsWith('zeros:')) {
chars = parseInt(option.replace('zeros:', ''));
}
}
const placeholderValue = chars
? idxValue.toString().padStart(chars, '0')
: idxValue.toString();
value = value.replace(match, placeholderValue);
}
}
}
}
}
return value;
};

View File

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { DEFAULT_CONTENT_TYPE, DEFAULT_CONTENT_TYPE_NAME } from '../constants/ContentType';
import { Settings } from '../dashboardWebView/models';
import { ContentType, PanelSettings } from '../models';
import { DefaultFields } from '../constants';
export default function useContentType(
settings: PanelSettings | Settings | undefined | null,
@@ -13,8 +14,12 @@ export default function useContentType(
if (settings && metadata) {
let contentTypeName = DEFAULT_CONTENT_TYPE_NAME;
if (metadata?.type) {
contentTypeName = metadata.type;
if (metadata) {
if (metadata[DefaultFields.ContentType]) {
contentTypeName = metadata[DefaultFields.ContentType];
} else if (metadata[DefaultFields.Type]) {
contentTypeName = metadata[DefaultFields.Type];
}
}
// Get the content type by the folder name

View File

@@ -1,15 +1,17 @@
import { workspace } from 'vscode';
import { workspace, window } from 'vscode';
import { DataFile } from './../../models/DataFile';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { BaseListener } from './BaseListener';
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
import { Folders } from '../../commands/Folders';
import { dirname } from 'path';
import { basename, dirname, join } from 'path';
import * as yaml from 'js-yaml';
import { DataFileHelper, Logger } from '../../helpers';
import { existsAsync, readFileAsync, writeFileAsync } from '../../utils';
import { mkdirAsync } from '../../utils/mkdirAsync';
import { PostMessageData } from '../../models';
import { DataFolder, PostMessageData } from '../../models';
import { LocalizationKey, localize } from '../../localization';
import { SettingsListener } from './SettingsListener';
export class DataListener extends BaseListener {
public static process(msg: PostMessageData) {
@@ -26,11 +28,35 @@ export class DataListener extends BaseListener {
case DashboardMessage.putDataEntries:
this.processDataUpdate(msg?.payload);
break;
case DashboardMessage.createDataFile:
this.createDataFile(msg.command, msg.requestId || '', msg.payload);
break;
default:
return;
}
}
/**
* Creates a DataFile object based on the provided path and folder.
*
* @param path - The path of the file.
* @param folder - The DataFolder object.
* @returns The created DataFile object.
*/
public static createDataFileObject(path: string, folder: DataFolder): DataFile {
const filePath = Folders.wsPath(path);
return {
id: basename(path),
title: basename(path),
file: filePath,
fileType: path.endsWith('.json') ? 'json' : 'yaml',
labelField: folder.labelField,
schema: folder.schema,
type: folder.type,
singleEntry: typeof folder.singleEntry === 'boolean' ? folder.singleEntry : false
};
}
/**
* Process the data update
* @param msgData
@@ -81,4 +107,57 @@ export class DataListener extends BaseListener {
const entries = await DataFileHelper.process(msgData);
this.sendMsg(DashboardCommand.dataFileEntries, entries);
}
/**
* Create a new data file
* @param command
* @param requestId
* @param data
*/
private static async createDataFile(command: string, requestId: string, dataFolder: DataFolder) {
if (!command || !requestId || !dataFolder) {
return;
}
if (!dataFolder.id || !dataFolder.path) {
this.sendError(
command as DashboardCommand,
requestId,
localize(LocalizationKey.listenersPanelDataListenerCreateDataFileError)
);
return;
}
const fileName = await window.showInputBox({
title: localize(LocalizationKey.listenersPanelDataListenerCreateDataFileInputTitle),
prompt: localize(LocalizationKey.listenersPanelDataListenerCreateDataFileInputTitle),
ignoreFocusOut: true
});
if (!fileName || fileName.trim() === '') {
this.sendError(
command as DashboardCommand,
requestId,
localize(LocalizationKey.listenersPanelDataListenerCreateDataFileNoFileName)
);
return;
}
const absPath = Folders.getAbsFilePath(dataFolder.path);
if (!(await existsAsync(absPath))) {
const dirPath = dirname(absPath);
if (!(await existsAsync(dirPath))) {
await mkdirAsync(dirPath, { recursive: true });
}
}
// Check the file type and create the file
const filePath = join(absPath, `${fileName}.${dataFolder.fileType || 'json'}`);
await writeFileAsync(filePath, dataFolder.fileType === 'json' ? '{}' : '', 'utf8');
// Update the settings
await SettingsListener.getSettings(true);
const dataFile = DataListener.createDataFileObject(filePath, dataFolder);
this.sendRequest(command as DashboardCommand, requestId, dataFile);
}
}

View File

@@ -1,11 +1,10 @@
import { Telemetry } from '../../helpers/Telemetry';
import { MediaHelpers } from '../../helpers/MediaHelpers';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { BaseListener } from './BaseListener';
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
import { SortingOption } from '../../dashboardWebView/models';
import { commands, env, Uri } from 'vscode';
import { COMMAND_NAME, TelemetryEvent } from '../../constants';
import { COMMAND_NAME } from '../../constants';
import * as os from 'os';
import { Folders } from '../../commands';
import { PostMessageData, UnmappedMedia } from '../../models';
@@ -24,31 +23,25 @@ export class MediaListener extends BaseListener {
this.sendMediaFiles(page, folder, sorting);
break;
case DashboardMessage.refreshMedia:
Telemetry.send(TelemetryEvent.refreshMedia);
MediaHelpers.resetMedia();
this.sendMediaFiles(0, msg?.payload?.folder);
break;
case DashboardMessage.uploadMedia:
Telemetry.send(TelemetryEvent.uploadMedia);
this.store(msg?.payload);
break;
case DashboardMessage.deleteMedia:
Telemetry.send(TelemetryEvent.deleteMedia);
this.delete(msg?.payload);
break;
case DashboardMessage.revealMedia:
this.openFileInFinder(msg?.payload?.file);
break;
case DashboardMessage.insertMedia:
Telemetry.send(TelemetryEvent.insertMediaToContent);
MediaHelpers.insertMediaToMarkdown(msg?.payload);
break;
case DashboardMessage.insertFile:
Telemetry.send(TelemetryEvent.insertFileToContent);
MediaHelpers.insertMediaToMarkdown(msg?.payload);
break;
case DashboardMessage.updateMediaMetadata:
Telemetry.send(TelemetryEvent.updateMediaMetadata);
this.update(msg.payload);
break;
case DashboardMessage.getUnmappedMedia:

View File

@@ -1,13 +1,11 @@
import { EditorHelper } from '@estruyf/vscode';
import { window, Range, Position } from 'vscode';
import { Dashboard } from '../../commands/Dashboard';
import { SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, TelemetryEvent } from '../../constants';
import { SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT } from '../../constants';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import {
ArticleHelper,
Notifications,
Settings,
Telemetry,
processArticlePlaceholdersFromPath,
processTimePlaceholders
} from '../../helpers';
@@ -29,7 +27,6 @@ export class SnippetListener extends BaseListener {
this.updateSnippet(msg.payload);
break;
case DashboardMessage.insertSnippet:
Telemetry.send(TelemetryEvent.insertContentSnippet);
this.insertSnippet(msg.payload);
break;
case DashboardMessage.updateSnippetPlaceholders:

View File

@@ -1,20 +0,0 @@
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { Telemetry } from '../../helpers/Telemetry';
import { PostMessageData } from '../../models';
import { BaseListener } from './BaseListener';
export class TelemetryListener extends BaseListener {
/**
* Process the messages for the dashboard views
* @param msg
*/
public static process(msg: PostMessageData) {
super.process(msg);
switch (msg.command) {
case DashboardMessage.sendTelemetry:
Telemetry.send(msg.payload.event, msg.payload.properties);
break;
}
}
}

View File

@@ -6,7 +6,6 @@ export * from './MediaListener';
export * from './PagesListener';
export * from './SettingsListener';
export * from './SnippetListener';
export * from './TelemetryListener';
export * from './TaxonomyListener';
export * from './LocalizationListener';
export * from './SsgListener';

View File

@@ -10,8 +10,7 @@ import {
SETTING_GIT_SUBMODULE_BRANCH,
SETTING_GIT_SUBMODULE_FOLDER,
SETTING_GIT_SUBMODULE_PULL,
SETTING_GIT_SUBMODULE_PUSH,
TelemetryEvent
SETTING_GIT_SUBMODULE_PUSH
} from './../../constants';
import { Settings } from './../../helpers/SettingsHelper';
import { Dashboard } from '../../commands/Dashboard';
@@ -22,8 +21,7 @@ import {
Logger,
Notifications,
parseWinPath,
processTimePlaceholders,
Telemetry
processTimePlaceholders
} from '../../helpers';
import { GeneralCommands } from './../../constants/GeneralCommands';
import simpleGit, { SimpleGit } from 'simple-git';
@@ -158,8 +156,6 @@ export class GitListener {
try {
this.sendMsg(GeneralCommands.toWebview.git.syncingStart, isSync ? 'syncing' : 'fetching');
Telemetry.send(isSync ? TelemetryEvent.gitSync : TelemetryEvent.gitFetch);
await this.pull();
if (isSync) {

View File

@@ -5,12 +5,7 @@ import { Folders } from '../../commands/Folders';
import { Command } from '../../panelWebView/Command';
import { CommandToCode } from '../../panelWebView/CommandToCode';
import { BaseListener } from './BaseListener';
import {
Uri,
authentication,
commands,
window
} from 'vscode';
import { Uri, authentication, commands, window } from 'vscode';
import {
ArticleHelper,
Extension,
@@ -20,7 +15,8 @@ import {
processArticlePlaceholdersFromData,
processTimePlaceholders,
processFmPlaceholders,
parseWinPath
parseWinPath,
Questions
} from '../../helpers';
import {
COMMAND_NAME,
@@ -30,7 +26,6 @@ import {
SETTING_DATE_FORMAT,
SETTING_GLOBAL_ACTIVE_MODE,
SETTING_GLOBAL_MODES,
SETTING_SEO_TITLE_FIELD,
SETTING_TAXONOMY_CONTENT_TYPES
} from '../../constants';
import { Article, Preview } from '../../commands';
@@ -42,7 +37,7 @@ import {
ContentType as IContentType,
FolderInfo
} from '../../models';
import { encodeEmoji, fieldWhenClause } from '../../utils';
import { encodeEmoji, fieldWhenClause, getTitleField } from '../../utils';
import { PanelProvider } from '../../panelWebView/PanelProvider';
import { MessageHandlerData } from '@estruyf/vscode';
import { SponsorAi } from '../../services/SponsorAI';
@@ -113,6 +108,24 @@ export class DataListener extends BaseListener {
case CommandToCode.copilotSuggestDescription:
this.copilotSuggestDescription(msg.command, msg.requestId);
break;
case CommandToCode.copilotSuggestTitle:
this.copilotSuggestTitle(msg.command, msg.requestId, msg.payload);
break;
}
}
private static async copilotSuggestTitle(command: string, requestId?: string, title?: string) {
if (!command || !requestId || !title) {
return;
}
const aiTitles = await Copilot.suggestTitles(title);
title = await Questions.pickTitleSuggestions(title, aiTitles || [], true);
if (title) {
this.sendRequest(command, requestId, title);
} else {
this.sendRequestError(command, requestId, 'Failed to suggest title');
}
}
@@ -140,7 +153,7 @@ export class DataListener extends BaseListener {
const extPath = Extension.getInstance().extensionPath;
const panel = PanelProvider.getInstance(extPath);
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const titleField = getTitleField();
const description = await Copilot.suggestDescription(
articleDetails.data[titleField],
articleDetails.content
@@ -199,7 +212,7 @@ export class DataListener extends BaseListener {
return;
}
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const titleField = getTitleField();
const suggestion = await SponsorAi.getDescription(
githubAuth.accessToken,
@@ -417,7 +430,7 @@ export class DataListener extends BaseListener {
}
let beforeValue: any;
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const titleField = getTitleField();
const editor = window.activeTextEditor;
@@ -443,6 +456,8 @@ export class DataListener extends BaseListener {
if (!sourceField.default) {
value = undefined;
}
} else if (sourceField?.type === 'number') {
// We don't have to do anything for numbers, we can leave the 0 value
} else {
value = undefined;
}
@@ -774,9 +789,11 @@ export class DataListener extends BaseListener {
data && contentType ? processArticlePlaceholdersFromData(value, data, contentType) : value;
value = processTimePlaceholders(value, dateFormat);
value = processFmPlaceholders(value, data);
const titleField = getTitleField();
value = await ArticleHelper.processCustomPlaceholders(
value,
data.title || '',
data[titleField] || '',
crntFile?.uri.fsPath || ''
);
}

View File

@@ -1,5 +1,6 @@
import { Folders } from '../../commands';
import { SETTING_CUSTOM_SCRIPTS } from '../../constants';
import { CustomScript, Settings } from '../../helpers';
import { CustomScript, Notifications, Settings } from '../../helpers';
import { CustomScript as ICustomScript, PostMessageData } from '../../models';
import { CommandToCode } from '../../panelWebView/CommandToCode';
import { BaseListener } from './BaseListener';
@@ -16,6 +17,32 @@ export class ScriptListener extends BaseListener {
case CommandToCode.runCustomScript:
this.runCustomScript(msg);
break;
case CommandToCode.runFieldAction:
this.runFieldAction(msg);
break;
}
}
private static async runFieldAction({ command, payload, requestId }: PostMessageData) {
if (!payload || !requestId || !command) {
return;
}
const script = payload as ICustomScript;
if (script.script) {
const wsFolder = Folders.getWorkspaceFolder();
if (!wsFolder) {
return;
}
const fieldValue = await CustomScript.singleRun(wsFolder.fsPath, script);
if (fieldValue) {
this.sendRequest(command, requestId, fieldValue);
} else {
Notifications.error('The script did not return a field value');
this.sendRequestError(command, requestId, 'The script did not return a field value');
}
}
}

View File

@@ -5,17 +5,13 @@ import { authentication, window } from 'vscode';
import { ArticleHelper, Extension, Settings, TaxonomyHelper } from '../../helpers';
import { BlockFieldData, CustomTaxonomyData, PostMessageData, TaxonomyType } from '../../models';
import { DataListener } from '.';
import {
DefaultFields,
SETTING_SEO_DESCRIPTION_FIELD,
SETTING_SEO_TITLE_FIELD
} from '../../constants';
import { SponsorAi } from '../../services/SponsorAI';
import { PanelProvider } from '../../panelWebView/PanelProvider';
import { MessageHandlerData } from '@estruyf/vscode';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
import { Copilot } from '../../services/Copilot';
import { getDescriptionField, getTitleField } from '../../utils';
export class TaxonomyListener extends BaseListener {
/**
@@ -73,10 +69,9 @@ export class TaxonomyListener extends BaseListener {
}
}
/**
* Suggests a taxonomy for a given command, request ID, and tag type.
*
*
* @param command - The command to execute.
* @param requestId - The ID of the request.
* @param type - The type of the tag.
@@ -100,8 +95,8 @@ export class TaxonomyListener extends BaseListener {
const extPath = Extension.getInstance().extensionPath;
const panel = PanelProvider.getInstance(extPath);
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const descriptionField = (Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
const titleField = getTitleField();
const descriptionField = getDescriptionField();
const tags = await Copilot.suggestTaxonomy(
articleDetails.data[titleField],
@@ -127,7 +122,7 @@ export class TaxonomyListener extends BaseListener {
/**
* Suggests taxonomy based on the provided command, request ID, and tag type.
*
*
* @param command - The command to execute.
* @param requestId - The ID of the request.
* @param type - The type of tag.
@@ -166,9 +161,8 @@ export class TaxonomyListener extends BaseListener {
return;
}
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const descriptionField =
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
const titleField = getTitleField();
const descriptionField = getDescriptionField();
const suggestions = await SponsorAi.getTaxonomySuggestions(
githubAuth.accessToken,

View File

@@ -1 +1,2 @@
export * from './localization.enum';
export * from './localize';

View File

@@ -479,10 +479,26 @@ export enum LocalizationKey {
* Updated your data entries
*/
dashboardDataViewDataViewUpdateMessage = 'dashboard.dataView.dataView.update.message',
/**
* Create new data file
*/
dashboardDataViewDataViewCreateNew = 'dashboard.dataView.dataView.createNew',
/**
* Select data folder
*/
dashboardDataViewDataViewSelectDataFolder = 'dashboard.dataView.dataView.selectDataFolder',
/**
* Close data file
*/
dashboardDataViewDataViewCloseSelectedDataFile = 'dashboard.dataView.dataView.closeSelectedDataFile',
/**
* Select your date type first
*/
dashboardDataViewEmptyViewHeading = 'dashboard.dataView.emptyView.heading',
/**
* Start by creating a new data file
*/
dashboardDataViewEmptyViewHeadingCreate = 'dashboard.dataView.emptyView.heading.create',
/**
* Edit "{0}"
*/
@@ -515,6 +531,10 @@ export enum LocalizationKey {
* {0} selected
*/
dashboardHeaderActionsBarItemsSelected = 'dashboard.header.actionsBar.itemsSelected',
/**
* Select all
*/
dashboardHeaderActionsBarSelectAll = 'dashboard.header.actionsBar.selectAll',
/**
* Delete selected files
*/
@@ -1416,6 +1436,14 @@ export enum LocalizationKey {
* Unkown field type: {0}
*/
panelFieldsWrapperFieldUnknown = 'panel.fields.wrapperField.unknown',
/**
* Custom action
*/
panelFieldsFieldCustomActionButtonTitle = 'panel.fields.fieldCustomAction.button.title',
/**
* Executing field action...
*/
panelFieldsFieldCustomActionExecuting = 'panel.fields.fieldCustomAction.executing',
/**
* Actions
*/
@@ -2321,7 +2349,7 @@ export enum LocalizationKey {
*/
helpersQuestionsContentTitleAiInputQuickPickAiSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.ai.separator',
/**
* GitHub Copilot generated title
* GitHub Copilot suggestions
*/
helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.copilot.separator',
/**
@@ -2580,6 +2608,18 @@ export enum LocalizationKey {
* Something went wrong while parsing your front matter. Please check the contents of your file.
*/
listenersPanelDataListenerPushMetadataFrontMatterError = 'listeners.panel.dataListener.pushMetadata.frontMatter.error',
/**
* What is the name of the data file?
*/
listenersPanelDataListenerCreateDataFileInputTitle = 'listeners.panel.dataListener.createDataFile.inputTitle',
/**
* No data file id or path defined.
*/
listenersPanelDataListenerCreateDataFileError = 'listeners.panel.dataListener.createDataFile.error',
/**
* No filename provided.
*/
listenersPanelDataListenerCreateDataFileNoFileName = 'listeners.panel.dataListener.createDataFile.noFileName',
/**
* No active editor
*/

View File

@@ -0,0 +1,5 @@
import * as l10n from '@vscode/l10n';
export const localize = (key: string, ...args: any[]): string => {
return l10n.t(key, ...args);
};

View File

@@ -5,4 +5,6 @@ export interface DataFolder {
schema?: any;
type?: string;
singleEntry?: boolean;
enableFileCreation?: boolean;
fileType?: 'json' | 'yaml';
}

View File

@@ -141,6 +141,9 @@ export interface Field {
// When clause
when?: WhenClause;
// Custom action
actions?: CustomScript[];
}
export interface NumberOptions {

View File

@@ -42,9 +42,11 @@ export enum CommandToCode {
stopServer = 'stop-server',
aiSuggestTaxonomy = 'ai-suggest-taxonomy',
aiSuggestDescription = 'ai-suggest-description',
copilotSuggestTitle = 'copilot-suggest-title',
copilotSuggestDescription = 'copilot-suggest-description',
copilotSuggestTaxonomy = 'copilot-suggest-taxonomy',
searchByType = 'search-by-type',
processMediaData = 'process-media-data',
isServerStarted = 'is-server-started'
isServerStarted = 'is-server-started',
runFieldAction = 'run-field-action'
}

View File

@@ -9,7 +9,7 @@ import {
FieldsListener,
LocalizationListener
} from './../listeners/panel';
import { SETTING_EXPERIMENTAL, SETTING_EXTENSIBILITY_SCRIPTS, TelemetryEvent } from '../constants';
import { SETTING_EXPERIMENTAL } from '../constants';
import {
CancellationToken,
Disposable,
@@ -27,9 +27,8 @@ import { WebviewHelper } from '@estruyf/vscode';
import { Extension } from '../helpers/Extension';
import { Telemetry } from '../helpers/Telemetry';
import { GitListener, ModeListener } from '../listeners/general';
import { Folders } from '../commands';
import { basename } from 'path';
import { ignoreMsgCommand } from '../utils';
import { getExtensibilityScripts, getWebviewJsFiles, ignoreMsgCommand } from '../utils';
export class PanelProvider implements WebviewViewProvider, Disposable {
public static readonly viewType = 'frontMatter.explorer';
@@ -90,7 +89,7 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
enableCommandUris: true
};
webviewView.webview.html = this.getWebviewContent(webviewView.webview);
webviewView.webview.html = await this.getWebviewContent(webviewView.webview);
this.disposable = Disposable.from(
webviewView.onDidDispose(() => {
@@ -120,7 +119,6 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
webviewView.onDidChangeVisibility(() => {
if (this.visible) {
Telemetry.send(TelemetryEvent.openPanelWebview);
DataListener.getFileData();
}
});
@@ -208,12 +206,11 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
* Retrieve the webview HTML contents
* @param webView
*/
private getWebviewContent(webView: Webview): string {
private async getWebviewContent(webView: Webview): Promise<string> {
const ext = Extension.getInstance();
const dashboardFile = 'panelWebView.js';
const webviewFile = 'panel.main.js';
const localPort = `9001`;
const localServerUrl = `localhost:${localPort}`;
const extensionPath = ext.extensionPath;
const styleVSCodeUri = webView.asWebviewUri(
Uri.joinPath(this.extPath, 'assets/media', 'vscode.css')
@@ -230,32 +227,18 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
let scriptUri = '';
const isProd = Extension.getInstance().isProductionMode;
let scriptUris = [];
if (isProd) {
scriptUri = webView
.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile))
.toString();
scriptUris = await getWebviewJsFiles('panel', webView);
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUris.push(`http://${localServerUrl}/${webviewFile}`);
}
// Get experimental setting
const experimental = Settings.get(SETTING_EXPERIMENTAL);
const extensibilityScripts = Settings.get<string[]>(SETTING_EXTENSIBILITY_SCRIPTS) || [];
const scriptsToLoad: string[] = [];
if (experimental) {
for (const script of extensibilityScripts) {
if (script.startsWith('https://')) {
scriptsToLoad.push(script);
} else {
const absScriptPath = Folders.getAbsFilePath(script);
const scriptUri = webView.asWebviewUri(Uri.file(absScriptPath));
scriptsToLoad.push(scriptUri.toString());
}
}
}
const scriptsToLoad: string[] = getExtensibilityScripts(webView);
const csp = [
`default-src 'none';`,
@@ -266,7 +249,7 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`
}`,
`style-src ${webView.cspSource} 'self' 'unsafe-inline' https://*`,
`font-src ${webView.cspSource}`,
`font-src ${webView.cspSource} data:;`,
`connect-src https://o1022172.ingest.sentry.io https://* ${
isProd
? ``
@@ -289,9 +272,9 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
<body>
<div id="app" data-isProd="${isProd}" data-environment="${
isBeta ? 'BETA' : 'main'
}" data-version="${
version.usedVersion
}" data-is-crash-disabled="${!Telemetry.isVscodeEnabled()}"></div>
}" data-version="${version.usedVersion}" ${
experimental ? `data-experimental="${experimental}"` : ''
} data-is-crash-disabled="${!Telemetry.isVscodeEnabled()}"></div>
${(scriptsToLoad || [])
.map((script) => {
@@ -299,7 +282,9 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
})
.join('')}
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
${scriptUris
.map((uri) => `<script ${isProd ? `nonce="${nonce}"` : ''} src="${uri}"></script>`)
.join('\n')}
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`panel-${version.installedVersion}`}" alt="Daily usage" />
</body>

View File

@@ -137,7 +137,8 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.seo}>
<SeoStatus
seo={settings.seo}
data={metadata}
metadata={metadata}
settings={settings}
focusElm={focusElm}
unsetFocus={unsetFocus}
/>

View File

@@ -1,8 +1,8 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { Command } from '../Command';
import { VsCollapsible } from './VscodeComponents';
import { Pane as VSCodePane } from 'vscrui';
export interface ICollapsibleProps {
id: string;
@@ -19,10 +19,16 @@ const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({
className
}: React.PropsWithChildren<ICollapsibleProps>) => {
const [isOpen, setIsOpen] = React.useState(false);
const collapseKey = `collapse-${id}`;
const collapseKey = useMemo(() => `collapse_${id}`, [id]);
useEffect(() => {
if (!collapseKey) {
return;
}
const prevState: any = Messenger.getState();
console.log(collapseKey, prevState[collapseKey]);
if (
!prevState ||
!prevState[collapseKey] ||
@@ -31,6 +37,9 @@ const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({
) {
setIsOpen(true);
updateStorage(true);
} else {
setIsOpen(false);
updateStorage(false);
}
window.addEventListener('message', (event) => {
@@ -40,7 +49,7 @@ const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({
updateStorage(false);
}
});
}, ['']);
}, [collapseKey]);
const updateStorage = (value: boolean) => {
const prevState: any = Messenger.getState();
@@ -50,26 +59,24 @@ const Collapsible: React.FunctionComponent<ICollapsibleProps> = ({
});
};
// This is a work around for a lit-element issue of duplicate slot names
const triggerClick = (e: React.MouseEvent<HTMLElement>) => {
if ((e.target as Node).nodeName.toUpperCase() === 'VSCODE-COLLAPSIBLE') {
setIsOpen((prev) => {
if (sendUpdate) {
sendUpdate(!prev);
}
updateStorage(!prev);
return !prev;
});
const triggerClick = (open: boolean) => {
if (sendUpdate) {
sendUpdate(open);
}
updateStorage(open);
setIsOpen(open);
};
return (
<VsCollapsible title={title} onClick={triggerClick} open={isOpen}>
<div className={`section collapsible__body ${className || ''}`}>
<VSCodePane
title={title}
onClick={triggerClick}
open={isOpen}>
<div className={`section collapsible__body overflow-y-auto ${className || ''}`}>
{children}
</div>
</VsCollapsible>
</VSCodePane>
);
};

View File

@@ -1,5 +1,4 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { VSCodeButton, VSCodeDivider } from '@vscode/webview-ui-toolkit/react';
import * as React from 'react';
import { useMemo } from 'react';
import { Field } from '../../../models';
@@ -8,13 +7,15 @@ import { IMetadata } from '../Metadata';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { VSCodeLabel } from '../VSCode';
import { DefaultFields } from '../../../constants';
import { Button as VSCodeButton, Divider as VSCodeDivider } from 'vscrui';
export interface IContentTypeValidatorProps {
fields: Field[];
metadata: IMetadata;
}
const fieldsToIgnore = [`filePath`, `articleDetails`, `slug`, `keywords`, `type`];
const fieldsToIgnore = [`filePath`, `articleDetails`, DefaultFields.Slug, DefaultFields.Keywords, DefaultFields.Type, DefaultFields.ContentType];
export const ContentTypeValidator: React.FunctionComponent<IContentTypeValidatorProps> = ({
fields,
@@ -76,16 +77,16 @@ export const ContentTypeValidator: React.FunctionComponent<IContentTypeValidator
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorHint).split(`\n`).map(s => (<p className="inline_hint" key={s}>{s}</p>))}
<div className="hint__buttons">
<VSCodeButton style={{ "--border-width": 0 }} appearance={`secondary`} onClick={generateContentType}>
<div className="hint__buttons space-y-2">
<VSCodeButton appearance={`secondary`} onClick={generateContentType}>
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorButtonCreate)}
</VSCodeButton>
<VSCodeButton style={{ "--border-width": 0 }} appearance={`secondary`} onClick={addMissingFields}>
<VSCodeButton appearance={`secondary`} onClick={addMissingFields}>
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorButtonAdd)}
</VSCodeButton>
<VSCodeButton style={{ "--border-width": 0 }} appearance={`secondary`} onClick={setContentType}>
<VSCodeButton appearance={`secondary`} onClick={setContentType}>
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorButtonChange)}
</VSCodeButton>
</div>

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
import { Field, FieldGroup } from '../../../models';
import { VSCodeDropdown, VSCodeOption } from '@vscode/webview-ui-toolkit/react';
import { LocalizationKey } from '../../../localization';
import { LocalizationKey, localize } from '../../../localization';
import { Dropdown as VSCodeDropdown, DropdownOption } from 'vscrui';
export interface IDataBlockSelectorProps {
field: Field;
@@ -31,8 +31,8 @@ export const DataBlockSelector: React.FunctionComponent<IDataBlockSelectorProps>
}, []);
const onGroupSelect = useCallback(
(event: any) => {
const group = event.currentTarget.value === EMPTY_OPTION ? null : event.currentTarget.value;
(selection: DropdownOption) => {
const group = selection.value === EMPTY_OPTION ? null : selection.value;
if (group) {
onGroupSet(group);
@@ -71,23 +71,17 @@ export const DataBlockSelector: React.FunctionComponent<IDataBlockSelectorProps>
return (
<div className="json_data__selector">
<h3>{LocalizationKey.panelDataBlockDataBlockSelectorLabel}</h3>
<h3>{localize(LocalizationKey.panelDataBlockDataBlockSelectorLabel)}</h3>
<VSCodeDropdown
value={selectedGroup ?? EMPTY_OPTION}
onChange={onGroupSelect}
style={{
width: '100%',
marginBottom: '1rem'
}}
>
<VSCodeOption value={EMPTY_OPTION}>&nbsp;</VSCodeOption>
{options.map((o) => (
<VSCodeOption key={o} value={o}>
{o}
</VSCodeOption>
))}
</VSCodeDropdown>
onChange={(value) => onGroupSelect(value as DropdownOption)}
className='!block mb-4'
options={[
{ value: EMPTY_OPTION, label: '\u00A0' },
...options.map((o) => ({ value: o, label: o }))
]}
/>
</div>
);
};

Some files were not shown because too many files have changed in this diff Show More