Compare commits

...

231 Commits

Author SHA1 Message Date
Elio Struyf
feff69d969 Merge pull request #217 from estruyf/dev 2022-01-01 20:21:23 +01:00
Elio Struyf
a34d77242a Updated entry 2022-01-01 20:20:57 +01:00
Elio Struyf
1b24c1277d Merge pull request #216 from estruyf/dev 2022-01-01 20:15:04 +01:00
Elio Struyf
e6750205be 🎇 New Year release 🎆 2022-01-01 20:14:15 +01:00
Elio Struyf
1da8bf3f8b To lowercase links 2021-12-29 19:14:10 +01:00
Elio Struyf
90c60b6a40 Merge branch 'LuiseFreese-main' into dev 2021-12-29 19:12:35 +01:00
Luise Freese
ee79f89c7f fixes casing issues that led to links that only referred to settings site but not to the specific heading 2021-12-29 11:17:11 +01:00
Elio Struyf
0668d48fd5 updated changelog 2021-12-28 21:20:16 +01:00
Elio Struyf
d046f73d16 #214 - Open file after creation fix 2021-12-28 20:52:42 +01:00
Elio Struyf
f144d713d1 added edit quick action 2021-12-28 10:45:33 +01:00
Elio Struyf
d31c403bdc Update icon 2021-12-28 10:43:36 +01:00
Elio Struyf
35a0327387 Added quick actions + some styling fixes 2021-12-28 10:35:46 +01:00
Elio Struyf
9b39649bde Update description 2021-12-27 16:29:37 +01:00
Elio Struyf
3d857463f0 Updated extension description 2021-12-27 16:25:55 +01:00
Elio Struyf
0428642a2f Update changelog 2021-12-27 16:22:26 +01:00
Elio Struyf
5182a9ae1a Fix spinner overlapping the global navigation 2021-12-27 16:22:01 +01:00
Elio Struyf
ab3686b3b5 #199 - Search media files 2021-12-27 15:56:12 +01:00
Elio Struyf
c5b7b7845d #212 - Added deleted folder watcher 2021-12-26 20:29:44 +01:00
Elio Struyf
2f13c335ed #212 - Added folder watchers 2021-12-26 20:24:23 +01:00
Elio Struyf
f219ac721f Updated navigation header 2021-12-26 19:13:42 +01:00
Elio Struyf
0149885289 Item borders 2021-12-26 18:56:24 +01:00
Elio Struyf
cb80a10de2 #213 - Media folder design 2021-12-26 17:19:31 +01:00
Elio Struyf
092eb0fd2a #211 - Replace text selection on media insert 2021-12-26 12:11:38 +01:00
Elio Struyf
24f79d9d3f #210 - Media file extension fix 2021-12-26 11:52:45 +01:00
Elio Struyf
d667b19716 5.9.0 2021-12-26 11:40:28 +01:00
Elio Struyf
4d38a0881a Merge pull request #209 from estruyf/dev 2021-12-21 15:45:06 +01:00
Elio Struyf
1478d0bd53 🎄 release 🎄 2021-12-21 15:39:43 +01:00
Elio Struyf
00a9e59bc2 #208 - Fix collapse section issue 2021-12-21 15:25:27 +01:00
Elio Struyf
2b2843234e Updated changelog 2021-12-21 11:13:46 +01:00
Elio Struyf
71449aa5cc Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2021-12-21 11:13:34 +01:00
Elio Struyf
87f293a3f6 added charset 2021-12-21 11:13:30 +01:00
Elio Struyf
6da3ddb5dd #206 - Removal of the front matter parsing error notification 2021-12-20 20:38:56 +01:00
Elio Struyf
87e80ccfe9 Update readme 2021-12-18 21:15:57 +01:00
Elio Struyf
af9865d91b Refresh the pages on content creation 2021-12-17 19:21:59 +01:00
Elio Struyf
e576b6e8a4 #207 - fix quick picks 2021-12-17 19:21:41 +01:00
Elio Struyf
c9f4c8f94e #206 - Added front matter diagnostic parsing 2021-12-17 19:04:41 +01:00
Elio Struyf
6ef63da973 Add sentry ignore rule 2021-12-16 14:02:57 +01:00
Elio Struyf
2d85b0a554 Updates for Webpack 5 support + HMR during dev 2021-12-16 13:31:30 +01:00
Elio Struyf
41cbcc4c46 Retain context on dashboard when hidden 2021-12-15 15:37:27 +01:00
Elio Struyf
6c94880497 Make output bundles smaller by smarter importing icons 2021-12-15 14:47:49 +01:00
Elio Struyf
1d75c6b6b8 Updated vs code engine 2021-12-15 14:22:47 +01:00
Elio Struyf
338bc022f9 Updated node version 2021-12-15 14:18:56 +01:00
Elio Struyf
2451730311 Updated workflow 2021-12-15 14:16:07 +01:00
Elio Struyf
22cdaabe28 Update changelog 2021-12-15 14:12:26 +01:00
Elio Struyf
5a69240178 #205 - Define a logging level setting 2021-12-15 14:10:58 +01:00
Elio Struyf
b81ef077f6 Merge branch 'dev-refactor' into dev 2021-12-15 13:51:01 +01:00
Elio Struyf
cb72b53653 Refactoring to message listeners 2021-12-15 13:50:40 +01:00
Elio Struyf
050a513b48 Refactoring 2021-12-15 11:16:39 +01:00
Elio Struyf
481d1f56e4 5.8.0 2021-12-15 08:29:41 +01:00
Elio Struyf
229238703a Updated vscode types 2021-12-15 08:29:35 +01:00
Elio Struyf
074de212ce Merge pull request #203 from estruyf/dev 2021-12-07 10:11:07 +01:00
Elio Struyf
98e4490318 updated changelog 2021-12-07 10:06:45 +01:00
Elio Struyf
c79dc3c41f Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2021-12-06 17:32:08 +01:00
Elio Struyf
b45abe88ac Updated changelog 2021-12-06 17:32:04 +01:00
Elio Struyf
2bac596e3d #200 - media file sorting fix 2021-12-05 13:11:22 +01:00
Elio Struyf
15a6ea8d8d #194 - Optimizations for the markup options 2021-12-04 13:55:52 +01:00
Elio Struyf
72d6f8263a Updated changelog 2021-12-04 09:18:21 +01:00
Elio Struyf
7549b3d989 #202 - Fix checkbox label color for light themes 2021-12-04 09:18:04 +01:00
Elio Struyf
ca628b37de #201 - Fix filename overflow 2021-12-03 19:15:20 +01:00
Elio Struyf
db120b5c45 #194 - ordered/unordered/task lists added 2021-12-03 19:06:27 +01:00
Elio Struyf
2973f5d27b #194 - Added heading and blockquote support 2021-12-03 14:11:18 +01:00
Elio Struyf
075c7ad350 Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2021-11-30 15:09:05 +01:00
Elio Struyf
23625ed8fa Merge branch 'issue/194' into dev 2021-11-30 15:06:05 +01:00
Elio Struyf
9bd06909c8 #194 - WYSIWYG controls implementation 2021-11-30 15:05:28 +01:00
Elio Struyf
1b45c2af13 Update changelog 2021-11-26 08:42:08 +01:00
Elio Struyf
b4bbf6c6fb #191 - Fix beta settings 2021-11-26 08:41:43 +01:00
Elio Struyf
a5b8d9668d Merge branch 'main' into dev 2021-11-25 15:23:43 +01:00
Elio Struyf
490210da00 Updated changelog 2021-11-25 15:22:56 +01:00
Elio Struyf
6878d440b1 Merge branch 'main' into dev 2021-11-25 15:21:06 +01:00
Elio Struyf
81fa0a7d0f #188 - support for markdown file extension added 2021-11-25 15:20:19 +01:00
Elio Struyf
b7d4e547a1 Total nr of files in workspace 2021-11-25 12:59:49 +01:00
Elio Struyf
2b2e256b8f #190 - More diagnostic values 2021-11-25 12:12:32 +01:00
Elio Struyf
7a7b31c2ae #190 - First implementation of the diagnostic output 2021-11-25 11:00:47 +01:00
Elio Struyf
dea5eb1053 5.7.0 2021-11-25 10:46:35 +01:00
Elio Struyf
df3144ea73 Remove unused reference 2021-11-25 10:46:30 +01:00
Elio Struyf
a170cc3ad9 Updated sponsors 2021-11-24 08:36:56 +01:00
Elio Struyf
35b3813022 Merge pull request #184 from estruyf/dev 2021-11-23 20:02:10 +01:00
Elio Struyf
f0472fe89b Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2021-11-23 20:00:04 +01:00
Elio Struyf
7bdb2fa025 Updated changelog for new 5.6.0 release 2021-11-23 19:59:11 +01:00
Elio Struyf
5ba7ad449a Updated changelog 2021-11-23 19:56:13 +01:00
Elio Struyf
145441d55e Updated package sorting 2021-11-22 17:12:29 +01:00
Elio Struyf
62ff3419c1 #181 - Support for custom taxonomy fields added 2021-11-22 16:43:30 +01:00
Elio Struyf
34aee134d6 #183 - Updated changelog 2021-11-22 08:09:20 +01:00
Elio Struyf
5899120d87 Updated sort setting 2021-11-21 18:58:39 +01:00
Elio Struyf
816a2fefe7 #182 - Support default sort option 2021-11-21 17:56:49 +01:00
Elio Struyf
1f64e59917 Fix sorting object 2021-11-21 17:28:02 +01:00
Elio Struyf
5e54334fb9 #97 - Support for folder scripts added 2021-11-21 12:07:24 +01:00
Elio Struyf
d354af306f Update changelog 2021-11-21 11:35:16 +01:00
Elio Struyf
045cece0ce #97 - Custom scripts to support media management 2021-11-21 11:32:56 +01:00
Elio Struyf
506012200f First steps to allow custom scripts for media files 2021-11-20 16:59:41 +01:00
Elio Struyf
4ad2f0d495 Remove image compress icon 2021-11-19 16:36:32 +01:00
Elio Struyf
b00e3cfd4b Image compression icon test 2021-11-19 16:02:56 +01:00
Elio Struyf
bb05489872 Fix postinstall 2021-11-19 15:49:12 +01:00
Elio Struyf
ede4d417bd Various updates 2021-11-19 15:45:08 +01:00
Elio Struyf
928072fa27 #178 - Type fixes 2021-11-18 19:55:56 +01:00
Elio Struyf
6a313fcc8a #178 - Making it faster 2021-11-18 19:53:37 +01:00
Elio Struyf
725f7f4915 #178 - Revert sorting implementation 2021-11-18 17:23:05 +01:00
Elio Struyf
b9cb0ea16d #180 - Filename placeholder added 2021-11-18 16:42:34 +01:00
Elio
61b80795a4 #178 - Windows support with PowerShell added 2021-11-18 16:24:45 +01:00
Elio Struyf
8daaa23774 #178 - macOS and Linux support for date retrieval 2021-11-18 12:56:23 +01:00
Elio Struyf
8a0d308ceb #178 - Sorting for media files 2021-11-17 14:18:04 +01:00
Elio Struyf
e6a7a9aae7 #179 - New open dashboard icon 2021-11-17 10:03:19 +01:00
Elio Struyf
f3943bd846 5.6.0 2021-11-17 09:07:47 +01:00
Elio Struyf
d2ae94df34 Merge pull request #177 from estruyf/dev 2021-11-15 10:42:15 +01:00
Elio Struyf
f799613b1a Update changelog 2021-11-15 10:38:46 +01:00
Elio Struyf
3f057a01d8 Removed char 2021-11-12 15:36:51 +01:00
Elio Struyf
3ad5136735 #173 - Persistent sorting 2021-11-12 12:11:46 +01:00
Elio Struyf
e201ce6f83 updated changelog 2021-11-12 10:55:26 +01:00
Elio Struyf
383a3a7d4c Updated changelog 2021-11-10 15:19:12 +01:00
Elio Struyf
8261f1de1b #173 - Allow to specify your own sorting 2021-11-10 15:18:35 +01:00
Elio Struyf
5b38e6fa56 Updated changelog 2021-11-10 11:55:33 +01:00
Elio Struyf
717f34bc85 #174 - Enhancement for excluding sub-directories 2021-11-10 11:54:39 +01:00
Elio Struyf
47fb2a90a9 5.5.0 2021-11-10 11:16:24 +01:00
Elio Struyf
85a7221895 Updated flows 2021-11-09 10:02:38 +01:00
Elio Struyf
9618a89528 Merge pull request #169 from estruyf/dev 2021-11-05 09:25:09 +01:00
Elio Struyf
14f0af2754 Updated changelog 2021-11-05 09:24:26 +01:00
Elio Struyf
ebe248670d 5.4.0 2021-11-03 12:00:56 +01:00
Elio Struyf
511960c4a9 #167 - Allow to set a preview path per content type 2021-11-03 12:00:49 +01:00
Elio Struyf
31fd1f93ce Url joining 2021-11-02 16:32:36 +01:00
Elio Struyf
6625b69170 #166 - Filename and folder logic for slug 2021-11-02 13:48:52 +01:00
Elio Struyf
9e8533fbb8 Updated changelog 2021-11-02 11:58:43 +01:00
Elio Struyf
9c9cbb7dcb #166 - Add preview button to panel when no markdown file is opened 2021-11-02 11:56:40 +01:00
Elio Struyf
079a13e161 Merge pull request #164 from estruyf/dev
Merge for 5.3.1
2021-10-29 10:25:17 +02:00
Elio Struyf
69c1e587d0 5.3.1 2021-10-29 10:18:31 +02:00
Elio Struyf
3996252531 #163 - Set workspace state instead of global 2021-10-29 10:18:20 +02:00
Elio Struyf
4fddda65e6 Merge pull request #162 from estruyf/dev
5.3.0 merge
2021-10-28 15:33:06 +02:00
Elio Struyf
5916344092 added release notes 2021-10-28 12:00:24 +02:00
Elio Struyf
b96722dd69 #158 - Update boolean field check 2021-10-27 11:42:57 +02:00
Elio Struyf
263ccab311 5.3.0 2021-10-26 15:59:45 +02:00
Elio Struyf
3571af82c7 Updated readme 2021-10-26 15:59:31 +02:00
Elio Struyf
c60520c0ff Fix replace in action button 2021-10-26 15:28:01 +02:00
Elio Struyf
b473431eae #159 - SEO enhancements 2021-10-26 14:42:01 +02:00
Elio Struyf
cbf434f741 Updated changelog 2021-10-25 12:54:19 +02:00
Elio Struyf
04c401207f #158 - New draft field setting + choice implementation 2021-10-25 12:53:04 +02:00
Elio Struyf
7291e6aac6 Fix tag replacement 2021-10-25 10:24:08 +02:00
Elio Struyf
a7aab96f0e Fix time formatting 2021-10-25 10:11:31 +02:00
Elio Struyf
f500749644 Fix slug punctuation 2021-10-20 15:21:27 +02:00
Elio Struyf
47e59bc54c Merge pull request #157 from estruyf/dev
Merge for v5.2.0 release
2021-10-19 15:41:12 +02:00
Elio Struyf
8902e25021 Release 5.2.0 2021-10-19 15:40:17 +02:00
Elio Struyf
33093e1eb4 Change useEffect order 2021-10-19 14:56:49 +02:00
Elio Struyf
d36178c44f Added component display names for better error reporting 2021-10-19 14:46:47 +02:00
Elio Struyf
15b09ccc75 #156 - Fix for media files in new folder 2021-10-19 11:04:05 +02:00
Elio Struyf
dffa6c87a0 Fix for rendering less hooks 2021-10-19 08:45:32 +02:00
Elio Struyf
c4a1caee09 Remove console 2021-10-18 14:33:32 +02:00
Elio Struyf
1d9f07b86d #155 - Fallback image 2021-10-18 14:29:54 +02:00
Elio Struyf
a794a95bb8 #154 - Implementation of the bulk script execution 2021-10-18 10:13:34 +02:00
Elio Struyf
40a56f6057 Fix SEO keyword check 2021-10-17 19:51:41 +02:00
Elio Struyf
82353f7b64 validate date 2021-10-17 15:31:19 +02:00
Elio Struyf
82a22da90a check the value of remove puntuation 2021-10-17 15:28:05 +02:00
Elio Struyf
380e40ea05 update changelog 2021-10-17 15:25:22 +02:00
Elio Struyf
2bedb23341 #153 - Support old date-fns format 2021-10-17 15:24:26 +02:00
Elio Struyf
1110b76364 Update changelog 2021-10-15 16:14:42 +02:00
Elio Struyf
d19e632f80 #151 #152 - Framework detection + preset public folder 2021-10-15 16:11:59 +02:00
Elio Struyf
4e040b5f7a 5.2.0 2021-10-15 14:02:08 +02:00
Elio Struyf
7a2a0934c2 Merge pull request #150 from estruyf/dev
Merge for 5.1.1
2021-10-14 16:24:29 +02:00
Elio Struyf
d3eb7b223c 5.1.1 2021-10-14 16:23:51 +02:00
Elio Struyf
f74eec954f #149 - Fix keywords 2021-10-14 16:23:28 +02:00
Elio Struyf
864c4e7aa6 Merge pull request #148 from estruyf/dev
Merge for v5.1.0
2021-10-13 11:34:29 +02:00
Elio Struyf
5667906caf Update changelog for release 2021-10-13 10:46:06 +02:00
Elio Struyf
2fe7c524e7 #147 - Error boundary added for metadata fields 2021-10-13 09:05:32 +02:00
Elio Struyf
5cc83526ad Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2021-10-13 08:27:48 +02:00
Elio Struyf
76b5e99a08 File check 2021-10-13 08:27:18 +02:00
Elio Struyf
7d5505d421 Updated changelog 2021-10-12 20:34:15 +02:00
Elio Struyf
d97a11f8b5 #146 - Date parsing logic added with fallbacks 2021-10-12 20:34:06 +02:00
Elio Struyf
0590cec684 Add version and environment details 2021-10-12 19:49:34 +02:00
Elio Struyf
b4cdc4feb9 Version fixes 2021-10-12 15:52:54 +02:00
Elio Struyf
986fd95524 #145 - Moved folder registration settings 2021-10-12 15:22:43 +02:00
Elio Struyf
f51fec5fb9 Fix issue in sorting of taxonomy picker 2021-10-12 10:50:42 +02:00
Elio Struyf
8198ce2af3 Added VSCode webcomponents 2021-10-12 10:23:55 +02:00
Elio Struyf
defffc4c8e Remove unused settings 2021-10-12 09:12:18 +02:00
Elio Struyf
8f47cbfb0b #141 - Merge content creation logic to ArticleHelper 2021-10-12 09:11:47 +02:00
Elio Struyf
0be91c17d0 #141 - Added support for page bundles with templates 2021-10-11 21:40:05 +02:00
Elio Struyf
fae7ab8417 Date to string fix 2021-10-11 17:49:45 +02:00
Elio Struyf
df239f2cc0 #144 - Pass a default value when null 2021-10-11 11:10:47 +02:00
Elio Struyf
70099dc97f #144 - Fix date value issue 2021-10-11 11:08:45 +02:00
Elio Struyf
c11be0e3ec #143 - Fix for recent files unique values 2021-10-11 10:55:58 +02:00
Elio Struyf
f8b7870180 Updated changlog 2021-10-11 10:50:28 +02:00
Elio Struyf
c58a5c62d9 #142 - Fix for unknown tags 2021-10-11 10:49:46 +02:00
Elio Struyf
ce92444bf2 Updated dashboard icon 2021-10-11 10:39:50 +02:00
Elio Struyf
b2709ebffd #141 - Support opening of the page bundle folder 2021-10-11 10:21:40 +02:00
Elio Struyf
2b20cf9d24 Updated changelog 2021-10-11 09:21:56 +02:00
Elio Struyf
f4a499ad0f 5.1.0 2021-10-11 09:20:48 +02:00
Elio Struyf
4494b158c0 #141 - Page bundle functionality 2021-10-11 09:20:42 +02:00
Elio Struyf
3416e55264 Fix rending more hooks 2021-10-08 15:30:56 +02:00
Elio Struyf
9b53e31cd5 Merge pull request #138 from estruyf/dev
Merge for v5.0.0 release
2021-10-07 14:11:47 +02:00
Elio Struyf
f49b93b042 Added release date 2021-10-07 14:06:01 +02:00
Elio Struyf
a005930c14 Added small icon 2021-10-07 14:05:12 +02:00
Elio Struyf
9dea1ee6ed Update logo 2021-10-07 08:46:05 +02:00
Elio Struyf
1ea0999d17 #137 - Ask to move the templates foler into frontmatter 2021-10-06 21:03:17 +02:00
Elio Struyf
2e6a466ba5 New sponsor added 2021-10-06 13:51:33 +02:00
Elio Struyf
fbcd430dc6 Style fix for preview images 2021-10-06 11:42:57 +02:00
Elio Struyf
2bd910db47 Update changelog 2021-10-06 11:35:04 +02:00
Elio Struyf
8158c9a483 #135 - Hidden property added for fields 2021-10-06 11:34:34 +02:00
Elio Struyf
4622fbe757 Version check when migration is running 2021-10-06 09:00:08 +02:00
Elio
97a635c2de Making sure paths are parsed for Windows 2021-10-05 20:20:31 +02:00
Elio
e8c67c75fd Fix file path issues for windows + open file with parsing error 2021-10-05 19:58:25 +02:00
Elio Struyf
6151ecb4c1 #132 - Open only an existing folder 2021-10-05 13:45:42 +02:00
Elio Struyf
1d8c192c07 #134 - Promote settings question 2021-10-05 13:26:11 +02:00
Elio Struyf
014911b7a9 #133 - Fix for overriding default content type settings 2021-10-05 13:23:58 +02:00
Elio Struyf
7b2d7b8aa5 #132 - Drag and drop fix 2021-10-05 09:12:03 +02:00
Elio Struyf
caceed2d4c Updated changelog 2021-10-05 08:46:54 +02:00
Elio Struyf
476ec6c2fd #132 - Persist the last opened folder location 2021-10-05 08:44:30 +02:00
Elio Struyf
5374edfa01 5.0.0 2021-10-05 08:11:35 +02:00
Elio Struyf
6a0cac9dfb Updated version 2021-10-05 08:11:32 +02:00
Elio Struyf
b525a6a211 #131 #132 - Changes to media dashboard 2021-10-04 20:57:54 +02:00
Elio Struyf
c295761560 #128 - support multi image selection with isPreviewImage property 2021-10-04 11:46:07 +02:00
Elio Struyf
6154164b4b #128 - 🚀 multi image selection support 2021-10-04 11:32:52 +02:00
Elio Struyf
05ce2d3537 #126 - Create new content from conent type 2021-10-03 12:55:58 +02:00
Elio Struyf
544f24bcba #127 - Dashboard action 2021-10-02 17:36:14 +02:00
Elio Struyf
1c354ed976 Link positioning issue 2021-10-01 17:04:29 +02:00
Elio Struyf
9b9bf1bfbe Include error tracing 2021-10-01 15:28:00 +02:00
Elio Struyf
29d5f02d10 #124 - Update preview image 2021-10-01 14:50:57 +02:00
Elio Struyf
9bc2fbc141 Remove unused references 2021-10-01 13:15:10 +02:00
Elio Struyf
cac009b773 #110 - Support for workspaces with multiple folders 2021-10-01 10:18:48 +02:00
Elio Struyf
bf1639cac7 Update changelog 2021-09-30 16:27:17 +02:00
Elio Struyf
5f28e145c4 #124 - Add support to specify preview image 2021-09-30 16:26:21 +02:00
Elio Struyf
8199ab964e Placeholder styling 2021-09-30 15:52:54 +02:00
Elio Struyf
14c050e34b #122 - Check if file already exists 2021-09-30 15:52:41 +02:00
Elio Struyf
b96411d1f9 #122 - Update media filename 2021-09-30 12:26:58 +02:00
Elio Struyf
87c469a6c7 Remove log 2021-09-30 10:12:00 +02:00
Elio Struyf
c29aef03f0 Changed img description to caption 2021-09-30 10:05:06 +02:00
Elio Struyf
9eaf94de7a #119 #121 - Choice field enhancements 2021-09-30 09:34:00 +02:00
Elio Struyf
f5e7526fae #120 - Fix for choice and number field 2021-09-30 08:41:35 +02:00
Elio Struyf
579e4925c8 Updated readme 2021-09-29 09:52:33 +02:00
Elio Struyf
4a4c558d9d Fix field type 2021-09-28 15:57:37 +02:00
Elio Struyf
9bb34850e2 Updated changelog 2021-09-28 09:01:44 +02:00
Elio Struyf
c6e37532bc #113 - Move the database init code 2021-09-27 18:23:36 +02:00
Elio Struyf
221f962beb updated changelog 2021-09-27 16:25:38 +02:00
Elio Struyf
17566279e2 Updated dependencies 2021-09-27 16:23:35 +02:00
Elio Struyf
4e7488414d #113 - Implemented local database for media 2021-09-27 16:22:23 +02:00
Elio Struyf
c206817431 #117 - Added single line support 2021-09-27 09:23:09 +02:00
225 changed files with 20840 additions and 8662 deletions

View File

@@ -0,0 +1,7 @@
---
name: Feedback
about: Tell more on what you think
title: 'Feedback: '
labels: ''
assignees: ''
---

View File

@@ -11,9 +11,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/setup-node@v2
with:
node-version: 14
node-version: 16
registry-url: https://registry.npmjs.org/
- name: Install the dependencies
@@ -25,3 +25,6 @@ jobs:
- name: Publish
run: npx vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
- name: Publish to open-vsx.org
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}

View File

@@ -12,9 +12,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/setup-node@v2
with:
node-version: 14
node-version: 16
registry-url: https://registry.npmjs.org/
- name: Install the dependencies
@@ -26,3 +26,5 @@ jobs:
- name: Publish
run: npx vsce publish -p ${{ secrets.VSCE_PAT }}
- name: Publish to open-vsx.org
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}

View File

@@ -17,4 +17,9 @@ postcss.config.js
.templates
.github
scripts
.all-contributorsrc
.all-contributorsrc
assets/v2.*
assets/v3.*
assets/v4.*
assets/sponsors
dist/*.html

View File

@@ -1,5 +1,166 @@
# Change Log
## [5.9.0] - 2022-01-01 - 🎇🎆
### 🎨 Enhancements
- Fixing the spinner which overlaps the global navigation bar
- Quick actions added for media files (edit, delete, insert markdown, insert snippet)
- [#199](https://github.com/estruyf/vscode-front-matter/issues/199): Search media files in the currently selected folder
- [#211](https://github.com/estruyf/vscode-front-matter/issues/211): Replace text selection on media inserts
- [#212](https://github.com/estruyf/vscode-front-matter/issues/212): Create folder watchers for content folders. When new content gets created, the dashboard updates.
- [#213](https://github.com/estruyf/vscode-front-matter/issues/213): New media folder overview design
### 🐞 Fixes
- [#210](https://github.com/estruyf/vscode-front-matter/issues/210): Fix for adding media files with uppercase file extensions
- [#214](https://github.com/estruyf/vscode-front-matter/issues/214): Fix for opening markdown file after creating it for the specified content type
## [5.8.0] - 2021-12-21 - 🎄
### 🎨 Enhancements
- Refactoring of the WebView logic to new message handlers
- Optimized the `getMedia` call from the webview
- Keep the dashboard its context when switching tabs
- [#205](https://github.com/estruyf/vscode-front-matter/issues/205): Define a logging level setting
- [#206](https://github.com/estruyf/vscode-front-matter/issues/206): Add front matter issues to the diagnostic tab
### 🐞 Fixes
- [#207](https://github.com/estruyf/vscode-front-matter/issues/207): Fix the quick picks for content creation
- [#208](https://github.com/estruyf/vscode-front-matter/issues/208): Fix for the collapse sections action so that it is not available everywhere, but only on the Front Matter panel
## [5.7.0] - 2021-12-07 - [Release Notes](https://beta.frontmatter.codes/updates/v5.7.0)
### 🎨 Enhancements
- [#188](https://github.com/estruyf/vscode-front-matter/issues/188): Support for `.markdown` files added to the dashboard
- [#190](https://github.com/estruyf/vscode-front-matter/issues/190): Diagnostic output for the extension
- [#194](https://github.com/estruyf/vscode-front-matter/issues/194): WYSIWYG controls added for markdown files + configuration to enable/disable the functionality
### 🐞 Fixes
- [#191](https://github.com/estruyf/vscode-front-matter/issues/191): Fix beta settings page
- [#200](https://github.com/estruyf/vscode-front-matter/issues/200): Fix last modified date sorting for media files
- [#201](https://github.com/estruyf/vscode-front-matter/issues/201): Fix overflow issue with the media filename
- [#202](https://github.com/estruyf/vscode-front-matter/issues/202): Fix checkbox label color for light themes
## [5.6.0] - 2021-11-23
### 🎨 Enhancements
- Updated camera icon from VS Code to media icon
- Updated the media card actions to show it within a menu. This will give a better experience with custom scripts.
- [#97](https://github.com/estruyf/vscode-front-matter/issues/97): Custom Script support for media files and folders
- [#178](https://github.com/estruyf/vscode-front-matter/issues/178): Sorting added to the media dashboard
- [#179](https://github.com/estruyf/vscode-front-matter/issues/179): Updated the `open dashboard` icon to make it easier to spot it
- [#180](https://github.com/estruyf/vscode-front-matter/issues/180): Added `{filename}` as placeholder for media snippets
- [#181](https://github.com/estruyf/vscode-front-matter/issues/181): Support for custom taxonomy fields added
### 🐞 Fixes
- [#183](https://github.com/estruyf/vscode-front-matter/issues/183): Fix type error on the `frontMatter.content.sorting` setting
## [5.5.0] - 2021-11-15
As from this version onwards, the extension will be published to [open-vsx.org](https://open-vsx.org/).
### 🎨 Enhancements
- [#173](https://github.com/estruyf/vscode-front-matter/issues/173): Allow to specify your own sorting for the content dashboard
- [#174](https://github.com/estruyf/vscode-front-matter/issues/174): Added option to exclude sub-directories from page/markdown content retrieval
## [5.4.0] - 2021-11-05
### 🎨 Enhancements
- [#166](https://github.com/estruyf/vscode-front-matter/issues/166): Added preview button to the panel base view
- [#167](https://github.com/estruyf/vscode-front-matter/issues/167): Allow to set the preview path per content type
## [5.3.1] - 2021-10-29
### 🐞 Fixes
- [#163](https://github.com/estruyf/vscode-front-matter/issues/163): Setting workspace state instead of global state for the media view
## [5.3.0] - 2021-10-28 - [Release Notes](https://beta.frontmatter.codes/updates/v5.3.0)
### 🎨 Enhancements
- [#158](https://github.com/estruyf/vscode-front-matter/issues/158): Add support for non-boolean draft/publish status fields
- [#159](https://github.com/estruyf/vscode-front-matter/issues/159): Enhancements to SEO checks: Slug check, keyword details, more article information
### 🐞 Fixes
- Value check when generating slug from title
- Fix for date time formatting with `DD` and `YYYY` tokens
- Fix in tag space replacing when object is passed
## [5.2.0] - 2021-10-19
### 🎨 Enhancements
- [#151](https://github.com/estruyf/vscode-front-matter/issues/151): Detect which site-generator or framework is used
- [#152](https://github.com/estruyf/vscode-front-matter/issues/152): Automatically set setting based on the used site-generator or framework
- [#154](https://github.com/estruyf/vscode-front-matter/issues/154): Bulk script support added
- [#155](https://github.com/estruyf/vscode-front-matter/issues/155): Fallback image added for the images shown in the editor panel
### 🐞 Fixes
- [#153](https://github.com/estruyf/vscode-front-matter/issues/153): Support old date formatting for date-fns
- [#156](https://github.com/estruyf/vscode-front-matter/issues/156): Fix for uploading media files into a new folder
## [5.1.1] - 2021-10-14
### 🐞 Fixes
- [#149](https://github.com/estruyf/vscode-front-matter/issues/149): Fix panel rendering when incorrect type for keywords is provided
## [5.1.0] - 2021-10-13
### 🎨 Enhancements
- [#141](https://github.com/estruyf/vscode-front-matter/issues/141): Allow content creation for page bundles or single files
- [#145](https://github.com/estruyf/vscode-front-matter/issues/145): Moved folder registration settings to `frontmatter.json` file
- [#147](https://github.com/estruyf/vscode-front-matter/issues/147): Error boundary added for metadata fields
### 🐞 Fixes
- Rendered more hooks than during the previous render in `FileList`
- [#142](https://github.com/estruyf/vscode-front-matter/issues/142): Fix for unknown tags where it throws an error
- [#143](https://github.com/estruyf/vscode-front-matter/issues/143): Fix for duplicate values in the file list
- [#144](https://github.com/estruyf/vscode-front-matter/issues/144): Fix for `toISOString` does not exist on object
- [#146](https://github.com/estruyf/vscode-front-matter/issues/146): Date parsing logic added with fallbacks
## [5.0.0] - 2021-10-07 - [Release Notes](https://beta.frontmatter.codes/updates/v5.0.0)
### ✨ New features
- [#113](https://github.com/estruyf/vscode-front-matter/issues/113): Integrating a local DB for media metadata (caption, alt)
- [#132](https://github.com/estruyf/vscode-front-matter/issues/132): Major changes to the media dashboard which allows you to navigate through all folders
### 🎨 Enhancements
- [#110](https://github.com/estruyf/vscode-front-matter/issues/110): Add support for workspaces with multiple folders
- [#117](https://github.com/estruyf/vscode-front-matter/issues/117): Allow to specify a singleline of text in the metadata fields
- [#119](https://github.com/estruyf/vscode-front-matter/issues/119): Multi-select support for choice fields
- [#121](https://github.com/estruyf/vscode-front-matter/issues/121): Choice fields support ID/title objects as well as a regular string
- [#122](https://github.com/estruyf/vscode-front-matter/issues/122): Update the filenames of your media
- [#124](https://github.com/estruyf/vscode-front-matter/issues/124): Add new `isPreviewImage` property to the content type field to specify custom preview images
- [#126](https://github.com/estruyf/vscode-front-matter/issues/126): Create new content from the available content types
- [#127](https://github.com/estruyf/vscode-front-matter/issues/127): Title bar action added to open the dashboard
- [#128](https://github.com/estruyf/vscode-front-matter/issues/128): Support for multi-select on image fields added
- [#131](https://github.com/estruyf/vscode-front-matter/issues/131): Folder creation support added on media dashboard
- [#134](https://github.com/estruyf/vscode-front-matter/issues/134): On startup, the extension checks if local settings can be promoted
- [#135](https://github.com/estruyf/vscode-front-matter/issues/135): `Hidden` property added for field configuration
- [#137](https://github.com/estruyf/vscode-front-matter/issues/137): Ask to move the `.templates` folder to the new `.frontmatter` folder
### 🐞 Fixes
- [#120](https://github.com/estruyf/vscode-front-matter/issues/120): Choice and number field not updating when set manually in front matter of the file
- [#133](https://github.com/estruyf/vscode-front-matter/issues/133): Fix for overriding default content type settings
## [4.0.1] - 2021-09-24
- [#114](https://github.com/estruyf/vscode-front-matter/issues/114): Fix for categories/tags provided as string in YAML

View File

@@ -48,6 +48,10 @@ Our main extension features are:
> If you see something missing in your article creation flow, please feel free to reach out.
**Version 5**
The new media dashboard redesign got introduced + support for setting metadata on media files [v5.0.0 release notes](https://frontmatter.codes/updates/v5.0.0).
**Version 4**
Support for Team level settings, content-types, and image support. Get to know more at: [v4.0.0 release notes](https://frontmatter.codes/updates/v4_0_0).
@@ -96,18 +100,31 @@ If you have the courage to test out the beta features, we made available a beta
## 👉 Contributors 🤘
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
</a>
<p align="center">
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
</a>
</p>
## 🖤 Backers & Sponsors 👇 🤘
<p align="center">
<a href="https://github.com/timschps" title="Tim Schaeps">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/13098307" />
</a>
<a href="https://github.com/zivbk1" title="Bryan Klein">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/6154767" />
</a>
<a href="https://github.com/flikteoh" title="FlikTeoh">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1472065" />
</a>
</p>
<br />
<br />
## 🖤 Sponsors
<p align="center">
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
<img src="assets/sponsors/powered-by-vercel.png" />
<img src="assets/sponsors/powered-by-vercel.png" />
</a>
</p>

View File

@@ -46,6 +46,10 @@ Our main extension features are:
> If you see something missing in your article creation flow, please feel free to reach out.
**Version 5**
The new media dashboard redesign got introduced + support for setting metadata on media files [v5.0.0 release notes](https://frontmatter.codes/updates/v5.0.0).
**Version 4**
Support for Team level settings, content-types, and image support. Get to know more at: [v4.0.0 release notes](https://frontmatter.codes/updates/v4_0_0).
@@ -94,18 +98,31 @@ If you have the courage to test out the beta features, we made available a beta
## 👉 Contributors 🤘
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
</a>
<p align="center">
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
</a>
</p>
## 🖤 Backers & Sponsors 👇 🤘
<p align="center">
<a href="https://github.com/timschps" title="Tim Schaeps">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/13098307" />
</a>
<a href="https://github.com/zivbk1" title="Bryan Klein">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/6154767" />
</a>
<a href="https://github.com/flikteoh" title="FlikTeoh">
<img height="64px" style="border-radius:50%" src="https://avatars.githubusercontent.com/u/1472065" />
</a>
</p>
<br />
<br />
## 🖤 Sponsors
<p align="center">
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
<img src="assets/sponsors/powered-by-vercel.png" />
<img src="assets/sponsors/powered-by-vercel.png" />
</a>
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,40 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" style="enable-background:new 0 0 28 28;" xml:space="preserve">
<style type="text/css">
.st0{enable-background:new ;}
.st1{fill:#FFFFFF;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
.st3{fill:none;}
</style>
<g class="st0">
<path class="st1" d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z"/>
<path class="st1" d="M10.9,11.4H9l-0.9-3c0-0.1,0-0.1,0-0.2C8,8.1,8,8,7.9,7.8v0.6v3H6.1V2.9H8c0.8,0,1.4,0.2,1.9,0.6
c0.5,0.5,0.8,1.3,0.8,2.2c0,1-0.4,1.7-1.1,2.1L10.9,11.4z M8,6.8h0.1c0.2,0,0.4-0.1,0.5-0.3C8.7,6.3,8.8,6,8.8,5.7
c0-0.6-0.3-1-0.8-1l0,0V6.8z"/>
<path class="st1" d="M16.5,7.2c0,1.3-0.2,2.4-0.7,3.2c-0.5,0.8-1.1,1.2-1.8,1.2s-1.2-0.3-1.7-0.9c-0.6-0.8-0.9-2-0.9-3.5
s0.3-2.7,0.9-3.5c0.5-0.6,1-0.9,1.7-0.9c0.8,0,1.4,0.4,1.9,1.2C16.2,4.7,16.5,5.8,16.5,7.2z M14.6,7.2c0-1.5-0.2-2.3-0.7-2.3
c-0.2,0-0.4,0.2-0.5,0.6s-0.2,0.9-0.2,1.7c0,0.7,0.1,1.3,0.2,1.7c0.1,0.4,0.3,0.6,0.5,0.6s0.4-0.2,0.5-0.6
C14.5,8.4,14.6,7.9,14.6,7.2z"/>
<path class="st1" d="M17.2,11.4V2.9H19l0.9,3C20,6,20,6.2,20.1,6.5c0.1,0.2,0.1,0.5,0.2,0.8L20.5,8c-0.1-0.7-0.1-1.4-0.2-1.9
s-0.1-1-0.1-1.3V2.9H22v8.5h-1.7l-0.9-3.1c-0.1-0.3-0.2-0.6-0.3-0.9S19,6.8,18.9,6.6c0,0.6,0.1,1.1,0.1,1.6c0,0.4,0,0.8,0,1.2v2.2
h-1.8V11.4z"/>
<path class="st1" d="M25.3,11.4h-1.8V4.9h-1v-2h3.9v2h-1.1V11.4z"/>
</g>
<rect class="st2" width="28" height="28"/>
<g class="st0">
<path class="st1" d="M2.9,17h0.9l0.6,3c0.1,0.4,0.2,0.8,0.2,1.2c0.1,0.4,0.1,0.8,0.2,1.2c0-0.1,0-0.1,0-0.1v-0.1L5,21.3l0.1-0.8
L5.2,20l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3s0-0.2,0-0.2v-1v-0.9l0,0c0,0,0,0,0-0.1v0.2c0,0.2,0,0.3-0.1,0.5
s0,0.2-0.1,0.3l-0.1,0.7v0.3l-0.6,3.3H4.5l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2l-0.3,5.2h-1L2.9,17z"/>
<path class="st1" d="M9.3,17h0.8l1.6,7.5h-1L10.4,23H8.9l-0.3,1.5h-1L9.3,17z M10.3,22.2L10,21c-0.1-0.8-0.3-1.7-0.4-2.6
c0,0.5-0.1,0.9-0.2,1.4c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1L10.3,22.2L10.3,22.2z"/>
<path class="st1" d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z"/>
<path class="st1" d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z"/>
<path class="st1" d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z"/>
<path class="st1" d="M22.3,17h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6
l1.4,3.7h-1l-1.4-3.7v3.7h-1L22.3,17L22.3,17z M23.3,20.3c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6
s-0.1-0.3-0.2-0.4s-0.2-0.2-0.3-0.2c-0.1,0-0.3-0.1-0.4-0.1h-0.2v2.5H23.3z"/>
</g>
<rect x="-33.5" y="14" class="st3" width="8.6" height="14"/>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#ffffff;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#ffffff"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#ffffff"/></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#01aeb7"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#01aeb7" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#01aeb7"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#01aeb7"/></svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#AD0670;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#AD0670"/></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,44 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 28 28" style="color:#ad0670" xml:space="preserve">
<style type="text/css">
.st1 {
fill: none;
stroke: #ad0670;
stroke-width: 2;
stroke-miterlimit: 10;
}
</style>
<g class="st0">
<path fill="currentcolor" d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z" />
<path fill="currentcolor" d="M10.9,11.4H9l-0.9-3c0-0.1,0-0.1,0-0.2C8,8.1,8,8,7.9,7.8l0,0.6v3H6.1V2.9H8c0.8,0,1.4,0.2,1.9,0.6
c0.5,0.5,0.8,1.3,0.8,2.2c0,1-0.4,1.7-1.1,2.1L10.9,11.4z M8,6.8h0.1c0.2,0,0.4-0.1,0.5-0.3C8.7,6.3,8.8,6,8.8,5.7
c0-0.6-0.3-1-0.8-1H8V6.8z" />
<path fill="currentcolor" d="M16.5,7.2c0,1.3-0.2,2.4-0.7,3.2c-0.5,0.8-1.1,1.2-1.8,1.2c-0.7,0-1.2-0.3-1.7-0.9c-0.6-0.8-0.9-2-0.9-3.5
c0-1.5,0.3-2.7,0.9-3.5c0.5-0.6,1-0.9,1.7-0.9c0.8,0,1.4,0.4,1.9,1.2C16.2,4.7,16.5,5.8,16.5,7.2z M14.6,7.2c0-1.5-0.2-2.3-0.7-2.3
c-0.2,0-0.4,0.2-0.5,0.6c-0.1,0.4-0.2,0.9-0.2,1.7c0,0.7,0.1,1.3,0.2,1.7c0.1,0.4,0.3,0.6,0.5,0.6c0.2,0,0.4-0.2,0.5-0.6
C14.5,8.4,14.6,7.9,14.6,7.2z" />
<path fill="currentcolor" d="M17.2,11.4V2.9H19l0.9,3C20,6,20,6.2,20.1,6.5c0.1,0.2,0.1,0.5,0.2,0.8L20.5,8c-0.1-0.7-0.1-1.4-0.2-1.9s-0.1-1-0.1-1.3
V2.9H22v8.5h-1.7l-0.9-3.1c-0.1-0.3-0.2-0.6-0.3-0.9s-0.1-0.6-0.2-0.8c0,0.6,0.1,1.1,0.1,1.6c0,0.4,0,0.8,0,1.2v2.2H17.2z" />
<path fill="currentcolor" d="M25.3,11.4h-1.8V4.9h-1v-2h3.9v2h-1.1V11.4z" />
</g>
<rect class="st1" width="28" height="28" />
<g class="st0">
<path fill="currentcolor" d="M2.9,17h0.9L4.4,20c0.1,0.4,0.2,0.8,0.2,1.2c0.1,0.4,0.1,0.8,0.2,1.2c0-0.1,0-0.1,0-0.1c0,0,0-0.1,0-0.1L5,21.3l0.1-0.8
L5.2,20l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3s0-0.2,0-0.2l0-1l0-0.9c0,0,0,0,0,0c0,0,0,0,0-0.1l0,0.2
c0,0.2,0,0.3-0.1,0.5s0,0.2-0.1,0.3l-0.1,0.7l0,0.3l-0.6,3.3H4.5l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2
l-0.3,5.2h-1L2.9,17z" />
<path fill="currentcolor" d="M9.3,17h0.8l1.6,7.5h-1L10.4,23H8.9l-0.3,1.5h-1L9.3,17z M10.3,22.2L10,21c-0.1-0.8-0.3-1.7-0.4-2.6c0,0.5-0.1,0.9-0.2,1.4
c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1H10.3z" />
<path fill="currentcolor" d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z" />
<path fill="currentcolor" d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z" />
<path fill="currentcolor" d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z" />
<path fill="currentcolor" d="M22.3,17h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6l1.4,3.7h-1
l-1.4-3.7v3.7h-1V17z M23.3,20.3c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6s-0.1-0.3-0.2-0.4
s-0.2-0.2-0.3-0.2c-0.1,0-0.3-0.1-0.4-0.1h-0.2V20.3z" />
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#AD0670;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#AD0670"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#AD0670"/></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#02AEB7;stroke-width:50;stroke-miterlimit:10;}
.st1{fill:#02AEB7;}
</style>
<rect x="25" y="25" class="st0" width="1200" height="1200"/>
<path class="st1" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path class="st1" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#02aeb7;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#02aeb7"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#02aeb7"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,39 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" style="enable-background:new 0 0 28 28;" xml:space="preserve">
<style type="text/css">
.st0{enable-background:new ;}
.st1{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
.st2{fill:none;}
</style>
<g class="st0">
<path d="M4,11.4H2.2V2.9h3.2v2H4v1.2h1.3V8H4V11.4z"/>
<path d="M10.9,11.4H9l-0.9-3c0-0.1,0-0.1,0-0.2C8,8.1,8,8,7.9,7.8l0,0.6v3H6.1V2.9H8c0.8,0,1.4,0.2,1.9,0.6
c0.5,0.5,0.8,1.3,0.8,2.2c0,1-0.4,1.7-1.1,2.1L10.9,11.4z M8,6.8h0.1c0.2,0,0.4-0.1,0.5-0.3C8.7,6.3,8.8,6,8.8,5.7
c0-0.6-0.3-1-0.8-1H8V6.8z"/>
<path d="M16.5,7.2c0,1.3-0.2,2.4-0.7,3.2c-0.5,0.8-1.1,1.2-1.8,1.2c-0.7,0-1.2-0.3-1.7-0.9c-0.6-0.8-0.9-2-0.9-3.5
c0-1.5,0.3-2.7,0.9-3.5c0.5-0.6,1-0.9,1.7-0.9c0.8,0,1.4,0.4,1.9,1.2C16.2,4.7,16.5,5.8,16.5,7.2z M14.6,7.2c0-1.5-0.2-2.3-0.7-2.3
c-0.2,0-0.4,0.2-0.5,0.6c-0.1,0.4-0.2,0.9-0.2,1.7c0,0.7,0.1,1.3,0.2,1.7c0.1,0.4,0.3,0.6,0.5,0.6c0.2,0,0.4-0.2,0.5-0.6
C14.5,8.4,14.6,7.9,14.6,7.2z"/>
<path d="M17.2,11.4V2.9H19l0.9,3C20,6,20,6.2,20.1,6.5c0.1,0.2,0.1,0.5,0.2,0.8L20.5,8c-0.1-0.7-0.1-1.4-0.2-1.9s-0.1-1-0.1-1.3
V2.9H22v8.5h-1.7l-0.9-3.1c-0.1-0.3-0.2-0.6-0.3-0.9s-0.1-0.6-0.2-0.8c0,0.6,0.1,1.1,0.1,1.6c0,0.4,0,0.8,0,1.2v2.2H17.2z"/>
<path d="M25.3,11.4h-1.8V4.9h-1v-2h3.9v2h-1.1V11.4z"/>
</g>
<rect class="st1" width="28" height="28"/>
<g class="st0">
<path d="M2.9,17h0.9L4.4,20c0.1,0.4,0.2,0.8,0.2,1.2c0.1,0.4,0.1,0.8,0.2,1.2c0-0.1,0-0.1,0-0.1c0,0,0-0.1,0-0.1L5,21.3l0.1-0.8
L5.2,20l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3s0-0.2,0-0.2l0-1l0-0.9c0,0,0,0,0,0c0,0,0,0,0-0.1l0,0.2
c0,0.2,0,0.3-0.1,0.5s0,0.2-0.1,0.3l-0.1,0.7l0,0.3l-0.6,3.3H4.5l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2
l-0.3,5.2h-1L2.9,17z"/>
<path d="M9.3,17h0.8l1.6,7.5h-1L10.4,23H8.9l-0.3,1.5h-1L9.3,17z M10.3,22.2L10,21c-0.1-0.8-0.3-1.7-0.4-2.6c0,0.5-0.1,0.9-0.2,1.4
c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1H10.3z"/>
<path d="M11.5,17h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V17z"/>
<path d="M14.8,17h3.3v0.9H17v6.7h-1v-6.7h-1.2V17z"/>
<path d="M18.7,17h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V17z"/>
<path d="M22.3,17h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6l1.4,3.7h-1
l-1.4-3.7v3.7h-1V17z M23.3,20.3c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6s-0.1-0.3-0.2-0.4
s-0.2-0.2-0.3-0.2c-0.1,0-0.3-0.1-0.4-0.1h-0.2V20.3z"/>
</g>
<rect x="-33.5" y="14" class="st2" width="8.6" height="14"/>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1249.98 1249.98"><rect x="25" y="25" width="1199.98" height="1199.98" style="fill:none;stroke:#000000;stroke-miterlimit:10;stroke-width:50px"/><path d="M171.89,489.56H95.38V127.21H230.6V212.4H171.89v52.8h54.68v81.91H171.89Z" transform="translate(24 24)" style="fill:#000000"/><path d="M461.79,489.56H379l-37.8-129.08c-.37-2.18-1-5.08-1.93-8.68s-2.05-7.9-3.39-12.91l.55,23.94V489.56H260.33V127.21h78.34q51.75,0,77.43,26.05,32.65,33.32,32.66,94.81,0,65.71-43.85,90.82ZM336.84,295H342q13.21,0,22-12.91t8.81-32.86q0-40.59-33.21-40.6h-2.75Z" transform="translate(24 24)" style="fill:#000000"/><path d="M691.68,309.56q0,82.85-29.54,134.71-29.35,51.63-76.51,51.63-41.82,0-71.74-39.66Q476.29,406,476.28,305.57q0-96.23,39.26-147.15,29.18-37.78,69.18-37.79,49,0,78,51.17T691.68,309.56Zm-79.44.7q0-98.32-27.16-98.33-13.58,0-21.65,25.81-7.89,23.94-7.89,70.41,0,45.77,7.43,71t20.65,25.23q13.57,0,20.91-24.88Q612.24,354.62,612.24,310.26Z" transform="translate(24 24)" style="fill:#000000"/><path d="M724.34,489.56V127.21h73l38.35,127.2q3.1,11.27,7.06,25.81t8.72,33.56l7.88,31.92Q855.17,298.52,853,265t-2.2-56.33V127.21h73V489.56h-73l-38.53-133.3q-6.06-21.35-10.92-40t-8.53-35.56q2.38,38.26,3.49,66.65t1.1,49.76v92.46Z" transform="translate(24 24)" style="fill:#000000"/><path d="M1062.31,489.56H985.8V214H943.6V127.21h162.56V214h-43.85Z" transform="translate(24 24)" style="fill:#000000"/><path d="M122.7,730.59h35.82l27.36,133.72q5,25.05,9.16,50.2t7.55,52.74q.39-3.6.6-5.62a25.33,25.33,0,0,1,.4-2.87l5.84-37.56,5.23-35.66L219.29,862l24.35-131.39h36.22l28.57,327.72h-40l-7-111.22q-.41-8.49-.71-14.64c-.2-4.11-.3-7.5-.3-10.19l-1.81-43.94-1-40.33c0-.28,0-.88-.1-1.8s-.17-2.16-.3-3.72l-1,6.58q-1.61,11.69-2.91,20.38t-2.32,14.65L245.65,904l-2,11.25-26.16,143.06H189.3L164.75,934.78q-5-24.4-8.95-49.56t-7.14-52.75l-12.08,225.84H97.14Z" transform="translate(24 24)" style="fill:#000000"/><path d="M395.56,730.59h32.6l66.6,327.72H453.31l-11.67-63.89H380.06l-11.87,63.89H327.94Zm40,229.66L426.35,908q-9.27-53.28-15.1-113.77Q408.43,823.78,404,854t-10.46,64.2l-7.65,42Z" transform="translate(24 24)" style="fill:#000000"/><path d="M496.17,730.59H632.4v38.63H585.51v289.09h-41V769.22H496.17Z" transform="translate(24 24)" style="fill:#000000"/><path d="M639,730.59H775.26v38.63H728.38v289.09h-41V769.22H639Z" transform="translate(24 24)" style="fill:#000000"/><path d="M806.65,730.59H917.93V768H848.5V871.74h61.58V909.1H848.5V1021h69.43v37.35H806.65Z" transform="translate(24 24)" style="fill:#000000"/><path d="M964.61,730.59h55.13q34.21,0,50.91,17.19,21.13,22.29,21.13,68.14,0,35.24-11.16,56.56t-31.9,26.43l57.15,159.4h-42.46l-57-160.46v160.46H964.61Zm41.85,145.18q24.35,0,34.41-11.88t10.06-40.12a138.46,138.46,0,0,0-2.11-26.11q-2.11-10.81-6.64-17.61a27.08,27.08,0,0,0-11.67-10,41.58,41.58,0,0,0-17-3.18h-7Z" transform="translate(24 24)" style="fill:#000000"/></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M6 15h15" />
<path d="M21 19h-15" />
<path d="M15 11h6" />
<path d="M21 7h-6" />
<path d="M9 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
<path d="M3 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
</svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M6 15h15" />
<path d="M21 19h-15" />
<path d="M15 11h6" />
<path d="M21 7h-6" />
<path d="M9 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
<path d="M3 9h1a1 1 0 1 1 -1 1v-2.5a2 2 0 0 1 2 -2" />
</svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z" />
<path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" />
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z" />
<path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" />
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -1,4 +1,4 @@
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#F3EFF5">
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#C5C5C5">
<path d="M9 9H4v1h5V9z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2V3zm1 2h4l1 1v4h2V3H6v2zm4 1H3v7h7V6z" />
</svg>

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -1,4 +1,4 @@
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentcolor">
<svg width="32px" height="32px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#424242">
<path d="M9 9H4v1h5V9z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2V3zm1 2h4l1 1v4h2V3H6v2zm4 1H3v7h7V6z" />
</svg>

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<polyline points="7 8 3 12 7 16" />
<polyline points="17 8 21 12 17 16" />
<line x1="14" y1="4" x2="10" y2="20" />
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<polyline points="7 8 3 12 7 16" />
<polyline points="17 8 21 12 17 16" />
<line x1="14" y1="4" x2="10" y2="20" />
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 12h6" />
<path d="M12 9v6" />
<path d="M6 19a2 2 0 0 1 -2 -2v-4l-1 -1l1 -1v-4a2 2 0 0 1 2 -2" />
<path d="M18 19a2 2 0 0 0 2 -2v-4l1 -1l-1 -1v-4a2 2 0 0 0 -2 -2" />
</svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 12h6" />
<path d="M12 9v6" />
<path d="M6 19a2 2 0 0 1 -2 -2v-4l-1 -1l1 -1v-4a2 2 0 0 1 2 -2" />
<path d="M18 19a2 2 0 0 0 2 -2v-4l1 -1l-1 -1v-4a2 2 0 0 0 -2 -2" />
</svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#C5C5C5"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#C5C5C5" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#C5C5C5"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M4,11.4H2.2V2.9H5.4v2H4V6.1H5.3V8H4Z" transform="translate(1 1)" fill="#424242"/><path d="M10.9,11.4H9l-.9-3V8.2C8,8.1,8,8,7.9,7.8v3.6H6.1V2.9H8a2.88,2.88,0,0,1,1.9.6,3.11,3.11,0,0,1,.8,2.2A2.25,2.25,0,0,1,9.6,7.8ZM8,6.8h.1a.55.55,0,0,0,.5-.3,1.88,1.88,0,0,0,.2-.8c0-.6-.3-1-.8-1H8Z" transform="translate(1 1)" fill="#424242"/><path d="M16.5,7.2a6.08,6.08,0,0,1-.7,3.2A2.14,2.14,0,0,1,14,11.6a2.09,2.09,0,0,1-1.7-.9,5.84,5.84,0,0,1-.9-3.5,5.84,5.84,0,0,1,.9-3.5A2.09,2.09,0,0,1,14,2.8,2.16,2.16,0,0,1,15.9,4,8.24,8.24,0,0,1,16.5,7.2Zm-1.9,0c0-1.5-.2-2.3-.7-2.3-.2,0-.4.2-.5.6a6.53,6.53,0,0,0-.2,1.7,7.18,7.18,0,0,0,.2,1.7c.1.4.3.6.5.6s.4-.2.5-.6A7.93,7.93,0,0,0,14.6,7.2Z" transform="translate(1 1)" fill="#424242"/><path d="M17.2,11.4V2.9H19l.9,3c.1.1.1.3.2.6s.1.5.2.8l.2.7c-.1-.7-.1-1.4-.2-1.9a6.64,6.64,0,0,1-.1-1.3V2.9H22v8.5H20.3l-.9-3.1-.3-.9c-.1-.3-.1-.6-.2-.8,0,.6.1,1.1.1,1.6v3.4H17.2Z" transform="translate(1 1)" fill="#424242"/><path d="M25.3,11.4H23.5V4.9h-1v-2h3.9v2H25.3Z" transform="translate(1 1)" fill="#424242"/><rect x="1" y="1" width="28" height="28" fill="none" stroke="#424242" stroke-miterlimit="10" stroke-width="2"/><path d="M2.9,17h.9l.6,3a5,5,0,0,1,.2,1.2c.1.4.1.8.2,1.2v-.2l.2-.9.1-.8.1-.5.6-3h.9l.7,7.5h-1l-.2-2.6V19.5h0v.1a.9.9,0,0,1-.1.5c-.1.2,0,.2-.1.3l-.1.7v.3l-.6,3.3H4.5l-.6-2.8a5.16,5.16,0,0,1-.2-1.1c-.1-.4-.1-.8-.2-1.2l-.3,5.2h-1Z" transform="translate(1 1)" fill="#424242"/><path d="M9.3,17h.8l1.6,7.5h-1L10.4,23H8.9l-.3,1.5h-1Zm1,5.2L10,21c-.1-.8-.3-1.7-.4-2.6a6.75,6.75,0,0,1-.2,1.4l-.3,1.5-.2,1h1.4Z" transform="translate(1 1)" fill="#424242"/><path d="M11.5,17h3.3v.9H13.7v6.7h-1V17.9H11.5Z" transform="translate(1 1)" fill="#424242"/><path d="M14.8,17h3.3v.9H17v6.7H16V17.9H14.8Z" transform="translate(1 1)" fill="#424242"/><path d="M18.7,17h2.7v.9H19.7v2.4h1.5v.9H19.7v2.6h1.7v.9H18.7Z" transform="translate(1 1)" fill="#424242"/><path d="M22.3,17h1.3c.6,0,1,.1,1.2.4a2.35,2.35,0,0,1,.5,1.6,2.5,2.5,0,0,1-.3,1.3,1.24,1.24,0,0,1-.8.6l1.4,3.7h-1l-1.4-3.7v3.7h-1V17Zm1,3.3c.4,0,.7-.1.8-.3s.2-.5.2-.9a1.27,1.27,0,0,0-.1-.6c-.1-.2-.1-.3-.2-.4s-.2-.2-.3-.2-.3-.1-.4-.1h-.2v2.5Z" transform="translate(1 1)" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
<rect x="25" y="25" fill="none" stroke="#ffffff" stroke-width="50" stroke-miterlimit="10" width="1200" height="1200"/>
<path fill="#ffffff" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#ffffff" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
<rect x="25" y="25" fill="none" stroke="#424242" stroke-width="50" stroke-miterlimit="10" width="1200" height="1200"/>
<path fill="#424242" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#424242" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
<path fill="#C5C5C5" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#C5C5C5" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
<path fill="#424242" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#424242" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve" width="16" height="16">
<path fill="#02aeb7" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#02aeb7" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6c7.9,47.6,15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
c-2.5,16.5-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8
c-7.5-47.7-14.2-97.6-20.3-149.9l-34.3,641.6H529.6L602.2,151.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 12h10" />
<path d="M7 4v16" />
<path d="M17 4v16" />
<path d="M15 20h4" />
<path d="M15 4h4" />
<path d="M5 20h4" />
<path d="M5 4h4" />
</svg>

After

Width:  |  Height:  |  Size: 400 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 12h10" />
<path d="M7 4v16" />
<path d="M17 4v16" />
<path d="M15 20h4" />
<path d="M15 4h4" />
<path d="M5 20h4" />
<path d="M5 4h4" />
</svg>

After

Width:  |  Height:  |  Size: 400 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="11" y1="5" x2="17" y2="5" />
<line x1="7" y1="19" x2="13" y2="19" />
<line x1="14" y1="5" x2="10" y2="19" />
</svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="11" y1="5" x2="17" y2="5" />
<line x1="7" y1="19" x2="13" y2="19" />
<line x1="14" y1="5" x2="10" y2="19" />
</svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#C5C5C5" width="24" height="24">
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="#424242" width="24" height="24">
<path fillRule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clipRule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<line x1="8" y1="12" x2="8" y2="12.01" />
<line x1="12" y1="12" x2="12" y2="12.01" />
<line x1="16" y1="12" x2="16" y2="12.01" />
</svg>

After

Width:  |  Height:  |  Size: 408 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<line x1="8" y1="12" x2="8" y2="12.01" />
<line x1="12" y1="12" x2="12" y2="12.01" />
<line x1="16" y1="12" x2="16" y2="12.01" />
</svg>

After

Width:  |  Height:  |  Size: 408 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list-numbers" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M11 6h9" />
<path d="M11 12h9" />
<path d="M12 18h8" />
<path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" />
<path d="M6 10v-6l-2 2" />
</svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list-numbers" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M11 6h9" />
<path d="M11 12h9" />
<path d="M12 18h8" />
<path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" />
<path d="M6 10v-6l-2 2" />
</svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 5v9a5 5 0 0 0 10 0v-9" />
<path d="M4 12h16" />
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M7 5v9a5 5 0 0 0 10 0v-9" />
<path d="M4 12h16" />
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#C5C5C5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="9" y1="6" x2="20" y2="6" />
<line x1="9" y1="12" x2="20" y2="12" />
<line x1="9" y1="18" x2="20" y2="18" />
<line x1="5" y1="6" x2="5" y2="6.01" />
<line x1="5" y1="12" x2="5" y2="12.01" />
<line x1="5" y1="18" x2="5" y2="18.01" />
</svg>

After

Width:  |  Height:  |  Size: 533 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#424242" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="9" y1="6" x2="20" y2="6" />
<line x1="9" y1="12" x2="20" y2="12" />
<line x1="9" y1="18" x2="20" y2="18" />
<line x1="5" y1="6" x2="5" y2="6.01" />
<line x1="5" y1="12" x2="5" y2="12.01" />
<line x1="5" y1="18" x2="5" y2="18.01" />
</svg>

After

Width:  |  Height:  |  Size: 533 B

View File

@@ -356,8 +356,18 @@
text-transform: capitalize;
}
.table__cell__seo_details {
padding: 10px;
}
.table__cell__validation {
text-align: center;
text-align: left;
}
.table__cell__validation div {
display: flex;
align-items: center;
padding: 2px 0;
}
.table__cell__validation .valid {
@@ -368,6 +378,15 @@
color: #E6AF2E;
}
.table__cell__validation div span + span {
margin-left: .5rem;
}
.seo__status__note {
font-size: 10px;
padding: 3px 0;
}
/* Fields */
.field__toggle {
position: relative;
@@ -437,6 +456,26 @@ input:checked + .field__toggle__slider:before {
margin-right: .5rem;
}
.metadata_field__error {
color: var(--vscode-errorForeground);
display: flex;
justify-content: space-between;
align-items: center;
}
.metadata_field__error button {
color: var(--vscode-button-secondaryForeground);
background-color: var(--vscode-button-secondaryBackground);
padding-left: 1rem;
padding-right: 1rem;
width: auto;
}
.metadata_field__error button:hover {
background-color: var(--vscode-button-secondaryHoverBackground);
}
.metadata_field__input, .metadata_field__input:focus,
.metadata_field__textarea, .metadata_field__textarea:focus {
outline: none;
}
@@ -451,17 +490,92 @@ input:checked + .field__toggle__slider:before {
outline: none !important;
}
.metadata_field__choice {
.metadata_field__choice__toggle {
color: var(--vscode-input-placeholderForeground);
border: 1px solid var(--vscode-inputValidation-infoBorder) !important;
outline: none !important;
width: 100%;
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
color: var(--vscode-input-foreground);
background-color: var(--vscode-input-background);
display: flex;
align-items: center;
position: relative;
}
.metadata_field__choice__toggle:hover,
.metadata_field__choice__toggle:focus,
.metadata_field__choice__toggle:active,
.metadata_field__choice__toggle:disabled {
background-color: var(--vscode-input-background);
}
.metadata_field__choice::placeholde {
color: var(--vscode-input-placeholderForeground);
.metadata_field__choice__toggle span {
margin-right: 1rem;
}
.metadata_field__choice__toggle svg.icon {
height: 1rem;
width: 1rem;
margin-left: .25rem;
position: absolute;
right: .25rem;
}
.metadata_field__choice_list {
width: 90%;
margin: 0;
padding: 0;
z-index: 1;
position: absolute;
list-style: none;
overflow: auto;
max-height: 200px;
color: var(--vscode-dropdown-foreground);
background-color: var(--vscode-dropdown-background);
}
.metadata_field__choice_list.open {
border: 1px solid rgba(0, 0, 0, .9);
}
.metadata_field__choice_list li {
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
cursor: pointer;
}
.metadata_field__choice_list li:active {
color: var(--vscode-button-foreground);
background-color: var(--vscode-button-background);
}
.metadata_field__choice_list li[aria-selected="true"] {
color: var(--vscode-button-foreground);
background-color: var(--vscode-button-hoverBackground);
}
.metadata_field__choice_list li[aria-disabled="true"] {
display: none;
}
.metadata_field__choice_list__item {
opacity: 0.8;
}
.metadata_field__choice__button {
margin-top: .5rem;
display: inline-flex;
align-items: center;
width: auto;
margin-right: .5rem;
}
.metadata_field__choice__button_icon {
height: 1.25rem;
width: 1.25rem;
margin-left: .5rem;
}
.metadata_field__datetime {
@@ -481,12 +595,56 @@ input:checked + .field__toggle__slider:before {
width: auto;
}
.metadata_field__datetime > button:hover {
background-color: var(--vscode-button-secondaryHoverBackground);
}
.metadata_field__multiple_images {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem;
}
.metadata_field__preview_image img {
display: block;
margin: 0 auto;
max-height: 16rem;
}
.metadata_field__preview_image__button {
background-color: transparent;
border: 2px dashed var(--vscode-button-background);
padding: 1.5rem;
filter: brightness(85%);
}
.metadata_field__preview_image__button:hover {
background-color: rgba(255, 255, 255, .1);
filter: brightness(100%);
}
.metadata_field__preview_image__button svg {
color: var(--vscode-foreground);
display: block;
width: 3rem;
height: 3rem;
margin: 0 auto;
}
.metadata_field__preview_image__button span {
color: var(--vscode-foreground);
display: inline-block;
margin: 0 auto;
margin-top: .5rem;
}
.metadata_field__preview_image__preview {
background-color: var(--vscode-button-secondaryBackground);
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.metadata_field__preview_image__remove {
background-color: var(--vscode-inputValidation-errorBackground);
color: var(--vscode-inputValidation-errorForeground);

20635
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{
"name": "vscode-front-matter-beta",
"displayName": "Front Matter",
"description": "An essential Visual Studio Code extension when you want to manage the markdown pages of your static site like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...",
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...",
"icon": "assets/frontmatter-teal-128x128.png",
"version": "4.0.1",
"version": "5.9.0",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -23,7 +23,7 @@
}
],
"engines": {
"vscode": "^1.51.0"
"vscode": "^1.63.0"
},
"categories": [
"Other"
@@ -66,7 +66,7 @@
"onCommand:frontMatter.insertImage",
"onView:frontMatter.explorer"
],
"main": "./dist/extension",
"main": "./dist/extension.js",
"contributes": {
"viewsContainers": {
"activitybar": [
@@ -97,10 +97,66 @@
"markdownDescription": "Specify if you want to automatically update the modified date of your article/page. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.autoupdatedate)",
"scope": "Content"
},
"frontMatter.content.defaultSorting": {
"type": "string",
"default": "",
"oneOf": [
{
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
]
},
{
"type": "string"
}
],
"markdownDescription": "Specify the default sorting option for the content dashboard. You can use one of the values from the enum or define your own ID. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.defaultSorting)",
"scope": "Content"
},
"frontMatter.content.draftField": {
"type": "object",
"markdownDescription": "Define the draft field you want to use to manage your content. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.draftfield)",
"default": {
"name": "draft",
"type": "boolean"
},
"properties": {
"type": {
"type": "string",
"enum": [
"boolean",
"choice"
],
"description": "Type of the draft field you want to use"
},
"name": {
"type": "string",
"description": "Name of the field to use"
},
"choices": {
"type": "array",
"description": "List of choices for the field",
"items": {
"type": [
"string"
]
}
}
},
"additionalProperties": false,
"required": [
"type",
"name"
],
"scope": "Content"
},
"frontMatter.content.fmHighlight": {
"type": "boolean",
"default": true,
"markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.content.fmhighlight)",
"markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.fmhighlight)",
"scope": "Content"
},
"frontMatter.content.pageFolders": {
@@ -117,6 +173,11 @@
"path": {
"type": "string",
"description": "Path of the folder"
},
"excludeSubdir": {
"type": "boolean",
"default": false,
"description": "Exclude sub-directories"
}
},
"additionalProperties": false,
@@ -133,6 +194,58 @@
"markdownDescription": "Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.publicfolder)",
"scope": "Content"
},
"frontMatter.content.sorting": {
"type": "array",
"default": [],
"markdownDescription": "Define the sorting options for your dashboard content. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.sorting)",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The ID of the sorting option. This will be used for the storing the last used sorting option or the default option."
},
"title": {
"type": "string",
"description": "Name of the sorting label"
},
"name": {
"type": "string",
"description": "Name of the metadata field to sort by"
},
"order": {
"type": "string",
"enum": [
"asc",
"desc"
],
"description": "Order of the sorting"
},
"type": {
"type": "string",
"default": "string",
"enum": [
"string",
"date"
],
"description": "Type of the field value"
}
},
"additionalProperties": false,
"required": [
"title",
"name",
"order"
]
},
"scope": "Content"
},
"frontMatter.content.wysiwyg": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies if you want to enable/disable the What You See, Is What You Get (WYSIWYG) markdown controls. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.wysiwyg)",
"scope": "Content"
},
"frontMatter.custom.scripts": {
"type": "array",
"default": [],
@@ -151,6 +264,32 @@
"nodeBin": {
"type": "string",
"description": "Path to the node executable. This is required when using NVM, so that there is no confusion of which node version to use."
},
"bulk": {
"type": "boolean",
"description": "Run the script for all content files"
},
"output": {
"type": "string",
"enum": [
"editor",
"notification"
],
"description": "Define where you want to output your script output. Default is a notification, but you can specify to show it in an editor panel."
},
"outputType": {
"type": "string",
"description": "The type of output for the editor panel. Can be used to change it to 'markdown' for example"
},
"type": {
"type": "string",
"default": "content",
"enum": [
"content",
"mediaFolder",
"mediaFile"
],
"description": "The type for which the script will be used."
}
},
"additionalProperties": false,
@@ -164,7 +303,7 @@
"frontMatter.dashboard.mediaSnippet": {
"type": "array",
"default": [],
"markdownDescription": "Specify the a snippet for your custom media insert markup. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.dashboard.mediaSnippet)",
"markdownDescription": "Specify the a snippet for your custom media insert markup. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.mediasnippet)",
"items": {
"type": "string",
"description": "The parts of your snippet. Use `{mediaUrl}` as placeholder where the path of the image needs to be inserted."
@@ -180,6 +319,23 @@
"markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.openonstart)",
"scope": "Dashboard"
},
"frontMatter.framework.id": {
"type": "string",
"default": "",
"markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.framework.id)"
},
"frontMatter.media.defaultSorting": {
"type": "string",
"default": "",
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
],
"markdownDescription": "Specify the default sorting option for the media dashboard. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.media.defaultsorting)",
"scope": "Content"
},
"frontMatter.panel.freeform": {
"type": "boolean",
"default": true,
@@ -198,6 +354,12 @@
"markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.preview.pathname)",
"scope": "Site preview"
},
"frontMatter.site.baseURL": {
"type": "string",
"default": "",
"markdownDescription": "Specify the base URL of your site, this will be used for SEO checks. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.site.baseurl)",
"scope": "Site"
},
"frontMatter.taxonomy.alignFilename": {
"type": "boolean",
"default": false,
@@ -214,7 +376,7 @@
},
"frontMatter.taxonomy.commaSeparatedFields": {
"type": "array",
"markdownDescription": "Specify the fields names that Front Matter should treat as a comma-separated array. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.taxonomy.commaSeparatedFields)",
"markdownDescription": "Specify the fields names that Front Matter should treat as a comma-separated array. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.commaSeparatedFields)",
"items": {
"type": "string",
"description": "Name of the fields you want to use as comma-separated arrays."
@@ -226,7 +388,7 @@
"array",
"null"
],
"markdownDescription": "Specify the type of contents you want to use for your articles/pages/etc. Make sure the `type` is correctly set in your front matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.taxonomy.contentTypes)",
"markdownDescription": "Specify the type of contents you want to use for your articles/pages/etc. Make sure the `type` is correctly set in your front matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.contentTypes)",
"items": {
"type": "object",
"description": "Define the content types you want to use in Front Matter.",
@@ -251,8 +413,10 @@
"boolean",
"image",
"choice",
"taxonomy",
"tags",
"categories"
"categories",
"draft"
],
"description": "Define the type of field"
},
@@ -268,16 +432,100 @@
"type": "array",
"description": "Define your choices",
"items": {
"type": "string"
"type": [
"object",
"string"
],
"properties": {
"id": {
"type": [
"null",
"string"
],
"description": "The choice ID"
},
"title": {
"type": "string",
"description": "The choice title"
}
}
}
},
"single": {
"type": "boolean",
"default": false,
"description": "Is a single line field"
},
"multiple": {
"type": "boolean",
"default": false,
"description": "Do you allow to select multiple values?"
},
"isPreviewImage": {
"type": "boolean",
"default": false,
"description": "Specify if the image field can be used as preview. Be aware, you can only have one preview image per content type."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Do you want to hide the field from the metadata section?"
},
"taxonomyId": {
"type": "string",
"default": "",
"description": "The ID of your taxonomy field"
}
},
"additionalProperties": false,
"required": [
"type",
"name"
],
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "taxonomy"
}
}
},
"then": {
"required": [
"taxonomyId"
]
}
},
{
"if": {
"properties": {
"type": {
"const": "choice"
}
}
},
"then": {
"required": [
"choices"
]
}
}
]
}
},
"pageBundle": {
"type": "boolean",
"default": false,
"description": "Specify if you want to create a folder when creating new content."
},
"previewPath": {
"type": [
"null",
"string"
],
"default": null,
"description": "Defines a custom preview path for the content type."
}
},
"additionalProperties": false,
@@ -289,6 +537,7 @@
"default": [
{
"name": "default",
"pageBundle": false,
"fields": [
{
"title": "Title",
@@ -306,7 +555,7 @@
"type": "datetime"
},
{
"title": "Article preview",
"title": "Content preview",
"name": "preview",
"type": "image"
},
@@ -330,6 +579,32 @@
],
"scope": "Taxonomy"
},
"frontMatter.taxonomy.customTaxonomy": {
"type": "array",
"markdownDescription": "Specify the custom taxonomy field data. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.tags)",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID for your taxonomy field"
},
"options": {
"type": "array",
"description": "Options from which you can pick",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"id",
"options"
]
},
"scope": "Taxonomy"
},
"frontMatter.taxonomy.dateField": {
"type": "string",
"default": "date",
@@ -393,6 +668,12 @@
"markdownDescription": "Specifies the optimal description length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seodescriptionlength)",
"scope": "Taxonomy"
},
"frontMatter.taxonomy.seoSlugLength": {
"type": "number",
"default": 75,
"markdownDescription": "Specifies the optimal slug length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seoSlugLength)",
"scope": "Taxonomy"
},
"frontMatter.taxonomy.seoTitleLength": {
"type": "number",
"default": 60,
@@ -419,7 +700,7 @@
},
"frontMatter.templates.folder": {
"type": "string",
"default": ".templates",
"default": ".frontmatter/templates",
"markdownDescription": "Specify the folder to use for your article templates. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.folder)",
"scope": "Templates"
},
@@ -428,6 +709,24 @@
"default": "yyyy-MM-dd",
"markdownDescription": "Specify the prefix you want to add for your new article filenames. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.prefix)",
"scope": "Templates"
},
"frontMatter.global.notifications": {
"type": "array",
"items": {
"type": "string",
"enum": [
"info",
"warning",
"error"
]
},
"default": [
"info",
"warning",
"error"
],
"markdownDescription": "Specifies the notifications you want to see. By default, all notifications types will be shown. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.global.notifications)",
"scope": "Templates"
}
}
},
@@ -475,7 +774,7 @@
},
{
"command": "frontMatter.generateSlug",
"title": "Generate slug based on article title",
"title": "Generate slug based on content title",
"category": "Front matter"
},
{
@@ -490,9 +789,12 @@
},
{
"command": "frontMatter.insertImage",
"title": "Insert image into article",
"title": "Insert image into your content",
"category": "Front matter",
"icon": "$(device-camera)"
"icon": {
"dark": "/assets/icons/media-dark.svg",
"light": "/assets/icons/media-light.svg"
}
},
{
"command": "frontMatter.insertTags",
@@ -501,17 +803,39 @@
},
{
"command": "frontMatter.createContent",
"title": "New article from template",
"title": "Create new content from defined content type or template",
"category": "Front matter"
},
{
"command": "frontMatter.dashboard",
"title": "Open dashboard",
"category": "Front matter"
"category": "Front matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
}
},
{
"command": "frontMatter.dashboard.media",
"title": "Open media dashboard",
"category": "Front matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
}
},
{
"command": "frontMatter.dashboard.close",
"title": "Close dashboard",
"category": "Front matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-teal.svg",
"light": "/assets/icons/frontmatter-small-teal.svg"
}
},
{
"command": "frontMatter.preview",
"title": "Preview article",
"title": "Preview content",
"category": "Front matter"
},
{
@@ -528,14 +852,179 @@
"command": "frontMatter.setLastModifiedDate",
"title": "Set lastmod date",
"category": "Front matter"
},
{
"command": "frontMatter.markup.bold",
"title": "Bold",
"category": "Front matter",
"icon": {
"light": "assets/icons/bold-light.svg",
"dark": "assets/icons/bold-dark.svg"
}
},
{
"command": "frontMatter.markup.italic",
"title": "Italic",
"category": "Front matter",
"icon": {
"light": "assets/icons/italic-light.svg",
"dark": "assets/icons/italic-dark.svg"
}
},
{
"command": "frontMatter.markup.strikethrough",
"title": "Strikethrough",
"category": "Front matter",
"icon": {
"light": "assets/icons/strikethrough-light.svg",
"dark": "assets/icons/strikethrough-dark.svg"
}
},
{
"command": "frontMatter.markup.code",
"title": "Code",
"category": "Front matter",
"icon": {
"light": "assets/icons/code-light.svg",
"dark": "assets/icons/code-dark.svg"
}
},
{
"command": "frontMatter.markup.codeblock",
"title": "Codeblock",
"category": "Front matter",
"icon": {
"light": "assets/icons/codeblock-light.svg",
"dark": "assets/icons/codeblock-dark.svg"
}
},
{
"command": "frontMatter.markup.blockquote",
"title": "Codeblock",
"category": "Front matter",
"icon": {
"light": "assets/icons/blockquote-light.svg",
"dark": "assets/icons/blockquote-dark.svg"
}
},
{
"command": "frontMatter.markup.heading",
"title": "Heading",
"category": "Front matter",
"icon": {
"light": "assets/icons/heading-light.svg",
"dark": "assets/icons/heading-dark.svg"
}
},
{
"command": "frontMatter.markup.unorderedlist",
"title": "Unordered list",
"category": "Front matter",
"icon": {
"light": "assets/icons/unordered-list-light.svg",
"dark": "assets/icons/unordered-list-dark.svg"
}
},
{
"command": "frontMatter.markup.orderedlist",
"title": "Ordered list",
"category": "Front matter",
"icon": {
"light": "assets/icons/ordered-list-light.svg",
"dark": "assets/icons/ordered-list-dark.svg"
}
},
{
"command": "frontMatter.markup.tasklist",
"title": "Task list",
"category": "Front matter"
},
{
"command": "frontMatter.markup.options",
"title": "Other markup options",
"category": "Front matter",
"icon": {
"light": "assets/icons/options-light.svg",
"dark": "assets/icons/options-dark.svg"
}
},
{
"command": "frontMatter.diagnostics",
"title": "Diagnostic logging",
"category": "Front matter"
}
],
"menus": {
"editor/title": [
{
"when": "resourceLangId == markdown",
"command": "frontMatter.markup.heading",
"group": "navigation@-132",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.bold",
"group": "navigation@-131",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.italic",
"group": "navigation@-130",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.strikethrough",
"group": "navigation@-129",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.blockquote",
"group": "navigation@-128",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.insertImage",
"group": "navigation"
"group": "navigation@-127",
"when": "resourceLangId == markdown"
},
{
"command": "frontMatter.markup.options",
"group": "navigation@-126",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.orderedlist",
"group": "1_markup@1",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.unorderedlist",
"group": "1_markup@2",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.tasklist",
"group": "1_markup@3",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.code",
"group": "1_markup@4",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.markup.codeblock",
"group": "1_markup@5",
"when": "resourceLangId == markdown && frontMatter:markdown:wysiwyg"
},
{
"command": "frontMatter.dashboard",
"group": "navigation@-98",
"when": "frontMatter:enabled == true && frontMatter:dashboard:open == false"
},
{
"command": "frontMatter.dashboard.close",
"group": "navigation@-98",
"when": "frontMatter:enabled == true && frontMatter:dashboard:open == true"
}
],
"explorer/context": [
@@ -583,12 +1072,60 @@
{
"command": "frontMatter.createFromTemplate",
"when": "false"
},
{
"command": "frontMatter.dashboard.close",
"when": "false"
},
{
"command": "frontMatter.markup.bold",
"when": "false"
},
{
"command": "frontMatter.markup.italic",
"when": "false"
},
{
"command": "frontMatter.markup.strikethrough",
"when": "false"
},
{
"command": "frontMatter.markup.code",
"when": "false"
},
{
"command": "frontMatter.markup.codeblock",
"when": "false"
},
{
"command": "frontMatter.markup.blockquote",
"when": "false"
},
{
"command": "frontMatter.markup.heading",
"when": "false"
},
{
"command": "frontMatter.markup.unorderedlist",
"when": "false"
},
{
"command": "frontMatter.markup.orderedlist",
"when": "false"
},
{
"command": "frontMatter.markup.tasklist",
"when": "false"
},
{
"command": "frontMatter.markup.options",
"when": "false"
}
],
"view/title": [
{
"command": "frontMatter.collapseSections",
"group": "navigation"
"group": "frontmatter-explorer"
}
]
},
@@ -603,9 +1140,15 @@
]
},
"scripts": {
"vscode:prepublish": "npm run clean && webpack --mode production",
"build:ext": "npm run clean && webpack --mode development",
"dev:ext": "npm run clean && webpack --mode development --watch",
"dev:ext": "npm run clean && npm-run-all --parallel watch:*",
"vscode:prepublish": "npm run clean && npm-run-all --parallel prod:*",
"build:ext": "npm run clean && npm-run-all --parallel dev:build:*",
"watch:ext": "webpack --mode development --watch --config ./webpack/extension.config.js",
"watch:dashboard": "webpack serve --mode development --config ./webpack/dashboard.config.js",
"dev:build:ext": "webpack --mode development --config ./webpack/extension.config.js",
"dev:build:dashboard": "webpack --mode development --config ./webpack/dashboard.config.js",
"prod:ext": "webpack --mode production --config ./webpack/extension.config.js",
"prod:dashboard": "webpack --mode production --config ./webpack/dashboard.config.js",
"test-compile": "tsc -p ./",
"clean": "rimraf dist",
"start:site": "cd ./docs && npm run dev"
@@ -616,6 +1159,8 @@
"@headlessui/react": "^1.4.1",
"@heroicons/react": "1.0.4",
"@iarna/toml": "2.2.3",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@tailwindcss/forms": "^0.3.3",
"@types/glob": "7.1.3",
"@types/js-yaml": "3.12.1",
@@ -625,8 +1170,10 @@
"@types/react": "17.0.0",
"@types/react-datepicker": "^4.1.7",
"@types/react-dom": "17.0.0",
"@types/vscode": "1.51.0",
"@types/vscode": "^1.63.0",
"@vscode/codicons": "0.0.20",
"@vscode/webview-ui-toolkit": "^0.8.1",
"@webpack-cli/serve": "^1.6.0",
"autoprefixer": "^10.3.2",
"css-loader": "5.2.7",
"date-fns": "2.23.0",
@@ -636,8 +1183,13 @@
"gray-matter": "4.0.2",
"html-loader": "1.3.2",
"html-webpack-plugin": "4.5.0",
"image-size": "^1.0.0",
"lodash-es": "^4.17.21",
"lodash.uniqby": "4.7.0",
"mdast-util-from-markdown": "1.0.0",
"node-json-db": "^1.3.0",
"npm-run-all": "^4.1.5",
"path-browserify": "^1.0.1",
"postcss": "^8.3.6",
"postcss-loader": "4.3.0",
"react": "17.0.1",
@@ -651,11 +1203,11 @@
"ts-loader": "8.0.3",
"tslint": "6.1.3",
"typescript": "4.0.2",
"url-join-ts": "^1.0.5",
"wc-react": "github:estruyf/wc-react",
"webpack": "4.44.2",
"webpack-cli": "3.3.12"
},
"dependencies": {
"@docsearch/js": "^3.0.0-alpha.40"
"webpack": "^5.65.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.6.0"
}
}

View File

@@ -1,15 +1,17 @@
import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX } from './../constants/settings';
import { isValidFile } from './../helpers/isValidFile';
import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX, CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX } from './../constants';
import * as vscode from 'vscode';
import { TaxonomyType } from "../models";
import { CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX } from "../constants/settings";
import { format } from "date-fns";
import { ArticleHelper, Settings, SlugHelper } from '../helpers';
import matter = require('gray-matter');
import { Notifications } from '../helpers/Notifications';
import { extname, basename } from 'path';
import { extname, basename, parse, dirname } from 'path';
import { COMMAND_NAME, DefaultFields } from '../constants';
import { DashboardData } from '../models/DashboardData';
import { ExplorerView } from '../explorerView/ExplorerView';
import { DateHelper } from '../helpers/DateHelper';
import { parseWinPath } from '../helpers/parseWinPath';
export class Article {
@@ -172,7 +174,7 @@ export class Article {
let newFileName = `${slugName}${ext}`;
if (filePrefix && typeof filePrefix === "string") {
newFileName = `${format(new Date(), filePrefix)}-${newFileName}`;
newFileName = `${format(new Date(), DateHelper.formatUpdate(filePrefix) as string)}-${newFileName}`;
}
const newPath = editor.document.uri.fsPath.replace(fileName, newFileName);
@@ -183,7 +185,7 @@ export class Article {
await vscode.workspace.fs.rename(editor.document.uri, vscode.Uri.file(newPath), {
overwrite: false
});
} catch (e) {
} catch (e: any) {
Notifications.error(`Failed to rename file: ${e?.message || e}`);
}
}
@@ -191,6 +193,31 @@ export class Article {
}
}
/**
* Retrieve the slug from the front matter
*/
public static getSlug() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const file = parseWinPath(editor.document.fileName);
if (!isValidFile(file)) {
return;
}
const parsedFile = parse(file);
if (parsedFile.name.toLowerCase() !== "index") {
return parsedFile.name;
}
const folderName = basename(dirname(file));
return folderName;
}
/**
* Toggle the page its draft mode
*/
@@ -240,13 +267,13 @@ export class Article {
/**
* Format the date to the defined format
*/
public static formatDate(dateValue: Date) {
public static formatDate(dateValue: Date): string {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
if (dateFormat && typeof dateFormat === "string") {
return format(dateValue, dateFormat);
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
} else {
return dateValue.toISOString();
return typeof dateValue.toISOString === 'function' ? dateValue.toISOString() : dateValue?.toString();
}
}

31
src/commands/Content.ts Normal file
View File

@@ -0,0 +1,31 @@
import { commands, QuickPickItem, window } from 'vscode';
import { COMMAND_NAME } from '../constants';
export class Content {
public static async create() {
const options: QuickPickItem[] = [{
label: "Create content by content type",
description: "Select if you want to create new content by the available content type(s)"
}, {
label: "Create content by template",
description: "Select if you want to create new content by the available template(s)"
} as QuickPickItem];
const selectedOption = await window.showQuickPick(options, {
placeHolder: `Select how you want to create your new content`,
canPickMany: false
});
if (selectedOption) {
if (selectedOption.label === options[0].label) {
commands.executeCommand(COMMAND_NAME.createByContentType);
} else {
commands.executeCommand(COMMAND_NAME.createByTemplate);
}
}
return;
}
}

View File

@@ -1,42 +1,27 @@
import { SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET } from './../constants/settings';
import { ArticleHelper } from './../helpers/ArticleHelper';
import { basename, dirname, extname, join } from "path";
import { existsSync, statSync, unlinkSync, writeFileSync } from "fs";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env, Position } from "vscode";
import { Settings as SettingsHelper } from '../helpers';
import { TaxonomyType } from '../models';
import { Folders } from './Folders';
import { PagesListener } from './../listeners/PagesListener';
import { ExtensionListener } from './../listeners/ExtensionListener';
import { SETTINGS_DASHBOARD_OPENONSTART, CONTEXT } from '../constants';
import { join } from "path";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from "vscode";
import { Logger, Settings as SettingsHelper } from '../helpers';
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
import { Page } from '../dashboardWebView/models/Page';
import { openFileInEditor } from '../helpers/openFileInEditor';
import { COMMAND_NAME, EXTENSION_STATE_PAGES_VIEW } from '../constants/Extension';
import { Template } from './Template';
import { Notifications } from '../helpers/Notifications';
import { Settings } from '../dashboardWebView/models/Settings';
import { Extension } from '../helpers/Extension';
import { parseJSON } from 'date-fns';
import { ViewType } from '../dashboardWebView/state';
import { EditorHelper, WebviewHelper } from '@estruyf/vscode';
import { MediaInfo, MediaPaths } from './../models/MediaPaths';
import { decodeBase64Image } from '../helpers/decodeBase64Image';
import { DefaultFields } from '../constants';
import { WebviewHelper } from '@estruyf/vscode';
import { DashboardData } from '../models/DashboardData';
import { ExplorerView } from '../explorerView/ExplorerView';
import { MediaLibrary } from '../helpers/MediaLibrary';
import { DashboardListener, MediaListener, SettingsListener } from '../listeners';
export class Dashboard {
private static webview: WebviewPanel | null = null;
private static isDisposed: boolean = true;
private static media: MediaInfo[] = [];
private static timers: { [folder: string]: any } = {};
private static _viewData: DashboardData | undefined;
private static isDisposed: boolean = true;
public static get viewData(): DashboardData | undefined {
return Dashboard._viewData;
}
/** 
/**
* Init the dashboard
*/
public static async init() {
@@ -50,13 +35,17 @@ export class Dashboard {
* Open or reveal the dashboard
*/
public static async open(data?: DashboardData) {
MediaLibrary.getInstance();
Dashboard._viewData = data;
if (Dashboard.isOpen) {
Dashboard.reveal();
Dashboard.reveal(!!data);
} else {
Dashboard.create();
}
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, true);
}
/**
@@ -69,11 +58,33 @@ export class Dashboard {
/**
* Reveal the dashboard if it is open
*/
public static reveal() {
public static reveal(hasData: boolean = false) {
if (Dashboard.webview) {
Dashboard.webview.reveal();
if (hasData) {
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard.viewData });
}
}
}
public static close() {
Dashboard.webview?.dispose();
}
public static reload() {
if (Dashboard.isOpen) {
Dashboard.webview?.dispose();
setTimeout(() => {
Dashboard.open();
}, 100);
}
}
public static resetViewData() {
Dashboard._viewData = undefined;
}
/**
* Create the dashboard webview
@@ -87,399 +98,68 @@ export class Dashboard {
'FrontMatter Dashboard',
ViewColumn.One,
{
enableScripts: true
enableScripts: true,
retainContextWhenHidden: true
}
);
Dashboard.isDisposed = false;
Dashboard.webview.iconPath = {
dark: Uri.file(join(extensionUri.fsPath, 'assets/frontmatter-dark.svg')),
light: Uri.file(join(extensionUri.fsPath, 'assets/frontmatter.svg'))
dark: Uri.file(join(extensionUri.fsPath, 'assets/icons/frontmatter-short-dark.svg')),
light: Uri.file(join(extensionUri.fsPath, 'assets/icons/frontmatter-short-light.svg'))
};
Dashboard.webview.webview.html = Dashboard.getWebviewContent(Dashboard.webview.webview, extensionUri);
Dashboard.webview.onDidChangeViewState(() => {
Dashboard.webview.onDidChangeViewState(async () => {
if (!this.webview?.visible) {
Dashboard._viewData = undefined;
const panel = ExplorerView.getInstance(extensionUri);
panel.getMediaSelection();
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: null });
}
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, this.webview?.visible);
});
Dashboard.webview.onDidDispose(() => {
Dashboard.webview.onDidDispose(async () => {
Dashboard.isDisposed = true;
Dashboard._viewData = undefined;
const panel = ExplorerView.getInstance(extensionUri);
panel.getMediaSelection();
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false);
});
SettingsHelper.onConfigChange((global?: any) => {
Dashboard.getSettings();
SettingsListener.getSettings();
});
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
switch(msg.command) {
case DashboardMessage.getViewType:
if (Dashboard._viewData) {
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard._viewData });
}
break;
case DashboardMessage.getData:
Dashboard.getSettings();
Dashboard.getPages();
break;
case DashboardMessage.openFile:
openFileInEditor(msg.data);
break;
case DashboardMessage.createContent:
await commands.executeCommand(COMMAND_NAME.createContent);
break;
case DashboardMessage.updateSetting:
Dashboard.updateSetting(msg.data);
break;
case DashboardMessage.initializeProject:
await commands.executeCommand(COMMAND_NAME.init, Dashboard.getSettings);
break;
case DashboardMessage.reload:
if (!Dashboard.isDisposed) {
Dashboard.webview?.dispose();
setTimeout(() => {
Dashboard.open();
}, 100);
}
break;
case DashboardMessage.setPageViewType:
Extension.getInstance().setState(EXTENSION_STATE_PAGES_VIEW, msg.data);
break;
case DashboardMessage.getMedia:
Dashboard.getMedia(msg?.data?.page, msg?.data?.folder)
break;
case DashboardMessage.copyToClipboard:
env.clipboard.writeText(msg.data);
break;
case DashboardMessage.refreshMedia:
Dashboard.media = [];
Dashboard.getMedia(0, msg?.data?.folder);
break;
case DashboardMessage.uploadMedia:
Dashboard.saveFile(msg?.data);
break;
case DashboardMessage.deleteMedia:
Dashboard.deleteFile(msg?.data);
break;
case DashboardMessage.insertPreviewImage:
Dashboard.insertImage(msg?.data);
break;
}
});
}
/**
* Insert an image into the front matter or contents
* @param data
*/
private static async insertImage(data: any) {
if (data?.file && data?.image) {
if (!data?.position) {
await commands.executeCommand(`workbench.view.extension.frontmatter-explorer`);
}
await EditorHelper.showFile(data.file);
Dashboard._viewData = undefined;
Logger.info(`Receiving message from webview: ${msg.command}`);
const extensionUri = Extension.getInstance().extensionPath;
const panel = ExplorerView.getInstance(extensionUri);
if (data?.position) {
const editor = window.activeTextEditor;
const line = data.position.line;
const character = data.position.character;
if (line) {
await editor?.edit(builder => builder.insert(new Position(line, character), data.snippet || `![](${data.image})`));
}
panel.getMediaSelection();
} else {
panel.getMediaSelection();
panel.updateMetadata({field: data.fieldName, value: data.image});
}
}
}
/**
* Retrieve the settings for the dashboard
*/
private static async getSettings() {
const ext = Extension.getInstance();
const wsFolder = Folders.getWorkspaceFolder();
Dashboard.postWebviewMessage({
command: DashboardCommand.settings,
data: {
beta: ext.isBetaVersion(),
wsFolder: wsFolder ? wsFolder.fsPath : '',
staticFolder: SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS),
folders: Folders.get(),
initialized: await Template.isInitialized(),
tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag),
categories: SettingsHelper.getTaxonomy(TaxonomyType.Category),
openOnStart: SettingsHelper.get(SETTINGS_DASHBOARD_OPENONSTART),
versionInfo: ext.getVersion(),
pageViewType: await ext.getState<ViewType | undefined>(EXTENSION_STATE_PAGES_VIEW),
mediaSnippet: SettingsHelper.get<string[]>(SETTINGS_DASHBOARD_MEDIA_SNIPPET) || [],
} as Settings
DashboardListener.process(msg);
ExtensionListener.process(msg);
MediaListener.process(msg);
PagesListener.process(msg);
SettingsListener.process(msg);
});
}
/**
* Update a setting from the dashboard
* Return the webview
* @returns The webview
*/
private static async updateSetting(data: { name: string, value: any }) {
await SettingsHelper.update(data.name, data.value);
Dashboard.getSettings();
}
/**
* Retrieve all media files
*/
private static async getMedia(page: number = 0, folder: string = '') {
const wsFolder = Folders.getWorkspaceFolder();
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
if (Dashboard.media.length === 0) {
const contentFolder = Folders.get();
let allMedia: MediaInfo[] = [];
if (staticFolder) {
const files = await workspace.findFiles(`${staticFolder || ""}/**/*`);
const media = Dashboard.filterMedia(files);
allMedia = [...media];
}
if (contentFolder && wsFolder) {
for (let i = 0; i < contentFolder.length; i++) {
const folder = contentFolder[i];
const relFolderPath = folder.path.substring(wsFolder.fsPath.length + 1);
const files = await workspace.findFiles(`${relFolderPath}/**/*`);
const media = Dashboard.filterMedia(files);
allMedia = [...allMedia, ...media];
}
}
allMedia = allMedia.sort((a, b) => {
if (b.fsPath < a.fsPath) {
return -1;
}
if (b.fsPath > a.fsPath) {
return 1;
}
return 0;
});
Dashboard.media = Object.assign([], allMedia);
}
// Filter the media
let files: MediaInfo[] = Dashboard.media;
if (folder) {
files = files.filter(f => f.fsPath.includes(folder));
}
// Retrieve the total after filtering and before the slicing happens
const total = files.length;
// Get media set
files = files.slice(page * 16, ((page + 1) * 16));
files = files.map((file) => {
try {
return {
...file,
stats: statSync(file.fsPath)
};
} catch (e) {
return {...file, stats: undefined};
}
}).filter(f => f.stats !== undefined);
const folders = [...new Set(Dashboard.media.map((file) => {
let relFolderPath = wsFolder ? file.fsPath.substring(wsFolder.fsPath.length + 1) : file.fsPath;
if (staticFolder && relFolderPath.startsWith(staticFolder)) {
relFolderPath = relFolderPath.substring(staticFolder.length);
}
if (relFolderPath?.startsWith('/')) {
relFolderPath = relFolderPath.substring(1);
}
return dirname(relFolderPath);
}))];
Dashboard.postWebviewMessage({
command: DashboardCommand.media,
data: {
media: files,
total: total,
folders
} as MediaPaths
});
}
/**
* Retrieve all the markdown pages
*/
private static async getPages() {
const wsFolder = Folders.getWorkspaceFolder();
const descriptionField = SettingsHelper.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description;
const dateField = SettingsHelper.get(SETTING_DATE_FIELD) as string || DefaultFields.PublishingDate;
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const folderInfo = await Folders.getInfo();
const pages: Page[] = [];
if (folderInfo) {
for (const folder of folderInfo) {
for (const file of folder.lastModified) {
if (file.fileName.endsWith(`.md`) || file.fileName.endsWith(`.mdx`)) {
try {
const article = ArticleHelper.getFrontMatterByPath(file.filePath);
if (article?.data.title) {
const page: Page = {
...article.data,
// FrontMatter properties
fmFolder: folder.title,
fmModified: file.mtime,
fmFilePath: file.filePath,
fmFileName: file.fileName,
fmDraft: article?.data.draft ? "Draft" : "Published",
fmYear: article?.data[dateField] ? parseJSON(article?.data[dateField]).getFullYear() : null,
// Make sure these are always set
title: article?.data.title,
slug: article?.data.slug,
date: article?.data[dateField] || "",
draft: article?.data.draft,
description: article?.data[descriptionField] || "",
};
if (article?.data.preview && wsFolder) {
const staticPath = join(wsFolder.fsPath, staticFolder || "", article?.data.preview);
const contentFolderPath = join(dirname(file.filePath), article?.data.preview);
let previewUri = null;
if (existsSync(staticPath)) {
previewUri = Uri.file(staticPath);
} else if (existsSync(contentFolderPath)) {
previewUri = Uri.file(contentFolderPath);
}
if (previewUri) {
const preview = Dashboard.webview?.webview.asWebviewUri(previewUri);
page.preview = preview?.toString() || "";
} else {
page.preview = "";
}
}
pages.push(page);
}
} catch (error: any) {
Notifications.error(`File error: ${file.filePath} - ${error?.message || error}`);
}
}
}
}
}
Dashboard.postWebviewMessage({
command: DashboardCommand.pages,
data: pages
});
}
/**
* Filter the media files
*/
private static filterMedia(files: Uri[]) {
return files.filter(file => {
const ext = extname(file.fsPath);
return ['.jpg', '.jpeg', '.png', '.gif', '.svg'].includes(ext);
}).map((file) => ({
fsPath: file.fsPath,
vsPath: Dashboard.webview?.webview.asWebviewUri(file).toString(),
stats: undefined
} as MediaInfo));
}
/**
* Save the dropped file in the current folder
* @param fileData
*/
private static async saveFile({fileName, contents, folder}: { fileName: string; contents: string; folder: string | null }) {
if (fileName && contents) {
const wsFolder = Folders.getWorkspaceFolder();
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const wsPath = wsFolder ? wsFolder.fsPath : "";
let absFolderPath = join(wsPath, staticFolder || "", folder || "");
if (!existsSync(absFolderPath)) {
absFolderPath = join(wsPath, folder || "");
}
if (!existsSync(absFolderPath)) {
Notifications.error(`We couldn't find your selected folder.`);
return;
}
const staticPath = join(absFolderPath, fileName);
const imgData = decodeBase64Image(contents);
if (imgData) {
writeFileSync(staticPath, imgData.data);
Notifications.info(`File ${fileName} uploaded to: ${staticFolder}/${folder}`);
const folderPath = `${staticFolder}/${folder}`;
if (Dashboard.timers[folderPath]) {
clearTimeout(Dashboard.timers[folderPath]);
delete Dashboard.timers[folderPath];
}
Dashboard.timers[folderPath] = setTimeout(() => {
Dashboard.media = [];
Dashboard.getMedia(0, folder || "");
delete Dashboard.timers[folderPath];
}, 500);
} else {
Notifications.error(`Something went wrong uploading ${fileName}`);
}
}
}
/**
* Delete the selected file
* @param fileData
*/
private static async deleteFile({ file, page, folder }: { file: string; page: number; folder: string | null; }) {
if (!file) {
return;
}
try {
unlinkSync(file);
Dashboard.media = [];
Dashboard.getMedia(page || 0, folder || "");
} catch(err) {
Notifications.error(`Something went wrong deleting ${basename(file)}`);
}
public static getWebview() {
return Dashboard.webview?.webview;
}
/**
* Post data to the dashboard
* @param msg
*/
private static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
public static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
if (Dashboard.isDisposed) {
return;
}
@@ -494,27 +174,48 @@ export class Dashboard {
* @param webView
*/
private static getWebviewContent(webView: Webview, extensionPath: Uri): string {
const scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', 'pages.js'));
const dashboardFile = "dashboardWebView.js";
const localServerUrl = "http://localhost:9000";
let scriptUri = "";
const isProd = Extension.getInstance().isProductionMode;
if (isProd) {
scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile)).toString();
} else {
scriptUri = `${localServerUrl}/${dashboardFile}`;
}
const nonce = WebviewHelper.getNonce();
const version = Extension.getInstance().getVersion();
const ext = Extension.getInstance();
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
const csp = [
`default-src 'none';`,
`img-src ${`vscode-file://vscode-app`} ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'`,
`script-src ${isProd ? `'nonce-${nonce}'` : "http://localhost:9000 http://0.0.0.0:9000"}`,
`style-src ${webView.cspSource} 'self' 'unsafe-inline'`,
`font-src ${webView.cspSource}`,
`connect-src https://o1022172.ingest.sentry.io ${isProd ? `` : "ws://localhost:9000 ws://0.0.0.0:9000 http://localhost:9000 http://0.0.0.0:9000"}`
];
return `
<!DOCTYPE html>
<html lang="en" style="width:100%;height:100%;margin:0;padding:0;">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${`vscode-file://vscode-app`} ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'; script-src 'nonce-${nonce}'; style-src ${webView.cspSource} 'self' 'unsafe-inline'; font-src ${webView.cspSource}">
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="${csp.join('; ')}">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Front Matter Dashboard</title>
</head>
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden" class="bg-gray-100 text-vulcan-500 dark:bg-vulcan-500 dark:text-whisper-500">
<div id="app" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
<div id="app" data-isProd="${isProd}" data-environment="${isBeta ? "BETA" : "main"}" data-version="${version.usedVersion}" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`dashboard-${version.installedVersion}`}" alt="Daily usage" />
<script nonce="${nonce}" src="${scriptUri}"></script>
<script ${isProd ? `nonce="${nonce}"` : ""} src="${scriptUri}"></script>
</body>
</html>
`;

View File

@@ -0,0 +1,63 @@
import { Folders } from "./Folders";
import { ViewColumn, workspace } from "vscode";
import ContentProvider from "../providers/ContentProvider";
import { join } from "path";
import { ContentFolder } from "../models";
export class Diagnostics {
public static async show() {
const folders = Folders.get();
const projectName = Folders.getProjectFolderName();
const wsFolder = Folders.getWorkspaceFolder();
const folderData = [];
for (const folder of folders) {
folderData.push(await Diagnostics.processFolder(folder, projectName));
}
const all = await Diagnostics.allProjectFiles();
const logging = `# Project name
${projectName}
# Folders
${folders.map(f => `- ${f.title}: "${f.path}"`).join("\n")}
# Workspace folder
${wsFolder ? wsFolder.fsPath : "No workspace folder"}
# Total files
${all}
# Folders to search files
${folderData.join("\n")}
`;
ContentProvider.show(logging, `${projectName} diagnostics`, "markdown", ViewColumn.One);
}
private static async allProjectFiles() {
const allFiles = await workspace.findFiles(`**/*.*`);
return `Total files found: ${allFiles.length}`;
}
private static async processFolder(folder: ContentFolder, projectName: string) {
let projectStart = folder.path.split(projectName).pop();
projectStart = projectStart || "";
projectStart = projectStart?.replace(/\\/g, '/');
projectStart = projectStart?.startsWith('/') ? projectStart.substr(1) : projectStart;
const mdFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md'));
const mdxFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx'));
const markdownFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown'));
return `- Project start length: ${projectStart.length} | Search in: "${join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.*')}" | mdFiles: ${mdFiles.length} | mdxFiles: ${mdxFiles.length} | markdownFiles: ${markdownFiles.length}`;
}
}

View File

@@ -1,43 +1,85 @@
import { SETTINGS_CONTENT_PAGE_FOLDERS } from './../constants/settings';
import { Questions } from './../helpers/Questions';
import { SETTINGS_CONTENT_PAGE_FOLDERS, SETTINGS_CONTENT_STATIC_FOLDER } from './../constants';
import { commands, Uri, workspace, window } from "vscode";
import { basename, join } from "path";
import { ContentFolder, FileInfo, FolderInfo } from "../models";
import uniqBy = require("lodash.uniqby");
import { Template } from "./Template";
import { Notifications } from "../helpers/Notifications";
import { CONTEXT } from "../constants/context";
import { Settings } from "../helpers";
import { existsSync, mkdirSync } from 'fs';
import { format } from 'date-fns';
import { Dashboard } from './Dashboard';
import { parseWinPath } from '../helpers/parseWinPath';
import { MediaHelpers } from '../helpers/MediaHelpers';
import { MediaListener, PagesListener } from '../listeners';
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
export class Folders {
/**
* Add a media folder
* @returns
*/
public static async addMediaFolder(data?: {selectedFolder?: string}) {
let wsFolder = Folders.getWorkspaceFolder();
const staticFolder = Settings.get<string>(SETTINGS_CONTENT_STATIC_FOLDER);
let startPath = "";
if (data?.selectedFolder) {
startPath = data.selectedFolder.replace(parseWinPath(wsFolder?.fsPath || ""), "");
} else if (staticFolder) {
startPath = `/${staticFolder}`;
}
if (startPath && !startPath.endsWith("/")) {
startPath += "/";
}
const folderName = await window.showInputBox({
prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`,
value: startPath,
ignoreFocusOut: true,
placeHolder: `${format(new Date(), `yyyy/MM`)}`
});
if (!folderName) {
Notifications.warning(`No folder name was specified.`);
return;
}
const folders = folderName.split("/").filter(f => f);
let parentFolders: string[] = [];
for (const folder of folders) {
const folderPath = join(parseWinPath(wsFolder?.fsPath || ""), parentFolders.join("/"), folder);
parentFolders.push(folder);
if (!existsSync(folderPath)) {
mkdirSync(folderPath);
}
}
if (Dashboard.isOpen) {
MediaHelpers.resetMedia();
MediaListener.sendMediaFiles(0, folderName);
}
}
/**
* Create content in a registered folder
* @returns
*/
public static async create() {
const folders = Folders.get();
if (!folders || folders.length === 0) {
Notifications.warning(`There are no known content locations defined in this project.`);
return;
}
let selectedFolder: string | undefined;
if (folders.length > 1) {
selectedFolder = await window.showQuickPick(folders.map(f => f.title), {
placeHolder: `Select where you want to create your content`
});
} else {
selectedFolder = folders[0].title;
}
const selectedFolder = await Questions.SelectContentFolder();
if (!selectedFolder) {
Notifications.warning(`You didn't select a place where you wanted to create your content.`);
return;
}
const folders = Folders.get();
const location = folders.find(f => f.title === selectedFolder);
if (location) {
const folderPath = Folders.getFolderPath(Uri.file(location.path));
@@ -115,9 +157,33 @@ export class Folders {
*/
public static getWorkspaceFolder(): Uri | undefined {
const folders = workspace.workspaceFolders;
if (folders && folders.length > 0) {
if (folders && folders.length === 1) {
return folders[0].uri;
} else if (folders && folders.length > 1) {
let projectFolder = undefined;
for (const folder of folders) {
if (!projectFolder && existsSync(join(folder.uri.fsPath, Settings.globalFile))) {
projectFolder = folder.uri;
}
}
if (!projectFolder) {
window.showWorkspaceFolderPick({
placeHolder: `Please select the main workspace folder for Front Matter to use.`
}).then(selectedFolder => {
if (selectedFolder) {
Settings.createGlobalFile(selectedFolder.uri);
// Full reload to make sure the whole extension is reloaded correctly
commands.executeCommand(`workbench.action.reloadWindow`);
}
});
}
return projectFolder;
}
return undefined;
}
@@ -127,7 +193,6 @@ export class Folders {
public static getProjectFolderName(): string {
const wsFolder = Folders.getWorkspaceFolder();
if (wsFolder) {
// const projectFolder = wsFolder?.fsPath.split('\\').join('/').split('/').pop();
return basename(wsFolder.fsPath);
}
return "";
@@ -145,19 +210,23 @@ export class Folders {
try {
const projectName = Folders.getProjectFolderName();
let projectStart = folder.path.split(projectName).pop();
if (projectStart) {
projectStart = projectStart.replace(/\\/g, '/');
projectStart = projectStart.startsWith('/') ? projectStart.substr(1) : projectStart;
const mdFiles = await workspace.findFiles(join(projectStart, '**/*.md'));
const mdxFiles = await workspace.findFiles(join(projectStart, '**/*.mdx'));
let files = [...mdFiles, ...mdxFiles];
const mdFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md'));
const markdownFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown'));
const mdxFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx'));
let files = [...mdFiles, ...markdownFiles, ...mdxFiles];
if (files) {
let fileStats: FileInfo[] = [];
for (const file of files) {
try {
const fileName = basename(file.fsPath);
const stats = await workspace.fs.stat(file);
fileStats.push({
filePath: file.fsPath,
fileName,
@@ -201,7 +270,7 @@ export class Folders {
const folders: ContentFolder[] = Settings.get(SETTINGS_CONTENT_PAGE_FOLDERS) as ContentFolder[];
return folders.map(folder => ({
title: folder.title,
...folder,
path: Folders.absWsFolder(folder, wsFolder)
}));
}
@@ -212,7 +281,16 @@ export class Folders {
*/
private static async update(folders: ContentFolder[]) {
const wsFolder = Folders.getWorkspaceFolder();
await Settings.update(SETTINGS_CONTENT_PAGE_FOLDERS, folders.map(folder => ({ title: folder.title, path: Folders.relWsFolder(folder, wsFolder) })));
let folderDetails = folders.map(folder => ({
...folder,
path: Folders.relWsFolder(folder, wsFolder)
}));
await Settings.update(SETTINGS_CONTENT_PAGE_FOLDERS, folderDetails, true);
// Reinitialize the folder listeners
PagesListener.startWatchers();
}
/**
@@ -223,7 +301,7 @@ export class Folders {
*/
private static absWsFolder(folder: ContentFolder, wsFolder?: Uri) {
const isWindows = process.platform === 'win32';
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, wsFolder?.fsPath || "");
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ""));
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
return absPath;
}
@@ -236,7 +314,7 @@ export class Folders {
*/
private static relWsFolder(folder: ContentFolder, wsFolder?: Uri) {
const isWindows = process.platform === 'win32';
let absPath = folder.path.replace(wsFolder?.fsPath || "", WORKSPACE_PLACEHOLDER);
let absPath = folder.path.replace(parseWinPath(wsFolder?.fsPath || ""), WORKSPACE_PLACEHOLDER);
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
return absPath;
}

View File

@@ -1,11 +1,13 @@
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME } from './../constants/settings';
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME, CONTEXT } from './../constants';
import { ArticleHelper } from './../helpers/ArticleHelper';
import { join } from "path";
import { commands, env, Uri, ViewColumn, window } from "vscode";
import { Settings } from '../helpers';
import { PreviewSettings } from '../models';
import { format } from 'date-fns';
import { CONTEXT } from '../constants/context';
import { DateHelper } from '../helpers/DateHelper';
import { Article } from '.';
import { urlJoin } from 'url-join-ts';
export class Preview {
@@ -32,12 +34,25 @@ export class Preview {
const article = editor ? ArticleHelper.getFrontMatter(editor) : null;
let slug = article?.data ? article.data.slug : "";
if (settings.pathname) {
let pathname = settings.pathname;
if (article?.data) {
const contentType = ArticleHelper.getContentType(article.data);
if (contentType && contentType.previewPath) {
pathname = contentType.previewPath;
}
}
if (!slug) {
slug = Article.getSlug();
}
if (pathname) {
const articleDate = ArticleHelper.getDate(article);
try {
slug = join(format(articleDate || new Date(), settings.pathname), slug);
slug = join(format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string), slug);
} catch (error) {
slug = join(settings.pathname, slug);
slug = join(pathname, slug);
}
}
@@ -113,9 +128,9 @@ export class Preview {
</head>
<body>
<div class="slug">
<input type="text" value="${join(localhostUrl.toString(), slug)}" disabled />
<input type="text" value="${urlJoin(localhostUrl.toString(), slug || '')}" disabled />
</div>
<iframe src="${join(localhostUrl.toString(), slug)}" >
<iframe src="${urlJoin(localhostUrl.toString(), slug || '')}" >
</body>
</html>`;
}
@@ -132,4 +147,4 @@ export class Preview {
pathname
};
}
}
}

View File

@@ -1,5 +1,4 @@
import { workspace, Uri } from "vscode";
import { CONFIG_KEY, SETTING_TEMPLATES_FOLDER } from "../constants";
import { join } from "path";
import * as fs from "fs";
import { Notifications } from "../helpers/Notifications";

View File

@@ -1,8 +1,9 @@
import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants/settings';
import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants';
import * as vscode from 'vscode';
import { ArticleHelper, SeoHelper, Settings } from '../helpers';
import { ExplorerView } from '../explorerView/ExplorerView';
import { DefaultFields } from '../constants';
import { ContentType } from '../helpers/ContentType';
export class StatusListener {
@@ -15,6 +16,11 @@ export class StatusListener {
public static async verify(frontMatterSB: vscode.StatusBarItem, collection: vscode.DiagnosticCollection) {
const draftMsg = "in draft";
const publishMsg = "to publish";
const draft = ContentType.getDraftField();
if (!draft || draft.type !== "boolean") {
frontMatterSB.hide();
}
let editor = vscode.window.activeTextEditor;
if (editor && ArticleHelper.isMarkdownFile()) {

View File

@@ -1,15 +1,17 @@
import { Questions } from './../helpers/Questions';
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants';
import { format } from 'date-fns';
import sanitize from '../helpers/Sanitize';
import { ArticleHelper, Settings } from '../helpers';
import { Article } from '.';
import { Notifications } from '../helpers/Notifications';
import { CONTEXT } from '../constants/context';
import { CONTEXT } from '../constants';
import { Project } from './Project';
import { Folders } from './Folders';
import { ContentType } from '../helpers/ContentType';
import { ContentType as IContentType } from '../models';
import { PagesListener } from '../listeners';
export class Template {
@@ -67,7 +69,7 @@ export class Template {
["yes", "no"],
{
canPickMany: false,
placeHolder: `Do you want to keep the article its contents for the template?`,
placeHolder: `Do you want to keep the contents for the template?`,
}
);
@@ -94,7 +96,7 @@ export class Template {
*/
public static async create(folderPath: string) {
const folder = Settings.get<string>(SETTING_TEMPLATES_FOLDER);
const prefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
const contentTypes = ContentType.getAll();
if (!folderPath) {
Notifications.warning(`Incorrect project folder path retrieved.`);
@@ -113,39 +115,33 @@ export class Template {
}
const selectedTemplate = await vscode.window.showQuickPick(templates.map(t => path.basename(t.fsPath)), {
placeHolder: `Select the article template to use`
placeHolder: `Select the content template to use`
});
if (!selectedTemplate) {
Notifications.warning(`No template selected.`);
return;
}
const titleValue = await vscode.window.showInputBox({
prompt: `What would you like to use as a title for the new article?`,
placeHolder: `Article title`
});
const titleValue = await Questions.ContentTitle();
if (!titleValue) {
Notifications.warning(`You did not specify an article title.`);
return;
}
// Start the template read
const template = templates.find(t => t.fsPath.endsWith(selectedTemplate));
if (!template) {
Notifications.warning(`Article template could not be found.`);
Notifications.warning(`Content template could not be found.`);
return;
}
const fileExt = path.parse(selectedTemplate).ext;
const sanitizedName = sanitize(titleValue.toLowerCase().replace(/ /g, "-"));
let newFileName = `${sanitizedName}${fileExt}`;
if (prefix && typeof prefix === "string") {
newFileName = `${format(new Date(), prefix)}-${newFileName}`;
const templateData = ArticleHelper.getFrontMatterByPath(template.fsPath);
let contentType: IContentType | undefined;
if (templateData && templateData.data && templateData.data.type) {
contentType = contentTypes?.find(t => t.name === templateData.data.type);
}
const newFilePath = path.join(folderPath, newFileName);
if (fs.existsSync(newFilePath)) {
Notifications.warning(`File already exists, please remove it before creating a new one with the same title.`);
let newFilePath: string | undefined = ArticleHelper.createContent(contentType, folderPath, titleValue);
if (!newFilePath) {
return;
}
@@ -165,7 +161,7 @@ export class Template {
fmData.title = titleValue;
}
if (typeof fmData.slug !== "undefined") {
fmData.slug = sanitizedName;
fmData.slug = ArticleHelper.sanitize(titleValue);
}
frontMatter = Article.updateDate(frontMatter);
@@ -180,7 +176,10 @@ export class Template {
vscode.window.showTextDocument(txtDoc);
}
Notifications.info(`Your new article has been created.`);
Notifications.info(`Your new content has been created.`);
// Trigger a refresh for the dashboard
PagesListener.refresh();
}
/**

230
src/commands/Wysiwyg.ts Normal file
View File

@@ -0,0 +1,230 @@
import { commands, window, Selection, QuickPickItem } from "vscode";
import { COMMAND_NAME, CONTEXT, SETTINGS_CONTENT_WYSIWYG } from "../constants";
import { Settings } from "../helpers";
enum MarkupType {
bold = 1,
italic,
strikethrough,
code,
codeblock,
blockquote,
heading,
unorderedList,
orderedList,
taskList
}
export class Wysiwyg {
/**
* Registers the markup commands for the WYSIWYG controls
* @param subscriptions
* @returns
*/
public static async registerCommands(subscriptions: any) {
const wysiwygEnabled = Settings.get(SETTINGS_CONTENT_WYSIWYG);
if (!wysiwygEnabled) {
return;
}
await commands.executeCommand('setContext', CONTEXT.wysiwyg, true);
// Surrounding markup
subscriptions.push(commands.registerCommand(COMMAND_NAME.bold, () => this.addMarkup(MarkupType.bold)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.italic, () => this.addMarkup(MarkupType.italic)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.strikethrough, () => this.addMarkup(MarkupType.strikethrough)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.code, () => this.addMarkup(MarkupType.code)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.codeblock, () => this.addMarkup(MarkupType.codeblock)));
// Prefix markup
subscriptions.push(commands.registerCommand(COMMAND_NAME.heading, () => this.addMarkup(MarkupType.heading)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.blockquote, () => this.addMarkup(MarkupType.blockquote)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.unorderedlist, () => this.addMarkup(MarkupType.unorderedList)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.orderedlist, () => this.addMarkup(MarkupType.orderedList)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.taskList, () => this.addMarkup(MarkupType.taskList)));
// Options
subscriptions.push(commands.registerCommand(COMMAND_NAME.options, async () => {
const qpItems: QuickPickItem[] = [
{ label: "$(list-unordered) Unordered list", detail: "Add an unordered list", alwaysShow: true, },
{ label: "$(list-ordered) Ordered list", detail: "Add an ordered list", alwaysShow: true },
{ label: "$(tasklist) Task list", detail: "Add a task list", alwaysShow: true },
{ label: "$(code) Code", detail: "Add inline code snippet", alwaysShow: true },
{ label: "$(symbol-namespace) Code block", detail: "Add a code block", alwaysShow: true },
]
const option = await window.showQuickPick([ ...qpItems ], {
placeHolder: "Which type of markup would you like to insert?",
canPickMany: false,
ignoreFocusOut: false,
});
if (option) {
if (option.label === qpItems[0].label) {
await this.addMarkup(MarkupType.unorderedList);
} else if (option.label === qpItems[1].label) {
await this.addMarkup(MarkupType.orderedList);
} else if (option.label === qpItems[2].label) {
await this.addMarkup(MarkupType.taskList);
} else if (option.label === qpItems[3].label) {
await this.addMarkup(MarkupType.code);
} else if (option.label === qpItems[4].label) {
await this.addMarkup(MarkupType.codeblock);
}
}
}));
}
/**
* Add the markup to the content
* @param type
* @returns
*/
private static async addMarkup(type: MarkupType) {
const editor = window.activeTextEditor;
if (!editor) {
return;
}
const selection = editor.selection;
const hasTextSelection = !selection.isEmpty;
const markers = this.getMarkers(type);
if (!markers) {
return;
}
const crntSelection = selection.active;
if (hasTextSelection) {
// Replace the selection and surround with the markup
const selectionText = editor.document.getText(selection);
const txt = await this.insertText(markers, type, selectionText);
editor.edit(builder => {
builder.replace(selection, txt);
});
} else {
const txt = await this.insertText(markers, type);
// Insert the markers where cursor is located.
const markerLength = this.isMarkupWrapping(type) ? txt.length + 1 : markers.length;
let newPosition = crntSelection.with(crntSelection.line, crntSelection.character + markerLength);
await editor.edit(builder => {
builder.insert(newPosition, txt);
});
if (type === MarkupType.codeblock) {
newPosition = crntSelection.with(crntSelection.line + 1, 0);
}
editor.selection = new Selection(newPosition, newPosition);
}
}
/**
* Check if the text will be wrapped
* @param type
* @returns
*/
private static isMarkupWrapping(type: MarkupType) {
return (
type === MarkupType.blockquote ||
type === MarkupType.heading ||
type === MarkupType.unorderedList ||
type === MarkupType.orderedList ||
type === MarkupType.taskList
);
}
/**
* Insert text at the current cursor position
*/
private static async insertText(marker: string | undefined, type: MarkupType, text: string | null = null) {
const crntText = text || this.lineBreak(type);
if (this.isMarkupWrapping(type)) {
if (type === MarkupType.heading) {
const headingLvl = await window.showQuickPick([
"Heading 1",
"Heading 2",
"Heading 3",
"Heading 4",
"Heading 5",
"Heading 6"
], {
canPickMany: false,
placeHolder: "Which heading level do you want to insert?",
ignoreFocusOut: false
});
if (headingLvl) {
const headingNr = parseInt(headingLvl.replace("Heading ", ""));
return `${Array(headingNr + 1).join(marker)} ${crntText}`;
}
}
if (type === MarkupType.unorderedList || type === MarkupType.taskList) {
const lines = crntText.split("\n").map(line => `${marker} ${line}`);
return lines.join("\n");
}
if (type === MarkupType.orderedList) {
const lines = crntText.split("\n").map((line, idx) => `${idx+1}. ${line}`);
return lines.join("\n");
}
return `${marker} ${crntText}`;
} else {
return `${marker}${crntText}${marker}`;
}
}
/**
* Check if linebreak needs to be added
* @param type
* @returns
*/
private static lineBreak(type: MarkupType) {
if (type === MarkupType.codeblock) {
return `\n\n`;
}
return "";
}
/**
* Retrieve the type of markers
* @param type
* @returns
*/
private static getMarkers(type: MarkupType) {
switch(type) {
case MarkupType.bold:
return `**`;
case MarkupType.italic:
return `*`;
case MarkupType.strikethrough:
return `~~`;
case MarkupType.code:
return "`";
case MarkupType.codeblock:
return "```";
case MarkupType.blockquote:
return ">";
case MarkupType.heading:
return "#";
case MarkupType.unorderedList:
return "-";
case MarkupType.orderedList:
return "1.";
case MarkupType.taskList:
return "- [ ]";
default:
return;
}
}
}

View File

@@ -0,0 +1,13 @@
import * as React from 'react';
export interface ICompressIconProps {
className?: string;
}
export const CompressIcon: React.FunctionComponent<ICompressIconProps> = ({className}: React.PropsWithChildren<ICompressIconProps>) => {
return (
<svg className={className || ""} aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path>
</svg>
);
};

View File

@@ -4,6 +4,8 @@ export const DEFAULT_CONTENT_TYPE_NAME = 'default';
export const DEFAULT_CONTENT_TYPE: ContentType = {
"name": "default",
"pageBundle": false,
"previewPath": null,
"fields": [
{
"title": "Title",
@@ -21,14 +23,14 @@ export const DEFAULT_CONTENT_TYPE: ContentType = {
"type": "datetime"
},
{
"title": "Article preview",
"title": "Content preview",
"name": "preview",
"type": "image"
},
{
"title": "Is in draft",
"name": "draft",
"type": "boolean"
"type": "draft"
},
{
"title": "Tags",

View File

@@ -2,5 +2,6 @@
export const DefaultFields = {
PublishingDate: `date`,
LastModified: `lastmod`,
Description: `description`
Description: `description`,
Slug: `slug`
};

View File

@@ -3,9 +3,6 @@ const extensionName = "frontMatter";
export const EXTENSION_ID = 'eliostruyf.vscode-front-matter';
export const EXTENSION_BETA_ID = 'eliostruyf.vscode-front-matter-beta';
export const EXTENSION_STATE_VERSION = 'frontMatter:Version';
export const EXTENSION_STATE_PAGES_VIEW = 'frontMatter:Pages:ViewType';
export const getCommandName = (command: string) => {
return `${extensionName}.${command}`;
};
@@ -25,10 +22,29 @@ export const COMMAND_NAME = {
registerFolder: getCommandName("registerFolder"),
unregisterFolder: getCommandName("unregisterFolder"),
createContent: getCommandName("createContent"),
createByContentType: getCommandName("createByContentType"),
createByTemplate: getCommandName("createByTemplate"),
createTemplate: getCommandName("createTemplate"),
collapseSections: getCommandName("collapseSections"),
preview: getCommandName("preview"),
dashboard: getCommandName("dashboard"),
dashboardMedia: getCommandName("dashboard.media"),
dashboardClose: getCommandName("dashboard.close"),
promote: getCommandName("promoteSettings"),
insertImage: getCommandName("insertImage"),
createFolder: getCommandName("createFolder"),
diagnostics: getCommandName("diagnostics"),
// WYSIWYG
bold: getCommandName("markup.bold"),
italic: getCommandName("markup.italic"),
strikethrough: getCommandName("markup.strikethrough"),
code: getCommandName("markup.code"),
codeblock: getCommandName("markup.codeblock"),
heading: getCommandName("markup.heading"),
blockquote: getCommandName("markup.blockquote"),
unorderedlist: getCommandName("markup.unorderedlist"),
orderedlist: getCommandName("markup.orderedlist"),
taskList: getCommandName("markup.tasklist"),
options: getCommandName("markup.options"),
};

View File

@@ -0,0 +1,17 @@
export const ExtensionState = {
PagesView: `frontMatter:Pages:ViewType`,
SelectedFolder: `frontMatter:SelectedFolder`,
Version: `frontMatter:Version`,
SettingPromoted: `frontMatter:Settings:Promoted`,
MoveTemplatesFolder: `frontMatter:Templates:Move`,
Dashboard: {
Contents: {
Sorting: `frontMatter:Dashboard:Contents:Sorting`,
},
Media: {
Sorting: `frontMatter:Dashboard:Media:Sorting`,
}
}
};

View File

@@ -0,0 +1,21 @@
export const FrameworkDetectors = [
{
"framework": {"name": "gatsby", "dist": "public", "static": "static", "build": "gatsby build"},
"requiredFiles": ["gatsby-config.js"],
"requiredDependencies": ["gatsby"]
},
{
"framework": {"name": "hugo", "dist": "public", "static": "static", "build": "hugo"},
"requiredFiles": ["config.toml", "config.yaml", "config.yml"]
},
{
"framework": {"name": "next", "dist": ".next", "static": "public", "build": "next build"},
"requiredFiles": ["next.config.js"],
"requiredDependencies": ["next"]
},
{
"framework": {"name": "nuxt", "dist": "dist", "static": "static", "build": "nuxt"},
"requiredFiles": ["nuxt.config.js"],
"requiredDependencies": ["nuxt"]
}
];

View File

@@ -1,4 +1,6 @@
export const GITHUB_LINK = "https://github.com/estruyf/vscode-front-matter";
export const ISSUE_LINK = "https://github.com/estruyf/vscode-front-matter/issues";
export const SPONSOR_LINK = "https://github.com/sponsors/estruyf";
export const REVIEW_LINK = "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details";
export const REVIEW_LINK = "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details";
export const SENTRY_LINK = "https://1ac45704bbe74264a7b4674bdc2abf48@o1022172.ingest.sentry.io/5988293";

View File

@@ -0,0 +1,8 @@
export const LocalStore = {
rootFolder: ".frontmatter",
contentFolder: "content",
templatesFolder: "templates",
mediaDatabaseFile: "mediaDb.json"
}

View File

@@ -0,0 +1 @@
export const HOME_PAGE_NAVIGATION_ID = "FrontMatter:RootFolder";

View File

@@ -1,5 +1,8 @@
export const CONTEXT = {
canInit: "frontMatterCanInit",
canOpenPreview: "frontMatterCanOpenPreview",
canOpenDashboard: "frontMatterCanOpenDashboard"
canOpenDashboard: "frontMatterCanOpenDashboard",
isEnabled: "frontMatter:enabled",
isDashboardOpen: "frontMatter:dashboard:open",
wysiwyg: "frontMatter:markdown:wysiwyg",
};

View File

@@ -1,6 +1,10 @@
export * from './ContentType';
export * from './DefaultFields';
export * from './Extension';
export * from './ExtensionState';
export * from './Links';
export * from './LocalStore';
export * from './Navigation';
export * from './charMap';
export * from './context';
export * from './settings';

View File

@@ -2,8 +2,12 @@ export const EXTENSION_NAME = "Front Matter";
export const CONFIG_KEY = "frontMatter";
export const SETTING_GLOBAL_NOTIFICATIONS = "global.notifications";
export const SETTING_TAXONOMY_TAGS = "taxonomy.tags";
export const SETTING_TAXONOMY_CATEGORIES = "taxonomy.categories";
export const SETTING_TAXONOMY_CUSTOM = "taxonomy.customTaxonomy";
export const SETTING_DATE_FORMAT = "taxonomy.dateFormat";
export const SETTING_COMMA_SEPARATED_FIELDS = "taxonomy.commaSeparatedFields";
export const SETTING_TAXONOMY_CONTENT_TYPES = "taxonomy.contentTypes";
@@ -20,6 +24,7 @@ export const SETTING_REMOVE_QUOTES = "taxonomy.noPropertyValueQuotes";
export const SETTING_FRONTMATTER_TYPE = "taxonomy.frontMatterType";
export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength";
export const SETTING_SEO_SLUG_LENGTH = "taxonomy.seoSlugLength";
export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
export const SETTING_SEO_CONTENT_MIN_LENGTH = "taxonomy.seoContentLengh";
export const SETTING_SEO_DESCRIPTION_FIELD = "taxonomy.seoDescriptionField";
@@ -36,12 +41,22 @@ export const SETTING_CUSTOM_SCRIPTS = "custom.scripts";
export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate";
export const SETTINGS_CONTENT_PAGE_FOLDERS = "content.pageFolders";
export const SETTINGS_CONTENT_STATIC_FOLDERS = "content.publicFolder";
export const SETTINGS_CONTENT_STATIC_FOLDER = "content.publicFolder";
export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight";
export const SETTINGS_CONTENT_DRAFT_FIELD = "content.draftField";
export const SETTINGS_CONTENT_SORTING = "content.sorting";
export const SETTINGS_CONTENT_WYSIWYG = "content.wysiwyg";
export const SETTINGS_CONTENT_SORTING_DEFAULT = "content.defaultSorting";
export const SETTINGS_MEDIA_SORTING_DEFAULT = "content.defaultSorting";
export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart";
export const SETTINGS_DASHBOARD_MEDIA_SNIPPET = "dashboard.mediaSnippet";
export const SETTINGS_FRAMEWORK_ID = "framework.id";
export const SETTING_SITE_BASEURL = "site.baseURL";
/**
* @deprecated
*/

View File

@@ -4,4 +4,5 @@ export enum DashboardCommand {
settings = "settings",
media = "media",
viewData = "viewData",
mediaUpdate = "mediaUpdate"
}

View File

@@ -4,6 +4,8 @@ export enum DashboardMessage {
openFile = 'openFile',
getTheme = 'getTheme',
createContent = 'createContent',
createByContentType = 'createByContentType',
createByTemplate = 'createByTemplate',
updateSetting = 'updateSetting',
initializeProject = 'initializeProject',
reload = 'reload',
@@ -14,4 +16,9 @@ export enum DashboardMessage {
uploadMedia = 'uploadMedia',
deleteMedia = 'deleteMedia',
insertPreviewImage = 'insertPreviewImage',
updateMediaMetadata = 'updateMediaMetadata',
createMediaFolder = 'createMediaFolder',
setFramework = 'setFramework',
setState = 'setState',
runCustomScript = 'runCustomScript',
}

View File

@@ -0,0 +1,52 @@
import { Menu } from '@headlessui/react';
import {ChevronDownIcon} from '@heroicons/react/outline';
import * as React from 'react';
import { MenuItem, MenuItems } from './Menu';
export interface IChoiceButtonProps {
title: string;
choices: {
title: string;
disabled?: boolean;
onClick: () => void;
}[];
disabled?: boolean;
onClick: () => void;
}
export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({onClick, disabled, choices, title}: React.PropsWithChildren<IChoiceButtonProps>) => {
return (
<span className="relative z-50 inline-flex shadow-sm rounded-md">
<button
type="button"
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500"
onClick={onClick}
disabled={disabled}
>
{title}
</button>
<Menu as="span" className="-ml-px relative block">
<Menu.Button
className="h-full inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-700 hover:bg-teal-800 focus:outline-none disabled:bg-gray-500"
disabled={disabled}>
<span className="sr-only">Open options</span>
<ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button>
<MenuItems widthClass={`w-56`}>
<div className="py-1">
{choices.map((choice) => (
<MenuItem
key={choice.title}
title={choice.title}
value={null}
onClick={choice.onClick}
disabled={choice.disabled} />
))}
</div>
</MenuItems>
</Menu>
</span>
);
};

View File

@@ -6,6 +6,7 @@ import { Header } from '../Header';
import { Overview } from './Overview';
import { Spinner } from '../Spinner';
import { SponsorMsg } from '../SponsorMsg';
import usePages from '../../hooks/usePages';
export interface IContentsProps {
pages: Page[];
@@ -14,23 +15,22 @@ export interface IContentsProps {
export const Contents: React.FunctionComponent<IContentsProps> = ({pages, loading}: React.PropsWithChildren<IContentsProps>) => {
const settings = useRecoilValue(SettingsSelector);
const { pageItems } = usePages(pages);
const pageFolders = [...new Set(pages.map(page => page.fmFolder))];
const pageFolders = [...new Set(pageItems.map(page => page.fmFolder))];
return (
<main className={`h-full w-full`}>
<div className="flex flex-col h-full overflow-auto">
<Header
folders={pageFolders}
totalPages={pages.length}
settings={settings} />
<div className="flex flex-col h-full overflow-auto">
<Header
folders={pageFolders}
totalPages={pageItems.length}
settings={settings} />
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
{ loading ? <Spinner /> : <Overview pages={pages} settings={settings} /> }
</div>
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
</div>
</main>
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
</div>
);
};

View File

@@ -3,29 +3,35 @@ import { useRecoilValue } from 'recoil';
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
import { DashboardMessage } from '../../DashboardMessage';
import { Page } from '../../models/Page';
import { ViewSelector, ViewType } from '../../state';
import { SettingsSelector, ViewSelector } from '../../state';
import { DateField } from '../DateField';
import { Status } from '../Status';
import { Messenger } from '@estruyf/vscode/dist/client';
import useContentType from '../../../hooks/useContentType';
import { DashboardViewType } from '../../models';
export interface IItemProps extends Page {}
export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, title, draft, description, preview }: React.PropsWithChildren<IItemProps>) => {
export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, title, draft, description, type, ...pageData }: React.PropsWithChildren<IItemProps>) => {
const view = useRecoilValue(ViewSelector);
const settings = useRecoilValue(SettingsSelector);
const contentType = useContentType(settings, { type });
const previewField = contentType.fields.find(field => field.isPreviewImage && field.type === "image")?.name || "preview";
const openFile = () => {
Messenger.send(DashboardMessage.openFile, fmFilePath);
};
if (view === ViewType.Grid) {
if (view === DashboardViewType.Grid) {
return (
<li className="relative">
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100`}
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100 border border-gray-100 dark:border-vulcan-50`}
onClick={openFile}>
<div className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200">
{
preview ? (
<img src={`${preview}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
previewField && pageData[previewField] ? (
<img src={`${pageData[previewField]}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
) : (
<div className={`flex items-center justify-center bg-whisper-500 dark:bg-vulcan-200 dark:group-hover:bg-vulcan-100`}>
<MarkdownIcon className={`h-32 text-vulcan-100 dark:text-whisper-100`} />
@@ -36,7 +42,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
<div className="p-4 w-full">
<div className={`flex justify-between items-center`}>
<Status draft={!!draft} />
<Status draft={draft} />
<DateField value={date} />
</div>
@@ -48,7 +54,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
</button>
</li>
);
} else if (view === ViewType.List) {
} else if (view === DashboardViewType.List) {
return (
<li className="relative">
<button 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 border-gray-300 hover:bg-gray-200 dark:border-vulcan-50 dark:hover:bg-vulcan-50 hover:bg-opacity-70`} onClick={openFile}>
@@ -59,7 +65,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
<DateField value={date} />
</div>
<div className="col-span-2">
<Status draft={!!draft} />
<Status draft={draft} />
</div>
</button>
</li>

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { ViewSelector, ViewType } from '../../state';
import { DashboardViewType } from '../../models';
import { ViewSelector } from '../../state';
export interface IListProps {}
@@ -8,15 +9,15 @@ export const List: React.FunctionComponent<IListProps> = ({children}: React.Prop
const view = useRecoilValue(ViewSelector);
let className = '';
if (view === ViewType.Grid) {
if (view === DashboardViewType.Grid) {
className = `grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`;
} else if (view === ViewType.List) {
} else if (view === DashboardViewType.List) {
className = `-mx-4`;
}
return (
<ul role="list" className={className}>
{view === ViewType.List && (
{view === DashboardViewType.List && (
<li className="px-5 relative uppercase text-vulcan-100 dark:text-whisper-900 py-2 border-b border-vulcan-50">
<div className={`grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8`}>
<div className="col-span-8">

View File

@@ -1,5 +1,5 @@
import { Disclosure } from '@headlessui/react';
import { ChevronRightIcon } from '@heroicons/react/solid';
import {ChevronRightIcon} from '@heroicons/react/solid';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { groupBy } from '../../../helpers/GroupBy';

View File

@@ -2,12 +2,12 @@ import * as React from 'react';
import { Spinner } from './Spinner';
import useMessages from '../hooks/useMessages';
import useDarkMode from '../../hooks/useDarkMode';
import usePages from '../hooks/usePages';
import { WelcomeScreen } from './WelcomeScreen';
import { useRecoilValue } from 'recoil';
import { DashboardViewSelector, ViewDataAtom } from '../state';
import { DashboardViewSelector } from '../state';
import { Contents } from './Contents/Contents';
import { Media } from './Media/Media';
import { NavigationType } from '../models';
export interface IDashboardProps {
showWelcome: boolean;
@@ -15,7 +15,6 @@ export interface IDashboardProps {
export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome}: React.PropsWithChildren<IDashboardProps>) => {
const { loading, pages, settings } = useMessages();
const { pageItems } = usePages(pages);
const view = useRecoilValue(DashboardViewSelector);
useDarkMode();
@@ -31,9 +30,15 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
return <WelcomeScreen settings={settings} />;
}
if (view === 'media') {
return <Media />;
}
return <Contents pages={pageItems} loading={loading} />;
return (
<main className={`h-full w-full`}>
{
view === NavigationType.Media ? (
<Media />
) : (
<Contents pages={pages} loading={loading} />
)
}
</main>
);
};

View File

@@ -1,5 +1,6 @@
import { format, parseJSON } from 'date-fns';
import { format } from 'date-fns';
import * as React from 'react';
import { DateHelper } from '../../helpers/DateHelper';
export interface IDateFieldProps {
value: Date | string;
@@ -10,9 +11,9 @@ export const DateField: React.FunctionComponent<IDateFieldProps> = ({value}: Rea
React.useEffect(() => {
try {
const parsedValue = typeof value === 'string' ? parseJSON(value) : value;
const dateString = format(parsedValue, 'yyyy-MM-dd');
setDateValue(dateString);
const parsedValue = typeof value === 'string' ? DateHelper.tryParse(value) : value;
const dateString = parsedValue ? format(parsedValue, 'yyyy-MM-dd') : parsedValue;
setDateValue(dateString || "");
} catch (e) {
// Date is invalid
}

View File

@@ -0,0 +1,105 @@
import { CollectionIcon } from '@heroicons/react/outline';
import { basename, join } from 'path';
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { HOME_PAGE_NAVIGATION_ID } from '../../../constants';
import { parseWinPath } from '../../../helpers/parseWinPath';
import { SearchAtom, SelectedMediaFolderAtom, SettingsAtom } from '../../state';
export interface IBreadcrumbProps {}
export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (props: React.PropsWithChildren<IBreadcrumbProps>) => {
const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const [ , setSearchValue ] = useRecoilState(SearchAtom);
const [ folders, setFolders ] = React.useState<string[]>([]);
const settings = useRecoilValue(SettingsAtom);
const updateFolder = (folder: string) => {
setSearchValue('');
setSelectedFolder(folder);
}
React.useEffect(() => {
if (!settings) {
return;
}
const { wsFolder, staticFolder, contentFolders } = settings;
const isValid = (folderPath: string) => {
if (staticFolder) {
const staticPath = parseWinPath(join(wsFolder, staticFolder)) as string;
const relPath = folderPath.replace(staticPath, '') as string;
if (relPath.length > 1 && folderPath.startsWith(staticPath)) {
return true;
} else if (relPath.length === 0) {
return false;
}
}
for (let i = 0; i < contentFolders.length; i++) {
const folder = contentFolders[i];
const contentFolder = parseWinPath(folder.path) as string;
const relContentPath = folderPath.replace(contentFolder, '');
return relContentPath.length > 1 && folderPath.startsWith(contentFolder);
}
return false;
};
if (!selectedFolder) {
setFolders([]);
} else {
const relPath = parseWinPath(selectedFolder.replace(parseWinPath(settings.wsFolder) as string, '')) as string;
const folderParts = relPath.split('/').filter(f => f);
const allFolders: string[] = [];
let previousFolder = parseWinPath(settings.wsFolder) as string;
for (const part of folderParts) {
const folder = join(previousFolder, part);
if (isValid(folder)) {
allFolders.push(folder);
}
previousFolder = folder;
}
setFolders(allFolders);
}
}, [selectedFolder, settings]);
return (
<ol role="list" className="flex space-x-4 px-5 flex-1">
<li className="flex">
<div className="flex items-center">
<button onClick={() => setSelectedFolder(HOME_PAGE_NAVIGATION_ID)} className="text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500">
<CollectionIcon className="flex-shrink-0 h-5 w-5" aria-hidden="true" />
<span className="sr-only">Home</span>
</button>
</div>
</li>
{folders.map((folder) => (
<li key={folder} className="flex">
<div className="flex items-center">
<svg
className="flex-shrink-0 h-5 w-5 text-gray-300 dark:text-whisper-900"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<button
onClick={() => updateFolder(folder)}
className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500"
>
{basename(folder)}
</button>
</div>
</li>
))}
</ol>
);
};

View File

@@ -1,7 +1,7 @@
import { XCircleIcon } from '@heroicons/react/solid';
import {XCircleIcon} from '@heroicons/react/solid';
import * as React from 'react';
import { useRecoilValue, useResetRecoilState } from 'recoil';
import { SortingSelector, FolderSelector, TagSelector, CategorySelector, SortingAtom, DEFAULT_SORTING_OPTION, FolderAtom, DEFAULT_FOLDER_STATE, TagAtom, CategoryAtom, DEFAULT_TAG_STATE, DEFAULT_CATEGORY_STATE } from '../../state';
import { FolderSelector, TagSelector, CategorySelector, SortingAtom, FolderAtom, DEFAULT_FOLDER_STATE, TagAtom, CategoryAtom, DEFAULT_TAG_STATE, DEFAULT_CATEGORY_STATE } from '../../state';
import { DefaultValue } from 'recoil';
@@ -17,7 +17,6 @@ export interface IClearFiltersProps {}
export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (props: React.PropsWithChildren<IClearFiltersProps>) => {
const [ show, setShow ] = React.useState(false);
const sorting = useRecoilValue(SortingSelector);
const folder = useRecoilValue(FolderSelector);
const tag = useRecoilValue(TagSelector);
const category = useRecoilValue(CategorySelector);
@@ -36,19 +35,19 @@ export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (props:
};
React.useEffect(() => {
if (sorting !== DEFAULT_SORTING_OPTION || folder !== DEFAULT_FOLDER_STATE || tag !== DEFAULT_TAG_STATE || category !== DEFAULT_CATEGORY_STATE) {
if (folder !== DEFAULT_FOLDER_STATE || tag !== DEFAULT_TAG_STATE || category !== DEFAULT_CATEGORY_STATE) {
setShow(true);
} else {
setShow(false);
}
}, [sorting, folder, tag, category]);
}, [folder, tag, category]);
if (!show) return null;
return (
<button className="flex items-center hover:text-teal-600" onClick={reset} title={`Clear filters, grouping, and sorting`}>
<XCircleIcon className={`inline-block w-5 h-5 mr-1`} /><span>Clear</span>
<span className={`sr-only`}> filters, grouping, and sorting</span>
<span className={`sr-only`}> filters and grouping</span>
</button>
);
};

View File

@@ -1,5 +1,5 @@
import { Menu } from '@headlessui/react';
import { FilterIcon } from '@heroicons/react/solid';
import {FilterIcon} from '@heroicons/react/solid';
import * as React from 'react';
import { MenuButton, MenuItem, MenuItems } from '../Menu';

View File

@@ -1,19 +1,19 @@
import { Menu, Transition } from '@headlessui/react';
import { Menu } from '@headlessui/react';
import * as React from 'react';
import { useRecoilState } from 'recoil';
import { FolderAtom } from '../../state';
import { useRecoilState, useRecoilValue } from 'recoil';
import { FolderAtom, SettingsSelector } from '../../state';
import { MenuButton, MenuItem, MenuItems } from '../Menu';
export interface IFoldersProps {
folders: string[];
}
export interface IFoldersProps {}
const DEFAULT_TYPE = "All types";
export const Folders: React.FunctionComponent<IFoldersProps> = ({folders}: React.PropsWithChildren<IFoldersProps>) => {
export const Folders: React.FunctionComponent<IFoldersProps> = ({}: React.PropsWithChildren<IFoldersProps>) => {
const [ crntFolder, setCrntFolder ] = useRecoilState(FolderAtom);
const settings = useRecoilValue(SettingsSelector);
const contentFolders = settings?.contentFolders || [];
if (folders.length <= 1) {
if (contentFolders.length <= 1) {
return null;
}
@@ -29,12 +29,12 @@ export const Folders: React.FunctionComponent<IFoldersProps> = ({folders}: React
isCurrent={!crntFolder}
onClick={(value) => setCrntFolder(value)} />
{folders.map((option) => (
{contentFolders.map((option) => (
<MenuItem
key={option}
title={option}
value={option}
isCurrent={option === crntFolder}
key={option.title}
title={option.title}
value={option.title}
isCurrent={option.title === crntFolder}
onClick={(value) => setCrntFolder(value)} />
))}
</MenuItems>

View File

@@ -3,20 +3,21 @@ import { Sorting } from './Sorting';
import { Searchbox } from './Searchbox';
import { Filter } from './Filter';
import { Folders } from './Folders';
import { Settings } from '../../models';
import { Settings, NavigationType } from '../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { Startup } from '../Startup';
import { Button } from '../Button';
import { Navigation } from '../Navigation';
import { Grouping } from '.';
import { ViewSwitch } from './ViewSwitch';
import { useRecoilState } from 'recoil';
import { CategoryAtom, DashboardViewAtom, TagAtom } from '../../state';
import { useRecoilState, useResetRecoilState } from 'recoil';
import { CategoryAtom, DashboardViewAtom, SortingAtom, TagAtom } from '../../state';
import { Messenger } from '@estruyf/vscode/dist/client';
import { ClearFilters } from './ClearFilters';
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
import { PhotographIcon } from '@heroicons/react/outline';
import { Pagination } from '../Media/Pagination';
import {PhotographIcon} from '@heroicons/react/outline';
import { MediaHeaderTop } from '../Media/MediaHeaderTop';
import { ChoiceButton } from '../ChoiceButton';
import { MediaHeaderBottom } from '../Media/MediaHeaderBottom';
export interface IHeaderProps {
settings: Settings | null;
@@ -32,27 +33,45 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
const [ crntTag, setCrntTag ] = useRecoilState(TagAtom);
const [ crntCategory, setCrntCategory ] = useRecoilState(CategoryAtom);
const [ view, setView ] = useRecoilState(DashboardViewAtom);
const resetSorting = useResetRecoilState(SortingAtom)
const createContent = () => {
Messenger.send(DashboardMessage.createContent);
};
const createByContentType = () => {
Messenger.send(DashboardMessage.createByContentType);
};
const createByTemplate = () => {
Messenger.send(DashboardMessage.createByTemplate);
};
const updateView = (view: NavigationType) => {
setView(view);
resetSorting();
}
return (
<div className={`w-full sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
<div className={`px-4 bg-gray-50 dark:bg-vulcan-50 border-b-2 border-gray-200 dark:border-vulcan-200`}>
<div className={`flex items-center justify-start`}>
<button className={`p-2 flex items-center ${view === "contents" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => setView("contents")}>
<MarkdownIcon className={`h-6 w-auto mr-2`} /><span>Contents</span>
</button>
<button className={`p-2 flex items-center ${view === "media" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => setView("media")}>
<PhotographIcon className={`h-6 w-auto mr-2`} /><span>Media</span>
</button>
</div>
<div className="mb-0 border-b bg-gray-100 dark:bg-vulcan-500 border-gray-200 dark:border-vulcan-300 h-12">
<ul className="flex items-center justify-start h-full -mb-px" data-tabs-toggle="#myTabContent" role="tablist">
<li className="mr-2" role="presentation">
<button className={`flex items-center py-2 px-4 text-sm font-medium text-center border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 ${view === NavigationType.Contents ? "border-vulcan-500 text-vulcan-500 dark:border-whisper-500 dark:text-whisper-500" : "text-gray-500 dark:text-gray-400"}`} type="button" role="tab" aria-controls="profile" aria-selected="false" onClick={() => updateView(NavigationType.Contents)}>
<MarkdownIcon className={`h-6 w-auto mr-2`} /><span>Contents</span>
</button>
</li>
<li className="mr-2" role="presentation">
<button className={`flex items-center py-2 px-4 text-sm font-medium text-center text-gray-500 border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 ${view === NavigationType.Media ? "border-vulcan-500 text-vulcan-500 dark:border-whisper-500 dark:text-whisper-500" : "text-gray-500 dark:text-gray-400"}`} type="button" role="tab" aria-controls="dashboard" aria-selected="true" onClick={() => updateView(NavigationType.Media)}>
<PhotographIcon className={`h-6 w-auto mr-2`} /><span>Media</span>
</button>
</li>
</ul>
</div>
{
view === "contents" && (
view === NavigationType.Contents && (
<>
<div className={`px-4 mt-3 mb-2 flex items-center justify-between`}>
<Searchbox />
@@ -60,7 +79,19 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
<div className={`flex items-center space-x-4`}>
<Startup settings={settings} />
<Button onClick={createContent} disabled={!settings?.initialized}>Create content</Button>
<ChoiceButton
title={`Create content`}
choices={[{
title: `Create by content type`,
onClick: createByContentType,
disabled: !settings?.initialized
}, {
title: `Create by template`,
onClick: createByTemplate,
disabled: !settings?.initialized
}]}
onClick={createContent}
disabled={!settings?.initialized} />
</div>
</div>
@@ -74,10 +105,10 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
</div>
</div>
<div className={`py-4 px-5 w-full flex items-center justify-between lg:justify-end space-x-4 lg:space-x-6 xl:space-x-8 bg-gray-200 border-b border-gray-300 dark:bg-vulcan-400 dark:border-vulcan-100`}>
<div className={`py-4 px-5 w-full flex items-center justify-between lg:justify-end bg-gray-200 border-b border-gray-300 dark:bg-vulcan-400 dark:border-vulcan-100 space-x-4 lg:space-x-6 xl:space-x-8`}>
<ClearFilters />
<Folders folders={folders || []} />
<Folders />
<Filter label={`Tag`} activeItem={crntTag} items={settings?.tags || []} onClick={(value) => setCrntTag(value)} />
@@ -85,15 +116,19 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
<Grouping />
<Sorting />
<Sorting view={NavigationType.Contents} />
</div>
</>
)
}
{
view === "media" && (
<Pagination />
view === NavigationType.Media && (
<>
<MediaHeaderTop />
<MediaHeaderBottom />
</>
)
}
</div>

View File

@@ -0,0 +1,72 @@
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { LIMIT } from '../../hooks/useMedia';
import { MediaTotalSelector, PageAtom } from '../../state';
import { PaginationButton } from './PaginationButton';
export interface IPaginationProps {}
export const Pagination: React.FunctionComponent<IPaginationProps> = (props: React.PropsWithChildren<IPaginationProps>) => {
const [ page, setPage ] = useRecoilState(PageAtom);
const totalMedia = useRecoilValue(MediaTotalSelector);
const totalPages = Math.ceil(totalMedia / LIMIT) - 1;
const getButtons = (): number[] => {
const maxButtons = 5;
const buttons: number[] = [];
const start = page - maxButtons;
const end = page + maxButtons;
for (let i = start; i <= end; i++) {
if (i >= 0 && i <= totalPages) {
buttons.push(i);
}
}
return buttons;
};
return (
<div className="flex justify-between items-center sm:justify-end space-x-2 text-sm">
<PaginationButton
title="First"
disabled={page === 0}
onClick={() => {
if (page > 0) {
setPage(0)
}
}} />
<PaginationButton
title="Previous"
disabled={page === 0}
onClick={() => {
if (page > 0) {
setPage(page - 1)
}
}} />
{getButtons().map((button) => (
<button
key={button}
disabled={button === page}
onClick={() => {
setPage(button)
}
}
className={`${page === button ? 'bg-gray-200 px-2 text-vulcan-500' : 'text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500'} max-h-8`}
>{button + 1}</button>
))}
<PaginationButton
title="Next"
disabled={page >= totalPages}
onClick={() => setPage(page + 1)} />
<PaginationButton
title="Last"
disabled={page >= totalPages}
onClick={() => setPage(totalPages)} />
</div>
);
};

View File

@@ -0,0 +1,46 @@
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { MediaTotalSelector, PageAtom, SearchAtom, SelectedMediaFolderSelector } from '../../state';
import { Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { RefreshIcon } from '@heroicons/react/outline';
import { LIMIT } from '../../hooks/useMedia';
export interface IPaginationStatusProps {}
export const PaginationStatus: React.FunctionComponent<IPaginationStatusProps> = (props: React.PropsWithChildren<IPaginationStatusProps>) => {
const totalMedia = useRecoilValue(MediaTotalSelector);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const [ page, setPage ] = useRecoilState(PageAtom);
const [ , setSearch ] = useRecoilState(SearchAtom);
const getTotalPage = () => {
const mediaItems = ((page + 1) * LIMIT);
if (totalMedia < mediaItems) {
return totalMedia;
}
return mediaItems;
};
const refresh = () => {
setPage(0);
setSearch('');
Messenger.send(DashboardMessage.refreshMedia, { folder: selectedFolder });
}
return (
<div className="hidden sm:flex">
<button className={`mr-2 text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500`}
title="Refresh media"
onClick={refresh}>
<RefreshIcon className={`h-5 w-5`} />
<span className="sr-only">Refresh media</span>
</button>
<p className="text-sm text-gray-500 dark:text-whisper-900">
Showing <span className="font-medium">{(page * LIMIT) + 1}</span> to <span className="font-medium">{getTotalPage()}</span> of{' '}
<span className="font-medium">{totalMedia}</span> results
</p>
</div>
);
};

View File

@@ -1,37 +1,46 @@
import { FilterIcon, SearchIcon } from '@heroicons/react/solid';
import {SearchIcon} from '@heroicons/react/solid';
import * as React from 'react';
import { useRecoilState } from 'recoil';
import { useDebounce } from '../../../hooks/useDebounce';
import { SearchAtom } from '../../state';
export interface ISearchboxProps {}
export interface ISearchboxProps {
placeholder?: string;
}
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({}: React.PropsWithChildren<ISearchboxProps>) => {
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({placeholder}: React.PropsWithChildren<ISearchboxProps>) => {
const [ value, setValue ] = React.useState('');
const [ , setDebounceValue ] = useRecoilState(SearchAtom);
const [ debounceSearchValue, setDebounceValue ] = useRecoilState(SearchAtom);
const debounceSearch = useDebounce<string>(value, 500);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
React.useEffect(() => {
if (!debounceSearchValue && value) {
setValue('');
}
} , [debounceSearchValue]);
React.useEffect(() => {
setDebounceValue(debounceSearch);
}, [debounceSearch]);
return (
<div className="flex space-x-4">
<div className="flex-1 min-w-0">
<div className="flex space-x-4 flex-1">
<div className="min-w-0">
<label htmlFor="search" className="sr-only">Search</label>
<div className="relative flex justify-center">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<SearchIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
<input
type="search"
name="search"
className={`block w-full py-2 pl-10 pr-3 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`}
placeholder="Search"
placeholder={placeholder || "Search"}
value={value}
onChange={handleChange}
/>

View File

@@ -1,37 +1,85 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { Menu } from '@headlessui/react';
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { ExtensionState } from '../../../constants';
import { SortOrder, SortType } from '../../../models';
import { SortOption } from '../../constants/SortOption';
import { SearchSelector, SortingAtom } from '../../state';
import { DashboardMessage } from '../../DashboardMessage';
import { NavigationType } from '../../models';
import { SortingOption } from '../../models/SortingOption';
import { SearchSelector, SettingsSelector, SortingAtom } from '../../state';
import { MenuButton, MenuItem, MenuItems } from '../Menu';
export interface ISortingProps {}
export interface ISortingProps {
disableCustomSorting?: boolean;
view: NavigationType;
}
export const sortOptions = [
{ name: "Last modified", id: SortOption.LastModified },
{ name: "By filename (asc)", id: SortOption.FileNameAsc },
{ name: "By filename (desc)", id: SortOption.FileNameDesc },
export const sortOptions: SortingOption[] = [
{ name: "Last modified (asc)", id: SortOption.LastModifiedAsc, order: SortOrder.asc, type: SortType.date },
{ name: "Last modified (desc)", id: SortOption.LastModifiedDesc, order: SortOrder.desc, type: SortType.date },
{ name: "By filename (asc)", id: SortOption.FileNameAsc, order: SortOrder.asc, type: SortType.string },
{ name: "By filename (desc)", id: SortOption.FileNameDesc, order: SortOrder.desc, type: SortType.string },
];
export const Sorting: React.FunctionComponent<ISortingProps> = ({}: React.PropsWithChildren<ISortingProps>) => {
export const Sorting: React.FunctionComponent<ISortingProps> = ({disableCustomSorting, view}: React.PropsWithChildren<ISortingProps>) => {
const [ crntSorting, setCrntSorting ] = useRecoilState(SortingAtom);
const searchValue = useRecoilValue(SearchSelector);
const settings = useRecoilValue(SettingsSelector);
const crntSort = sortOptions.find(x => x.id === crntSorting);
const updateSorting = (value: SortingOption) => {
Messenger.send(DashboardMessage.setState, {
key: `${view === NavigationType.Contents ? ExtensionState.Dashboard.Contents.Sorting : ExtensionState.Dashboard.Media.Sorting}`,
value: value
});
setCrntSorting(value)
};
let allOptions = [...sortOptions];
if (settings?.customSorting && !disableCustomSorting) {
allOptions = [...allOptions, ...settings.customSorting.map((s) => ({
title: s.title || s.name,
name: s.name,
id: s.id || `${s.name}-${s.order}`,
order: s.order,
type: s.type
}))];
}
let crntSortingOption = crntSorting;
if (!crntSortingOption) {
if (view === NavigationType.Contents) {
crntSortingOption = settings?.dashboardState?.contents?.sorting || null;
} else if (view === NavigationType.Media) {
crntSortingOption = settings?.dashboardState?.media?.sorting || null;
}
if (crntSortingOption === null) {
if (view === NavigationType.Contents && settings?.dashboardState.contents.defaultSorting) {
crntSortingOption = allOptions.find(f => f.id === settings?.dashboardState.contents.defaultSorting) || null;
} else if (view === NavigationType.Media && settings?.dashboardState.contents.defaultSorting) {
crntSortingOption = allOptions.find(f => f.id === settings?.dashboardState.contents.defaultSorting) || null;
}
}
}
let crntSort = allOptions.find(x => x.id === crntSortingOption?.id) || sortOptions[0];
return (
<div className="flex items-center">
<Menu as="div" className="relative z-10 inline-block text-left">
<MenuButton label={`Sort by`} title={crntSort?.name || ""} disabled={!!searchValue} />
<MenuButton label={`Sort by`} title={crntSort?.title || crntSort?.name || ""} disabled={!!searchValue} />
<MenuItems>
{sortOptions.map((option) => (
{allOptions.map((option) => (
<MenuItem
key={option.id}
title={option.name}
value={option.id}
isCurrent={option.id === crntSorting}
onClick={(value) => setCrntSorting(value)} />
title={option.title || option.name}
value={option}
isCurrent={option.id === crntSort.id}
onClick={(value) => updateSorting(value)} />
))}
</MenuItems>
</Menu>

View File

@@ -1,9 +1,10 @@
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { ViewAtom, ViewType, SettingsSelector } from '../../state';
import { ViewGridIcon, ViewListIcon } from '@heroicons/react/solid';
import { ViewAtom, SettingsSelector } from '../../state';
import {ViewListIcon, ViewGridIcon} from '@heroicons/react/solid';
import { Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { DashboardViewType } from '../../models';
export interface IViewSwitchProps {}
@@ -12,7 +13,7 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (props: Rea
const settings = useRecoilValue(SettingsSelector);
const toggleView = () => {
const newView = view === ViewType.Grid ? ViewType.List : ViewType.Grid;
const newView = view === DashboardViewType.Grid ? DashboardViewType.List : DashboardViewType.Grid;
setView(newView);
Messenger.send(DashboardMessage.setPageViewType, newView);
};
@@ -25,11 +26,11 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (props: Rea
return (
<div className={`flex rounded-sm bg-vulcan-50 lg:mb-1`}>
<button className={`flex items-center px-2 py-1 rounded-l-sm ${view === ViewType.Grid ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<button className={`flex items-center px-2 py-1 rounded-l-sm ${view === DashboardViewType.Grid ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<ViewGridIcon className={`w-4 h-4`} />
<span className={`sr-only`}>Change to grid</span>
</button>
<button className={`flex items-center px-2 py-1 rounded-r-sm ${view === ViewType.List ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<button className={`flex items-center px-2 py-1 rounded-r-sm ${view === DashboardViewType.List ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<ViewListIcon className={`w-4 h-4`} />
<span className={`sr-only`}>Change to list</span>
</button>

View File

@@ -0,0 +1,53 @@
import * as React from 'react';
import {FolderAddIcon} from '@heroicons/react/outline';
import { useRecoilValue } from 'recoil';
import { DashboardMessage } from '../../DashboardMessage';
import { SelectedMediaFolderAtom, SettingsSelector } from '../../state';
import { Messenger } from '@estruyf/vscode/dist/client';
import { ChoiceButton } from '../ChoiceButton';
import { CustomScript, ScriptType } from '../../../models';
export interface IFolderCreationProps {}
export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (props: React.PropsWithChildren<IFolderCreationProps>) => {
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
const settings = useRecoilValue(SettingsSelector);
const onFolderCreation = () => {
Messenger.send(DashboardMessage.createMediaFolder, {
selectedFolder
});
};
const runCustomScript = (script: CustomScript) => {
Messenger.send(DashboardMessage.runCustomScript, {script, path: selectedFolder});
};
const scripts = (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFolder);
if (scripts.length > 0) {
return (
<div className="flex flex-1 justify-end">
<ChoiceButton
title={`Create new folder`}
choices={scripts.map(s => ({
title: s.title,
onClick: () => runCustomScript(s)
}))}
onClick={onFolderCreation}
disabled={!settings?.initialized} />
</div>
)
}
return (
<div className="flex flex-1 justify-end">
<button
className={`inline-flex items-center px-3 py-1 border border-transparent text-xs leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500`}
title={`Create new folder`}
onClick={onFolderCreation}>
<FolderAddIcon className={`mr-2 h-6 w-6`} />
<span className={``}>Create new folder</span>
</button>
</div>
);
};

View File

@@ -0,0 +1,31 @@
import {FolderIcon} from '@heroicons/react/solid';
import { basename } from 'path';
import * as React from 'react';
import { useRecoilState } from 'recoil';
import { SelectedMediaFolderAtom } from '../../state';
export interface IFolderItemProps {
folder: string;
wsFolder?: string;
staticFolder?: string;
}
export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({ folder, wsFolder, staticFolder }: React.PropsWithChildren<IFolderItemProps>) => {
const [ , setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder;
return (
<li className={`group relative hover:shadow-xl dark:hover:bg-vulcan-100 text-gray-600 hover:text-gray-700 dark:text-whisper-900 dark:hover:text-whisper-800 p-4`}>
<button className={`w-full flex flex-row items-center h-full`} onClick={() => setSelectedFolder(folder)}>
<div>
<FolderIcon className={`h-12 w-12 mr-4`} />
</div>
<p className="text-sm font-bold pointer-events-none flex items-center text-left overflow-hidden break-words">
{basename(relFolderPath)}
</p>
</button>
</li>
);
};

View File

@@ -1,77 +0,0 @@
import { Menu } from '@headlessui/react';
import { XIcon } from '@heroicons/react/outline';
import Downshift from 'downshift';
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { MediaFoldersSelector, SelectedMediaFolderAtom } from '../../state';
export interface IFolderSelectionProps {}
export const FolderSelection: React.FunctionComponent<IFolderSelectionProps> = (props: React.PropsWithChildren<IFolderSelectionProps>) => {
const folders = useRecoilValue(MediaFoldersSelector);
const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const [ focus, setFocus ] = React.useState(false);
let allFolders: string[] = Object.assign([], folders);
allFolders = allFolders.sort((a: string, b: string) => {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
});
return (
<div>
<Downshift
isOpen={focus}
selectedItem={selectedFolder}
onOuterClick={() => setFocus(false)}
onSelect={(selFolder) => {
setSelectedFolder(selFolder);
setFocus(false);
}}>
{
({
getInputProps,
getItemProps,
getMenuProps,
isOpen,
inputValue,
getRootProps
}) => (
<div className={`relative flex items-center`}>
<label className={`text-sm text-gray-500 dark:text-whisper-900`}>Filter by: </label>
<div
className={`inline-flex items-center`}
{...getRootProps({} as any, {suppressRefError: true})}
>
<input disabled={!!selectedFolder} onFocus={() => setFocus(true)} className={`ml-2 py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`} {...getInputProps()} />
{
selectedFolder && (
<button title={`Clear`} onClick={() => setSelectedFolder(null)}><XIcon className={`ml-2 h-6 w-6 text-red-500 hover:text-red-800`} /></button>
)
}
</div>
<div className={`${focus ? `block` : `hidden`} top-8 absolute right-0 z-10 mt-2 w-min rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto`} {...getMenuProps()}>
{isOpen
? allFolders
.filter((item: string) => !inputValue || item.includes(inputValue))
.map((item, index) => (
<div
className="cursor-pointer text-gray-500 dark:text-whisper-900 block px-4 py-2 text-sm font-medium w-full text-left hover:bg-gray-100 hover:text-gray-700 dark:hover:text-whisper-600 dark:hover:bg-vulcan-100"
{...getItemProps({ key: item, index, item })}
>
{item}
</div>
))
: null}
</div>
</div>
)
}
</Downshift>
</div>
);
};

View File

@@ -1,27 +1,37 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { CheckCircleIcon, ClipboardCopyIcon, CodeIcon, PhotographIcon, TrashIcon } from '@heroicons/react/outline';
import { Menu } from '@headlessui/react';
import { ClipboardIcon, CodeIcon, PencilIcon, PhotographIcon, PlusIcon, TrashIcon } from '@heroicons/react/outline';
import { basename, dirname } from 'path';
import * as React from 'react';
import { useEffect } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { CustomScript } from '../../../helpers/CustomScript';
import { parseWinPath } from '../../../helpers/parseWinPath';
import { ScriptType } from '../../../models';
import { MediaInfo } from '../../../models/MediaPaths';
import { DashboardMessage } from '../../DashboardMessage';
import { LightboxAtom, SelectedMediaFolderSelector, SettingsSelector, ViewDataSelector } from '../../state';
import { LightboxAtom, PageSelector, SelectedMediaFolderSelector, SettingsSelector, ViewDataSelector } from '../../state';
import { MenuItem, MenuItems } from '../Menu';
import { Alert } from '../Modals/Alert';
import { Metadata } from '../Modals/Metadata';
import { MenuButton } from './MenuButton'
import { QuickAction } from './QuickAction';
export interface IItemProps {
media: MediaInfo;
}
export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWithChildren<IItemProps>) => {
const settings = useRecoilValue(SettingsSelector);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const [ , setLightbox ] = useRecoilState(LightboxAtom);
const [ showAlert, setShowAlert ] = React.useState(false);
const [ showForm, setShowForm ] = React.useState(false);
const [ caption, setCaption ] = React.useState(media.caption);
const [ alt, setAlt ] = React.useState(media.alt);
const [ filename, setFilename ] = React.useState<string | null>(null);
const settings = useRecoilValue(SettingsSelector);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const viewData = useRecoilValue(ViewDataSelector);
const parseWinPath = (path: string | undefined) => {
return path?.split(`\\`).join(`/`);
}
const page = useRecoilValue(PageSelector);
const getFolder = () => {
if (settings?.wsFolder && media.fsPath) {
@@ -48,29 +58,48 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
return relPath;
};
const getFileName = () => {
return basename(parseWinPath(media.fsPath) || "");
};
const copyToClipboard = () => {
const relPath = getRelPath();
Messenger.send(DashboardMessage.copyToClipboard, parseWinPath(relPath) || "");
};
const runCustomScript = (script: CustomScript) => {
Messenger.send(DashboardMessage.runCustomScript, {script, path: media.fsPath});
};
const insertToArticle = () => {
const relPath = getRelPath();
Messenger.send(DashboardMessage.insertPreviewImage, {
image: parseWinPath(relPath) || "",
file: viewData?.data?.filePath,
fieldName: viewData?.data?.fieldName,
position: viewData?.data?.position || null
multiple: viewData?.data?.multiple,
value: viewData?.data?.value,
position: viewData?.data?.position || null,
alt: alt || "",
caption: caption || ""
});
};
const insertSnippet = () => {
const relPath = getRelPath();
let snippet = settings?.mediaSnippet.join("\n");
snippet = snippet?.replace("{mediaUrl}", parseWinPath(relPath) || "");
snippet = snippet?.replace("{alt}", alt || "");
snippet = snippet?.replace("{caption}", caption || "");
snippet = snippet?.replace("{filename}", basename(relPath || ""));
Messenger.send(DashboardMessage.insertPreviewImage, {
image: parseWinPath(relPath) || "",
file: viewData?.data?.filePath,
fieldName: viewData?.data?.fieldName,
position: viewData?.data?.position || null,
snippet: settings?.mediaSnippet.join("\n").replace("{mediaUrl}", parseWinPath(relPath) || "")
snippet
});
};
@@ -86,23 +115,87 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
};
const calculateSize = () => {
if (media?.stats?.size) {
const size = media.stats.size / (1024*1024);
if (size > 1) {
return `${size.toFixed(2)} MB`;
} else {
return `${(size * 1024).toFixed(2)} KB`;
let sizeDetails = [];
if (media?.dimensions) {
if (media.dimensions.width && media.dimensions.height) {
sizeDetails.push(`${media.dimensions.width}x${media.dimensions.height}`);
}
}
if (media?.size) {
const size = media.size / (1024*1024);
if (size > 1) {
sizeDetails.push(`${size.toFixed(2)} MB`);
} else {
sizeDetails.push(`${(size * 1024).toFixed(2)} KB`);
}
}
return sizeDetails.join(" — ");
};
const openLightbox = () => {
setLightbox(media.vsPath || "");
};
const updateMetadata = () => {
setShowForm(true);
};
const submitMetadata = () => {
Messenger.send(DashboardMessage.updateMediaMetadata, {
file: media.fsPath,
filename,
caption,
alt,
folder: selectedFolder,
page
});
setShowForm(false);
// Reset the values
setAlt(media.alt);
setCaption(media.caption);
setFilename(getFileName());
};
const customScriptActions = () => {
return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile).map(script => (
<MenuItem
key={script.title}
title={script.title}
onClick={() => runCustomScript(script)} />
))
}
useEffect(() => {
if (media.alt !== alt) {
setAlt(media.alt);
}
}, [media.alt]);
useEffect(() => {
if (media.caption !== caption) {
setCaption(media.caption);
}
}, [media.caption]);
useEffect(() => {
const name = basename(parseWinPath(media.fsPath) || "");
if (name !== filename) {
setFilename(getFileName());
}
}, [media.fsPath]);
const fileInfo = filename ? basename(filename).split('.') : null;
const extension = fileInfo?.pop();
const name = fileInfo?.join('.');
return (
<>
<li className="group relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100">
<li className="group relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100 border border-gray-100 dark:border-vulcan-50">
<button className="relative bg-gray-200 dark:bg-vulcan-300 block w-full aspect-w-10 aspect-h-7 overflow-hidden cursor-pointer h-48" onClick={openLightbox}>
<div className={`absolute top-0 right-0 bottom-0 left-0 flex items-center justify-center`}>
<PhotographIcon className={`h-1/2 text-gray-300 dark:text-vulcan-200`} />
@@ -111,64 +204,192 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
<img src={media.vsPath} alt={basename(media.fsPath)} className="mx-auto object-cover" />
</div>
</button>
<div className={`relative py-4 pl-4 pr-10`}>
<div className={`absolute top-4 right-4 flex flex-col space-y-2`}>
{
viewData?.data?.filePath ? (
<>
<button
title={`Insert into your article`}
className={`hover:text-teal-900 focus:outline-none`}
onClick={insertToArticle}>
<CheckCircleIcon className={`h-5 w-5`} />
<span className={`sr-only`}>Insert into your article</span>
</button>
<div className={`relative py-4 pl-4 pr-12`}>
<div className={`absolute top-4 right-4 flex flex-col space-y-4`}>
<div className="flex items-center border border-transparent group-hover:bg-gray-50 dark:group-hover:bg-vulcan-200 group-hover:border-gray-100 dark:group-hover:border-vulcan-50 rounded-full p-2 -mr-2 -mt-2">
<div className='hidden group-hover:inline-block h-5'>
<QuickAction
title='Edit metadata'
onClick={updateMetadata}>
<PencilIcon className={`h-5 w-5`} aria-hidden="true" />
</QuickAction>
{
viewData?.data?.filePath ? (
<>
<QuickAction
title='Insert image with markdown markup'
onClick={insertToArticle}>
<PlusIcon className={`h-5 w-5`} aria-hidden="true" />
</QuickAction>
{
(viewData?.data?.position && settings?.mediaSnippet && settings?.mediaSnippet.length > 0) && (
<QuickAction
title='Insert snippet'
onClick={insertSnippet}>
<CodeIcon className={`h-5 w-5`} aria-hidden="true" />
</QuickAction>
)
}
</>
) : (
<>
<QuickAction
title='Copy media path'
onClick={copyToClipboard}>
<ClipboardIcon className={`h-5 w-5`} aria-hidden="true" />
</QuickAction>
<QuickAction
title='Delete media file'
onClick={deleteMedia}>
<TrashIcon className={`h-5 w-5`} aria-hidden="true" />
</QuickAction>
</>
)
}
</div>
<Menu as="div" className="relative z-10 inline-block text-left h-5">
<MenuButton title={`Menu`} />
<MenuItems widthClass='w-40'>
<MenuItem
title={`Edit metadata`}
onClick={updateMetadata}
/>
{
(viewData?.data?.position && settings?.mediaSnippet && settings?.mediaSnippet.length > 0) && (
<button
title={`Insert your media snippet`}
className={`hover:text-teal-900 focus:outline-none`}
onClick={insertSnippet}>
<CodeIcon className={`h-5 w-5`} />
<span className={`sr-only`}>Insert your media snippet</span>
</button>
viewData?.data?.filePath ? (
<>
<MenuItem
title={`Insert image markdown`}
onClick={insertToArticle} />
{
(viewData?.data?.position && settings?.mediaSnippet && settings?.mediaSnippet.length > 0) && (
<MenuItem
title={`Insert snippet`}
onClick={insertSnippet} />
)
}
{ customScriptActions() }
</>
) : (
<>
<MenuItem
title={`Copy media path`}
onClick={copyToClipboard} />
{ customScriptActions() }
<MenuItem
title={`Delete`}
onClick={deleteMedia} />
</>
)
}
</>
) : (
<>
<button title={`Copy media path`}
className={`hover:text-teal-900 focus:outline-none`}
onClick={copyToClipboard}>
<ClipboardCopyIcon className={`h-5 w-5`} />
<span className={`sr-only`}>Copy media path</span>
</button>
<button title={`Delete media`}
className={`hover:text-teal-900 focus:outline-none`}
onClick={deleteMedia}>
<TrashIcon className={`h-5 w-5`} />
<span className={`sr-only`}>Delete media</span>
</button>
</>
)
}
</MenuItems>
</Menu>
</div>
</div>
<p className="text-sm dark:text-whisper-900 font-bold pointer-events-none flex items-center">
<p className="text-sm dark:text-whisper-900 font-bold pointer-events-none flex items-center break-all">
{basename(parseWinPath(media.fsPath) || "")}
</p>
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
<b className={`mr-2`}>Folder:</b> {getFolder()}
{
media.caption && (
<p className="mt-2 text-xs dark:text-whisper-900 font-medium pointer-events-none flex flex-col items-start">
<b className={`mr-2`}>Caption:</b>
<span className={`block mt-1 dark:text-whisper-500 text-xs`}>{media.caption}</span>
</p>
)
}
{
media.alt && (
<p className="mt-2 text-xs dark:text-whisper-900 font-medium pointer-events-none flex flex-col items-start">
<b className={`mr-2`}>Alt:</b>
<span className={`block mt-1 dark:text-whisper-500 text-xs`}>{media.alt}</span>
</p>
)
}
<p className="mt-2 text-xs dark:text-whisper-900 font-medium pointer-events-none flex flex-col items-start">
<b className={`mr-2`}>Folder:</b>
<span className={`block mt-1 dark:text-whisper-500 text-xs`}>{getFolder()}</span>
</p>
{
media?.stats?.size && (
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
<b className={`mr-1`}>Size:</b> {calculateSize()}
(media?.size || media?.dimensions) && (
<p className="mt-2 text-xs dark:text-whisper-900 font-medium pointer-events-none flex flex-col items-start">
<b className={`mr-1`}>Size:</b>
<span className={`block mt-1 dark:text-whisper-500 text-xs`}>{calculateSize()}</span>
</p>
)
}
</div>
</li>
{
showForm && (
<Metadata
title={`Set metadata for: ${basename(parseWinPath(media.fsPath) || "")}`}
description={`Please specify the metadata you want to set for the file.`}
okBtnText={`Save`}
cancelBtnText={`Cancel`}
dismiss={() => setShowForm(false)}
trigger={submitMetadata}
isSaveDisabled={!filename}>
<div className="flex flex-col space-y-2">
<div>
<label htmlFor="about" className="block text-sm font-medium text-gray-700 dark:text-whisper-900">
Filename
</label>
<div className="relative mt-1">
<input
className="py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none w-full"
value={name}
onChange={(e) => setFilename(`${e.target.value}.${extension}`)} />
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<span className="text-gray-500 sm:text-sm">
.{extension}
</span>
</div>
</div>
</div>
<div>
<label htmlFor="about" className="block text-sm font-medium text-gray-700 dark:text-whisper-900">
Caption
</label>
<div className="mt-1">
<textarea
rows={3}
className="py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none w-full"
value={caption || ''}
onChange={(e) => setCaption(e.target.value)}
/>
</div>
</div>
<div>
<label htmlFor="about" className="block text-sm font-medium text-gray-700 dark:text-whisper-900">
Alt tag value
</label>
<div className="mt-1">
<input
className="py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none w-full"
value={alt || ''}
onChange={(e) => setAlt(e.target.value)}
/>
</div>
</div>
</div>
</Metadata>
)
}
{
showAlert && (
<Alert

View File

@@ -1,10 +1,14 @@
import * as React from 'react';
export interface IListProps {}
export interface IListProps {
gap?: number;
}
export const List: React.FunctionComponent<IListProps> = ({gap, children}: React.PropsWithChildren<IListProps>) => {
const gapClass = gap !== undefined ? `gap-y-${gap}` : `gap-y-8`;
export const List: React.FunctionComponent<IListProps> = ({children}: React.PropsWithChildren<IListProps>) => {
return (
<ul role="list" className="grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8">
<ul role="list" className={`grid grid-cols-2 gap-x-4 ${gapClass} sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`}>
{children}
</ul>
);

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