Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a571b34724 | ||
|
|
f46e4999a1 | ||
|
|
f9138cb3c3 | ||
|
|
893c46362e | ||
|
|
9136841b30 | ||
|
|
0e21093f92 | ||
|
|
3abd9589f1 | ||
|
|
81265e3c49 | ||
|
|
f6fd57e126 | ||
|
|
20d613452f | ||
|
|
35a6c8bada | ||
|
|
0b7f58d0ab | ||
|
|
c859874470 | ||
|
|
d70d2284b4 | ||
|
|
03236da793 | ||
|
|
07935aec73 | ||
|
|
f64c8c5958 | ||
|
|
c4267a69fa | ||
|
|
34b331b0ee | ||
|
|
5d0fc4f605 | ||
|
|
169f4ef14a | ||
|
|
7ea386328c | ||
|
|
c17400ce6d | ||
|
|
7b20d9f23d | ||
|
|
449bb110c2 | ||
|
|
0d3a99abe6 | ||
|
|
d2b9307a65 | ||
|
|
3842777f71 | ||
|
|
3a74c14ba6 | ||
|
|
a5ac7379bc | ||
|
|
c245e1474c | ||
|
|
c82c081fce | ||
|
|
31e344f358 | ||
|
|
366ae82318 | ||
|
|
c1a0609216 | ||
|
|
87bdabf515 | ||
|
|
15870bcc99 | ||
|
|
e0cdc5cf65 | ||
|
|
f39b707e30 | ||
|
|
dd13d8779c | ||
|
|
6f6015cf83 | ||
|
|
afd2878428 | ||
|
|
c66deb032c | ||
|
|
4c079b3e9d | ||
|
|
03c2cd31d7 | ||
|
|
d1dba01923 | ||
|
|
286ac4adfe | ||
|
|
6e2633572a | ||
|
|
36ae7081d1 |
22
CHANGELOG.md
@@ -1,16 +1,34 @@
|
||||
# Change Log
|
||||
|
||||
## [10.1.0] - 2024-xx-xx
|
||||
## [10.1.0] - 2024-04-11 - [Release notes](https://beta.frontmatter.codes/updates/v10.1.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#671](https://github.com/estruyf/vscode-front-matter/issues/671): Command bar for contents and media dashboard
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
### ⚡️ Optimizations
|
||||
- [#773](https://github.com/estruyf/vscode-front-matter/issues/773): Added the ability to rename content files
|
||||
- [#777](https://github.com/estruyf/vscode-front-matter/issues/777): Show an error in the metadata panel if something went wrong while parsing the front matter
|
||||
- [#778](https://github.com/estruyf/vscode-front-matter/issues/778): Added the ability to open a file or webpage when custom scripts is completed
|
||||
- [#783](https://github.com/estruyf/vscode-front-matter/issues/783): Always show the custom panel view
|
||||
- [#785](https://github.com/estruyf/vscode-front-matter/issues/785): Adding common actions at the bottom of the content and media cards
|
||||
- [#787](https://github.com/estruyf/vscode-front-matter/issues/787): Support for glob patterns in the page folder paths
|
||||
- [#790](https://github.com/estruyf/vscode-front-matter/pull/790): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387)
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#716](https://github.com/estruyf/vscode-front-matter/issues/716): Fix `dataFile` dropdown class
|
||||
- [#768](https://github.com/estruyf/vscode-front-matter/issues/768): Update broken link to the documentation
|
||||
- [#771](https://github.com/estruyf/vscode-front-matter/issues/771): Fix lowercase `data` tab label
|
||||
- [#782](https://github.com/estruyf/vscode-front-matter/issues/782): Fix for setting the correct view when inserting media or snippets
|
||||
- [#786](https://github.com/estruyf/vscode-front-matter/issues/786): Remove on startup as VSCode now triggers on known commands
|
||||
|
||||
## [10.0.2] - 2024-03-01
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#769](https://github.com/estruyf/vscode-front-matter/issues/769): Fix to remove internal properties for content folders
|
||||
|
||||
## [10.0.1] - 2024-02-28
|
||||
|
||||
|
||||
@@ -182,15 +182,31 @@ You can open showcase issues for the following things:
|
||||
## 🖤 Backers & Sponsors 👇 🤘
|
||||
|
||||
<p align="center">
|
||||
<img src="https://frontmatter.codes/api/img-sponsors" />
|
||||
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://run.events/?utm_source=frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/runevents-purple.webp" alt="run.events - Event Management Platform" height="50px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" height="44px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" />
|
||||
</a>
|
||||
<a href="http://bejs.io/" title="Supported by the BEJS Community">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/bejs-community.png" alt="Supported by the BEJS Community" height="50px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 📊 Telemetry
|
||||
|
||||
10
README.md
@@ -185,9 +185,17 @@ You can open showcase issues for the following things:
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://run.events/?utm_source=frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/runevents-purple.webp" alt="run.events - Event Management Platform" height="50px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" />
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" height="44px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#C5C5C5" width="16" height="16">
|
||||
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" fill="#C5C5C5" width="16" height="16" class="w-6 h-6">
|
||||
<path fill-rule="evenodd" d="M1.5 6a2.25 2.25 0 0 1 2.25-2.25h16.5A2.25 2.25 0 0 1 22.5 6v12a2.25 2.25 0 0 1-2.25 2.25H3.75A2.25 2.25 0 0 1 1.5 18V6ZM3 16.06V18c0 .414.336.75.75.75h16.5A.75.75 0 0 0 21 18v-1.94l-2.69-2.689a1.5 1.5 0 0 0-2.12 0l-.88.879.97.97a.75.75 0 1 1-1.06 1.06l-5.16-5.159a1.5 1.5 0 0 0-2.12 0L3 16.061Zm10.125-7.81a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 555 B |
@@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#424242" width="16" height="16">
|
||||
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" fill="#424242" width="16" height="16" class="w-6 h-6">
|
||||
<path fill-rule="evenodd" d="M1.5 6a2.25 2.25 0 0 1 2.25-2.25h16.5A2.25 2.25 0 0 1 22.5 6v12a2.25 2.25 0 0 1-2.25 2.25H3.75A2.25 2.25 0 0 1 1.5 18V6ZM3 16.06V18c0 .414.336.75.75.75h16.5A.75.75 0 0 0 21 18v-1.94l-2.69-2.689a1.5 1.5 0 0 0-2.12 0l-.88.879.97.97a.75.75 0 1 1-1.06 1.06l-5.16-5.159a1.5 1.5 0 0 0-2.12 0L3 16.061Zm10.125-7.81a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 555 B |
@@ -1,3 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="#C5C5C5" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" fill="#C5C5C5" width="16" height="16" class="w-6 h-6">
|
||||
<path fill-rule="evenodd" d="M8.128 9.155a3.751 3.751 0 1 1 .713-1.321l1.136.656a.75.75 0 0 1 .222 1.104l-.006.007a.75.75 0 0 1-1.032.157 1.421 1.421 0 0 0-.113-.072l-.92-.531Zm-4.827-3.53a2.25 2.25 0 0 1 3.994 2.063.756.756 0 0 0-.122.23 2.25 2.25 0 0 1-3.872-2.293ZM13.348 8.272a5.073 5.073 0 0 0-3.428 3.57 5.08 5.08 0 0 0-.165 1.202 1.415 1.415 0 0 1-.707 1.201l-.96.554a3.751 3.751 0 1 0 .734 1.309l13.729-7.926a.75.75 0 0 0-.181-1.374l-.803-.215a5.25 5.25 0 0 0-2.894.05l-5.325 1.629Zm-9.223 7.03a2.25 2.25 0 1 0 2.25 3.897 2.25 2.25 0 0 0-2.25-3.897ZM12 12.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||
<path d="M16.372 12.615a.75.75 0 0 1 .75 0l5.43 3.135a.75.75 0 0 1-.182 1.374l-.802.215a5.25 5.25 0 0 1-2.894-.051l-5.147-1.574a.75.75 0 0 1-.156-1.367l3-1.732Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 939 B |
@@ -1,3 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="#424242" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" fill="#424242" width="16" height="16" class="w-6 h-6">
|
||||
<path fill-rule="evenodd" d="M8.128 9.155a3.751 3.751 0 1 1 .713-1.321l1.136.656a.75.75 0 0 1 .222 1.104l-.006.007a.75.75 0 0 1-1.032.157 1.421 1.421 0 0 0-.113-.072l-.92-.531Zm-4.827-3.53a2.25 2.25 0 0 1 3.994 2.063.756.756 0 0 0-.122.23 2.25 2.25 0 0 1-3.872-2.293ZM13.348 8.272a5.073 5.073 0 0 0-3.428 3.57 5.08 5.08 0 0 0-.165 1.202 1.415 1.415 0 0 1-.707 1.201l-.96.554a3.751 3.751 0 1 0 .734 1.309l13.729-7.926a.75.75 0 0 0-.181-1.374l-.803-.215a5.25 5.25 0 0 0-2.894.05l-5.325 1.629Zm-9.223 7.03a2.25 2.25 0 1 0 2.25 3.897 2.25 2.25 0 0 0-2.25-3.897ZM12 12.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||
<path d="M16.372 12.615a.75.75 0 0 1 .75 0l5.43 3.135a.75.75 0 0 1-.182 1.374l-.802.215a5.25 5.25 0 0 1-2.894-.051l-5.147-1.574a.75.75 0 0 1-.156-1.367l3-1.732Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 939 B |
@@ -105,7 +105,7 @@
|
||||
"dashboard.header.tabs.contents": "Contenus",
|
||||
"dashboard.header.tabs.media": "Médias",
|
||||
"dashboard.header.tabs.snippets": "Snippets",
|
||||
"dashboard.header.tabs.data": "données",
|
||||
"dashboard.header.tabs.data": "Données",
|
||||
"dashboard.header.tabs.taxonomies": "Taxonomies",
|
||||
"dashboard.header.viewSwitch.toGrid": "Afficher en grille",
|
||||
"dashboard.header.viewSwitch.toList": "Afficher en liste",
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
"dashboard.header.tabs.contents": "Contenuto",
|
||||
"dashboard.header.tabs.media": "Media",
|
||||
"dashboard.header.tabs.snippets": "Snippets",
|
||||
"dashboard.header.tabs.data": "dati",
|
||||
"dashboard.header.tabs.data": "Dati",
|
||||
"dashboard.header.tabs.taxonomies": "Tassonomie",
|
||||
"dashboard.header.viewSwitch.toGrid": "Passa alla griglia",
|
||||
"dashboard.header.viewSwitch.toList": "Passa all'elenco",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"common.edit": "編集",
|
||||
"common.delete": "削除",
|
||||
"common.cancel": "キャンセル",
|
||||
"common.clear": "クリア",
|
||||
"common.apply": "適用",
|
||||
"common.clear": "クリア",
|
||||
"common.clear.value": "値をクリア",
|
||||
"common.search": "検索",
|
||||
"common.save": "保存",
|
||||
@@ -35,6 +35,16 @@
|
||||
"common.no": "いいえ",
|
||||
"common.openSettings": "設定を開く",
|
||||
"common.back": "戻る",
|
||||
"common.open": "開く",
|
||||
"common.openWithValue": "開く: {0}",
|
||||
"common.openCustomActions": "カスタムコマンドを開く",
|
||||
"common.view": "表示",
|
||||
"common.translate": "翻訳する",
|
||||
"common.languages": "言語",
|
||||
"common.scripts": "スクリプト",
|
||||
"common.rename": "ファイル名を変更する",
|
||||
|
||||
"loading.initPages": "記事を読み込んでいます",
|
||||
|
||||
"notifications.outputChannel.link": "出力ウィンドウ",
|
||||
"notifications.outputChannel.description": "詳細は{0}を確認してください。",
|
||||
@@ -42,18 +52,36 @@
|
||||
"settings.view.common": "一般",
|
||||
"settings.view.contentFolders": "記事フォルダー",
|
||||
"settings.view.astro": "Astro",
|
||||
"settings.view.integration": "統合機能",
|
||||
|
||||
"settings.openOnStartup": "起動時にダッシュボードを開く",
|
||||
"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 key",
|
||||
"settings.integrationsView.deepl.intput.placeholder": "DeepL API keyを入力",
|
||||
|
||||
"settings.integrationsView.azure.title": "Azure AI Translator",
|
||||
"settings.integrationsView.azure.intput.label": "サブスクリプションキー",
|
||||
"settings.integrationsView.azure.intput.placeholder": "Azure AI Translatorのサブスクリプションキーを入力",
|
||||
"settings.integrationsView.azure.region.label": "リージョン",
|
||||
"settings.integrationsView.azure.region.placeholder": "Azure AI Translatorのリージョンを入力 例: westeurope",
|
||||
|
||||
"developer.title": "開発モード",
|
||||
"developer.reload.title": "ダッシュボードを再読み込み",
|
||||
"developer.reload.label": "再読み込み",
|
||||
@@ -81,6 +109,8 @@
|
||||
"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": "<無効なディスクリプション>",
|
||||
@@ -108,6 +138,7 @@
|
||||
"dashboard.dataView.dataView.getStarted": "データタイプを選択して開始する",
|
||||
"dashboard.dataView.dataView.noDataFiles": "データファイルが見つかりませんでした",
|
||||
"dashboard.dataView.dataView.getStarted.link": "データファイルの利用方法について確認する",
|
||||
"dashboard.dataView.dataView.update.message": "データエントリーを更新しました。",
|
||||
|
||||
"dashboard.dataView.emptyView.heading": "最初にデータタイプを選んでください",
|
||||
|
||||
@@ -118,6 +149,13 @@
|
||||
|
||||
"dashboard.errorView.description": "ダッシュボードを一旦閉じてからやり直してください。",
|
||||
|
||||
"dashboard.filters.languageFilter.label": "ロケール",
|
||||
"dashboard.filters.languageFilter.all": "全て",
|
||||
|
||||
"dashboard.header.actionsBar.itemsSelected": "{0}件を選択中",
|
||||
"dashboard.header.actionsBar.alertDelete.title": "選択ファイルを削除",
|
||||
"dashboard.header.actionsBar.alertDelete.description": "選択したファイルを本当に削除しますか?",
|
||||
|
||||
"dashboard.header.breadcrumb.home": "ホーム",
|
||||
|
||||
"dashboard.header.clearFilters.title": "絞り込み・グループ・並べ替えを解除",
|
||||
@@ -201,10 +239,17 @@
|
||||
"dashboard.media.folderCreation.hexo.create": "Assetフォルダーを作成",
|
||||
"dashboard.media.folderCreation.folder.create": "新規フォルダーを作成",
|
||||
|
||||
"dashboard.media.folderItem.contentDirectory": "コンテンツディレクトリー",
|
||||
"dashboard.media.folderItem.publicDirectory": "Publicディレクトリー",
|
||||
|
||||
"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": "メディアの場所を表示",
|
||||
@@ -275,6 +320,8 @@
|
||||
"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": "設定用のテンプレートを使用する",
|
||||
@@ -283,6 +330,7 @@
|
||||
"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": "他のタクソノミーへ移行",
|
||||
@@ -329,6 +377,11 @@
|
||||
"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": "記事タイプのフィールドは設定と異なります。この記事の記事タイプを、作成・更新または設定しますか?",
|
||||
"panel.contentType.contentTypeValidator.button.create": "新しい記事タイプを作成",
|
||||
@@ -414,6 +467,7 @@
|
||||
"panel.globalSettings.action.server.placeholder": "例: {0}",
|
||||
|
||||
"panel.metadata.title": "メタデータ",
|
||||
"panel.metadata.focusProblems": "詳細を「問題」表示で確認してください。",
|
||||
|
||||
"panel.otherActions.title": "他のコマンド",
|
||||
"panel.otherActions.writingSettings.enabled": "ライティング設定が有効",
|
||||
@@ -474,6 +528,10 @@
|
||||
|
||||
"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": "キャッシュがクリアされました。",
|
||||
|
||||
@@ -501,6 +559,19 @@
|
||||
"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.translate.progress.title": "記事を翻訳しています...",
|
||||
|
||||
"commands.preview.panel.title": "プレビュー: {0}",
|
||||
"commands.preview.askUserToPickFolder.title": "プレビュー用の記事フォルダーを選択してください。",
|
||||
|
||||
@@ -611,9 +682,6 @@
|
||||
"helpers.extension.getVersion.changelog": "変更履歴を確認する",
|
||||
"helpers.extension.getVersion.starIt": "⭐️を付ける",
|
||||
"helpers.extension.getVersion.update.notification": "{0} が v{1} に更新されました!新機能をチェックしてください!",
|
||||
"helpers.extension.migrateSettings.deprecated.warning": "\"{0}\"及び\"{1}\"の設定は非推奨になりました。代わりに\"isPublishDate\"と\"isModifiedDate\"の日付フィールドを使用してください。",
|
||||
"helpers.extension.migrateSettings.deprecated.warning.hide": "非表示にする",
|
||||
"helpers.extension.migrateSettings.deprecated.warning.seeGuide": "移行ガイドを読む",
|
||||
"helpers.extension.migrateSettings.templates.quickPick.title": "{0} - テンプレート",
|
||||
"helpers.extension.migrateSettings.templates.quickPick.placeholder": "テンプレート機能の使用を継続しますか?",
|
||||
"helpers.extension.checkIfExtensionCanRun.warning": "Front MatterのBETA版は安定版がインストールされている場合は利用できません。BETA版のみがインストールされていることを確認してください。",
|
||||
@@ -648,6 +716,7 @@
|
||||
"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}文字以内に収めることが推奨されます。",
|
||||
|
||||
@@ -690,6 +759,7 @@
|
||||
"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": "スニペットのタイトルまたはbodyが空です。",
|
||||
"listeners.dashboard.snippetListener.addSnippet.exists.warning": "同じタイトルのスニペットが既に存在しています。",
|
||||
@@ -700,6 +770,7 @@
|
||||
"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の解析中にエラーが発生しまいsた。ファイルの内容を確認してください。",
|
||||
|
||||
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "アクティブなエディターがありません。",
|
||||
|
||||
@@ -37,10 +37,12 @@
|
||||
"common.back": "Back",
|
||||
"common.open": "Open",
|
||||
"common.openWithValue": "Open: {0}",
|
||||
"common.openCustomActions": "Open custom actions",
|
||||
"common.view": "View",
|
||||
"common.translate": "Translate",
|
||||
"common.languages": "Languages",
|
||||
"common.scripts": "Scripts",
|
||||
"common.rename": "Rename",
|
||||
|
||||
"loading.initPages": "Loading content",
|
||||
|
||||
@@ -72,7 +74,7 @@
|
||||
|
||||
"settings.integrationsView.deepl.title": "DeepL",
|
||||
"settings.integrationsView.deepl.intput.label": "API key",
|
||||
"settings.integrationsView.deepl.intput.placeholder": "Enter your Azure Translator API key",
|
||||
"settings.integrationsView.deepl.intput.placeholder": "Enter your Deepl API key",
|
||||
|
||||
"settings.integrationsView.azure.title": "Azure AI Translator Service",
|
||||
"settings.integrationsView.azure.intput.label": "Subscription key",
|
||||
@@ -207,7 +209,7 @@
|
||||
"dashboard.header.tabs.contents": "Contents",
|
||||
"dashboard.header.tabs.media": "Media",
|
||||
"dashboard.header.tabs.snippets": "Snippets",
|
||||
"dashboard.header.tabs.data": "data",
|
||||
"dashboard.header.tabs.data": "Data",
|
||||
"dashboard.header.tabs.taxonomies": "Taxonomies",
|
||||
|
||||
"dashboard.header.viewSwitch.toGrid": "Change to grid",
|
||||
@@ -465,6 +467,7 @@
|
||||
"panel.globalSettings.action.server.placeholder": "Example: {0}",
|
||||
|
||||
"panel.metadata.title": "Metadata",
|
||||
"panel.metadata.focusProblems": "Check the problems view for more information",
|
||||
|
||||
"panel.otherActions.title": "Other actions",
|
||||
"panel.otherActions.writingSettings.enabled": "Writing settings enabled",
|
||||
@@ -525,6 +528,10 @@
|
||||
|
||||
"commands.article.setDate.error": "Something failed while parsing the date format. Check your \"{0}\" setting.",
|
||||
"commands.article.updateSlug.error": "Failed to rename file: {0}",
|
||||
"commands.article.rename.fileNotExists.error": "The file did not exist",
|
||||
"commands.article.rename.fileExists.error": "A file with the name \"{0}\" already exists",
|
||||
"commands.article.rename.fileName.title": "Rename: {0}",
|
||||
"commands.article.rename.fileName.prompt": "File name",
|
||||
|
||||
"commands.cache.cleared": "Cache cleared",
|
||||
|
||||
@@ -763,6 +770,7 @@
|
||||
"listeners.panel.dataListener.aiSuggestTaxonomy.noEditor.error": "No active editor",
|
||||
"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.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "No active editor",
|
||||
|
||||
337
package.json
@@ -10,8 +10,7 @@
|
||||
"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"
|
||||
@@ -51,8 +50,7 @@
|
||||
},
|
||||
"activationEvents": [
|
||||
"workspaceContains:**/.frontmatter",
|
||||
"workspaceContains:**/frontmatter.json",
|
||||
"onStartupFinished"
|
||||
"workspaceContains:**/frontmatter.json"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
@@ -71,12 +69,16 @@
|
||||
"**/.frontmatter/config/*.json": "jsonc"
|
||||
}
|
||||
},
|
||||
"keybindings": [
|
||||
{
|
||||
"keybindings": [{
|
||||
"command": "frontMatter.dashboard",
|
||||
"key": "alt+d"
|
||||
},
|
||||
{
|
||||
"command": "workbench.action.webview.reloadWebviewAction",
|
||||
"key": "ctrl+r",
|
||||
"mac": "cmd+r",
|
||||
"when": "activeWebviewPanelId == frontMatterDashboard"
|
||||
}, {
|
||||
"command": "frontMatter.insertMedia",
|
||||
"key": "ctrl+shift+i",
|
||||
"mac": "cmd+shift+i",
|
||||
@@ -90,23 +92,19 @@
|
||||
}
|
||||
],
|
||||
"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,8 +172,7 @@
|
||||
"frontMatter.content.defaultFileType": {
|
||||
"type": "string",
|
||||
"default": "md",
|
||||
"oneOf": [
|
||||
{
|
||||
"oneOf": [{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -191,8 +188,7 @@
|
||||
"frontMatter.content.defaultSorting": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [
|
||||
{
|
||||
"oneOf": [{
|
||||
"enum": [
|
||||
"LastModifiedAsc",
|
||||
"LastModifiedDesc",
|
||||
@@ -540,8 +536,7 @@
|
||||
"categories"
|
||||
],
|
||||
"markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%",
|
||||
"items": [
|
||||
{
|
||||
"items": [{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"contentFolders",
|
||||
@@ -614,8 +609,7 @@
|
||||
"command": {
|
||||
"$id": "#scriptCommand",
|
||||
"type": "string",
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [{
|
||||
"enum": [
|
||||
"node",
|
||||
"bash",
|
||||
@@ -811,8 +805,7 @@
|
||||
"title",
|
||||
"file"
|
||||
],
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -866,8 +859,7 @@
|
||||
"id",
|
||||
"path"
|
||||
],
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -1108,29 +1100,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -1360,8 +1349,7 @@
|
||||
"default": "",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%",
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1555,8 +1543,7 @@
|
||||
"type",
|
||||
"name"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"allOf": [{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -1764,51 +1751,48 @@
|
||||
"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": {
|
||||
@@ -1821,8 +1805,7 @@
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%",
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -2007,8 +1990,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"commands": [{
|
||||
"command": "frontMatter.project.switch",
|
||||
"title": "%command.frontMatter.project.switch%",
|
||||
"category": "Front Matter",
|
||||
@@ -2334,21 +2316,16 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
@@ -2434,14 +2411,11 @@
|
||||
"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"
|
||||
@@ -2457,8 +2431,7 @@
|
||||
"group": "frontmatter@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [
|
||||
{
|
||||
"commandPalette": [{
|
||||
"command": "frontMatter.init",
|
||||
"when": "frontMatterCanInit"
|
||||
},
|
||||
@@ -2635,8 +2608,7 @@
|
||||
"when": "frontMatter:file:isValid == true"
|
||||
}
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"view/title": [{
|
||||
"command": "frontMatter.chatbot",
|
||||
"group": "navigation@0",
|
||||
"when": "view == frontMatter.explorer"
|
||||
@@ -2668,57 +2640,52 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"grammars": [
|
||||
{
|
||||
"path": "./syntaxes/hugo.tmLanguage.json",
|
||||
"scopeName": "frontmatter.markdown.hugo",
|
||||
"injectTo": [
|
||||
"text.html.markdown"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
]
|
||||
"grammars": [{
|
||||
"path": "./syntaxes/hugo.tmLanguage.json",
|
||||
"scopeName": "frontmatter.markdown.hugo",
|
||||
"injectTo": [
|
||||
"text.html.markdown"
|
||||
]
|
||||
}],
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"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.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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
"scripts": {
|
||||
"dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*",
|
||||
@@ -2847,4 +2814,4 @@
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@
|
||||
"command.frontMatter.markup.unorderedlist": "順序なしリスト",
|
||||
"command.frontMatter.git.sync": "同期",
|
||||
"command.frontMatter.cache.clear": "キャッシュをクリア",
|
||||
"command.frontMatter.i18n.create": "新しい翻訳を作成",
|
||||
"settings.configuration.title": "Front Matter: チームで作業する場合はfrontmatter.jsonで設定してください。",
|
||||
"setting.frontMatter.projects.markdownDescription": "Front Matter CMSを利用するプロジェクトを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.projects)",
|
||||
"setting.frontMatter.projects.items.properties.name.markdownDescription": "プロジェクトの名前を指定します。",
|
||||
@@ -60,7 +61,7 @@
|
||||
"setting.frontMatter.content.defaultFileType.markdownDescription": "新しい記事を作成する際の既定のファイル形式を設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultfiletype)",
|
||||
"setting.frontMatter.content.defaultSorting.markdownDescription": "ダッシュボード上に表示される記事一覧の既定の並び順を設定します。Enum(列挙型)や任意のIDを指定してください。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultsorting)",
|
||||
"setting.frontMatter.content.draftField.markdownDescription": "記事の下書きステータスを管理するフィールドを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.draftfield)",
|
||||
"setting.frontMatter.content.draftField.properties.type.description": "使用する下書きフィールドの種類",
|
||||
"setting.frontMatter.content.draftField.properties.type.description": "使用する下書きフィールドの型",
|
||||
"setting.frontMatter.content.draftField.properties.name.description": "使用するフィールドの名前",
|
||||
"setting.frontMatter.content.draftField.properties.invert.description": "既定では、記事が下書きの場合、下書きフィールドは true に設定されます。これを true に設定すると、false に設定されます。",
|
||||
"setting.frontMatter.content.draftField.properties.choices.description": "フィールドの選択肢のリスト",
|
||||
@@ -75,6 +76,12 @@
|
||||
"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.i18n.markdownDescription": "ウェブサイトで利用するロケールを設定します。この設定はページフォルダーレベルの設定より優先されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n)",
|
||||
"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": "記事タイプとテンプレートで使用するプレースホルダーを配列で定義して、記事のfront matterを自動で入力できるようにします。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.placeholders)",
|
||||
"setting.frontMatter.content.placeholders.items.properties.id.description": "プレースホルダーのIDを記事タイプまたはテンプレートで、次のように使用します: {{placeholder}}",
|
||||
"setting.frontMatter.content.placeholders.items.properties.value.description": "プレースホルダーの値",
|
||||
@@ -92,6 +99,7 @@
|
||||
"setting.frontMatter.content.sorting.items.properties.type.description": "フィールド値の型",
|
||||
"setting.frontMatter.content.supportedFileTypes.markdownDescription": "Front Matterでサポートされるファイル形式を設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.supportedfiletypes)",
|
||||
"setting.frontMatter.content.wysiwyg.markdownDescription": "What You See, Is What You Get(WYSIWYG)Markdownコントロールを有効にします。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.wysiwyg)",
|
||||
"setting.frontMatter.content.filters.markdownDescription": "ダッシュボードで利用するフィルターを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.filters)",
|
||||
"setting.frontMatter.custom.scripts.markdownDescription": "実行するNode.jsスクリプトのパスを指定します。現在のファイルのパスが引数として渡されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.custom.scripts)",
|
||||
"setting.frontMatter.custom.scripts.items.properties.id.description": "スクリプトの ID。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.title.description": "スクリプトに付けるタイトル。ボタンのタイトルとして表示されます。",
|
||||
@@ -100,8 +108,8 @@
|
||||
"setting.frontMatter.custom.scripts.items.properties.bulk.description": "全ての記事ファイルに対してスクリプトを実行する",
|
||||
"setting.frontMatter.custom.scripts.items.properties.output.description": "スクリプト出力を出力する場所を定義します。デフォルトは通知表示ですが、エディターパネルに表示するように指定できます。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.outputType.description": "エディター・パネルの出力のタイプ。たとえば、「マークダウン」に変更するために使用できます",
|
||||
"setting.frontMatter.custom.scripts.items.properties.type.description": "スクリプトが使用される型。",
|
||||
"setting.frontMatter.custom.scripts.items.properties.command.description": "実行するスクリプトの種類。",
|
||||
"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": "実行するスクリプトへのパス",
|
||||
@@ -152,7 +160,18 @@
|
||||
"setting.frontMatter.global.notifications.markdownDescription": "表示したい通知を設定します。既定では全ての通知が表示されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.global.notifications)",
|
||||
"setting.frontMatter.global.disabledNotifications.markdownDescription": "これは、Front Matter CMSで無効にできる通知タイプの配列です。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications)",
|
||||
"setting.frontMatter.media.defaultSorting.markdownDescription": "ダッシュボードのメディア一覧での既定の並び順を設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.media.defaultsorting)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "メディアファイルでサポートされるMIMEタイプを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "メディアコンテンツでサポートされるMIMEタイプを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes)",
|
||||
|
||||
"setting.frontMatter.media.contentTypes.markdownDescription": "Front Matterで利用するメディアコンテンツのタイプを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.media.contenttypes)",
|
||||
"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.freeform.markdownDescription": "未登録のタグ/カテゴリーをタグピッカーに入力可能にするかどうかを設定します(有効にすると、後で保存するオプションが使えます)。規定値はtrue。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "パネル内で非表示にしたいコマンドを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "プレビュー表示に使用するホストのURLを設定します(例:`http://localhost:1313`)。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host)",
|
||||
@@ -164,11 +183,11 @@
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.items.description": "コンマ区切りの配列として使用するフィールドの名前。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.markdownDescription": "記事・ページ・その他で利用したい記事タイプを設定します。front matterで正しく`type`が設定されていることを確認してください。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.contenttypes)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.description": "Front Matterで使用する記事タイプを定義します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.name.description": "フィールドの種類を定義する",
|
||||
"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.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.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 に表示する説明",
|
||||
@@ -176,8 +195,8 @@
|
||||
"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": "WYSIWYG フィールド (HTML 出力) である",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.single.description": "単一行フィールド",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.wysiwyg.description": "WYSIWYG フィールド (HTML 出力) ",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.multiple.description": "複数の値を選択できますか?",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.isPreviewImage.description": "画像フィールドをプレビューとして使用できるかどうかを指定します。記事タイプごとに使用できるプレビュー画像は1つだけです。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.hidden.description": "メタデータ セクションからフィールドを非表示にしますか?",
|
||||
@@ -210,6 +229,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "比較で大文字と小文字を区別するかどうかを指定します。デフォルト: true",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "新しい記事を作成するときにフォルダーを作成するかどうかを指定します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "記事タイプのカスタム プレビュー パスを定義します。",
|
||||
"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": "ファイル名の接頭辞を定義します。",
|
||||
@@ -236,6 +256,7 @@
|
||||
"setting.frontMatter.taxonomy.seoTitleLength.markdownDescription": "SEOに適したタイトルの文字数を設定します。(`-1`に設定するとオフになります。)[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlelength)",
|
||||
"setting.frontMatter.taxonomy.slugPrefix.markdownDescription": "スラッグに付与する接頭辞を設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugprefix)",
|
||||
"setting.frontMatter.taxonomy.slugSuffix.markdownDescription": "スラッグに付与する接尾辞を設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugsuffix)",
|
||||
"setting.frontMatter.taxonomy.slugTemplate.markdownDescription": "記事を作成する際のカスタムスラッグのテンプレートを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugtemplate)",
|
||||
"setting.frontMatter.taxonomy.tags.markdownDescription": "Front Matterで利用するタグを管理します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags)",
|
||||
"setting.frontMatter.telemetry.disable.markdownDescription": "利用状況の送信をオフにします。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.telemetry.disable)",
|
||||
"setting.frontMatter.templates.enabled.markdownDescription": "テンプレート機能を利用します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.enabled)",
|
||||
@@ -250,5 +271,8 @@
|
||||
"command.frontMatter.settings.refresh": "Front Matterの設定の再読み込み",
|
||||
"setting.frontMatter.config.dynamicFilePath.markdownDescription": "動的構成ファイルへのパスを指定します (例: [[ワークスペース]]/config.js)。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.allowAsSubContent.description": "記事をサブコンテンツとして作成可能かどうかを設定します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "記事をサブコンテンツとして作成するかどうかを設定します。"
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "記事をサブコンテンツとして作成するかどうかを設定します。",
|
||||
|
||||
"setting.frontMatter.git.disableOnBranches.markdownDescription": "Git Actionsを無効化したいブランチを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches)",
|
||||
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "特定のブランチを更新する場合にコミットメッセージを必須にします。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage)"
|
||||
}
|
||||
@@ -1,3 +1,13 @@
|
||||
import {
|
||||
Position,
|
||||
TextDocument,
|
||||
TextDocumentWillSaveEvent,
|
||||
TextEdit,
|
||||
Uri,
|
||||
commands,
|
||||
window,
|
||||
workspace
|
||||
} from 'vscode';
|
||||
import { Folders } from './Folders';
|
||||
import { DEFAULT_CONTENT_TYPE } from './../constants/ContentType';
|
||||
import { isValidFile } from './../helpers/isValidFile';
|
||||
@@ -13,7 +23,6 @@ import {
|
||||
TelemetryEvent,
|
||||
SETTING_SLUG_TEMPLATE
|
||||
} from './../constants';
|
||||
import * as vscode from 'vscode';
|
||||
import { CustomPlaceholder, Field } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import {
|
||||
@@ -33,17 +42,35 @@ import { Telemetry } from '../helpers/Telemetry';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { MediaListener } from '../listeners/panel';
|
||||
import { NavigationType } from '../dashboardWebView/models';
|
||||
import { Position } from 'vscode';
|
||||
import { SNIPPET } from '../constants/Snippet';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
export class Article {
|
||||
/**
|
||||
* Registers the commands for the Article class.
|
||||
*
|
||||
* @param subscriptions - The array of subscriptions to register the commands with.
|
||||
*/
|
||||
public static async registerCommands(subscriptions: unknown[]) {
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.setLastModifiedDate, Article.setLastModifiedDate)
|
||||
);
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.generateSlug, Article.updateSlug));
|
||||
|
||||
// Inserting an image in Markdown
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.insertMedia, Article.insertMedia));
|
||||
|
||||
// Inserting a snippet in Markdown
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.insertSnippet, Article.insertSnippet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the article date
|
||||
*/
|
||||
public static async setDate() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -77,7 +104,7 @@ export class Article {
|
||||
* Sets the article lastmod date
|
||||
*/
|
||||
public static async setLastModifiedDate() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -91,9 +118,7 @@ export class Article {
|
||||
ArticleHelper.update(editor, updatedArticle as ParsedFrontMatter);
|
||||
}
|
||||
|
||||
public static async setLastModifiedDateOnSave(
|
||||
document: vscode.TextDocument
|
||||
): Promise<vscode.TextEdit[]> {
|
||||
public static async setLastModifiedDateOnSave(document: TextDocument): Promise<TextEdit[]> {
|
||||
const updatedArticle = this.setLastModifiedDateInner(document);
|
||||
|
||||
if (typeof updatedArticle === 'undefined') {
|
||||
@@ -105,9 +130,7 @@ export class Article {
|
||||
return [update];
|
||||
}
|
||||
|
||||
private static setLastModifiedDateInner(
|
||||
document: vscode.TextDocument
|
||||
): ParsedFrontMatter | undefined {
|
||||
private static setLastModifiedDateInner(document: TextDocument): ParsedFrontMatter | undefined {
|
||||
const article = ArticleHelper.getFrontMatterFromDocument(document);
|
||||
|
||||
// Only set the date, if there is already front matter set
|
||||
@@ -160,7 +183,7 @@ export class Article {
|
||||
Telemetry.send(TelemetryEvent.generateSlug);
|
||||
|
||||
const updateFileName = Settings.get(SETTING_SLUG_UPDATE_FILE_NAME) as string;
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
@@ -219,7 +242,7 @@ export class Article {
|
||||
// Check if the file name should be updated by the slug
|
||||
// This is required for systems like Jekyll
|
||||
if (updateFileName) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (editor) {
|
||||
const ext = extname(editor.document.fileName);
|
||||
const fileName = basename(editor.document.fileName);
|
||||
@@ -237,7 +260,7 @@ export class Article {
|
||||
try {
|
||||
await editor.document.save();
|
||||
|
||||
await vscode.workspace.fs.rename(editor.document.uri, vscode.Uri.file(newPath), {
|
||||
await workspace.fs.rename(editor.document.uri, Uri.file(newPath), {
|
||||
overwrite: false
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
@@ -257,7 +280,7 @@ export class Article {
|
||||
* Retrieve the slug from the front matter
|
||||
*/
|
||||
public static getSlug() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -297,7 +320,7 @@ export class Article {
|
||||
* Toggle the page its draft mode
|
||||
*/
|
||||
public static async toggleDraft() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -315,7 +338,7 @@ export class Article {
|
||||
* Article auto updater
|
||||
* @param event
|
||||
*/
|
||||
public static async autoUpdate(event: vscode.TextDocumentWillSaveEvent) {
|
||||
public static async autoUpdate(event: TextDocumentWillSaveEvent) {
|
||||
const document = event.document;
|
||||
if (document && ArticleHelper.isSupportedFile(document)) {
|
||||
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
|
||||
@@ -355,7 +378,7 @@ export class Article {
|
||||
* Insert an image from the media dashboard into the article
|
||||
*/
|
||||
public static async insertMedia() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -367,7 +390,7 @@ export class Article {
|
||||
const position = editor.selection.active;
|
||||
const selectionText = editor.document.getText(editor.selection);
|
||||
|
||||
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: 'media',
|
||||
data: {
|
||||
pageBundle: !!contentType.pageBundle,
|
||||
@@ -386,7 +409,7 @@ export class Article {
|
||||
* Insert a snippet into the article
|
||||
*/
|
||||
public static async insertSnippet() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -442,7 +465,7 @@ export class Article {
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
|
||||
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: NavigationType.Snippets,
|
||||
data: {
|
||||
fileTitle: article?.data.title || '',
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
ExtensionListener,
|
||||
SnippetListener,
|
||||
TaxonomyListener,
|
||||
LogListener,
|
||||
LocalizationListener,
|
||||
SsgListener
|
||||
} from '../listeners/dashboard';
|
||||
@@ -237,7 +236,6 @@ export class Dashboard {
|
||||
ModeListener.process(msg);
|
||||
GitListener.process(msg);
|
||||
TaxonomyListener.process(msg);
|
||||
LogListener.process(msg);
|
||||
SsgListener.process(msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -315,6 +315,26 @@ export class Folders {
|
||||
|
||||
const contentFolders: ContentFolder[] = [];
|
||||
|
||||
// Check if wildcard is used
|
||||
const wildcardFolders = folders.filter((f) => f.path.includes('*'));
|
||||
if (wildcardFolders && wildcardFolders.length > 0) {
|
||||
for (const folder of wildcardFolders) {
|
||||
folders = folders.filter((f) => f.path !== folder.path);
|
||||
|
||||
const folderPath = Folders.absWsFolder(folder, wsFolder);
|
||||
const subFolders = glob.sync(folderPath, { ignore: '**/node_modules/**' });
|
||||
for (const subFolder of subFolders) {
|
||||
const subFolderPath = parseWinPath(subFolder);
|
||||
|
||||
folders.push({
|
||||
...folder,
|
||||
title: `${folder.title} (${subFolderPath.replace(wsFolder?.fsPath || '', '')})`,
|
||||
path: subFolderPath
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folders.forEach((folder) => {
|
||||
if (!folder.title) {
|
||||
folder.title = basename(folder.path);
|
||||
@@ -405,8 +425,13 @@ export class Folders {
|
||||
* @param folders
|
||||
*/
|
||||
public static async update(folders: ContentFolder[]) {
|
||||
const originalFolders = Settings.get(SETTING_CONTENT_PAGE_FOLDERS) as ContentFolder[];
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
// Filter out the locale folders
|
||||
folders = folders.filter((folder) => !folder.locale || folder.locale === folder.defaultLocale);
|
||||
|
||||
// Remove the internal FM properties
|
||||
const folderDetails = folders
|
||||
.map((folder) => {
|
||||
const detail = {
|
||||
@@ -418,6 +443,19 @@ export class Folders {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (detail.locale && detail.locale === detail.defaultLocale) {
|
||||
// Check if the folder was on the original list
|
||||
const originalFolder = originalFolders.find((f) => f.path === folder.originalPath);
|
||||
|
||||
if (originalFolder && !originalFolder.locales && folder.locales) {
|
||||
delete detail.locales;
|
||||
}
|
||||
|
||||
delete detail.localeSourcePath;
|
||||
delete detail.localeTitle;
|
||||
}
|
||||
|
||||
delete detail.locale;
|
||||
delete detail.originalPath;
|
||||
|
||||
return detail;
|
||||
|
||||
13
src/components/icons/ArrowClockwiseIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IArrowClockwiseIconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ArrowClockwiseIcon: React.FunctionComponent<IArrowClockwiseIconProps> = ({ className }: React.PropsWithChildren<IArrowClockwiseIconProps>) => {
|
||||
return (
|
||||
<svg className={className || 'h-4 w-4'} fill="currentColor" aria-hidden="true" width="1em" height="1em" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.07 9.05a7 7 0 0 1 12.55-3.22l.13.17H12.5a.5.5 0 1 0 0 1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 0-1 0v2.2a8 8 0 1 0 1.99 4.77.5.5 0 0 0-1 .08 7 7 0 1 1-13.92-.5Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
13
src/components/icons/RenameIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IRenameIconProps {
|
||||
className: string;
|
||||
}
|
||||
|
||||
export const RenameIcon: React.FunctionComponent<IRenameIconProps> = ({
|
||||
className
|
||||
}: React.PropsWithChildren<IRenameIconProps>) => {
|
||||
return (
|
||||
<svg className={className} fill="currentColor" aria-hidden="true" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8.5 2a.5.5 0 0 0 0 1h1v14h-1a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-1V3h1a.5.5 0 0 0 0-1h-3Zm-4 2h4v1h-4C3.67 5 3 5.67 3 6.5v7c0 .83.67 1.5 1.5 1.5h4v1h-4A2.5 2.5 0 0 1 2 13.5v-7A2.5 2.5 0 0 1 4.5 4Zm11 11h-4v1h4a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 15.5 4h-4v1h4c.83 0 1.5.67 1.5 1.5v7c0 .83-.67 1.5-1.5 1.5Z" fill="currentColor"></path></svg>
|
||||
);
|
||||
};
|
||||
@@ -24,6 +24,11 @@ export const GeneralCommands = {
|
||||
content: {
|
||||
locales: 'getContentLocales'
|
||||
},
|
||||
logging: {
|
||||
info: 'logInfo',
|
||||
warn: 'logWarn',
|
||||
error: 'logError'
|
||||
},
|
||||
runCommand: 'runCommand',
|
||||
getLocalization: 'getLocalization',
|
||||
openOnWebsite: 'openOnWebsite'
|
||||
|
||||
@@ -30,6 +30,7 @@ export enum DashboardMessage {
|
||||
getPinnedItems = 'getPinnedItems',
|
||||
pinItem = 'pinItem',
|
||||
unpinItem = 'unpinItem',
|
||||
rename = 'rename',
|
||||
|
||||
// Media Dashboard
|
||||
getMedia = 'getMedia',
|
||||
@@ -74,7 +75,6 @@ export enum DashboardMessage {
|
||||
getState = 'getState',
|
||||
runCustomScript = 'runCustomScript',
|
||||
sendTelemetry = 'sendTelemetry',
|
||||
logError = 'logError',
|
||||
showNotification = 'showNotification',
|
||||
|
||||
// Settings
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Contents } from './Contents/Contents';
|
||||
import { Media } from './Media/Media';
|
||||
import { DataView } from './DataView';
|
||||
import { Snippets } from './SnippetsView/Snippets';
|
||||
import { FEATURE_FLAG } from '../../constants';
|
||||
import { FEATURE_FLAG, GeneralCommands } from '../../constants';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { TaxonomyView } from './TaxonomyView';
|
||||
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||
@@ -18,7 +18,6 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { UnknownView } from './UnknownView';
|
||||
import { ErrorBoundary } from '@sentry/react';
|
||||
import { ErrorView } from './ErrorView';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { SettingsView } from './SettingsView/SettingsView';
|
||||
@@ -55,6 +54,21 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.taxonomy.view);
|
||||
}, [mode?.features]);
|
||||
|
||||
const checkDevMode = (retry: number = 0) => {
|
||||
if (!window.fmExternal) {
|
||||
if (retry < 5) {
|
||||
setTimeout(() => checkDevMode(retry + 1), 150);
|
||||
} else {
|
||||
setIsDevMode(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.fmExternal && window.fmExternal.isDevelopment) {
|
||||
setIsDevMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (view && routePaths[view]) {
|
||||
navigate(routePaths[view]);
|
||||
@@ -65,9 +79,7 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
}, [view]);
|
||||
|
||||
useEffect(() => {
|
||||
if (window.fmExternal && window.fmExternal.isDevelopment) {
|
||||
setIsDevMode(true);
|
||||
}
|
||||
checkDevMode();
|
||||
}, []);
|
||||
|
||||
if (!settings) {
|
||||
@@ -87,7 +99,7 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
fallback={<ErrorView />}
|
||||
onError={(error: Error, componentStack: string, eventId: string) => {
|
||||
Messenger.send(
|
||||
DashboardMessage.logError,
|
||||
GeneralCommands.toVSCode.logging.error,
|
||||
`Event ID: ${eventId}
|
||||
Message: ${error.message}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const DateField: React.FunctionComponent<IDateFieldProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`date__field ${className || ''} text-xs text-[var(--frontmatter-text)]`}>
|
||||
<span className={`date__field ${className || ''} text-xs text-[var(--frontmatter-secondary-text)]`}>
|
||||
{dateValue}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -5,27 +5,27 @@ import { useMemo } from 'react';
|
||||
|
||||
export interface IItemSelectionProps {
|
||||
filePath: string;
|
||||
isRowItem?: boolean;
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
export const ItemSelection: React.FunctionComponent<IItemSelectionProps> = ({
|
||||
filePath,
|
||||
isRowItem
|
||||
show
|
||||
}: React.PropsWithChildren<IItemSelectionProps>) => {
|
||||
const { onMultiSelect, selectedFiles } = useSelectedItems();
|
||||
|
||||
const cssNames = useMemo(() => {
|
||||
if (isRowItem) {
|
||||
if (show) {
|
||||
return 'block';
|
||||
}
|
||||
return `${selectedFiles.includes(filePath) ? 'block' : 'hidden'} absolute top-2 left-2`;
|
||||
}, [isRowItem, selectedFiles]);
|
||||
}, [show, selectedFiles]);
|
||||
|
||||
return (
|
||||
<div className={`${cssNames} group-hover:block`}>
|
||||
<VSCodeCheckbox
|
||||
style={{
|
||||
boxShadow: isRowItem ? "" : "0 0 3px var(--frontmatter-border-preserve)"
|
||||
boxShadow: show ? "" : "0 0 3px var(--frontmatter-border-preserve)"
|
||||
}}
|
||||
onClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Spinner: React.FunctionComponent<ISpinnerProps> = (
|
||||
|
||||
{
|
||||
type === 'initPages' && (
|
||||
<div className='spinner-msg h-full text-2xl flex justify-center items-center text-[var(--vscode-foreground)]'>
|
||||
<div className='spinner-msg h-full text-2xl flex justify-center items-center text-[var(--frontmatter-text)]'>
|
||||
<span>{l10n.t(LocalizationKey.loadingInitPages)}</span>
|
||||
<span className='dots'></span>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, EllipsisVerticalIcon, LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, TrashIcon, LanguageIcon, EllipsisHorizontalIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { CustomScript, I18nConfig, ScriptType } from '../../../models';
|
||||
import { CustomScript, I18nConfig } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { SelectedItemActionAtom, SettingsSelector } from '../../state';
|
||||
import { COMMAND_NAME, GeneralCommands } from '../../../constants';
|
||||
import { PinIcon } from '../Icons/PinIcon';
|
||||
import { PinnedItemsAtom } from '../../state/atom/PinnedItems';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { RenameIcon } from '../../../components/icons/RenameIcon';
|
||||
import { openOnWebsite } from '../../utils';
|
||||
import { CustomActions } from './CustomActions';
|
||||
import { TranslationMenu } from './TranslationMenu';
|
||||
|
||||
export interface IContentActionsProps {
|
||||
title: string;
|
||||
path: string;
|
||||
relPath: string;
|
||||
contentType: string;
|
||||
scripts: CustomScript[] | undefined;
|
||||
listView?: boolean;
|
||||
locale?: I18nConfig;
|
||||
@@ -32,9 +34,9 @@ export interface IContentActionsProps {
|
||||
}
|
||||
|
||||
export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
title,
|
||||
path,
|
||||
relPath,
|
||||
contentType,
|
||||
scripts,
|
||||
onOpen,
|
||||
listView,
|
||||
@@ -42,8 +44,8 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
translations,
|
||||
locale
|
||||
}: React.PropsWithChildren<IContentActionsProps>) => {
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
const [pinnedItems, setPinnedItems] = useRecoilState(PinnedItemsAtom);
|
||||
const [showDeletionAlert, setShowDeletionAlert] = React.useState(false);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const onView = (e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
@@ -51,30 +53,19 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
onOpen();
|
||||
};
|
||||
|
||||
const onDelete = (e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
const onDelete = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
setShowDeletionAlert(true);
|
||||
};
|
||||
setSelectedItemAction({ path, action: 'delete' });
|
||||
}, [path]);
|
||||
|
||||
const onDeleteConfirm = () => {
|
||||
if (path) {
|
||||
Messenger.send(DashboardMessage.deleteFile, path);
|
||||
}
|
||||
setShowDeletionAlert(false);
|
||||
};
|
||||
|
||||
const onOpenFile = (filePath: string) => {
|
||||
messageHandler.send(DashboardMessage.openFile, filePath);
|
||||
}
|
||||
|
||||
const openOnWebsite = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
const onRename = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
if (settings?.websiteUrl && path) {
|
||||
Messenger.send(GeneralCommands.toVSCode.openOnWebsite, {
|
||||
websiteUrl: settings.websiteUrl,
|
||||
filePath: path
|
||||
});
|
||||
}
|
||||
messageHandler.send(DashboardMessage.rename, path);
|
||||
}, [path])
|
||||
|
||||
const onOpenWebsite = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
openOnWebsite(settings?.websiteUrl, path);
|
||||
}, [settings?.websiteUrl, path]);
|
||||
|
||||
const pinItem = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
@@ -91,14 +82,6 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
})
|
||||
}, [path]);
|
||||
|
||||
const runCustomScript = React.useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>, script: CustomScript) => {
|
||||
e.stopPropagation();
|
||||
Messenger.send(DashboardMessage.runCustomScript, { script, path });
|
||||
},
|
||||
[path]
|
||||
);
|
||||
|
||||
const runCommand = React.useCallback((commandId: string) => {
|
||||
messageHandler.send(GeneralCommands.toVSCode.runCommand, {
|
||||
command: commandId,
|
||||
@@ -110,103 +93,22 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
return pinnedItems.includes(relPath);
|
||||
}, [pinnedItems, relPath]);
|
||||
|
||||
const customScriptActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter(
|
||||
(script) =>
|
||||
(script.type === undefined || script.type === ScriptType.Content) &&
|
||||
!script.bulk &&
|
||||
!script.hidden
|
||||
)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem key={script.id || script.title} onClick={(e) => runCustomScript(e, script)}>
|
||||
<CommandLineIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
const translationsMenu = React.useMemo(() => {
|
||||
if (!locale || !translations || Object.keys(translations).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const crntLocale = translations[locale.locale];
|
||||
const otherLocales = Object.entries(translations).filter(([key]) => key !== locale.locale);
|
||||
|
||||
if (otherLocales.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsMenu)}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuItem onClick={() => onOpenFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => onOpenFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
}, [translations, locale, isDefaultLocale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${listView ? '' : 'group/card absolute top-6 right-0'
|
||||
className={`${listView ? '' : 'group/card absolute top-6 right-2'
|
||||
} flex flex-col space-y-4`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center border border-transparent rounded-full ${listView ? '' : 'p-2 -mt-4'
|
||||
className={`flex items-center border border-transparent rounded-full ${listView ? '' : 'p-1 -mt-3'
|
||||
} group-hover/card:bg-[var(--vscode-sideBar-background)] group-hover/card:border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
<div className={`relative flex text-left`}>
|
||||
{!listView && (
|
||||
<div className="hidden group-hover/card:flex">
|
||||
<QuickAction title={l10n.t(LocalizationKey.dashboardContentsContentActionsMenuItemView)} onClick={onView}>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
{
|
||||
settings?.websiteUrl && (
|
||||
<QuickAction title={l10n.t(LocalizationKey.commonOpenOnWebsite)} onClick={openOnWebsite}>
|
||||
<GlobeEuropeAfricaIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonDelete)}
|
||||
className={`hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] data-[state=open]:text-[var(--vscode-tab-activeForeground)] focus:outline-none'>
|
||||
<DropdownMenuTrigger
|
||||
className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] data-[state=open]:text-[var(--vscode-tab-activeForeground)] focus:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.dashboardContentsContentActionsActionMenuButtonTitle)}</span>
|
||||
<EllipsisVerticalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
<EllipsisHorizontalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align="end">
|
||||
@@ -220,9 +122,14 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsContentActionsMenuItemView)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={onRename}>
|
||||
<RenameIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonRename)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{
|
||||
settings?.websiteUrl && (
|
||||
<DropdownMenuItem onClick={openOnWebsite}>
|
||||
<DropdownMenuItem onClick={onOpenWebsite}>
|
||||
<GlobeEuropeAfricaIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonOpenOnWebsite)}</span>
|
||||
</DropdownMenuItem>
|
||||
@@ -238,9 +145,15 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
{translationsMenu}
|
||||
<TranslationMenu
|
||||
isDefaultLocale={isDefaultLocale}
|
||||
locale={locale}
|
||||
translations={translations} />
|
||||
|
||||
{customScriptActions}
|
||||
<CustomActions
|
||||
filePath={path}
|
||||
contentType={contentType}
|
||||
scripts={scripts} />
|
||||
|
||||
<DropdownMenuItem onClick={onDelete} className={`focus:bg-[var(--vscode-statusBarItem-errorBackground)] focus:text-[var(--vscode-statusBarItem-errorForeground)]`}>
|
||||
<TrashIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
@@ -252,17 +165,6 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showDeletionAlert && (
|
||||
<Alert
|
||||
title={l10n.t(LocalizationKey.dashboardContentsContentActionsAlertTitle, title)}
|
||||
description={l10n.t(LocalizationKey.dashboardContentsContentActionsAlertDescription, title)}
|
||||
okBtnText={l10n.t(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={l10n.t(LocalizationKey.commonCancel)}
|
||||
dismiss={() => setShowDeletionAlert(false)}
|
||||
trigger={onDeleteConfirm}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Page } from '../../models';
|
||||
import { LoadingAtom, SettingsSelector } from '../../state';
|
||||
import { LoadingAtom, SelectedItemActionAtom, SettingsSelector } from '../../state';
|
||||
import { Overview } from './Overview';
|
||||
import { Spinner } from '../Common/Spinner';
|
||||
import { SponsorMsg } from '../Layout/SponsorMsg';
|
||||
import usePages from '../../hooks/usePages';
|
||||
import { useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { TelemetryEvent } from '../../../constants';
|
||||
import { PageLayout } from '../Layout/PageLayout';
|
||||
import { FilesProvider } from '../../providers/FilesProvider';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { deletePage } from '../../utils';
|
||||
|
||||
export interface IContentsProps {
|
||||
pages: Page[];
|
||||
@@ -23,9 +27,38 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({
|
||||
const loading = useRecoilValue(LoadingAtom);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const { pageItems } = usePages(pages);
|
||||
const [showDeletionAlert, setShowDeletionAlert] = React.useState(false);
|
||||
const [page, setPage] = useState<Page | undefined>(undefined);
|
||||
const [selectedItemAction, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
|
||||
const pageFolders = [...new Set(pageItems.map((page) => page.fmFolder))];
|
||||
|
||||
const onDismiss = useCallback(() => {
|
||||
setShowDeletionAlert(false);
|
||||
setSelectedItemAction(undefined);
|
||||
}, []);
|
||||
|
||||
const onDeleteConfirm = useCallback(() => {
|
||||
if (page) {
|
||||
deletePage(page.fmFilePath);
|
||||
}
|
||||
setShowDeletionAlert(false);
|
||||
setSelectedItemAction(undefined);
|
||||
}, [page]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItemAction && selectedItemAction.path && selectedItemAction.action === 'delete') {
|
||||
const page = pageItems.find((p) => p.fmFilePath === selectedItemAction.path);
|
||||
|
||||
if (page) {
|
||||
setPage(page);
|
||||
setShowDeletionAlert(true);
|
||||
}
|
||||
|
||||
setSelectedItemAction(undefined);
|
||||
}
|
||||
}, [pageItems, selectedItemAction]);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewContentsView
|
||||
@@ -46,6 +79,17 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({
|
||||
/>
|
||||
|
||||
<img className='hidden' src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Ffrontmatter.codes%2Fmetrics%2Fdashboards&slug=content" alt="Content metrics" />
|
||||
|
||||
{showDeletionAlert && page && (
|
||||
<Alert
|
||||
title={l10n.t(LocalizationKey.dashboardContentsContentActionsAlertTitle, page.title)}
|
||||
description={l10n.t(LocalizationKey.dashboardContentsContentActionsAlertDescription, page.title)}
|
||||
okBtnText={l10n.t(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={l10n.t(LocalizationKey.commonCancel)}
|
||||
dismiss={onDismiss}
|
||||
trigger={onDeleteConfirm}
|
||||
/>
|
||||
)}
|
||||
</PageLayout>
|
||||
</FilesProvider>
|
||||
);
|
||||
|
||||
89
src/dashboardWebView/components/Contents/CustomActions.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { CommandLineIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandLineIcon as CommandLineIconSolid } from '@heroicons/react/24/solid';
|
||||
import { runCustomScript } from '../../utils';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ICustomActionsProps {
|
||||
filePath: string;
|
||||
contentType: string;
|
||||
scripts: CustomScript[] | undefined;
|
||||
showTrigger?: boolean;
|
||||
}
|
||||
|
||||
export const CustomActions: React.FunctionComponent<ICustomActionsProps> = ({
|
||||
filePath,
|
||||
contentType,
|
||||
scripts,
|
||||
showTrigger = false,
|
||||
}: React.PropsWithChildren<ICustomActionsProps>) => {
|
||||
|
||||
const onRunCustomScript = React.useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>, script: CustomScript) => {
|
||||
e.stopPropagation();
|
||||
runCustomScript(script, filePath);
|
||||
},
|
||||
[filePath]
|
||||
);
|
||||
|
||||
const customScripts = React.useMemo(() => {
|
||||
return (scripts || []).filter((script: CustomScript) => {
|
||||
if (script.contentTypes && script.contentTypes.length > 0) {
|
||||
return script.contentTypes.includes(contentType);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [scripts, contentType]);
|
||||
|
||||
const customActions = React.useMemo(() => {
|
||||
if (!customScripts || customScripts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
(customScripts || [])
|
||||
.filter(
|
||||
(script) =>
|
||||
(script.type === undefined || script.type === ScriptType.Content) &&
|
||||
!script.bulk &&
|
||||
!script.hidden
|
||||
)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.id || script.title}
|
||||
title={script.title}
|
||||
onClick={(e) => onRunCustomScript(e, script)}>
|
||||
<CommandLineIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
);
|
||||
}, [customScripts, onRunCustomScript]);
|
||||
|
||||
if (!customActions || customActions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (showTrigger) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
title={l10n.t(LocalizationKey.commonOpenCustomActions)}
|
||||
className='px-2 text-[var(--frontmatter-secondary-text)] hover:text-[var(--frontmatter-button-hoverBackground)] focus-visible:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonOpenCustomActions)}</span>
|
||||
<CommandLineIconSolid className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent>
|
||||
{customActions}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{customActions}</>;
|
||||
};
|
||||
66
src/dashboardWebView/components/Contents/FooterActions.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { openFile, openOnWebsite } from '../../utils';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedItemActionAtom } from '../../state';
|
||||
import { CustomScript } from '../../../models';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IFooterActionsProps {
|
||||
filePath: string;
|
||||
contentType: string;
|
||||
websiteUrl?: string;
|
||||
scripts?: CustomScript[];
|
||||
}
|
||||
|
||||
export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
filePath,
|
||||
contentType,
|
||||
websiteUrl,
|
||||
scripts
|
||||
}: React.PropsWithChildren<IFooterActionsProps>) => {
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
|
||||
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)] rounded`}>
|
||||
{/* <ItemSelection filePath={filePath} show /> */}
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardContentsContentActionsMenuItemView)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => openFile(filePath)}>
|
||||
<span className={`sr-only`}>{l10n.t(LocalizationKey.dashboardContentsContentActionsMenuItemView)}</span>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
{
|
||||
websiteUrl && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonOpenOnWebsite)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => openOnWebsite(websiteUrl, filePath)}>
|
||||
<span className={`sr-only`}>{l10n.t(LocalizationKey.commonOpenOnWebsite)}</span>
|
||||
<GlobeEuropeAfricaIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
|
||||
<CustomActions
|
||||
filePath={filePath}
|
||||
contentType={contentType}
|
||||
scripts={scripts}
|
||||
showTrigger />
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonDelete)}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={() => setSelectedItemAction({ path: filePath, action: 'delete' })}>
|
||||
<span className={`sr-only`}>{l10n.t(LocalizationKey.commonDelete)}</span>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Page } from '../../models/Page';
|
||||
import { SettingsSelector, ViewSelector } from '../../state';
|
||||
import { DateField } from '../Common/DateField';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardViewType } from '../../models';
|
||||
import { ContentActions } from './ContentActions';
|
||||
import { useMemo } from 'react';
|
||||
@@ -13,11 +11,14 @@ import * as React from 'react';
|
||||
import useExtensibility from '../../hooks/useExtensibility';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '../..';
|
||||
import useCard from '../../hooks/useCard';
|
||||
import { I18nLabel } from './I18nLabel';
|
||||
import { ItemSelection } from '../Common/ItemSelection';
|
||||
import { openFile } from '../../utils';
|
||||
import { FooterActions } from './FooterActions';
|
||||
import useSelectedItems from '../../hooks/useSelectedItems';
|
||||
import { cn } from '../../../utils/cn';
|
||||
import { Tags } from './Tags';
|
||||
|
||||
export interface IItemProps extends Page { }
|
||||
|
||||
@@ -26,12 +27,12 @@ const PREVIEW_IMAGE_FIELD = 'fmPreviewImage';
|
||||
export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
...pageData
|
||||
}: React.PropsWithChildren<IItemProps>) => {
|
||||
const { selectedFiles } = useSelectedItems();
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const draftField = useMemo(() => settings?.draftField, [settings]);
|
||||
const cardFields = useMemo(() => settings?.dashboardState?.contents?.cardFields, [settings?.dashboardState?.contents?.cardFields]);
|
||||
const { escapedTitle, escapedDescription } = useCard(pageData, settings?.dashboardState?.contents?.cardFields);
|
||||
const navigate = useNavigate();
|
||||
const { titleHtml, descriptionHtml, dateHtml, statusHtml, tagsHtml, imageHtml, footerHtml } = useExtensibility({
|
||||
fmFilePath: pageData.fmFilePath,
|
||||
date: pageData.date,
|
||||
@@ -41,9 +42,11 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
pageData
|
||||
});
|
||||
|
||||
const openFile = () => {
|
||||
Messenger.send(DashboardMessage.openFile, pageData.fmFilePath);
|
||||
};
|
||||
const isSelected = useMemo(() => selectedFiles.includes(pageData.fmFilePath), [selectedFiles, pageData.fmFilePath]);
|
||||
|
||||
const onOpenFile = React.useCallback(() => {
|
||||
openFile(pageData.fmFilePath);
|
||||
}, [pageData.fmFilePath]);
|
||||
|
||||
const tags: string[] | undefined = useMemo(() => {
|
||||
if (!settings?.dashboardState?.contents?.tags) {
|
||||
@@ -92,9 +95,9 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
|
||||
return (
|
||||
dateHtml ? (
|
||||
<div className='mr-4' dangerouslySetInnerHTML={{ __html: dateHtml }} />
|
||||
<div className='mr-6' dangerouslySetInnerHTML={{ __html: dateHtml }} />
|
||||
) : (
|
||||
cardFields?.date && pageData.date ? <DateField className={`mr-4`} value={pageData.date} format={pageData.fmDateFormat} /> : null
|
||||
cardFields?.date && pageData.date ? <DateField className={`mr-6`} value={pageData.date} format={pageData.fmDateFormat} /> : null
|
||||
)
|
||||
)
|
||||
}, [dateHtml, cardFields?.date, pageData]);
|
||||
@@ -107,12 +110,12 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
return (
|
||||
<li className="relative">
|
||||
<div
|
||||
className={`group flex flex-col items-start content-start h-full w-full text-left shadow-md dark:shadow-none hover:shadow-xl border rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] border-[var(--frontmatter-border)]`}
|
||||
className={cn(`group flex flex-col items-start content-start h-full w-full text-left shadow-md dark:shadow-none hover:shadow-xl border rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] border-[var(--frontmatter-border)]`, isSelected && `border-[var(--frontmatter-border-active)]`)}
|
||||
>
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={openFile}
|
||||
className={`relative h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]`}
|
||||
onClick={onOpenFile}
|
||||
className={`relative rounded h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{
|
||||
imageHtml ?
|
||||
@@ -139,29 +142,29 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<div className="relative p-4 w-full grow">
|
||||
{
|
||||
(statusPlaceholder || datePlaceholder) && (
|
||||
<div className={`flex justify-between items-center ${hasDraftOrDate ? `mb-2` : ``}`}>
|
||||
{statusPlaceholder}
|
||||
{datePlaceholder}
|
||||
<div className={`space-y-2 ${hasDraftOrDate ? `mb-2` : ``}`}>
|
||||
<div>{statusPlaceholder}</div>
|
||||
<div>{datePlaceholder}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<ContentActions
|
||||
title={pageData.title}
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
locale={pageData.fmLocale}
|
||||
isDefaultLocale={pageData.fmDefaultLocale}
|
||||
translations={pageData.fmTranslations}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={openFile}
|
||||
onOpen={onOpenFile}
|
||||
/>
|
||||
|
||||
<I18nLabel page={pageData} />
|
||||
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={openFile}
|
||||
onClick={onOpenFile}
|
||||
className={`text-left block`}>
|
||||
{
|
||||
titleHtml ? (
|
||||
@@ -178,13 +181,13 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
(escapedDescription || descriptionHtml) && (
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={openFile}
|
||||
onClick={onOpenFile}
|
||||
className={`mt-2 text-left block`}>
|
||||
{
|
||||
descriptionHtml ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: descriptionHtml }} />
|
||||
) : (
|
||||
<p className={`text-xs text-[vara(--vscode-titleBar-activeForeground)]`}>{escapedDescription}</p>
|
||||
<p className={`text-xs text-[var(--frontmatter-secondary-text)]`}>{escapedDescription}</p>
|
||||
)
|
||||
}
|
||||
</button>
|
||||
@@ -195,27 +198,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
tagsHtml ? (
|
||||
<div className="mt-2" dangerouslySetInnerHTML={{ __html: tagsHtml }} />
|
||||
) : (
|
||||
tags && tags.length > 0 && (
|
||||
<div className="mt-2">
|
||||
{tags.map(
|
||||
(tag, index) => tag && (
|
||||
<button
|
||||
key={index}
|
||||
className={`inline-block mr-1 mt-1 text-xs text-[var(--vscode-textPreformat-foreground)] hover:brightness-75 hover:underline hover:underline-offset-1`}
|
||||
title={l10n.t(LocalizationKey.commonFilterValue, tag)}
|
||||
onClick={() => {
|
||||
const tagField = settings?.dashboardState.contents.tags;
|
||||
if (tagField) {
|
||||
navigate(`${routePaths.contents}?taxonomy=${tagField}&value=${tag}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
#{tag}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
<Tags values={tags} tagField={settings?.dashboardState?.contents?.tags} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
@@ -225,6 +208,12 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<div className="placeholder__card__footer p-4 w-full" dangerouslySetInnerHTML={{ __html: footerHtml }} />
|
||||
)
|
||||
}
|
||||
|
||||
<FooterActions
|
||||
filePath={pageData.fmFilePath}
|
||||
contentType={pageData.fmContentType}
|
||||
websiteUrl={settings?.websiteUrl}
|
||||
scripts={settings?.scripts} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
@@ -235,20 +224,20 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b hover:bg-opacity-70 border-[var(--frontmatter-border)] hover:bg-[var(--vscode-sideBar-background)]`}
|
||||
>
|
||||
<div className="col-span-8 font-bold truncate flex items-center space-x-4">
|
||||
<ItemSelection filePath={pageData.fmFilePath} isRowItem />
|
||||
<ItemSelection filePath={pageData.fmFilePath} show />
|
||||
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={openFile}>
|
||||
onClick={onOpenFile}>
|
||||
{escapedTitle}
|
||||
</button>
|
||||
|
||||
<ContentActions
|
||||
title={escapedTitle || ""}
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={openFile}
|
||||
onOpen={onOpenFile}
|
||||
listView
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,28 +2,32 @@ import * as React from 'react';
|
||||
import { Page } from '../../models';
|
||||
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
|
||||
import { ContentActions } from './ContentActions';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import useCard from '../../hooks/useCard';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ItemSelection } from '../Common/ItemSelection';
|
||||
import { openFile } from '../../utils';
|
||||
import useSelectedItems from '../../hooks/useSelectedItems';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface IPinnedItemProps extends Page { }
|
||||
|
||||
export const PinnedItem: React.FunctionComponent<IPinnedItemProps> = ({
|
||||
...pageData
|
||||
}: React.PropsWithChildren<IPinnedItemProps>) => {
|
||||
const { selectedFiles } = useSelectedItems();
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const { escapedTitle } = useCard(pageData, settings?.dashboardState?.contents?.cardFields);
|
||||
|
||||
const openFile = React.useCallback(() => {
|
||||
messageHandler.send(DashboardMessage.openFile, pageData.fmFilePath);
|
||||
const isSelected = React.useMemo(() => selectedFiles.includes(pageData.fmFilePath), [selectedFiles, pageData.fmFilePath]);
|
||||
|
||||
const onOpenFile = React.useCallback(() => {
|
||||
openFile(pageData.fmFilePath);
|
||||
}, [pageData.fmFilePath]);
|
||||
|
||||
return (
|
||||
<li className='group flex w-full border border-[var(--frontmatter-border)] rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] relative'>
|
||||
<button onClick={openFile} className='relative h-full w-1/3'>
|
||||
<li className={cn(`group flex w-full border border-[var(--frontmatter-border)] rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] relative`, isSelected && `border-[var(--frontmatter-border-active)]`)}>
|
||||
<button onClick={onOpenFile} className='relative h-full w-1/3'>
|
||||
{
|
||||
pageData["fmPreviewImage"] ? (
|
||||
<img
|
||||
@@ -44,13 +48,13 @@ export const PinnedItem: React.FunctionComponent<IPinnedItemProps> = ({
|
||||
|
||||
<ItemSelection filePath={pageData.fmFilePath} />
|
||||
|
||||
<button onClick={openFile} className='relative w-2/3 p-4 pr-6 text-left flex items-start'>
|
||||
<button onClick={onOpenFile} className='relative w-2/3 p-4 pr-6 text-left flex items-start'>
|
||||
<p className='font-bold'>{escapedTitle}</p>
|
||||
|
||||
<ContentActions
|
||||
title={pageData.title}
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={openFile}
|
||||
/>
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Status: React.FunctionComponent<IStatusProps> = ({
|
||||
if (draftValue) {
|
||||
return (
|
||||
<span
|
||||
className={`inline-block px-1 py-1 leading-none rounded-sm font-semibold uppercase tracking-wide text-[0.7rem] text-[var(--vscode-badge-foreground)] bg-[var(--vscode-badge-background)]`}
|
||||
className={`inline-block px-[3px] py-[2px] rounded font-semibold uppercase tracking-wide text-[0.7rem] text-[var(--vscode-badge-foreground)] bg-[var(--vscode-badge-background)]`}
|
||||
>
|
||||
{draftValue}
|
||||
</span>
|
||||
@@ -51,7 +51,7 @@ export const Status: React.FunctionComponent<IStatusProps> = ({
|
||||
return (
|
||||
<span
|
||||
className={`draft__status
|
||||
inline-block px-1 py-1 leading-none rounded-sm font-semibold uppercase tracking-wide text-[0.7rem]
|
||||
inline-block px-[3px] py-[2px] rounded font-semibold uppercase tracking-wide text-[0.7rem]
|
||||
${draftValue ?
|
||||
'bg-[var(--vscode-statusBarItem-errorBackground)] text-[var(--vscode-statusBarItem-errorForeground)]' :
|
||||
isFuture ?
|
||||
|
||||
35
src/dashboardWebView/components/Contents/Tag.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { routePaths } from '../..';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export interface ITagProps {
|
||||
value?: string;
|
||||
tagField?: string | null | undefined;
|
||||
}
|
||||
|
||||
export const Tag: React.FunctionComponent<ITagProps> = ({
|
||||
value,
|
||||
tagField
|
||||
}: React.PropsWithChildren<ITagProps>) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`inline-block mr-1 mt-1 text-xs text-[var(--vscode-button-secondaryForeground)] bg-[var(--vscode-button-secondaryBackground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)] border border-[var(--frontmatter-border)] rounded px-1 py-0.5`}
|
||||
title={l10n.t(LocalizationKey.commonFilterValue, value)}
|
||||
onClick={() => {
|
||||
if (tagField) {
|
||||
navigate(`${routePaths.contents}?taxonomy=${tagField}&value=${value}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
#{value}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
29
src/dashboardWebView/components/Contents/Tags.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as React from 'react';
|
||||
import { Tag } from './Tag';
|
||||
|
||||
export interface ITagsProps {
|
||||
values?: string[];
|
||||
tagField?: string | null | undefined;
|
||||
}
|
||||
|
||||
export const Tags: React.FunctionComponent<ITagsProps> = ({
|
||||
values,
|
||||
tagField
|
||||
}: React.PropsWithChildren<ITagsProps>) => {
|
||||
if (!values || values.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-2">
|
||||
{values.map(
|
||||
(tag, index) => tag && (
|
||||
<Tag
|
||||
key={index}
|
||||
value={tag}
|
||||
tagField={tagField} />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
80
src/dashboardWebView/components/Contents/TranslationMenu.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { openFile } from '../../utils/MessageHandlers';
|
||||
import { I18nConfig } from '../../../models';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ITranslationMenuProps {
|
||||
isDefaultLocale?: boolean;
|
||||
locale?: I18nConfig;
|
||||
translations?: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const TranslationMenu: React.FunctionComponent<ITranslationMenuProps> = ({
|
||||
isDefaultLocale,
|
||||
locale,
|
||||
translations,
|
||||
}: React.PropsWithChildren<ITranslationMenuProps>) => {
|
||||
|
||||
const otherLocales = React.useMemo(() => {
|
||||
if (!translations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(translations).filter(([key]) => key !== locale?.locale);
|
||||
}, [translations]);
|
||||
|
||||
const crntLocale = React.useMemo(() => {
|
||||
if (!locale?.locale || !translations || !translations[locale.locale]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return translations[locale.locale];
|
||||
}, [translations, locale]);
|
||||
|
||||
|
||||
if (!locale || !translations || Object.keys(translations).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (otherLocales.length === 0 || !crntLocale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsMenu)}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuItem onClick={() => openFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => openFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
};
|
||||
@@ -13,6 +13,8 @@ import { CustomScript, ScriptType } from '../../../models';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { useFilesContext } from '../../providers/FilesProvider';
|
||||
import { COMMAND_NAME, GeneralCommands } from '../../../constants';
|
||||
import { RenameIcon } from '../../../components/icons/RenameIcon';
|
||||
import { openFile } from '../../utils';
|
||||
|
||||
export interface IActionsBarProps {
|
||||
view: NavigationType;
|
||||
@@ -31,7 +33,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
const viewFile = React.useCallback(() => {
|
||||
if (selectedFiles.length === 1) {
|
||||
if (view === NavigationType.Contents) {
|
||||
messageHandler.send(DashboardMessage.openFile, selectedFiles[0]);
|
||||
openFile(selectedFiles[0]);
|
||||
} else if (view === NavigationType.Media) {
|
||||
setSelectedItemAction({ path: selectedFiles[0], action: 'view' })
|
||||
}
|
||||
@@ -106,7 +108,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
|
||||
<DropdownMenuContent align='start'>
|
||||
|
||||
<DropdownMenuItem onClick={() => messageHandler.send(DashboardMessage.openFile, crntLocale.path)}>
|
||||
<DropdownMenuItem onClick={() => openFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -116,7 +118,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => messageHandler.send(DashboardMessage.openFile, value.path)}
|
||||
onClick={() => openFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
@@ -195,6 +197,21 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
<span>{l10n.t(LocalizationKey.commonView)}</span>
|
||||
</ActionsBarItem>
|
||||
|
||||
{
|
||||
view === NavigationType.Contents && (
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === 0 || selectedFiles.length > 1}
|
||||
onClick={() => {
|
||||
messageHandler.send(DashboardMessage.rename, selectedFiles[0]);
|
||||
setSelectedFiles([]);
|
||||
}}
|
||||
>
|
||||
<RenameIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonRename)}</span>
|
||||
</ActionsBarItem>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
view === NavigationType.Media && (
|
||||
<>
|
||||
@@ -234,7 +251,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
onClick={() => setSelectedFiles([])}
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected)}</span>
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import { Link } from '../Common/Link';
|
||||
import { SPONSOR_LINK } from '../../../constants';
|
||||
import { Filters } from './Filters';
|
||||
import { ActionsBar } from './ActionsBar';
|
||||
import { RefreshDashboardData } from './RefreshDashboardData';
|
||||
|
||||
export interface IHeaderProps {
|
||||
header?: React.ReactNode;
|
||||
@@ -124,10 +125,10 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
|
||||
return (
|
||||
<div className={`w-full sticky top-0 z-20 bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)]`}>
|
||||
<div className={`overflow-x-auto mb-0 border-b flex justify-between bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)] border-[var(--frontmatter-border)]`}>
|
||||
<div className={`px-4 overflow-x-auto mb-0 border-b flex justify-between bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)] border-[var(--frontmatter-border)]`}>
|
||||
<Tabs onNavigate={updateView} />
|
||||
|
||||
<div className='flex items-center space-x-2 pr-4'>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<ProjectSwitcher />
|
||||
|
||||
{
|
||||
@@ -163,13 +164,15 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
{location.pathname === routePaths.contents && (
|
||||
<>
|
||||
<div className={`px-4 mt-2 mb-2 flex items-center justify-between`}>
|
||||
<div className={`flex items-center justify-start space-x-4 flex-1`}>
|
||||
<div className={`flex items-center justify-start space-x-2 flex-1`}>
|
||||
<ChoiceButton
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderHeaderCreateContent)}
|
||||
choices={choiceOptions}
|
||||
onClick={createContent}
|
||||
disabled={!settings?.initialized}
|
||||
/>
|
||||
|
||||
<RefreshDashboardData />
|
||||
</div>
|
||||
|
||||
<Searchbox />
|
||||
|
||||
@@ -63,7 +63,7 @@ export const Navigation: React.FunctionComponent<INavigationProps> = ({
|
||||
}, [settings?.draftField?.type, tabInfo]);
|
||||
|
||||
return (
|
||||
<nav className="flex-1 -mb-px flex space-x-6 xl:space-x-8" aria-label="Tabs">
|
||||
<nav className="flex-1 -mb-px flex space-x-2 xl:space-x-4" aria-label="Tabs">
|
||||
{settings?.draftField?.type === 'boolean' ? (
|
||||
tabs.map((tab) => (
|
||||
<NavigationItem
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
} from '../../state';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { ArrowClockwiseIcon } from '../../../components/icons/ArrowClockwiseIcon';
|
||||
|
||||
export interface IRefreshDashboardDataProps { }
|
||||
|
||||
@@ -62,11 +62,11 @@ export const RefreshDashboardData: React.FunctionComponent<IRefreshDashboardData
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`mr-2 text-[var(--vscode-foreground)] hover:text-[var(--vscode-textLink-foreground)]`}
|
||||
className={`mr-2 text-[var(--frontmatter-text)] hover:text-[var(--vscode-textLink-foreground)]`}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderRefreshDashboardLabel)}
|
||||
onClick={refresh}
|
||||
>
|
||||
<ArrowPathIcon className={`h-5 w-5`} />
|
||||
<ArrowClockwiseIcon className={`h-5 w-5`} />
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.dashboardHeaderRefreshDashboardLabel)}</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { MagnifyingGlassIcon, XCircleIcon } from '@heroicons/react/24/solid';
|
||||
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useDebounce } from '../../../hooks/useDebounce';
|
||||
import { SearchAtom, SearchReadyAtom } from '../../state';
|
||||
import { RefreshDashboardData } from './RefreshDashboardData';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { TextField } from '../Common/TextField';
|
||||
@@ -58,8 +57,6 @@ export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({
|
||||
onReset={reset}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<RefreshDashboardData />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { NavigationType } from '../../models';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface ITabProps {
|
||||
navigationType: NavigationType;
|
||||
@@ -16,11 +17,11 @@ export const Tab: React.FunctionComponent<ITabProps> = ({
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`h-full flex items-center py-2 px-4 text-sm font-medium text-center border-b-2 border-transparent hover:border-[var(--vscode-tab-activeForeground)] hover:text-[var(--vscode-tab-activeForeground)] ${location.pathname === `/${navigationType}`
|
||||
className={cn(`h-full flex items-center py-2 px-1 text-sm font-medium text-center border-b-2 border-transparent hover:text-[var(--vscode-tab-activeForeground)] ${location.pathname === `/${navigationType}`
|
||||
?
|
||||
`text-[var(--vscode-tab-activeForeground)] border-[var(--vscode-tab-activeForeground)]` :
|
||||
`text-[var(--vscode-tab-inactiveForeground)]`
|
||||
}`}
|
||||
`text-[var(--frontmatter-nav-active)] border-[var(--frontmatter-nav-active)]` :
|
||||
`text-[var(--frontmatter-nav-inactive)]`
|
||||
}`)}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="profile"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { CircleStackIcon, PhotoIcon, ScissorsIcon, TagIcon } from '@heroicons/react/24/outline';
|
||||
import { PhotoIcon, ScissorsIcon, TagIcon, CircleStackIcon } from '@heroicons/react/24/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FeatureFlag } from '../../../components/features/FeatureFlag';
|
||||
import { FEATURE_FLAG } from '../../../constants';
|
||||
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
|
||||
import { NavigationType } from '../../models';
|
||||
import { ModeAtom } from '../../state';
|
||||
import { Tab } from './Tab';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { PageIcon } from '../../../panelWebView/components/Icons';
|
||||
|
||||
export interface ITabsProps {
|
||||
onNavigate: (navigationType: NavigationType) => void;
|
||||
@@ -21,42 +21,42 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
|
||||
|
||||
return (
|
||||
<ul
|
||||
className="flex items-center justify-start h-full"
|
||||
className="flex items-center justify-start h-full space-x-4"
|
||||
data-tabs-toggle="#myTabContent"
|
||||
role="tablist"
|
||||
>
|
||||
<li className="mr-2" role="presentation">
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Contents} onNavigate={onNavigate}>
|
||||
<MarkdownIcon className={`h-6 w-auto mr-2`} />
|
||||
<PageIcon className={`h-4 w-auto mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsContents)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
<li className="mr-2" role="presentation">
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Media} onNavigate={onNavigate}>
|
||||
<PhotoIcon className={`h-6 w-auto mr-2`} />
|
||||
<PhotoIcon className={`h-4 w-auto mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsMedia)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.snippets.view}>
|
||||
<li className="mr-2" role="presentation">
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Snippets} onNavigate={onNavigate}>
|
||||
<ScissorsIcon className={`h-6 w-auto mr-2`} />
|
||||
<ScissorsIcon className={`h-4 w-auto mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsSnippets)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.data.view}>
|
||||
<li className="mr-2" role="presentation">
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Data} onNavigate={onNavigate}>
|
||||
<CircleStackIcon className={`h-6 w-auto mr-2`} />
|
||||
<CircleStackIcon className={`h-4 w-auto mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsData)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.taxonomy.view}>
|
||||
<li className="mr-2" role="presentation">
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Taxonomy} onNavigate={onNavigate}>
|
||||
<TagIcon className={`h-6 w-auto mr-2`} />
|
||||
<TagIcon className={`h-4 w-auto mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsTaxonomies)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
|
||||
55
src/dashboardWebView/components/Media/CustomActions.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { runCustomScript } from '../../utils';
|
||||
import { CommandLineIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandLineIcon as CommandLineIconSolid } from '@heroicons/react/24/solid';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ICustomActionsProps {
|
||||
filePath: string;
|
||||
scripts?: CustomScript[];
|
||||
showTrigger?: boolean;
|
||||
}
|
||||
|
||||
export const CustomActions: React.FunctionComponent<ICustomActionsProps> = ({
|
||||
filePath,
|
||||
scripts,
|
||||
showTrigger = false,
|
||||
}: React.PropsWithChildren<ICustomActionsProps>) => {
|
||||
|
||||
const customActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter((script) => script.type === ScriptType.MediaFile && !script.hidden)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.title}
|
||||
onClick={() => runCustomScript(script, filePath)}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
|
||||
if (showTrigger) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
title={l10n.t(LocalizationKey.commonOpenCustomActions)}
|
||||
className='px-2 text-[var(--frontmatter-secondary-text)] hover:text-[var(--frontmatter-button-hoverBackground)] focus-visible:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonOpenCustomActions)}</span>
|
||||
<CommandLineIconSolid className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent>
|
||||
{customActions}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{customActions}</>;
|
||||
};
|
||||
@@ -10,7 +10,7 @@ export const DetailsItem: React.FunctionComponent<IDetailsItemProps> = ({ title,
|
||||
<>
|
||||
<div className="py-3 flex justify-between text-sm font-medium">
|
||||
<dt className={`text-[var(--vscode-editor-foreground)]`}>{title}</dt>
|
||||
<dd className={`text-right text-[var(--vscode-foreground)]`}>
|
||||
<dd className={`text-right text-[var(--frontmatter-text)]`}>
|
||||
{details}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@@ -67,23 +67,23 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
fields?.forEach((field) => {
|
||||
if (field.name === "title") {
|
||||
items.push(
|
||||
<DetailsItem title={l10n.t(LocalizationKey.dashboardMediaCommonTitle)} details={media.metadata.title || ""} />
|
||||
<DetailsItem key="title" title={l10n.t(LocalizationKey.dashboardMediaCommonTitle)} details={media.metadata.title || ""} />
|
||||
);
|
||||
} else if (field.name === "caption") {
|
||||
if (isImageFile) {
|
||||
items.push(
|
||||
<DetailsItem title={l10n.t(LocalizationKey.dashboardMediaCommonCaption)} details={media.metadata.caption || ""} />
|
||||
<DetailsItem key="caption" title={l10n.t(LocalizationKey.dashboardMediaCommonCaption)} details={media.metadata.caption || ""} />
|
||||
);
|
||||
}
|
||||
} else if (field.name === "alt") {
|
||||
if (isImageFile) {
|
||||
items.push(
|
||||
<DetailsItem title={l10n.t(LocalizationKey.dashboardMediaCommonAlt)} details={media.metadata.alt || ""} />
|
||||
<DetailsItem key="alt" title={l10n.t(LocalizationKey.dashboardMediaCommonAlt)} details={media.metadata.alt || ""} />
|
||||
);
|
||||
}
|
||||
} else {
|
||||
items.push(
|
||||
<DetailsItem title={field.title || field.name} details={(media.metadata[field.name] || "") as string} />
|
||||
<DetailsItem key={field.name} title={field.title || field.name} details={(media.metadata[field.name] || "") as string} />
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -147,7 +147,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
)}
|
||||
<div className="mt-4 flex items-start justify-between">
|
||||
<div>
|
||||
<h2 className={`text-lg font-medium text-[var(--vscode-foreground)]`}>
|
||||
<h2 className={`text-lg font-medium text-[var(--frontmatter-text)]`}>
|
||||
{media.filename}
|
||||
</h2>
|
||||
<p className={`text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
@@ -169,7 +169,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
|
||||
{!showForm && (
|
||||
<>
|
||||
<h3 className={`text-base flex items-center text-[var(--vscode-foreground)]`}>
|
||||
<h3 className={`text-base flex items-center text-[var(--frontmatter-text)]`}>
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaMetadataPanelFormMetadataTitle)}</span>
|
||||
<button onClick={onEdit}>
|
||||
<PencilSquareIcon className="w-4 h-4 ml-2" aria-hidden="true" />
|
||||
@@ -185,7 +185,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
|
||||
{!showForm && (
|
||||
<div>
|
||||
<h3 className={`text-base text-[var(--vscode-foreground)]`}>
|
||||
<h3 className={`text-base text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaMetadataPanelFormInformationTitle)}
|
||||
</h3>
|
||||
<dl className={`mt-2 border-t border-b divide-y border-[var(--frontmatter-border)] divide-[var(--frontmatter-border)]`}>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useMediaFolder from '../../hooks/useMediaFolder';
|
||||
import { RefreshDashboardData } from '../Header/RefreshDashboardData';
|
||||
|
||||
export interface IFolderCreationProps { }
|
||||
|
||||
@@ -107,7 +108,7 @@ export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 justify-start">
|
||||
<div className="flex flex-1 justify-start space-x-2">
|
||||
{renderPostAssetsButton}
|
||||
<button
|
||||
className={`inline-flex items-center px-3 py-1 border border-transparent text-xs leading-4 font-medium focus:outline-none rounded text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`}
|
||||
@@ -117,6 +118,8 @@ export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (
|
||||
<FolderPlusIcon className={`mr-2 h-6 w-6`} />
|
||||
<span className={``}>{l10n.t(LocalizationKey.dashboardMediaFolderCreationFolderCreate)}</span>
|
||||
</button>
|
||||
|
||||
<RefreshDashboardData />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
<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(--vscode-foreground)]`}>
|
||||
<span className={`font-extrabold absolute bottom-3 left-1/2 transform -translate-x-1/2 text-[var(--frontmatter-text)]`}>
|
||||
C
|
||||
</span>
|
||||
)}
|
||||
|
||||
111
src/dashboardWebView/components/Media/FooterActions.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { ClipboardIcon, CodeBracketIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedItemActionAtom } from '../../state';
|
||||
import { CustomScript, MediaInfo, Snippet, ViewData } from '../../../models';
|
||||
import { copyToClipboard } from '../../utils';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IFooterActionsProps {
|
||||
media: MediaInfo;
|
||||
snippets: Snippet[];
|
||||
relPath?: string;
|
||||
viewData?: ViewData | undefined
|
||||
scripts?: CustomScript[];
|
||||
insertIntoArticle: () => void;
|
||||
insertSnippet: () => void;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
relPath,
|
||||
media,
|
||||
snippets,
|
||||
viewData,
|
||||
scripts,
|
||||
insertIntoArticle,
|
||||
insertSnippet,
|
||||
onDelete,
|
||||
}: React.PropsWithChildren<IFooterActionsProps>) => {
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
|
||||
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)}
|
||||
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>
|
||||
</QuickAction>
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(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>
|
||||
</QuickAction>
|
||||
|
||||
{viewData?.filePath ? (
|
||||
<>
|
||||
<QuickAction
|
||||
title={
|
||||
viewData.metadataInsert && viewData.fieldName
|
||||
? l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertField, viewData.fieldName)
|
||||
: l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertMarkdown)
|
||||
}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertIntoArticle}
|
||||
>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
{viewData?.position && snippets.length > 0 && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertSnippet}>
|
||||
<CodeBracketIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{
|
||||
relPath && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => copyToClipboard(parseWinPath(relPath) || '')}>
|
||||
<ClipboardIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
<CustomActions
|
||||
filePath={media.fsPath}
|
||||
scripts={scripts}
|
||||
showTrigger />
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(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>
|
||||
);
|
||||
};
|
||||
@@ -32,6 +32,7 @@ import { getRelPath } from '../../utils';
|
||||
import { Snippet } from '../../../models';
|
||||
import useMediaInfo from '../../hooks/useMediaInfo';
|
||||
import { ItemSelection } from '../Common/ItemSelection';
|
||||
import { FooterActions } from './FooterActions';
|
||||
|
||||
export interface IItemProps {
|
||||
media: MediaInfo;
|
||||
@@ -185,13 +186,6 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
}
|
||||
}, [media.vsPath]);
|
||||
|
||||
const updateMetadata = useCallback(() => {
|
||||
setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'edit'
|
||||
});
|
||||
}, [media]);
|
||||
|
||||
const renderMediaIcon = useMemo(() => {
|
||||
const path = media.fsPath;
|
||||
const extension = path.split('.').pop();
|
||||
@@ -265,7 +259,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className={`group relative shadow-md hover:shadow-xl dark:shadow-none border rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] border-[var(--frontmatter-border)]`}>
|
||||
<li className={`group flex flex-col relative shadow-md hover:shadow-xl dark:shadow-none border rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-sideBarTitle-foreground)] border-[var(--frontmatter-border)]`}>
|
||||
<button
|
||||
className={`group/button relative block w-full aspect-w-10 aspect-h-7 overflow-hidden h-48 ${isImage ? 'cursor-pointer' : 'cursor-default'} border-b border-[var(--frontmatter-border)]`}
|
||||
onClick={hasViewData ? undefined : openLightbox}
|
||||
@@ -318,7 +312,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
<div className={`relative py-4 pl-4 pr-12`}>
|
||||
|
||||
<div className={`relative py-4 pl-4 pr-12 grow`}>
|
||||
<ItemMenu
|
||||
media={media}
|
||||
relPath={relPath}
|
||||
@@ -327,50 +322,65 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
snippets={mediaSnippets}
|
||||
scripts={settings?.scripts}
|
||||
insertIntoArticle={insertIntoArticle}
|
||||
insertSnippet={insertSnippet}
|
||||
showUpdateMedia={updateMetadata}
|
||||
showMediaDetails={() => setSelectedItemAction({ path: media.fsPath, action: 'view' })}
|
||||
showUpdateMedia={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'edit'
|
||||
})}
|
||||
showMediaDetails={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'view'
|
||||
})}
|
||||
processSnippet={processSnippet}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
|
||||
<p className={`text-sm font-bold pointer-events-none flex items-center break-all text-[var(--vscode-foreground)]}`}>
|
||||
<p className={`text-sm font-bold pointer-events-none flex items-center break-all text-[var(--frontmatter-text)]`}>
|
||||
{basename(parseWinPath(media.fsPath) || '')}
|
||||
</p>
|
||||
{!isImage && media.metadata.title && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonTitle)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.title}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.title}</span>
|
||||
</p>
|
||||
)}
|
||||
{media.metadata.caption && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonCaption)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.caption}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.caption}</span>
|
||||
</p>
|
||||
)}
|
||||
{!media.metadata.caption && media.metadata.alt && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonAlt)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.alt}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.alt}</span>
|
||||
</p>
|
||||
)}
|
||||
{(media?.size || media?.dimensions) && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-1`}>
|
||||
<b className={`mr-1 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonSize)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>
|
||||
{mediaDetails}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FooterActions
|
||||
media={media}
|
||||
relPath={relPath}
|
||||
snippets={mediaSnippets}
|
||||
viewData={viewData?.data}
|
||||
scripts={settings?.scripts}
|
||||
insertIntoArticle={insertIntoArticle}
|
||||
insertSnippet={insertSnippet}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
</li>
|
||||
|
||||
{showSnippetSelection && (
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { ClipboardIcon, CodeBracketIcon, CommandLineIcon, EllipsisVerticalIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { CustomScript, MediaInfo, ScriptType, Snippet, ViewData } from '../../../models';
|
||||
import { ClipboardIcon, CodeBracketIcon, EllipsisHorizontalIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { CustomScript, MediaInfo, Snippet, ViewData } from '../../../models';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { copyToClipboard } from '../../utils';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IItemMenuProps {
|
||||
media: MediaInfo;
|
||||
@@ -17,7 +18,6 @@ export interface IItemMenuProps {
|
||||
snippets: Snippet[];
|
||||
scripts?: CustomScript[];
|
||||
insertIntoArticle: () => void;
|
||||
insertSnippet: () => void;
|
||||
showUpdateMedia: () => void;
|
||||
showMediaDetails: () => void;
|
||||
processSnippet: (snippet: Snippet) => void;
|
||||
@@ -32,26 +32,16 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
snippets,
|
||||
scripts,
|
||||
insertIntoArticle,
|
||||
insertSnippet,
|
||||
showUpdateMedia,
|
||||
showMediaDetails,
|
||||
processSnippet,
|
||||
onDelete,
|
||||
}: React.PropsWithChildren<IItemMenuProps>) => {
|
||||
|
||||
const copyToClipboard = React.useCallback(() => {
|
||||
if (relPath) {
|
||||
messageHandler.send(DashboardMessage.copyToClipboard, parseWinPath(relPath) || '');
|
||||
}
|
||||
const onCopyToClipboard = React.useCallback(() => {
|
||||
copyToClipboard(parseWinPath(relPath) || '');
|
||||
}, [relPath]);
|
||||
|
||||
const runCustomScript = React.useCallback((script: CustomScript) => {
|
||||
messageHandler.send(DashboardMessage.runCustomScript, {
|
||||
script,
|
||||
path: media.fsPath
|
||||
});
|
||||
}, [media]);
|
||||
|
||||
const revealMedia = React.useCallback(() => {
|
||||
messageHandler.send(DashboardMessage.revealMedia, {
|
||||
file: media.fsPath,
|
||||
@@ -59,81 +49,22 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
});
|
||||
}, [selectedFolder]);
|
||||
|
||||
const customScriptActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter((script) => script.type === ScriptType.MediaFile && !script.hidden)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.title}
|
||||
onClick={() => runCustomScript(script)}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
return (
|
||||
<div className={`group/actions absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-2 -mr-2 -mt-2 group-hover/actions:bg-[var(--vscode-sideBar-background)] group-hover/actions:border-[var(--frontmatter-border)]`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-1 -mr-2 -mt-1 group-hover/actions:bg-[var(--vscode-sideBar-background)] group-hover/actions:border-[var(--frontmatter-border)]`}>
|
||||
<div className="relative z-10 flex text-left">
|
||||
<div className="hidden group-hover/actions:flex">
|
||||
<QuickAction title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemView)} onClick={showMediaDetails}>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemView)}</span>
|
||||
</QuickAction>
|
||||
|
||||
<QuickAction title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)} onClick={showUpdateMedia}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}</span>
|
||||
</QuickAction>
|
||||
|
||||
{viewData?.filePath ? (
|
||||
<>
|
||||
<QuickAction
|
||||
title={
|
||||
viewData.metadataInsert && viewData.fieldName
|
||||
? l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertField, viewData.fieldName)
|
||||
: l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertMarkdown)
|
||||
}
|
||||
onClick={insertIntoArticle}
|
||||
>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
{viewData?.position && snippets.length > 0 && (
|
||||
<QuickAction title={l10n.t(LocalizationKey.commonInsertSnippet)} onClick={insertSnippet}>
|
||||
<CodeBracketIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{
|
||||
relPath && (
|
||||
<QuickAction title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)} onClick={copyToClipboard}>
|
||||
<ClipboardIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
className={`hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonMenu)}</span>
|
||||
<EllipsisVerticalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
<EllipsisHorizontalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={showMediaDetails}>
|
||||
<EyeIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonView)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={showUpdateMedia}>
|
||||
<PencilIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}</span>
|
||||
@@ -160,21 +91,19 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
|
||||
{customScriptActions}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuItem onClick={copyToClipboard}>
|
||||
<ClipboardIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{customScriptActions}
|
||||
</>
|
||||
<DropdownMenuItem onClick={onCopyToClipboard}>
|
||||
<ClipboardIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
<CustomActions
|
||||
filePath={media.fsPath}
|
||||
scripts={scripts} />
|
||||
|
||||
<DropdownMenuItem onClick={revealMedia}>
|
||||
<EyeIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemRevealMedia)}</span>
|
||||
|
||||
@@ -177,7 +177,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
)}
|
||||
|
||||
{isDragActive && (
|
||||
<div className={`absolute top-0 left-0 w-full h-full flex flex-col justify-center items-center z-50 text-[var(--vscode-foreground)] bg-[var(--vscode-editor-background)] opacity-75`}>
|
||||
<div className={`absolute top-0 left-0 w-full h-full flex flex-col justify-center items-center z-50 text-[var(--frontmatter-text)] bg-[var(--vscode-editor-background)] opacity-75`}>
|
||||
<ArrowUpTrayIcon className={`h-32`} />
|
||||
<p className={`text-xl max-w-md text-center`}>
|
||||
{selectedFolder
|
||||
|
||||
@@ -11,16 +11,24 @@ export interface IMediaItemPanelProps {
|
||||
}
|
||||
|
||||
export const MediaItemPanel: React.FunctionComponent<IMediaItemPanelProps> = ({ allMedia }: React.PropsWithChildren<IMediaItemPanelProps>) => {
|
||||
const [crntPath, setCrntPath] = useState<string>('');
|
||||
const [media, setMedia] = useState<MediaInfo | undefined>(undefined);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const [selectedItemAction, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
const { mediaFolder, mediaSize, mediaDimensions, isImage, isVideo } = useMediaInfo(media);
|
||||
|
||||
const onDismiss = () => {
|
||||
setShowDetails(false);
|
||||
setShowForm(false);
|
||||
setMedia(undefined);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItemAction && selectedItemAction.path) {
|
||||
const mediaFile = allMedia.find((m) => m.fsPath === selectedItemAction.path);
|
||||
setMedia(mediaFile);
|
||||
setCrntPath(selectedItemAction.path);
|
||||
|
||||
if (selectedItemAction.action === 'edit') {
|
||||
setShowForm(true);
|
||||
@@ -31,8 +39,11 @@ export const MediaItemPanel: React.FunctionComponent<IMediaItemPanelProps> = ({
|
||||
}
|
||||
|
||||
setSelectedItemAction(undefined);
|
||||
} else if (crntPath) {
|
||||
const mediaFile = allMedia.find((m) => m.fsPath === crntPath);
|
||||
setMedia(mediaFile);
|
||||
}
|
||||
}, [allMedia, selectedItemAction])
|
||||
}, [allMedia, selectedItemAction, crntPath]);
|
||||
|
||||
if (showDetails && media) {
|
||||
return (
|
||||
@@ -47,11 +58,7 @@ export const MediaItemPanel: React.FunctionComponent<IMediaItemPanelProps> = ({
|
||||
isVideoFile={isVideo}
|
||||
onEdit={() => setShowForm(true)}
|
||||
onEditClose={() => setShowForm(false)}
|
||||
onDismiss={() => {
|
||||
setShowDetails(false);
|
||||
setShowForm(false);
|
||||
setMedia(undefined);
|
||||
}}
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const QuickAction: React.FunctionComponent<IQuickActionProps> = ({
|
||||
type="button"
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
className={cn(`px-2 group inline-flex justify-center text-sm font-medium text-[var(--vscode-foreground)] hover:text-[var(--frontmatter-button-hoverBackground)]`, className)}
|
||||
className={cn(`px-2 group inline-flex justify-center text-sm font-medium text-[var(--frontmatter-text)] hover:text-[var(--frontmatter-button-hoverBackground)]`, className)}
|
||||
>
|
||||
{children}
|
||||
<span className="sr-only">{title}</span>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { NewForm } from './NewForm';
|
||||
import SnippetForm, { SnippetFormHandle } from './SnippetForm';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { openFile } from '../../utils';
|
||||
|
||||
export interface IItemProps {
|
||||
snippetKey: string;
|
||||
@@ -65,7 +66,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
};
|
||||
|
||||
const showFile = useCallback(() => {
|
||||
Messenger.send(DashboardMessage.openFile, snippet.sourcePath);
|
||||
openFile(snippet.sourcePath);
|
||||
}, [snippet]);
|
||||
|
||||
const onOpenEdit = useCallback(() => {
|
||||
@@ -163,7 +164,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<>
|
||||
<li className={`group relative overflow-hidden shadow-md hover:shadow-xl dark:shadow-none border p-4 space-y-2 rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] border-[var(--frontmatter-border)]`}>
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<CodeBracketIcon className={`w-64 h-64 opacity-5 text-[var(--vscode-foreground)]`} />
|
||||
<CodeBracketIcon className={`w-64 h-64 opacity-5 text-[var(--frontmatter-text)]`} />
|
||||
</div>
|
||||
|
||||
<h2
|
||||
@@ -243,7 +244,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
</div>
|
||||
</FeatureFlag>
|
||||
|
||||
<p className={`text-xs text-[var(--vscode-foreground)]`}>{snippet.description}</p>
|
||||
<p className={`text-xs text-[var(--frontmatter-text)]`}>{snippet.description}</p>
|
||||
</li>
|
||||
|
||||
{showInsertDialog && (
|
||||
|
||||
@@ -110,7 +110,7 @@ export const NewForm: React.FunctionComponent<INewFormProps> = ({
|
||||
>
|
||||
{l10n.t(LocalizationKey.dashboardSnippetsViewNewFormSnippetInputIsMediaSnippetCheckboxLabel)}
|
||||
</label>
|
||||
<p id="isMediaSnippet-description" className={`text-[var(--vscode-foreground)] text-left`}>
|
||||
<p id="isMediaSnippet-description" className={`text-[var(--frontmatter-text)] text-left`}>
|
||||
{l10n.t(LocalizationKey.dashboardSnippetsViewNewFormSnippetInputIsMediaSnippetCheckboxDescription)}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { SettingsAtom, ViewDataSelector } from '../../state';
|
||||
import { SnippetInputField } from './SnippetInputField';
|
||||
import { SNIPPET } from '../../../constants/Snippet';
|
||||
import { GeneralCommands } from '../../../constants';
|
||||
|
||||
export interface ISnippetFormProps {
|
||||
snippetKey?: string;
|
||||
@@ -144,22 +145,17 @@ ${snippetBody}
|
||||
const allFields: SnippetField[] = [];
|
||||
const snippetFields = snippet.fields || [];
|
||||
|
||||
// Loop over all fields to check if they are present in the snippet
|
||||
console.log('placeholders', placeholders);
|
||||
console.log('snippetFields', snippetFields);
|
||||
|
||||
for await (const field of snippetFields) {
|
||||
console.log('field', field);
|
||||
const idx = placeholders.findIndex((fieldName) => fieldName === field.name);
|
||||
if (idx > -1) {
|
||||
try {
|
||||
const value = await insertPlaceholderValues(field.default || '');
|
||||
console.log('value', value);
|
||||
allFields.push({
|
||||
...field,
|
||||
value
|
||||
});
|
||||
} catch (e) {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.error, `SnippetForm: ${(e as Error).message}`);
|
||||
console.log('Error', (e as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { ExtensionState } from '../../../constants';
|
||||
import { LinkButton } from '../Common/LinkButton';
|
||||
import { openFile } from '../../utils';
|
||||
|
||||
export interface ITaxonomyTaggingProps {
|
||||
taxonomy: string | null;
|
||||
@@ -125,7 +126,7 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
|
||||
}, [untaggedPages, pageMappings.tagged]);
|
||||
|
||||
const onFileView = (filePath: string) => {
|
||||
Messenger.send(DashboardMessage.openFile, filePath);
|
||||
openFile(filePath);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -15,8 +15,11 @@ import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { NavigationType } from '../models';
|
||||
import { GeneralCommands } from '../../constants';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '..';
|
||||
|
||||
export default function useMessages() {
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useRecoilState(LoadingAtom);
|
||||
const [pages, setPages] = useState<Page[]>([]);
|
||||
const [settings, setSettings] = useRecoilState(SettingsAtom);
|
||||
@@ -36,14 +39,19 @@ export default function useMessages() {
|
||||
setViewData(message.payload);
|
||||
if (message.payload?.type === NavigationType.Media) {
|
||||
setView(NavigationType.Media);
|
||||
navigate(routePaths[NavigationType.Media]);
|
||||
} else if (message.payload?.type === NavigationType.Contents) {
|
||||
setView(NavigationType.Contents);
|
||||
navigate(routePaths[NavigationType.Contents]);
|
||||
} else if (message.payload?.type === NavigationType.Data) {
|
||||
setView(NavigationType.Data);
|
||||
navigate(routePaths[NavigationType.Data]);
|
||||
} else if (message.payload?.type === NavigationType.Taxonomy) {
|
||||
setView(NavigationType.Taxonomy);
|
||||
navigate(routePaths[NavigationType.Taxonomy]);
|
||||
} else if (message.payload?.type === NavigationType.Snippets) {
|
||||
setView(NavigationType.Snippets);
|
||||
navigate(routePaths[NavigationType.Snippets]);
|
||||
}
|
||||
break;
|
||||
case DashboardCommand.settings:
|
||||
|
||||
@@ -52,7 +52,8 @@ export const routePaths: { [name: string]: string } = {
|
||||
};
|
||||
|
||||
const mutationObserver = new MutationObserver((_, __) => {
|
||||
updateCssVariables();
|
||||
const darkMode = document.body.classList.contains('vscode-dark');
|
||||
updateCssVariables(darkMode);
|
||||
});
|
||||
|
||||
const elm = document.querySelector('#app');
|
||||
@@ -67,7 +68,7 @@ if (elm) {
|
||||
const webviewUrl = elm?.getAttribute('data-webview-url');
|
||||
const isCrashDisabled = elm?.getAttribute('data-is-crash-disabled');
|
||||
|
||||
updateCssVariables();
|
||||
updateCssVariables(document.body.classList.contains('vscode-dark'));
|
||||
mutationObserver.observe(document.body, { childList: false, attributes: true });
|
||||
|
||||
if (isProd === 'true' && isCrashDisabled === 'false') {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { atom } from 'recoil';
|
||||
export const SelectedItemActionAtom = atom<
|
||||
| {
|
||||
path: string;
|
||||
action: 'view' | 'edit';
|
||||
action: 'view' | 'edit' | 'delete';
|
||||
}
|
||||
| undefined
|
||||
>({
|
||||
|
||||
47
src/dashboardWebView/utils/MessageHandlers.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import { GeneralCommands } from '../../constants';
|
||||
import { CustomScript } from '../../models';
|
||||
|
||||
export const openFile = (filePath?: string) => {
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(DashboardMessage.openFile, filePath);
|
||||
};
|
||||
|
||||
export const deletePage = (filePath?: string) => {
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(DashboardMessage.deleteFile, filePath);
|
||||
};
|
||||
|
||||
export const openOnWebsite = (websiteUrl?: string, filePath?: string) => {
|
||||
if (!websiteUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(GeneralCommands.toVSCode.openOnWebsite, {
|
||||
websiteUrl,
|
||||
filePath
|
||||
});
|
||||
};
|
||||
|
||||
export const copyToClipboard = (value: string) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(DashboardMessage.copyToClipboard, value);
|
||||
};
|
||||
|
||||
export const runCustomScript = (script: CustomScript, path: string) => {
|
||||
if (!script) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(DashboardMessage.runCustomScript, { script, path });
|
||||
};
|
||||
71
src/dashboardWebView/utils/darkenColor.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
export const darkenColor = (color: string | undefined, percentage: number) => {
|
||||
if (!color) {
|
||||
return color;
|
||||
}
|
||||
|
||||
// Remove any whitespace and convert to lowercase
|
||||
color = color.trim().toLowerCase();
|
||||
|
||||
// Check if the color is in hex format
|
||||
if (color.startsWith('#')) {
|
||||
// Remove the "#" symbol
|
||||
color = color.slice(1);
|
||||
|
||||
// Convert the color to rgb format
|
||||
const hexToRgb = (hex: string) => {
|
||||
const bigint = parseInt(hex, 16);
|
||||
const r = (bigint >> 16) & 255;
|
||||
const g = (bigint >> 8) & 255;
|
||||
const b = bigint & 255;
|
||||
return [r, g, b];
|
||||
};
|
||||
|
||||
const [r, g, b] = hexToRgb(color);
|
||||
|
||||
// Calculate the darkened color
|
||||
const darkenValue = Math.round(255 * (percentage / 100));
|
||||
const darkenedR = Math.max(r - darkenValue, 0);
|
||||
const darkenedG = Math.max(g - darkenValue, 0);
|
||||
const darkenedB = Math.max(b - darkenValue, 0);
|
||||
|
||||
// Convert the darkened color back to hex format
|
||||
const rgbToHex = (r: number, g: number, b: number) => {
|
||||
const toHex = (c: number) => {
|
||||
const hex = c.toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
};
|
||||
|
||||
return rgbToHex(darkenedR, darkenedG, darkenedB);
|
||||
}
|
||||
|
||||
// Check if the color is in rgb or rgba format
|
||||
if (color.startsWith('rgb')) {
|
||||
// Extract the RGB values
|
||||
const rgbValues = color.match(/\d+/g);
|
||||
|
||||
if (rgbValues) {
|
||||
const [r, g, b] = rgbValues.map(Number);
|
||||
|
||||
// Calculate the darkened color
|
||||
const darkenValue = Math.round(255 * (percentage / 100));
|
||||
const darkenedR = Math.max(r - darkenValue, 0);
|
||||
const darkenedG = Math.max(g - darkenValue, 0);
|
||||
const darkenedB = Math.max(b - darkenValue, 0);
|
||||
|
||||
// Check if the color is in rgba format
|
||||
if (color.startsWith('rgba')) {
|
||||
// Extract the alpha value
|
||||
const alphaMatch = color.match(/[\d\.]+(?=\))/);
|
||||
const alpha = alphaMatch ? Number(alphaMatch[0]) : 1;
|
||||
|
||||
return `rgba(${darkenedR}, ${darkenedG}, ${darkenedB}, ${alpha})`;
|
||||
}
|
||||
|
||||
return `rgb(${darkenedR}, ${darkenedG}, ${darkenedB})`;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from './MessageHandlers';
|
||||
export * from './darkenColor';
|
||||
export * from './getRelPath';
|
||||
export * from './opacityColor';
|
||||
export * from './preserveColor';
|
||||
export * from './updateCssVariables';
|
||||
|
||||
19
src/dashboardWebView/utils/opacityColor.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const opacityColor = (color: string | undefined, opacity: number) => {
|
||||
if (color) {
|
||||
if (color.startsWith('#')) {
|
||||
return `${color}${Math.round(opacity * 255)
|
||||
.toString(16)
|
||||
.padStart(2, '0')}`;
|
||||
} else if (color.startsWith('rgba')) {
|
||||
const splits = color.split(',');
|
||||
splits.pop();
|
||||
return `${splits.join(', ')}, ${opacity})`;
|
||||
} else if (color.startsWith('rgb')) {
|
||||
return `${color.replace('rgb', 'rgba').replace(')', `, ${opacity})`)}`;
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { preserveColor } from './preserveColor';
|
||||
import { darkenColor, opacityColor, preserveColor } from '.';
|
||||
|
||||
export const updateCssVariables = () => {
|
||||
export const updateCssVariables = (isDarkTheme: boolean = true) => {
|
||||
const styles = getComputedStyle(document.documentElement);
|
||||
|
||||
// Lightbox
|
||||
@@ -51,6 +51,17 @@ export const updateCssVariables = () => {
|
||||
'var(--vscode-list-activeSelectionForeground)'
|
||||
);
|
||||
|
||||
// Navigation
|
||||
const tabActiveForeground = styles.getPropertyValue('--vscode-tab-activeForeground');
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-nav-active',
|
||||
preserveColor(tabActiveForeground) || 'var(--vscode-tab-activeForeground)'
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-nav-inactive',
|
||||
opacityColor(tabActiveForeground, 0.6) || 'var(--vscode-tab-inactiveForeground)'
|
||||
);
|
||||
|
||||
// Borders
|
||||
const borderColor = styles.getPropertyValue('--vscode-panel-border');
|
||||
document.documentElement.style.setProperty('--frontmatter-border', 'var(--vscode-panel-border)');
|
||||
@@ -75,4 +86,16 @@ export const updateCssVariables = () => {
|
||||
preserveColor(buttonHoverBackground) || 'var(--vscode-button-hoverBackground)'
|
||||
);
|
||||
}
|
||||
|
||||
// Darken the background of a color
|
||||
const sideBarBg = styles.getPropertyValue('--vscode-sideBar-background');
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-sideBar-background',
|
||||
darkenColor(sideBarBg, 2) || 'var(--vscode-sideBar-background)'
|
||||
);
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
'--frontmatter-border-active',
|
||||
darkenColor(borderColor, isDarkTheme ? -30 : 30) || 'var(--vscode-activityBar-activeBorder)'
|
||||
);
|
||||
};
|
||||
|
||||
@@ -140,15 +140,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const remap = vscode.commands.registerCommand(COMMAND_NAME.remap, Settings.remap);
|
||||
|
||||
const setLastModifiedDate = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.setLastModifiedDate,
|
||||
Article.setLastModifiedDate
|
||||
);
|
||||
|
||||
const generateSlug = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.generateSlug,
|
||||
Article.updateSlug
|
||||
);
|
||||
// Register all the article commands
|
||||
Article.registerCommands(subscriptions);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.initTemplate, () =>
|
||||
@@ -291,16 +284,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand(COMMAND_NAME.chatbot, () => Chatbot.open(extensionPath))
|
||||
);
|
||||
|
||||
// Inserting an image in Markdown
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.insertMedia, Article.insertMedia)
|
||||
);
|
||||
|
||||
// Inserting a snippet in Markdown
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.insertSnippet, Article.insertSnippet)
|
||||
);
|
||||
|
||||
// Create the editor experience for bulk scripts
|
||||
subscriptions.push(
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
@@ -340,8 +323,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
createCategory,
|
||||
exportTaxonomy,
|
||||
remap,
|
||||
setLastModifiedDate,
|
||||
generateSlug,
|
||||
createFromTemplate,
|
||||
createTemplate,
|
||||
registerFolder,
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
import { format, parse } from 'date-fns';
|
||||
import { Notifications } from './Notifications';
|
||||
import { Article } from '../commands';
|
||||
import { join, parse as parseFile } from 'path';
|
||||
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';
|
||||
@@ -183,6 +183,52 @@ export class ArticleHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file.
|
||||
* @param filePath - The path of the file to be renamed.
|
||||
*/
|
||||
public static async rename(filePath: string) {
|
||||
filePath = parseWinPath(filePath);
|
||||
const fileUri = Uri.file(filePath);
|
||||
const file = workspace.openTextDocument(fileUri);
|
||||
if (!file) {
|
||||
Notifications.error(l10n.t(LocalizationKey.commandsArticleRenameFileNotExistsError));
|
||||
return;
|
||||
}
|
||||
|
||||
const folderPath = dirname(fileUri.fsPath);
|
||||
const fileName = parseFile(filePath).base;
|
||||
const fileNameWithoutExt = parseFile(filePath).name;
|
||||
const fileExtension = parseFile(filePath).ext;
|
||||
const newFileName = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.commandsArticleRenameFileNameTitle, fileName),
|
||||
prompt: l10n.t(LocalizationKey.commandsArticleRenameFileNamePrompt),
|
||||
value: fileNameWithoutExt,
|
||||
ignoreFocusOut: true,
|
||||
validateInput: async (value) => {
|
||||
try {
|
||||
const newFileUri = Uri.joinPath(Uri.file(folderPath), `${value}${fileExtension}`);
|
||||
const exists = await workspace.fs.readFile(newFileUri);
|
||||
if (exists && value !== fileNameWithoutExt) {
|
||||
return l10n.t(LocalizationKey.commandsArticleRenameFileExistsError, value);
|
||||
}
|
||||
} catch (e) {
|
||||
// File does not exist
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
if (!newFileName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newFileUri = Uri.joinPath(Uri.file(folderPath), `${newFileName}${fileExtension}`);
|
||||
await workspace.fs.rename(fileUri, newFileUri, {
|
||||
overwrite: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the update to be applied to the article.
|
||||
* @param article
|
||||
@@ -694,15 +740,17 @@ export class ArticleHelper {
|
||||
* Get the details of the current article
|
||||
* @returns
|
||||
*/
|
||||
public static async getDetails(filePath: string) {
|
||||
public static async getDetails(
|
||||
filePath: string
|
||||
): Promise<Article | 'nofilepath' | 'nodata' | 'notsupported'> {
|
||||
const baseUrl = Settings.get<string>(SETTING_SITE_BASEURL);
|
||||
if (!filePath) {
|
||||
return null;
|
||||
return 'nofilepath';
|
||||
}
|
||||
|
||||
const document = await workspace.openTextDocument(filePath);
|
||||
if (!ArticleHelper.isSupportedFile(document)) {
|
||||
return null;
|
||||
return 'notsupported';
|
||||
}
|
||||
|
||||
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
@@ -758,7 +806,7 @@ export class ArticleHelper {
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
return 'nodata';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Settings } from './SettingsHelper';
|
||||
import { CommandType, EnvironmentType } from './../models/PanelSettings';
|
||||
import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings';
|
||||
import { window, env as vscodeEnv, ProgressLocation } from 'vscode';
|
||||
import { window, env as vscodeEnv, ProgressLocation, Uri, commands } from 'vscode';
|
||||
import { ArticleHelper, Logger, Telemetry } from '.';
|
||||
import { Folders, WORKSPACE_PLACEHOLDER } from '../commands/Folders';
|
||||
import { exec, execSync } from 'child_process';
|
||||
@@ -301,6 +301,11 @@ export class CustomScript {
|
||||
l10n.t(LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess, script.title)
|
||||
);
|
||||
}
|
||||
} else if (data.fmAction) {
|
||||
if (data.fmAction === 'open' && data.fmPath) {
|
||||
const uri = data.fmPath.startsWith(`http`) ? data.fmPath : Uri.file(data.fmPath);
|
||||
commands.executeCommand('vscode.open', uri);
|
||||
}
|
||||
} else {
|
||||
Logger.error(`No frontmatter found.`);
|
||||
throw new Error(`No frontmatter found.`);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
DiagnosticCollection,
|
||||
languages
|
||||
} from 'vscode';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { Folders, WORKSPACE_PLACEHOLDER } from '../commands/Folders';
|
||||
import { Template } from '../commands/Template';
|
||||
import {
|
||||
EXTENSION_NAME,
|
||||
@@ -30,6 +30,7 @@ import { TaxonomyHelper } from './TaxonomyHelper';
|
||||
import { Cache } from '../commands/Cache';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { parseWinPath } from './parseWinPath';
|
||||
|
||||
export class Extension {
|
||||
private static instance: Extension;
|
||||
@@ -243,6 +244,31 @@ export class Extension {
|
||||
}
|
||||
}
|
||||
|
||||
if (major === 10 && minor === 0 && patch < 2) {
|
||||
let folders = Settings.get<ContentFolder[]>(SETTING_CONTENT_PAGE_FOLDERS) || [];
|
||||
folders = folders
|
||||
.filter((f) => !f.locale || f.locale === f.defaultLocale)
|
||||
.map((f) => {
|
||||
delete f.locale;
|
||||
delete f.localeTitle;
|
||||
|
||||
if (f.localeSourcePath) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const localPath = parseWinPath(f.localeSourcePath).replace(
|
||||
parseWinPath(wsFolder.fsPath),
|
||||
WORKSPACE_PLACEHOLDER
|
||||
);
|
||||
f.path = localPath;
|
||||
}
|
||||
delete f.localeSourcePath;
|
||||
}
|
||||
|
||||
return f;
|
||||
});
|
||||
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folders, true);
|
||||
}
|
||||
|
||||
// The tags and categories settings need to be moved to the database
|
||||
const tags = Settings.get<string[]>(SETTING_TAXONOMY_TAGS) || [];
|
||||
const categories = Settings.get<string[]>(SETTING_TAXONOMY_CATEGORIES) || [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function useDarkMode() {
|
||||
const setTheme = (elm: HTMLElement) => {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
|
||||
import { Logger } from '../../helpers';
|
||||
import { PostMessageData } from '../../models';
|
||||
import { BaseListener } from './BaseListener';
|
||||
|
||||
export class LogListener extends BaseListener {
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: PostMessageData) {
|
||||
super.process(msg);
|
||||
|
||||
switch (msg.command) {
|
||||
case DashboardMessage.logError:
|
||||
Logger.error(msg.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,9 @@ export class PagesListener extends BaseListener {
|
||||
case DashboardMessage.deleteFile:
|
||||
this.deletePage(msg.payload);
|
||||
break;
|
||||
case DashboardMessage.rename:
|
||||
ArticleHelper.rename(msg.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,5 @@ export * from './SettingsListener';
|
||||
export * from './SnippetListener';
|
||||
export * from './TelemetryListener';
|
||||
export * from './TaxonomyListener';
|
||||
export * from './LogListener';
|
||||
export * from './LocalizationListener';
|
||||
export * from './SsgListener';
|
||||
|
||||
@@ -25,6 +25,15 @@ export abstract class BaseListener {
|
||||
commands.executeCommand(command, args);
|
||||
}
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.info:
|
||||
Logger.info(msg.payload);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.warn:
|
||||
Logger.warning(msg.payload);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.error:
|
||||
Logger.error(msg.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
SETTING_TAXONOMY_CONTENT_TYPES
|
||||
} from '../../constants';
|
||||
import { Article, Preview } from '../../commands';
|
||||
import { ParsedFrontMatter } from '../../parsers';
|
||||
import { FrontMatterParser, ParsedFrontMatter } from '../../parsers';
|
||||
import { Field, Mode, PostMessageData, ContentType as IContentType } from '../../models';
|
||||
import { encodeEmoji, fieldWhenClause } from '../../utils';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
@@ -198,9 +198,37 @@ export class DataListener extends BaseListener {
|
||||
try {
|
||||
if (filePath) {
|
||||
articleDetails = await ArticleHelper.getDetails(filePath);
|
||||
|
||||
if (!articleDetails || articleDetails === 'nodata') {
|
||||
try {
|
||||
const contents = await ArticleHelper.getContents(filePath);
|
||||
if (contents) {
|
||||
FrontMatterParser.fromFile(contents);
|
||||
}
|
||||
} catch (e) {
|
||||
this.sendMsg(Command.metadata, {
|
||||
fmError: l10n.t(
|
||||
LocalizationKey.listenersPanelDataListenerPushMetadataFrontMatterError
|
||||
),
|
||||
fmErrorMessage: (e as Error).message
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (articleDetails === 'notsupported' || articleDetails === 'nofilepath') {
|
||||
// No file or invalid file format
|
||||
this.sendMsg(Command.metadata, undefined);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.sendMsg(Command.metadata, undefined);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error(`DataListener::pushMetadata: ${(e as Error).message}`);
|
||||
if (filePath) {
|
||||
Logger.error(`DataListener::pushMetadata: ${(e as Error).message}`);
|
||||
}
|
||||
this.sendMsg(Command.metadata, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (articleDetails) {
|
||||
|
||||
@@ -151,6 +151,10 @@ export enum LocalizationKey {
|
||||
* Open: {0}
|
||||
*/
|
||||
commonOpenWithValue = 'common.openWithValue',
|
||||
/**
|
||||
* Open custom actions
|
||||
*/
|
||||
commonOpenCustomActions = 'common.openCustomActions',
|
||||
/**
|
||||
* View
|
||||
*/
|
||||
@@ -167,6 +171,10 @@ export enum LocalizationKey {
|
||||
* Scripts
|
||||
*/
|
||||
commonScripts = 'common.scripts',
|
||||
/**
|
||||
* Rename
|
||||
*/
|
||||
commonRename = 'common.rename',
|
||||
/**
|
||||
* Loading content
|
||||
*/
|
||||
@@ -268,7 +276,7 @@ export enum LocalizationKey {
|
||||
*/
|
||||
settingsIntegrationsViewDeeplIntputLabel = 'settings.integrationsView.deepl.intput.label',
|
||||
/**
|
||||
* Enter your Azure Translator API key
|
||||
* Enter your Deepl API key
|
||||
*/
|
||||
settingsIntegrationsViewDeeplIntputPlaceholder = 'settings.integrationsView.deepl.intput.placeholder',
|
||||
/**
|
||||
@@ -672,7 +680,7 @@ export enum LocalizationKey {
|
||||
*/
|
||||
dashboardHeaderTabsSnippets = 'dashboard.header.tabs.snippets',
|
||||
/**
|
||||
* data
|
||||
* Data
|
||||
*/
|
||||
dashboardHeaderTabsData = 'dashboard.header.tabs.data',
|
||||
/**
|
||||
@@ -1500,6 +1508,10 @@ export enum LocalizationKey {
|
||||
* Metadata
|
||||
*/
|
||||
panelMetadataTitle = 'panel.metadata.title',
|
||||
/**
|
||||
* Check the problems view for more information
|
||||
*/
|
||||
panelMetadataFocusProblems = 'panel.metadata.focusProblems',
|
||||
/**
|
||||
* Other actions
|
||||
*/
|
||||
@@ -1680,6 +1692,22 @@ export enum LocalizationKey {
|
||||
* Failed to rename file: {0}
|
||||
*/
|
||||
commandsArticleUpdateSlugError = 'commands.article.updateSlug.error',
|
||||
/**
|
||||
* The file did not exist
|
||||
*/
|
||||
commandsArticleRenameFileNotExistsError = 'commands.article.rename.fileNotExists.error',
|
||||
/**
|
||||
* A file with the name "{0}" already exists
|
||||
*/
|
||||
commandsArticleRenameFileExistsError = 'commands.article.rename.fileExists.error',
|
||||
/**
|
||||
* Rename: {0}
|
||||
*/
|
||||
commandsArticleRenameFileNameTitle = 'commands.article.rename.fileName.title',
|
||||
/**
|
||||
* File name
|
||||
*/
|
||||
commandsArticleRenameFileNamePrompt = 'commands.article.rename.fileName.prompt',
|
||||
/**
|
||||
* Cache cleared
|
||||
*/
|
||||
@@ -2516,6 +2544,10 @@ export enum LocalizationKey {
|
||||
* Couldn't find data file entries
|
||||
*/
|
||||
listenersPanelDataListenerGetDataFileEntriesNoDataFilesError = 'listeners.panel.dataListener.getDataFileEntries.noDataFiles.error',
|
||||
/**
|
||||
* Something went wrong while parsing your front matter. Please check the contents of your file.
|
||||
*/
|
||||
listenersPanelDataListenerPushMetadataFrontMatterError = 'listeners.panel.dataListener.pushMetadata.frontMatter.error',
|
||||
/**
|
||||
* No active editor
|
||||
*/
|
||||
|
||||
@@ -17,5 +17,5 @@ export interface ContentFolder {
|
||||
localeTitle?: string;
|
||||
localeSourcePath?: string;
|
||||
defaultLocale?: string;
|
||||
locales: I18nConfig[];
|
||||
locales?: I18nConfig[];
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
if (!metadata || Object.keys(metadata || {}).length === 0) {
|
||||
if (!metadata) {
|
||||
return <BaseView mode={mode} settings={settings} folderAndFiles={folderAndFiles} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import * as l10n from "@vscode/l10n"
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { InitializeAction } from './InitializeAction';
|
||||
import { Actions } from './Actions';
|
||||
import { CustomView } from './CustomView';
|
||||
|
||||
export interface IBaseViewProps {
|
||||
settings: PanelSettings | undefined;
|
||||
@@ -56,6 +57,8 @@ const BaseView: React.FunctionComponent<IBaseViewProps> = ({
|
||||
<>
|
||||
<GitAction settings={settings} />
|
||||
|
||||
<CustomView />
|
||||
|
||||
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.globalSettings}>
|
||||
<GlobalSettings settings={settings} isBase />
|
||||
</FeatureFlag>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CustomPanelViewResult } from '../../../models';
|
||||
import { Collapsible } from '../Collapsible';
|
||||
|
||||
export interface ICustomViewProps {
|
||||
metadata: any;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export const CustomView: React.FunctionComponent<ICustomViewProps> = ({ metadata }: React.PropsWithChildren<ICustomViewProps>) => {
|
||||
|
||||
@@ -166,7 +166,7 @@ export const DataFileField: React.FunctionComponent<IDataFileFieldProps> = ({
|
||||
</button>
|
||||
|
||||
<ul
|
||||
className={`metadata_field__choice_list ${isOpen ? 'open' : 'closed'}`}
|
||||
className={`field_dropdown metadata_field__choice_list ${isOpen ? 'open' : 'closed'}`}
|
||||
style={{
|
||||
bottom: getDropdownStyle(isOpen)
|
||||
}}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import { Tags } from './Tags';
|
||||
import { usePrevious } from '../hooks/usePrevious';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { TagType } from '../TagType';
|
||||
import { Tags } from '../Tags';
|
||||
import { usePrevious } from '../../hooks/usePrevious';
|
||||
import { CommandToCode } from '../../CommandToCode';
|
||||
import { TagType } from '../../TagType';
|
||||
import Downshift from 'downshift';
|
||||
import { AddIcon } from './Icons/AddIcon';
|
||||
import { BlockFieldData, CustomTaxonomyData } from '../../models';
|
||||
import { AddIcon } from '../Icons/AddIcon';
|
||||
import { BlockFieldData, CustomTaxonomyData } from '../../../models';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { messageHandler, Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { FieldMessage } from './Fields/FieldMessage';
|
||||
import { FieldTitle } from './Fields/FieldTitle';
|
||||
import { FieldMessage } from '../Fields/FieldMessage';
|
||||
import { FieldTitle } from '../Fields/FieldTitle';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { PanelSettingsAtom } from '../state';
|
||||
import { PanelSettingsAtom } from '../../state';
|
||||
import { SparklesIcon } from '@heroicons/react/24/outline';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import useDropdownStyle from '../hooks/useDropdownStyle';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useDropdownStyle from '../../hooks/useDropdownStyle';
|
||||
|
||||
export interface ITagPickerProps {
|
||||
type: TagType;
|
||||
@@ -11,7 +11,6 @@ import { ListUnorderedIcon } from '../Icons/ListUnorderedIcon';
|
||||
import { TagIcon } from '../Icons/TagIcon';
|
||||
import { JsonField } from '../JsonField';
|
||||
import { IMetadata } from '../Metadata';
|
||||
import { TagPicker } from '../TagPicker';
|
||||
import {
|
||||
ChoiceField,
|
||||
DataFileField,
|
||||
@@ -27,7 +26,8 @@ import {
|
||||
PreviewImageValue,
|
||||
NumberField,
|
||||
CustomField,
|
||||
FieldCollection
|
||||
FieldCollection,
|
||||
TagPicker
|
||||
} from '.';
|
||||
import { fieldWhenClause } from '../../../utils/fieldWhenClause';
|
||||
import { ContentTypeRelationshipField } from './ContentTypeRelationshipField';
|
||||
|
||||
@@ -16,6 +16,7 @@ export * from './PreviewImage';
|
||||
export * from './PreviewImageField';
|
||||
export * from './RequiredAsterix';
|
||||
export * from './SlugField';
|
||||
export * from './TagPicker';
|
||||
export * from './TextField';
|
||||
export * from './Toggle';
|
||||
export * from './WrapperField';
|
||||
|
||||
15
src/panelWebView/components/Icons/PageIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IPageIconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const PageIcon: React.FunctionComponent<IPageIconProps> = ({
|
||||
className
|
||||
}: React.PropsWithChildren<IPageIconProps>) => {
|
||||
return (
|
||||
<svg className={className || ''} fill="currentColor" aria-hidden="true" width="1em" height="1em" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 7H6v2h8V7Zm-2 5h2v1h-2v-1ZM6 3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3H6ZM5 7a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7Zm7 4h2a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1Zm-7 .5c0-.28.22-.5.5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5Zm.5 1.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1 0-1Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
20
src/panelWebView/components/Icons/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export * from './AddIcon';
|
||||
export * from './ArchiveIcon';
|
||||
export * from './BranchIcon';
|
||||
export * from './BugIcon';
|
||||
export * from './CenterIcon';
|
||||
export * from './FileIcon';
|
||||
export * from './FolderOpenedIcon';
|
||||
export * from './FrontMatterIcon';
|
||||
export * from './GitHubIcon';
|
||||
export * from './HeartIcon';
|
||||
export * from './ListUnorderedIcon';
|
||||
export * from './MarkdownIcon';
|
||||
export * from './PageIcon';
|
||||
export * from './RocketIcon';
|
||||
export * from './SettingsIcon';
|
||||
export * from './SymbolKeywordIcon';
|
||||
export * from './TagIcon';
|
||||
export * from './TemplateIcon';
|
||||
export * from './ToggleIcon';
|
||||
export * from './WritingIcon';
|
||||
@@ -8,7 +8,7 @@ import useContentType from '../../hooks/useContentType';
|
||||
import { WrapperField } from './Fields/WrapperField';
|
||||
import { ContentTypeValidator } from './ContentType/ContentTypeValidator';
|
||||
import { FeatureFlag } from '../../components/features/FeatureFlag';
|
||||
import { FEATURE_FLAG } from '../../constants';
|
||||
import { FEATURE_FLAG, GeneralCommands } from '../../constants';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
@@ -33,6 +33,12 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
|
||||
}: React.PropsWithChildren<IMetadataProps>) => {
|
||||
const contentType = useContentType(settings, metadata);
|
||||
|
||||
const focusProblems = () => {
|
||||
Messenger.send(GeneralCommands.toVSCode.runCommand, {
|
||||
command: `workbench.panel.markers.view.focus`
|
||||
});
|
||||
}
|
||||
|
||||
const onSendUpdate = React.useCallback((field: string | undefined, value: any, parents: string[]) => {
|
||||
if (!field) {
|
||||
return;
|
||||
@@ -93,7 +99,20 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
|
||||
<ContentTypeValidator fields={contentType?.fields || []} metadata={metadata} />
|
||||
</FeatureFlag>
|
||||
|
||||
{allFields}
|
||||
{
|
||||
metadata.fmError && metadata.fmErrorMessage ? (
|
||||
<div className={`space-y-4`}>
|
||||
<p className={`text-[var(--vscode-errorForeground)]`}>{metadata.fmError}</p>
|
||||
|
||||
<button
|
||||
title={l10n.t(LocalizationKey.panelMetadataFocusProblems)}
|
||||
onClick={focusProblems}
|
||||
type={`button`}>
|
||||
{l10n.t(LocalizationKey.panelMetadataFocusProblems)}
|
||||
</button>
|
||||
</div>
|
||||
) : allFields
|
||||
}
|
||||
</Collapsible>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import FieldBoundary from './ErrorBoundary/FieldBoundary';
|
||||
import { SymbolKeywordIcon } from './Icons/SymbolKeywordIcon';
|
||||
import { SeoFieldInfo } from './SeoFieldInfo';
|
||||
import { SeoKeywords } from './SeoKeywords';
|
||||
import { TagPicker } from './TagPicker';
|
||||
import { TagPicker } from './Fields/TagPicker';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
|
||||