@lvce-editor/extension-detail-view 7.11.0 → 7.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const emptyObject = {};
|
|
2
|
-
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
|
|
3
2
|
const i18nString = (key, placeholders = emptyObject) => {
|
|
4
3
|
if (placeholders === emptyObject) {
|
|
5
4
|
return key;
|
|
6
5
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
let result = key;
|
|
7
|
+
for (const [placeholder, replacement] of Object.entries(placeholders)) {
|
|
8
|
+
result = result.split(`{${placeholder}}`).join(String(replacement));
|
|
9
|
+
}
|
|
10
|
+
return result;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
const ActivationEvents$1 = 'Activation Events';
|
|
@@ -23,9 +23,11 @@ const CopyLink = 'Copy Link';
|
|
|
23
23
|
const Created = 'Created';
|
|
24
24
|
const Details$1 = 'Details';
|
|
25
25
|
const Disable$1 = 'Disable';
|
|
26
|
+
const DownloadCount = 'Downloads';
|
|
26
27
|
const Elements = 'Elements';
|
|
27
28
|
const EmptyCommandsArray = 'The extension contributes an empty commands array.';
|
|
28
29
|
const Enable$1 = 'Enable';
|
|
30
|
+
const ExtensionNotAvailable = 'The extension "{PH1}" is not available in this version of LVCE Editor.';
|
|
29
31
|
const Features$2 = 'Features';
|
|
30
32
|
const FileExtensions = 'File Extensions';
|
|
31
33
|
const FileMatch = 'File Match';
|
|
@@ -35,9 +37,10 @@ const Identifier = 'Identifier';
|
|
|
35
37
|
const ImportTime = 'Import Time: ';
|
|
36
38
|
const Installation = 'Installation';
|
|
37
39
|
const InvalidLink = 'Invalid link';
|
|
40
|
+
const ImageCannotBeLoaded = 'Image cannot be loaded';
|
|
38
41
|
const Issues = 'Issues';
|
|
39
42
|
const JsonValidation$1 = 'Json Validation';
|
|
40
|
-
const Label = 'Label';
|
|
43
|
+
const Label$1 = 'Label';
|
|
41
44
|
const LastReleased = 'Last Released';
|
|
42
45
|
const LastUpdated = 'Last Updated';
|
|
43
46
|
const License = 'License';
|
|
@@ -50,6 +53,7 @@ const OpenInNewTab = 'Open in New Tab';
|
|
|
50
53
|
const ProgrammingLanguages$1 = 'Programming Languages';
|
|
51
54
|
const PropertyMustBeOfTypeString = 'Property must be a string';
|
|
52
55
|
const Published = 'Published';
|
|
56
|
+
const Rating = 'Rating';
|
|
53
57
|
const Repository = 'Repository';
|
|
54
58
|
const Resources$1 = 'Resources';
|
|
55
59
|
const RuntimeStatus$1 = 'Runtime Status';
|
|
@@ -67,6 +71,8 @@ const Status = 'Status';
|
|
|
67
71
|
const StringMustNotBeEmpty = 'String must not be empty';
|
|
68
72
|
const Theme$1 = 'Theme';
|
|
69
73
|
const Uninstall$1 = 'Uninstall';
|
|
74
|
+
const UnableToLoadExtension = 'Unable to load extension';
|
|
75
|
+
const UnableToLoadExtensionWithError = 'The extension details could not be loaded: {PH1}';
|
|
70
76
|
const UnsupportedFeature = 'Unsupported Feature';
|
|
71
77
|
const Version = 'Version';
|
|
72
78
|
const WebViews$1 = 'WebViews';
|
|
@@ -114,7 +120,7 @@ const openInNewTab = () => {
|
|
|
114
120
|
return i18nString(OpenInNewTab);
|
|
115
121
|
};
|
|
116
122
|
const label = () => {
|
|
117
|
-
return i18nString(Label);
|
|
123
|
+
return i18nString(Label$1);
|
|
118
124
|
};
|
|
119
125
|
const openImageInNewTab$1 = () => {
|
|
120
126
|
return i18nString(OpenImageInNewTab);
|
|
@@ -137,6 +143,11 @@ const setColorTheme$2 = () => {
|
|
|
137
143
|
const enable = () => {
|
|
138
144
|
return i18nString(Enable$1);
|
|
139
145
|
};
|
|
146
|
+
const extensionNotAvailable = extensionId => {
|
|
147
|
+
return i18nString(ExtensionNotAvailable, {
|
|
148
|
+
PH1: extensionId
|
|
149
|
+
});
|
|
150
|
+
};
|
|
140
151
|
const theme = () => {
|
|
141
152
|
return i18nString(Theme$1);
|
|
142
153
|
};
|
|
@@ -167,6 +178,9 @@ const id$1 = () => {
|
|
|
167
178
|
const name = () => {
|
|
168
179
|
return i18nString(Name);
|
|
169
180
|
};
|
|
181
|
+
const imageCannotBeLoaded = () => {
|
|
182
|
+
return i18nString(ImageCannotBeLoaded);
|
|
183
|
+
};
|
|
170
184
|
const fileExtensions = () => {
|
|
171
185
|
return i18nString(FileExtensions);
|
|
172
186
|
};
|
|
@@ -188,6 +202,14 @@ const elements = () => {
|
|
|
188
202
|
const uninstall = () => {
|
|
189
203
|
return i18nString(Uninstall$1);
|
|
190
204
|
};
|
|
205
|
+
const unableToLoadExtension = () => {
|
|
206
|
+
return i18nString(UnableToLoadExtension);
|
|
207
|
+
};
|
|
208
|
+
const unableToLoadExtensionWithError = errorMessage => {
|
|
209
|
+
return i18nString(UnableToLoadExtensionWithError, {
|
|
210
|
+
PH1: errorMessage
|
|
211
|
+
});
|
|
212
|
+
};
|
|
191
213
|
const scrollToTop = () => {
|
|
192
214
|
return i18nString(ScrollToTop$1);
|
|
193
215
|
};
|
|
@@ -239,6 +261,12 @@ const repository = () => {
|
|
|
239
261
|
const license = () => {
|
|
240
262
|
return i18nString(License);
|
|
241
263
|
};
|
|
264
|
+
const downloadCount = () => {
|
|
265
|
+
return i18nString(DownloadCount);
|
|
266
|
+
};
|
|
267
|
+
const rating = () => {
|
|
268
|
+
return i18nString(Rating);
|
|
269
|
+
};
|
|
242
270
|
const propertyMustBeOfTypeString = () => {
|
|
243
271
|
return i18nString(PropertyMustBeOfTypeString);
|
|
244
272
|
};
|
|
@@ -302,9 +330,14 @@ const Panel = 'panel';
|
|
|
302
330
|
const Tab = 'tab';
|
|
303
331
|
const TabList = 'tablist';
|
|
304
332
|
|
|
333
|
+
const Audio = 0;
|
|
305
334
|
const Button$1 = 1;
|
|
335
|
+
const Col = 2;
|
|
336
|
+
const ColGroup = 3;
|
|
306
337
|
const Div = 4;
|
|
307
338
|
const H1 = 5;
|
|
339
|
+
const Input = 6;
|
|
340
|
+
const Kbd = 7;
|
|
308
341
|
const Span = 8;
|
|
309
342
|
const Table$1 = 9;
|
|
310
343
|
const TBody = 10;
|
|
@@ -313,20 +346,159 @@ const Text$1 = 12;
|
|
|
313
346
|
const Th = 13;
|
|
314
347
|
const THead = 14;
|
|
315
348
|
const Tr = 15;
|
|
349
|
+
const I = 16;
|
|
316
350
|
const Img = 17;
|
|
351
|
+
const Root = 0;
|
|
352
|
+
const Ins = 20;
|
|
353
|
+
const Del = 21;
|
|
317
354
|
const H2 = 22;
|
|
355
|
+
const H3 = 23;
|
|
356
|
+
const H4 = 24;
|
|
357
|
+
const H5 = 25;
|
|
358
|
+
const H6 = 26;
|
|
359
|
+
const Article = 27;
|
|
318
360
|
const Aside$1 = 28;
|
|
361
|
+
const Footer = 29;
|
|
362
|
+
const Header = 30;
|
|
363
|
+
const Nav = 40;
|
|
364
|
+
const Section = 41;
|
|
365
|
+
const Search = 42;
|
|
319
366
|
const Dd = 43;
|
|
320
367
|
const Dl = 44;
|
|
368
|
+
const Figcaption = 45;
|
|
369
|
+
const Figure = 46;
|
|
370
|
+
const Hr = 47;
|
|
321
371
|
const Li = 48;
|
|
372
|
+
const Ol = 49;
|
|
322
373
|
const P = 50;
|
|
323
374
|
const Pre = 51;
|
|
324
375
|
const A = 53;
|
|
376
|
+
const Abbr = 54;
|
|
377
|
+
const Br = 55;
|
|
378
|
+
const Cite = 56;
|
|
379
|
+
const Data = 57;
|
|
380
|
+
const Time = 58;
|
|
381
|
+
const Tfoot = 59;
|
|
325
382
|
const Ul = 60;
|
|
383
|
+
const Video = 61;
|
|
384
|
+
const TextArea = 62;
|
|
385
|
+
const Select = 63;
|
|
386
|
+
const Option = 64;
|
|
326
387
|
const Code$2 = 65;
|
|
388
|
+
const Label = 66;
|
|
327
389
|
const Dt = 67;
|
|
390
|
+
const Iframe = 68;
|
|
391
|
+
const Main = 69;
|
|
392
|
+
const Strong = 70;
|
|
393
|
+
const Em = 71;
|
|
394
|
+
const Style = 72;
|
|
395
|
+
const Html = 73;
|
|
396
|
+
const Head = 74;
|
|
397
|
+
const Title = 75;
|
|
398
|
+
const Meta = 76;
|
|
399
|
+
const Canvas = 77;
|
|
400
|
+
const Form = 78;
|
|
401
|
+
const BlockQuote = 79;
|
|
402
|
+
const Quote = 80;
|
|
403
|
+
const Circle = 81;
|
|
404
|
+
const Defs = 82;
|
|
405
|
+
const Ellipse = 83;
|
|
406
|
+
const G = 84;
|
|
407
|
+
const Line = 85;
|
|
408
|
+
const Path = 86;
|
|
409
|
+
const Polygon = 87;
|
|
410
|
+
const Polyline = 88;
|
|
411
|
+
const Rect = 89;
|
|
412
|
+
const Svg = 90;
|
|
413
|
+
const Use = 91;
|
|
328
414
|
const Reference = 100;
|
|
329
415
|
|
|
416
|
+
const VirtualDomElements = {
|
|
417
|
+
__proto__: null,
|
|
418
|
+
A,
|
|
419
|
+
Abbr,
|
|
420
|
+
Article,
|
|
421
|
+
Aside: Aside$1,
|
|
422
|
+
Audio,
|
|
423
|
+
BlockQuote,
|
|
424
|
+
Br,
|
|
425
|
+
Button: Button$1,
|
|
426
|
+
Canvas,
|
|
427
|
+
Circle,
|
|
428
|
+
Cite,
|
|
429
|
+
Code: Code$2,
|
|
430
|
+
Col,
|
|
431
|
+
ColGroup,
|
|
432
|
+
Data,
|
|
433
|
+
Dd,
|
|
434
|
+
Defs,
|
|
435
|
+
Del,
|
|
436
|
+
Div,
|
|
437
|
+
Dl,
|
|
438
|
+
Dt,
|
|
439
|
+
Ellipse,
|
|
440
|
+
Em,
|
|
441
|
+
Figcaption,
|
|
442
|
+
Figure,
|
|
443
|
+
Footer,
|
|
444
|
+
Form,
|
|
445
|
+
G,
|
|
446
|
+
H1,
|
|
447
|
+
H2,
|
|
448
|
+
H3,
|
|
449
|
+
H4,
|
|
450
|
+
H5,
|
|
451
|
+
H6,
|
|
452
|
+
Head,
|
|
453
|
+
Header,
|
|
454
|
+
Hr,
|
|
455
|
+
Html,
|
|
456
|
+
I,
|
|
457
|
+
Iframe,
|
|
458
|
+
Img,
|
|
459
|
+
Input,
|
|
460
|
+
Ins,
|
|
461
|
+
Kbd,
|
|
462
|
+
Label,
|
|
463
|
+
Li,
|
|
464
|
+
Line,
|
|
465
|
+
Main,
|
|
466
|
+
Meta,
|
|
467
|
+
Nav,
|
|
468
|
+
Ol,
|
|
469
|
+
Option,
|
|
470
|
+
P,
|
|
471
|
+
Path,
|
|
472
|
+
Polygon,
|
|
473
|
+
Polyline,
|
|
474
|
+
Pre,
|
|
475
|
+
Quote,
|
|
476
|
+
Rect,
|
|
477
|
+
Reference,
|
|
478
|
+
Root,
|
|
479
|
+
Search,
|
|
480
|
+
Section,
|
|
481
|
+
Select,
|
|
482
|
+
Span,
|
|
483
|
+
Strong,
|
|
484
|
+
Style,
|
|
485
|
+
Svg,
|
|
486
|
+
TBody,
|
|
487
|
+
THead,
|
|
488
|
+
Table: Table$1,
|
|
489
|
+
Td,
|
|
490
|
+
Text: Text$1,
|
|
491
|
+
TextArea,
|
|
492
|
+
Tfoot,
|
|
493
|
+
Th,
|
|
494
|
+
Time,
|
|
495
|
+
Title,
|
|
496
|
+
Tr,
|
|
497
|
+
Ul,
|
|
498
|
+
Use,
|
|
499
|
+
Video
|
|
500
|
+
};
|
|
501
|
+
|
|
330
502
|
const ClientX = 'event.clientX';
|
|
331
503
|
const ClientY = 'event.clientY';
|
|
332
504
|
const TargetHref = 'event.target.href';
|
|
@@ -371,6 +543,8 @@ const text = data => {
|
|
|
371
543
|
};
|
|
372
544
|
};
|
|
373
545
|
|
|
546
|
+
new Set(Object.values(VirtualDomElements));
|
|
547
|
+
|
|
374
548
|
const SetText = 1;
|
|
375
549
|
const Replace = 2;
|
|
376
550
|
const SetAttribute = 3;
|
|
@@ -642,6 +816,7 @@ const Categories = 'Categories';
|
|
|
642
816
|
const Category = 'Category';
|
|
643
817
|
const Changelog$1 = 'Changelog';
|
|
644
818
|
const MaskIcon = 'MaskIcon';
|
|
819
|
+
const MaskIconWarning = 'MaskIconWarning';
|
|
645
820
|
const ResourceIcon = 'ResourceIcon';
|
|
646
821
|
const Code$1 = 'Code';
|
|
647
822
|
const DefaultMarkdown = 'DefaultMarkdown';
|
|
@@ -651,13 +826,22 @@ const DefinitionListItemHeading = 'DefinitionListItemHeading';
|
|
|
651
826
|
const DefinitionListItemValue = 'DefinitionListItemValue';
|
|
652
827
|
const ExtensionDetail = 'ExtensionDetail';
|
|
653
828
|
const ExtensionDetailDescription = 'ExtensionDetailDescription';
|
|
829
|
+
const ExtensionDetailDownloadCount = 'ExtensionDetailDownloadCount';
|
|
830
|
+
const ExtensionDetailError = 'ExtensionDetailError';
|
|
831
|
+
const ExtensionDetailErrorCard = 'ExtensionDetailErrorCard';
|
|
832
|
+
const ExtensionDetailErrorIcon = 'ExtensionDetailErrorIcon';
|
|
833
|
+
const ExtensionDetailErrorMessage = 'ExtensionDetailErrorMessage';
|
|
834
|
+
const ExtensionDetailErrorTitle = 'ExtensionDetailErrorTitle';
|
|
654
835
|
const ExtensionDetailHeader = 'ExtensionDetailHeader';
|
|
655
836
|
const ExtensionDetailHeaderActions = 'ExtensionDetailHeaderActions';
|
|
656
837
|
const ExtensionDetailHeaderDetails = 'ExtensionDetailHeaderDetails';
|
|
657
838
|
const ExtensionDetailIcon = 'ExtensionDetailIcon';
|
|
839
|
+
const ExtensionDetailMetadata = 'ExtensionDetailMetadata';
|
|
658
840
|
const ExtensionDetailName = 'ExtensionDetailName';
|
|
659
841
|
const ExtensionDetailNameBadge = 'ExtensionDetailNameBadge';
|
|
660
842
|
const ExtensionDetailPanel = 'ExtensionDetailPanel';
|
|
843
|
+
const ExtensionDetailRating = 'ExtensionDetailRating';
|
|
844
|
+
const ExtensionDetailStatistic = 'ExtensionDetailStatistic';
|
|
661
845
|
const ExtensionDetailTab = 'ExtensionDetailTab';
|
|
662
846
|
const ExtensionDetailTabs = 'ExtensionDetailTabs';
|
|
663
847
|
const ExtensionDetailTabSelected = 'ExtensionDetailTabSelected';
|
|
@@ -669,6 +853,7 @@ const FeatureWebView = 'FeatureWebView';
|
|
|
669
853
|
const Large$1 = 'Large';
|
|
670
854
|
const Link$1 = 'Link';
|
|
671
855
|
const Markdown = 'Markdown';
|
|
856
|
+
const MarkdownImageError = 'MarkdownImageError';
|
|
672
857
|
const MoreInfo = 'MoreInfo';
|
|
673
858
|
const MoreInfoEntry = 'MoreInfoEntry';
|
|
674
859
|
const MoreInfoEntryKey = 'MoreInfoEntryKey';
|
|
@@ -1375,7 +1560,7 @@ class AssertionError extends Error {
|
|
|
1375
1560
|
const Object$1 = 1;
|
|
1376
1561
|
const Number$1 = 2;
|
|
1377
1562
|
const Array$1 = 3;
|
|
1378
|
-
const String = 4;
|
|
1563
|
+
const String$1 = 4;
|
|
1379
1564
|
const Boolean$1 = 5;
|
|
1380
1565
|
const Function = 6;
|
|
1381
1566
|
const Null = 7;
|
|
@@ -1387,7 +1572,7 @@ const getType = value => {
|
|
|
1387
1572
|
case 'function':
|
|
1388
1573
|
return Function;
|
|
1389
1574
|
case 'string':
|
|
1390
|
-
return String;
|
|
1575
|
+
return String$1;
|
|
1391
1576
|
case 'object':
|
|
1392
1577
|
if (value === null) {
|
|
1393
1578
|
return Null;
|
|
@@ -1410,7 +1595,7 @@ const number = value => {
|
|
|
1410
1595
|
};
|
|
1411
1596
|
const string = value => {
|
|
1412
1597
|
const type = getType(value);
|
|
1413
|
-
if (type !== String) {
|
|
1598
|
+
if (type !== String$1) {
|
|
1414
1599
|
throw new AssertionError('expected value to be of type string');
|
|
1415
1600
|
}
|
|
1416
1601
|
};
|
|
@@ -1457,7 +1642,6 @@ const walkValue = (value, transferrables, isTransferrable) => {
|
|
|
1457
1642
|
for (const property of Object.values(value)) {
|
|
1458
1643
|
walkValue(property, transferrables, isTransferrable);
|
|
1459
1644
|
}
|
|
1460
|
-
return;
|
|
1461
1645
|
}
|
|
1462
1646
|
};
|
|
1463
1647
|
const getTransferrables = value => {
|
|
@@ -1611,7 +1795,14 @@ class IpcError extends VError {
|
|
|
1611
1795
|
const cause = new Error(message);
|
|
1612
1796
|
// @ts-ignore
|
|
1613
1797
|
cause.code = code;
|
|
1614
|
-
|
|
1798
|
+
if (stack) {
|
|
1799
|
+
Object.defineProperty(cause, 'stack', {
|
|
1800
|
+
configurable: true,
|
|
1801
|
+
enumerable: false,
|
|
1802
|
+
value: stack,
|
|
1803
|
+
writable: true
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1615
1806
|
super(cause, betterMessage);
|
|
1616
1807
|
} else {
|
|
1617
1808
|
super(betterMessage);
|
|
@@ -2810,6 +3001,25 @@ const HandleTabFocus = 18;
|
|
|
2810
3001
|
const HandleResourceLinkClick = 19;
|
|
2811
3002
|
const HandleMouseEnterEnable = 20;
|
|
2812
3003
|
const HandleMouseLeaveEnable = 21;
|
|
3004
|
+
const HandleHeaderContextMenu = 22;
|
|
3005
|
+
const HandleMarkdownImageError = 23;
|
|
3006
|
+
|
|
3007
|
+
const addMarkdownImageErrorHandler = node => {
|
|
3008
|
+
if (node.type !== Img) {
|
|
3009
|
+
return node;
|
|
3010
|
+
}
|
|
3011
|
+
const {
|
|
3012
|
+
onError,
|
|
3013
|
+
...rest
|
|
3014
|
+
} = node;
|
|
3015
|
+
return {
|
|
3016
|
+
onError: HandleMarkdownImageError,
|
|
3017
|
+
...rest
|
|
3018
|
+
};
|
|
3019
|
+
};
|
|
3020
|
+
const addMarkdownImageErrorHandlers = dom => {
|
|
3021
|
+
return dom.map(addMarkdownImageErrorHandler);
|
|
3022
|
+
};
|
|
2813
3023
|
|
|
2814
3024
|
const ActivationEvents = 'ActivationEvents';
|
|
2815
3025
|
const Changelog = 'Changelog';
|
|
@@ -2851,19 +3061,22 @@ const {
|
|
|
2851
3061
|
set: set$3
|
|
2852
3062
|
} = MarkdownWorker;
|
|
2853
3063
|
|
|
3064
|
+
const addScrollToTopVirtualDom = dom => {
|
|
3065
|
+
const [firstNode, ...rest] = dom;
|
|
3066
|
+
const extraDom = getScrollToTopVirtualDom();
|
|
3067
|
+
return [{
|
|
3068
|
+
...firstNode,
|
|
3069
|
+
childCount: firstNode.childCount + 1,
|
|
3070
|
+
onClick: HandleReadmeClick,
|
|
3071
|
+
onScroll: HandleReadmeScroll,
|
|
3072
|
+
onSelectionChange: HandleSelectionChange
|
|
3073
|
+
}, ...extraDom, ...rest];
|
|
3074
|
+
};
|
|
2854
3075
|
const getMarkdownVirtualDom = async (html, options) => {
|
|
2855
3076
|
string(html);
|
|
2856
|
-
const dom = await getVirtualDom(html);
|
|
3077
|
+
const dom = addMarkdownImageErrorHandlers(await getVirtualDom(html));
|
|
2857
3078
|
if (options?.scrollToTopEnabled) {
|
|
2858
|
-
|
|
2859
|
-
const extraDom = getScrollToTopVirtualDom();
|
|
2860
|
-
return [{
|
|
2861
|
-
...firstNode,
|
|
2862
|
-
childCount: firstNode.childCount + 1,
|
|
2863
|
-
onClick: HandleReadmeClick,
|
|
2864
|
-
onScroll: HandleReadmeScroll,
|
|
2865
|
-
onSelectionChange: HandleSelectionChange
|
|
2866
|
-
}, ...extraDom, ...rest];
|
|
3079
|
+
return addScrollToTopVirtualDom(dom);
|
|
2867
3080
|
}
|
|
2868
3081
|
return dom;
|
|
2869
3082
|
};
|
|
@@ -3223,10 +3436,67 @@ const toCommandId = key => {
|
|
|
3223
3436
|
return key.slice(dotIndex + 1);
|
|
3224
3437
|
};
|
|
3225
3438
|
const create$1 = () => {
|
|
3439
|
+
const commandQueues = new Map();
|
|
3440
|
+
const generations = Object.create(null);
|
|
3226
3441
|
const states = Object.create(null);
|
|
3227
3442
|
const commandMapRef = {};
|
|
3443
|
+
const getGeneration = uid => generations[uid] || 0;
|
|
3444
|
+
const isCurrentGeneration = (uid, generation) => {
|
|
3445
|
+
return states[uid] !== undefined && getGeneration(uid) === generation;
|
|
3446
|
+
};
|
|
3447
|
+
const updateState = (uid, generation, fallbackState, updater) => {
|
|
3448
|
+
if (!isCurrentGeneration(uid, generation)) {
|
|
3449
|
+
return Promise.resolve(fallbackState);
|
|
3450
|
+
}
|
|
3451
|
+
const current = states[uid];
|
|
3452
|
+
const updatedState = updater(current.newState);
|
|
3453
|
+
if (updatedState !== current.newState) {
|
|
3454
|
+
states[uid] = {
|
|
3455
|
+
newState: updatedState,
|
|
3456
|
+
oldState: current.oldState,
|
|
3457
|
+
scheduledState: updatedState
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
return Promise.resolve(updatedState);
|
|
3461
|
+
};
|
|
3462
|
+
const createAsyncCommandContext = (uid, generation) => {
|
|
3463
|
+
let latestState = states[uid].newState;
|
|
3464
|
+
return {
|
|
3465
|
+
getState: () => {
|
|
3466
|
+
if (isCurrentGeneration(uid, generation)) {
|
|
3467
|
+
latestState = states[uid].newState;
|
|
3468
|
+
}
|
|
3469
|
+
return latestState;
|
|
3470
|
+
},
|
|
3471
|
+
updateState: async updater => {
|
|
3472
|
+
latestState = await updateState(uid, generation, latestState, updater);
|
|
3473
|
+
return latestState;
|
|
3474
|
+
}
|
|
3475
|
+
};
|
|
3476
|
+
};
|
|
3477
|
+
const enqueueCommand = async (uid, command) => {
|
|
3478
|
+
const previous = commandQueues.get(uid) || Promise.resolve();
|
|
3479
|
+
const run = async () => {
|
|
3480
|
+
try {
|
|
3481
|
+
await previous;
|
|
3482
|
+
} catch {
|
|
3483
|
+
// The previous caller receives its error; later commands must still run.
|
|
3484
|
+
}
|
|
3485
|
+
await command();
|
|
3486
|
+
};
|
|
3487
|
+
const current = run();
|
|
3488
|
+
commandQueues.set(uid, current);
|
|
3489
|
+
try {
|
|
3490
|
+
await current;
|
|
3491
|
+
} finally {
|
|
3492
|
+
if (commandQueues.get(uid) === current) {
|
|
3493
|
+
commandQueues.delete(uid);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
};
|
|
3228
3497
|
return {
|
|
3229
3498
|
clear() {
|
|
3499
|
+
commandQueues.clear();
|
|
3230
3500
|
for (const key of Object.keys(states)) {
|
|
3231
3501
|
delete states[key];
|
|
3232
3502
|
}
|
|
@@ -3246,6 +3516,7 @@ const create$1 = () => {
|
|
|
3246
3516
|
return diffResult;
|
|
3247
3517
|
},
|
|
3248
3518
|
dispose(uid) {
|
|
3519
|
+
commandQueues.delete(uid);
|
|
3249
3520
|
delete states[uid];
|
|
3250
3521
|
},
|
|
3251
3522
|
get(uid) {
|
|
@@ -3263,14 +3534,27 @@ const create$1 = () => {
|
|
|
3263
3534
|
Object.assign(commandMapRef, commandMap);
|
|
3264
3535
|
},
|
|
3265
3536
|
set(uid, oldState, newState, scheduledState) {
|
|
3537
|
+
const current = states[uid];
|
|
3538
|
+
if (!current || oldState === newState && newState !== current.newState) {
|
|
3539
|
+
generations[uid] = getGeneration(uid) + 1;
|
|
3540
|
+
}
|
|
3266
3541
|
states[uid] = {
|
|
3267
3542
|
newState,
|
|
3268
3543
|
oldState,
|
|
3269
3544
|
scheduledState: scheduledState ?? newState
|
|
3270
3545
|
};
|
|
3271
3546
|
},
|
|
3547
|
+
wrapAsyncCommand(fn) {
|
|
3548
|
+
const wrapped = async (uid, ...args) => {
|
|
3549
|
+
const generation = getGeneration(uid);
|
|
3550
|
+
const context = createAsyncCommandContext(uid, generation);
|
|
3551
|
+
await fn(context, ...args);
|
|
3552
|
+
};
|
|
3553
|
+
return wrapped;
|
|
3554
|
+
},
|
|
3272
3555
|
wrapCommand(fn) {
|
|
3273
3556
|
const wrapped = async (uid, ...args) => {
|
|
3557
|
+
const generation = getGeneration(uid);
|
|
3274
3558
|
const {
|
|
3275
3559
|
newState,
|
|
3276
3560
|
oldState
|
|
@@ -3279,6 +3563,9 @@ const create$1 = () => {
|
|
|
3279
3563
|
if (oldState === newerState || newState === newerState) {
|
|
3280
3564
|
return;
|
|
3281
3565
|
}
|
|
3566
|
+
if (!isCurrentGeneration(uid, generation)) {
|
|
3567
|
+
return;
|
|
3568
|
+
}
|
|
3282
3569
|
const latestOld = states[uid];
|
|
3283
3570
|
const latestNew = {
|
|
3284
3571
|
...latestOld.newState,
|
|
@@ -3303,6 +3590,7 @@ const create$1 = () => {
|
|
|
3303
3590
|
},
|
|
3304
3591
|
wrapLoadContent(fn) {
|
|
3305
3592
|
const wrapped = async (uid, ...args) => {
|
|
3593
|
+
const generation = getGeneration(uid);
|
|
3306
3594
|
const {
|
|
3307
3595
|
newState,
|
|
3308
3596
|
oldState
|
|
@@ -3317,6 +3605,11 @@ const create$1 = () => {
|
|
|
3317
3605
|
error
|
|
3318
3606
|
};
|
|
3319
3607
|
}
|
|
3608
|
+
if (!isCurrentGeneration(uid, generation)) {
|
|
3609
|
+
return {
|
|
3610
|
+
error
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3320
3613
|
const latestOld = states[uid];
|
|
3321
3614
|
const latestNew = {
|
|
3322
3615
|
...latestOld.newState,
|
|
@@ -3332,6 +3625,51 @@ const create$1 = () => {
|
|
|
3332
3625
|
};
|
|
3333
3626
|
};
|
|
3334
3627
|
return wrapped;
|
|
3628
|
+
},
|
|
3629
|
+
wrapSerialAsyncCommand(fn) {
|
|
3630
|
+
const wrapped = async (uid, ...args) => {
|
|
3631
|
+
await enqueueCommand(uid, async () => {
|
|
3632
|
+
if (!states[uid]) {
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
const generation = getGeneration(uid);
|
|
3636
|
+
const context = createAsyncCommandContext(uid, generation);
|
|
3637
|
+
await fn(context, ...args);
|
|
3638
|
+
});
|
|
3639
|
+
};
|
|
3640
|
+
return wrapped;
|
|
3641
|
+
},
|
|
3642
|
+
wrapSerialCommand(fn) {
|
|
3643
|
+
const wrapped = async (uid, ...args) => {
|
|
3644
|
+
await enqueueCommand(uid, async () => {
|
|
3645
|
+
if (!states[uid]) {
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3648
|
+
const generation = getGeneration(uid);
|
|
3649
|
+
const {
|
|
3650
|
+
newState,
|
|
3651
|
+
oldState
|
|
3652
|
+
} = states[uid];
|
|
3653
|
+
const newerState = await fn(newState, ...args);
|
|
3654
|
+
if (oldState === newerState || newState === newerState) {
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
if (!isCurrentGeneration(uid, generation)) {
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3660
|
+
const latestOld = states[uid];
|
|
3661
|
+
const latestNew = {
|
|
3662
|
+
...latestOld.newState,
|
|
3663
|
+
...newerState
|
|
3664
|
+
};
|
|
3665
|
+
states[uid] = {
|
|
3666
|
+
newState: latestNew,
|
|
3667
|
+
oldState: latestOld.oldState,
|
|
3668
|
+
scheduledState: latestNew
|
|
3669
|
+
};
|
|
3670
|
+
});
|
|
3671
|
+
};
|
|
3672
|
+
return wrapped;
|
|
3335
3673
|
}
|
|
3336
3674
|
};
|
|
3337
3675
|
};
|
|
@@ -3431,6 +3769,8 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
3431
3769
|
disabled: false,
|
|
3432
3770
|
displaySize: '',
|
|
3433
3771
|
downloadCount: 'n/a',
|
|
3772
|
+
errorMessage: '',
|
|
3773
|
+
errorTitle: '',
|
|
3434
3774
|
extension: {},
|
|
3435
3775
|
extensionId: '',
|
|
3436
3776
|
extensionUri: '',
|
|
@@ -3703,6 +4043,96 @@ const getMenus = () => {
|
|
|
3703
4043
|
}];
|
|
3704
4044
|
};
|
|
3705
4045
|
|
|
4046
|
+
let mock;
|
|
4047
|
+
const getPage = url => {
|
|
4048
|
+
return Number(new URL(url).searchParams.get('page')) || 1;
|
|
4049
|
+
};
|
|
4050
|
+
const createGeneratedReleases = (url, count) => {
|
|
4051
|
+
const page = getPage(url);
|
|
4052
|
+
const start = (page - 1) * 100;
|
|
4053
|
+
const end = Math.min(start + 100, count);
|
|
4054
|
+
return Array.from({
|
|
4055
|
+
length: Math.max(0, end - start)
|
|
4056
|
+
}, (_, index) => {
|
|
4057
|
+
const releaseNumber = count - start - index;
|
|
4058
|
+
return {
|
|
4059
|
+
body: `Release notes for version ${releaseNumber}`,
|
|
4060
|
+
html_url: `https://github.com/test-owner/test-repository/releases/tag/v${releaseNumber}`,
|
|
4061
|
+
name: `Version ${releaseNumber}`,
|
|
4062
|
+
published_at: '2026-01-01T00:00:00Z',
|
|
4063
|
+
tag_name: `v${releaseNumber}`
|
|
4064
|
+
};
|
|
4065
|
+
});
|
|
4066
|
+
};
|
|
4067
|
+
const createMockResponse = (url, value) => {
|
|
4068
|
+
const status = value.status ?? 200;
|
|
4069
|
+
const statusText = value.statusText || getStatusText(status);
|
|
4070
|
+
const headers = new Headers(value.headers);
|
|
4071
|
+
if (value.type === 'invalid-json') {
|
|
4072
|
+
headers.set('content-type', 'text/html');
|
|
4073
|
+
return new Response('<!doctype html><title>Unexpected response</title>', {
|
|
4074
|
+
headers,
|
|
4075
|
+
status,
|
|
4076
|
+
statusText
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
const body = value.type === 'generated' ? createGeneratedReleases(url, value.releaseCount ?? 0) : value.body ?? [];
|
|
4080
|
+
headers.set('content-type', 'application/json');
|
|
4081
|
+
return Response.json(body, {
|
|
4082
|
+
headers,
|
|
4083
|
+
status,
|
|
4084
|
+
statusText
|
|
4085
|
+
});
|
|
4086
|
+
};
|
|
4087
|
+
const getStatusText = status => {
|
|
4088
|
+
switch (status) {
|
|
4089
|
+
case 400:
|
|
4090
|
+
return 'Bad Request';
|
|
4091
|
+
case 401:
|
|
4092
|
+
return 'Unauthorized';
|
|
4093
|
+
case 403:
|
|
4094
|
+
return 'Forbidden';
|
|
4095
|
+
case 404:
|
|
4096
|
+
return 'Not Found';
|
|
4097
|
+
case 429:
|
|
4098
|
+
return 'Too Many Requests';
|
|
4099
|
+
case 500:
|
|
4100
|
+
return 'Internal Server Error';
|
|
4101
|
+
case 503:
|
|
4102
|
+
return 'Service Unavailable';
|
|
4103
|
+
default:
|
|
4104
|
+
return '';
|
|
4105
|
+
}
|
|
4106
|
+
};
|
|
4107
|
+
const request = url => {
|
|
4108
|
+
if (!mock) {
|
|
4109
|
+
return fetch(url, {
|
|
4110
|
+
headers: {
|
|
4111
|
+
Accept: 'application/vnd.github+json',
|
|
4112
|
+
'X-GitHub-Api-Version': '2022-11-28'
|
|
4113
|
+
}
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4116
|
+
if (mock.type === 'network-error') {
|
|
4117
|
+
return Promise.reject(new TypeError(mock.message || 'Failed to fetch'));
|
|
4118
|
+
}
|
|
4119
|
+
return Promise.resolve(createMockResponse(url, mock));
|
|
4120
|
+
};
|
|
4121
|
+
const mockGithubApi = value => {
|
|
4122
|
+
mock = value;
|
|
4123
|
+
};
|
|
4124
|
+
const handleMockGithubApi = (state, value) => {
|
|
4125
|
+
mockGithubApi(value);
|
|
4126
|
+
return state;
|
|
4127
|
+
};
|
|
4128
|
+
const resetGithubApiMock = () => {
|
|
4129
|
+
mock = undefined;
|
|
4130
|
+
};
|
|
4131
|
+
const handleResetGithubApiMock = state => {
|
|
4132
|
+
resetGithubApiMock();
|
|
4133
|
+
return state;
|
|
4134
|
+
};
|
|
4135
|
+
|
|
3706
4136
|
const show2 = async (uid, menuId, x, y, args) => {
|
|
3707
4137
|
await showContextMenu2(uid, menuId, x, y, args);
|
|
3708
4138
|
};
|
|
@@ -3900,6 +4330,15 @@ const handleClickFeatures = async (state, name) => {
|
|
|
3900
4330
|
};
|
|
3901
4331
|
|
|
3902
4332
|
const handleClickScrollToTop = state => {
|
|
4333
|
+
if (state.selectedTab === Changelog) {
|
|
4334
|
+
if (state.changelogScrollTop === 0) {
|
|
4335
|
+
return state;
|
|
4336
|
+
}
|
|
4337
|
+
return {
|
|
4338
|
+
...state,
|
|
4339
|
+
changelogScrollTop: 0
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
3903
4342
|
const {
|
|
3904
4343
|
readmeScrollTop
|
|
3905
4344
|
} = state;
|
|
@@ -4033,6 +4472,16 @@ const getCurrentColorTheme = async () => {
|
|
|
4033
4472
|
return '';
|
|
4034
4473
|
};
|
|
4035
4474
|
|
|
4475
|
+
const getErrorMessage$1 = error => {
|
|
4476
|
+
if (error instanceof Error && error.message) {
|
|
4477
|
+
return error.message;
|
|
4478
|
+
}
|
|
4479
|
+
if (typeof error === 'string' && error) {
|
|
4480
|
+
return error;
|
|
4481
|
+
}
|
|
4482
|
+
return 'Unknown error';
|
|
4483
|
+
};
|
|
4484
|
+
|
|
4036
4485
|
const getExtensionIdFromUri = uri => {
|
|
4037
4486
|
const id = uri.slice('extension-detail://'.length);
|
|
4038
4487
|
return id;
|
|
@@ -4045,6 +4494,47 @@ const getExtensionUri = (uri, platform, origin) => {
|
|
|
4045
4494
|
return new URL(uri, origin).href;
|
|
4046
4495
|
};
|
|
4047
4496
|
|
|
4497
|
+
const segmentRegex = /^[\w.-]+$/;
|
|
4498
|
+
const getRepositoryUrl = extension => {
|
|
4499
|
+
if (!extension || !hasProperty(extension, 'repository')) {
|
|
4500
|
+
return '';
|
|
4501
|
+
}
|
|
4502
|
+
const {
|
|
4503
|
+
repository
|
|
4504
|
+
} = extension;
|
|
4505
|
+
if (typeof repository === 'string') {
|
|
4506
|
+
return repository;
|
|
4507
|
+
}
|
|
4508
|
+
if (repository && hasProperty(repository, 'url') && typeof repository.url === 'string') {
|
|
4509
|
+
return repository.url;
|
|
4510
|
+
}
|
|
4511
|
+
return '';
|
|
4512
|
+
};
|
|
4513
|
+
const getGithubRepository = extension => {
|
|
4514
|
+
const rawUrl = getRepositoryUrl(extension).replace(/^git\+/, '');
|
|
4515
|
+
try {
|
|
4516
|
+
const url = new URL(rawUrl);
|
|
4517
|
+
if (url.protocol !== 'https:' || url.hostname.toLowerCase() !== 'github.com' || url.username || url.password || url.port) {
|
|
4518
|
+
return undefined;
|
|
4519
|
+
}
|
|
4520
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
4521
|
+
if (segments.length !== 2) {
|
|
4522
|
+
return undefined;
|
|
4523
|
+
}
|
|
4524
|
+
const owner = segments[0];
|
|
4525
|
+
const repository = segments[1].replace(/\.git$/i, '');
|
|
4526
|
+
if (!owner || !repository || !segmentRegex.test(owner) || !segmentRegex.test(repository)) {
|
|
4527
|
+
return undefined;
|
|
4528
|
+
}
|
|
4529
|
+
return {
|
|
4530
|
+
owner,
|
|
4531
|
+
repository
|
|
4532
|
+
};
|
|
4533
|
+
} catch {
|
|
4534
|
+
return undefined;
|
|
4535
|
+
}
|
|
4536
|
+
};
|
|
4537
|
+
|
|
4048
4538
|
const getLinkProtectionEnabled = async () => {
|
|
4049
4539
|
try {
|
|
4050
4540
|
const setting = await getPreference('application.linkProtectionEnabled');
|
|
@@ -4507,23 +4997,8 @@ const formatCreated = (created, now = Date.now()) => {
|
|
|
4507
4997
|
return relativeTimeFormat.format(value, unit);
|
|
4508
4998
|
};
|
|
4509
4999
|
|
|
4510
|
-
const formatLastUpdated = lastUpdated => {
|
|
4511
|
-
|
|
4512
|
-
return 'n/a';
|
|
4513
|
-
}
|
|
4514
|
-
try {
|
|
4515
|
-
const date = new Date(lastUpdated * 1000);
|
|
4516
|
-
if (Number.isNaN(date.getTime())) {
|
|
4517
|
-
return 'n/a';
|
|
4518
|
-
}
|
|
4519
|
-
return date.toLocaleDateString(undefined, {
|
|
4520
|
-
day: 'numeric',
|
|
4521
|
-
month: 'short',
|
|
4522
|
-
year: 'numeric'
|
|
4523
|
-
});
|
|
4524
|
-
} catch {
|
|
4525
|
-
return 'n/a';
|
|
4526
|
-
}
|
|
5000
|
+
const formatLastUpdated = (lastUpdated, now = Date.now()) => {
|
|
5001
|
+
return formatCreated(lastUpdated, now);
|
|
4527
5002
|
};
|
|
4528
5003
|
|
|
4529
5004
|
const getSizeEntries = (showSizeLink, displaySize, extensionUri) => {
|
|
@@ -4704,6 +5179,16 @@ const parseCreated = extension => {
|
|
|
4704
5179
|
return timestamp;
|
|
4705
5180
|
};
|
|
4706
5181
|
|
|
5182
|
+
const secondsToMillisecondsThreshold = 10_000_000_000;
|
|
5183
|
+
const normalizeTimestamp = timestamp => {
|
|
5184
|
+
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
5185
|
+
return null;
|
|
5186
|
+
}
|
|
5187
|
+
if (timestamp < secondsToMillisecondsThreshold) {
|
|
5188
|
+
return timestamp * 1000;
|
|
5189
|
+
}
|
|
5190
|
+
return timestamp;
|
|
5191
|
+
};
|
|
4707
5192
|
const parseLastUpdated = extension => {
|
|
4708
5193
|
if (!extension || typeof extension !== 'object') {
|
|
4709
5194
|
return null;
|
|
@@ -4715,16 +5200,14 @@ const parseLastUpdated = extension => {
|
|
|
4715
5200
|
return null;
|
|
4716
5201
|
}
|
|
4717
5202
|
if (typeof lastUpdated === 'number') {
|
|
4718
|
-
|
|
4719
|
-
return lastUpdated;
|
|
4720
|
-
}
|
|
4721
|
-
return null;
|
|
5203
|
+
return normalizeTimestamp(lastUpdated);
|
|
4722
5204
|
}
|
|
4723
5205
|
if (typeof lastUpdated === 'string') {
|
|
4724
|
-
const
|
|
4725
|
-
if (Number.isFinite(
|
|
4726
|
-
return
|
|
5206
|
+
const numericTimestamp = Number(lastUpdated);
|
|
5207
|
+
if (Number.isFinite(numericTimestamp)) {
|
|
5208
|
+
return normalizeTimestamp(numericTimestamp);
|
|
4727
5209
|
}
|
|
5210
|
+
return normalizeTimestamp(Date.parse(lastUpdated));
|
|
4728
5211
|
}
|
|
4729
5212
|
return null;
|
|
4730
5213
|
};
|
|
@@ -4777,7 +5260,7 @@ const restoreState = savedState => {
|
|
|
4777
5260
|
const isEnabled = tab => {
|
|
4778
5261
|
return tab.enabled;
|
|
4779
5262
|
};
|
|
4780
|
-
const
|
|
5263
|
+
const loadContentInternal = async (state, platform, savedState, isTest = false) => {
|
|
4781
5264
|
if (isTest) {
|
|
4782
5265
|
savedState = undefined;
|
|
4783
5266
|
}
|
|
@@ -4839,7 +5322,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4839
5322
|
} = restoreState(savedState);
|
|
4840
5323
|
const features = getFeatures(selectedFeature || Theme, extension);
|
|
4841
5324
|
const hasFeatures = features.length > 0;
|
|
4842
|
-
const
|
|
5325
|
+
const hasGithubReleases = Boolean(getGithubRepository(extension));
|
|
5326
|
+
const tabs = getTabs(selectedTab, hasReadme, hasFeatures, hasChangelog || hasGithubReleases);
|
|
4843
5327
|
const enabledTabs = tabs.filter(isEnabled);
|
|
4844
5328
|
const sizeValue = getViewletSize(width || 0);
|
|
4845
5329
|
const showSizeLink = platform !== Web$1;
|
|
@@ -4872,6 +5356,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4872
5356
|
disabled,
|
|
4873
5357
|
displaySize,
|
|
4874
5358
|
downloadCount,
|
|
5359
|
+
errorMessage: '',
|
|
5360
|
+
errorTitle: '',
|
|
4875
5361
|
extension,
|
|
4876
5362
|
extensionId,
|
|
4877
5363
|
extensionUri,
|
|
@@ -4908,6 +5394,21 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4908
5394
|
tabs: enabledTabs
|
|
4909
5395
|
};
|
|
4910
5396
|
};
|
|
5397
|
+
const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
5398
|
+
try {
|
|
5399
|
+
return await loadContentInternal(state, platform, savedState, isTest);
|
|
5400
|
+
} catch (error) {
|
|
5401
|
+
const extensionId = getExtensionIdFromUri(state.uri);
|
|
5402
|
+
const errorMessage = error instanceof ExtensionNotFoundError ? extensionNotAvailable(extensionId) : unableToLoadExtensionWithError(getErrorMessage$1(error));
|
|
5403
|
+
return {
|
|
5404
|
+
...state,
|
|
5405
|
+
errorMessage,
|
|
5406
|
+
errorTitle: unableToLoadExtension(),
|
|
5407
|
+
extensionId,
|
|
5408
|
+
initial: false
|
|
5409
|
+
};
|
|
5410
|
+
}
|
|
5411
|
+
};
|
|
4911
5412
|
|
|
4912
5413
|
const handleExtensionsChanged = async state => {
|
|
4913
5414
|
const {
|
|
@@ -4927,6 +5428,10 @@ const handleExtensionsStatusUpdate = async state => {
|
|
|
4927
5428
|
};
|
|
4928
5429
|
};
|
|
4929
5430
|
|
|
5431
|
+
const handleHeaderContextMenu = async state => {
|
|
5432
|
+
return state;
|
|
5433
|
+
};
|
|
5434
|
+
|
|
4930
5435
|
const handleIconError = state => {
|
|
4931
5436
|
const {
|
|
4932
5437
|
assetDir,
|
|
@@ -4953,6 +5458,51 @@ const handleImageContextMenu = async (state, eventX, eventY) => {
|
|
|
4953
5458
|
return state;
|
|
4954
5459
|
};
|
|
4955
5460
|
|
|
5461
|
+
const getMarkdownImageErrorVirtualDom = () => {
|
|
5462
|
+
return [{
|
|
5463
|
+
childCount: 1,
|
|
5464
|
+
className: MarkdownImageError,
|
|
5465
|
+
type: Span
|
|
5466
|
+
}, text(imageCannotBeLoaded())];
|
|
5467
|
+
};
|
|
5468
|
+
|
|
5469
|
+
const isMatchingImageSrc = (nodeSrc, failedSrc) => {
|
|
5470
|
+
if (typeof nodeSrc !== 'string' || !failedSrc) {
|
|
5471
|
+
return false;
|
|
5472
|
+
}
|
|
5473
|
+
const normalizedNodeSrc = nodeSrc.startsWith('./') ? nodeSrc.slice(2) : nodeSrc;
|
|
5474
|
+
return nodeSrc === failedSrc || failedSrc.endsWith(nodeSrc) || nodeSrc.endsWith(failedSrc) || failedSrc.endsWith(`/${normalizedNodeSrc}`);
|
|
5475
|
+
};
|
|
5476
|
+
const replaceMarkdownImageWithError = (dom, failedSrc) => {
|
|
5477
|
+
let replaced = false;
|
|
5478
|
+
const newDom = [];
|
|
5479
|
+
for (const node of dom) {
|
|
5480
|
+
if (node.type === Img && isMatchingImageSrc(node.src, failedSrc)) {
|
|
5481
|
+
newDom.push(...getMarkdownImageErrorVirtualDom());
|
|
5482
|
+
replaced = true;
|
|
5483
|
+
} else {
|
|
5484
|
+
newDom.push(node);
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
if (!replaced) {
|
|
5488
|
+
return dom;
|
|
5489
|
+
}
|
|
5490
|
+
return newDom;
|
|
5491
|
+
};
|
|
5492
|
+
|
|
5493
|
+
const handleMarkdownImageError = (state, failedSrc) => {
|
|
5494
|
+
const detailsVirtualDom = replaceMarkdownImageWithError(state.detailsVirtualDom, failedSrc);
|
|
5495
|
+
const changelogVirtualDom = replaceMarkdownImageWithError(state.changelogVirtualDom, failedSrc);
|
|
5496
|
+
if (detailsVirtualDom === state.detailsVirtualDom && changelogVirtualDom === state.changelogVirtualDom) {
|
|
5497
|
+
return state;
|
|
5498
|
+
}
|
|
5499
|
+
return {
|
|
5500
|
+
...state,
|
|
5501
|
+
changelogVirtualDom,
|
|
5502
|
+
detailsVirtualDom
|
|
5503
|
+
};
|
|
5504
|
+
};
|
|
5505
|
+
|
|
4956
5506
|
const previewColorTheme = async colorThemeId => {
|
|
4957
5507
|
await invoke$1('ColorTheme.previewColorTheme', colorThemeId);
|
|
4958
5508
|
};
|
|
@@ -5057,6 +5607,13 @@ const handleResourceLinkClick = async (state, href) => {
|
|
|
5057
5607
|
|
|
5058
5608
|
const handleScroll = (state, scrollTop, scrollSource = Script) => {
|
|
5059
5609
|
const newScrollTop = Math.max(0, scrollTop);
|
|
5610
|
+
if (state.selectedTab === Changelog) {
|
|
5611
|
+
return {
|
|
5612
|
+
...state,
|
|
5613
|
+
changelogScrollTop: newScrollTop,
|
|
5614
|
+
scrollSource
|
|
5615
|
+
};
|
|
5616
|
+
}
|
|
5060
5617
|
return {
|
|
5061
5618
|
...state,
|
|
5062
5619
|
readmeScrollTop: newScrollTop,
|
|
@@ -5083,6 +5640,39 @@ const handleTabFocus = (state, name) => {
|
|
|
5083
5640
|
};
|
|
5084
5641
|
};
|
|
5085
5642
|
|
|
5643
|
+
const escapeMarkdown = value => {
|
|
5644
|
+
return value.replaceAll('\\', '\\\\').replaceAll('[', '\\[').replaceAll(']', '\\]');
|
|
5645
|
+
};
|
|
5646
|
+
const getPublishedText = publishedAt => {
|
|
5647
|
+
if (!publishedAt) {
|
|
5648
|
+
return 'Publication date unavailable';
|
|
5649
|
+
}
|
|
5650
|
+
const date = new Date(publishedAt);
|
|
5651
|
+
if (Number.isNaN(date.getTime())) {
|
|
5652
|
+
return 'Publication date unavailable';
|
|
5653
|
+
}
|
|
5654
|
+
return `Published ${date.toLocaleDateString(undefined, {
|
|
5655
|
+
dateStyle: 'long'
|
|
5656
|
+
})}`;
|
|
5657
|
+
};
|
|
5658
|
+
const getGithubReleasesMarkdown = (releases, githubRepository) => {
|
|
5659
|
+
if (releases.length === 0) {
|
|
5660
|
+
return `# Changelog\n\nNo GitHub releases were found for **${escapeMarkdown(githubRepository.owner)}/${escapeMarkdown(githubRepository.repository)}**.`;
|
|
5661
|
+
}
|
|
5662
|
+
return releases.map(release => {
|
|
5663
|
+
const title = release.name || release.tagName;
|
|
5664
|
+
const body = release.body.trim() || '_No release notes were provided._';
|
|
5665
|
+
return `# [${escapeMarkdown(title)}](${release.htmlUrl})\n\n${getPublishedText(release.publishedAt)} · \`${release.tagName}\`\n\n${body}`;
|
|
5666
|
+
}).join('\n\n---\n\n');
|
|
5667
|
+
};
|
|
5668
|
+
|
|
5669
|
+
class GithubReleasesError extends Error {
|
|
5670
|
+
constructor(message) {
|
|
5671
|
+
super(message);
|
|
5672
|
+
this.name = 'GithubReleasesError';
|
|
5673
|
+
}
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5086
5676
|
const error = async error => {
|
|
5087
5677
|
// TODO send message to error worker or log worker
|
|
5088
5678
|
// @ts-ignore
|
|
@@ -5103,21 +5693,206 @@ const loadChangelogContent = async path => {
|
|
|
5103
5693
|
}
|
|
5104
5694
|
};
|
|
5105
5695
|
|
|
5696
|
+
const pageSize = 100;
|
|
5697
|
+
const maximumReleases = 100;
|
|
5698
|
+
const getErrorMessage = async response => {
|
|
5699
|
+
try {
|
|
5700
|
+
const body = await response.clone().json();
|
|
5701
|
+
if (body && hasProperty(body, 'message') && typeof body.message === 'string') {
|
|
5702
|
+
return body.message;
|
|
5703
|
+
}
|
|
5704
|
+
} catch {
|
|
5705
|
+
// The status code still provides a useful error when the error body is invalid.
|
|
5706
|
+
}
|
|
5707
|
+
return '';
|
|
5708
|
+
};
|
|
5709
|
+
const getHttpError = async response => {
|
|
5710
|
+
const apiMessage = await getErrorMessage(response);
|
|
5711
|
+
const rateLimitRemaining = response.headers.get('x-ratelimit-remaining');
|
|
5712
|
+
if (response.status === 429 || response.status === 403 && (rateLimitRemaining === '0' || /rate limit/i.test(apiMessage))) {
|
|
5713
|
+
const resetValue = Number(response.headers.get('x-ratelimit-reset'));
|
|
5714
|
+
const retry = Number.isFinite(resetValue) && resetValue > 0 ? ` Try again after ${new Date(resetValue * 1000).toLocaleString()}.` : ' Please try again later.';
|
|
5715
|
+
return new GithubReleasesError(`GitHub API rate limit exceeded.${retry}`);
|
|
5716
|
+
}
|
|
5717
|
+
if (response.status === 404) {
|
|
5718
|
+
return new GithubReleasesError('The GitHub repository could not be found or is not publicly reachable.');
|
|
5719
|
+
}
|
|
5720
|
+
if (response.status >= 500) {
|
|
5721
|
+
return new GithubReleasesError(`GitHub is temporarily unavailable (server error ${response.status}). Please try again later.`);
|
|
5722
|
+
}
|
|
5723
|
+
if (response.status >= 400) {
|
|
5724
|
+
const detail = apiMessage ? `: ${apiMessage}` : '';
|
|
5725
|
+
return new GithubReleasesError(`GitHub rejected the releases request (${response.status} ${response.statusText || 'Client Error'})${detail}.`);
|
|
5726
|
+
}
|
|
5727
|
+
return new GithubReleasesError(`GitHub returned an unexpected HTTP status (${response.status}).`);
|
|
5728
|
+
};
|
|
5729
|
+
const parseNullableString = value => {
|
|
5730
|
+
if (typeof value === 'string') {
|
|
5731
|
+
return value;
|
|
5732
|
+
}
|
|
5733
|
+
if (value === null) {
|
|
5734
|
+
return '';
|
|
5735
|
+
}
|
|
5736
|
+
return undefined;
|
|
5737
|
+
};
|
|
5738
|
+
const parseRelease = value => {
|
|
5739
|
+
if (!value || !hasProperty(value, 'body') || !hasProperty(value, 'html_url') || !hasProperty(value, 'name') || !hasProperty(value, 'published_at') || !hasProperty(value, 'tag_name')) {
|
|
5740
|
+
return undefined;
|
|
5741
|
+
}
|
|
5742
|
+
const body = parseNullableString(value.body);
|
|
5743
|
+
const name = parseNullableString(value.name);
|
|
5744
|
+
const publishedAt = parseNullableString(value.published_at);
|
|
5745
|
+
if (body === undefined || name === undefined || publishedAt === undefined || typeof value.html_url !== 'string' || typeof value.tag_name !== 'string') {
|
|
5746
|
+
return undefined;
|
|
5747
|
+
}
|
|
5748
|
+
try {
|
|
5749
|
+
const htmlUrl = new URL(value.html_url);
|
|
5750
|
+
if (htmlUrl.protocol !== 'https:' || htmlUrl.hostname !== 'github.com') {
|
|
5751
|
+
return undefined;
|
|
5752
|
+
}
|
|
5753
|
+
} catch {
|
|
5754
|
+
return undefined;
|
|
5755
|
+
}
|
|
5756
|
+
return {
|
|
5757
|
+
body,
|
|
5758
|
+
htmlUrl: value.html_url,
|
|
5759
|
+
name,
|
|
5760
|
+
publishedAt,
|
|
5761
|
+
tagName: value.tag_name
|
|
5762
|
+
};
|
|
5763
|
+
};
|
|
5764
|
+
const parsePage = async response => {
|
|
5765
|
+
let value;
|
|
5766
|
+
try {
|
|
5767
|
+
value = await response.json();
|
|
5768
|
+
} catch {
|
|
5769
|
+
throw new GithubReleasesError('GitHub returned an invalid response instead of JSON release data. Please try again later.');
|
|
5770
|
+
}
|
|
5771
|
+
if (!Array.isArray(value)) {
|
|
5772
|
+
throw new GithubReleasesError('GitHub returned invalid release data. Please try again later.');
|
|
5773
|
+
}
|
|
5774
|
+
const releases = value.map(parseRelease);
|
|
5775
|
+
if (releases.some(release => !release)) {
|
|
5776
|
+
throw new GithubReleasesError('GitHub returned invalid release data. Please try again later.');
|
|
5777
|
+
}
|
|
5778
|
+
return releases;
|
|
5779
|
+
};
|
|
5780
|
+
const loadGithubReleases = async ({
|
|
5781
|
+
owner,
|
|
5782
|
+
repository
|
|
5783
|
+
}) => {
|
|
5784
|
+
const releases = [];
|
|
5785
|
+
for (let page = 1;; page++) {
|
|
5786
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/releases?per_page=${pageSize}&page=${page}`;
|
|
5787
|
+
let response;
|
|
5788
|
+
try {
|
|
5789
|
+
response = await request(url);
|
|
5790
|
+
} catch {
|
|
5791
|
+
throw new GithubReleasesError('GitHub releases could not be loaded because GitHub is not reachable. Check your connection and try again.');
|
|
5792
|
+
}
|
|
5793
|
+
if (!response.ok) {
|
|
5794
|
+
throw await getHttpError(response);
|
|
5795
|
+
}
|
|
5796
|
+
const pageReleases = await parsePage(response);
|
|
5797
|
+
const remaining = maximumReleases - releases.length;
|
|
5798
|
+
if (pageReleases.length > remaining) {
|
|
5799
|
+
releases.push(...pageReleases.slice(0, remaining));
|
|
5800
|
+
return {
|
|
5801
|
+
isTruncated: true,
|
|
5802
|
+
releases
|
|
5803
|
+
};
|
|
5804
|
+
}
|
|
5805
|
+
releases.push(...pageReleases);
|
|
5806
|
+
if (pageReleases.length < pageSize) {
|
|
5807
|
+
return {
|
|
5808
|
+
isTruncated: false,
|
|
5809
|
+
releases
|
|
5810
|
+
};
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
};
|
|
5814
|
+
|
|
5815
|
+
const releaseBatchSize = 100;
|
|
5816
|
+
const mergeMarkdownVirtualDoms = chunks => {
|
|
5817
|
+
let root;
|
|
5818
|
+
const children = [];
|
|
5819
|
+
for (const chunk of chunks) {
|
|
5820
|
+
const [chunkRoot, ...chunkChildren] = chunk;
|
|
5821
|
+
if (!chunkRoot) {
|
|
5822
|
+
continue;
|
|
5823
|
+
}
|
|
5824
|
+
root = root ? {
|
|
5825
|
+
...root,
|
|
5826
|
+
childCount: root.childCount + chunkRoot.childCount
|
|
5827
|
+
} : chunkRoot;
|
|
5828
|
+
children.push(...chunkChildren);
|
|
5829
|
+
}
|
|
5830
|
+
return root ? [root, ...children] : [];
|
|
5831
|
+
};
|
|
5832
|
+
const renderGithubReleases = async (result, githubRepository, languages, locationProtocol) => {
|
|
5833
|
+
const chunks = [];
|
|
5834
|
+
const {
|
|
5835
|
+
isTruncated,
|
|
5836
|
+
releases
|
|
5837
|
+
} = result;
|
|
5838
|
+
const releaseGroups = releases.length === 0 ? [[]] : Array.from({
|
|
5839
|
+
length: Math.ceil(releases.length / releaseBatchSize)
|
|
5840
|
+
}, (_, index) => {
|
|
5841
|
+
return releases.slice(index * releaseBatchSize, (index + 1) * releaseBatchSize);
|
|
5842
|
+
});
|
|
5843
|
+
const baseUrl = `https://github.com/${githubRepository.owner}/${githubRepository.repository}/blob/HEAD/`;
|
|
5844
|
+
for (const [index, releaseGroup] of releaseGroups.entries()) {
|
|
5845
|
+
const limitMessage = index === 0 && isTruncated ? `> Showing the newest ${releases.length} GitHub releases. Older releases are not displayed to keep the editor responsive.\n\n` : '';
|
|
5846
|
+
const markdown = limitMessage + getGithubReleasesMarkdown(releaseGroup, githubRepository);
|
|
5847
|
+
const html = await renderMarkdown(markdown, {
|
|
5848
|
+
baseUrl,
|
|
5849
|
+
languages,
|
|
5850
|
+
linksExternal: true,
|
|
5851
|
+
locationProtocol
|
|
5852
|
+
});
|
|
5853
|
+
chunks.push([...(await getMarkdownVirtualDom(html))]);
|
|
5854
|
+
}
|
|
5855
|
+
return addScrollToTopVirtualDom(mergeMarkdownVirtualDoms(chunks));
|
|
5856
|
+
};
|
|
5106
5857
|
const selectTabChangelog = async state => {
|
|
5107
5858
|
const {
|
|
5108
5859
|
baseUrl,
|
|
5860
|
+
extension,
|
|
5109
5861
|
extensionUri,
|
|
5110
5862
|
languages,
|
|
5111
5863
|
locationProtocol,
|
|
5112
5864
|
tabs
|
|
5113
5865
|
} = state;
|
|
5114
|
-
const
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5866
|
+
const githubRepository = getGithubRepository(extension);
|
|
5867
|
+
let changelogDom;
|
|
5868
|
+
if (githubRepository) {
|
|
5869
|
+
try {
|
|
5870
|
+
const result = await loadGithubReleases(githubRepository);
|
|
5871
|
+
changelogDom = await renderGithubReleases(result, githubRepository, languages, locationProtocol);
|
|
5872
|
+
} catch (error) {
|
|
5873
|
+
const message = error instanceof GithubReleasesError ? error.message : 'GitHub releases could not be loaded. Please try again later.';
|
|
5874
|
+
const html = await renderMarkdown(`# Changelog\n\n${message}`, {
|
|
5875
|
+
baseUrl,
|
|
5876
|
+
languages,
|
|
5877
|
+
linksExternal: true,
|
|
5878
|
+
locationProtocol
|
|
5879
|
+
});
|
|
5880
|
+
changelogDom = await getMarkdownVirtualDom(html, {
|
|
5881
|
+
scrollToTopEnabled: true
|
|
5882
|
+
});
|
|
5883
|
+
}
|
|
5884
|
+
} else {
|
|
5885
|
+
const changelogContent = await loadChangelogContent(extensionUri);
|
|
5886
|
+
const html = await renderMarkdown(changelogContent, {
|
|
5887
|
+
baseUrl,
|
|
5888
|
+
languages,
|
|
5889
|
+
linksExternal: true,
|
|
5890
|
+
locationProtocol
|
|
5891
|
+
});
|
|
5892
|
+
changelogDom = await getMarkdownVirtualDom(html, {
|
|
5893
|
+
scrollToTopEnabled: true
|
|
5894
|
+
});
|
|
5895
|
+
}
|
|
5121
5896
|
const newTabs = tabs.map(tab => {
|
|
5122
5897
|
return {
|
|
5123
5898
|
...tab,
|
|
@@ -5391,7 +6166,8 @@ const getChangelogVirtualDom = changelogDom => {
|
|
|
5391
6166
|
// TODO set tabpanel role
|
|
5392
6167
|
return [{
|
|
5393
6168
|
childCount: 1,
|
|
5394
|
-
className: Changelog$1
|
|
6169
|
+
className: `${ExtensionDetailPanel} ${Changelog$1}`,
|
|
6170
|
+
role: Panel,
|
|
5395
6171
|
type: Div
|
|
5396
6172
|
}, ...changelogDom];
|
|
5397
6173
|
};
|
|
@@ -5695,6 +6471,31 @@ const getExtensionDetailContentVirtualDom = (sanitizedReadmeHtml, selectedTab, w
|
|
|
5695
6471
|
}
|
|
5696
6472
|
};
|
|
5697
6473
|
|
|
6474
|
+
const getExtensionDetailErrorVirtualDom = (title, message) => {
|
|
6475
|
+
return [{
|
|
6476
|
+
childCount: 1,
|
|
6477
|
+
className: mergeClassNames(Viewlet, ExtensionDetail, ExtensionDetailError),
|
|
6478
|
+
type: Div
|
|
6479
|
+
}, {
|
|
6480
|
+
childCount: 3,
|
|
6481
|
+
className: ExtensionDetailErrorCard,
|
|
6482
|
+
role: 'alert',
|
|
6483
|
+
type: Div
|
|
6484
|
+
}, {
|
|
6485
|
+
childCount: 0,
|
|
6486
|
+
className: mergeClassNames(MaskIcon, MaskIconWarning, ExtensionDetailErrorIcon),
|
|
6487
|
+
type: Div
|
|
6488
|
+
}, {
|
|
6489
|
+
childCount: 1,
|
|
6490
|
+
className: ExtensionDetailErrorTitle,
|
|
6491
|
+
type: H1
|
|
6492
|
+
}, text(title), {
|
|
6493
|
+
childCount: 1,
|
|
6494
|
+
className: ExtensionDetailErrorMessage,
|
|
6495
|
+
type: P
|
|
6496
|
+
}, text(message)];
|
|
6497
|
+
};
|
|
6498
|
+
|
|
5698
6499
|
const getExtensionDetailDescriptionVirtualDom = description => {
|
|
5699
6500
|
return [{
|
|
5700
6501
|
childCount: 1,
|
|
@@ -5764,6 +6565,24 @@ const getExtensionDetailIconVirtualDom = iconSrc => {
|
|
|
5764
6565
|
};
|
|
5765
6566
|
};
|
|
5766
6567
|
|
|
6568
|
+
const getStatisticVirtualDom = (label, value, className) => {
|
|
6569
|
+
const accessibleLabel = `${label}: ${value}`;
|
|
6570
|
+
return [{
|
|
6571
|
+
ariaLabel: accessibleLabel,
|
|
6572
|
+
childCount: 1,
|
|
6573
|
+
className: mergeClassNames(ExtensionDetailStatistic, className),
|
|
6574
|
+
title: accessibleLabel,
|
|
6575
|
+
type: Span
|
|
6576
|
+
}, text(value)];
|
|
6577
|
+
};
|
|
6578
|
+
const getExtensionDetailMetadataVirtualDom = (downloadCount$1, rating$1) => {
|
|
6579
|
+
return [{
|
|
6580
|
+
childCount: 2,
|
|
6581
|
+
className: ExtensionDetailMetadata,
|
|
6582
|
+
type: Div
|
|
6583
|
+
}, ...getStatisticVirtualDom(downloadCount(), downloadCount$1, ExtensionDetailDownloadCount), ...getStatisticVirtualDom(rating(), rating$1, ExtensionDetailRating)];
|
|
6584
|
+
};
|
|
6585
|
+
|
|
5767
6586
|
const getNameBadgeVirtualDom = badge => {
|
|
5768
6587
|
if (!badge) {
|
|
5769
6588
|
return [];
|
|
@@ -5799,16 +6618,17 @@ const getExtensionDetailNameVirtualDom = (name, badge) => {
|
|
|
5799
6618
|
return getExtensionDetailNameDefaultVirtualDom(name);
|
|
5800
6619
|
};
|
|
5801
6620
|
|
|
5802
|
-
const getExtensionDetailHeaderVirtualDom = (name, iconSrc, description, badge, buttonDefs, settingsButtonEnabled) => {
|
|
6621
|
+
const getExtensionDetailHeaderVirtualDom = (name, iconSrc, description, badge, buttonDefs, settingsButtonEnabled, downloadCount = 'n/a', rating = 'n/a') => {
|
|
5803
6622
|
const dom = [{
|
|
5804
6623
|
childCount: 2,
|
|
5805
6624
|
className: ExtensionDetailHeader,
|
|
5806
6625
|
type: Div
|
|
5807
6626
|
}, getExtensionDetailIconVirtualDom(iconSrc), {
|
|
5808
|
-
childCount:
|
|
6627
|
+
childCount: 4,
|
|
5809
6628
|
className: ExtensionDetailHeaderDetails,
|
|
6629
|
+
onContextMenu: HandleHeaderContextMenu,
|
|
5810
6630
|
type: Div
|
|
5811
|
-
}, ...getExtensionDetailNameVirtualDom(name, badge), ...getExtensionDetailDescriptionVirtualDom(description), ...getExtensionDetailHeaderActionsVirtualDom(buttonDefs, settingsButtonEnabled)];
|
|
6631
|
+
}, ...getExtensionDetailNameVirtualDom(name, badge), ...getExtensionDetailDescriptionVirtualDom(description), ...getExtensionDetailMetadataVirtualDom(downloadCount, rating), ...getExtensionDetailHeaderActionsVirtualDom(buttonDefs, settingsButtonEnabled)];
|
|
5812
6632
|
return dom;
|
|
5813
6633
|
};
|
|
5814
6634
|
|
|
@@ -5863,6 +6683,9 @@ const getClassNames = size => {
|
|
|
5863
6683
|
};
|
|
5864
6684
|
|
|
5865
6685
|
const getExtensionDetailVirtualDom = (newState, selectedTab) => {
|
|
6686
|
+
if (newState.errorMessage) {
|
|
6687
|
+
return getExtensionDetailErrorVirtualDom(newState.errorTitle, newState.errorMessage);
|
|
6688
|
+
}
|
|
5866
6689
|
// TODO move this to view model so that rendering occurs like
|
|
5867
6690
|
// 1. state
|
|
5868
6691
|
// 2. view model
|
|
@@ -5875,9 +6698,11 @@ const getExtensionDetailVirtualDom = (newState, selectedTab) => {
|
|
|
5875
6698
|
changelogVirtualDom,
|
|
5876
6699
|
description,
|
|
5877
6700
|
detailsVirtualDom,
|
|
6701
|
+
downloadCount,
|
|
5878
6702
|
focusedTabIndex,
|
|
5879
6703
|
iconSrc,
|
|
5880
6704
|
name,
|
|
6705
|
+
rating,
|
|
5881
6706
|
resources,
|
|
5882
6707
|
scrollToTopButtonEnabled,
|
|
5883
6708
|
settingsButtonEnabled,
|
|
@@ -5891,7 +6716,7 @@ const getExtensionDetailVirtualDom = (newState, selectedTab) => {
|
|
|
5891
6716
|
childCount: 3,
|
|
5892
6717
|
className: mergeClassNames(Viewlet, ExtensionDetail, sizeClass),
|
|
5893
6718
|
type: Div
|
|
5894
|
-
}, ...getExtensionDetailHeaderVirtualDom(name, iconSrc, description, badge, buttons, settingsButtonEnabled), ...getTabsVirtualDom(tabs, focusedTabIndex), ...getExtensionDetailContentVirtualDom(detailsVirtualDom, selectedTab, width, scrollToTopButtonEnabled, categories, resources, showAdditionalDetailsBreakpoint, changelogVirtualDom, newState)];
|
|
6719
|
+
}, ...getExtensionDetailHeaderVirtualDom(name, iconSrc, description, badge, buttons, settingsButtonEnabled, downloadCount, rating), ...getTabsVirtualDom(tabs, focusedTabIndex), ...getExtensionDetailContentVirtualDom(detailsVirtualDom, selectedTab, width, scrollToTopButtonEnabled, categories, resources, showAdditionalDetailsBreakpoint, changelogVirtualDom, newState)];
|
|
5895
6720
|
return dom;
|
|
5896
6721
|
};
|
|
5897
6722
|
|
|
@@ -6029,6 +6854,13 @@ const renderEventListeners = () => {
|
|
|
6029
6854
|
name: HandleImageContextMenu,
|
|
6030
6855
|
params: ['handleImageContextMenu', ClientX, ClientY],
|
|
6031
6856
|
preventDefault: true
|
|
6857
|
+
}, {
|
|
6858
|
+
name: HandleHeaderContextMenu,
|
|
6859
|
+
params: ['handleHeaderContextMenu'],
|
|
6860
|
+
preventDefault: true
|
|
6861
|
+
}, {
|
|
6862
|
+
name: HandleMarkdownImageError,
|
|
6863
|
+
params: ['handleMarkdownImageError', 'event.currentTarget.src']
|
|
6032
6864
|
}, {
|
|
6033
6865
|
name: HandleReadmeScroll,
|
|
6034
6866
|
params: ['handleScroll', 'event.target.scrollTop', User],
|
|
@@ -6084,6 +6916,10 @@ const renderEventListeners = () => {
|
|
|
6084
6916
|
}];
|
|
6085
6917
|
};
|
|
6086
6918
|
|
|
6919
|
+
const renderTitle = state => {
|
|
6920
|
+
return state.name;
|
|
6921
|
+
};
|
|
6922
|
+
|
|
6087
6923
|
const resize = (state, dimensions) => {
|
|
6088
6924
|
const padding = getPadding(dimensions.width);
|
|
6089
6925
|
const sideBarWidth = getSideBarWidth(dimensions.width);
|
|
@@ -6141,8 +6977,10 @@ const commandMap = {
|
|
|
6141
6977
|
'ExtensionDetail.handleExtensionsChanged': wrapCommand(handleExtensionsChanged),
|
|
6142
6978
|
'ExtensionDetail.handleExtensionsStatusUpdate': wrapCommand(handleExtensionsStatusUpdate),
|
|
6143
6979
|
'ExtensionDetail.handleFeaturesClick': wrapCommand(handleClickFeatures),
|
|
6980
|
+
'ExtensionDetail.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
|
|
6144
6981
|
'ExtensionDetail.handleIconError': wrapCommand(handleIconError),
|
|
6145
6982
|
'ExtensionDetail.handleImageContextMenu': wrapCommand(handleImageContextMenu),
|
|
6983
|
+
'ExtensionDetail.handleMarkdownImageError': wrapCommand(handleMarkdownImageError),
|
|
6146
6984
|
'ExtensionDetail.handleMouseEnterEnable': wrapCommand(handleMouseEnterEnable),
|
|
6147
6985
|
'ExtensionDetail.handleMouseLeaveEnable': wrapCommand(handleMouseLeaveEnable),
|
|
6148
6986
|
'ExtensionDetail.handleReadmeClick': wrapCommand(handleReadmeClick),
|
|
@@ -6157,9 +6995,12 @@ const commandMap = {
|
|
|
6157
6995
|
'ExtensionDetail.hideSizeLink': wrapCommand(hideSizeLink),
|
|
6158
6996
|
'ExtensionDetail.initialize': initialize,
|
|
6159
6997
|
'ExtensionDetail.loadContent2': wrapCommand(loadContent2),
|
|
6998
|
+
'ExtensionDetail.mockGithubApi': wrapCommand(handleMockGithubApi),
|
|
6160
6999
|
'ExtensionDetail.openImageInNewTab': wrapCommand(openImageInNewTab),
|
|
6161
7000
|
'ExtensionDetail.render2': render2,
|
|
6162
7001
|
'ExtensionDetail.renderEventListeners': renderEventListeners,
|
|
7002
|
+
'ExtensionDetail.renderTitle': wrapGetter(renderTitle),
|
|
7003
|
+
'ExtensionDetail.resetGithubApiMock': wrapCommand(handleResetGithubApiMock),
|
|
6163
7004
|
'ExtensionDetail.resize': wrapCommand(resize),
|
|
6164
7005
|
'ExtensionDetail.saveState': wrapGetter(saveState),
|
|
6165
7006
|
'ExtensionDetail.selectTab': wrapCommand(selectTab),
|