forked from iarv/vscode-front-matter
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5b636d960 | ||
|
|
7fac27b73e | ||
|
|
aa0ee4708a | ||
|
|
24c26ac855 | ||
|
|
cda217ac76 | ||
|
|
cb42bd4b4b | ||
|
|
d4c5ca1c18 | ||
|
|
61398c4e25 | ||
|
|
b62d1e8177 | ||
|
|
65fc9f38ed | ||
|
|
c8ebac32d3 | ||
|
|
219c4bd657 | ||
|
|
73e58c7b52 | ||
|
|
e4147eed09 | ||
|
|
beef6f36d8 | ||
|
|
f3df0f6856 | ||
|
|
d11dbc9d76 | ||
|
|
bb535961a3 | ||
|
|
0c7e3fb42b | ||
|
|
a6188b0060 | ||
|
|
43a6a22721 | ||
|
|
99405042ed | ||
|
|
76b103cb62 | ||
|
|
be158d4365 | ||
|
|
b31550afe3 | ||
|
|
b845fd422b | ||
|
|
feb6b008e9 | ||
|
|
0fdbada702 | ||
|
|
6d010e48e8 | ||
|
|
18d7fb0c20 | ||
|
|
00dfbcb029 | ||
|
|
89aede03f0 | ||
|
|
ef9f1f7f56 | ||
|
|
d57d0c5d45 | ||
|
|
bb9ea9f1b9 | ||
|
|
7d5fde1182 | ||
|
|
33693d9315 | ||
|
|
8c52ef7bde | ||
|
|
9ce7754b1a | ||
|
|
9f2f279c20 | ||
|
|
0568149335 | ||
|
|
dde0231f19 | ||
|
|
b0dcbfd58b | ||
|
|
1b4e39b806 | ||
|
|
1fa73efe11 | ||
|
|
ddefb9f138 | ||
|
|
5e258ac218 | ||
|
|
d2b0228809 | ||
|
|
a164a849da | ||
|
|
710ef136b4 | ||
|
|
d3b7f73c66 | ||
|
|
ee5af88851 | ||
|
|
482cbc3bf6 | ||
|
|
64f1da6355 | ||
|
|
e27adececb | ||
|
|
b391aa3270 | ||
|
|
b58c02b6d0 | ||
|
|
88cad8caa2 | ||
|
|
a04d56fbde | ||
|
|
ec86b079a6 | ||
|
|
838ced0560 | ||
|
|
1c269db91d | ||
|
|
1ed5131abe | ||
|
|
d944319d53 | ||
|
|
146bbbf6a1 | ||
|
|
7bfc72469d | ||
|
|
324184964b | ||
|
|
1f6ea6ac20 | ||
|
|
5a45fdc94f | ||
|
|
b043c22437 | ||
|
|
b48e34ecb0 | ||
|
|
3bdae40ff0 | ||
|
|
94df672f4c | ||
|
|
98c5b56310 | ||
|
|
f38144b8a7 | ||
|
|
231bd89619 | ||
|
|
4907a7aaa9 | ||
|
|
2dc4865581 | ||
|
|
f1ae0d60cc | ||
|
|
2f76de2a28 | ||
|
|
d7282b18eb | ||
|
|
eb22a97198 | ||
|
|
42fe1c2887 | ||
|
|
c02275d20b | ||
|
|
147823bfd0 | ||
|
|
a7f183b6cc | ||
|
|
e10ee11f0e | ||
|
|
fca8d260d5 | ||
|
|
8cecf8d8be | ||
|
|
22ce41c3eb | ||
|
|
cb649a9a97 | ||
|
|
65d430b7cf | ||
|
|
f1f0e0ab58 | ||
|
|
5f7f847ff8 | ||
|
|
2c4dbeb1eb | ||
|
|
17164df11f | ||
|
|
228c46084d | ||
|
|
e838f18abc | ||
|
|
3d6359bc2e | ||
|
|
57b710cc61 | ||
|
|
7796d52ff9 | ||
|
|
f8f539be0d | ||
|
|
fc96c8922c | ||
|
|
c84af8493b | ||
|
|
6f288ff757 | ||
|
|
0e04e687fa | ||
|
|
cec3cbee3a | ||
|
|
c6f40194b4 | ||
|
|
6c591a90bd | ||
|
|
bece544934 | ||
|
|
c40fcba088 | ||
|
|
ea9f8a2651 | ||
|
|
b9b927c800 | ||
|
|
bc0f2e7bf7 | ||
|
|
b18f5e1e36 | ||
|
|
8b05da5a76 | ||
|
|
0062117c3b | ||
|
|
1d485adbca | ||
|
|
9f2aa34aac | ||
|
|
46a7a49e7c | ||
|
|
61ae29c37a | ||
|
|
9d51531d59 | ||
|
|
0cb7d2463b | ||
|
|
ceeb1bf9a7 | ||
|
|
c11efa56f1 | ||
|
|
fa3215fa64 | ||
|
|
305c95fa86 | ||
|
|
3b7671afc9 | ||
|
|
8660f5f680 | ||
|
|
2269994b43 | ||
|
|
bdcd901e51 | ||
|
|
6d7df4266d | ||
|
|
8c2d243777 | ||
|
|
4282ec83e5 | ||
|
|
0f3c43e0fc | ||
|
|
a377f27765 | ||
|
|
17860a18f4 | ||
|
|
73609ca346 | ||
|
|
d6dfa8c9cf | ||
|
|
1c00362b1c | ||
|
|
63ea564734 | ||
|
|
38f128e1b6 | ||
|
|
39704f3a55 | ||
|
|
2020198e90 | ||
|
|
ba1cf95ffd | ||
|
|
aea87a6168 | ||
|
|
179a71db39 | ||
|
|
8d8e3fe3cc | ||
|
|
3d8c550f60 | ||
|
|
6fd526e962 | ||
|
|
788d0241fd | ||
|
|
017a2d7597 | ||
|
|
3019ba1dff | ||
|
|
13e58d26a1 | ||
|
|
634196b056 | ||
|
|
8b95468c78 | ||
|
|
dc23aba128 | ||
|
|
a778be9737 | ||
|
|
b9508df4f8 | ||
|
|
0110b7365c | ||
|
|
6588b90e7d | ||
|
|
47dba5f510 | ||
|
|
121a84659f | ||
|
|
620966c08e | ||
|
|
06718c3577 | ||
|
|
178207fd82 | ||
|
|
657e9054f6 | ||
|
|
36a8002cea | ||
|
|
07f124dcf5 | ||
|
|
ff1d4487f4 | ||
|
|
66151083c0 | ||
|
|
83abff67ac | ||
|
|
431a83b882 | ||
|
|
d240e8fdc8 | ||
|
|
e95e9a8fc7 | ||
|
|
d8e3338abe | ||
|
|
6f6b97e6ca | ||
|
|
3f8665cadf | ||
|
|
8cc68be4da | ||
|
|
27f2b57c24 | ||
|
|
9b1be1a6c1 | ||
|
|
d0b7af5c86 | ||
|
|
f13058c59b | ||
|
|
cf28e5fc85 | ||
|
|
cf787ab0f6 | ||
|
|
c7424a6d73 |
@@ -12,6 +12,9 @@
|
||||
"no-throw-literal": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"curly": "error",
|
||||
"class-methods-use-this": "warn"
|
||||
"class-methods-use-this": "warn",
|
||||
"no-console": "warn",
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"no-extra-boolean-cast": "off"
|
||||
}
|
||||
}
|
||||
|
||||
3
.github/actions/localization/action.yml
vendored
3
.github/actions/localization/action.yml
vendored
@@ -20,7 +20,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
|
||||
@@ -42,5 +42,6 @@ runs:
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
include-hidden-files: true
|
||||
name: ${{ inputs.PACKAGE_NAME }}
|
||||
path: .
|
||||
4
.github/workflows/release-beta.yml
vendored
4
.github/workflows/release-beta.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -10,12 +10,6 @@
|
||||
"values": ["#build", "#deploy", "#skip"]
|
||||
}
|
||||
],
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.activeBackground": "#15c2cb",
|
||||
"titleBar.inactiveBackground": "#44ffd299",
|
||||
"titleBar.activeForeground": "#0E131F",
|
||||
"titleBar.inactiveForeground": "#0E131F99"
|
||||
},
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
|
||||
129
CHANGELOG.md
129
CHANGELOG.md
@@ -1,5 +1,134 @@
|
||||
# Change Log
|
||||
|
||||
## [10.10.0] - 2025-xx-xx
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#937](https://github.com/estruyf/vscode-front-matter/issues/937): Dashboard "Structure" view for documentation sites
|
||||
- [#965](https://github.com/estruyf/vscode-front-matter/issues/965): Added SEO support for the keyword in the first paragraph
|
||||
- [#973](https://github.com/estruyf/vscode-front-matter/issues/973): Support for number fields in the snippets
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#969](https://github.com/estruyf/vscode-front-matter/issues/969): Fix typo on welcome screen
|
||||
|
||||
## [10.9.0] - 2025-07-01 - [Release notes](https://beta.frontmatter.codes/updates/v10.9.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#962](https://github.com/estruyf/vscode-front-matter/issues/962): Added Simplified Chinese localization thanks to [Randerion(HaoJun0823)](https://github.com/HaoJun0823)
|
||||
|
||||
### ⚡️ Optimizations
|
||||
|
||||
- [#922](https://github.com/estruyf/vscode-front-matter/issues/922): Added the `{{slugifiedFileName}}` for better naming
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#933](https://github.com/estruyf/vscode-front-matter/issues/933): Timezone setting integration in the DateTime field
|
||||
- [#942](https://github.com/estruyf/vscode-front-matter/issues/942): Fix to typo on welcome screen thanks to [Stephanie Wertman](https://github.com/stephanie-wertman)
|
||||
- [#957](https://github.com/estruyf/vscode-front-matter/issues/957): Fix media assets retrieval where `mtime` is not defined. Fallback to the `mtimeMs` property if available.
|
||||
|
||||
## [10.8.0] - 2025-02-27 - [Release notes](https://beta.frontmatter.codes/updates/v10.8.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#915](https://github.com/estruyf/vscode-front-matter/issues/915): Added a new setting `frontMatter.panel.openOnSupportedFile` which allows you to open the panel view on supported files
|
||||
- [#921](https://github.com/estruyf/vscode-front-matter/issues/921): Improve the filename sanitization
|
||||
- [#922](https://github.com/estruyf/vscode-front-matter/issues/922): Added `{{fileName}}` and `{{sluggedFileName}}` placeholders for the slug template setting
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- Fix for media folder parsing on Windows
|
||||
- Refresh button was not available on the media dashboard when having custom scripts defined
|
||||
- [#909](https://github.com/estruyf/vscode-front-matter/issues/909): Schema fix for the view modes
|
||||
- [#913](https://github.com/estruyf/vscode-front-matter/issues/913): Fix for relative media paths in page bundles
|
||||
- [#914](https://github.com/estruyf/vscode-front-matter/issues/914): Fix sanitizing of default filenames with an `_` in it
|
||||
|
||||
## [10.7.0] - 2024-12-31 - [Release notes](https://beta.frontmatter.codes/updates/v10.7.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#405](https://github.com/estruyf/vscode-front-matter/issues/405): Added new `frontMatter.content.grouping` setting which allows you to define custom "group by" options
|
||||
- [#705](https://github.com/estruyf/vscode-front-matter/issues/705): UX improvements for the panel view
|
||||
- [#887](https://github.com/estruyf/vscode-front-matter/issues/887): Added new `frontMatter.global.timezone` setting, by default it is set to `UTC` for date formatting
|
||||
- [#888](https://github.com/estruyf/vscode-front-matter/issues/888): Added the ability to prompt GitHub Copilot from a custom script/action
|
||||
- [#892](https://github.com/estruyf/vscode-front-matter/issues/892): Added media folder common actions
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#895](https://github.com/estruyf/vscode-front-matter/issues/895): Fix issue with array values in filters
|
||||
|
||||
## [10.6.0] - 2024-11-06 - [Release notes](https://beta.frontmatter.codes/updates/v10.6.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#878](https://github.com/estruyf/vscode-front-matter/issues/878): Allow the `select all` button to work on other pages when there is a selection present
|
||||
- [#882](https://github.com/estruyf/vscode-front-matter/issues/882): Dynamic evaluation of the `node` executable path
|
||||
- [#884](https://github.com/estruyf/vscode-front-matter/issues/884): Hide WYSIWYG actions when the file is in git diff mode
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#859](https://github.com/estruyf/vscode-front-matter/issues/859): Fix label in the data view dropdown field
|
||||
- [#876](https://github.com/estruyf/vscode-front-matter/issues/876): Fix snippet type on the snippet card
|
||||
- [#879](https://github.com/estruyf/vscode-front-matter/issues/879): Fix for auto updating last modified date on save
|
||||
- [#885](https://github.com/estruyf/vscode-front-matter/issues/885): Fix content relationship for none i18n content
|
||||
|
||||
## [10.5.1] - 2024-10-23
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#873](https://github.com/estruyf/vscode-front-matter/issues/873): Add retry logic to get the AI model for calling GitHub Copilot
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#872](https://github.com/estruyf/vscode-front-matter/issues/872): Check the default field value as well for the field's `when` clause
|
||||
- [#874](https://github.com/estruyf/vscode-front-matter/issues/874): Fix media snippet markup insertion to article content's
|
||||
- [#875](https://github.com/estruyf/vscode-front-matter/issues/875): Clean up the exclamation marks from the file name when creating new content
|
||||
|
||||
## [10.5.0] - 2024-10-21 - [Release notes](https://beta.frontmatter.codes/updates/v10.5.0)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#840](https://github.com/estruyf/vscode-front-matter/issues/840): Added the `excludePaths` option for the content folder settings
|
||||
- [#850](https://github.com/estruyf/vscode-front-matter/issues/850): Extended the i18n/language button to open or create new language files (thanks to [Dennis Zoma](https://github.com/wottpal))
|
||||
- [#851](https://github.com/estruyf/vscode-front-matter/issues/851): Added `sameContentLocale` option to `contentRelationship` field (thanks to [Dennis Zoma](https://github.com/wottpal))
|
||||
- [#866](https://github.com/estruyf/vscode-front-matter/issues/866): Support Markdown in the WYSIWYG `string` field
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#858](https://github.com/estruyf/vscode-front-matter/issues/858): Fix button styling on the data screen
|
||||
- [#860](https://github.com/estruyf/vscode-front-matter/issues/860): Fix typo on the data screen
|
||||
- [#870](https://github.com/estruyf/vscode-front-matter/issues/870): Fix data number field styling
|
||||
|
||||
## [10.4.1] - 2024-09-27
|
||||
|
||||
- [#855](https://github.com/estruyf/vscode-front-matter/issues/855): Fix in panel sections
|
||||
|
||||
## [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
|
||||
|
||||
- [#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
|
||||
|
||||
@@ -117,7 +117,7 @@ In version v2 we released the re-designed sidebar panel with improved SEO suppor
|
||||
You can get the extension via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter`
|
||||
- The extension CLI: `code --install-extension eliostruyf.vscode-front-matter`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The docs can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
@@ -129,7 +129,7 @@ If you have the courage to test out the beta features, we made available a beta
|
||||
- Uninstall the main Front Matter version
|
||||
- Install the beta version
|
||||
- VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- The extension CLI: `code --install-extension eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The BETA docs can be found on [beta.frontmatter.codes](https://beta.frontmatter.codes).
|
||||
@@ -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 />
|
||||
|
||||
@@ -115,7 +115,7 @@ In version v2 we released the re-designed sidebar panel with improved SEO suppor
|
||||
You can get the extension via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter`
|
||||
- The extension CLI: `code --install-extension eliostruyf.vscode-front-matter`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The docs can be found on [frontmatter.codes](https://frontmatter.codes).
|
||||
@@ -127,7 +127,7 @@ If you have the courage to test out the beta features, we made available a beta
|
||||
- Uninstall the main Front Matter version
|
||||
- Install the beta version
|
||||
- VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- The extension CLI: `code --install-extension eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The BETA docs can be found on [beta.frontmatter.codes](https://beta.frontmatter.codes).
|
||||
@@ -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 />
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
|
||||
.frontmatter h3 {
|
||||
margin-bottom: 1rem;
|
||||
/* margin-bottom: 1rem; */
|
||||
}
|
||||
|
||||
.frontmatter p,
|
||||
@@ -99,11 +99,6 @@
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.seo__status__details,
|
||||
.seo__status__keywords {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.collapsible__body h4 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
@@ -131,7 +126,8 @@
|
||||
}
|
||||
|
||||
.article__tags__dropbox.open {
|
||||
border: 1px solid rgba(0, 0, 0, 0.9);
|
||||
border: 1px solid var(--vscode-focusBorder);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.article__tags ul {
|
||||
@@ -224,6 +220,7 @@
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.ext_link_block button.active {
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"dashboard.header.tabs.taxonomies": "Taxonomien",
|
||||
"dashboard.header.viewSwitch.toGrid": "Zur Rasteransicht wechseln",
|
||||
"dashboard.header.viewSwitch.toList": "Zur Listenansicht wechseln",
|
||||
"dashboard.header.viewSwitch.toStructure": "Zur Strukturansicht wechseln",
|
||||
"dashboard.layout.sponsor.support.msg": "Unterstützen Sie Front Matter",
|
||||
"dashboard.layout.sponsor.review.label": "Bewerten",
|
||||
"dashboard.layout.sponsor.review.msg": "Bewerten Sie Front Matter",
|
||||
@@ -258,9 +259,6 @@
|
||||
"panel.fields.textField.limit": "Feldgrenze erreicht {0}",
|
||||
"panel.fields.wrapperField.unknown": "Unbekannter Feldtyp: {0}",
|
||||
"panel.actions.title": "Aktionen",
|
||||
"panel.articleDetails.title": "Weitere Details",
|
||||
"panel.articleDetails.type": "Typ",
|
||||
"panel.articleDetails.total": "Gesamt",
|
||||
"panel.articleDetails.headings": "Überschriften",
|
||||
"panel.articleDetails.paragraphs": "Absätze",
|
||||
"panel.articleDetails.internalLinks": "Interne Links",
|
||||
@@ -299,16 +297,13 @@
|
||||
"panel.publishAction.publish": "Veröffentlichen",
|
||||
"panel.publishAction.unpublish": "Zurück zu Entwurf",
|
||||
"panel.seoDetails.recommended": "Empfohlen",
|
||||
"panel.seoKeywordInfo.density": "Stichwortdichte {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Verwendet in Überschrift(en)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Inhalt",
|
||||
"panel.seoKeywords.title": "Stichwörter",
|
||||
"panel.seoKeywords.header.keyword": "Stichwort",
|
||||
"panel.seoKeywords.header.details": "Details",
|
||||
"panel.seoKeywords.density": "* Eine Stichwortdichte von 1-1,5 % ist in den meisten Fällen ausreichend.",
|
||||
"panel.seoStatus.title": "Empfehlungen",
|
||||
"panel.seoKeywords.density.description": "* Eine Stichwortdichte von 1-1,5 % ist in den meisten Fällen ausreichend.",
|
||||
"panel.seoStatus.header.property": "Eigenschaft",
|
||||
"panel.seoStatus.header.length": "Länge",
|
||||
"panel.seoStatus.header.valid": "Gültig",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} Zeichen",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} Wörter",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"dashboard.header.tabs.taxonomies": "Taxonomies",
|
||||
"dashboard.header.viewSwitch.toGrid": "Afficher en grille",
|
||||
"dashboard.header.viewSwitch.toList": "Afficher en liste",
|
||||
"dashboard.header.viewSwitch.toStructure": "Afficher en structure",
|
||||
"dashboard.layout.sponsor.support.msg": "Soutenir Front Matter",
|
||||
"dashboard.layout.sponsor.review.label": "Donnez votre avis",
|
||||
"dashboard.layout.sponsor.review.msg": "Donnez votre avis sur Front Matter",
|
||||
@@ -263,9 +264,6 @@
|
||||
"panel.fields.textField.limit": "Limite de champ atteinte {0}",
|
||||
"panel.fields.wrapperField.unknown": "Type de champ inconnu : {0}",
|
||||
"panel.actions.title": "Actions",
|
||||
"panel.articleDetails.title": "Plus de détails",
|
||||
"panel.articleDetails.type": "Type",
|
||||
"panel.articleDetails.total": "Total",
|
||||
"panel.articleDetails.headings": "En-têtes",
|
||||
"panel.articleDetails.paragraphs": "Paragraphes",
|
||||
"panel.articleDetails.internalLinks": "Liens internes",
|
||||
@@ -304,16 +302,13 @@
|
||||
"panel.publishAction.publish": "Publié",
|
||||
"panel.publishAction.unpublish": "Retourner au brouillon",
|
||||
"panel.seoDetails.recommended": "Recommandé",
|
||||
"panel.seoKeywordInfo.density": "Utilisation du mot clé {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Utilisé dans le ou les en-tête(s)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Contenu",
|
||||
"panel.seoKeywords.title": "Mot-clés",
|
||||
"panel.seoKeywords.header.keyword": "Mot-clé",
|
||||
"panel.seoKeywords.header.details": "Détails",
|
||||
"panel.seoKeywords.density": "* Une densité de mot-clé de 1-1.5% est suffisante dans la plupart des cas",
|
||||
"panel.seoStatus.title": "Recommandations",
|
||||
"panel.seoKeywords.density.description": "* Une densité de mot-clé de 1-1.5% est suffisante dans la plupart des cas",
|
||||
"panel.seoStatus.header.property": "Propriété",
|
||||
"panel.seoStatus.header.length": "Longueur",
|
||||
"panel.seoStatus.header.valid": "Valide",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} caractères",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} mots",
|
||||
|
||||
@@ -263,9 +263,6 @@
|
||||
"panel.fields.textField.limit": "Limite di campi raggiunto {0}",
|
||||
"panel.fields.wrapperField.unknown": "Tipo di campo sconosciuto: {0}",
|
||||
"panel.actions.title": "Azioni",
|
||||
"panel.articleDetails.title": "Più dettagli",
|
||||
"panel.articleDetails.type": "Digitare",
|
||||
"panel.articleDetails.total": "Totale",
|
||||
"panel.articleDetails.headings": "Intestazioni",
|
||||
"panel.articleDetails.paragraphs": "Paragrafi",
|
||||
"panel.articleDetails.internalLinks": "Collegamenti esterni",
|
||||
@@ -304,16 +301,13 @@
|
||||
"panel.publishAction.publish": "Pubblica",
|
||||
"panel.publishAction.unpublish": "Tornare alla bozza",
|
||||
"panel.seoDetails.recommended": "Raccomandato",
|
||||
"panel.seoKeywordInfo.density": "Utilizzo delle parole chiave {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Utilizzato nelle rubriche",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Contenuto",
|
||||
"panel.seoKeywords.title": "Parole chiavi",
|
||||
"panel.seoKeywords.header.keyword": "Parola chiave",
|
||||
"panel.seoKeywords.header.details": "Dettagli",
|
||||
"panel.seoKeywords.density": "* Una densità di parole chiave dell'1-1,5% è sufficiente nella maggior parte dei casi.",
|
||||
"panel.seoStatus.title": "Consigli",
|
||||
"panel.seoKeywords.density.description": "* Una densità di parole chiave dell'1-1,5% è sufficiente nella maggior parte dei casi.",
|
||||
"panel.seoStatus.header.property": "Proprietà",
|
||||
"panel.seoStatus.header.length": "Lunghezza",
|
||||
"panel.seoStatus.header.valid": "Valido",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} caratteri",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} parole",
|
||||
|
||||
@@ -214,6 +214,7 @@
|
||||
|
||||
"dashboard.header.viewSwitch.toGrid": "グリッド表示",
|
||||
"dashboard.header.viewSwitch.toList": "リスト表示",
|
||||
"dashboard.header.viewSwitch.toStructure": "構造表示",
|
||||
|
||||
"dashboard.layout.sponsor.support.msg": "Front Matterをサポートする",
|
||||
"dashboard.layout.sponsor.review.label": "評価する",
|
||||
@@ -438,9 +439,6 @@
|
||||
|
||||
"panel.actions.title": "コマンド",
|
||||
|
||||
"panel.articleDetails.title": "詳細",
|
||||
"panel.articleDetails.type": "項目",
|
||||
"panel.articleDetails.total": "数",
|
||||
"panel.articleDetails.headings": "見出し",
|
||||
"panel.articleDetails.paragraphs": "パラグラフ",
|
||||
"panel.articleDetails.internalLinks": "内部リンク",
|
||||
@@ -489,18 +487,15 @@
|
||||
|
||||
"panel.seoDetails.recommended": "推奨",
|
||||
|
||||
"panel.seoKeywordInfo.density": "キーワード出現率 {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "見出しへの利用",
|
||||
"panel.seoKeywordInfo.validInfo.content": "本文",
|
||||
|
||||
"panel.seoKeywords.title": "キーワード",
|
||||
"panel.seoKeywords.header.keyword": "キーワード",
|
||||
"panel.seoKeywords.header.details": "詳細",
|
||||
"panel.seoKeywords.density": "* キーワード出現率は通常1~1.5%で十分です。",
|
||||
"panel.seoKeywords.density.description": "* キーワード出現率は通常1~1.5%で十分です。",
|
||||
|
||||
"panel.seoStatus.title": "推奨項目",
|
||||
"panel.seoStatus.header.property": "項目",
|
||||
"panel.seoStatus.header.length": "長さ",
|
||||
"panel.seoStatus.header.valid": "有効",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} 文字",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} 語",
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"settings.view.integration": "Integration",
|
||||
|
||||
"settings.openOnStartup": "Open dashboard on startup",
|
||||
"settings.openPanelForSupportedFiles": "Open panel for supported files",
|
||||
"settings.openPanelForSupportedFiles.label": "Do you want to open the panel for supported files?",
|
||||
"settings.contentTypes": "Content types",
|
||||
"settings.contentFolders": "Content folders",
|
||||
"settings.diagnostic": "Diagnostic",
|
||||
@@ -140,8 +142,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": "Select your data 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}\"",
|
||||
@@ -183,7 +189,7 @@
|
||||
|
||||
"dashboard.header.pagination.first": "First",
|
||||
"dashboard.header.pagination.previous": "Previous",
|
||||
"dashboard.header.pagination.next": "next",
|
||||
"dashboard.header.pagination.next": "Next",
|
||||
"dashboard.header.pagination.last": "Last",
|
||||
|
||||
"dashboard.header.paginationStatus.text": "Showing {0} to {1} of {2} results",
|
||||
@@ -216,6 +222,7 @@
|
||||
|
||||
"dashboard.header.viewSwitch.toGrid": "Change to grid",
|
||||
"dashboard.header.viewSwitch.toList": "Change to list",
|
||||
"dashboard.header.viewSwitch.toStructure": "Change to structure",
|
||||
|
||||
"dashboard.layout.sponsor.support.msg": "Support Front Matter",
|
||||
"dashboard.layout.sponsor.review.label": "Review",
|
||||
@@ -243,6 +250,7 @@
|
||||
|
||||
"dashboard.media.folderItem.contentDirectory": "Content directory",
|
||||
"dashboard.media.folderItem.publicDirectory": "Public directory",
|
||||
"dashboard.media.folderItem.deleteDescription": "Are you sure you want to delete the folder ({0})?",
|
||||
|
||||
"dashboard.media.item.buttom.insert.image": "Insert image",
|
||||
"dashboard.media.item.buttom.insert.snippet": "Insert snippet",
|
||||
@@ -325,7 +333,7 @@
|
||||
"dashboard.steps.stepsToGetStarted.tags.name": "Import all tags and categories (optional)",
|
||||
"dashboard.steps.stepsToGetStarted.tags.description": "Now that Front Matter knows all the content folders. Would you like to import all tags and categories from the available content?",
|
||||
"dashboard.steps.stepsToGetStarted.git.name": "Do you want to enable Git synchronization?",
|
||||
"dashboard.steps.stepsToGetStarted.git.description": "Enable Git synchronization to eaily sync your changes with your repository.",
|
||||
"dashboard.steps.stepsToGetStarted.git.description": "Enable Git synchronization to easily sync your changes with your repository.",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.name": "Show the dashboard",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.description": "Once all actions are completed, the dashboard can be loaded.",
|
||||
"dashboard.steps.stepsToGetStarted.template.name": "Use a configuration template",
|
||||
@@ -364,7 +372,7 @@
|
||||
|
||||
"dashboard.welcomeScreen.title": "Manage your static site with Front Matter",
|
||||
"dashboard.welcomeScreen.thanks": "Thank you for using Front Matter!",
|
||||
"dashboard.welcomeScreen.description": "We try to aim to make Front Matter as easy to use as possible, but if you have any questions or suggestions. Please don't hesitate to reach out to us on GitHub.",
|
||||
"dashboard.welcomeScreen.description": "We aim to make Front Matter as easy to use as possible. If you have any questions or suggestions, please contact us on GitHub.",
|
||||
"dashboard.welcomeScreen.link.github.title": "GitHub",
|
||||
"dashboard.welcomeScreen.link.github.label": "GitHub",
|
||||
"dashboard.welcomeScreen.link.documentation.label": "Documentation",
|
||||
@@ -444,9 +452,6 @@
|
||||
|
||||
"panel.actions.title": "Actions",
|
||||
|
||||
"panel.articleDetails.title": "More details",
|
||||
"panel.articleDetails.type": "Type",
|
||||
"panel.articleDetails.total": "Total",
|
||||
"panel.articleDetails.headings": "Headings",
|
||||
"panel.articleDetails.paragraphs": "Paragraphs",
|
||||
"panel.articleDetails.internalLinks": "Internal links",
|
||||
@@ -495,18 +500,21 @@
|
||||
|
||||
"panel.seoDetails.recommended": "Recommended",
|
||||
|
||||
"panel.seoKeywordInfo.density": "Keyword usage {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Used in heading(s)",
|
||||
"panel.seoKeywords.checks": "Checks",
|
||||
"panel.seoKeywords.density.tableTitle": "Frequency",
|
||||
"panel.seoKeywords.density": "Keyword density",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Heading(s)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Content",
|
||||
"panel.seoKeywordInfo.validInfo.firstParagraph": "First paragraph",
|
||||
"panel.seoKeywordInfo.density.tooltip": "Recommended frequency: 0.75% - 1.5%",
|
||||
|
||||
"panel.seoKeywords.title": "Keywords",
|
||||
"panel.seoKeywords.header.keyword": "Keyword",
|
||||
"panel.seoKeywords.header.details": "Details",
|
||||
"panel.seoKeywords.density": "* A keyword density of 1-1.5% is sufficient in most cases.",
|
||||
"panel.seoKeywords.density.description": "* A keyword density of 1-1.5% is sufficient in most cases.",
|
||||
|
||||
"panel.seoStatus.title": "Recommendations",
|
||||
"panel.seoStatus.title": "Insights",
|
||||
"panel.seoStatus.header.property": "Property",
|
||||
"panel.seoStatus.header.length": "Length",
|
||||
"panel.seoStatus.header.valid": "Valid",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} chars",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} words",
|
||||
@@ -579,6 +587,11 @@
|
||||
"commands.i18n.create.success.created": "Created \"{0}\" i18n content file.",
|
||||
"commands.i18n.create.quickPick.title": "Create content for locale",
|
||||
"commands.i18n.create.quickPick.placeHolder": "To which locale do you want to create a new content?",
|
||||
"commands.i18n.createOrOpen.quickPick.title": "Open or create translation",
|
||||
"commands.i18n.createOrOpen.quickPick.category.existing": "Existing translations",
|
||||
"commands.i18n.createOrOpen.quickPick.action.open": "Open \"{0}\"",
|
||||
"commands.i18n.createOrOpen.quickPick.category.new": "New translations",
|
||||
"commands.i18n.createOrOpen.quickPick.action.create": "Create \"{0}\"",
|
||||
"commands.i18n.translate.progress.title": "Translating content...",
|
||||
|
||||
"commands.preview.panel.title": "Preview: {0}",
|
||||
@@ -767,6 +780,9 @@
|
||||
"listeners.dashboard.dashboardListener.pinItem.coundNotPin.error": "Could not pin item.",
|
||||
"listeners.dashboard.dashboardListener.pinItem.coundNotUnPin.error": "Could not unpin item.",
|
||||
|
||||
"listeners.dashboard.mediaListeners.deleteMediaFolder.progress.title": "Deleting folder...",
|
||||
"listeners.dashboard.mediaListeners.updateMediaFolder.progress.title": "Updating folder...",
|
||||
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Template files copied.",
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.progress.title": "Downloading and initializing the template...",
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.download.error": "Failed to download the template.",
|
||||
@@ -783,7 +799,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",
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error": "No article data",
|
||||
@@ -802,4 +820,4 @@
|
||||
"services.sponsorAi.getTaxonomySuggestions.warning": "The AI taxonomy generation took too long. Please try again later.",
|
||||
|
||||
"services.terminal.openLocalServerTerminal.terminalOption.message": "Starting local server"
|
||||
}
|
||||
}
|
||||
|
||||
822
l10n/bundle.l10n.zh-cn.json
Normal file
822
l10n/bundle.l10n.zh-cn.json
Normal file
@@ -0,0 +1,822 @@
|
||||
{
|
||||
"common.add": "添加",
|
||||
"common.edit": "编辑",
|
||||
"common.delete": "删除",
|
||||
"common.cancel": "取消",
|
||||
"common.apply": "应用",
|
||||
"common.clear": "清除",
|
||||
"common.clear.value": "清除值",
|
||||
"common.search": "搜索",
|
||||
"common.save": "保存",
|
||||
"common.menu": "菜单",
|
||||
"common.insert": "插入",
|
||||
"common.insert.snippet": "插入片段",
|
||||
"common.title": "标题",
|
||||
"common.description": "描述",
|
||||
"common.retry": "重试",
|
||||
"common.update": "更新",
|
||||
"common.information": "信息",
|
||||
"common.important": "重要",
|
||||
"common.sync": "同步",
|
||||
"common.slug": "别名",
|
||||
"common.support": "支持",
|
||||
"common.remove.value": "移除 {0}",
|
||||
"common.filter": "筛选",
|
||||
"common.filter.value": "按 {0} 筛选",
|
||||
"common.error.message": "抱歉,出错了。",
|
||||
"common.openOnWebsite": "在网站上打开",
|
||||
"common.settings": "设置",
|
||||
"common.refreshSettings": "刷新设置",
|
||||
"common.pin": "固定",
|
||||
"common.unpin": "取消固定",
|
||||
"common.noResults": "无结果",
|
||||
"common.error": "抱歉,出错了。",
|
||||
"common.yes": "是",
|
||||
"common.no": "否",
|
||||
"common.openSettings": "打开设置",
|
||||
"common.back": "返回",
|
||||
"common.open": "打开",
|
||||
"common.openWithValue": "打开:{0}",
|
||||
"common.openCustomActions": "打开自定义操作",
|
||||
"common.view": "查看",
|
||||
"common.translate": "翻译",
|
||||
"common.languages": "语言",
|
||||
"common.scripts": "脚本",
|
||||
"common.rename": "重命名",
|
||||
"common.docs": "文档",
|
||||
|
||||
"loading.initPages": "正在加载内容",
|
||||
|
||||
"notifications.outputChannel.link": "输出窗口",
|
||||
"notifications.outputChannel.description": "更多详情请查看 {0}。",
|
||||
|
||||
"settings.view.common": "通用",
|
||||
"settings.view.contentFolders": "内容文件夹",
|
||||
"settings.view.astro": "Astro",
|
||||
"settings.view.integration": "集成",
|
||||
|
||||
"settings.openOnStartup": "启动时打开仪表盘",
|
||||
"settings.openPanelForSupportedFiles": "为支持的文件打开面板",
|
||||
"settings.openPanelForSupportedFiles.label": "是否要为支持的文件打开面板?",
|
||||
"settings.contentTypes": "内容类型",
|
||||
"settings.contentFolders": "内容文件夹",
|
||||
"settings.diagnostic": "诊断",
|
||||
"settings.diagnostic.description": "您可以运行诊断程序来检查整个 Front Matter CMS 配置。",
|
||||
"settings.diagnostic.link": "运行完整诊断",
|
||||
"settings.git": "Git 同步",
|
||||
"settings.git.enabled": "启用 Git 同步,以便轻松将更改同步到您的仓库。",
|
||||
"settings.git.commitMessage": "提交消息",
|
||||
"settings.git.submoduleInfo": "当使用 Git 子模块时,您可以在文档中参考子模块设置。",
|
||||
"settings.git.submoduleLink": "了解更多关于 Git 子模块",
|
||||
"settings.integration.title": "集成",
|
||||
|
||||
"settings.commonSettings.website.title": "网站和 SSG 设置",
|
||||
"settings.commonSettings.previewUrl": "预览 URL",
|
||||
"settings.commonSettings.websiteUrl": "网站 URL",
|
||||
"settings.commonSettings.startCommand": "SSG/框架 启动命令",
|
||||
|
||||
"settings.integrationsView.deepl.title": "DeepL",
|
||||
"settings.integrationsView.deepl.intput.label": "API 密钥",
|
||||
"settings.integrationsView.deepl.intput.placeholder": "输入您的 Deepl API 密钥",
|
||||
|
||||
"settings.integrationsView.azure.title": "Azure AI 翻译服务",
|
||||
"settings.integrationsView.azure.intput.label": "订阅密钥",
|
||||
"settings.integrationsView.azure.intput.placeholder": "输入您的 Azure AI 翻译 - 订阅密钥",
|
||||
"settings.integrationsView.azure.region.label": "区域",
|
||||
"settings.integrationsView.azure.region.placeholder": "输入您的 Azure AI 翻译 - 区域。例如:westeurope",
|
||||
|
||||
"developer.title": "开发者模式",
|
||||
"developer.reload.title": "重新加载仪表盘",
|
||||
"developer.reload.label": "重新加载",
|
||||
"developer.devTools.title": "打开开发者工具",
|
||||
"developer.devTools.label": "开发者工具",
|
||||
|
||||
"field.required": "必填字段",
|
||||
"field.unknown": "未知字段",
|
||||
|
||||
"dashboard.chatbot.answer.answer": "答案",
|
||||
"dashboard.chatbot.answer.resources": "资源",
|
||||
"dashboard.chatbot.answer.warning": "警告:答案可能有误。如有疑问,请查阅文档。",
|
||||
|
||||
"dashboard.chatbot.chatbot.loading": "助手正在准备中",
|
||||
"dashboard.chatbot.chatbot.ready": "我准备好了,您想知道什么?",
|
||||
|
||||
"dashboard.chatbot.chatbox.placeholder": "如何配置 Front Matter?",
|
||||
|
||||
"dashboard.chatbot.header.heading": "询问 Front Matter AI",
|
||||
"dashboard.chatbot.header.description": "我们的 AI 由 mendable.ai 提供支持,已处理文档,可以协助您解决任何关于 Front Matter 的疑问。请随时提问!",
|
||||
|
||||
"dashboard.common.choiceButton.open": "打开选项",
|
||||
|
||||
"dashboard.contents.contentActions.actionMenuButton.title": "菜单",
|
||||
"dashboard.contents.contentActions.menuItem.view": "查看",
|
||||
"dashboard.contents.contentActions.alert.title": "删除:{0}",
|
||||
"dashboard.contents.contentActions.alert.description": "您确定要删除 \"{0}\" 内容吗?",
|
||||
"dashboard.contents.contentActions.translations.create": "创建翻译",
|
||||
"dashboard.contents.contentActions.translations.menu": "翻译",
|
||||
|
||||
"dashboard.contents.item.invalidTitle": "<无效标题>",
|
||||
"dashboard.contents.item.invalidDescription": "<无效描述>",
|
||||
|
||||
"dashboard.contents.list.title": "标题",
|
||||
"dashboard.contents.list.date": "日期",
|
||||
"dashboard.contents.list.status": "状态",
|
||||
|
||||
"dashboard.contents.overview.noMarkdown": "无 Markdown 可显示",
|
||||
"dashboard.contents.overview.noFolders": "请确保在项目中注册了一个内容文件夹,以便 Front Matter 能够找到内容。",
|
||||
"dashboard.contents.overview.pinned": "已固定",
|
||||
|
||||
"dashboard.contents.status.draft": "草稿",
|
||||
"dashboard.contents.status.published": "已发布",
|
||||
"dashboard.contents.status.scheduled": "已排期",
|
||||
|
||||
"dashboard.dataView.dataForm.modify": "修改数据",
|
||||
"dashboard.dataView.dataForm.add": "添加新数据",
|
||||
|
||||
"dashboard.dataView.dataView.select": "选择您的数据类型",
|
||||
"dashboard.dataView.dataView.title": "您的 {0} 数据项",
|
||||
"dashboard.dataView.dataView.add": "添加新条目",
|
||||
"dashboard.dataView.dataView.empty": "未找到 {0} 数据条目",
|
||||
"dashboard.dataView.dataView.createOrModify": "创建或修改您的 {0} 数据",
|
||||
"dashboard.dataView.dataView.getStarted": "选择一个数据类型开始",
|
||||
"dashboard.dataView.dataView.noDataFiles": "未找到数据文件",
|
||||
"dashboard.dataView.dataView.getStarted.link": "阅读更多以开始使用数据文件",
|
||||
"dashboard.dataView.dataView.update.message": "已更新您的数据条目",
|
||||
"dashboard.dataView.dataView.createNew": "创建新数据文件",
|
||||
"dashboard.dataView.dataView.selectDataFolder": "选择数据文件夹",
|
||||
"dashboard.dataView.dataView.closeSelectedDataFile": "关闭数据文件",
|
||||
|
||||
"dashboard.dataView.emptyView.heading": "请先选择您的数据类型",
|
||||
"dashboard.dataView.emptyView.heading.create": "通过创建新的数据文件开始",
|
||||
|
||||
"dashboard.dataView.sortableItem.editButton.title": "编辑 \"{0}\"",
|
||||
"dashboard.dataView.sortableItem.deleteButton.title": "删除 \"{0}\"",
|
||||
"dashboard.dataView.sortableItem.alert.title": "删除数据条目",
|
||||
"dashboard.dataView.sortableItem.alert.description": "您确定要删除该数据条目吗?",
|
||||
|
||||
"dashboard.errorView.description": "请关闭仪表盘并重试。",
|
||||
|
||||
"dashboard.filters.languageFilter.label": "语言",
|
||||
"dashboard.filters.languageFilter.all": "全部",
|
||||
|
||||
"dashboard.header.actionsBar.itemsSelected": "已选 {0} 项",
|
||||
"dashboard.header.actionsBar.selectAll": "全选",
|
||||
"dashboard.header.actionsBar.alertDelete.title": "删除所选文件",
|
||||
"dashboard.header.actionsBar.alertDelete.description": "您确定要删除所选文件吗?",
|
||||
|
||||
"dashboard.header.breadcrumb.home": "首页",
|
||||
|
||||
"dashboard.header.clearFilters.title": "清除筛选、分组和排序",
|
||||
|
||||
"dashboard.header.filter.default": "无筛选条件",
|
||||
|
||||
"dashboard.header.folders.default": "所有类型",
|
||||
"dashboard.header.folders.menuButton.showing": "显示",
|
||||
|
||||
"dashboard.header.grouping.option.none": "无",
|
||||
"dashboard.header.grouping.option.year": "年份",
|
||||
"dashboard.header.grouping.option.draft": "草稿/已发布",
|
||||
"dashboard.header.grouping.menuButton.label": "分组方式",
|
||||
|
||||
"dashboard.header.navigation.allArticles": "所有文章",
|
||||
"dashboard.header.navigation.published": "已发布",
|
||||
"dashboard.header.navigation.scheduled": "已排期",
|
||||
"dashboard.header.navigation.draft": "草稿中",
|
||||
|
||||
"dashboard.header.header.createContent": "创建内容",
|
||||
"dashboard.header.header.createByContentType": "按内容类型创建",
|
||||
"dashboard.header.header.createByTemplate": "按模板创建",
|
||||
|
||||
"dashboard.header.pagination.first": "首页",
|
||||
"dashboard.header.pagination.previous": "上一页",
|
||||
"dashboard.header.pagination.next": "下一页",
|
||||
"dashboard.header.pagination.last": "末页",
|
||||
|
||||
"dashboard.header.paginationStatus.text": "显示第 {0} 到 {1} 条,共 {2} 条结果",
|
||||
|
||||
"dashboard.header.projectSwitcher.label": "项目",
|
||||
|
||||
"dashboard.header.refreshDashboard.label": "刷新仪表盘",
|
||||
|
||||
"dashboard.header.sorting.lastModified.asc": "最后修改时间 (升序)",
|
||||
"dashboard.header.sorting.lastModified.desc": "最后修改时间 (降序)",
|
||||
"dashboard.header.sorting.filename.asc": "按文件名 (升序)",
|
||||
"dashboard.header.sorting.filename.desc": "按文件名 (降序)",
|
||||
"dashboard.header.sorting.published.asc": "发布日期 (升序)",
|
||||
"dashboard.header.sorting.published.desc": "发布日期 (降序)",
|
||||
"dashboard.header.sorting.size.asc": "大小 (升序)",
|
||||
"dashboard.header.sorting.size.desc": "大小 (降序)",
|
||||
"dashboard.header.sorting.caption.asc": "说明文字 (升序)",
|
||||
"dashboard.header.sorting.caption.desc": "说明文字 (降序)",
|
||||
"dashboard.header.sorting.alt.asc": "替代文本 (升序)",
|
||||
"dashboard.header.sorting.alt.desc": "替代文本 (降序)",
|
||||
"dashboard.header.sorting.label": "排序方式",
|
||||
|
||||
"dashboard.header.startup.label": "启动时打开?",
|
||||
|
||||
"dashboard.header.tabs.contents": "内容",
|
||||
"dashboard.header.tabs.media": "媒体",
|
||||
"dashboard.header.tabs.snippets": "片段",
|
||||
"dashboard.header.tabs.data": "数据",
|
||||
"dashboard.header.tabs.taxonomies": "分类法",
|
||||
|
||||
"dashboard.header.viewSwitch.toGrid": "切换到网格视图",
|
||||
"dashboard.header.viewSwitch.toList": "切换到列表视图",
|
||||
"dashboard.header.viewSwitch.toStructure": "切换到结构视图",
|
||||
|
||||
"dashboard.layout.sponsor.support.msg": "支持 Front Matter",
|
||||
"dashboard.layout.sponsor.review.label": "评价",
|
||||
"dashboard.layout.sponsor.review.msg": "评价 Front Matter",
|
||||
|
||||
"dashboard.media.common.title": "标题",
|
||||
"dashboard.media.common.caption": "说明文字",
|
||||
"dashboard.media.common.alt": "替代文本",
|
||||
"dashboard.media.common.size": "大小",
|
||||
|
||||
"dashboard.media.dialog.title": "查看详情",
|
||||
"dashboard.media.panel.close": "关闭面板",
|
||||
"dashboard.media.metadata.panel.title": "更新元数据",
|
||||
"dashboard.media.metadata.panel.description": "请指定您要为文件设置的元数据。",
|
||||
"dashboard.media.metadata.panel.field.fileName": "文件名",
|
||||
"dashboard.media.metadata.panel.form.metadata.title": "元数据",
|
||||
"dashboard.media.metadata.panel.form.information.title": "信息",
|
||||
"dashboard.media.metadata.panel.form.information.createdDate": "创建时间",
|
||||
"dashboard.media.metadata.panel.form.information.modifiedDate": "最后修改时间",
|
||||
"dashboard.media.metadata.panel.form.information.dimensions": "尺寸",
|
||||
"dashboard.media.metadata.panel.form.information.folder": "文件夹",
|
||||
|
||||
"dashboard.media.folderCreation.hexo.create": "创建文章资源文件夹",
|
||||
"dashboard.media.folderCreation.folder.create": "创建新文件夹",
|
||||
|
||||
"dashboard.media.folderItem.contentDirectory": "内容目录",
|
||||
"dashboard.media.folderItem.publicDirectory": "公共目录",
|
||||
"dashboard.media.folderItem.deleteDescription": "您确定要删除该文件夹 ({0}) 吗?",
|
||||
|
||||
"dashboard.media.item.buttom.insert.image": "插入图片",
|
||||
"dashboard.media.item.buttom.insert.snippet": "插入片段",
|
||||
|
||||
"dashboard.media.item.quickAction.insert.field": "为您的 \"{0}\" 字段插入图片",
|
||||
"dashboard.media.item.quickAction.insert.markdown": "使用 Markdown 标记插入图片",
|
||||
"dashboard.media.item.quickAction.copy.path": "复制媒体路径",
|
||||
"dashboard.media.item.quickAction.delete": "删除媒体文件",
|
||||
"dashboard.media.item.menuItem.view": "查看媒体详情",
|
||||
"dashboard.media.item.menuItem.edit.metadata": "编辑元数据",
|
||||
"dashboard.media.item.menuItem.insert.image": "插入图片",
|
||||
"dashboard.media.item.menuItem.reveal.media": "显示媒体位置",
|
||||
"dashboard.media.item.infoDialog.snippet.description": "选择要用于当前媒体文件的媒体片段。",
|
||||
"dashboard.media.item.alert.delete.description": "您确定要从 {0} 文件夹中删除该文件吗?",
|
||||
|
||||
"dashboard.media.media.description": "选择要添加到您内容中的媒体文件。",
|
||||
"dashboard.media.media.dragAndDrop": "您也可以从桌面拖放图片,上传后选择它们。",
|
||||
"dashboard.media.media.folder.upload": "上传到 {0}",
|
||||
"dashboard.media.media.folder.default": "未选择文件夹,您拖放的文件将被添加到 {0} 文件夹",
|
||||
"dashboard.media.media.placeholder": "无媒体文件可显示。按住 [Shift] 键可以拖放新文件。",
|
||||
"dashboard.media.media.contentFolder": "内容文件夹",
|
||||
"dashboard.media.media.publicFolder": "公共文件夹",
|
||||
|
||||
"dashboard.media.mediaHeaderTop.searchbox.placeholder": "在文件夹中搜索",
|
||||
|
||||
"dashboard.media.mediaSnippetForm.formDialog.title": "插入媒体:{0}",
|
||||
"dashboard.media.mediaSnippetForm.formDialog.description": "将 {0} 媒体文件插入当前文章",
|
||||
|
||||
"dashboard.preview.input.placeholder": "输入 URL",
|
||||
"dashboard.preview.button.navigate.title": "导航",
|
||||
"dashboard.preview.button.refresh.title": "刷新",
|
||||
"dashboard.preview.button.open.title": "打开",
|
||||
|
||||
"dashboard.snippetsView.item.type.content": "内容片段",
|
||||
"dashboard.snippetsView.item.type.media": "媒体片段",
|
||||
"dashboard.snippetsView.item.quickAction.editSnippet": "编辑片段",
|
||||
"dashboard.snippetsView.item.quickAction.deleteSnippet": "删除片段",
|
||||
"dashboard.snippetsView.item.quickAction.viewSnippet": "查看片段文件",
|
||||
"dashboard.snippetsView.item.insert.formDialog.title": "插入片段:{0}",
|
||||
"dashboard.snippetsView.item.insert.formDialog.description": "将 {0} 片段插入当前文章",
|
||||
"dashboard.snippetsView.item.edit.formDialog.title": "编辑片段:{0}",
|
||||
"dashboard.snippetsView.item.edit.formDialog.description": "编辑 {0} 片段",
|
||||
"dashboard.snippetsView.item.alert.title": "删除片段:{0}",
|
||||
"dashboard.snippetsView.item.alert.description": "您确定要删除 {0} 片段吗?",
|
||||
|
||||
"dashboard.snippetsView.newForm.snippetInput.title.placeholder": "片段标题",
|
||||
"dashboard.snippetsView.newForm.snippetInput.description.label": "描述",
|
||||
"dashboard.snippetsView.newForm.snippetInput.description.placeholder": "片段描述",
|
||||
"dashboard.snippetsView.newForm.snippetInput.snippet.label": "片段",
|
||||
"dashboard.snippetsView.newForm.snippetInput.snippet.placeholder": "片段内容",
|
||||
"dashboard.snippetsView.newForm.snippetInput.isMediaSnippet.label": "是媒体片段吗?",
|
||||
"dashboard.snippetsView.newForm.snippetInput.isMediaSnippet.checkbox.label": "媒体片段",
|
||||
"dashboard.snippetsView.newForm.snippetInput.isMediaSnippet.checkbox.description": "使用当前片段将媒体文件插入您的内容中。",
|
||||
"dashboard.snippetsView.newForm.snippetInput.docsButton.title": "阅读更多关于使用媒体片段占位符的信息",
|
||||
"dashboard.snippetsView.newForm.snippetInput.docsButton.description": "查看我们的媒体片段占位符文档,了解可以使用的占位符。",
|
||||
|
||||
"dashboard.snippetsView.snippets.ariaLabel": "片段标题",
|
||||
"dashboard.snippetsView.snippets.button.create": "创建新片段",
|
||||
"dashboard.snippetsView.snippets.select.description": "选择要添加到您内容中的片段。",
|
||||
"dashboard.snippetsView.snippets.empty.message": "未找到片段",
|
||||
"dashboard.snippetsView.snippets.readMore": "阅读更多以开始使用片段",
|
||||
"dashboard.snippetsView.snippets.formDialog.title": "创建片段",
|
||||
|
||||
"dashboard.steps.stepsToGetStarted.button.addFolder.title": "添加为 Front Matter 的内容文件夹",
|
||||
"dashboard.steps.stepsToGetStarted.initializeProject.name": "初始化项目",
|
||||
"dashboard.steps.stepsToGetStarted.initializeProject.description": "初始化项目将创建使用 Front Matter CMS 所需的文件和文件夹。点击此操作开始。",
|
||||
"dashboard.steps.stepsToGetStarted.framework.name": "框架预设",
|
||||
"dashboard.steps.stepsToGetStarted.framework.description": "选择您的站点生成器或框架以预填充一些推荐设置。",
|
||||
"dashboard.steps.stepsToGetStarted.framework.select": "选择您的框架",
|
||||
"dashboard.steps.stepsToGetStarted.framework.select.other": "其他",
|
||||
"dashboard.steps.stepsToGetStarted.assetsFolder.name": "您的资源文件夹是什么?",
|
||||
"dashboard.steps.stepsToGetStarted.assetsFolder.description": "选择包含您资源的文件夹。此文件夹将用于存储您文章的所有媒体文件。",
|
||||
"dashboard.steps.stepsToGetStarted.assetsFolder.public.title": "使用 'public' 文件夹",
|
||||
"dashboard.steps.stepsToGetStarted.assetsFolder.assets.title": "使用 Astro 资源文件夹 (src/assets)",
|
||||
"dashboard.steps.stepsToGetStarted.assetsFolder.other.description": "如果您想配置其他文件夹,可以手动在 frontmatter.json 文件中进行设置。",
|
||||
"dashboard.steps.stepsToGetStarted.contentFolders.name": "注册内容文件夹",
|
||||
"dashboard.steps.stepsToGetStarted.contentFolders.description": "将我们在您项目中找到的一个文件夹添加为内容文件夹。设置文件夹后,Front Matter 可以列出所有内容并允许您创建内容。",
|
||||
"dashboard.steps.stepsToGetStarted.contentFolders.label": "包含内容的文件夹:",
|
||||
"dashboard.steps.stepsToGetStarted.contentFolders.information.description": "您也可以通过右键单击资源管理器视图中的文件夹并选择“注册文件夹”来执行此操作。",
|
||||
"dashboard.steps.stepsToGetStarted.tags.name": "导入所有标签和分类(可选)",
|
||||
"dashboard.steps.stepsToGetStarted.tags.description": "现在 Front Matter 知道所有内容文件夹。您是否想从可用内容中导入所有标签和分类?",
|
||||
"dashboard.steps.stepsToGetStarted.git.name": "您要启用 Git 同步吗?",
|
||||
"dashboard.steps.stepsToGetStarted.git.description": "启用 Git 同步以轻松将更改同步到您的仓库。",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.name": "显示仪表盘",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.description": "所有操作完成后,即可加载仪表盘。",
|
||||
"dashboard.steps.stepsToGetStarted.template.name": "使用配置模板",
|
||||
"dashboard.steps.stepsToGetStarted.template.description": "选择一个模板,用推荐设置预填充 frontmatter.json 文件。",
|
||||
"dashboard.steps.stepsToGetStarted.template.warning": "选择模板会将整个配置应用到您的项目,并关闭此配置视图。",
|
||||
"dashboard.steps.stepsToGetStarted.astroContentTypes.name": "为您的 Astro 内容集合创建内容类型",
|
||||
|
||||
"dashboard.taxonomyView.button.add.title": "将 {0} 添加到分类法设置",
|
||||
"dashboard.taxonomyView.button.tag.title": "标记内容",
|
||||
"dashboard.taxonomyView.button.edit.title": "编辑 {0}",
|
||||
"dashboard.taxonomyView.button.merge.title": "合并 {0}",
|
||||
"dashboard.taxonomyView.button.move.title": "移动到其他分类法类型",
|
||||
"dashboard.taxonomyView.button.delete.title": "删除 {0}",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyLookup.button.title": "显示包含 {1} 中 {0} 的内容",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyManager.description": "创建、编辑和管理您网站的 {0}",
|
||||
"dashboard.taxonomyView.taxonomyManager.button.create": "创建新的 {0} 值",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.heading.name": "名称",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.heading.count": "数量",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.heading.action": "操作",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.row.empty": "未找到 {0}",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.unmapped.title": "在您的设置中缺失",
|
||||
"dashboard.taxonomyView.taxonomyManager.filterInput.placeholder": "筛选",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyTagging.pageTitle": "将您的内容映射到:{0}",
|
||||
"dashboard.taxonomyView.taxonomyTagging.checkbox": "使用 {0} 标记页面",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyView.navigationBar.title": "选择分类法",
|
||||
"dashboard.taxonomyView.taxonomyView.button.import": "导入分类法",
|
||||
"dashboard.taxonomyView.taxonomyView.navigationItem.tags": "标签",
|
||||
"dashboard.taxonomyView.taxonomyView.navigationItem.categories": "分类",
|
||||
|
||||
"dashboard.unkownView.title": "视图不存在",
|
||||
"dashboard.unkownView.description": "您似乎进入了一个不存在的视图。请重新打开仪表盘。",
|
||||
|
||||
"dashboard.welcomeScreen.title": "使用 Front Matter 管理您的静态站点",
|
||||
"dashboard.welcomeScreen.thanks": "感谢您使用 Front Matter!",
|
||||
"dashboard.welcomeScreen.description": "我们致力于让 Front Matter 尽可能易于使用。如果您有任何问题或建议,请在 GitHub 上联系我们。",
|
||||
"dashboard.welcomeScreen.link.github.title": "GitHub",
|
||||
"dashboard.welcomeScreen.link.github.label": "GitHub",
|
||||
"dashboard.welcomeScreen.link.documentation.label": "文档",
|
||||
"dashboard.welcomeScreen.link.sponsor.title": "成为赞助商",
|
||||
"dashboard.welcomeScreen.link.sponsor.label": "赞助",
|
||||
"dashboard.welcomeScreen.link.review.title": "写评价",
|
||||
"dashboard.welcomeScreen.link.review.label": "评价",
|
||||
"dashboard.welcomeScreen.actions.heading": "执行以下步骤以开始使用该扩展",
|
||||
"dashboard.welcomeScreen.actions.description": "您也可以从 Front Matter 侧边栏使用该扩展。在那里您将找到可以专门为您的页面执行的操作。",
|
||||
"dashboard.welcomeScreen.actions.thanks": "我们希望您喜欢 Front Matter!",
|
||||
|
||||
"dashboard.media.detailsSlideOver.unmapped.description": "您是否要重新映射未映射文件的元数据?",
|
||||
|
||||
"dashboard.configuration.astro.astroContentTypes.empty": "未找到 Astro 内容集合。",
|
||||
"dashboard.configuration.astro.astroContentTypes.description": "以下 Astro 内容集合可用于生成内容类型。",
|
||||
|
||||
"panel.git.gitAction.title": "发布更改",
|
||||
"panel.git.gitAction.branch.select": "选择分支",
|
||||
"panel.git.gitAction.input.placeholder": "提交消息",
|
||||
"panel.git.gitAction.button.fetch": "获取",
|
||||
|
||||
"panel.contentType.contentTypeValidator.title": "内容类型",
|
||||
"panel.contentType.contentTypeValidator.hint": "我们注意到内容类型和 front matter 数据之间存在字段差异。\n 您想为此内容创建、更新或设置内容类型吗?",
|
||||
"panel.contentType.contentTypeValidator.button.create": "创建内容类型",
|
||||
"panel.contentType.contentTypeValidator.button.add": "将缺失字段添加到内容类型",
|
||||
"panel.contentType.contentTypeValidator.button.change": "更改文件的内容类型",
|
||||
|
||||
"panel.dataBlock.dataBlockField.group.selected.edit": "正在编辑:{0}",
|
||||
"panel.dataBlock.dataBlockField.group.selected.create": "创建新的 {0}",
|
||||
"panel.dataBlock.dataBlockField.group.select": "选择一个分组",
|
||||
"panel.dataBlock.dataBlockField.add": "添加 {0}",
|
||||
|
||||
"panel.dataBlock.dataBlockRecord.edit": "编辑记录",
|
||||
"panel.dataBlock.dataBlockRecord.delete": "删除记录",
|
||||
|
||||
"panel.dataBlock.dataBlockRecords.label": "记录",
|
||||
|
||||
"panel.dataBlock.dataBlockSelector.label": "区块类型",
|
||||
|
||||
"panel.errorBoundary.fieldBoundary.label": "查看字段失败",
|
||||
|
||||
"panel.fields.choiceField.select": "选择 {0}",
|
||||
"panel.fields.choiceField.clear": "清除值",
|
||||
|
||||
"panel.fields.contentTypeRelationshipField.loading": "正在获取可能的值...",
|
||||
|
||||
"panel.fields.dateTimeField.button.pick": "选择日期",
|
||||
"panel.fields.dateTimeField.time": "时间:",
|
||||
|
||||
"panel.fields.fieldMessage.required": "{0} 字段是必填的",
|
||||
|
||||
"panel.fields.fileField.delete": "删除文件",
|
||||
"panel.fields.fileField.add": "添加您的 {0}",
|
||||
|
||||
"panel.fields.imageFallback.label": "图片加载失败",
|
||||
|
||||
"panel.fields.listField.edit": "编辑记录",
|
||||
"panel.fields.listField.delete": "删除记录",
|
||||
|
||||
"panel.fields.previewImage.remove": "移除图片",
|
||||
|
||||
"panel.fields.previewImageField.add": "添加您的 {0}",
|
||||
|
||||
"panel.fields.slugField.update": "有更新可用",
|
||||
"panel.fields.slugField.generate": "生成别名",
|
||||
|
||||
"panel.fields.textField.ai.message": "使用 Front Matter AI 建议 {0}",
|
||||
"panel.fields.textField.copilot.message": "使用 Copilot 建议 {0}",
|
||||
"panel.fields.textField.ai.generate": "正在生成建议...",
|
||||
"panel.fields.textField.loading": "正在加载字段",
|
||||
"panel.fields.textField.limit": "字段限制已达 {0}",
|
||||
|
||||
"panel.fields.wrapperField.unknown": "未知字段类型:{0}",
|
||||
|
||||
"panel.fields.fieldCustomAction.button.title": "自定义操作",
|
||||
"panel.fields.fieldCustomAction.executing": "正在执行字段操作...",
|
||||
|
||||
"panel.actions.title": "操作",
|
||||
|
||||
"panel.articleDetails.headings": "标题",
|
||||
"panel.articleDetails.paragraphs": "段落",
|
||||
"panel.articleDetails.internalLinks": "内部链接",
|
||||
"panel.articleDetails.externalLinks": "外部链接",
|
||||
"panel.articleDetails.images": "图片",
|
||||
|
||||
"panel.baseView.initialize": "初始化项目",
|
||||
"panel.baseView.actions.title": "操作",
|
||||
"panel.baseView.action.openDashboard": "打开仪表盘",
|
||||
"panel.baseView.action.createContent": "创建内容",
|
||||
"panel.baseView.empty": "打开文件以查看更多操作",
|
||||
|
||||
"panel.fileList.label.singular": "个文件",
|
||||
"panel.fileList.label.plural": "个文件",
|
||||
|
||||
"panel.folderAndFiles.title": "最近修改",
|
||||
|
||||
"panel.globalSettings.title": "全局设置",
|
||||
"panel.globalSettings.action.modifiedDate.label": "修改日期",
|
||||
"panel.globalSettings.action.modifiedDate.description": "自动更新修改日期",
|
||||
"panel.globalSettings.action.frontMatter.label": "Front Matter 高亮",
|
||||
"panel.globalSettings.action.frontMatter.description": "高亮显示 Front Matter",
|
||||
"panel.globalSettings.action.preview.label": "本地预览",
|
||||
"panel.globalSettings.action.preview.placeholder": "例如:{0}",
|
||||
"panel.globalSettings.action.server.label": "本地服务器命令",
|
||||
"panel.globalSettings.action.server.placeholder": "例如:{0}",
|
||||
|
||||
"panel.metadata.title": "元数据",
|
||||
"panel.metadata.focusProblems": "查看问题视图获取更多信息",
|
||||
|
||||
"panel.otherActions.title": "其他操作",
|
||||
"panel.otherActions.writingSettings.enabled": "写作设置已启用",
|
||||
"panel.otherActions.writingSettings.disabled": "启用写作设置",
|
||||
"panel.otherActions.centerMode": "切换居中模式",
|
||||
"panel.otherActions.createTemplate": "创建模板",
|
||||
"panel.otherActions.revealFile": "在文件夹中显示文件",
|
||||
"panel.otherActions.openProject": "显示项目文件夹",
|
||||
"panel.otherActions.documentation": "打开文档",
|
||||
"panel.otherActions.settings": "设置概览",
|
||||
"panel.otherActions.issue": "报告问题",
|
||||
|
||||
"panel.preview.title": "打开预览",
|
||||
|
||||
"panel.publishAction.publish": "发布",
|
||||
"panel.publishAction.unpublish": "恢复为草稿",
|
||||
|
||||
"panel.seoDetails.recommended": "推荐",
|
||||
|
||||
"panel.seoKeywords.checks": "检查项",
|
||||
"panel.seoKeywords.density.tableTitle": "频率",
|
||||
"panel.seoKeywords.density": "关键词密度",
|
||||
"panel.seoKeywordInfo.validInfo.label": "标题",
|
||||
"panel.seoKeywordInfo.validInfo.content": "内容",
|
||||
"panel.seoKeywordInfo.density.tooltip": "推荐频率:0.75% - 1.5%",
|
||||
|
||||
"panel.seoKeywords.title": "关键词",
|
||||
"panel.seoKeywords.header.keyword": "关键词",
|
||||
"panel.seoKeywords.header.details": "详情",
|
||||
"panel.seoKeywords.density.description": "* 在大多数情况下,1-1.5% 的关键词密度就足够了。",
|
||||
|
||||
"panel.seoStatus.title": "洞察",
|
||||
"panel.seoStatus.header.property": "属性",
|
||||
"panel.seoStatus.header.valid": "有效",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} 个字符",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} 个单词",
|
||||
"panel.seoStatus.seoFieldInfo.article": "文章长度",
|
||||
"panel.seoStatus.collapsible.title": "SEO 状态",
|
||||
"panel.seoStatus.required": "{0} 或 {1} 是必需的。",
|
||||
|
||||
"panel.slugAction.title": "优化别名",
|
||||
|
||||
"panel.spinner.loading": "加载中...",
|
||||
|
||||
"panel.startServerbutton.start": "启动服务器",
|
||||
"panel.startServerbutton.stop": "停止服务器",
|
||||
|
||||
"panel.tag.add": "将 {0} 添加到您的设置中",
|
||||
|
||||
"panel.tagPicker.inputPlaceholder.empty": "选择您的 {0}",
|
||||
"panel.tagPicker.inputPlaceholder.disabled": "您已达到 {0} 的限制",
|
||||
"panel.tagPicker.ai.suggest": "使用 Front Matter AI 建议 {0}",
|
||||
"panel.tagPicker.copilot.suggest": "使用 GitHub Copilot 建议 {0}",
|
||||
"panel.tagPicker.ai.generating": "正在生成建议...",
|
||||
"panel.tagPicker.limit": "最大:{0}",
|
||||
"panel.tagPicker.unkown": "添加未知标签",
|
||||
|
||||
"panel.tags.tag.warning": "请注意,标签 \"{0}\" 未保存在您的设置中。一旦移除,它将永久消失。",
|
||||
|
||||
"panel.viewPanel.mediaInsert": "继续在媒体仪表盘中选择您想要插入的图片。",
|
||||
|
||||
"commands.article.setDate.error": "解析日期格式时出错。请检查您的 \"{0}\" 设置。",
|
||||
"commands.article.updateSlug.error": "重命名文件失败:{0}",
|
||||
"commands.article.rename.fileNotExists.error": "文件不存在",
|
||||
"commands.article.rename.fileExists.error": "名为 \"{0}\" 的文件已存在",
|
||||
"commands.article.rename.fileName.title": "重命名:{0}",
|
||||
"commands.article.rename.fileName.prompt": "文件名",
|
||||
|
||||
"commands.cache.cleared": "缓存已清除",
|
||||
|
||||
"commands.chatbot.title": "问我任何问题",
|
||||
|
||||
"commands.content.option.contentType.label": "按内容类型创建内容",
|
||||
"commands.content.option.contentType.description": "选择是否要根据可用的内容类型创建新内容",
|
||||
"commands.content.option.template.label": "按模板创建内容",
|
||||
"commands.content.option.template.description": "选择是否要根据可用的模板创建新内容",
|
||||
"commands.content.quickPick.title": "创建内容",
|
||||
"commands.content.quickPick.placeholder": "选择您想要创建新内容的方式",
|
||||
|
||||
"commands.dashboard.title": "仪表盘",
|
||||
|
||||
"commands.folders.addMediaFolder.inputBox.title": "添加媒体文件夹",
|
||||
"commands.folders.addMediaFolder.inputBox.prompt": "您想给您的文件夹起什么名字(使用 \"/\" 创建多级文件夹)?",
|
||||
"commands.folders.addMediaFolder.noFolder.warning": "未指定文件夹名称。",
|
||||
"commands.folders.create.folderExists.warning": "文件夹已注册",
|
||||
"commands.folders.create.input.title": "注册文件夹",
|
||||
"commands.folders.create.input.prompt": "您想为此文件夹指定什么名称?",
|
||||
"commands.folders.create.input.placeholder": "文件夹名称",
|
||||
"commands.folders.create.success": "文件夹已注册",
|
||||
"commands.folders.getWorkspaceFolder.workspaceFolderPick.placeholder": "请选择 Front Matter 要使用的主工作区文件夹。",
|
||||
"commands.folders.get.notificationError.title": "文件夹 \"{0}\" 不存在。请从设置中移除它。",
|
||||
"commands.folders.get.notificationError.remove.action": "移除文件夹",
|
||||
"commands.folders.get.notificationError.create.action": "创建文件夹",
|
||||
|
||||
"commands.i18n.create.warning.noFileSelected": "未选择文件。",
|
||||
"commands.i18n.create.warning.noFile": "无法检索到文件。",
|
||||
"commands.i18n.create.warning.noContentType": "无法检索当前文件的内容类型。",
|
||||
"commands.i18n.create.warning.noConfig": "未找到 i18n 配置。",
|
||||
"commands.i18n.create.error.noLocaleDefinition": "无法检索当前文件的区域设置。",
|
||||
"commands.i18n.create.error.noLocales": "当前文件已翻译为所有可用语言。",
|
||||
"commands.i18n.create.error.noContentFolder": "无法为当前文件定义内容文件夹。",
|
||||
"commands.i18n.create.error.fileExists": "i18n 翻译已存在。",
|
||||
"commands.i18n.create.success.created": "已创建 \"{0}\" i18n 内容文件。",
|
||||
"commands.i18n.create.quickPick.title": "为区域创建内容",
|
||||
"commands.i18n.create.quickPick.placeHolder": "您想为哪个区域创建新内容?",
|
||||
"commands.i18n.createOrOpen.quickPick.title": "打开或创建翻译",
|
||||
"commands.i18n.createOrOpen.quickPick.category.existing": "现有翻译",
|
||||
"commands.i18n.createOrOpen.quickPick.action.open": "打开 \"{0}\"",
|
||||
"commands.i18n.createOrOpen.quickPick.category.new": "新翻译",
|
||||
"commands.i18n.createOrOpen.quickPick.action.create": "创建 \"{0}\"",
|
||||
"commands.i18n.translate.progress.title": "正在翻译内容...",
|
||||
|
||||
"commands.preview.panel.title": "预览:{0}",
|
||||
"commands.preview.askUserToPickFolder.title": "选择要预览的文章所在文件夹",
|
||||
|
||||
"commands.project.initialize.success": "项目初始化成功。",
|
||||
"commands.project.switchProject.title": "您想切换到哪个项目?",
|
||||
"commands.project.createSampleTemplate.info": "示例模板已创建。",
|
||||
|
||||
"commands.settings.create.input.prompt": "插入您要添加到配置中的 {0} 的值。",
|
||||
"commands.settings.create.input.placeholder": "{0} 的名称",
|
||||
"commands.settings.create.warning": "提供的 {0} 已存在。",
|
||||
"commands.settings.create.quickPick.placeholder": "您要将新的 {0} 添加到页面吗?",
|
||||
"commands.settings.export.progress.title": "{0}: 正在导出标签和分类",
|
||||
"commands.settings.export.progress.success": "导出完成。标签:{0} - 分类:{1}。",
|
||||
"commands.settings.remap.quickpick.title": "重新映射",
|
||||
"commands.settings.remap.quickpick.placeholder": "您想重新映射什么?",
|
||||
"commands.settings.remap.noTaxonomy.warning": "未配置 {0}。",
|
||||
"commands.settings.remap.selectTaxonomy.placeholder": "选择要插入的 {0}。",
|
||||
"commands.settings.remap.newOption.input.prompt": "指定您想用哪个 {0} 的值来重新映射 \"{1}\"。如果要从所有文章中移除该 {0},请将输入留空。",
|
||||
"commands.settings.remap.newOption.input.placeholder": "{0} 的名称",
|
||||
"commands.settings.remap.delete.placeholder": "删除 {0} {1}?",
|
||||
|
||||
"commands.statusListener.verifyRequiredFields.diagnostic.emptyField": "{0} 字段是必填的。请为该字段定义一个值。",
|
||||
"commands.statusListener.verifyRequiredFields.notification.error": "以下字段必须包含值:{0}",
|
||||
|
||||
"commands.template.generate.input.title": "模板标题",
|
||||
"commands.template.generate.input.prompt": "您想给模板起什么名字?",
|
||||
"commands.template.generate.input.placeholder": "文章",
|
||||
"commands.template.generate.noTitle.warning": "您未指定模板标题。",
|
||||
"commands.template.generate.keepContents.title": "保留内容",
|
||||
"commands.template.generate.keepContents.placeholder": "您想保留模板的内容吗?",
|
||||
"commands.template.generate.keepContents.noOption.warning": "您没有选择保留模板内容的任何选项。",
|
||||
"commands.template.generate.keepContents.success": "模板已创建,现在可在您的 {0} 文件夹中使用。",
|
||||
"commands.template.getTemplates.warning": "未找到模板。",
|
||||
"commands.template.create.folderPath.warning": "检索到的项目文件夹路径不正确。",
|
||||
"commands.template.create.noTemplates.warning": "未找到模板。",
|
||||
"commands.template.create.selectTemplate.title": "选择一个模板",
|
||||
"commands.template.create.selectTemplate.placeholder": "选择要使用的内容模板",
|
||||
"commands.template.create.selectTemplate.noTemplate.warning": "未选择模板。",
|
||||
"commands.template.create.selectTemplate.notFound.warning": "找不到内容模板。",
|
||||
"commands.template.create.success": "您的新内容现已可用。",
|
||||
|
||||
"commands.wysiwyg.command.unorderedList.label": "无序列表",
|
||||
"commands.wysiwyg.command.unorderedList.detail": "添加无序列表",
|
||||
"commands.wysiwyg.command.orderedList.label": "有序列表",
|
||||
"commands.wysiwyg.command.orderedList.detail": "添加有序列表",
|
||||
"commands.wysiwyg.command.taskList.label": "任务列表",
|
||||
"commands.wysiwyg.command.taskList.detail": "添加任务列表",
|
||||
"commands.wysiwyg.command.code.label": "代码",
|
||||
"commands.wysiwyg.command.code.detail": "添加内联代码片段",
|
||||
"commands.wysiwyg.command.codeblock.label": "代码块",
|
||||
"commands.wysiwyg.command.codeblock.detail": "添加代码块",
|
||||
"commands.wysiwyg.command.blockquote.label": "引用",
|
||||
"commands.wysiwyg.command.blockquote.detail": "添加引用块",
|
||||
"commands.wysiwyg.command.strikethrough.label": "删除线",
|
||||
"commands.wysiwyg.command.strikethrough.detail": "添加删除线文本",
|
||||
"commands.wysiwyg.quickPick.title": "WYSIWYG 选项",
|
||||
"commands.wysiwyg.quickPick.placeholder": "您想插入哪种类型的标记?",
|
||||
"commands.wysiwyg.addHyperlink.hyperlinkInput.title": "WYSIWYG 超链接",
|
||||
"commands.wysiwyg.addHyperlink.hyperlinkInput.prompt": "输入 URL",
|
||||
"commands.wysiwyg.addHyperlink.textInput.title": "WYSIWYG 文本",
|
||||
"commands.wysiwyg.addHyperlink.textInput.prompt": "输入超链接的文本",
|
||||
"commands.wysiwyg.insertText.heading.input.title": "标题级别",
|
||||
"commands.wysiwyg.insertText.heading.input.placeholder": "您想插入哪个标题级别?",
|
||||
|
||||
"helpers.articleHelper.createContent.pageBundle.error": "名为 {0} 的页面捆绑包已存在于 {1} 中。",
|
||||
"helpers.articleHelper.createContent.contentExists.warning": "标题的内容已存在。请指定一个新标题。",
|
||||
"helpers.articleHelper.processCustomPlaceholders.placeholder.error": "处理 {0} 占位符时出错。",
|
||||
"helpers.articleHelper.parseFile.diagnostic.error": "解析 {0} 的 front matter 时出错。",
|
||||
|
||||
"helpers.contentType.generate.noFrontMatter.error": "未找到 front matter 数据来生成内容类型。",
|
||||
"helpers.contentType.generate.override.quickPick.title": "覆盖默认内容类型",
|
||||
"helpers.contentType.generate.override.quickPick.placeholder": "您是否想用当前字段中使用的字段覆盖默认内容类型配置?",
|
||||
"helpers.contentType.generate.contentTypeInput.title": "生成内容类型",
|
||||
"helpers.contentType.generate.contentTypeInput.prompt": "输入要生成的内容类型的名称",
|
||||
"helpers.contentType.generate.contentTypeInput.validation.enterName": "请输入内容类型的名称。",
|
||||
"helpers.contentType.generate.contentTypeInput.validation.nameExists": "已存在具有此名称的内容类型。",
|
||||
"helpers.contentType.generate.noContentTypeName.warning": "您未指定内容类型的名称。",
|
||||
"helpers.contentType.generate.pageBundle.quickPick.title": "用作页面捆绑包",
|
||||
"helpers.contentType.generate.pageBundle.quickPick.placeHolder": "您想将此内容类型用作页面捆绑包吗?",
|
||||
"helpers.contentType.generate.updated.success": "内容类型 {0} 已更新。",
|
||||
"helpers.contentType.generate.generated.success": "内容类型 {0} 已生成。",
|
||||
"helpers.contentType.addMissingFields.noFrontMatter.warning": "未找到 front matter 数据来添加缺失字段。",
|
||||
"helpers.contentType.addMissingFields.updated.success": "内容类型 {0} 已更新。",
|
||||
"helpers.contentType.setContentType.noFrontMatter.warning": "未找到 front matter 数据来设置内容类型。",
|
||||
"helpers.contentType.setContentType.quickPick.title": "选择内容类型",
|
||||
"helpers.contentType.setContentType.quickPick.placeholder": "您想使用哪个内容类型?",
|
||||
"helpers.contentType.create.allowSubContent.title": "您想将其创建为子内容吗?",
|
||||
"helpers.contentType.create.allowSubContent.placeHolder": "您想将其创建为子内容吗?",
|
||||
"helpers.contentType.create.allowSubContent.showOpenDialog.openLabel": "选择文件夹",
|
||||
"helpers.contentType.create.allowSubContent.showOpenDialog.title": "选择创建内容的文件夹",
|
||||
"helpers.contentType.create.pageBundle.title": "创建为页面捆绑包?",
|
||||
"helpers.contentType.create.pageBundle.placeHolder": "您想将子内容创建为页面捆绑包吗?",
|
||||
"helpers.contentType.create.progress.title": "{0}: 正在创建内容...",
|
||||
"helpers.contentType.create.success": "您的新内容已创建。",
|
||||
"helpers.contentType.verify.warning": "内容类型操作在此模式下不可用。",
|
||||
|
||||
"helpers.customScript.executing": "正在执行:{0}",
|
||||
"helpers.customScript.singleRun.article.warning": "{0}: 无法检索到文章。",
|
||||
"helpers.customScript.bulkRun.noFiles.warning": "{0}: 未找到文件",
|
||||
"helpers.customScript.runMediaScript.noFolder.warning": "{0}: 未指定文件夹或媒体路径。",
|
||||
"helpers.customScript.showOutput.frontMatter.success": "{0}: front matter 已更新。",
|
||||
"helpers.customScript.showOutput.copyOutput.action": "复制输出",
|
||||
"helpers.customScript.showOutput.success": "{0}: 已执行您的自定义脚本。",
|
||||
"helpers.customScript.validateCommand.error": "无效命令:{0}",
|
||||
|
||||
"helpers.dataFileHelper.process.error": "处理数据文件时出错。",
|
||||
|
||||
"helpers.extension.getVersion.changelog": "查看更新日志",
|
||||
"helpers.extension.getVersion.starIt": "给它一个 ⭐️",
|
||||
"helpers.extension.getVersion.update.notification": "{0} 已更新至 v{1} — 查看新功能!",
|
||||
"helpers.extension.migrateSettings.templates.quickPick.title": "{0} - 模板",
|
||||
"helpers.extension.migrateSettings.templates.quickPick.placeholder": "您想继续使用模板功能吗?",
|
||||
"helpers.extension.checkIfExtensionCanRun.warning": "Front Matter BETA 无法在安装了稳定版本的情况下使用。请确保您只安装了一个版本。",
|
||||
|
||||
"helpers.mediaHelper.saveFile.folder.error": "我们找不到您选择的文件夹。",
|
||||
"helpers.mediaHelper.saveFile.file.uploaded.success": "文件 {0} 已上传至:{1}",
|
||||
"helpers.mediaHelper.saveFile.file.uploaded.failed": "抱歉,上传 {0} 时出错",
|
||||
"helpers.mediaHelper.deleteFile.file.deletion.failed": "抱歉,删除 {0} 时出错",
|
||||
|
||||
"helpers.mediaLibrary.remove.warning": "名称 \"{0}\" 在文件位置已存在。",
|
||||
"helpers.mediaLibrary.remove.error": "抱歉,将 \"{0}\" 更新为 \"{1}\" 时出错。",
|
||||
|
||||
"helpers.openFileInEditor.error": "无法打开文件。",
|
||||
|
||||
"helpers.questions.contentTitle.aiInput.title": "标题或描述",
|
||||
"helpers.questions.contentTitle.aiInput.prompt": "您想写什么?",
|
||||
"helpers.questions.contentTitle.aiInput.placeholder": "您想写什么?",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.title.separator": "您的标题/描述",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.ai.separator": "AI 生成的标题",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.copilot.separator": "GitHub Copilot 建议",
|
||||
"helpers.questions.contentTitle.aiInput.select.title": "选择一个标题",
|
||||
"helpers.questions.contentTitle.aiInput.select.placeholder": "为您的内容选择一个标题",
|
||||
"helpers.questions.contentTitle.aiInput.failed": "获取 AI 标题失败。请尝试使用您自己的标题或稍后再试。",
|
||||
"helpers.questions.contentTitle.copilotInput.failed": "获取 GitHub Copilot 标题建议失败。请尝试使用您自己的标题或稍后再试。",
|
||||
"helpers.questions.contentTitle.aiInput.warning": "您未指定内容的标题。",
|
||||
"helpers.questions.contentTitle.titleInput.title": "内容标题",
|
||||
"helpers.questions.contentTitle.titleInput.prompt": "您想为要创建的内容使用什么标题?",
|
||||
"helpers.questions.contentTitle.titleInput.placeholder": "内容标题",
|
||||
"helpers.questions.contentTitle.titleInput.warning": "您未指定内容的标题。",
|
||||
"helpers.questions.selectContentFolder.quickPick.title": "选择一个文件夹",
|
||||
"helpers.questions.selectContentFolder.quickPick.placeholder": "选择您要创建内容的位置",
|
||||
"helpers.questions.selectContentFolder.quickPick.noSelection.warning": "您没有选择要创建内容的位置。",
|
||||
"helpers.questions.selectContentType.noContentType.warning": "未找到内容类型。请先创建内容类型。",
|
||||
"helpers.questions.selectContentType.quickPick.title": "内容类型",
|
||||
"helpers.questions.selectContentType.quickPick.placeholder": "选择用于创建新内容的内容类型",
|
||||
"helpers.questions.selectContentType.noSelection.warning": "未选择内容类型。",
|
||||
"helpers.questions.selectContentType.quickPick.error.noContentTypes": "此文件夹没有配置匹配的内容类型。",
|
||||
|
||||
"helpers.seoHelper.checkLength.diagnostic.message": "文章 {0} 长度超过 {1} 个字符(当前长度:{2})。出于 SEO 原因,最好将其控制在 {1} 个字符以内。",
|
||||
|
||||
"helpers.settingsHelper.checkToPromote.message": "您有本地设置。是否要将它们提升为全局设置(\"frontmatter.json\")?",
|
||||
"helpers.settingsHelper.promote.success": "所有设置已提升到团队级别。",
|
||||
"helpers.settingsHelper.readConfig.progress.title": "{0}: 正在读取动态配置文件...",
|
||||
"helpers.settingsHelper.readConfig.error": "读取配置时出错。",
|
||||
"helpers.settingsHelper.refreshConfig.success": "设置已刷新。",
|
||||
"helpers.settingsHelper.safeUpdate.warning": "无法更新设置 \"{0}\",因为您已扩展或拆分 Front Matter CMS 配置。请手动添加您的更改。请查看输出以获取设置更新。",
|
||||
|
||||
"helpers.taxonomyHelper.rename.input.title": "重命名 {0}",
|
||||
"helpers.taxonomyHelper.rename.validate.equalValue": "新值必须与旧值不同。",
|
||||
"helpers.taxonomyHelper.rename.validate.noValue": "必须提供新值。",
|
||||
"helpers.taxonomyHelper.merge.quickPick.title": "将 \"{0}\" 与另一个 {1} 值合并",
|
||||
"helpers.taxonomyHelper.merge.quickPick.placeholder": "选择要合并的 {0} 值",
|
||||
"helpers.taxonomyHelper.delete.quickPick.title": "删除 \"{0}\" {1} 值",
|
||||
"helpers.taxonomyHelper.delete.quickPick.placeholder": "您确定要删除 \"{0}\" {1} 值吗?",
|
||||
"helpers.taxonomyHelper.createNew.input.title": "创建新的 {0} 值",
|
||||
"helpers.taxonomyHelper.createNew.input.placeholder": "输入您要添加的值",
|
||||
"helpers.taxonomyHelper.createNew.input.validate.noValue": "必须提供一个值。",
|
||||
"helpers.taxonomyHelper.createNew.input.validate.exists": "该值已存在。",
|
||||
"helpers.taxonomyHelper.process.insert": "{0}: 正在将 \"{1}\" 插入您选择的页面。",
|
||||
"helpers.taxonomyHelper.process.edit": "{0}: 正在将 \"{1}\" 从 {2} 重命名为 {3}。",
|
||||
"helpers.taxonomyHelper.process.merge": "{0}: 正在将 \"{1}\" 从 {2} 合并到 {3}。",
|
||||
"helpers.taxonomyHelper.process.delete": "{0}: 正在从 {2} 中删除 \"{1}\"。",
|
||||
"helpers.taxonomyHelper.process.insert.success": "插入完成。",
|
||||
"helpers.taxonomyHelper.process.edit.success": "编辑完成。",
|
||||
"helpers.taxonomyHelper.process.merge.success": "合并完成。",
|
||||
"helpers.taxonomyHelper.process.delete.success": "删除完成。",
|
||||
"helpers.taxonomyHelper.move.quickPick.title": "将 \"{0}\" 移动到其他类型",
|
||||
"helpers.taxonomyHelper.move.quickPick.placeholder": "选择要移动到的类型",
|
||||
"helpers.taxonomyHelper.move.progress.title": "{0}: 正在将 \"{1}\" 从 {2} 移动到 \"{3}\"。",
|
||||
"helpers.taxonomyHelper.move.success": "移动完成。",
|
||||
|
||||
"listeners.dashboard.dashboardListener.openConfig.notification": "如果您想查看配置,请打开 \"frontmatter.json\" 文件。",
|
||||
"listeners.dashboard.dashboardListener.pinItem.noPath.error": "未提供路径。",
|
||||
"listeners.dashboard.dashboardListener.pinItem.coundNotPin.error": "无法固定项目。",
|
||||
"listeners.dashboard.dashboardListener.pinItem.coundNotUnPin.error": "无法取消固定项目。",
|
||||
|
||||
"listeners.dashboard.mediaListeners.deleteMediaFolder.progress.title": "正在删除文件夹...",
|
||||
"listeners.dashboard.mediaListeners.updateMediaFolder.progress.title": "正在更新文件夹...",
|
||||
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.notification": "模板文件已复制。",
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.progress.title": "正在下载并初始化模板...",
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.download.error": "下载模板失败。",
|
||||
"listeners.dashboard.settingsListener.triggerTemplate.init.error": "初始化模板失败。",
|
||||
"listeners.dashboard.settingsListener.setSecretValue.message": "设置已更新。",
|
||||
|
||||
"listeners.dashboard.snippetListener.addSnippet.missingFields.warning": "片段缺少标题或正文",
|
||||
"listeners.dashboard.snippetListener.addSnippet.exists.warning": "已存在具有相同标题的片段",
|
||||
"listeners.dashboard.snippetListener.updateSnippet.noSnippets.warning": "没有要更新的片段",
|
||||
|
||||
"listeners.general.gitListener.push.error": "推送子模块失败。",
|
||||
|
||||
"listeners.panel.dataListener.aiSuggestTaxonomy.noEditor.error": "无活动编辑器",
|
||||
"listeners.panel.dataListener.aiSuggestTaxonomy.noData.error": "无文章数据",
|
||||
"listeners.panel.dataListener.getDataFileEntries.noDataFiles.error": "找不到数据文件条目",
|
||||
"listeners.panel.dataListener.pushMetadata.frontMatter.error": "解析您的 front matter 时出错。请检查文件内容。",
|
||||
"listeners.panel.dataListener.createDataFile.inputTitle": "数据文件的名称是什么?",
|
||||
"listeners.panel.dataListener.createDataFile.error": "未定义数据文件 ID 或路径。",
|
||||
"listeners.panel.dataListener.createDataFile.noFileName": "未提供文件名。",
|
||||
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "无活动编辑器",
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error": "无文章数据",
|
||||
|
||||
"services.copilot.getChatResponse.error": "未能从 GitHub Copilot 获得响应。",
|
||||
|
||||
"services.modeSwitch.switchMode.quickPick.placeholder": "选择您要使用的模式",
|
||||
"services.modeSwitch.switchMode.quickPick.title": "{0}: 模式选择",
|
||||
"services.modeSwitch.setText.mode": "模式:{0}",
|
||||
|
||||
"services.pagesParser.parsePages.statusBar.text": "正在处理...",
|
||||
"services.pagesParser.parsePages.file.error": "文件错误:{0}",
|
||||
|
||||
"services.sponsorAi.getTitles.warning": "AI 标题生成耗时过长。请稍后再试。",
|
||||
"services.sponsorAi.getDescription.warning": "AI 描述生成耗时过长。请稍后再试。",
|
||||
"services.sponsorAi.getTaxonomySuggestions.warning": "AI 分类法生成耗时过长。请稍后再试。",
|
||||
|
||||
"services.terminal.openLocalServerTerminal.terminalOption.message": "正在启动本地服务器"
|
||||
}
|
||||
6416
package-lock.json
generated
6416
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
491
package.json
491
package.json
@@ -3,14 +3,15 @@
|
||||
"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.9.0",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
"color": "#0e131f",
|
||||
"theme": "dark"
|
||||
},
|
||||
"badges": [{
|
||||
"badges": [
|
||||
{
|
||||
"description": "version",
|
||||
"url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square",
|
||||
"href": "https://github.com/estruyf/vscode-front-matter"
|
||||
@@ -31,7 +32,7 @@
|
||||
"l10n": "./l10n",
|
||||
"categories": [
|
||||
"AI",
|
||||
"Other"
|
||||
"Visualization"
|
||||
],
|
||||
"keywords": [
|
||||
"Front Matter",
|
||||
@@ -70,7 +71,8 @@
|
||||
"**/.frontmatter/config/*.json": "jsonc"
|
||||
}
|
||||
},
|
||||
"keybindings": [{
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "frontMatter.dashboard",
|
||||
"key": "alt+d"
|
||||
},
|
||||
@@ -94,19 +96,23 @@
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [{
|
||||
"id": "frontmatter-explorer",
|
||||
"title": "FM",
|
||||
"icon": "$(fm-logo)"
|
||||
}]
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "frontmatter-explorer",
|
||||
"title": "FM",
|
||||
"icon": "$(fm-logo)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"frontmatter-explorer": [{
|
||||
"id": "frontMatter.explorer",
|
||||
"name": "Front Matter",
|
||||
"icon": "$(fm-logo)",
|
||||
"type": "webview"
|
||||
}]
|
||||
"frontmatter-explorer": [
|
||||
{
|
||||
"id": "frontMatter.explorer",
|
||||
"name": "Front Matter",
|
||||
"icon": "$(fm-logo)",
|
||||
"type": "webview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "%settings.configuration.title%",
|
||||
@@ -174,7 +180,8 @@
|
||||
"frontMatter.content.defaultFileType": {
|
||||
"type": "string",
|
||||
"default": "md",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -190,7 +197,8 @@
|
||||
"frontMatter.content.defaultSorting": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"LastModifiedAsc",
|
||||
"LastModifiedDesc",
|
||||
@@ -283,6 +291,14 @@
|
||||
"default": false,
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description%"
|
||||
},
|
||||
"excludePaths": {
|
||||
"type": "array",
|
||||
"default": false,
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.excludePaths.description%",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"previewPath": {
|
||||
"type": [
|
||||
"null",
|
||||
@@ -468,6 +484,34 @@
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"frontMatter.content.grouping": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "%setting.frontMatter.content.grouping.markdownDescription%",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.content.grouping.items.properties.id.description%"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.content.grouping.items.properties.title.description%"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.content.grouping.items.properties.name.description%"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"title",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"scope": "Content"
|
||||
},
|
||||
"frontMatter.content.sorting": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
@@ -542,7 +586,8 @@
|
||||
"categories"
|
||||
],
|
||||
"markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%",
|
||||
"items": [{
|
||||
"items": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"contentFolders",
|
||||
@@ -616,7 +661,8 @@
|
||||
"command": {
|
||||
"$id": "#scriptCommand",
|
||||
"type": "string",
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"node",
|
||||
"bash",
|
||||
@@ -812,7 +858,8 @@
|
||||
"title",
|
||||
"file"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -859,6 +906,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,
|
||||
@@ -866,7 +927,8 @@
|
||||
"id",
|
||||
"path"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -999,8 +1061,9 @@
|
||||
"panel.globalSettings",
|
||||
"panel.seo",
|
||||
"panel.actions",
|
||||
"panel.contentType",
|
||||
"panel.metadata",
|
||||
"panel.contentType",
|
||||
"panel.gitActions",
|
||||
"panel.recentlyModified",
|
||||
"panel.otherActions",
|
||||
"dashboard.snippets.view",
|
||||
@@ -1034,7 +1097,7 @@
|
||||
"error"
|
||||
],
|
||||
"markdownDescription": "%setting.frontMatter.global.notifications.markdownDescription%",
|
||||
"scope": "Templates"
|
||||
"scope": "Global"
|
||||
},
|
||||
"frontMatter.global.disabledNotifications": {
|
||||
"type": "array",
|
||||
@@ -1044,6 +1107,12 @@
|
||||
"requiredFieldValidation"
|
||||
]
|
||||
},
|
||||
"frontMatter.global.timezone": {
|
||||
"default": "UTC",
|
||||
"type": "string",
|
||||
"markdownDescription": "%setting.frontMatter.global.timezone.markdownDescription%",
|
||||
"scope": "Global"
|
||||
},
|
||||
"frontMatter.media.defaultSorting": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
@@ -1107,26 +1176,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [{
|
||||
"name": "default",
|
||||
"fileTypes": null,
|
||||
"fields": [{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Caption",
|
||||
"name": "caption",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Alt text",
|
||||
"name": "alt",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"default": [
|
||||
{
|
||||
"name": "default",
|
||||
"fileTypes": null,
|
||||
"fields": [
|
||||
{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Caption",
|
||||
"name": "caption",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Alt text",
|
||||
"name": "alt",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scope": "Media"
|
||||
},
|
||||
"frontMatter.media.supportedMimeTypes": {
|
||||
@@ -1142,6 +1214,12 @@
|
||||
},
|
||||
"scope": "Media"
|
||||
},
|
||||
"frontMatter.panel.openOnSupportedFile": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%setting.frontMatter.panel.openOnSupportedFile.markdownDescription%",
|
||||
"scope": "Dashboard"
|
||||
},
|
||||
"frontMatter.panel.freeform": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
@@ -1229,7 +1307,8 @@
|
||||
"fileType": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -1344,7 +1423,14 @@
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.single.description%"
|
||||
},
|
||||
"wysiwyg": {
|
||||
"type": "boolean",
|
||||
"type": [
|
||||
"boolean",
|
||||
"string"
|
||||
],
|
||||
"enum": [
|
||||
"html",
|
||||
"markdown"
|
||||
],
|
||||
"default": false,
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.wysiwyg.description%"
|
||||
},
|
||||
@@ -1368,7 +1454,8 @@
|
||||
"default": "",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1512,6 +1599,11 @@
|
||||
"default": "path",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeValue.description%"
|
||||
},
|
||||
"sameContentLocale": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.sameContentLocale.description%"
|
||||
},
|
||||
"when": {
|
||||
"type": "object",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.description%",
|
||||
@@ -1569,7 +1661,8 @@
|
||||
"type",
|
||||
"name"
|
||||
],
|
||||
"allOf": [{
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -1781,48 +1874,51 @@
|
||||
"fields"
|
||||
]
|
||||
},
|
||||
"default": [{
|
||||
"name": "default",
|
||||
"pageBundle": false,
|
||||
"fields": [{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Publishing date",
|
||||
"name": "date",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "Content preview",
|
||||
"name": "preview",
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"title": "Is in draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"title": "Tags",
|
||||
"name": "tags",
|
||||
"type": "tags"
|
||||
},
|
||||
{
|
||||
"title": "Categories",
|
||||
"name": "categories",
|
||||
"type": "categories"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"default": [
|
||||
{
|
||||
"name": "default",
|
||||
"pageBundle": false,
|
||||
"fields": [
|
||||
{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Publishing date",
|
||||
"name": "date",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "Content preview",
|
||||
"name": "preview",
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"title": "Is in draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"title": "Tags",
|
||||
"name": "tags",
|
||||
"type": "tags"
|
||||
},
|
||||
{
|
||||
"title": "Categories",
|
||||
"name": "categories",
|
||||
"type": "categories"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.customTaxonomy": {
|
||||
@@ -1835,7 +1931,8 @@
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1979,7 +2076,11 @@
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.slugTemplate": {
|
||||
"type": "string",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"markdownDescription": "%setting.frontMatter.taxonomy.slugTemplate.markdownDescription%",
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
@@ -2005,7 +2106,7 @@
|
||||
},
|
||||
"frontMatter.templates.prefix": {
|
||||
"type": "string",
|
||||
"default": "yyyy-MM-dd",
|
||||
"default": "{{date|yyyy-MM-dd}}",
|
||||
"markdownDescription": "%setting.frontMatter.templates.prefix.markdownDescription%",
|
||||
"scope": "Templates"
|
||||
},
|
||||
@@ -2025,12 +2126,13 @@
|
||||
},
|
||||
"frontMatter.copilot.family": {
|
||||
"type": "string",
|
||||
"default": "gpt-3.5-turbo",
|
||||
"default": "gpt-4o-mini",
|
||||
"markdownDescription": "%setting.frontMatter.copilot.family.markdownDescription%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [{
|
||||
"commands": [
|
||||
{
|
||||
"command": "frontMatter.project.switch",
|
||||
"title": "%command.frontMatter.project.switch%",
|
||||
"category": "Front Matter",
|
||||
@@ -2352,6 +2454,15 @@
|
||||
"title": "%command.frontMatter.cache.clear%",
|
||||
"category": "Front Matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.i18n.createOrOpen",
|
||||
"title": "%command.frontMatter.i18n.createOrOpen%",
|
||||
"category": "Front Matter",
|
||||
"icon": {
|
||||
"light": "assets/icons/i18n-light.svg",
|
||||
"dark": "assets/icons/i18n-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.i18n.create",
|
||||
"title": "%command.frontMatter.i18n.create%",
|
||||
@@ -2362,84 +2473,89 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"submenus": [{
|
||||
"id": "frontmatter.submenu",
|
||||
"label": "Front Matter"
|
||||
}],
|
||||
"submenus": [
|
||||
{
|
||||
"id": "frontmatter.submenu",
|
||||
"label": "Front Matter"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"webview/context": [{
|
||||
"command": "workbench.action.webview.openDeveloperTools",
|
||||
"when": "frontMatter:isDevelopment"
|
||||
}],
|
||||
"editor/title": [{
|
||||
"webview/context": [
|
||||
{
|
||||
"command": "workbench.action.webview.openDeveloperTools",
|
||||
"when": "frontMatter:isDevelopment"
|
||||
}
|
||||
],
|
||||
"editor/title": [
|
||||
{
|
||||
"command": "frontMatter.markup.heading",
|
||||
"group": "navigation@-133",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.bold",
|
||||
"group": "navigation@-132",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.italic",
|
||||
"group": "navigation@-131",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.hyperlink",
|
||||
"group": "navigation@-130",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.insertSnippet",
|
||||
"group": "navigation@-129",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:dashboard:snippets:enabled"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:dashboard:snippets:enabled && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.insertMedia",
|
||||
"group": "navigation@-128",
|
||||
"when": "frontMatter:file:isValid == true"
|
||||
"when": "frontMatter:file:isValid == true && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.i18n.create",
|
||||
"command": "frontMatter.i18n.createOrOpen",
|
||||
"group": "navigation@-127",
|
||||
"when": "frontMatter:file:isValid && frontMatter:i18n:enabled"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.options",
|
||||
"group": "navigation@-126",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.orderedlist",
|
||||
"group": "1_markup@1",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.unorderedlist",
|
||||
"group": "1_markup@2",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.tasklist",
|
||||
"group": "1_markup@3",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.code",
|
||||
"group": "1_markup@4",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.codeblock",
|
||||
"group": "1_markup@5",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.markup.blockquote",
|
||||
"group": "1_markup@6",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.dashboard",
|
||||
@@ -2457,11 +2573,14 @@
|
||||
"when": "resourceFilename == 'frontmatter.json'"
|
||||
}
|
||||
],
|
||||
"explorer/context": [{
|
||||
"submenu": "frontmatter.submenu",
|
||||
"group": "frontmatter@1"
|
||||
}],
|
||||
"frontmatter.submenu": [{
|
||||
"explorer/context": [
|
||||
{
|
||||
"submenu": "frontmatter.submenu",
|
||||
"group": "frontmatter@1"
|
||||
}
|
||||
],
|
||||
"frontmatter.submenu": [
|
||||
{
|
||||
"command": "frontMatter.createFromTemplate",
|
||||
"when": "explorerResourceIsFolder",
|
||||
"group": "frontmatter@1"
|
||||
@@ -2477,7 +2596,8 @@
|
||||
"group": "frontmatter@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [{
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "frontMatter.init",
|
||||
"when": "frontMatterCanInit"
|
||||
},
|
||||
@@ -2654,7 +2774,8 @@
|
||||
"when": "frontMatter:file:isValid == true"
|
||||
}
|
||||
],
|
||||
"view/title": [{
|
||||
"view/title": [
|
||||
{
|
||||
"command": "frontMatter.docs",
|
||||
"group": "navigation@-1",
|
||||
"when": "view == frontMatter.explorer"
|
||||
@@ -2691,13 +2812,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"languages": [{
|
||||
"id": "frontmatter.project.output",
|
||||
"mimetypes": [
|
||||
"text/x-code-output"
|
||||
]
|
||||
}],
|
||||
"grammars": [{
|
||||
"languages": [
|
||||
{
|
||||
"id": "frontmatter.project.output",
|
||||
"mimetypes": [
|
||||
"text/x-code-output"
|
||||
]
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"path": "./syntaxes/hugo.tmLanguage.json",
|
||||
"scopeName": "frontmatter.markdown.hugo",
|
||||
"injectTo": [
|
||||
@@ -2710,45 +2834,48 @@
|
||||
"path": "./syntaxes/frontmatter-output.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"walkthroughs": [{
|
||||
"id": "frontmatter.welcome",
|
||||
"title": "Get started with Front Matter",
|
||||
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
|
||||
"steps": [{
|
||||
"id": "frontmatter.welcome.init",
|
||||
"title": "Get started",
|
||||
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/get-started.md"
|
||||
"walkthroughs": [
|
||||
{
|
||||
"id": "frontmatter.welcome",
|
||||
"title": "Get started with Front Matter",
|
||||
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
|
||||
"steps": [
|
||||
{
|
||||
"id": "frontmatter.welcome.init",
|
||||
"title": "Get started",
|
||||
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/get-started.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onContext:frontMatterInitialized"
|
||||
]
|
||||
},
|
||||
"completionEvents": [
|
||||
"onContext:frontMatterInitialized"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "frontmatter.welcome.documentation",
|
||||
"title": "Documentation",
|
||||
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/documentation.md"
|
||||
{
|
||||
"id": "frontmatter.welcome.documentation",
|
||||
"title": "Documentation",
|
||||
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/documentation.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://frontmatter.codes/docs"
|
||||
]
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://frontmatter.codes/docs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "frontmatter.welcome.supporter",
|
||||
"title": "Support the project",
|
||||
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/support-the-project.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://github.com/sponsors/estruyf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
{
|
||||
"id": "frontmatter.welcome.supporter",
|
||||
"title": "Support the project",
|
||||
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/support-the-project.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://github.com/sponsors/estruyf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*",
|
||||
@@ -2779,6 +2906,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",
|
||||
@@ -2795,11 +2923,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",
|
||||
@@ -2808,10 +2935,12 @@
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"clsx": "^2.1.0",
|
||||
"css-loader": "5.2.7",
|
||||
"date-fns": "2.23.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"downshift": "6.0.6",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-webpack-plugin": "^4.2.0",
|
||||
"fuse.js": "6.5.3",
|
||||
"github-directory-downloader": "^1.3.6",
|
||||
"glob": "^10.3.12",
|
||||
@@ -2845,8 +2974,16 @@
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-remark": "^10.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
"simple-git": "^3.16.0",
|
||||
@@ -2856,24 +2993,24 @@
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.5",
|
||||
"unified": "^11.0.5",
|
||||
"uniforms": "^3.10.2",
|
||||
"uniforms-antd": "^3.10.2",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"command.frontMatter.git.sync": "Sync",
|
||||
"command.frontMatter.cache.clear": "Clear cache",
|
||||
"command.frontMatter.i18n.create": "Create new translation",
|
||||
"command.frontMatter.i18n.createOrOpen": "Create or open translation",
|
||||
"settings.configuration.title": "Front Matter: use frontmatter.json for shared team settings",
|
||||
"setting.frontMatter.projects.markdownDescription": "Specify the list of projects to load in the Front Matter CMS. [Local](https://file%2B.vscode-resource.vscode-cdn.net/Users/eliostruyf/nodejs/frontmatter-test-projects/astro-blog/test.html) - [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.projects) - [View in VS Code](vscode://simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.projects%22%5D)",
|
||||
"setting.frontMatter.projects.items.properties.name.markdownDescription": "Specify the name of the project.",
|
||||
@@ -73,6 +74,7 @@
|
||||
"setting.frontMatter.content.pageFolders.items.properties.title.description": "Name of the folder",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.path.description": "Path of the folder",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description": "Exclude sub-directories",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludePaths.description": "Exclude paths (e.g. api, _*.*)",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.previewPath.description": "Defines a custom preview path for the folder.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.filePrefix.description": "Defines a prefix for the file name.",
|
||||
@@ -94,6 +96,10 @@
|
||||
"setting.frontMatter.content.publicFolder.properties.relative.description": "Defines if the path to your media files be relative to the content file?",
|
||||
"setting.frontMatter.snippets.wrapper.enabled.markdownDescription": "Specify if you want to wrap the snippets. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.snippets.wrapper.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.snippets.wrapper.enabled%22%5D)",
|
||||
"setting.frontMatter.content.snippets.markdownDescription": "Define the snippets you want to use in your content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.snippets) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.snippets%22%5D)",
|
||||
"setting.frontMatter.content.grouping.markdownDescription": "Specify the grouping options for your dashboard content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.grouping) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.grouping%22%5D)",
|
||||
"setting.frontMatter.content.grouping.items.properties.id.description": "The ID of the grouping option.",
|
||||
"setting.frontMatter.content.grouping.items.properties.title.description": "Title of the grouping which will be shown in the UI.",
|
||||
"setting.frontMatter.content.grouping.items.properties.name.description": "Name of the content-type field to group by.",
|
||||
"setting.frontMatter.content.sorting.markdownDescription": "Define the sorting options for your dashboard content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.sorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.sorting%22%5D)",
|
||||
"setting.frontMatter.content.sorting.items.properties.id.description": "The ID of the sorting option. This will be used for the storing the last used sorting option or the default option.",
|
||||
"setting.frontMatter.content.sorting.items.properties.title.description": "Name of the sorting label",
|
||||
@@ -145,6 +151,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)",
|
||||
@@ -162,6 +170,7 @@
|
||||
"setting.frontMatter.global.modes.items.properties.features.description": "The features you want to use for your mode.",
|
||||
"setting.frontMatter.global.notifications.markdownDescription": "Specifies the notifications you want to see. By default, all notifications types will be shown. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.notifications) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.notifications%22%5D)",
|
||||
"setting.frontMatter.global.disabledNotifications.markdownDescription": "This is an array with the notifications types that can be disabled for Front Matter CMS. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.disablednotifications%22%5D)",
|
||||
"setting.frontMatter.global.timezone.markdownDescription": "Specify the timezone for your date formatting. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.datetimezone) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.datetimezone%22%5D)",
|
||||
"setting.frontMatter.media.defaultSorting.markdownDescription": "Specify the default sorting option for the media dashboard. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.defaultsorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.defaultsorting%22%5D)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "Specify the mime types to support for the media files. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.supportedmimetypes%22%5D)",
|
||||
|
||||
@@ -175,6 +184,7 @@
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.type.description": "Define the type of field",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.single.description": "Is a single line field",
|
||||
|
||||
"setting.frontMatter.panel.openOnSupportedFile.markdownDescription": "Specifies if you want to open the panel when opening a supported file. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.openonsupportedfile) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.openonsupportedfile%22%5D)",
|
||||
"setting.frontMatter.panel.freeform.markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.freeform%22%5D)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "Specify the actions you want to disable in the panel. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.actions.disabled%22%5D)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.host%22%5D)",
|
||||
@@ -200,7 +210,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.choices.items.properties.id.description": "The choice ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.choices.items.properties.title.description": "The choice title",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.single.description": "Is a single line field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.wysiwyg.description": "Is a WYSIWYG field (HTML output)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.wysiwyg.description": "Is a WYSIWYG field. You can set it to markdown or HTML.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.multiple.description": "Do you allow to select multiple values?",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.isPreviewImage.description": "Specify if the image field can be used as preview. Be aware, you can only have one preview image per content type.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.hidden.description": "Do you want to hide the field from the metadata section?",
|
||||
@@ -226,6 +236,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.required.description": "Specify if the field is required",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeName.description": "Specify the content type name to filter content for the contentRelationship field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeValue.description": "Specify the value to insert for the contentRelationship field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.sameContentLocale.description": "Specify if you only want to show the content with the same locale",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.description": "Specify the conditions to show the field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.fieldRef.description": "The field ID to use",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.operator.description": "The operator to use",
|
||||
@@ -282,4 +293,4 @@
|
||||
"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.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)"
|
||||
}
|
||||
}
|
||||
|
||||
296
package.nls.zh-cn.json
Normal file
296
package.nls.zh-cn.json
Normal file
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"command.frontMatter.project.switch": "切换项目",
|
||||
"command.frontMatter.config.reload": "重新加载配置",
|
||||
"command.frontMatter.authenticate": "身份验证",
|
||||
"command.frontMatter.contenttype.generate": "从当前文件生成内容类型",
|
||||
"command.frontMatter.contenttype.addMissingFields": "将缺失字段从前端元数据添加到内容类型",
|
||||
"command.frontMatter.contenttype.setContentType": "设置当前文件使用的内容类型",
|
||||
"command.frontMatter.markup.blockquote": "引用",
|
||||
"command.frontMatter.markup.bold": "加粗",
|
||||
"command.frontMatter.dashboard.close": "关闭仪表盘",
|
||||
"command.frontMatter.markup.code": "代码",
|
||||
"command.frontMatter.markup.codeblock": "代码块",
|
||||
"command.frontMatter.markup.hyperlink": "超链接",
|
||||
"command.frontMatter.collapseSections": "折叠部分",
|
||||
"command.frontMatter.initTemplate": "初始化模板文件夹",
|
||||
"command.frontMatter.createTemplate": "从当前文件创建模板",
|
||||
"command.frontMatter.createCategory": "创建分类",
|
||||
"command.frontMatter.createContent": "创建新内容",
|
||||
"command.frontMatter.createTag": "创建标签",
|
||||
"command.frontMatter.diagnostics": "诊断日志",
|
||||
"command.frontMatter.exportTaxonomy": "将所有标签和分类导出到设置",
|
||||
"command.frontMatter.createFromTemplate": "从模板创建新文章",
|
||||
"command.frontMatter.registerFolder": "注册文件夹",
|
||||
"command.frontMatter.unregisterFolder": "取消注册文件夹",
|
||||
"command.frontMatter.generateSlug": "基于内容标题生成别名",
|
||||
"command.frontMatter.markup.heading": "标题",
|
||||
"command.frontMatter.init": "初始化项目",
|
||||
"command.frontMatter.insertCategories": "插入分类",
|
||||
"command.frontMatter.insertMedia": "将媒体插入内容",
|
||||
"command.frontMatter.insertSnippet": "将片段插入内容",
|
||||
"command.frontMatter.insertTags": "插入标签",
|
||||
"command.frontMatter.markup.italic": "斜体",
|
||||
"command.frontMatter.dashboard": "打开仪表盘",
|
||||
"command.frontMatter.dashboard.data": "打开数据仪表盘",
|
||||
"command.frontMatter.dashboard.media": "打开媒体仪表盘",
|
||||
"command.frontMatter.dashboard.snippets": "打开片段仪表盘",
|
||||
"command.frontMatter.dashboard.taxonomy": "打开分类法仪表盘",
|
||||
"command.frontMatter.markup.orderedlist": "有序列表",
|
||||
"command.frontMatter.markup.options": "其他标记选项",
|
||||
"command.frontMatter.preview": "预览内容",
|
||||
"command.frontMatter.docs": "文档",
|
||||
"command.frontMatter.chatbot": "向 Front Matter AI 寻求帮助",
|
||||
"command.frontMatter.promoteSettings": "将设置从本地提升到团队级别",
|
||||
"command.frontMatter.remap": "在所有文章中重新映射或移除标签/分类",
|
||||
"command.frontMatter.setLastModifiedDate": "设置最后修改日期",
|
||||
"command.frontMatter.markup.strikethrough": "删除线",
|
||||
"command.frontMatter.mode.switch": "切换模式",
|
||||
"command.frontMatter.markup.tasklist": "任务列表",
|
||||
"command.frontMatter.markup.unorderedlist": "无序列表",
|
||||
"command.frontMatter.git.sync": "同步",
|
||||
"command.frontMatter.cache.clear": "清除缓存",
|
||||
"command.frontMatter.i18n.create": "创建新翻译",
|
||||
"command.frontMatter.i18n.createOrOpen": "创建或打开翻译",
|
||||
"settings.configuration.title": "Front Matter: 使用 frontmatter.json 进行团队共享设置",
|
||||
"setting.frontMatter.projects.markdownDescription": "指定要在 Front Matter CMS 中加载的项目列表。[本地](https://file%2B.vscode-resource.vscode-cdn.net/Users/eliostruyf/nodejs/frontmatter-test-projects/astro-blog/test.html) - [文档](https://frontmatter.codes/docs/settings/overview#frontmatter.projects) - [在 VS Code 中查看](vscode://simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.projects%22%5D)",
|
||||
"setting.frontMatter.projects.items.properties.name.markdownDescription": "指定项目名称。",
|
||||
"setting.frontMatter.projects.items.properties.default.markdownDescription": "指定此项目是否为默认加载项目。",
|
||||
"setting.frontMatter.sponsors.ai.enabled.markdownDescription": "指定是否启用 AI 建议。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.sponsors.ai.enabled) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.sponsors.ai.enabled%22%5D)",
|
||||
"setting.frontMatter.extensibility.scripts.markdownDescription": "指定要在 Front Matter CMS 中加载的脚本列表。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.extensibility.scripts) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extensibility.scripts%22%5D)",
|
||||
"setting.frontMatter.experimental.markdownDescription": "指定是否启用实验性功能。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.experimental) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.experimental%22%5D)",
|
||||
"setting.frontMatter.extends.markdownDescription": "指定扩展 Front Matter CMS 配置的路径/URL 列表。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.extends) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extends%22%5D)",
|
||||
"setting.frontMatter.content.autoUpdateDate.markdownDescription": "指定是否自动更新文章/页面的修改日期(仅限内容文件夹中的内容)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.autoupdatedate) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.autoupdatedate%22%5D)",
|
||||
"setting.frontMatter.content.defaultFileType.markdownDescription": "指定要创建内容的默认文件类型。[文档](https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultfiletype) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultfiletype%22%5D)",
|
||||
"setting.frontMatter.content.defaultSorting.markdownDescription": "指定内容仪表盘的默认排序选项。您可以使用枚举中的值或定义自己的 ID。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultsorting) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultsorting%22%5D)",
|
||||
"setting.frontMatter.content.draftField.markdownDescription": "定义要用于管理内容的草稿字段。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.draftfield) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.draftfield%22%5D)",
|
||||
"setting.frontMatter.content.draftField.properties.type.description": "要使用的草稿字段类型",
|
||||
"setting.frontMatter.content.draftField.properties.name.description": "要使用的字段名称",
|
||||
"setting.frontMatter.content.draftField.properties.invert.description": "默认情况下,当内容为草稿时,draft 字段值为 true。如需将draft字段设为 false,请将此值设置为 true。",
|
||||
"setting.frontMatter.content.draftField.properties.choices.description": "字段的选择列表",
|
||||
"setting.frontMatter.content.fmHighlight.markdownDescription": "指定是否在 Markdown 文件中高亮显示 Front Matter。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.fmhighlight) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.fmhighlight%22%5D)",
|
||||
"setting.frontMatter.content.hideFm.markdownDescription": "指定是否在 Markdown 文件中隐藏 Front Matter。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefm) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.hidefm%22%5D)",
|
||||
"setting.frontMatter.content.hideFmMessage.markdownDescription": "指定隐藏 Front Matter 时显示的消息。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefmMessage) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.hidefmMessage%22%5D)",
|
||||
"setting.frontMatter.content.pageFolders.markdownDescription": "此文件夹数组定义扩展程序可以检索或创建新页面的位置。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.pagefolders) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.pagefolders%22%5D)",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.title.description": "文件夹名称",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.path.description": "文件夹路径",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description": "排除子目录",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludePaths.description": "排除路径(例如 api, _*.*)",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.previewPath.description": "为文件夹定义自定义预览路径。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description": "指定是否在预览 URL 中添加尾部斜杠。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.filePrefix.description": "定义文件名的前缀。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.contentTypes.description": "定义当前位置可用的内容类型。如果未定义,则所有内容类型都可用。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.disableCreation.description": "禁止在文件夹中创建新内容。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.defaultLocale.description": "设置页面文件夹的默认区域设置 ID。此文件夹中的所有内容均可翻译为 `frontMatter.content.i18n` 设置中定义的语言。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.locales.description": "定义页面文件夹的区域设置。这将用于内容翻译。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.slugTemplate.description": "定义自定义别名模板。",
|
||||
"setting.frontMatter.content.i18n.markdownDescription": "指定要用于网站的区域设置。此设置可在页面文件夹级别覆盖。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.i18n%22%5D)",
|
||||
"setting.frontMatter.content.i18n.items.properties.title.description": "区域设置标题",
|
||||
"setting.frontMatter.content.i18n.items.properties.locale.description": "区域设置代码",
|
||||
"setting.frontMatter.content.i18n.items.properties.path.description": "区域设置文件夹的相对路径",
|
||||
"setting.frontMatter.content.placeholders.markdownDescription": "此占位符数组定义了可在内容类型和模板中使用的占位符,用于自动填充内容的前端元数据。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.placeholders) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.placeholders%22%5D)",
|
||||
"setting.frontMatter.content.placeholders.items.properties.id.description": "占位符 ID,在内容类型或模板中按如下方式使用:{{placeholder}}",
|
||||
"setting.frontMatter.content.placeholders.items.properties.value.description": "占位符的值",
|
||||
"setting.frontMatter.content.placeholders.items.properties.script.description": "用于获取占位符值的要执行的脚本",
|
||||
"setting.frontMatter.content.publicFolder.markdownDescription": "指定所有资源所在的文件夹名称。例如在 Hugo 中这是 `static` 文件夹。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.publicfolder) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.publicfolder%22%5D)",
|
||||
"setting.frontMatter.content.publicFolder.properties.path.description": "指定资源文件夹的路径",
|
||||
"setting.frontMatter.content.publicFolder.properties.relative.description": "定义媒体文件的路径是否相对于内容文件?",
|
||||
"setting.frontMatter.snippets.wrapper.enabled.markdownDescription": "指定是否包装片段。[文档](https://frontmatter.codes/docs/settings/overview#frontMatter.snippets.wrapper.enabled) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.snippets.wrapper.enabled%22%5D)",
|
||||
"setting.frontMatter.content.snippets.markdownDescription": "定义要在内容中使用的片段。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.snippets) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.snippets%22%5D)",
|
||||
"setting.frontMatter.content.grouping.markdownDescription": "指定仪表盘内容的分组选项。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.grouping) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.grouping%22%5D)",
|
||||
"setting.frontMatter.content.grouping.items.properties.id.description": "分组选项的 ID。",
|
||||
"setting.frontMatter.content.grouping.items.properties.title.description": "将在 UI 中显示的分组标题。",
|
||||
"setting.frontMatter.content.grouping.items.properties.name.description": "要按之分组的 content-type 字段名称。",
|
||||
"setting.frontMatter.content.sorting.markdownDescription": "定义仪表盘内容的排序选项。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.sorting) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.sorting%22%5D)",
|
||||
"setting.frontMatter.content.sorting.items.properties.id.description": "排序选项的 ID。将用于存储上次使用的排序选项或默认选项。",
|
||||
"setting.frontMatter.content.sorting.items.properties.title.description": "排序标签的名称",
|
||||
"setting.frontMatter.content.sorting.items.properties.name.description": "要按之排序的元数据字段名称",
|
||||
"setting.frontMatter.content.sorting.items.properties.order.description": "排序顺序",
|
||||
"setting.frontMatter.content.sorting.items.properties.type.description": "字段值的类型",
|
||||
"setting.frontMatter.content.supportedFileTypes.markdownDescription": "指定要在 Front Matter 中使用的文件类型。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.supportedfiletypes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.supportedfiletypes%22%5D)",
|
||||
"setting.frontMatter.content.wysiwyg.markdownDescription": "指定是否启用所见即所得 (WYSIWYG) Markdown 控件。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.wysiwyg) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.wysiwyg%22%5D)",
|
||||
"setting.frontMatter.content.filters.markdownDescription": "指定内容仪表盘要使用的筛选器。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.content.filters) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.filters%22%5D)",
|
||||
"setting.frontMatter.custom.scripts.markdownDescription": "指定要执行的 Node.js 脚本的路径。当前文件路径将作为参数提供。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.custom.scripts) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.custom.scripts%22%5D)",
|
||||
"setting.frontMatter.custom.scripts.items.properties.id.description": "脚本的 ID。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.title.description": "要为脚本指定的标题。将显示为按钮的标题。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.script.description": "要执行的脚本路径",
|
||||
"setting.frontMatter.custom.scripts.items.properties.nodeBin.description": "node 可执行文件的路径。使用 NVM 时需要此选项,以避免混淆使用哪个 node 版本。(已弃用:改用 command 属性)",
|
||||
"setting.frontMatter.custom.scripts.items.properties.bulk.description": "对所有内容文件运行脚本",
|
||||
"setting.frontMatter.custom.scripts.items.properties.output.description": "定义脚本输出的位置。默认为通知,但可以指定在编辑器面板中显示。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.outputType.description": "编辑器面板的输出类型。例如,可更改为 'markdown'",
|
||||
"setting.frontMatter.custom.scripts.items.properties.type.description": "脚本将使用的类型。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.command.description": "要执行的脚本类型。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.hidden.description": "从 UI 中隐藏操作",
|
||||
"setting.frontMatter.custom.scripts.items.properties.environments.items.properties.type.description": "需要使用脚本的环境类型",
|
||||
"setting.frontMatter.custom.scripts.items.properties.environments.items.properties.script.description": "要执行的脚本路径",
|
||||
"setting.frontMatter.custom.scripts.items.properties.contentTypes.description": "定义脚本将使用的内容类型。如果未定义任何类型,则对所有类型可用。",
|
||||
"setting.frontMatter.dashboard.content.pagination.markdownDescription": "指定是否为内容启用分页。最多可定义页码为 52。默认每页项目数为 `16`。通过设置为 `false` 可禁用分页。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.pagination) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.content.pagination%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.cardTags.markdownDescription": "指定将用于在内容卡片上显示标签的元数据字段名称。为空或 null 时,将从卡片中隐藏标签。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.cardtags) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.content.cardtags%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.state.markdownDescription": "指定是否在内容卡片视图上显示状态/草稿状态。[文档](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.state) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.state%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.date.markdownDescription": "指定是否在内容卡片视图上显示日期。[文档](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.date) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.date%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.description.markdownDescription": "指定将用于在内容卡片上显示描述的元数据字段名称。[文档](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.description) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.description%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.title.markdownDescription": "指定将用于在内容卡片上显示标题的元数据字段名称。[文档](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.title) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.title%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.markdownDescription": "指定自定义媒体插入标记的片段。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.mediasnippet) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.mediasnippet%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.items.description": "在片段中使用 `{mediaUrl}`、`{caption}`、`{alt}`、`{filename}`、`{mediaHeight}` 和 `{mediaWidth}` 占位符以自动插入媒体信息。",
|
||||
"setting.frontMatter.dashboard.openOnStart.markdownDescription": "指定启动 VS Code 时是否打开仪表盘。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.openonstart) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.openonstart%22%5D)",
|
||||
"setting.frontMatter.data.files.markdownDescription": "指定要用于网站的数据文件。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.data.files) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.files%22%5D)",
|
||||
"setting.frontMatter.data.files.items.properties.id.description": "要为数据文件使用的唯一 ID。",
|
||||
"setting.frontMatter.data.files.items.properties.title.description": "要为数据文件指定的标题。",
|
||||
"setting.frontMatter.data.files.items.properties.labelField.description": "要用作数据条目标签的字段。",
|
||||
"setting.frontMatter.data.files.items.properties.file.description": "要加载的文件路径。仅支持 JSON 或 YAML 文件。",
|
||||
"setting.frontMatter.data.files.items.properties.fileType.description": "定义如何解析文件。默认为 JSON。",
|
||||
"setting.frontMatter.data.files.items.properties.schema.description": "数据的 JSON 模式,将用于渲染数据表单。",
|
||||
"setting.frontMatter.data.files.items.properties.schema.properties.title.description": "表单标题。",
|
||||
"setting.frontMatter.data.files.items.properties.schema.properties.type.description": "定义表单的类型。默认为 'object'。",
|
||||
"setting.frontMatter.data.files.items.properties.schema.properties.required.description": "定义表单的必填字段。",
|
||||
"setting.frontMatter.data.files.items.properties.schema.properties.properties.description": "定义表单的字段。",
|
||||
"setting.frontMatter.data.files.items.properties.type.description": "如果使用数据类型,可指定类型 ID。",
|
||||
"setting.frontMatter.data.files.items.properties.singleEntry.description": "是否要对数据文件使用单个条目。",
|
||||
"setting.frontMatter.data.folders.markdownDescription": "指定要用于网站的数据文件夹。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.data.folders) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.folders%22%5D)",
|
||||
"setting.frontMatter.data.folders.items.properties.id.description": "要为数据文件夹使用的唯一 ID。",
|
||||
"setting.frontMatter.data.folders.items.properties.labelField.description": "要用作数据条目标签的字段。",
|
||||
"setting.frontMatter.data.folders.items.properties.path.description": "要加载文件的文件夹路径。",
|
||||
"setting.frontMatter.data.folders.items.properties.type.description": "如果使用数据类型,可指定类型 ID。",
|
||||
"setting.frontMatter.data.folders.items.properties.singleEntry.description": "是否要对文件夹中的数据文件使用单个条目。",
|
||||
"setting.frontMatter.data.folders.items.properties.enableFileCreation.description": "启用文件夹中创建新数据文件。",
|
||||
"setting.frontMatter.data.folders.items.properties.fileType.description": "启用文件创建时定义文件类型。默认为 JSON。",
|
||||
"setting.frontMatter.data.types.markdownDescription": "指定数据类型。这些类型可用于数据文件。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.data.types) - [在 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": "要为数据类型使用的唯一 ID。",
|
||||
"setting.frontMatter.file.preserveCasing.markdownDescription": "指定是否保留文件名的标题大小写。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.file.preservecasing) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.file.preservecasing%22%5D)",
|
||||
"setting.frontMatter.framework.id.markdownDescription": "指定用于网站的静态站点生成器或框架的 ID。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.id) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.id%22%5D)",
|
||||
"setting.frontMatter.framework.startCommand.markdownDescription": "指定用于启动静态站点生成器或框架的命令。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.startcommand) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.startcommand%22%5D)",
|
||||
"setting.frontMatter.git.enabled.markdownDescription": "指定是否要为网站使用 Git 操作。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.enabled) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.enabled%22%5D)",
|
||||
"setting.frontMatter.git.commitMessage.markdownDescription": "指定同步要使用的提交消息。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.commitmessage) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.commitmessage%22%5D)",
|
||||
"setting.frontMatter.git.submodule.pull.markdownDescription": "指定同步时是否拉取子模块。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.pull) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.pull%22%5D)",
|
||||
"setting.frontMatter.git.submodule.push.markdownDescription": "指定同步时是否推送子模块。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.push) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.push%22%5D)",
|
||||
"setting.frontMatter.git.submodule.branch.markdownDescription": "指定要签出的子模块分支。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.branch) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.branch%22%5D)",
|
||||
"setting.frontMatter.git.submodule.folder.markdownDescription": "指定内容的子模块文件夹,当使用多个子模块时可能有用。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.folder) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.folder%22%5D)",
|
||||
"setting.frontMatter.global.activeMode.markdownDescription": "指定 Front Matter 的激活模式。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.global.activemode) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.activemode%22%5D)",
|
||||
"setting.frontMatter.global.modes.markdownDescription": "指定要用于 Front Matter 的模式。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.global.modes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.modes%22%5D)",
|
||||
"setting.frontMatter.global.modes.items.properties.id.description": "模式的 ID。",
|
||||
"setting.frontMatter.global.modes.items.properties.features.description": "要为模式使用的功能。",
|
||||
"setting.frontMatter.global.notifications.markdownDescription": "指定要查看的通知类型。默认情况下,将显示所有通知类型。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.global.notifications) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.notifications%22%5D)",
|
||||
"setting.frontMatter.global.disabledNotifications.markdownDescription": "这是一个包含可为 Front Matter CMS 禁用的通知类型的数组。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.disablednotifications%22%5D)",
|
||||
"setting.frontMatter.global.timezone.markdownDescription": "指定日期格式化的时区。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.datetimezone) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.datetimezone%22%5D)",
|
||||
"setting.frontMatter.media.defaultSorting.markdownDescription": "指定媒体仪表盘的默认排序选项。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.media.defaultsorting) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.defaultsorting%22%5D)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "指定媒体文件支持的 MIME 类型。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.supportedmimetypes%22%5D)",
|
||||
|
||||
"setting.frontMatter.media.contentTypes.markdownDescription": "指定要在 Front Matter 中使用的媒体内容类型。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.media.contenttypes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.contenttypes%22%5D)",
|
||||
"setting.frontMatter.media.contentTypes.items.description": "定义要在 Front Matter 中使用的媒体内容类型。",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.name.description": "媒体内容类型的名称",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fileTypes.description": "指定媒体内容类型允许的文件类型",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.description": "定义媒体内容类型的字段",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.title.description": "在 UI 中显示的标题",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.name.description": "要使用的字段名称",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.type.description": "定义字段类型",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.single.description": "是否为单行字段",
|
||||
|
||||
"setting.frontMatter.panel.openOnSupportedFile.markdownDescription": "指定打开支持的文件时是否打开面板。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.openonsupportedfile) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.openonsupportedfile%22%5D)",
|
||||
"setting.frontMatter.panel.freeform.markdownDescription": "指定是否允许在标签选择器中输入未知标签/分类(启用后,之后将可以选择存储它们)。默认值:true。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.freeform%22%5D)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "指定要在面板中禁用的操作。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.actions.disabled%22%5D)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "指定打开预览时要使用的主机 URL(例如:http://localhost:1313)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.host%22%5D)",
|
||||
"setting.frontMatter.preview.trailingSlash.markdownDescription": "指定是否在预览 URL 中添加尾部斜杠。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.trailingslash) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.trailingslash%22%5D)",
|
||||
"setting.frontMatter.preview.pathName.markdownDescription": "指定在主机和别名之间要添加的路径。例如,可用于包含年份/月份,如:`yyyy/MM`。日期将根据文章的日期字段值生成。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.pathname) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.pathname%22%5D)",
|
||||
"setting.frontMatter.site.baseURL.markdownDescription": "指定网站的基本 URL,这将用于 SEO 检查。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.site.baseurl) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.site.baseurl%22%5D)",
|
||||
"setting.frontMatter.taxonomy.alignFilename.markdownDescription": "生成新别名时使文件名与新别名对齐。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.alignfilename) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.alignfilename%22%5D)",
|
||||
"setting.frontMatter.taxonomy.categories.markdownDescription": "指定可在 Front Matter 中使用的分类。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.categories) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.categories%22%5D)",
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.markdownDescription": "指定 Front Matter 应视为逗号分隔数组的字段名称。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.commaseparatedfields) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.commaseparatedfields%22%5D)",
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.items.description": "要用作逗号分隔数组的字段名称。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.markdownDescription": "指定要用于文章/页面等的内容类型。确保在 front matter 中正确设置了 `type`。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.contenttypes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.contenttypes%22%5D)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.description": "定义要在 Front Matter 中使用的内容类型。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.name.description": "定义字段类型",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fileType.description": "指定要创建的内容类型。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.description": "定义内容类型的字段",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.description": "定义要在 Front Matter 中使用的内容类型。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.type.description": "定义字段类型",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.name.description": "要使用的字段名称",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.title.description": "在 UI 中显示的标题",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.description.description": "在 UI 中显示的描述",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.default.description": "默认值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.choices.description": "定义您的选项",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.choices.items.properties.id.description": "选项 ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.choices.items.properties.title.description": "选项标题",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.single.description": "是否为单行字段",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.wysiwyg.description": "是否为所见即所得字段。可设置为 markdown 或 HTML。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.multiple.description": "是否允许多选?",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.isPreviewImage.description": "指定图像字段是否可用作预览。注意,每个内容类型只能有一个预览图像。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.hidden.description": "是否要从元数据部分隐藏该字段?",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description": "分类法字段的 ID。不能包含 \"tags\" 或 \"categories\" 值。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.fileExtensions.description": "指定文件选择器允许的文件扩展名",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.fieldGroup.description": "在 `frontMatter.taxonomy.fieldGroups` 设置中定义的字段组 ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataType.description": "在 `frontMatter.data.types` 设置中定义的数据类型 ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.numberOptions.description": "指定数字字段的选项",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.numberOptions.properties.isDecimal.description": "指定数字是否为小数",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.numberOptions.properties.min.description": "最小值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.numberOptions.properties.max.description": "最大值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.numberOptions.properties.step.description": "步进值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyLimit.description": "限制可选择分类法的数量。设为 0 表示无限制。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.singleValueAsString.description": "指定是否将单个数组值存储为字符串而非数组。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.isPublishDate.description": "指定该字段是否为发布日期字段",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.isModifiedDate.description": "指定该字段是否为修改日期字段",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileId.description": "指定用于此字段的数据文件 ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileKey.description": "指定用于此字段的数据文件键",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileValue.description": "指定将用于显示字段值的属性名称",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.editable.description": "指定字段是否可编辑",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.encodeEmoji.description": "指定字段是否应编码表情符号",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dateFormat.description": "指定要使用的日期格式",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.required.description": "指定字段是否为必填项",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeName.description": "指定用于筛选内容的内容类型名称(用于内容关联字段)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeValue.description": "指定要插入内容关联字段的值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.sameContentLocale.description": "指定是否仅显示相同区域设置的内容",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.description": "指定显示字段的条件",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.fieldRef.description": "要使用的字段 ID",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.operator.description": "要使用的运算符",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.value.description": "要比较的值",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "指定比较是否区分大小写。默认值:true",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.actions.description": "指定字段自定义操作",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "指定创建新内容时是否创建文件夹。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "为内容类型定义自定义预览路径。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description": "指定是否在预览 URL 中添加尾部斜杠。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.slugTemplate.description": "为内容类型定义自定义别名模板。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.template.description": "可用于创建新内容的可选模板。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.postScript.description": "可在新内容创建后使用的可选后置脚本。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.filePrefix.description": "定义文件名的前缀。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.defaultFileName.description": "创建新内容时使用的默认文件名。",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.markdownDescription": "指定自定义分类法字段数据。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.tags%22%5D)",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description": "分类法字段的 ID。不能包含 \"tags\" 或 \"categories\" 值。",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.items.properties.options.description": "可供选择的选项。",
|
||||
"setting.frontMatter.taxonomy.dateField.markdownDescription": "此设置用于定义文章的发布日期字段。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.datefield) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.datefield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.dateFormat.markdownDescription": "指定文章的日期格式。查看 [date-fns 格式](https://date-fns.org/v2.0.1/docs/format) 获取更多信息。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.dateformat) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.dateformat%22%5D)",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.markdownDescription": "定义要用于区块字段或集合字段的字段组。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.fieldgroups) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.fieldgroups%22%5D)",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.items.properties.id.description": "字段组的名称",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.items.properties.labelField.description": "要用作显示值的字段名称",
|
||||
"setting.frontMatter.taxonomy.frontMatterType.markdownDescription": "指定要使用的 Front Matter 类型。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.frontmattertype) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.frontmattertype%22%5D)",
|
||||
"setting.frontMatter.taxonomy.indentArrays.markdownDescription": "指定前端元数据中的数组是否缩进。默认值:true。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.indentarrays) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.indentarrays%22%5D)",
|
||||
"setting.frontMatter.taxonomy.modifiedField.markdownDescription": "此设置用于定义文章的修改日期字段。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.modifiedfield) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.modifiedfield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.quoteStringValues.markdownDescription": "指定是否引用前端元数据中的字符串值。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.quotestringvalues) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.quotestringvalues%22%5D)",
|
||||
"setting.frontMatter.taxonomy.noPropertyValueQuotes.markdownDescription": "指定需要移除引号的属性。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.nopropertyvaluequotes) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.nopropertyvaluequotes%22%5D)",
|
||||
"setting.frontMatter.taxonomy.noPropertyValueQuotes.items.description": "要移除引号的属性名称。",
|
||||
"setting.frontMatter.taxonomy.seoContentLengh.markdownDescription": "指定文章的最佳最小长度。1,760 到 2,400 词是 SEO(2021年) 的绝对理想文章长度(设为 `-1` 可关闭)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seocontentlengh) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seocontentlengh%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionField.markdownDescription": "指定页面的 SEO 描述字段名称。默认为 'description'。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionfield) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seodescriptionfield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionLength.markdownDescription": "指定 SEO 的最佳描述长度(设为 `-1` 可关闭)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionlength) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seodescriptionlength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoSlugLength.markdownDescription": "指定 SEO 的最佳别名长度(设为 `-1` 可关闭)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seosluglength) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seosluglength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoTitleField.markdownDescription": "指定页面的 SEO 标题字段名称。默认为 'title'。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlefield) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seotitlefield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoTitleLength.markdownDescription": "指定 SEO 的最佳标题长度(设为 `-1` 可关闭)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlelength) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seotitlelength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugPrefix.markdownDescription": "指定别名的前缀。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugprefix) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugprefix%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugSuffix.markdownDescription": "指定别名的后缀。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugsuffix) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugsuffix%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugTemplate.markdownDescription": "定义要创建内容的自定义别名模板。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugtemplate) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugtemplate%22%5D)",
|
||||
"setting.frontMatter.taxonomy.tags.markdownDescription": "指定可在 Front Matter 中使用的标签。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.tags%22%5D)",
|
||||
"setting.frontMatter.telemetry.disable.markdownDescription": "指定是否禁用遥测。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.telemetry.disable) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.telemetry.disable%22%5D)",
|
||||
"setting.frontMatter.templates.enabled.markdownDescription": "指定是否使用模板。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.enabled) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.enabled%22%5D)",
|
||||
"setting.frontMatter.templates.folder.markdownDescription": "指定文章模板要使用的文件夹。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.folder) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.folder%22%5D)",
|
||||
"setting.frontMatter.templates.prefix.markdownDescription": "指定要为新文章文件名添加的前缀。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.prefix) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.prefix%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.deprecationMessage": "此设置已弃用,将在下一个主要版本中移除。请在 `frontMatter.content.snippet` 设置中定义媒体片段。",
|
||||
"setting.frontMatter.taxonomy.dateField.deprecationMessage": "此设置已弃用,将在下一个主要版本中移除。请在内容类型的日期字段中使用新的 `isPublishDate` 设置。",
|
||||
"setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "此设置已弃用,将在下一个主要版本中移除。请在内容类型的日期字段中使用新的 `isModifiedDate` 设置。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "指定要使用的自定义字段类型的名称。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.clearEmpty.description": "指定是否应清除空值。",
|
||||
"setting.frontMatter.website.host.markdownDescription": "指定网站的主机 URL。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.website.url%22%5D)",
|
||||
"command.frontMatter.settings.refresh": "刷新 Front Matter 设置",
|
||||
"setting.frontMatter.config.dynamicFilePath.markdownDescription": "指定动态配置文件的路径(例如:[[workspace]]/config.js)。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.config.dynamicfilepath%22%5D)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.allowAsSubContent.description": "指定内容类型是否可用作子内容。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "指定内容类型是否为子内容。",
|
||||
|
||||
"setting.frontMatter.git.disableOnBranches.markdownDescription": "指定要禁用 Git 操作的分支。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.disableonbranches%22%5D)",
|
||||
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "指定在发布指定分支的更改时是否需要提交消息。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.requirescommitmessage%22%5D)",
|
||||
"setting.frontMatter.copilot.family.markdownDescription": "指定要使用的 Copilot 的 LLM 系列。[文档](https://frontmatter.codes/docs/settings/overview#frontmatter.copilot.family) - [在 VS Code 中查看](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.copilot.family%22%5D)"
|
||||
}
|
||||
@@ -38,6 +38,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
SETTING_SLUG_TEMPLATE
|
||||
} from './../constants';
|
||||
import { CustomPlaceholder, Field } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Logger,
|
||||
@@ -44,7 +43,7 @@ import { NavigationType } from '../dashboardWebView/models';
|
||||
import { SNIPPET } from '../constants/Snippet';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { getTitleField } from '../utils';
|
||||
import { formatInTimezone, getTitleField } from '../utils';
|
||||
|
||||
export class Article {
|
||||
/**
|
||||
@@ -52,7 +51,7 @@ export class Article {
|
||||
*
|
||||
* @param subscriptions - The array of subscriptions to register the commands with.
|
||||
*/
|
||||
public static async registerCommands(subscriptions: unknown[]) {
|
||||
public static registerCommands(subscriptions: unknown[]) {
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.setLastModifiedDate, Article.setLastModifiedDate)
|
||||
);
|
||||
@@ -66,6 +65,15 @@ export class Article {
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.insertSnippet, Article.insertSnippet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers event listeners for the Article class.
|
||||
*
|
||||
* @param subscriptions - An array to which the event listener will be added.
|
||||
*/
|
||||
public static registerListeners(subscriptions: unknown[]) {
|
||||
subscriptions.push(workspace.onWillSaveTextDocument(Article.autoUpdate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the article date
|
||||
*/
|
||||
@@ -164,7 +172,12 @@ export class Article {
|
||||
/**
|
||||
* Generate the new slug
|
||||
*/
|
||||
public static generateSlug(title: string, article?: ParsedFrontMatter, slugTemplate?: string) {
|
||||
public static generateSlug(
|
||||
title: string,
|
||||
article?: ParsedFrontMatter,
|
||||
filePath?: string,
|
||||
slugTemplate?: string
|
||||
) {
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
@@ -173,9 +186,9 @@ export class Article {
|
||||
const suffix = Settings.get(SETTING_SLUG_SUFFIX) as string;
|
||||
|
||||
if (article?.data) {
|
||||
const slug = SlugHelper.createSlug(title, article?.data, slugTemplate);
|
||||
const slug = SlugHelper.createSlug(title, article?.data, filePath, slugTemplate);
|
||||
|
||||
if (slug) {
|
||||
if (typeof slug === 'string') {
|
||||
return {
|
||||
slug,
|
||||
slugWithPrefixAndSuffix: `${prefix}${slug}${suffix}`
|
||||
@@ -202,19 +215,32 @@ 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 = getTitleField();
|
||||
const articleTitle: string = article.data[titleField];
|
||||
const slugInfo = Article.generateSlug(articleTitle, article, contentType.slugTemplate);
|
||||
const slugInfo = Article.generateSlug(
|
||||
articleTitle,
|
||||
article,
|
||||
editor.document.uri.fsPath,
|
||||
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) {
|
||||
@@ -239,7 +265,8 @@ export class Article {
|
||||
article.data[pField.name] = processArticlePlaceholdersFromData(
|
||||
article.data[pField.name],
|
||||
article.data,
|
||||
contentType
|
||||
contentType,
|
||||
editor.document.uri.fsPath
|
||||
);
|
||||
article.data[pField.name] = processTimePlaceholders(
|
||||
article.data[pField.name],
|
||||
@@ -264,7 +291,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);
|
||||
@@ -315,7 +346,7 @@ export class Article {
|
||||
} else {
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article?.data) {
|
||||
return SlugHelper.createSlug(article.data[titleField], article.data, slugTemplate);
|
||||
return SlugHelper.createSlug(article.data[titleField], article.data, file, slugTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +388,7 @@ export class Article {
|
||||
* Article auto updater
|
||||
* @param event
|
||||
*/
|
||||
public static async autoUpdate(event: TextDocumentWillSaveEvent) {
|
||||
public static autoUpdate(event: TextDocumentWillSaveEvent) {
|
||||
const document = event.document;
|
||||
if (document && ArticleHelper.isSupportedFile(document)) {
|
||||
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
|
||||
@@ -365,7 +396,7 @@ export class Article {
|
||||
// Is article located in one of the content folders
|
||||
const folders = Folders.getCached();
|
||||
const documentPath = parseWinPath(document.fileName);
|
||||
const folder = folders.find((f) => documentPath.startsWith(f.path));
|
||||
const folder = folders?.find((f) => documentPath.startsWith(f.path));
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
@@ -386,10 +417,10 @@ export class Article {
|
||||
|
||||
if (fieldDateFormat) {
|
||||
Logger.verbose(`Article:formatDate:FieldDateFormat - ${fieldDateFormat}`);
|
||||
return format(dateValue, DateHelper.formatUpdate(fieldDateFormat) as string);
|
||||
return formatInTimezone(dateValue, DateHelper.formatUpdate(fieldDateFormat) as string);
|
||||
} else if (dateFormat && typeof dateFormat === 'string') {
|
||||
Logger.verbose(`Article:formatDate:DateFormat - ${dateFormat}`);
|
||||
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
|
||||
return formatInTimezone(dateValue, DateHelper.formatUpdate(dateFormat) as string);
|
||||
} else {
|
||||
Logger.verbose(`Article:formatDate:toISOString - ${dateValue}`);
|
||||
return typeof dateValue.toISOString === 'function'
|
||||
|
||||
@@ -6,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 {
|
||||
/**
|
||||
@@ -32,31 +33,36 @@ export class Chatbot {
|
||||
|
||||
const cspSource = webView.webview.cspSource;
|
||||
|
||||
const fetchLocalization = async (requestId: string) => {
|
||||
if (!requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileContents = await getLocalizationFile();
|
||||
|
||||
webView.webview.postMessage({
|
||||
command: GeneralCommands.toVSCode.getLocalization,
|
||||
requestId,
|
||||
payload: fileContents
|
||||
});
|
||||
};
|
||||
|
||||
webView.webview.onDidReceiveMessage(async (message) => {
|
||||
switch (message.command) {
|
||||
const { command, requestId, payload, data } = message;
|
||||
|
||||
switch (command) {
|
||||
case PreviewCommands.toVSCode.open:
|
||||
if (message.data) {
|
||||
commands.executeCommand('vscode.open', message.data);
|
||||
if (payload || data) {
|
||||
commands.executeCommand('vscode.open', payload || data);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case GeneralCommands.toVSCode.getLocalization:
|
||||
const { requestId } = message;
|
||||
if (!requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileContents = await getLocalizationFile();
|
||||
|
||||
webView.webview.postMessage({
|
||||
command: GeneralCommands.toVSCode.getLocalization,
|
||||
requestId,
|
||||
payload: fileContents
|
||||
});
|
||||
fetchLocalization(requestId);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
const dashboardFile = 'dashboardWebView.js';
|
||||
const webviewFile = 'dashboard.main.js';
|
||||
const localPort = `9000`;
|
||||
const localServerUrl = `localhost:${localPort}`;
|
||||
|
||||
@@ -66,7 +72,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';`,
|
||||
@@ -82,13 +87,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
|
||||
@@ -111,7 +114,11 @@ 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>
|
||||
`;
|
||||
|
||||
@@ -30,7 +30,7 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
|
||||
import { NavigationType } from '../dashboardWebView/models';
|
||||
import { getExtensibilityScripts, ignoreMsgCommand } from '../utils';
|
||||
import { getExtensibilityScripts, getWebviewJsFiles, ignoreMsgCommand } from '../utils';
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
@@ -274,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();
|
||||
@@ -351,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>
|
||||
|
||||
@@ -23,7 +23,6 @@ import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Extension, Logger, Settings, processTimePlaceholders } from '../helpers';
|
||||
import { existsSync } from 'fs';
|
||||
import { format } from 'date-fns';
|
||||
import { Dashboard } from './Dashboard';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { MediaHelpers } from '../helpers/MediaHelpers';
|
||||
@@ -31,7 +30,7 @@ import { MediaListener, PagesListener, SettingsListener } from '../listeners/das
|
||||
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { glob } from 'glob';
|
||||
import { mkdirAsync } from '../utils/mkdirAsync';
|
||||
import { existsAsync, isWindows, lstatAsync } from '../utils';
|
||||
import { existsAsync, formatInTimezone, isWindows, lstatAsync } from '../utils';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { Preview } from './Preview';
|
||||
@@ -39,7 +38,7 @@ import { Preview } from './Preview';
|
||||
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
|
||||
|
||||
export class Folders {
|
||||
private static _folders: ContentFolder[] = [];
|
||||
private static _folders: ContentFolder[] | undefined = undefined;
|
||||
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
@@ -50,7 +49,7 @@ export class Folders {
|
||||
|
||||
public static clearCached() {
|
||||
Logger.verbose(`Folders:clearCached`);
|
||||
Folders._folders = [];
|
||||
Folders._folders = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +84,7 @@ export class Folders {
|
||||
prompt: l10n.t(LocalizationKey.commandsFoldersAddMediaFolderInputBoxPrompt),
|
||||
value: startPath,
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: `${format(new Date(), `yyyy/MM`)}`
|
||||
placeHolder: `${formatInTimezone(new Date(), `yyyy/MM`)}`
|
||||
});
|
||||
|
||||
if (!folderName) {
|
||||
@@ -220,7 +219,9 @@ export class Folders {
|
||||
: Folders.getAbsFilePath(assetFolder);
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const relativePath = relative(parseWinPath(wsFolder.fsPath), parseWinPath(assetFolder));
|
||||
const relativePath = parseWinPath(
|
||||
relative(parseWinPath(wsFolder.fsPath), parseWinPath(assetFolder))
|
||||
);
|
||||
return relativePath === '' ? '/' : relativePath;
|
||||
}
|
||||
}
|
||||
@@ -327,7 +328,7 @@ export class Folders {
|
||||
public static async get(): Promise<ContentFolder[]> {
|
||||
Logger.verbose('Folders:get:start');
|
||||
|
||||
if (Folders._folders.length > 0) {
|
||||
if (Folders._folders && Folders._folders.length > 0) {
|
||||
Logger.verbose('Folders:get:end - cached folders');
|
||||
return Folders._folders;
|
||||
}
|
||||
@@ -401,8 +402,8 @@ export class Folders {
|
||||
folder.locales && folder.locales.length > 0 ? folder.locales : i18nSettings;
|
||||
|
||||
let defaultLocale;
|
||||
let sourcePath = folderPath;
|
||||
let localeFolders: ContentFolder[] = [];
|
||||
const sourcePath = folderPath;
|
||||
const localeFolders: ContentFolder[] = [];
|
||||
|
||||
if (i18nConfig && i18nConfig.length > 0) {
|
||||
for (const i18n of i18nConfig) {
|
||||
@@ -452,10 +453,23 @@ export class Folders {
|
||||
* Get the cached folder settings
|
||||
* @returns {ContentFolder[]} - The cached folder settings
|
||||
*/
|
||||
public static getCached(): ContentFolder[] {
|
||||
public static getCached(): ContentFolder[] | undefined {
|
||||
return Folders._folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the cached content folders if available, otherwise fetches fresh content folders.
|
||||
*
|
||||
* @returns {Promise<ContentFolder[]>} A promise that resolves to an array of content folders.
|
||||
*/
|
||||
public static async getCachedOrFresh(): Promise<ContentFolder[]> {
|
||||
if (Folders._folders && Folders._folders.length > 0) {
|
||||
return Folders._folders;
|
||||
}
|
||||
|
||||
return await Folders.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the folder settings
|
||||
* @param folders
|
||||
@@ -557,6 +571,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
|
||||
@@ -608,15 +638,23 @@ export class Folders {
|
||||
}
|
||||
}
|
||||
|
||||
// For Windows, we need to make sure the drive letter is lowercased for consistency
|
||||
if (isWindows()) {
|
||||
folders = folders.map((folder) => parseWinPath(folder));
|
||||
}
|
||||
|
||||
// Filter out the workspace folder
|
||||
if (wsFolder) {
|
||||
folders = folders.filter((folder) => folder !== wsFolder.fsPath);
|
||||
folders = folders.filter((folder) => folder !== parseWinPath(wsFolder.fsPath));
|
||||
}
|
||||
|
||||
const uniqueFolders = [...new Set(folders)];
|
||||
const relativeFolderPaths = uniqueFolders.map((folder) =>
|
||||
parseWinPath(relative(parseWinPath(wsFolder.fsPath), folder))
|
||||
);
|
||||
|
||||
Logger.verbose('Folders:getContentFolders:end');
|
||||
return uniqueFolders.map((folder) => relative(wsFolder?.path || '', folder));
|
||||
return relativeFolderPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -672,7 +710,7 @@ export class Folders {
|
||||
public static async getPageFolderByFilePath(
|
||||
filePath: string
|
||||
): Promise<ContentFolder | undefined> {
|
||||
const folders = Folders.getCached();
|
||||
const folders = await Folders.getCachedOrFresh();
|
||||
const parsedPath = parseWinPath(filePath);
|
||||
const pageFolderMatches = folders
|
||||
.filter((folder) => parsedPath && folder.path && parsedPath.includes(folder.path))
|
||||
@@ -703,7 +741,7 @@ export class Folders {
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = Folders.getCached();
|
||||
const folders = await Folders.getCachedOrFresh();
|
||||
let selectedFolder: ContentFolder | undefined;
|
||||
|
||||
// Try to find the folder by content type
|
||||
@@ -772,7 +810,11 @@ export class Folders {
|
||||
filePath = `*${fileType.startsWith('.') ? '' : '.'}${fileType}`;
|
||||
}
|
||||
|
||||
let foundFiles = await Folders.findFiles(filePath);
|
||||
let foundFiles = await Folders.findFiles(
|
||||
filePath,
|
||||
join(folderPath, folder.excludeSubdir ? '/' : '**'),
|
||||
folder.excludePaths
|
||||
);
|
||||
|
||||
// Make sure these file are coming from the folder path (this could be an issue in multi-root workspaces)
|
||||
foundFiles = foundFiles.filter((f) =>
|
||||
@@ -828,7 +870,7 @@ export class Folders {
|
||||
try {
|
||||
pattern = isWindows() ? parseWinPath(pattern) : pattern;
|
||||
const folders = await glob(pattern, {
|
||||
ignore: 'node_modules/**',
|
||||
ignore: '**/node_modules/**',
|
||||
dot: true
|
||||
});
|
||||
|
||||
@@ -860,12 +902,27 @@ export class Folders {
|
||||
* @param pattern
|
||||
* @returns
|
||||
*/
|
||||
private static async findFiles(pattern: string): Promise<Uri[]> {
|
||||
private static async findFiles(
|
||||
pattern: string,
|
||||
folderPath: string,
|
||||
excludePaths: string[] = []
|
||||
): Promise<Uri[]> {
|
||||
Logger.verbose(`Folders:findFiles:start - ${pattern}`);
|
||||
|
||||
try {
|
||||
pattern = isWindows() ? parseWinPath(pattern) : pattern;
|
||||
const files = await glob(pattern, { ignore: 'node_modules/**', dot: true });
|
||||
const files = await glob(pattern, {
|
||||
ignore: [
|
||||
'**/node_modules/**',
|
||||
...excludePaths.map((path) => {
|
||||
// path can be a folder name or a wildcard.
|
||||
// If its a folder name, we need to add a wildcard to the end
|
||||
path = path.includes('*') ? path : join(path, '**');
|
||||
return parseWinPath(join(folderPath, path));
|
||||
})
|
||||
],
|
||||
dot: true
|
||||
});
|
||||
const allFiles = (files || []).map((file) => Uri.file(file));
|
||||
Logger.verbose(`Folders:findFiles:end - ${allFiles.length}`);
|
||||
return allFiles;
|
||||
|
||||
@@ -29,7 +29,7 @@ import { ParsedFrontMatter } from '../parsers';
|
||||
import { getLocalizationFile } from '../utils/getLocalizationFile';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { getTitleField, joinUrl } from '../utils';
|
||||
import { getTitleField, getWebviewJsFiles, joinUrl } from '../utils';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
export class Preview {
|
||||
@@ -110,31 +110,36 @@ export class Preview {
|
||||
webView.dispose();
|
||||
});
|
||||
|
||||
const fetchLocalization = async (requestId: string) => {
|
||||
if (!requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileContents = await getLocalizationFile();
|
||||
|
||||
webView.webview.postMessage({
|
||||
command: GeneralCommands.toVSCode.getLocalization,
|
||||
requestId,
|
||||
payload: fileContents
|
||||
});
|
||||
};
|
||||
|
||||
webView.webview.onDidReceiveMessage(async (message) => {
|
||||
switch (message.command) {
|
||||
const { command, payload, requestId } = message;
|
||||
|
||||
switch (command) {
|
||||
case PreviewCommands.toVSCode.open:
|
||||
if (message.payload) {
|
||||
commands.executeCommand('vscode.open', message.payload);
|
||||
if (payload) {
|
||||
commands.executeCommand('vscode.open', payload);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case GeneralCommands.toVSCode.getLocalization:
|
||||
const { requestId } = message;
|
||||
if (!requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileContents = await getLocalizationFile();
|
||||
|
||||
webView.webview.postMessage({
|
||||
command: GeneralCommands.toVSCode.getLocalization,
|
||||
requestId,
|
||||
payload: fileContents
|
||||
});
|
||||
return;
|
||||
fetchLocalization(requestId);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const dashboardFile = 'dashboardWebView.js';
|
||||
const webviewFile = 'dashboard.main.js';
|
||||
const localPort = `9000`;
|
||||
const localServerUrl = `localhost:${localPort}`;
|
||||
|
||||
@@ -144,7 +149,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 +165,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,7 +195,11 @@ 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>
|
||||
`;
|
||||
@@ -310,7 +316,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);
|
||||
|
||||
@@ -42,7 +42,7 @@ categories: []
|
||||
|
||||
// Initialize command
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.init, async (cb: Function) => {
|
||||
commands.registerCommand(COMMAND_NAME.init, async (cb: () => void) => {
|
||||
await Project.init();
|
||||
|
||||
if (cb) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { ProgressLocation, Uri, commands, window, workspace } from 'vscode';
|
||||
import {
|
||||
ProgressLocation,
|
||||
QuickPickItem,
|
||||
QuickPickItemKind,
|
||||
QuickPickOptions,
|
||||
Uri,
|
||||
commands,
|
||||
window,
|
||||
workspace
|
||||
} from 'vscode';
|
||||
import {
|
||||
ArticleHelper,
|
||||
ContentType,
|
||||
@@ -16,8 +25,7 @@ import { existsAsync, getDescriptionField, getTitleField } from '../utils';
|
||||
import { Folders } from '.';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { PagesListener } from '../listeners/dashboard';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { LocalizationKey, localize } from '../localization';
|
||||
import { Translations } from '../services/Translations';
|
||||
|
||||
export class i18n {
|
||||
@@ -32,6 +40,7 @@ export class i18n {
|
||||
const subscriptions = Extension.getInstance().subscriptions;
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.i18n.create, i18n.create));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.i18n.createOrOpen, i18n.createOrOpen));
|
||||
|
||||
i18n.clearFiles();
|
||||
}
|
||||
@@ -264,7 +273,7 @@ export class i18n {
|
||||
}
|
||||
|
||||
if (!fileUri) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoFileSelected));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoFileSelected));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,19 +283,19 @@ export class i18n {
|
||||
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(fileUri.fsPath);
|
||||
if (!pageFolder || !pageFolder.localeSourcePath) {
|
||||
Notifications.error(l10n.t(LocalizationKey.commandsI18nCreateErrorNoContentFolder));
|
||||
Notifications.error(localize(LocalizationKey.commandsI18nCreateErrorNoContentFolder));
|
||||
return;
|
||||
}
|
||||
|
||||
const i18nSettings = await i18n.getSettings(fileUri.fsPath);
|
||||
if (!i18nSettings) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceLocale = await i18n.getLocale(fileUri.fsPath);
|
||||
if (!sourceLocale || !sourceLocale.locale) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateErrorNoLocaleDefinition));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateErrorNoLocaleDefinition));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,15 +309,15 @@ export class i18n {
|
||||
});
|
||||
|
||||
if (targetLocales.length === 0) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateErrorNoLocales));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateErrorNoLocales));
|
||||
return;
|
||||
}
|
||||
|
||||
const locale = await window.showQuickPick(
|
||||
targetLocales.map((i18n) => i18n.title || i18n.locale),
|
||||
{
|
||||
title: l10n.t(LocalizationKey.commandsI18nCreateQuickPickTitle),
|
||||
placeHolder: l10n.t(LocalizationKey.commandsI18nCreateQuickPickPlaceHolder),
|
||||
title: localize(LocalizationKey.commandsI18nCreateQuickPickTitle),
|
||||
placeHolder: localize(LocalizationKey.commandsI18nCreateQuickPickPlaceHolder),
|
||||
ignoreFocusOut: true
|
||||
}
|
||||
);
|
||||
@@ -321,19 +330,19 @@ export class i18n {
|
||||
(i18n) => i18n.title === locale || i18n.locale === locale
|
||||
);
|
||||
if (!targetLocale || !targetLocale.path) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
let article = await ArticleHelper.getFrontMatterByPath(fileUri.fsPath);
|
||||
if (!article) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoFile));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoFile));
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
if (!contentType) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoContentType));
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoContentType));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -365,7 +374,7 @@ export class i18n {
|
||||
|
||||
const newFilePath = join(i18nDir, fileInfo.base);
|
||||
if (await existsAsync(newFilePath)) {
|
||||
Notifications.error(l10n.t(LocalizationKey.commandsI18nCreateErrorFileExists));
|
||||
Notifications.error(localize(LocalizationKey.commandsI18nCreateErrorFileExists));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -384,7 +393,188 @@ export class i18n {
|
||||
PagesListener.refresh();
|
||||
|
||||
Notifications.info(
|
||||
l10n.t(
|
||||
localize(
|
||||
LocalizationKey.commandsI18nCreateSuccessCreated,
|
||||
sourceLocale.title || sourceLocale.locale
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the process of creating a new translation file if it doesn't exist,
|
||||
* or opening an existing translation file if it's already present.
|
||||
* @param filePath The path of the file where the new content file should be created or being switched to. Behaves like `create` if not provided.
|
||||
*/
|
||||
private static async createOrOpen(fileUri?: Uri | string) {
|
||||
if (!fileUri) {
|
||||
const filePath = ArticleHelper.getActiveFile();
|
||||
fileUri = filePath ? Uri.file(filePath) : undefined;
|
||||
}
|
||||
|
||||
if (!fileUri) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoFileSelected));
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof fileUri === 'string') {
|
||||
fileUri = Uri.file(fileUri);
|
||||
}
|
||||
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(fileUri.fsPath);
|
||||
if (!pageFolder || !pageFolder.localeSourcePath) {
|
||||
Notifications.error(localize(LocalizationKey.commandsI18nCreateErrorNoContentFolder));
|
||||
return;
|
||||
}
|
||||
|
||||
let article = await ArticleHelper.getFrontMatterByPath(fileUri.fsPath);
|
||||
if (!article) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoFile));
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
if (!contentType) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoContentType));
|
||||
return;
|
||||
}
|
||||
|
||||
const i18nSettings = await i18n.getSettings(fileUri.fsPath);
|
||||
if (!i18nSettings) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceLocale = await i18n.getLocale(fileUri.fsPath);
|
||||
if (!sourceLocale || !sourceLocale.locale) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateErrorNoLocaleDefinition));
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine translation file paths
|
||||
const fileInfo = parse(fileUri.fsPath);
|
||||
let pageBundleDir = '';
|
||||
if (await ArticleHelper.isPageBundle(fileUri.fsPath)) {
|
||||
const dir = ArticleHelper.getPageFolderFromBundlePath(fileUri.fsPath);
|
||||
pageBundleDir = fileUri.fsPath.replace(dir, '');
|
||||
pageBundleDir = join(parse(pageBundleDir).dir);
|
||||
}
|
||||
|
||||
// Gather target locales & metadata
|
||||
const translations = (await i18n.getTranslations(fileUri.fsPath)) || {};
|
||||
const targetLocales = i18nSettings
|
||||
.filter((i18n) => {
|
||||
return i18n.path && i18n.locale !== sourceLocale.locale;
|
||||
})
|
||||
.map((i18n) => {
|
||||
return {
|
||||
...i18n,
|
||||
dir: join(pageFolder.localeSourcePath!, i18n.path!, pageBundleDir),
|
||||
absolutePath: join(
|
||||
pageFolder.localeSourcePath!,
|
||||
i18n.path!,
|
||||
pageBundleDir,
|
||||
fileInfo.base
|
||||
),
|
||||
relativePath: join(i18n.path!, pageBundleDir, fileInfo.base)
|
||||
};
|
||||
})
|
||||
.sort((a, b) => (a.title || a.locale).localeCompare(b.title || b.locale));
|
||||
|
||||
if (targetLocales.length === 0) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateErrorNoLocales));
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure quick pick items & options
|
||||
const existingTargetLocales = targetLocales.filter((i18n) => translations[i18n.locale]);
|
||||
const newTargetLocales = targetLocales.filter((i18n) => !translations[i18n.locale]);
|
||||
const quickPickItems: QuickPickItem[] = [
|
||||
...(existingTargetLocales.length
|
||||
? [
|
||||
{
|
||||
label: localize(LocalizationKey.commandsI18nCreateOrOpenQuickPickCategoryExisting),
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...existingTargetLocales.map((i18n) => ({
|
||||
label: i18n.title || i18n.locale,
|
||||
detail: localize(
|
||||
LocalizationKey.commandsI18nCreateOrOpenQuickPickActionOpen,
|
||||
i18n.relativePath
|
||||
)
|
||||
}))
|
||||
]
|
||||
: []),
|
||||
...(newTargetLocales.length
|
||||
? [
|
||||
{
|
||||
label: localize(LocalizationKey.commandsI18nCreateOrOpenQuickPickCategoryNew),
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...newTargetLocales.map((i18n) => ({
|
||||
label: i18n.title || i18n.locale,
|
||||
detail: `$(file-add) ${localize(
|
||||
LocalizationKey.commandsI18nCreateOrOpenQuickPickActionCreate,
|
||||
i18n.relativePath
|
||||
)}`
|
||||
}))
|
||||
]
|
||||
: [])
|
||||
];
|
||||
const quickPickOptions: QuickPickOptions = {
|
||||
title: localize(LocalizationKey.commandsI18nCreateOrOpenQuickPickTitle),
|
||||
ignoreFocusOut: true,
|
||||
matchOnDetail: true
|
||||
};
|
||||
|
||||
const localeItem = await window.showQuickPick<QuickPickItem>(quickPickItems, quickPickOptions);
|
||||
const locale = localeItem?.label;
|
||||
if (!locale) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetLocale = targetLocales.find(
|
||||
(i18n) => i18n.title === locale || i18n.locale === locale
|
||||
);
|
||||
if (!targetLocale || !targetLocale.path) {
|
||||
Notifications.warning(localize(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
// If it exists, open the translation file
|
||||
if (await existsAsync(targetLocale.absolutePath)) {
|
||||
await openFileInEditor(targetLocale.absolutePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it doesn't exist, create the new translation file & update front matter
|
||||
if (!(await existsAsync(targetLocale.dir))) {
|
||||
await workspace.fs.createDirectory(Uri.file(targetLocale.dir));
|
||||
}
|
||||
|
||||
article = await i18n.updateFrontMatter(
|
||||
article,
|
||||
fileUri.fsPath,
|
||||
contentType,
|
||||
sourceLocale,
|
||||
targetLocale,
|
||||
targetLocale.dir
|
||||
);
|
||||
if (sourceLocale?.locale) {
|
||||
article = await i18n.translate(article, sourceLocale, targetLocale);
|
||||
}
|
||||
|
||||
const newFileUri = Uri.file(targetLocale.absolutePath);
|
||||
await workspace.fs.writeFile(
|
||||
newFileUri,
|
||||
Buffer.from(ArticleHelper.stringifyFrontMatter(article.content, article.data))
|
||||
);
|
||||
|
||||
await openFileInEditor(targetLocale.absolutePath);
|
||||
|
||||
PagesListener.refresh();
|
||||
|
||||
Notifications.info(
|
||||
localize(
|
||||
LocalizationKey.commandsI18nCreateSuccessCreated,
|
||||
sourceLocale.title || sourceLocale.locale
|
||||
)
|
||||
@@ -403,11 +593,11 @@ export class i18n {
|
||||
sourceLocale: I18nConfig,
|
||||
targetLocale: I18nConfig
|
||||
) {
|
||||
return new Promise<ParsedFrontMatter>(async (resolve) => {
|
||||
await window.withProgress(
|
||||
return new Promise<ParsedFrontMatter>((resolve) => {
|
||||
window.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: l10n.t(LocalizationKey.commandsI18nTranslateProgressTitle),
|
||||
title: localize(LocalizationKey.commandsI18nTranslateProgressTitle),
|
||||
cancellable: false
|
||||
},
|
||||
async () => {
|
||||
|
||||
26
src/components/common/Tooltip.tsx
Normal file
26
src/components/common/Tooltip.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { Tooltip as TT } from 'react-tooltip'
|
||||
|
||||
export interface ITooltipProps {
|
||||
id: string;
|
||||
render?: () => React.ReactNode;
|
||||
}
|
||||
|
||||
export const Tooltip: React.FunctionComponent<ITooltipProps> = ({
|
||||
id,
|
||||
render
|
||||
}: React.PropsWithChildren<ITooltipProps>) => {
|
||||
|
||||
const tooltipClasses = `!py-[2px] !px-[8px] !rounded-[3px] !border-[var(--vscode-editorHoverWidget-border)] !border !border-solid !bg-[var(--vscode-editorHoverWidget-background)] !text-[var(--vscode-editorHoverWidget-foreground)] !font-normal !opacity-100 shadow-[0_2px_8px_var(--vscode-widget-shadow)] text-left`;
|
||||
|
||||
return (
|
||||
<TT
|
||||
id={id}
|
||||
className={tooltipClasses}
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}}
|
||||
render={render} />
|
||||
);
|
||||
};
|
||||
@@ -30,6 +30,7 @@ function Num({
|
||||
<LabelField label={label} id={id} required={props.required} />
|
||||
|
||||
<input
|
||||
className='block w-full py-2 pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0'
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
max={max}
|
||||
|
||||
@@ -42,6 +42,7 @@ function Select({
|
||||
...props
|
||||
}: SelectFieldProps) {
|
||||
const multiple = fieldType === Array;
|
||||
|
||||
return (
|
||||
<div className="autoform__select_field" {...filterDOMProps(props)}>
|
||||
<LabelField label={label} id={id} required={required} />
|
||||
@@ -84,11 +85,12 @@ function Select({
|
||||
}}
|
||||
ref={inputRef}
|
||||
value={value ?? ''}
|
||||
className='text-[var(--vscode-foreground)] bg-[var(--vscode-list-activeSelectionBackground)] rounded-[2px] active:border-transparent disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none'
|
||||
style={{ width: '100%', padding: '0.5rem' }}
|
||||
>
|
||||
{(!!placeholder || !required || value === undefined) && !multiple && (
|
||||
{(!required || value === undefined) && !multiple && (
|
||||
<option value="" disabled={required} hidden={required}>
|
||||
{placeholder || label}
|
||||
{""}
|
||||
</option>
|
||||
)}
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ export const COMMAND_NAME = {
|
||||
|
||||
// i18n
|
||||
i18n: {
|
||||
create: getCommandName('i18n.create')
|
||||
create: getCommandName('i18n.create'),
|
||||
createOrOpen: getCommandName('i18n.createOrOpen')
|
||||
},
|
||||
|
||||
// Project
|
||||
|
||||
@@ -4,10 +4,10 @@ export const FEATURE_FLAG = {
|
||||
seo: 'panel.seo',
|
||||
actions: 'panel.actions',
|
||||
metadata: 'panel.metadata',
|
||||
recentlyModified: 'panel.recentlyModified',
|
||||
otherActions: 'panel.otherActions',
|
||||
contentType: 'panel.contentType',
|
||||
gitActions: 'panel.gitActions'
|
||||
gitActions: 'panel.gitActions',
|
||||
recentlyModified: 'panel.recentlyModified',
|
||||
otherActions: 'panel.otherActions'
|
||||
},
|
||||
dashboard: {
|
||||
snippets: {
|
||||
|
||||
@@ -13,6 +13,7 @@ export const SETTING_GLOBAL_NOTIFICATIONS = 'global.notifications';
|
||||
export const SETTING_GLOBAL_NOTIFICATIONS_DISABLED = 'global.disabledNotifications';
|
||||
export const SETTING_GLOBAL_MODES = 'global.modes';
|
||||
export const SETTING_GLOBAL_ACTIVE_MODE = 'global.activeMode';
|
||||
export const SETTING_GLOBAL_TIMEZONE = 'global.timezone';
|
||||
|
||||
export const SETTING_TAXONOMY_TAGS = 'taxonomy.tags';
|
||||
export const SETTING_TAXONOMY_CATEGORIES = 'taxonomy.categories';
|
||||
@@ -45,6 +46,7 @@ export const SETTING_TEMPLATES_FOLDER = 'templates.folder';
|
||||
export const SETTING_TEMPLATES_PREFIX = 'templates.prefix';
|
||||
export const SETTING_TEMPLATES_ENABLED = 'templates.enabled';
|
||||
|
||||
export const SETTING_PANEL_OPEN_ON_SUPPORTED_FILE = 'panel.openOnSupportedFile';
|
||||
export const SETTING_PANEL_FREEFORM = 'panel.freeform';
|
||||
export const SETTING_PANEL_ACTIONS_DISABLED = 'panel.actions.disabled';
|
||||
|
||||
@@ -61,6 +63,7 @@ export const SETTING_CONTENT_STATIC_FOLDER = 'content.publicFolder';
|
||||
export const SETTING_CONTENT_FRONTMATTER_HIGHLIGHT = 'content.fmHighlight';
|
||||
export const SETTING_CONTENT_DRAFT_FIELD = 'content.draftField';
|
||||
export const SETTING_CONTENT_SORTING = 'content.sorting';
|
||||
export const SETTING_CONTENT_GROUPING = 'content.grouping';
|
||||
export const SETTING_CONTENT_FILTERS = 'content.filters';
|
||||
export const SETTING_CONTENT_WYSIWYG = 'content.wysiwyg';
|
||||
export const SETTING_CONTENT_PLACEHOLDERS = 'content.placeholders';
|
||||
|
||||
@@ -42,6 +42,8 @@ export enum DashboardMessage {
|
||||
insertMedia = 'insertMedia',
|
||||
updateMediaMetadata = 'updateMediaMetadata',
|
||||
createMediaFolder = 'createMediaFolder',
|
||||
updateMediaFolder = 'updateMediaFolder',
|
||||
deleteMediaFolder = 'deleteMediaFolder',
|
||||
insertFile = 'insertFile',
|
||||
createHexoAssetFolder = 'createHexoAssetFolder',
|
||||
getUnmappedMedia = 'getUnmappedMedia',
|
||||
@@ -50,6 +52,7 @@ export enum DashboardMessage {
|
||||
// Data dashboard
|
||||
getDataEntries = 'getDataEntries',
|
||||
putDataEntries = 'putDataEntries',
|
||||
createDataFile = 'createDataFile',
|
||||
|
||||
// Snippets dashboard
|
||||
insertSnippet = 'insertSnippet',
|
||||
|
||||
@@ -54,7 +54,7 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.taxonomy.view);
|
||||
}, [mode?.features]);
|
||||
|
||||
const checkDevMode = (retry: number = 0) => {
|
||||
const checkDevMode = (retry = 0) => {
|
||||
if (!window.fmExternal) {
|
||||
if (retry < 5) {
|
||||
setTimeout(() => checkDevMode(retry + 1), 150);
|
||||
|
||||
@@ -11,10 +11,11 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { GeneralCommands, WEBSITE_LINKS } from '../../../constants';
|
||||
import { l10nJsonFormat } from '@vscode/l10n';
|
||||
|
||||
export interface IChatbotProps { }
|
||||
|
||||
export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.PropsWithChildren<IChatbotProps>) => {
|
||||
export const Chatbot: React.FunctionComponent<IChatbotProps> = () => {
|
||||
const { aiUrl } = useSettingsContext();
|
||||
const [company, setCompany] = React.useState<string | undefined>(undefined);
|
||||
const [chatId, setChatId] = React.useState<number | undefined>(undefined);
|
||||
@@ -27,7 +28,7 @@ export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.Props
|
||||
|
||||
const init = async () => {
|
||||
setLoading(true);
|
||||
messageHandler.request<any>(GeneralCommands.toVSCode.getLocalization).then((data) => {
|
||||
messageHandler.request<l10nJsonFormat>(GeneralCommands.toVSCode.getLocalization).then((data) => {
|
||||
if (data) {
|
||||
l10n.config({
|
||||
contents: data
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IButtonProps {
|
||||
secondary?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const Button: React.FunctionComponent<IButtonProps> = ({
|
||||
onClick,
|
||||
className,
|
||||
disabled,
|
||||
secondary,
|
||||
children
|
||||
}: React.PropsWithChildren<IButtonProps>) => {
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`${className || ''
|
||||
} inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium focus:outline-none rounded disabled:opacity-50 ${secondary ?
|
||||
`bg-[var(--vscode-button-secondaryBackground)] text-[--vscode-button-secondaryForeground] hover:bg-[var(--vscode-button-secondaryHoverBackground)]` :
|
||||
`bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)] hover:bg-[var(--frontmatter-button-hoverBackground)]`
|
||||
}
|
||||
`}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -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,11 @@ 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>) => {
|
||||
className={show ? "" : " shadow-[0_0_3px_var(--frontmatter-border-preserve)]"}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onChange={() => {
|
||||
onMultiSelect(filePath);
|
||||
}}
|
||||
checked={selectedFiles.includes(filePath)} />
|
||||
|
||||
68
src/dashboardWebView/components/Common/NumberField.tsx
Normal file
68
src/dashboardWebView/components/Common/NumberField.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { XCircleIcon } from '@heroicons/react/24/solid';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface INumberFieldProps {
|
||||
name: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
description?: string;
|
||||
icon?: JSX.Element;
|
||||
disabled?: boolean;
|
||||
autoFocus?: boolean;
|
||||
onChange?: (value: string) => void;
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
export const NumberField: React.FunctionComponent<INumberFieldProps> = ({
|
||||
name,
|
||||
value,
|
||||
placeholder,
|
||||
description,
|
||||
icon,
|
||||
autoFocus,
|
||||
disabled,
|
||||
onChange,
|
||||
onReset
|
||||
}: React.PropsWithChildren<INumberFieldProps>) => {
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex justify-center">
|
||||
{
|
||||
icon && (
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
{icon}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<input
|
||||
type="number"
|
||||
name={name}
|
||||
className={`block w-full py-2 ${icon ? "pl-10" : "pl-2"} pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
placeholder={placeholder || ""}
|
||||
value={value}
|
||||
autoFocus={!!autoFocus}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
|
||||
{(value && onReset) && (
|
||||
<button onClick={onReset} className="absolute inset-y-0 right-0 pr-3 flex items-center text-[var(--vscode-input-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]">
|
||||
<XCircleIcon className={`h-5 w-5`} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
description && (
|
||||
<p className="text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2">
|
||||
{description}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -17,6 +17,8 @@ export const List: React.FunctionComponent<IListProps> = ({
|
||||
className = `grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-5 gap-4`;
|
||||
} else if (view === DashboardViewType.List) {
|
||||
className = `-mx-4`;
|
||||
} else if (view === DashboardViewType.Structure) {
|
||||
className = `structure-view`;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,9 +9,9 @@ import { GroupOption } from '../../constants/GroupOption';
|
||||
import { GroupingSelector, PageAtom, PagedItems, ViewSelector } from '../../state';
|
||||
import { Item } from './Item';
|
||||
import { List } from './List';
|
||||
import { StructureView } from './StructureView';
|
||||
import usePagination from '../../hooks/usePagination';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import { PinnedItemsAtom } from '../../state/atom/PinnedItems';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
@@ -54,13 +54,18 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
|
||||
const groupName = useCallback(
|
||||
(groupId, groupedPages) => {
|
||||
const count = groupedPages[groupId].length;
|
||||
if (grouping === GroupOption.Draft) {
|
||||
return `${groupId} (${groupedPages[groupId].length})`;
|
||||
return `${groupId} (${count})`;
|
||||
} else if (typeof grouping === 'string') {
|
||||
const group = settings?.grouping?.find((g) => g.name === grouping);
|
||||
const prefix = group?.title ? `${group.title}: ` : '';
|
||||
return `${prefix}${groupId} (${count})`;
|
||||
}
|
||||
|
||||
return `${GroupOption[grouping]}: ${groupId} (${groupedPages[groupId].length})`;
|
||||
return `${GroupOption[grouping]}: ${groupId} (${count})`;
|
||||
},
|
||||
[grouping]
|
||||
[grouping, settings?.grouping]
|
||||
);
|
||||
|
||||
const { groupKeys, groupedPages } = useMemo(() => {
|
||||
@@ -68,7 +73,18 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
return { groupKeys: [], groupedPages: {} };
|
||||
}
|
||||
|
||||
let groupedPages = groupBy(pages, grouping === GroupOption.Year ? 'fmYear' : 'fmDraft');
|
||||
let groupName: string | undefined;
|
||||
if (grouping === GroupOption.Year) {
|
||||
groupName = 'fmYear';
|
||||
} else if (grouping === GroupOption.Draft) {
|
||||
groupName = 'fmDraft';
|
||||
} else if (typeof grouping === 'string') {
|
||||
groupName = grouping;
|
||||
} else {
|
||||
return { groupKeys: [], groupedPages: {} };
|
||||
}
|
||||
|
||||
let groupedPages = groupBy(pages, groupName);
|
||||
let groupKeys = Object.keys(groupedPages);
|
||||
|
||||
if (grouping === GroupOption.Year) {
|
||||
@@ -96,6 +112,8 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
...groupedPages,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
groupKeys = groupKeys.sort();
|
||||
}
|
||||
|
||||
return { groupKeys, groupedPages };
|
||||
@@ -127,15 +145,22 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
className={`h-32 mx-auto opacity-90 mb-8 text-[var(--vscode-editor-foreground)]`}
|
||||
/>
|
||||
{settings && settings?.contentFolders?.length > 0 ? (
|
||||
<p className={`text-xl font-medium`}>{l10n.t(LocalizationKey.dashboardContentsOverviewNoMarkdown)}</p>
|
||||
<p className={`text-xl font-medium`}>{localize(LocalizationKey.dashboardContentsOverviewNoMarkdown)}</p>
|
||||
|
||||
) : (
|
||||
<p className={`text-lg font-medium`}>{l10n.t(LocalizationKey.dashboardContentsOverviewNoFolders)}</p>
|
||||
<p className={`text-lg font-medium`}>{localize(LocalizationKey.dashboardContentsOverviewNoFolders)}</p>
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle Structure view first - it overrides all other display modes
|
||||
if (view === DashboardViewType.Structure) {
|
||||
return <StructureView pages={pages} />;
|
||||
}
|
||||
|
||||
if (grouping !== GroupOption.none) {
|
||||
return (
|
||||
<>
|
||||
@@ -176,7 +201,8 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
<div className='mb-8'>
|
||||
<h1 className='text-xl flex space-x-2 items-center mb-4'>
|
||||
<PinIcon className={`-rotate-45`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsOverviewPinned)}</span>
|
||||
<span>{localize(LocalizationKey.dashboardContentsOverviewPinned)}</span>
|
||||
|
||||
</h1>
|
||||
<List>
|
||||
{pinnedPages.map((page, idx) => (
|
||||
|
||||
79
src/dashboardWebView/components/Contents/StructureItem.tsx
Normal file
79
src/dashboardWebView/components/Contents/StructureItem.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
|
||||
import { Page } from '../../models/Page';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { DateField } from '../Common/DateField';
|
||||
import { ContentActions } from './ContentActions';
|
||||
import { useMemo } from 'react';
|
||||
import { Status } from './Status';
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useCard from '../../hooks/useCard';
|
||||
import { ItemSelection } from '../Common/ItemSelection';
|
||||
import { openFile } from '../../utils';
|
||||
import useSelectedItems from '../../hooks/useSelectedItems';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface IStructureItemProps extends Page { }
|
||||
|
||||
export const StructureItem: React.FunctionComponent<IStructureItemProps> = ({
|
||||
...pageData
|
||||
}: React.PropsWithChildren<IStructureItemProps>) => {
|
||||
const { selectedFiles } = useSelectedItems();
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const draftField = useMemo(() => settings?.draftField, [settings]);
|
||||
const { escapedTitle } = useCard(pageData, settings?.dashboardState?.contents?.cardFields);
|
||||
|
||||
const isSelected = useMemo(() => selectedFiles.includes(pageData.fmFilePath), [selectedFiles, pageData.fmFilePath]);
|
||||
|
||||
const onOpenFile = React.useCallback(() => {
|
||||
openFile(pageData.fmFilePath);
|
||||
}, [pageData.fmFilePath]);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div
|
||||
className={cn(
|
||||
`flex items-center space-x-3 py-1 px-2 rounded cursor-pointer hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-editor-foreground)]`,
|
||||
isSelected && `bg-[var(--vscode-list-activeSelectionBackground)]`
|
||||
)}
|
||||
>
|
||||
<ItemSelection filePath={pageData.fmFilePath} show />
|
||||
|
||||
<MarkdownIcon className="w-4 h-4 text-[var(--vscode-symbolIcon-fileForeground)] flex-shrink-0" />
|
||||
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={onOpenFile}
|
||||
className="flex-1 text-left truncate font-medium"
|
||||
>
|
||||
{escapedTitle}
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-2 flex-shrink-0">
|
||||
{pageData.date && (
|
||||
<DateField
|
||||
value={pageData.date}
|
||||
format={pageData.fmDateFormat}
|
||||
className="text-xs text-[var(--vscode-descriptionForeground)]"
|
||||
/>
|
||||
)}
|
||||
|
||||
{draftField && draftField.name && typeof pageData[draftField.name] !== "undefined" && (
|
||||
<Status draft={pageData[draftField.name]} published={pageData.fmPublished} />
|
||||
)}
|
||||
|
||||
<ContentActions
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={onOpenFile}
|
||||
listView
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
217
src/dashboardWebView/components/Contents/StructureView.tsx
Normal file
217
src/dashboardWebView/components/Contents/StructureView.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { ChevronRightIcon, FolderIcon } from '@heroicons/react/24/solid';
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Page } from '../../models';
|
||||
import { StructureItem } from './StructureItem';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
|
||||
export interface IStructureViewProps {
|
||||
pages: Page[];
|
||||
}
|
||||
|
||||
interface FolderNode {
|
||||
name: string;
|
||||
path: string;
|
||||
children: FolderNode[];
|
||||
pages: Page[];
|
||||
}
|
||||
|
||||
export const StructureView: React.FunctionComponent<IStructureViewProps> = ({
|
||||
pages
|
||||
}: React.PropsWithChildren<IStructureViewProps>) => {
|
||||
const folderTree = useMemo(() => {
|
||||
const root: FolderNode = {
|
||||
name: '',
|
||||
path: '',
|
||||
children: [],
|
||||
pages: []
|
||||
};
|
||||
|
||||
const folderMap = new Map<string, FolderNode>();
|
||||
folderMap.set('', root);
|
||||
|
||||
// Helper to compute the normalized folder path for a page.
|
||||
// It ensures the page's folder starts with the `fmFolder` segment and
|
||||
// preserves any subpaths after that segment (so subfolders are created).
|
||||
const computeNormalizedFolderPath = (page: Page): string => {
|
||||
if (!page.fmFolder) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fmFolder = page.fmFolder.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
||||
|
||||
// If we have a file path, use its directory (exclude the filename) to compute
|
||||
// the relative path. This avoids treating filenames as folder segments.
|
||||
const filePath = page.fmFilePath ? parseWinPath(page.fmFilePath).replace(/^\/+|\/+$/g, '') : '';
|
||||
const fileDir = filePath && filePath.includes('/') ? filePath.substring(0, filePath.lastIndexOf('/')).replace(/^\/+|\/+$/g, '') : '';
|
||||
|
||||
if (fileDir) {
|
||||
// If the content folder is known, and the file directory starts with it,
|
||||
// replace that root with the fmFolder (preserving subfolders after it).
|
||||
if (page.fmPageFolder?.path) {
|
||||
const contentFolderPath = parseWinPath(page.fmPageFolder.path).replace(/^\/+|\/+$/g, '');
|
||||
if (fileDir.startsWith(contentFolderPath)) {
|
||||
const rel = fileDir.substring(contentFolderPath.length).replace(/^\/+|\/+$/g, '');
|
||||
return rel ? `${fmFolder}/${rel}` : fmFolder;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try to find fmFolder as a directory segment in the fileDir
|
||||
const segments = fileDir.split('/').filter(Boolean);
|
||||
const fmIndex = segments.indexOf(fmFolder);
|
||||
if (fmIndex >= 0) {
|
||||
return segments.slice(fmIndex).join('/');
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: just use the fmFolder name
|
||||
return fmFolder;
|
||||
};
|
||||
|
||||
// First pass: create all folder nodes (ensure nodes exist even if a page lacks fmFilePath)
|
||||
for (const page of pages) {
|
||||
if (!page.fmFolder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalizedPath = computeNormalizedFolderPath(page).replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
||||
if (!normalizedPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parts = normalizedPath.split('/').filter(part => part.length > 0);
|
||||
|
||||
let currentPath = '';
|
||||
let currentNode = root;
|
||||
|
||||
for (const part of parts) {
|
||||
const fullPath = currentPath ? `${currentPath}/${part}` : part;
|
||||
|
||||
if (!folderMap.has(fullPath)) {
|
||||
const newNode: FolderNode = {
|
||||
name: part,
|
||||
path: fullPath,
|
||||
children: [],
|
||||
pages: []
|
||||
};
|
||||
folderMap.set(fullPath, newNode);
|
||||
currentNode.children.push(newNode);
|
||||
}
|
||||
|
||||
const nextNode = folderMap.get(fullPath);
|
||||
if (nextNode) {
|
||||
currentNode = nextNode;
|
||||
}
|
||||
currentPath = fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: assign pages to their exact folder node (including subfolders)
|
||||
for (const page of pages) {
|
||||
if (!page.fmFolder) {
|
||||
root.pages.push(page);
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalizedPath = computeNormalizedFolderPath(page).replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
||||
const folderNode = normalizedPath ? folderMap.get(normalizedPath) : folderMap.get(page.fmFolder.replace(/\\/g, '/').replace(/^\/+|\/+$/g, ''));
|
||||
if (folderNode) {
|
||||
folderNode.pages.push(page);
|
||||
} else {
|
||||
// If folder not found, add to root as fallback
|
||||
root.pages.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}, [pages]);
|
||||
|
||||
const renderFolderNode = (node: FolderNode, depth = 0): React.ReactNode => {
|
||||
const hasContent = node.pages.length > 0 || node.children.length > 0;
|
||||
|
||||
if (!hasContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isRoot = depth === 0;
|
||||
const paddingLeft = depth * 20;
|
||||
|
||||
if (isRoot) {
|
||||
// For root node, render children and pages directly
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
{/* Root level folders */}
|
||||
{node.children.map(child => renderFolderNode(child, depth + 1))}
|
||||
|
||||
{/* Root level pages */}
|
||||
{node.pages.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-medium mb-3 text-[var(--vscode-editor-foreground)]">
|
||||
Root Files
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
{node.pages.map((page, idx) => (
|
||||
<li key={`${page.slug}-${idx}`}>
|
||||
<StructureItem {...page} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={node.path} className="mb-4">
|
||||
<Disclosure defaultOpen={depth <= 1}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className="flex items-center w-full text-left"
|
||||
style={{ paddingLeft: `${paddingLeft}px` }}
|
||||
>
|
||||
<ChevronRightIcon
|
||||
className={`w-4 h-4 mr-2 transform transition-transform ${open ? 'rotate-90' : ''
|
||||
}`}
|
||||
/>
|
||||
<FolderIcon className="w-4 h-4 mr-2 text-[var(--vscode-symbolIcon-folderForeground)]" />
|
||||
<span className="font-medium text-[var(--vscode-editor-foreground)]">
|
||||
{node.name}
|
||||
{node.pages.length > 0 && (
|
||||
<span className="ml-2 text-sm text-[var(--vscode-descriptionForeground)]">
|
||||
({node.pages.length} {node.pages.length === 1 ? 'file' : 'files'})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</Disclosure.Button>
|
||||
|
||||
<Disclosure.Panel className="mt-2">
|
||||
{/* Child folders */}
|
||||
{node.children.map(child => renderFolderNode(child, depth + 1))}
|
||||
|
||||
{/* Pages in this folder */}
|
||||
{node.pages.length > 0 && (
|
||||
<ul className="space-y-1 mb-3">
|
||||
{node.pages.map((page, idx) => (
|
||||
<li key={`${page.slug}-${idx}`} style={{ paddingLeft: `${paddingLeft + 20}px` }}>
|
||||
<StructureItem {...page} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="structure-view">
|
||||
{renderFolderNode(folderTree)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -53,7 +53,7 @@ export const DataForm: React.FunctionComponent<IDataFormProps> = ({
|
||||
};
|
||||
} catch (error) {
|
||||
setError((error as Error).message);
|
||||
return () => { };
|
||||
return () => void 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { useForm } from 'uniforms';
|
||||
import { Button } from '../Common/Button';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { SubmitField } from '../../../components/uniforms-frontmatter';
|
||||
import { Button } from 'vscrui';
|
||||
|
||||
export interface IDataFormControlsProps {
|
||||
model: any | null;
|
||||
@@ -21,8 +21,8 @@ export const DataFormControls: React.FunctionComponent<IDataFormControlsProps> =
|
||||
<SubmitField value={model ? `Update` : `Add`} />
|
||||
|
||||
<Button
|
||||
className="ml-4"
|
||||
secondary
|
||||
className="ml-4 !py-2"
|
||||
appearance="secondary"
|
||||
onClick={() => {
|
||||
if (onClear) {
|
||||
onClear();
|
||||
|
||||
@@ -5,34 +5,36 @@ 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';
|
||||
import { DashboardCommand } from '../../DashboardCommand';
|
||||
import { Button } from '../Common/Button';
|
||||
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, 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';
|
||||
import { Button } from 'vscrui';
|
||||
|
||||
export interface IDataViewProps { }
|
||||
|
||||
export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
_: React.PropsWithChildren<IDataViewProps>
|
||||
) => {
|
||||
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) => {
|
||||
@@ -64,14 +66,14 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(data: any) => {
|
||||
(data: unknown) => {
|
||||
if (selectedData?.singleEntry) {
|
||||
// Needs to add a single entry
|
||||
updateData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
const dataClone: any[] = Object.assign([], dataEntries);
|
||||
const dataClone: unknown[] = Object.assign([], dataEntries);
|
||||
if (selectedIndex !== null && selectedIndex !== undefined) {
|
||||
dataClone[selectedIndex] = data;
|
||||
} else {
|
||||
@@ -110,11 +112,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 || {};
|
||||
@@ -123,12 +137,44 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
return dataEntries && selectedIndex !== null && selectedIndex !== undefined
|
||||
? dataEntries[selectedIndex]
|
||||
: null;
|
||||
}, [selectedData, , dataEntries, selectedIndex]);
|
||||
}, [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.setTitle, localize(LocalizationKey.dashboardHeaderTabsData));
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: 'Data view loaded',
|
||||
@@ -140,49 +186,27 @@ 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[];
|
||||
|
||||
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) => (
|
||||
@@ -208,30 +232,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 && (
|
||||
@@ -239,7 +315,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">
|
||||
@@ -258,14 +334,14 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
/>
|
||||
))}
|
||||
</Container>
|
||||
<Button className="mt-4" onClick={() => setSelectedIndex(null)}>
|
||||
{l10n.t(LocalizationKey.dashboardDataViewDataViewAdd)}
|
||||
<Button className="mt-4 !py-2" onClick={() => setSelectedIndex(null)}>
|
||||
{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>
|
||||
)}
|
||||
@@ -277,7 +353,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
|
||||
@@ -287,12 +363,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>
|
||||
@@ -300,14 +378,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>
|
||||
@@ -315,6 +393,8 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
)
|
||||
}
|
||||
|
||||
{loading && <Spinner />}
|
||||
|
||||
<SponsorMsg
|
||||
beta={settings?.beta}
|
||||
version={settings?.versionInfo}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ILanguageFilterProps { }
|
||||
|
||||
export const LanguageFilter: React.FunctionComponent<ILanguageFilterProps> = ({ }: React.PropsWithChildren<ILanguageFilterProps>) => {
|
||||
export const LanguageFilter: React.FunctionComponent<ILanguageFilterProps> = () => {
|
||||
const locales = useRecoilValue(LocalesAtom);
|
||||
const [crntLocale, setCrntLocale] = useRecoilState(LocaleAtom);
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, Eye
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { MultiSelectedItemsAtom, PagedItems, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
|
||||
import { ActionsBarItem } from './ActionsBarItem';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
@@ -68,8 +67,14 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
}, [selectedFiles]);
|
||||
|
||||
const selectAllItems = React.useCallback(() => {
|
||||
setSelectedFiles([...pagedItems]);
|
||||
}, [pagedItems]);
|
||||
const allSelected = [...selectedFiles, ...pagedItems];
|
||||
setSelectedFiles(Array.from(new Set(allSelected)));
|
||||
}, [selectedFiles, pagedItems]);
|
||||
|
||||
const hasAllItemsSelectedOnPage = React.useMemo(() => {
|
||||
const selectedItemsOnPage = selectedFiles.filter((file) => pagedItems.includes(file));
|
||||
return selectedItemsOnPage.length >= pagedItems.length;
|
||||
}, [selectedFiles, pagedItems]);
|
||||
|
||||
const languageActions = React.useMemo(() => {
|
||||
const actions: React.ReactNode[] = [];
|
||||
@@ -92,7 +97,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
})
|
||||
}}>
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonTranslate)}</span>
|
||||
<span>{localize(LocalizationKey.commonTranslate)}</span>
|
||||
</ActionsBarItem>
|
||||
)
|
||||
|
||||
@@ -107,7 +112,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
className='flex items-center text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]'
|
||||
>
|
||||
<LanguageIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonLanguages)}</span>
|
||||
<span>{localize(LocalizationKey.commonLanguages)}</span>
|
||||
<ChevronDownIcon className="ml-2 h-4 w-4" aria-hidden={true} />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -163,7 +168,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
disabled={selectedFiles.length === 0}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonScripts)}</span>
|
||||
<span>{localize(LocalizationKey.commonScripts)}</span>
|
||||
<ChevronDownIcon className="ml-2 h-4 w-4" aria-hidden={true} />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -197,10 +202,10 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === 0 || selectedFiles.length > 1}
|
||||
onClick={viewFile}
|
||||
title={l10n.t(LocalizationKey.commonView)}
|
||||
title={localize(LocalizationKey.commonView)}
|
||||
>
|
||||
<EyeIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonView)}</span>
|
||||
<span>{localize(LocalizationKey.commonView)}</span>
|
||||
</ActionsBarItem>
|
||||
|
||||
{
|
||||
@@ -211,10 +216,10 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
messageHandler.send(DashboardMessage.rename, selectedFiles[0]);
|
||||
setSelectedFiles([]);
|
||||
}}
|
||||
title={l10n.t(LocalizationKey.commonRename)}
|
||||
title={localize(LocalizationKey.commonRename)}
|
||||
>
|
||||
<RenameIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonRename)}</span>
|
||||
<span>{localize(LocalizationKey.commonRename)}</span>
|
||||
</ActionsBarItem>
|
||||
)
|
||||
}
|
||||
@@ -228,10 +233,10 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
path: selectedFiles[0],
|
||||
action: 'edit'
|
||||
})}
|
||||
title={l10n.t(LocalizationKey.commonEdit)}
|
||||
title={localize(LocalizationKey.commonEdit)}
|
||||
>
|
||||
<PencilIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
<span>{localize(LocalizationKey.commonEdit)}</span>
|
||||
</ActionsBarItem>
|
||||
</>
|
||||
)
|
||||
@@ -245,10 +250,10 @@ 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)}
|
||||
title={localize(LocalizationKey.commonDelete)}
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonDelete)}</span>
|
||||
<span>{localize(LocalizationKey.commonDelete)}</span>
|
||||
</ActionsBarItem>
|
||||
</div>
|
||||
|
||||
@@ -258,33 +263,33 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
<ActionsBarItem
|
||||
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
|
||||
onClick={() => setSelectedFiles([])}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}
|
||||
title={localize(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
|
||||
<span>{localize(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
|
||||
</ActionsBarItem>
|
||||
)
|
||||
}
|
||||
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === pagedItems.length}
|
||||
disabled={hasAllItemsSelectedOnPage}
|
||||
onClick={selectAllItems}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)}
|
||||
title={localize(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>
|
||||
<span>{localize(LocalizationKey.dashboardHeaderActionsBarSelectAll)}</span>
|
||||
</ActionsBarItem>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
{showAlert && (
|
||||
<Alert
|
||||
title={`${l10n.t(LocalizationKey.dashboardHeaderActionsBarAlertDeleteTitle)}`}
|
||||
description={l10n.t(LocalizationKey.dashboardHeaderActionsBarAlertDeleteDescription)}
|
||||
okBtnText={l10n.t(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={l10n.t(LocalizationKey.commonCancel)}
|
||||
title={`${localize(LocalizationKey.dashboardHeaderActionsBarAlertDeleteTitle)}`}
|
||||
description={localize(LocalizationKey.dashboardHeaderActionsBarAlertDeleteDescription)}
|
||||
okBtnText={localize(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={localize(LocalizationKey.commonCancel)}
|
||||
dismiss={() => setShowAlert(false)}
|
||||
trigger={onDeleteConfirm}
|
||||
/>
|
||||
|
||||
38
src/dashboardWebView/components/Header/BooleanOption.tsx
Normal file
38
src/dashboardWebView/components/Header/BooleanOption.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Checkbox as VSCodeCheckbox } from 'vscrui';
|
||||
|
||||
export interface IBooleanOptionProps {
|
||||
value: boolean | undefined | null;
|
||||
name: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const BooleanOption: React.FunctionComponent<IBooleanOptionProps> = ({
|
||||
value,
|
||||
name,
|
||||
label
|
||||
}: React.PropsWithChildren<IBooleanOptionProps>) => {
|
||||
const [isChecked, setIsChecked] = React.useState(false);
|
||||
|
||||
const onChange = React.useCallback((newValue: boolean) => {
|
||||
setIsChecked(newValue);
|
||||
Messenger.send(DashboardMessage.updateSetting, {
|
||||
name: name,
|
||||
value: newValue
|
||||
});
|
||||
}, [name]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsChecked(!!value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<VSCodeCheckbox
|
||||
onChange={onChange}
|
||||
checked={isChecked}>
|
||||
{label}
|
||||
</VSCodeCheckbox>
|
||||
);
|
||||
};
|
||||
@@ -21,16 +21,16 @@ import { useEffect, useMemo } from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export const guardRecoilDefaultValue = (candidate: any): candidate is DefaultValue => {
|
||||
if (candidate instanceof DefaultValue) return true;
|
||||
export const guardRecoilDefaultValue = (candidate: unknown): candidate is DefaultValue => {
|
||||
if (candidate instanceof DefaultValue) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export interface IClearFiltersProps { }
|
||||
|
||||
export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (
|
||||
_: React.PropsWithChildren<IClearFiltersProps>
|
||||
) => {
|
||||
export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = () => {
|
||||
const [show, setShow] = React.useState(false);
|
||||
|
||||
const folder = useRecoilValue(FolderSelector);
|
||||
@@ -75,7 +75,9 @@ export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (
|
||||
}
|
||||
}, [folder, tag, category, locale, hasCustomFilters]);
|
||||
|
||||
if (!show) return null;
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -10,7 +10,7 @@ import { LanguageFilter } from '../Filters/LanguageFilter';
|
||||
|
||||
export interface IFiltersProps { }
|
||||
|
||||
export const Filters: React.FunctionComponent<IFiltersProps> = (_: React.PropsWithChildren<IFiltersProps>) => {
|
||||
export const Filters: React.FunctionComponent<IFiltersProps> = () => {
|
||||
const [crntFilters, setCrntFilters] = useRecoilState(FiltersAtom);
|
||||
const [crntTag, setCrntTag] = useRecoilState(TagAtom);
|
||||
const [crntCategory, setCrntCategory] = useRecoilState(CategoryAtom);
|
||||
@@ -24,19 +24,37 @@ export const Filters: React.FunctionComponent<IFiltersProps> = (_: React.PropsWi
|
||||
return otherFilters?.map((filter) => {
|
||||
const filterName = typeof filter === "string" ? filter : filter.name;
|
||||
const filterTitle = typeof filter === "string" ? firstToUpper(filter) : filter.title;
|
||||
const values = filterValues?.[filterName];
|
||||
let values = filterValues?.[filterName];
|
||||
if (!values || values.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get all the unique values
|
||||
const individualValues = new Set<string>();
|
||||
values.forEach((value) => {
|
||||
if (value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((v) => individualValues.add(v));
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
individualValues.add(value);
|
||||
}
|
||||
});
|
||||
|
||||
values = Array.from(individualValues);
|
||||
|
||||
return (
|
||||
<Filter
|
||||
key={filterName}
|
||||
label={filterTitle}
|
||||
activeItem={crntFilters[filterName]}
|
||||
items={values}
|
||||
items={values as string[]}
|
||||
onClick={(value) => setCrntFilters((prev) => {
|
||||
let clone = Object.assign({}, prev);
|
||||
const clone = Object.assign({}, prev);
|
||||
if (!clone[filterName] && value) {
|
||||
clone[filterName] = value;
|
||||
} else {
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface IFoldersFilterProps { }
|
||||
|
||||
export const FoldersFilter: React.FunctionComponent<
|
||||
IFoldersFilterProps
|
||||
> = ({ }: React.PropsWithChildren<IFoldersFilterProps>) => {
|
||||
> = () => {
|
||||
const DEFAULT_TYPE = l10n.t(LocalizationKey.dashboardHeaderFoldersDefault);
|
||||
const [crntFolder, setCrntFolder] = useRecoilState(FolderAtom);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { AllPagesAtom, GroupingAtom } from '../../state';
|
||||
import { AllPagesAtom, GroupingAtom, SettingsAtom } from '../../state';
|
||||
import { MenuButton, MenuItem } from '../Menu';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import { DropdownMenu, DropdownMenuContent } from '../../../components/shadcn/Dropdown';
|
||||
|
||||
export interface IGroupingProps { }
|
||||
|
||||
export const Grouping: React.FunctionComponent<
|
||||
IGroupingProps
|
||||
> = ({ }: React.PropsWithChildren<IGroupingProps>) => {
|
||||
> = () => {
|
||||
const settings = useRecoilValue(SettingsAtom);
|
||||
const [group, setGroup] = useRecoilState(GroupingAtom);
|
||||
const pages = useRecoilValue(AllPagesAtom);
|
||||
|
||||
const GROUP_OPTIONS = React.useMemo(() => {
|
||||
let options: { name: string, id: GroupOption }[] = [];
|
||||
const options: { name: string, id?: GroupOption | string }[] = [];
|
||||
|
||||
if (pages.length > 0) {
|
||||
if (settings?.grouping) {
|
||||
const groups = settings.grouping.map((g) => ({ name: g.title, id: g.name }));
|
||||
options.push(...groups);
|
||||
}
|
||||
|
||||
if (pages.some((x) => x.fmYear)) {
|
||||
options.push({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionYear), id: GroupOption.Year })
|
||||
options.push({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionYear), id: GroupOption.Year })
|
||||
}
|
||||
|
||||
if (pages.some((x) => x.fmDraft)) {
|
||||
options.push({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionDraft), id: GroupOption.Draft })
|
||||
options.push({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionDraft), id: GroupOption.Draft })
|
||||
}
|
||||
}
|
||||
|
||||
if (options.length > 0) {
|
||||
options.unshift({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionNone), id: GroupOption.none })
|
||||
options.unshift({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionNone), id: GroupOption.none })
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [pages])
|
||||
}, [pages, settings?.grouping])
|
||||
|
||||
const crntGroup = GROUP_OPTIONS.find((x) => x.id === group);
|
||||
|
||||
@@ -43,7 +48,7 @@ export const Grouping: React.FunctionComponent<
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<MenuButton label={l10n.t(LocalizationKey.dashboardHeaderGroupingMenuButtonLabel)} title={crntGroup?.name || ''} />
|
||||
<MenuButton label={localize(LocalizationKey.dashboardHeaderGroupingMenuButtonLabel)} title={crntGroup?.name || ''} />
|
||||
|
||||
<DropdownMenuContent>
|
||||
{GROUP_OPTIONS.map((option) => (
|
||||
|
||||
@@ -37,9 +37,7 @@ const NavigationItem: React.FunctionComponent<INavigationItemProps> = ({
|
||||
)
|
||||
};
|
||||
|
||||
export const Navigation: React.FunctionComponent<INavigationProps> = ({
|
||||
|
||||
}: React.PropsWithChildren<INavigationProps>) => {
|
||||
export const Navigation: React.FunctionComponent<INavigationProps> = () => {
|
||||
const [crntTab, setCrntTab] = useRecoilState(TabAtom);
|
||||
const tabInfo = useRecoilValue(TabInfoAtom);
|
||||
const settings = useRecoilValue(SettingsAtom);
|
||||
|
||||
@@ -2,10 +2,11 @@ import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import usePagination from '../../hooks/usePagination';
|
||||
import { MediaTotalSelector, PageAtom, SettingsAtom } from '../../state';
|
||||
import { MediaTotalSelector, PageAtom, SettingsAtom, ViewSelector } from '../../state';
|
||||
import { PaginationButton } from './PaginationButton';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { DashboardViewType } from '../../models';
|
||||
|
||||
export interface IPaginationProps {
|
||||
totalPages?: number;
|
||||
@@ -17,21 +18,35 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({
|
||||
const [page, setPage] = useRecoilState(PageAtom);
|
||||
const totalMedia = useRecoilValue(MediaTotalSelector);
|
||||
const settings = useRecoilValue(SettingsAtom);
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
const { pageSetNr, totalPagesNr } = usePagination(
|
||||
settings?.dashboardState.contents.pagination,
|
||||
totalPages,
|
||||
totalMedia
|
||||
);
|
||||
|
||||
const getButtons = useCallback((): number[] => {
|
||||
const buttons = useMemo((): JSX.Element[] => {
|
||||
const maxButtons = 5;
|
||||
const buttons: number[] = [];
|
||||
const buttons: JSX.Element[] = [];
|
||||
const start = page - maxButtons;
|
||||
const end = page + maxButtons;
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i >= 0 && i <= totalPagesNr) {
|
||||
buttons.push(i);
|
||||
buttons.push(
|
||||
<button
|
||||
key={i}
|
||||
disabled={i === page}
|
||||
onClick={() => {
|
||||
setPage(i);
|
||||
}}
|
||||
className={`max-h-8 rounded ${page === i
|
||||
? `px-2 bg-[var(--vscode-list-activeSelectionBackground)] text-[var(--vscode-list-activeSelectionForeground)]`
|
||||
: `text-[var(--vscode-editor-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)]`}`}
|
||||
>
|
||||
{i + 1}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
@@ -45,6 +60,10 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({
|
||||
setPage(0);
|
||||
}, []);
|
||||
|
||||
if (view === DashboardViewType.Structure) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center sm:justify-end space-x-2 text-sm">
|
||||
<PaginationButton
|
||||
@@ -67,20 +86,7 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
{getButtons().map((button) => (
|
||||
<button
|
||||
key={button}
|
||||
disabled={button === page}
|
||||
onClick={() => {
|
||||
setPage(button);
|
||||
}}
|
||||
className={`max-h-8 rounded ${page === button
|
||||
? `px-2 bg-[var(--vscode-list-activeSelectionBackground)] text-[var(--vscode-list-activeSelectionForeground)]`
|
||||
: `text-[var(--vscode-editor-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)]`}`}
|
||||
>
|
||||
{button + 1}
|
||||
</button>
|
||||
))}
|
||||
{buttons}
|
||||
|
||||
<PaginationButton
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderPaginationNext)}
|
||||
|
||||
@@ -21,9 +21,7 @@ import { ArrowClockwiseIcon } from '../../../components/icons/ArrowClockwiseIcon
|
||||
|
||||
export interface IRefreshDashboardDataProps { }
|
||||
|
||||
export const RefreshDashboardData: React.FunctionComponent<IRefreshDashboardDataProps> = (
|
||||
{ }: React.PropsWithChildren<IRefreshDashboardDataProps>
|
||||
) => {
|
||||
export const RefreshDashboardData: React.FunctionComponent<IRefreshDashboardDataProps> = () => {
|
||||
const view = useRecoilValue(DashboardViewAtom);
|
||||
const [, setLoading] = useRecoilState(LoadingAtom);
|
||||
const resetSearch = useResetRecoilState(SearchAtom);
|
||||
|
||||
@@ -165,7 +165,7 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
let sort = allOptions.find((x) => x.id === crntSortingOption?.id) || sortOptions[0];
|
||||
const sort = allOptions.find((x) => x.id === crntSortingOption?.id) || sortOptions[0];
|
||||
setCrntSort(sort);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { ViewAtom, SettingsSelector } from '../../state';
|
||||
import { Bars4Icon, Squares2X2Icon } from '@heroicons/react/24/solid';
|
||||
import { Bars4Icon, Squares2X2Icon, FolderIcon } from '@heroicons/react/24/solid';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { DashboardViewType } from '../../models';
|
||||
@@ -16,9 +16,7 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (
|
||||
const [view, setView] = useRecoilState(ViewAtom);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const toggleView = () => {
|
||||
const newView =
|
||||
view === DashboardViewType.Grid ? DashboardViewType.List : DashboardViewType.Grid;
|
||||
const handleViewChange = (newView: DashboardViewType) => {
|
||||
setView(newView);
|
||||
Messenger.send(DashboardMessage.setPageViewType, newView);
|
||||
};
|
||||
@@ -36,7 +34,7 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (
|
||||
}`}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderViewSwitchToGrid)}
|
||||
type={`button`}
|
||||
onClick={toggleView}
|
||||
onClick={() => handleViewChange(DashboardViewType.Grid)}
|
||||
>
|
||||
<Squares2X2Icon className={`w-4 h-4`} />
|
||||
<span className={`sr-only`}>
|
||||
@@ -44,17 +42,29 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`flex items-center px-2 py-1 rounded-r-sm ${view === DashboardViewType.List ? `bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)]` : 'text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]'
|
||||
className={`flex items-center px-2 py-1 ${view === DashboardViewType.List ? `bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)]` : 'text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]'
|
||||
}`}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderViewSwitchToList)}
|
||||
type={`button`}
|
||||
onClick={toggleView}
|
||||
onClick={() => handleViewChange(DashboardViewType.List)}
|
||||
>
|
||||
<Bars4Icon className={`w-4 h-4`} />
|
||||
<span className={`sr-only`}>
|
||||
{l10n.t(LocalizationKey.dashboardHeaderViewSwitchToList)}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`flex items-center px-2 py-1 rounded-r-sm ${view === DashboardViewType.Structure ? `bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)]` : 'text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]'
|
||||
}`}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderViewSwitchToStructure)}
|
||||
type={`button`}
|
||||
onClick={() => handleViewChange(DashboardViewType.Structure)}
|
||||
>
|
||||
<FolderIcon className={`w-4 h-4`} />
|
||||
<span className={`sr-only`}>
|
||||
{l10n.t(LocalizationKey.dashboardHeaderViewSwitchToStructure)}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -91,8 +91,9 @@ export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (
|
||||
|
||||
if (scripts.length > 0) {
|
||||
return (
|
||||
<div className="flex flex-1 justify-start">
|
||||
<div className="flex flex-1 justify-start space-x-2">
|
||||
{renderPostAssetsButton}
|
||||
|
||||
<ChoiceButton
|
||||
title={l10n.t(LocalizationKey.dashboardMediaFolderCreationFolderCreate)}
|
||||
choices={scripts.map((s) => ({
|
||||
@@ -103,6 +104,8 @@ export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (
|
||||
onClick={onFolderCreation}
|
||||
disabled={!settings?.initialized}
|
||||
/>
|
||||
|
||||
<RefreshDashboardData />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { FolderIcon } from '@heroicons/react/24/solid';
|
||||
import { FolderIcon, PencilIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { basename, join } from 'path';
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import useMediaFolder from '../../hooks/useMediaFolder';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { useState } from 'react';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
|
||||
export interface IFolderItemProps {
|
||||
folder: string;
|
||||
@@ -17,6 +22,7 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
staticFolder
|
||||
}: React.PropsWithChildren<IFolderItemProps>) => {
|
||||
const { updateFolder } = useMediaFolder();
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
|
||||
const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder;
|
||||
|
||||
@@ -25,28 +31,73 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
[relFolderPath, staticFolder]
|
||||
);
|
||||
|
||||
return (
|
||||
<li
|
||||
className={`group relative hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-editor-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)]`}
|
||||
>
|
||||
<button
|
||||
title={isContentFolder ? l10n.t(LocalizationKey.dashboardMediaFolderItemContentDirectory) : l10n.t(LocalizationKey.dashboardMediaFolderItemPublicDirectory)}
|
||||
className={`p-4 w-full flex flex-row items-center h-full`}
|
||||
onClick={() => updateFolder(folder)}
|
||||
>
|
||||
<div className="relative mr-4">
|
||||
<FolderIcon className={`h-12 w-12`} />
|
||||
{isContentFolder && (
|
||||
<span className={`font-extrabold absolute bottom-3 left-1/2 transform -translate-x-1/2 text-[var(--frontmatter-text)]`}>
|
||||
C
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
const updateFolderName = React.useCallback(() => {
|
||||
messageHandler.send(DashboardMessage.updateMediaFolder, { folder, wsFolder, staticFolder })
|
||||
}, []);
|
||||
|
||||
<p className="text-sm font-bold pointer-events-none flex items-center text-left overflow-hidden break-words">
|
||||
{basename(relFolderPath)}
|
||||
</p>
|
||||
</button>
|
||||
</li>
|
||||
const onDelete = React.useCallback(() => {
|
||||
setShowAlert(true);
|
||||
}, []);
|
||||
|
||||
const confirmDeletion = React.useCallback(() => {
|
||||
messageHandler.send(DashboardMessage.deleteMediaFolder, { folder });
|
||||
setShowAlert(false);
|
||||
}, [folder]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<li
|
||||
className={`flex flex-col group relative text-[var(--vscode-sideBarTitle-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)] shadow-md hover:shadow-xl dark:shadow-none bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] border border-[var(--frontmatter-border)] rounded`}
|
||||
>
|
||||
<button
|
||||
title={isContentFolder ? localize(LocalizationKey.dashboardMediaFolderItemContentDirectory) : localize(LocalizationKey.dashboardMediaFolderItemPublicDirectory)}
|
||||
className={`p-4 w-full flex flex-row items-center h-full`}
|
||||
onClick={() => updateFolder(folder)}
|
||||
>
|
||||
<div className="relative mr-4">
|
||||
<FolderIcon className={`h-12 w-12`} />
|
||||
{isContentFolder && (
|
||||
<span className={`font-extrabold absolute bottom-3 left-1/2 transform -translate-x-1/2 text-[var(--frontmatter-text)]`}>
|
||||
C
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-bold pointer-events-none flex items-center text-left overflow-hidden break-words">
|
||||
{basename(relFolderPath)}
|
||||
</p>
|
||||
</button>
|
||||
|
||||
{!isContentFolder && (
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)]`}>
|
||||
<QuickAction
|
||||
title={localize(LocalizationKey.commonEdit)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={updateFolderName}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{localize(LocalizationKey.dashboardMediaItemMenuItemView)}</span>
|
||||
</QuickAction>
|
||||
|
||||
<QuickAction
|
||||
title={localize(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
|
||||
{showAlert && (
|
||||
<Alert
|
||||
title={`${localize(LocalizationKey.commonDelete)}: ${basename(parseWinPath(folder) || '')}`}
|
||||
description={localize(LocalizationKey.dashboardMediaFolderItemDeleteDescription, folder)}
|
||||
okBtnText={localize(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={localize(LocalizationKey.commonCancel)}
|
||||
dismiss={() => setShowAlert(false)}
|
||||
trigger={confirmDeletion}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import { ClipboardIcon, CodeBracketIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedItemActionAtom } from '../../state';
|
||||
@@ -36,25 +35,25 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
return (
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)]`}>
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemView)}
|
||||
title={localize(LocalizationKey.dashboardMediaItemMenuItemView)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'view'
|
||||
})}>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemView)}</span>
|
||||
<span className='sr-only'>{localize(LocalizationKey.dashboardMediaItemMenuItemView)}</span>
|
||||
</QuickAction>
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}
|
||||
title={localize(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'edit'
|
||||
})}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}</span>
|
||||
<span className='sr-only'>{localize(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}</span>
|
||||
</QuickAction>
|
||||
|
||||
{viewData?.filePath ? (
|
||||
@@ -62,8 +61,8 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
<QuickAction
|
||||
title={
|
||||
viewData.metadataInsert && viewData.fieldName
|
||||
? l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertField, viewData.fieldName)
|
||||
: l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertMarkdown)
|
||||
? localize(LocalizationKey.dashboardMediaItemQuickActionInsertField, viewData.fieldName)
|
||||
: localize(LocalizationKey.dashboardMediaItemQuickActionInsertMarkdown)
|
||||
}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertIntoArticle}
|
||||
@@ -73,7 +72,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
|
||||
{viewData?.position && snippets.length > 0 && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
title={localize(LocalizationKey.commonInsertSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertSnippet}>
|
||||
<CodeBracketIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
@@ -85,7 +84,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
{
|
||||
relPath && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}
|
||||
title={localize(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => copyToClipboard(parseWinPath(relPath) || '')}>
|
||||
<ClipboardIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
@@ -101,7 +100,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
showTrigger />
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
title={localize(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
PlusIcon,
|
||||
VideoCameraIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { basename } from 'path';
|
||||
import { basename, parse } from 'path';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
@@ -55,8 +55,17 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
const { mediaFolder, mediaDetails, isAudio, isImage, isVideo } = useMediaInfo(media);
|
||||
|
||||
const relPath = useMemo(() => {
|
||||
if (viewData?.data?.pageBundle && viewData?.data?.filePath) {
|
||||
const articlePath = viewData?.data?.filePath;
|
||||
const articleDir = parse(parseWinPath(articlePath)).dir;
|
||||
|
||||
const mediaPath = parseWinPath(media.fsPath);
|
||||
if (mediaPath.startsWith(articleDir)) {
|
||||
return getRelPath(media.fsPath, undefined, articleDir);
|
||||
}
|
||||
}
|
||||
return getRelPath(media.fsPath, settings?.staticFolder, settings?.wsFolder);
|
||||
}, [media.fsPath, settings?.staticFolder, settings?.wsFolder]);
|
||||
}, [media.fsPath, settings?.staticFolder, settings?.wsFolder, viewData?.data?.pageBundle, viewData?.data?.filePath]);
|
||||
|
||||
const hasViewData = useMemo(() => {
|
||||
return viewData?.data?.filePath !== undefined;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
PagedItems,
|
||||
SelectedMediaFolderAtom,
|
||||
SettingsSelector,
|
||||
SortingAtom,
|
||||
ViewDataSelector
|
||||
} from '../../state';
|
||||
import { Spinner } from '../Common/Spinner';
|
||||
@@ -30,18 +31,18 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { MediaItemPanel } from './MediaItemPanel';
|
||||
import { FilesProvider } from '../../providers/FilesProvider';
|
||||
import { SortOption } from '../../constants/SortOption';
|
||||
|
||||
export interface IMediaProps { }
|
||||
|
||||
export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
_: React.PropsWithChildren<IMediaProps>
|
||||
) => {
|
||||
export const Media: React.FunctionComponent<IMediaProps> = () => {
|
||||
const { media } = useMedia();
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const viewData = useRecoilValue(ViewDataSelector);
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
|
||||
const folders = useRecoilValue(MediaFoldersAtom);
|
||||
const loading = useRecoilValue(LoadingAtom);
|
||||
const crntSorting = useRecoilValue(SortingAtom);
|
||||
const [, setPagedItems] = useRecoilState(PagedItems);
|
||||
|
||||
const currentStaticFolder = useMemo(() => {
|
||||
@@ -67,7 +68,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
return [];
|
||||
}
|
||||
|
||||
let groupedFolders = [];
|
||||
const groupedFolders = [];
|
||||
|
||||
for (const cFolder of settings?.contentFolders || []) {
|
||||
const foldersPath = parseWinPath(cFolder.path);
|
||||
@@ -85,11 +86,18 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
currentStaticFolder &&
|
||||
settings?.staticFolder !== STATIC_FOLDER_PLACEHOLDER.hexo.placeholder
|
||||
) {
|
||||
return folders.filter((f) => parseWinPath(f).includes(currentStaticFolder));
|
||||
const allFolders = folders.filter((f) => parseWinPath(f).includes(currentStaticFolder));
|
||||
if (crntSorting && crntSorting.id === SortOption.FileNameAsc) {
|
||||
return allFolders.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
||||
} else if (crntSorting && crntSorting.id === SortOption.FileNameDesc) {
|
||||
return allFolders.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
||||
} else {
|
||||
return allFolders;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [folders, viewData, currentStaticFolder, settings?.staticFolder]);
|
||||
}, [folders, viewData, currentStaticFolder, settings?.staticFolder, crntSorting]);
|
||||
|
||||
const allMedia = useMemo(() => {
|
||||
let mediaFiles: MediaInfo[] = Object.assign([], media);
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface IMediaHeaderTopProps { }
|
||||
|
||||
export const MediaHeaderTop: React.FunctionComponent<
|
||||
IMediaHeaderTopProps
|
||||
> = ({ }: React.PropsWithChildren<IMediaHeaderTopProps>) => {
|
||||
> = () => {
|
||||
const [lastUpdated, setLastUpdated] = React.useState<string | null>(null);
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
|
||||
const crntSorting = useRecoilValue(SortingSelector);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -20,7 +20,8 @@ export const Preview: React.FunctionComponent<IPreviewProps> = ({
|
||||
|
||||
const onRefresh = () => {
|
||||
if (iframeRef.current?.src) {
|
||||
iframeRef.current.src = iframeRef.current.src;
|
||||
const url = iframeRef.current.src;
|
||||
iframeRef.current.src = url;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,10 +35,12 @@ export const Preview: React.FunctionComponent<IPreviewProps> = ({
|
||||
navUrl = `https://${navUrl}`;
|
||||
setCrntUrl(navUrl);
|
||||
}
|
||||
iframeRef.current!.src = navUrl;
|
||||
if (iframeRef.current) {
|
||||
iframeRef.current.src = navUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const msgListener = (message: MessageEvent<EventData<any>>) => {
|
||||
const msgListener = (message: MessageEvent<EventData<string>>) => {
|
||||
if (message.data.command === PreviewCommands.toWebview.updateUrl) {
|
||||
setCrntUrl(message.data.payload);
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ 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 { DOCS_SUBMODULES, FrameworkDetectors, GIT_CONFIG, SETTING_FRAMEWORK_START, SETTING_GIT_COMMIT_MSG, SETTING_GIT_ENABLED, SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL } from '../../../constants';
|
||||
import { Button as VSCodeButton } from 'vscrui';
|
||||
import { DOCS_SUBMODULES, FrameworkDetectors, GIT_CONFIG, SETTING_FRAMEWORK_START, SETTING_GIT_COMMIT_MSG, SETTING_GIT_ENABLED, SETTING_PANEL_OPEN_ON_SUPPORTED_FILE, SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL } from '../../../constants';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { SettingsCheckbox } from './SettingsCheckbox';
|
||||
import { ChevronRightIcon } from '@heroicons/react/24/outline';
|
||||
import { BooleanOption } from '../Header/BooleanOption';
|
||||
|
||||
export interface ICommonSettingsProps { }
|
||||
|
||||
@@ -73,6 +74,15 @@ export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (pr
|
||||
<Startup settings={settings} />
|
||||
</div>
|
||||
|
||||
<div className='py-4'>
|
||||
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsOpenPanelForSupportedFiles)}</h2>
|
||||
|
||||
<BooleanOption
|
||||
label={l10n.t(LocalizationKey.settingsOpenPanelForSupportedFilesLabel)}
|
||||
name={SETTING_PANEL_OPEN_ON_SUPPORTED_FILE}
|
||||
value={settings?.openPanelForSupportedFiles} />
|
||||
</div>
|
||||
|
||||
<div className='py-4'>
|
||||
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsGit)}</h2>
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ 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 { }
|
||||
|
||||
export const IntegrationsView: React.FunctionComponent<IIntegrationsViewProps> = ({ }: React.PropsWithChildren<IIntegrationsViewProps>) => {
|
||||
export const IntegrationsView: React.FunctionComponent<IIntegrationsViewProps> = () => {
|
||||
const [deeplApiKey, setDeeplApiKey] = React.useState<string>('');
|
||||
const [azureApiKey, setAzureApiKey] = React.useState<string>('');
|
||||
const [azureRegion, setAzureRegion] = React.useState<string>('');
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -10,12 +10,12 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
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,6 +23,74 @@ export const SettingsView: React.FunctionComponent<ISettingsViewProps> = (_: Rea
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const tabs: ITab[] = React.useMemo(() => {
|
||||
const 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 [];
|
||||
}
|
||||
|
||||
const 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));
|
||||
}, []);
|
||||
@@ -52,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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
let snippets: Snippets = Object.assign({}, settings?.snippets || {});
|
||||
const snippets: Snippets = Object.assign({}, settings?.snippets || {});
|
||||
const snippetLines = snippetOriginalBody.split('\n');
|
||||
|
||||
const crntSnippet = Object.assign({}, snippets[snippetKey]);
|
||||
@@ -183,7 +183,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
|
||||
<div className='inline-block mr-1 mt-1 text-xs text-[var(--vscode-button-secondaryForeground)] bg-[var(--vscode-button-secondaryBackground)] border border-[var(--frontmatter-border)] rounded px-1 py-0.5'>
|
||||
{
|
||||
snippet.isMediaSnippet ? l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeContent) : l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeMedia)
|
||||
snippet.isMediaSnippet ? l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeMedia) : l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeContent)
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -79,11 +79,11 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
|
||||
);
|
||||
|
||||
const snippetBody = useMemo(() => {
|
||||
let body = typeof snippet.body === 'string' ? snippet.body : snippet.body.join(`\n`);
|
||||
const body = typeof snippet.body === 'string' ? snippet.body : snippet.body.join(`\n`);
|
||||
|
||||
const obj: any = {};
|
||||
const obj: { [key: string]: string } = {};
|
||||
for (const field of fields) {
|
||||
obj[field.name] = field.value;
|
||||
obj[field.name] = field.value as string;
|
||||
}
|
||||
|
||||
return SnippetParser.render(body, obj, snippet.openingTags, snippet.closingTags);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ChevronDownIcon } from '@heroicons/react/24/outline';
|
||||
import { Choice, SnippetField, SnippetInfoField } from '../../../models';
|
||||
import { useEffect } from 'react';
|
||||
import { TextField } from '../Common/TextField';
|
||||
import { NumberField } from '../Common/NumberField';
|
||||
|
||||
export interface ISnippetInputFieldProps {
|
||||
field: SnippetField;
|
||||
@@ -31,7 +32,7 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
<div className="relative">
|
||||
<select
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
value={field.value as string || ''}
|
||||
className={`block w-full sm:text-sm pr-2 appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
@@ -69,7 +70,7 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
return (
|
||||
<TextField
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
value={field.value as string || ''}
|
||||
description={field.description}
|
||||
onChange={(e) => onValueChange(field, e)}
|
||||
rows={4}
|
||||
@@ -78,10 +79,21 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === 'number') {
|
||||
return (
|
||||
<NumberField
|
||||
name={field.name}
|
||||
value={field.value as string || ''}
|
||||
description={field.description}
|
||||
onChange={(e) => onValueChange(field, e)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TextField
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
value={field.value as string || ''}
|
||||
description={field.description}
|
||||
onChange={(e) => onValueChange(field, e)}
|
||||
/>
|
||||
|
||||
@@ -21,9 +21,7 @@ import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatu
|
||||
|
||||
export interface ISnippetsProps { }
|
||||
|
||||
export const Snippets: React.FunctionComponent<ISnippetsProps> = (
|
||||
_: React.PropsWithChildren<ISnippetsProps>
|
||||
) => {
|
||||
export const Snippets: React.FunctionComponent<ISnippetsProps> = () => {
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const viewData = useRecoilValue(ViewDataSelector);
|
||||
const mode = useRecoilValue(ModeAtom);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -41,7 +41,7 @@ export const TaxonomyLookup: React.FunctionComponent<ITaxonomyLookupProps> = ({
|
||||
return false;
|
||||
}
|
||||
|
||||
let fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
const fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
|
||||
return fieldName && page[fieldName] ? page[fieldName].includes(value) : false;
|
||||
}).length;
|
||||
|
||||
@@ -80,7 +80,7 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
|
||||
}, [data, taxonomy, debounceFilterValue]);
|
||||
|
||||
const unmappedItems = useMemo(() => {
|
||||
let unmapped: string[] = [];
|
||||
const unmapped: string[] = [];
|
||||
|
||||
if (!pages || !settings?.contentTypes || !taxonomy) {
|
||||
return unmapped;
|
||||
@@ -100,7 +100,7 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
|
||||
return false;
|
||||
}
|
||||
|
||||
let fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
const fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
|
||||
if (fieldName && page[fieldName]) {
|
||||
values = page[fieldName];
|
||||
|
||||
@@ -5,18 +5,18 @@ import { SettingsSelector } from '../../state';
|
||||
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 { Button } from 'vscrui';
|
||||
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;
|
||||
@@ -70,7 +70,7 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
|
||||
continue;
|
||||
}
|
||||
|
||||
let fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
const fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
|
||||
if (fieldName && (!page[fieldName] || page[fieldName].indexOf(value) === -1)) {
|
||||
untagged.push(page);
|
||||
@@ -78,7 +78,7 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
|
||||
}
|
||||
}
|
||||
|
||||
untagged = untagged.sort(Sorting.number('fmPublished')).reverse();
|
||||
untagged = untagged.sort(Sorting.numerically('fmPublished')).reverse();
|
||||
|
||||
if (debounceFilterValue) {
|
||||
return untagged.filter((p) => p.title.toLowerCase().includes(debounceFilterValue.toLowerCase()));
|
||||
@@ -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)}
|
||||
@@ -225,8 +229,8 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end space-x-2'>
|
||||
<Button onClick={onDismiss} secondary>{l10n.t(LocalizationKey.commonCancel)}</Button>
|
||||
<Button onClick={() => onContentMapping(value, pageMappings)}>{l10n.t(LocalizationKey.commonApply)}</Button>
|
||||
<Button className='!py-2' onClick={onDismiss} appearance='secondary'>{l10n.t(LocalizationKey.commonCancel)}</Button>
|
||||
<Button className='!py-2' onClick={() => onContentMapping(value, pageMappings)}>{l10n.t(LocalizationKey.commonApply)}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function useMediaInfo(media?: MediaInfo) {
|
||||
}, [media]);
|
||||
|
||||
const mediaDetails = useMemo(() => {
|
||||
let sizeDetails = [];
|
||||
const sizeDetails = [];
|
||||
|
||||
if (mediaDimensions) {
|
||||
sizeDetails.push(mediaDimensions);
|
||||
|
||||
@@ -107,7 +107,14 @@ export default function usePages(pages: Page[]) {
|
||||
for (const filter of filterNames) {
|
||||
const filterValue = filters[filter];
|
||||
if (filterValue) {
|
||||
pagesSorted = pagesSorted.filter((page) => page[filter] === filterValue);
|
||||
pagesSorted = pagesSorted.filter((page) => {
|
||||
const value = page[filter];
|
||||
if (Array.isArray(value)) {
|
||||
return value.includes(filterValue);
|
||||
} else {
|
||||
return value === filterValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,8 +166,6 @@ export default function usePages(pages: Page[]) {
|
||||
|
||||
if (tab !== Tab.All) {
|
||||
crntPages = crntPages.filter((page) => page.fmDraft === tab);
|
||||
} else {
|
||||
crntPages = crntPages;
|
||||
}
|
||||
} else {
|
||||
// Draft field is a boolean field
|
||||
@@ -194,8 +199,6 @@ export default function usePages(pages: Page[]) {
|
||||
crntPages = drafts;
|
||||
} else if (tab === Tab.Scheduled) {
|
||||
crntPages = scheduled;
|
||||
} else {
|
||||
crntPages = crntPages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,11 +243,11 @@ export default function usePages(pages: Page[]) {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let usedSorting = sorting;
|
||||
const usedSorting = sorting;
|
||||
|
||||
const startPageProcessing = () => {
|
||||
// Check if search needs to be performed
|
||||
let searchedPages = pages;
|
||||
const searchedPages = pages;
|
||||
if (search) {
|
||||
Messenger.send(DashboardMessage.searchPages, { query: search });
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { I10nProvider } from './providers/I10nProvider';
|
||||
import { SentryInit } from '../utils/sentryInit';
|
||||
import { WEBSITE_LINKS } from '../constants';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
declare const acquireVsCodeApi: <T = unknown>() => {
|
||||
getState: () => T;
|
||||
setState: (data: T) => void;
|
||||
@@ -119,4 +120,8 @@ if (elm) {
|
||||
}
|
||||
|
||||
// Webpack HMR
|
||||
if ((module as any).hot) (module as any).hot.accept();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((module as any).hot) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(module as any).hot.accept();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum DashboardViewType {
|
||||
Grid = 1,
|
||||
List
|
||||
List,
|
||||
Structure
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { I18nConfig } from '../../models';
|
||||
import { ContentFolder, I18nConfig } from '../../models';
|
||||
|
||||
export interface Page {
|
||||
// Properties for caching
|
||||
@@ -20,15 +20,16 @@ export interface Page {
|
||||
fmCategories: string[];
|
||||
fmContentType: string;
|
||||
fmDateFormat: string | undefined;
|
||||
fmPageFolder: ContentFolder | undefined;
|
||||
|
||||
// i18n fields
|
||||
fmDefaultLocale?: boolean;
|
||||
fmLocale?: I18nConfig;
|
||||
fmTranslations?: {
|
||||
fmTranslations?: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
title: string;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ContentType,
|
||||
CustomScript,
|
||||
CustomTaxonomy,
|
||||
DataFolder,
|
||||
DraftField,
|
||||
FilterType,
|
||||
Framework,
|
||||
@@ -30,6 +31,7 @@ export interface Settings {
|
||||
categories: string[];
|
||||
customTaxonomy: CustomTaxonomy[];
|
||||
openOnStart: boolean | null;
|
||||
openPanelForSupportedFiles: boolean | null;
|
||||
versionInfo: VersionInfo;
|
||||
pageViewType: DashboardViewType | undefined;
|
||||
contentTypes: ContentType[];
|
||||
@@ -40,9 +42,11 @@ export interface Settings {
|
||||
draftField: DraftField | null | undefined;
|
||||
customSorting: SortingSetting[] | undefined;
|
||||
filters: (FilterType | { title: string; name: string })[] | undefined;
|
||||
grouping: { title: string; name: string }[] | undefined;
|
||||
dashboardState: DashboardState;
|
||||
scripts: CustomScript[];
|
||||
dataFiles: DataFile[] | undefined;
|
||||
dataFolders: DataFolder[];
|
||||
dataTypes: DataType[] | undefined;
|
||||
isBacker: boolean | undefined;
|
||||
snippets: Snippets | undefined;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const FilterValuesAtom = atom<{ [filter: string]: string[] }>({
|
||||
export const FilterValuesAtom = atom<{ [filter: string]: string[] | string[][] }>({
|
||||
key: 'FilterValuesAtom',
|
||||
default: {}
|
||||
});
|
||||
|
||||
@@ -115,7 +115,9 @@
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
@apply mt-4 inline-flex w-auto items-center rounded border border-transparent bg-[var(--frontmatter-button-background)] px-3 py-2 text-sm font-medium leading-4 text-[var(--vscode-button-foreground)];
|
||||
@apply mt-4 inline-flex w-auto items-center rounded-[2px] border border-transparent bg-[var(--frontmatter-button-background)] px-[11px] py-[4px] text-[var(--vscode-button-foreground)];
|
||||
font-family: var(--vscode-font-family);
|
||||
font-size: var(--vscode-font-size, 13px);
|
||||
|
||||
&:hover {
|
||||
@apply bg-[var(--frontmatter-button-hoverBackground)];
|
||||
@@ -310,7 +312,7 @@
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
@apply rounded text-vulcan-500;
|
||||
@apply rounded-[2px] text-vulcan-500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export const darkenColor = (color: string | undefined, percentage: number) => {
|
||||
// Check if the color is in rgba format
|
||||
if (color.startsWith('rgba')) {
|
||||
// Extract the alpha value
|
||||
const alphaMatch = color.match(/[\d\.]+(?=\))/);
|
||||
const alphaMatch = color.match(/[\d.]+(?=\))/);
|
||||
const alpha = alphaMatch ? Number(alphaMatch[0]) : 1;
|
||||
|
||||
return `rgba(${darkenedR}, ${darkenedG}, ${darkenedB}, ${alpha})`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { darkenColor, opacityColor, preserveColor } from '.';
|
||||
|
||||
export const updateCssVariables = (isDarkTheme: boolean = true) => {
|
||||
export const updateCssVariables = (isDarkTheme = true) => {
|
||||
const styles = getComputedStyle(document.documentElement);
|
||||
|
||||
// Lightbox
|
||||
@@ -98,4 +98,17 @@ export const updateCssVariables = (isDarkTheme: boolean = true) => {
|
||||
'--frontmatter-border-active',
|
||||
darkenColor(borderColor, isDarkTheme ? -30 : 30) || 'var(--vscode-activityBar-activeBorder)'
|
||||
);
|
||||
|
||||
// SEO - Success/Warning colors
|
||||
const successColor = styles.getPropertyValue('--vscode-charts-green');
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-success-background',
|
||||
opacityColor(successColor, 0.05) || 'var(--vscode-charts-green)'
|
||||
);
|
||||
|
||||
const warningColor = styles.getPropertyValue('--vscode-statusBarItem-warningBackground');
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-warning-background',
|
||||
opacityColor(warningColor, 0.05) || 'var(--vscode-statusBarItem-warningBackground)'
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import ContentProvider from './providers/ContentProvider';
|
||||
import { PagesListener } from './listeners/dashboard';
|
||||
import { ModeSwitch } from './services/ModeSwitch';
|
||||
import { PagesParser } from './services/PagesParser';
|
||||
import { ContentType, Telemetry, Extension } from './helpers';
|
||||
import { ContentType, Extension } from './helpers';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import {
|
||||
Backers,
|
||||
@@ -39,6 +39,7 @@ import { i18n } from './commands/i18n';
|
||||
import { UriHandler } from './providers/UriHandler';
|
||||
|
||||
let pageUpdateDebouncer: { (fnc: any, time: number): void };
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let editDebounce: { (fnc: any, time: number): void };
|
||||
let collection: vscode.DiagnosticCollection;
|
||||
|
||||
@@ -48,7 +49,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
const extension = Extension.getInstance(context);
|
||||
|
||||
Logger.info(`Activating ${EXTENSION_NAME} version ${Extension.getInstance().version}...`);
|
||||
Logger.info(`Logging level: ${Logger.getLevel()}`);
|
||||
|
||||
// Set development context
|
||||
if (!Extension.getInstance().isProductionMode) {
|
||||
@@ -56,7 +56,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
// Sponsor check
|
||||
Backers.init(context).then(() => {});
|
||||
Backers.init(context);
|
||||
|
||||
// Make sure the EN language file is loaded
|
||||
if (!vscode.l10n.uri) {
|
||||
@@ -120,8 +120,9 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
// Register the taxonomy commands
|
||||
Taxonomy.registerCommands(subscriptions);
|
||||
|
||||
// Register all the article commands
|
||||
// Register all the article commands and listeners
|
||||
Article.registerCommands(subscriptions);
|
||||
Article.registerListeners(subscriptions);
|
||||
|
||||
// Template creation
|
||||
Template.registerCommands();
|
||||
@@ -143,7 +144,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
SettingsHelper.startListening();
|
||||
|
||||
// Create the status bar
|
||||
let fmStatusBarItem = vscode.window.createStatusBarItem(
|
||||
const fmStatusBarItem = vscode.window.createStatusBarItem(
|
||||
'fm-statusBarItem',
|
||||
vscode.StatusBarAlignment.Right,
|
||||
-100
|
||||
@@ -180,9 +181,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
// Automatically run the command
|
||||
triggerPageUpdate(`main`);
|
||||
|
||||
// Listener for file edit changes
|
||||
subscriptions.push(vscode.workspace.onWillSaveTextDocument(handleAutoDateUpdate));
|
||||
|
||||
// Listener for file saves
|
||||
subscriptions.push(PagesListener.saveFileWatcher());
|
||||
|
||||
@@ -240,22 +238,21 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
// Subscribe all commands
|
||||
subscriptions.push(PanelView, collapseAll, fmStatusBarItem);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`𝖥𝗋𝗈𝗇𝗍 𝖬𝖺𝗍𝗍𝖾𝗋 𝖢𝖬𝖲 𝖺𝖼𝗍𝗂𝗏𝖺𝗍𝖾𝖽! 𝖱𝖾𝖺𝖽𝗒 𝗍𝗈 𝗌𝗍𝖺𝗋𝗍 𝗐𝗋𝗂𝗍𝗂𝗇𝗀... 👩💻🧑💻👨💻`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export function deactivate() {}
|
||||
|
||||
const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => {
|
||||
Article.autoUpdate(e);
|
||||
};
|
||||
|
||||
const triggerPageUpdate = (location: string) => {
|
||||
const triggerPageUpdate = async (location: string) => {
|
||||
Logger.verbose(`Trigger page update: ${location}`);
|
||||
pageUpdateDebouncer(() => {
|
||||
StatusListener.verify(collection);
|
||||
}, 1000);
|
||||
|
||||
if (location === 'onDidChangeActiveTextEditor') {
|
||||
await PanelProvider.openOnSupportedFile();
|
||||
PanelProvider.getInstance()?.updateCurrentFile();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
@@ -354,7 +356,7 @@ export class ArticleHelper {
|
||||
* @returns A boolean indicating whether the file is a page bundle or not.
|
||||
*/
|
||||
public static async isPageBundle(filePath: string) {
|
||||
let article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
if (!article) {
|
||||
return false;
|
||||
}
|
||||
@@ -498,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);
|
||||
}
|
||||
}
|
||||
@@ -534,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);
|
||||
@@ -543,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);
|
||||
@@ -560,7 +572,7 @@ export class ArticleHelper {
|
||||
await mkdirAsync(newFolder, { recursive: true });
|
||||
newFilePath = join(
|
||||
newFolder,
|
||||
`${sanitize(contentType.defaultFileName ?? `index`)}.${
|
||||
`${sanitize(contentType.defaultFileName || `index`, { isFileName: true })}.${
|
||||
fileExtension || contentType.fileType || fileType
|
||||
}`
|
||||
);
|
||||
@@ -596,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);
|
||||
@@ -615,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;
|
||||
@@ -647,7 +684,7 @@ export class ArticleHelper {
|
||||
}
|
||||
|
||||
if (fieldName === 'slug' && (fieldValue === null || fieldValue === '')) {
|
||||
fmData[fieldName] = SlugHelper.createSlug(title, fmData, slugTemplate);
|
||||
fmData[fieldName] = SlugHelper.createSlug(title, fmData, filePath, slugTemplate);
|
||||
}
|
||||
|
||||
fmData[fieldName] = await processArticlePlaceholdersFromPath(fmData[fieldName], filePath);
|
||||
@@ -667,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;
|
||||
@@ -711,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);
|
||||
}
|
||||
|
||||
@@ -769,7 +807,8 @@ export class ArticleHelper {
|
||||
const elms: Parent[] | Link[] = this.getAllElms(mdTree);
|
||||
|
||||
const headings = elms.filter((node) => node.type === 'heading');
|
||||
const paragraphs = elms.filter((node) => node.type === 'paragraph').length;
|
||||
const paragraphNodes = elms.filter((node) => node.type === 'paragraph');
|
||||
const paragraphs = paragraphNodes.length;
|
||||
const images = elms.filter((node) => node.type === 'image').length;
|
||||
const links: string[] = elms
|
||||
.filter((node) => node.type === 'link')
|
||||
@@ -798,6 +837,21 @@ export class ArticleHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract first paragraph text for SEO keyword checking
|
||||
let firstParagraph = '';
|
||||
if (paragraphNodes.length > 0) {
|
||||
const firstParagraphNode = paragraphNodes[0];
|
||||
const extractTextFromNode = (node: any): string => {
|
||||
if (node.type === 'text') {
|
||||
return node.value || '';
|
||||
} else if (node.children && Array.isArray(node.children)) {
|
||||
return node.children.map(extractTextFromNode).join('');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
firstParagraph = extractTextFromNode(firstParagraphNode);
|
||||
}
|
||||
|
||||
const wordCount = this.wordCount(0, mdTree);
|
||||
|
||||
return {
|
||||
@@ -808,7 +862,8 @@ export class ArticleHelper {
|
||||
internalLinks,
|
||||
externalLinks: externalLinks.length,
|
||||
wordCount,
|
||||
content: article.content
|
||||
content: article.content,
|
||||
firstParagraph
|
||||
};
|
||||
}
|
||||
|
||||
@@ -875,7 +930,7 @@ export class ArticleHelper {
|
||||
const commaSeparated = Settings.get<string[]>(SETTING_COMMA_SEPARATED_FIELDS);
|
||||
|
||||
if (fileContents) {
|
||||
let article = FrontMatterParser.fromFile(fileContents);
|
||||
const article = FrontMatterParser.fromFile(fileContents);
|
||||
|
||||
if (article?.data) {
|
||||
if (commaSeparated) {
|
||||
|
||||
@@ -564,7 +564,7 @@ export class ContentType {
|
||||
|
||||
const allRequiredFields = ContentType.findRequiredFieldsDeep(contentType.fields);
|
||||
|
||||
let emptyFields: Field[][] = [];
|
||||
const emptyFields: Field[][] = [];
|
||||
|
||||
for (const fields of allRequiredFields) {
|
||||
const fieldValue = this.getFieldValue(
|
||||
@@ -656,7 +656,7 @@ export class ContentType {
|
||||
return [];
|
||||
}
|
||||
|
||||
let foundBlocks = [];
|
||||
const foundBlocks = [];
|
||||
for (const group of groups) {
|
||||
const block = blocks.find((block) => block.id === group);
|
||||
if (!block) {
|
||||
@@ -960,11 +960,12 @@ export class ContentType {
|
||||
templateData = await ArticleHelper.getFrontMatterByPath(templatePath);
|
||||
}
|
||||
|
||||
let newFilePath: string | undefined = await ArticleHelper.createContent(
|
||||
const newFilePath: string | undefined = await ArticleHelper.createContent(
|
||||
contentType,
|
||||
folderPath,
|
||||
titleValue
|
||||
);
|
||||
|
||||
if (!newFilePath) {
|
||||
return;
|
||||
}
|
||||
@@ -1045,7 +1046,7 @@ export class ContentType {
|
||||
filePath: string,
|
||||
clearEmpty: boolean,
|
||||
contentType: IContentType,
|
||||
isRoot: boolean = true
|
||||
isRoot = true
|
||||
): Promise<any> {
|
||||
if (obj.fields) {
|
||||
const titleField = getTitleField();
|
||||
@@ -1062,7 +1063,8 @@ export class ContentType {
|
||||
data[field.name] = processArticlePlaceholdersFromData(
|
||||
field.default as string,
|
||||
data,
|
||||
contentType
|
||||
contentType,
|
||||
filePath
|
||||
);
|
||||
data[field.name] = processTimePlaceholders(
|
||||
data[field.name],
|
||||
@@ -1106,7 +1108,7 @@ export class ContentType {
|
||||
filePath
|
||||
);
|
||||
} else if (defaultValue && Array.isArray(defaultValue)) {
|
||||
let defaultValues = [];
|
||||
const defaultValues = [];
|
||||
for (let value of defaultValue as string[]) {
|
||||
if (typeof value === 'string') {
|
||||
value = await ContentType.processFieldPlaceholders(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Settings } from './SettingsHelper';
|
||||
import { CommandType, EnvironmentType } from './../models/PanelSettings';
|
||||
import { CommandType } 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 } from '.';
|
||||
@@ -13,9 +13,10 @@ import { Dashboard } from '../commands/Dashboard';
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { SETTING_CUSTOM_SCRIPTS } from '../constants';
|
||||
import { existsAsync } from '../utils';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { evaluateCommand, existsAsync, getPlatform } from '../utils';
|
||||
import { LocalizationKey, localize } from '../localization';
|
||||
import { ScriptAction } from '../models';
|
||||
import { Copilot } from '../services/Copilot';
|
||||
|
||||
export class CustomScript {
|
||||
/**
|
||||
@@ -72,7 +73,9 @@ export class CustomScript {
|
||||
|
||||
if (!path) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) return;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
articlePath = editor.document.uri.fsPath;
|
||||
article = ArticleHelper.getFrontMatter(editor);
|
||||
@@ -99,7 +102,7 @@ export class CustomScript {
|
||||
);
|
||||
} else {
|
||||
Notifications.warning(
|
||||
l10n.t(LocalizationKey.helpersCustomScriptSingleRunArticleWarning, script.title)
|
||||
localize(LocalizationKey.helpersCustomScriptSingleRunArticleWarning, script.title)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -115,17 +118,17 @@ export class CustomScript {
|
||||
|
||||
if (!folders || folders.length === 0) {
|
||||
Notifications.warning(
|
||||
l10n.t(LocalizationKey.helpersCustomScriptBulkRunNoFilesWarning, script.title)
|
||||
localize(LocalizationKey.helpersCustomScriptBulkRunNoFilesWarning, script.title)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let output: string[] = [];
|
||||
const output: string[] = [];
|
||||
|
||||
window.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
|
||||
title: localize(LocalizationKey.helpersCustomScriptExecuting, script.title),
|
||||
cancellable: false
|
||||
},
|
||||
async (_, __) => {
|
||||
@@ -171,7 +174,7 @@ export class CustomScript {
|
||||
): Promise<void> {
|
||||
if (!path) {
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersCustomScriptRunMediaScriptNoFolderWarning, script.title)
|
||||
localize(LocalizationKey.helpersCustomScriptRunMediaScriptNoFolderWarning, script.title)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -180,7 +183,7 @@ export class CustomScript {
|
||||
window.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
|
||||
title: localize(LocalizationKey.helpersCustomScriptExecuting, script.title),
|
||||
cancellable: false
|
||||
},
|
||||
async () => {
|
||||
@@ -266,12 +269,7 @@ export class CustomScript {
|
||||
try {
|
||||
const data: {
|
||||
frontmatter?: { [key: string]: any };
|
||||
fmAction?:
|
||||
| 'open'
|
||||
| 'copyMediaMetadata'
|
||||
| 'copyMediaMetadataAndDelete'
|
||||
| 'deleteMedia'
|
||||
| 'fieldAction';
|
||||
fmAction?: ScriptAction;
|
||||
fmPath?: string;
|
||||
fmSourcePath?: string;
|
||||
fmDestinationPath?: string;
|
||||
@@ -283,7 +281,9 @@ export class CustomScript {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
if (!articlePath) {
|
||||
if (!editor) return;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
articlePath = editor.document.uri.fsPath;
|
||||
article = ArticleHelper.getFrontMatter(editor);
|
||||
@@ -305,7 +305,10 @@ export class CustomScript {
|
||||
throw new Error(`Couldn't update article.`);
|
||||
}
|
||||
Notifications.info(
|
||||
l10n.t(LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess, script.title)
|
||||
localize(
|
||||
LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess,
|
||||
script.title
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if (data.fmAction) {
|
||||
@@ -341,10 +344,12 @@ export class CustomScript {
|
||||
window
|
||||
.showInformationMessage(
|
||||
`${script.title}: ${output}`,
|
||||
l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)
|
||||
localize(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)
|
||||
)
|
||||
.then((value) => {
|
||||
if (value === l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)) {
|
||||
if (
|
||||
value === localize(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)
|
||||
) {
|
||||
vscodeEnv.clipboard.writeText(output);
|
||||
}
|
||||
});
|
||||
@@ -352,7 +357,7 @@ export class CustomScript {
|
||||
}
|
||||
} else {
|
||||
Notifications.info(
|
||||
l10n.t(LocalizationKey.helpersCustomScriptShowOutputSuccess, script.title)
|
||||
localize(LocalizationKey.helpersCustomScriptShowOutputSuccess, script.title)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -369,7 +374,7 @@ export class CustomScript {
|
||||
wsPath: string,
|
||||
args: string
|
||||
): Promise<string> {
|
||||
const osType = os.type();
|
||||
const platform = getPlatform();
|
||||
|
||||
// Check the command to use
|
||||
let command = script.nodeBin || 'node';
|
||||
@@ -377,6 +382,10 @@ export class CustomScript {
|
||||
command = script.command;
|
||||
}
|
||||
|
||||
if (script.command === CommandType.Node && platform !== 'windows') {
|
||||
command = await evaluateCommand(CommandType.Node);
|
||||
}
|
||||
|
||||
let scriptPath = join(wsPath, script.script);
|
||||
if (script.script.includes(WORKSPACE_PLACEHOLDER)) {
|
||||
scriptPath = Folders.getAbsFilePath(script.script);
|
||||
@@ -384,19 +393,15 @@ export class CustomScript {
|
||||
|
||||
// Check if there is an environments overwrite required
|
||||
if (script.environments) {
|
||||
let crntType: EnvironmentType | null = null;
|
||||
if (osType === 'Windows_NT') {
|
||||
crntType = 'windows';
|
||||
} else if (osType === 'Darwin') {
|
||||
crntType = 'macos';
|
||||
} else {
|
||||
crntType = 'linux';
|
||||
}
|
||||
|
||||
const environment = script.environments.find((e) => e.type === crntType);
|
||||
const environment = script.environments.find((e) => e.type === platform);
|
||||
if (environment && environment.script && environment.command) {
|
||||
if (await CustomScript.validateCommand(environment.command)) {
|
||||
command = environment.command;
|
||||
|
||||
if (command === CommandType.Node && platform !== 'windows') {
|
||||
command = await evaluateCommand(CommandType.Node);
|
||||
}
|
||||
|
||||
scriptPath = join(wsPath, environment.script);
|
||||
if (environment.script.includes(WORKSPACE_PLACEHOLDER)) {
|
||||
scriptPath = Folders.getAbsFilePath(environment.script);
|
||||
@@ -410,21 +415,38 @@ export class CustomScript {
|
||||
throw new Error(`Script not found: ${scriptPath}`);
|
||||
}
|
||||
|
||||
if (osType === 'Windows_NT' && command.toLowerCase() === 'powershell') {
|
||||
if (platform === 'windows' && command.toLowerCase() === 'powershell') {
|
||||
command = `${command} -File`;
|
||||
}
|
||||
|
||||
const fullScript = `${command} "${scriptPath}" ${args}`;
|
||||
Logger.info(l10n.t(LocalizationKey.helpersCustomScriptExecuting, fullScript));
|
||||
Logger.info(localize(LocalizationKey.helpersCustomScriptExecuting, fullScript));
|
||||
|
||||
const output = await CustomScript.processExecution(fullScript, wsPath);
|
||||
return output;
|
||||
}
|
||||
|
||||
// Recursive function to process the execution of the script
|
||||
private static async processExecution(fullScript: string, wsPath: string): Promise<string> {
|
||||
const output: string = await CustomScript.executeScriptAsync(fullScript, wsPath);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(output);
|
||||
if (data.questions) {
|
||||
const { questions, fmAction, fmPrompt } = data as {
|
||||
questions?: {
|
||||
name: string;
|
||||
message: string;
|
||||
default?: string;
|
||||
options?: string[];
|
||||
}[];
|
||||
fmAction?: ScriptAction;
|
||||
fmPrompt?: any; // When the 'promptCopilot' action is used
|
||||
};
|
||||
|
||||
if (questions) {
|
||||
const answers: string[] = [];
|
||||
|
||||
for (const question of data.questions) {
|
||||
for (const question of questions) {
|
||||
if (question.name && question.message) {
|
||||
let answer;
|
||||
if (question.options) {
|
||||
@@ -452,7 +474,16 @@ export class CustomScript {
|
||||
|
||||
if (answers.length > 0) {
|
||||
const newScript = `${fullScript} ${answers.join(' ')}`;
|
||||
return await CustomScript.executeScriptAsync(newScript, wsPath);
|
||||
return await CustomScript.processExecution(newScript, wsPath);
|
||||
}
|
||||
} else if (fmAction) {
|
||||
if (fmAction === 'promptCopilot' && fmPrompt) {
|
||||
const response = await Copilot.promptCopilot(fmPrompt);
|
||||
if (response) {
|
||||
const promptResponse = `promptResponse="${response}"`;
|
||||
const newScript = `${fullScript} ${promptResponse}`;
|
||||
return await CustomScript.processExecution(newScript, wsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -469,7 +500,7 @@ export class CustomScript {
|
||||
* @returns
|
||||
*/
|
||||
private static async executeScriptAsync(fullScript: string, wsPath: string): Promise<string> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(fullScript, { cwd: wsPath }, (error, stdout) => {
|
||||
if (error) {
|
||||
Logger.error(error.message);
|
||||
@@ -498,7 +529,7 @@ export class CustomScript {
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
Logger.error(l10n.t(LocalizationKey.helpersCustomScriptValidateCommandError, command));
|
||||
Logger.error(localize(LocalizationKey.helpersCustomScriptValidateCommandError, command));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GitListener } from './../listeners/general/GitListener';
|
||||
import { basename, join } from 'path';
|
||||
import { join } from 'path';
|
||||
import { workspace } from 'vscode';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { Project } from '../commands/Project';
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ExtensionState,
|
||||
SETTING_CONTENT_DRAFT_FIELD,
|
||||
SETTING_CONTENT_FILTERS,
|
||||
SETTING_CONTENT_GROUPING,
|
||||
SETTING_CONTENT_SORTING,
|
||||
SETTING_CONTENT_SORTING_DEFAULT,
|
||||
SETTING_DASHBOARD_OPENONSTART,
|
||||
@@ -23,7 +24,6 @@ import {
|
||||
SETTING_MEDIA_SUPPORTED_MIMETYPES,
|
||||
SETTING_TAXONOMY_CUSTOM,
|
||||
SETTING_TEMPLATES_ENABLED,
|
||||
SETTING_GIT_ENABLED,
|
||||
SETTING_DASHBOARD_CONTENT_PAGINATION,
|
||||
SETTING_SNIPPETS_WRAPPER,
|
||||
SETTING_DASHBOARD_CONTENT_CARD_DATE,
|
||||
@@ -31,7 +31,8 @@ import {
|
||||
SETTING_DASHBOARD_CONTENT_CARD_STATE,
|
||||
SETTING_DASHBOARD_CONTENT_CARD_DESCRIPTION,
|
||||
SETTING_WEBSITE_URL,
|
||||
SETTING_MEDIA_CONTENTTYPES
|
||||
SETTING_MEDIA_CONTENTTYPES,
|
||||
SETTING_PANEL_OPEN_ON_SUPPORTED_FILE
|
||||
} from '../constants';
|
||||
import {
|
||||
DashboardViewType,
|
||||
@@ -58,11 +59,12 @@ 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;
|
||||
|
||||
public static async get(clear: boolean = false) {
|
||||
public static async get(clear = false) {
|
||||
if (!this.cachedSettings || clear) {
|
||||
this.cachedSettings = await this.getSettings();
|
||||
}
|
||||
@@ -107,6 +109,7 @@ export class DashboardSettings {
|
||||
categories: (await TaxonomyHelper.get(TaxonomyType.Category)) || [],
|
||||
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
|
||||
openOnStart: Settings.get(SETTING_DASHBOARD_OPENONSTART),
|
||||
openPanelForSupportedFiles: Settings.get(SETTING_PANEL_OPEN_ON_SUPPORTED_FILE),
|
||||
versionInfo: ext.getVersion(),
|
||||
pageViewType: await ext.getState<DashboardViewType | undefined>(
|
||||
ExtensionState.PagesView,
|
||||
@@ -118,6 +121,7 @@ export class DashboardSettings {
|
||||
contentFolders: await Folders.get(),
|
||||
filters:
|
||||
Settings.get<(FilterType | { title: string; name: string })[]>(SETTING_CONTENT_FILTERS),
|
||||
grouping: Settings.get<{ title: string; name: string }[]>(SETTING_CONTENT_GROUPING),
|
||||
crntFramework: Settings.get<string>(SETTING_FRAMEWORK_ID),
|
||||
framework: !isInitialized && wsFolder ? await FrameworkDetector.get(wsFolder.fsPath) : null,
|
||||
scripts: Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || [],
|
||||
@@ -158,6 +162,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),
|
||||
@@ -190,9 +195,9 @@ export class DashboardSettings {
|
||||
const files = Settings.get<DataFile[]>(SETTING_DATA_FILES);
|
||||
const folders = Settings.get<DataFolder[]>(SETTING_DATA_FOLDERS);
|
||||
|
||||
let clonedFiles = Object.assign([], files);
|
||||
const clonedFiles = Object.assign([], files);
|
||||
if (folders) {
|
||||
for (let folder of folders) {
|
||||
for (const folder of folders) {
|
||||
if (!folder.path) {
|
||||
continue;
|
||||
}
|
||||
@@ -216,17 +221,8 @@ 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);
|
||||
for (const dataFile of dataFiles) {
|
||||
clonedFiles.push(DataListener.createDataFileObject(dataFile.fsPath, folder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { parse, parseISO, parseJSON, format } from 'date-fns';
|
||||
import { formatInTimeZone } from 'date-fns-tz';
|
||||
|
||||
export class DateHelper {
|
||||
public static formatUpdate(value: string | null | undefined): string | null {
|
||||
@@ -19,6 +20,20 @@ export class DateHelper {
|
||||
return format(date, DateHelper.formatUpdate(dateFormat) as string);
|
||||
}
|
||||
|
||||
public static formatInTimezone(
|
||||
date?: Date,
|
||||
dateFormat?: string,
|
||||
timezone?: string
|
||||
): string | null {
|
||||
if (!date || !dateFormat) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return timezone
|
||||
? formatInTimeZone(date, timezone, DateHelper.formatUpdate(dateFormat) as string)
|
||||
: format(date, DateHelper.formatUpdate(dateFormat) as string);
|
||||
}
|
||||
|
||||
public static tryParse(date: any, format?: string): Date | null {
|
||||
if (!date) {
|
||||
return null;
|
||||
|
||||
@@ -293,7 +293,7 @@ export class Extension {
|
||||
propKey: string,
|
||||
propValue: T,
|
||||
type: 'workspace' | 'global' = 'global',
|
||||
setState: boolean = false
|
||||
setState = false
|
||||
): Promise<void> {
|
||||
if (this.isFileStorageNeeded(propKey)) {
|
||||
let storageUri: Uri | undefined = undefined;
|
||||
|
||||
@@ -38,7 +38,7 @@ export class FilesHelper {
|
||||
*/
|
||||
public static relToAbsPath(filePath: string): string {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
let absPath = join(parseWinPath(wsFolder?.fsPath || ''), filePath);
|
||||
const absPath = join(parseWinPath(wsFolder?.fsPath || ''), filePath);
|
||||
return parseWinPath(absPath);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user