@lvce-editor/extension-detail-view 7.11.0 → 7.13.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';
|
|
@@ -26,6 +26,7 @@ const Disable$1 = 'Disable';
|
|
|
26
26
|
const Elements = 'Elements';
|
|
27
27
|
const EmptyCommandsArray = 'The extension contributes an empty commands array.';
|
|
28
28
|
const Enable$1 = 'Enable';
|
|
29
|
+
const ExtensionNotAvailable = 'The extension "{PH1}" is not available in this version of LVCE Editor.';
|
|
29
30
|
const Features$2 = 'Features';
|
|
30
31
|
const FileExtensions = 'File Extensions';
|
|
31
32
|
const FileMatch = 'File Match';
|
|
@@ -37,7 +38,7 @@ const Installation = 'Installation';
|
|
|
37
38
|
const InvalidLink = 'Invalid link';
|
|
38
39
|
const Issues = 'Issues';
|
|
39
40
|
const JsonValidation$1 = 'Json Validation';
|
|
40
|
-
const Label = 'Label';
|
|
41
|
+
const Label$1 = 'Label';
|
|
41
42
|
const LastReleased = 'Last Released';
|
|
42
43
|
const LastUpdated = 'Last Updated';
|
|
43
44
|
const License = 'License';
|
|
@@ -67,6 +68,8 @@ const Status = 'Status';
|
|
|
67
68
|
const StringMustNotBeEmpty = 'String must not be empty';
|
|
68
69
|
const Theme$1 = 'Theme';
|
|
69
70
|
const Uninstall$1 = 'Uninstall';
|
|
71
|
+
const UnableToLoadExtension = 'Unable to load extension';
|
|
72
|
+
const UnableToLoadExtensionWithError = 'The extension details could not be loaded: {PH1}';
|
|
70
73
|
const UnsupportedFeature = 'Unsupported Feature';
|
|
71
74
|
const Version = 'Version';
|
|
72
75
|
const WebViews$1 = 'WebViews';
|
|
@@ -114,7 +117,7 @@ const openInNewTab = () => {
|
|
|
114
117
|
return i18nString(OpenInNewTab);
|
|
115
118
|
};
|
|
116
119
|
const label = () => {
|
|
117
|
-
return i18nString(Label);
|
|
120
|
+
return i18nString(Label$1);
|
|
118
121
|
};
|
|
119
122
|
const openImageInNewTab$1 = () => {
|
|
120
123
|
return i18nString(OpenImageInNewTab);
|
|
@@ -137,6 +140,11 @@ const setColorTheme$2 = () => {
|
|
|
137
140
|
const enable = () => {
|
|
138
141
|
return i18nString(Enable$1);
|
|
139
142
|
};
|
|
143
|
+
const extensionNotAvailable = extensionId => {
|
|
144
|
+
return i18nString(ExtensionNotAvailable, {
|
|
145
|
+
PH1: extensionId
|
|
146
|
+
});
|
|
147
|
+
};
|
|
140
148
|
const theme = () => {
|
|
141
149
|
return i18nString(Theme$1);
|
|
142
150
|
};
|
|
@@ -188,6 +196,14 @@ const elements = () => {
|
|
|
188
196
|
const uninstall = () => {
|
|
189
197
|
return i18nString(Uninstall$1);
|
|
190
198
|
};
|
|
199
|
+
const unableToLoadExtension = () => {
|
|
200
|
+
return i18nString(UnableToLoadExtension);
|
|
201
|
+
};
|
|
202
|
+
const unableToLoadExtensionWithError = errorMessage => {
|
|
203
|
+
return i18nString(UnableToLoadExtensionWithError, {
|
|
204
|
+
PH1: errorMessage
|
|
205
|
+
});
|
|
206
|
+
};
|
|
191
207
|
const scrollToTop = () => {
|
|
192
208
|
return i18nString(ScrollToTop$1);
|
|
193
209
|
};
|
|
@@ -302,9 +318,14 @@ const Panel = 'panel';
|
|
|
302
318
|
const Tab = 'tab';
|
|
303
319
|
const TabList = 'tablist';
|
|
304
320
|
|
|
321
|
+
const Audio = 0;
|
|
305
322
|
const Button$1 = 1;
|
|
323
|
+
const Col = 2;
|
|
324
|
+
const ColGroup = 3;
|
|
306
325
|
const Div = 4;
|
|
307
326
|
const H1 = 5;
|
|
327
|
+
const Input = 6;
|
|
328
|
+
const Kbd = 7;
|
|
308
329
|
const Span = 8;
|
|
309
330
|
const Table$1 = 9;
|
|
310
331
|
const TBody = 10;
|
|
@@ -313,20 +334,159 @@ const Text$1 = 12;
|
|
|
313
334
|
const Th = 13;
|
|
314
335
|
const THead = 14;
|
|
315
336
|
const Tr = 15;
|
|
337
|
+
const I = 16;
|
|
316
338
|
const Img = 17;
|
|
339
|
+
const Root = 0;
|
|
340
|
+
const Ins = 20;
|
|
341
|
+
const Del = 21;
|
|
317
342
|
const H2 = 22;
|
|
343
|
+
const H3 = 23;
|
|
344
|
+
const H4 = 24;
|
|
345
|
+
const H5 = 25;
|
|
346
|
+
const H6 = 26;
|
|
347
|
+
const Article = 27;
|
|
318
348
|
const Aside$1 = 28;
|
|
349
|
+
const Footer = 29;
|
|
350
|
+
const Header = 30;
|
|
351
|
+
const Nav = 40;
|
|
352
|
+
const Section = 41;
|
|
353
|
+
const Search = 42;
|
|
319
354
|
const Dd = 43;
|
|
320
355
|
const Dl = 44;
|
|
356
|
+
const Figcaption = 45;
|
|
357
|
+
const Figure = 46;
|
|
358
|
+
const Hr = 47;
|
|
321
359
|
const Li = 48;
|
|
360
|
+
const Ol = 49;
|
|
322
361
|
const P = 50;
|
|
323
362
|
const Pre = 51;
|
|
324
363
|
const A = 53;
|
|
364
|
+
const Abbr = 54;
|
|
365
|
+
const Br = 55;
|
|
366
|
+
const Cite = 56;
|
|
367
|
+
const Data = 57;
|
|
368
|
+
const Time = 58;
|
|
369
|
+
const Tfoot = 59;
|
|
325
370
|
const Ul = 60;
|
|
371
|
+
const Video = 61;
|
|
372
|
+
const TextArea = 62;
|
|
373
|
+
const Select = 63;
|
|
374
|
+
const Option = 64;
|
|
326
375
|
const Code$2 = 65;
|
|
376
|
+
const Label = 66;
|
|
327
377
|
const Dt = 67;
|
|
378
|
+
const Iframe = 68;
|
|
379
|
+
const Main = 69;
|
|
380
|
+
const Strong = 70;
|
|
381
|
+
const Em = 71;
|
|
382
|
+
const Style = 72;
|
|
383
|
+
const Html = 73;
|
|
384
|
+
const Head = 74;
|
|
385
|
+
const Title = 75;
|
|
386
|
+
const Meta = 76;
|
|
387
|
+
const Canvas = 77;
|
|
388
|
+
const Form = 78;
|
|
389
|
+
const BlockQuote = 79;
|
|
390
|
+
const Quote = 80;
|
|
391
|
+
const Circle = 81;
|
|
392
|
+
const Defs = 82;
|
|
393
|
+
const Ellipse = 83;
|
|
394
|
+
const G = 84;
|
|
395
|
+
const Line = 85;
|
|
396
|
+
const Path = 86;
|
|
397
|
+
const Polygon = 87;
|
|
398
|
+
const Polyline = 88;
|
|
399
|
+
const Rect = 89;
|
|
400
|
+
const Svg = 90;
|
|
401
|
+
const Use = 91;
|
|
328
402
|
const Reference = 100;
|
|
329
403
|
|
|
404
|
+
const VirtualDomElements = {
|
|
405
|
+
__proto__: null,
|
|
406
|
+
A,
|
|
407
|
+
Abbr,
|
|
408
|
+
Article,
|
|
409
|
+
Aside: Aside$1,
|
|
410
|
+
Audio,
|
|
411
|
+
BlockQuote,
|
|
412
|
+
Br,
|
|
413
|
+
Button: Button$1,
|
|
414
|
+
Canvas,
|
|
415
|
+
Circle,
|
|
416
|
+
Cite,
|
|
417
|
+
Code: Code$2,
|
|
418
|
+
Col,
|
|
419
|
+
ColGroup,
|
|
420
|
+
Data,
|
|
421
|
+
Dd,
|
|
422
|
+
Defs,
|
|
423
|
+
Del,
|
|
424
|
+
Div,
|
|
425
|
+
Dl,
|
|
426
|
+
Dt,
|
|
427
|
+
Ellipse,
|
|
428
|
+
Em,
|
|
429
|
+
Figcaption,
|
|
430
|
+
Figure,
|
|
431
|
+
Footer,
|
|
432
|
+
Form,
|
|
433
|
+
G,
|
|
434
|
+
H1,
|
|
435
|
+
H2,
|
|
436
|
+
H3,
|
|
437
|
+
H4,
|
|
438
|
+
H5,
|
|
439
|
+
H6,
|
|
440
|
+
Head,
|
|
441
|
+
Header,
|
|
442
|
+
Hr,
|
|
443
|
+
Html,
|
|
444
|
+
I,
|
|
445
|
+
Iframe,
|
|
446
|
+
Img,
|
|
447
|
+
Input,
|
|
448
|
+
Ins,
|
|
449
|
+
Kbd,
|
|
450
|
+
Label,
|
|
451
|
+
Li,
|
|
452
|
+
Line,
|
|
453
|
+
Main,
|
|
454
|
+
Meta,
|
|
455
|
+
Nav,
|
|
456
|
+
Ol,
|
|
457
|
+
Option,
|
|
458
|
+
P,
|
|
459
|
+
Path,
|
|
460
|
+
Polygon,
|
|
461
|
+
Polyline,
|
|
462
|
+
Pre,
|
|
463
|
+
Quote,
|
|
464
|
+
Rect,
|
|
465
|
+
Reference,
|
|
466
|
+
Root,
|
|
467
|
+
Search,
|
|
468
|
+
Section,
|
|
469
|
+
Select,
|
|
470
|
+
Span,
|
|
471
|
+
Strong,
|
|
472
|
+
Style,
|
|
473
|
+
Svg,
|
|
474
|
+
TBody,
|
|
475
|
+
THead,
|
|
476
|
+
Table: Table$1,
|
|
477
|
+
Td,
|
|
478
|
+
Text: Text$1,
|
|
479
|
+
TextArea,
|
|
480
|
+
Tfoot,
|
|
481
|
+
Th,
|
|
482
|
+
Time,
|
|
483
|
+
Title,
|
|
484
|
+
Tr,
|
|
485
|
+
Ul,
|
|
486
|
+
Use,
|
|
487
|
+
Video
|
|
488
|
+
};
|
|
489
|
+
|
|
330
490
|
const ClientX = 'event.clientX';
|
|
331
491
|
const ClientY = 'event.clientY';
|
|
332
492
|
const TargetHref = 'event.target.href';
|
|
@@ -371,6 +531,8 @@ const text = data => {
|
|
|
371
531
|
};
|
|
372
532
|
};
|
|
373
533
|
|
|
534
|
+
new Set(Object.values(VirtualDomElements));
|
|
535
|
+
|
|
374
536
|
const SetText = 1;
|
|
375
537
|
const Replace = 2;
|
|
376
538
|
const SetAttribute = 3;
|
|
@@ -642,6 +804,7 @@ const Categories = 'Categories';
|
|
|
642
804
|
const Category = 'Category';
|
|
643
805
|
const Changelog$1 = 'Changelog';
|
|
644
806
|
const MaskIcon = 'MaskIcon';
|
|
807
|
+
const MaskIconWarning = 'MaskIconWarning';
|
|
645
808
|
const ResourceIcon = 'ResourceIcon';
|
|
646
809
|
const Code$1 = 'Code';
|
|
647
810
|
const DefaultMarkdown = 'DefaultMarkdown';
|
|
@@ -651,6 +814,11 @@ const DefinitionListItemHeading = 'DefinitionListItemHeading';
|
|
|
651
814
|
const DefinitionListItemValue = 'DefinitionListItemValue';
|
|
652
815
|
const ExtensionDetail = 'ExtensionDetail';
|
|
653
816
|
const ExtensionDetailDescription = 'ExtensionDetailDescription';
|
|
817
|
+
const ExtensionDetailError = 'ExtensionDetailError';
|
|
818
|
+
const ExtensionDetailErrorCard = 'ExtensionDetailErrorCard';
|
|
819
|
+
const ExtensionDetailErrorIcon = 'ExtensionDetailErrorIcon';
|
|
820
|
+
const ExtensionDetailErrorMessage = 'ExtensionDetailErrorMessage';
|
|
821
|
+
const ExtensionDetailErrorTitle = 'ExtensionDetailErrorTitle';
|
|
654
822
|
const ExtensionDetailHeader = 'ExtensionDetailHeader';
|
|
655
823
|
const ExtensionDetailHeaderActions = 'ExtensionDetailHeaderActions';
|
|
656
824
|
const ExtensionDetailHeaderDetails = 'ExtensionDetailHeaderDetails';
|
|
@@ -1375,7 +1543,7 @@ class AssertionError extends Error {
|
|
|
1375
1543
|
const Object$1 = 1;
|
|
1376
1544
|
const Number$1 = 2;
|
|
1377
1545
|
const Array$1 = 3;
|
|
1378
|
-
const String = 4;
|
|
1546
|
+
const String$1 = 4;
|
|
1379
1547
|
const Boolean$1 = 5;
|
|
1380
1548
|
const Function = 6;
|
|
1381
1549
|
const Null = 7;
|
|
@@ -1387,7 +1555,7 @@ const getType = value => {
|
|
|
1387
1555
|
case 'function':
|
|
1388
1556
|
return Function;
|
|
1389
1557
|
case 'string':
|
|
1390
|
-
return String;
|
|
1558
|
+
return String$1;
|
|
1391
1559
|
case 'object':
|
|
1392
1560
|
if (value === null) {
|
|
1393
1561
|
return Null;
|
|
@@ -1410,7 +1578,7 @@ const number = value => {
|
|
|
1410
1578
|
};
|
|
1411
1579
|
const string = value => {
|
|
1412
1580
|
const type = getType(value);
|
|
1413
|
-
if (type !== String) {
|
|
1581
|
+
if (type !== String$1) {
|
|
1414
1582
|
throw new AssertionError('expected value to be of type string');
|
|
1415
1583
|
}
|
|
1416
1584
|
};
|
|
@@ -1457,7 +1625,6 @@ const walkValue = (value, transferrables, isTransferrable) => {
|
|
|
1457
1625
|
for (const property of Object.values(value)) {
|
|
1458
1626
|
walkValue(property, transferrables, isTransferrable);
|
|
1459
1627
|
}
|
|
1460
|
-
return;
|
|
1461
1628
|
}
|
|
1462
1629
|
};
|
|
1463
1630
|
const getTransferrables = value => {
|
|
@@ -1611,7 +1778,14 @@ class IpcError extends VError {
|
|
|
1611
1778
|
const cause = new Error(message);
|
|
1612
1779
|
// @ts-ignore
|
|
1613
1780
|
cause.code = code;
|
|
1614
|
-
|
|
1781
|
+
if (stack) {
|
|
1782
|
+
Object.defineProperty(cause, 'stack', {
|
|
1783
|
+
configurable: true,
|
|
1784
|
+
enumerable: false,
|
|
1785
|
+
value: stack,
|
|
1786
|
+
writable: true
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1615
1789
|
super(cause, betterMessage);
|
|
1616
1790
|
} else {
|
|
1617
1791
|
super(betterMessage);
|
|
@@ -2810,6 +2984,7 @@ const HandleTabFocus = 18;
|
|
|
2810
2984
|
const HandleResourceLinkClick = 19;
|
|
2811
2985
|
const HandleMouseEnterEnable = 20;
|
|
2812
2986
|
const HandleMouseLeaveEnable = 21;
|
|
2987
|
+
const HandleHeaderContextMenu = 22;
|
|
2813
2988
|
|
|
2814
2989
|
const ActivationEvents = 'ActivationEvents';
|
|
2815
2990
|
const Changelog = 'Changelog';
|
|
@@ -2851,19 +3026,22 @@ const {
|
|
|
2851
3026
|
set: set$3
|
|
2852
3027
|
} = MarkdownWorker;
|
|
2853
3028
|
|
|
3029
|
+
const addScrollToTopVirtualDom = dom => {
|
|
3030
|
+
const [firstNode, ...rest] = dom;
|
|
3031
|
+
const extraDom = getScrollToTopVirtualDom();
|
|
3032
|
+
return [{
|
|
3033
|
+
...firstNode,
|
|
3034
|
+
childCount: firstNode.childCount + 1,
|
|
3035
|
+
onClick: HandleReadmeClick,
|
|
3036
|
+
onScroll: HandleReadmeScroll,
|
|
3037
|
+
onSelectionChange: HandleSelectionChange
|
|
3038
|
+
}, ...extraDom, ...rest];
|
|
3039
|
+
};
|
|
2854
3040
|
const getMarkdownVirtualDom = async (html, options) => {
|
|
2855
3041
|
string(html);
|
|
2856
3042
|
const dom = await getVirtualDom(html);
|
|
2857
3043
|
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];
|
|
3044
|
+
return addScrollToTopVirtualDom(dom);
|
|
2867
3045
|
}
|
|
2868
3046
|
return dom;
|
|
2869
3047
|
};
|
|
@@ -3431,6 +3609,8 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
3431
3609
|
disabled: false,
|
|
3432
3610
|
displaySize: '',
|
|
3433
3611
|
downloadCount: 'n/a',
|
|
3612
|
+
errorMessage: '',
|
|
3613
|
+
errorTitle: '',
|
|
3434
3614
|
extension: {},
|
|
3435
3615
|
extensionId: '',
|
|
3436
3616
|
extensionUri: '',
|
|
@@ -3703,6 +3883,96 @@ const getMenus = () => {
|
|
|
3703
3883
|
}];
|
|
3704
3884
|
};
|
|
3705
3885
|
|
|
3886
|
+
let mock;
|
|
3887
|
+
const getPage = url => {
|
|
3888
|
+
return Number(new URL(url).searchParams.get('page')) || 1;
|
|
3889
|
+
};
|
|
3890
|
+
const createGeneratedReleases = (url, count) => {
|
|
3891
|
+
const page = getPage(url);
|
|
3892
|
+
const start = (page - 1) * 100;
|
|
3893
|
+
const end = Math.min(start + 100, count);
|
|
3894
|
+
return Array.from({
|
|
3895
|
+
length: Math.max(0, end - start)
|
|
3896
|
+
}, (_, index) => {
|
|
3897
|
+
const releaseNumber = count - start - index;
|
|
3898
|
+
return {
|
|
3899
|
+
body: `Release notes for version ${releaseNumber}`,
|
|
3900
|
+
html_url: `https://github.com/test-owner/test-repository/releases/tag/v${releaseNumber}`,
|
|
3901
|
+
name: `Version ${releaseNumber}`,
|
|
3902
|
+
published_at: '2026-01-01T00:00:00Z',
|
|
3903
|
+
tag_name: `v${releaseNumber}`
|
|
3904
|
+
};
|
|
3905
|
+
});
|
|
3906
|
+
};
|
|
3907
|
+
const createMockResponse = (url, value) => {
|
|
3908
|
+
const status = value.status ?? 200;
|
|
3909
|
+
const statusText = value.statusText || getStatusText(status);
|
|
3910
|
+
const headers = new Headers(value.headers);
|
|
3911
|
+
if (value.type === 'invalid-json') {
|
|
3912
|
+
headers.set('content-type', 'text/html');
|
|
3913
|
+
return new Response('<!doctype html><title>Unexpected response</title>', {
|
|
3914
|
+
headers,
|
|
3915
|
+
status,
|
|
3916
|
+
statusText
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
const body = value.type === 'generated' ? createGeneratedReleases(url, value.releaseCount ?? 0) : value.body ?? [];
|
|
3920
|
+
headers.set('content-type', 'application/json');
|
|
3921
|
+
return Response.json(body, {
|
|
3922
|
+
headers,
|
|
3923
|
+
status,
|
|
3924
|
+
statusText
|
|
3925
|
+
});
|
|
3926
|
+
};
|
|
3927
|
+
const getStatusText = status => {
|
|
3928
|
+
switch (status) {
|
|
3929
|
+
case 400:
|
|
3930
|
+
return 'Bad Request';
|
|
3931
|
+
case 401:
|
|
3932
|
+
return 'Unauthorized';
|
|
3933
|
+
case 403:
|
|
3934
|
+
return 'Forbidden';
|
|
3935
|
+
case 404:
|
|
3936
|
+
return 'Not Found';
|
|
3937
|
+
case 429:
|
|
3938
|
+
return 'Too Many Requests';
|
|
3939
|
+
case 500:
|
|
3940
|
+
return 'Internal Server Error';
|
|
3941
|
+
case 503:
|
|
3942
|
+
return 'Service Unavailable';
|
|
3943
|
+
default:
|
|
3944
|
+
return '';
|
|
3945
|
+
}
|
|
3946
|
+
};
|
|
3947
|
+
const request = url => {
|
|
3948
|
+
if (!mock) {
|
|
3949
|
+
return fetch(url, {
|
|
3950
|
+
headers: {
|
|
3951
|
+
Accept: 'application/vnd.github+json',
|
|
3952
|
+
'X-GitHub-Api-Version': '2022-11-28'
|
|
3953
|
+
}
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
if (mock.type === 'network-error') {
|
|
3957
|
+
return Promise.reject(new TypeError(mock.message || 'Failed to fetch'));
|
|
3958
|
+
}
|
|
3959
|
+
return Promise.resolve(createMockResponse(url, mock));
|
|
3960
|
+
};
|
|
3961
|
+
const mockGithubApi = value => {
|
|
3962
|
+
mock = value;
|
|
3963
|
+
};
|
|
3964
|
+
const handleMockGithubApi = (state, value) => {
|
|
3965
|
+
mockGithubApi(value);
|
|
3966
|
+
return state;
|
|
3967
|
+
};
|
|
3968
|
+
const resetGithubApiMock = () => {
|
|
3969
|
+
mock = undefined;
|
|
3970
|
+
};
|
|
3971
|
+
const handleResetGithubApiMock = state => {
|
|
3972
|
+
resetGithubApiMock();
|
|
3973
|
+
return state;
|
|
3974
|
+
};
|
|
3975
|
+
|
|
3706
3976
|
const show2 = async (uid, menuId, x, y, args) => {
|
|
3707
3977
|
await showContextMenu2(uid, menuId, x, y, args);
|
|
3708
3978
|
};
|
|
@@ -3900,6 +4170,15 @@ const handleClickFeatures = async (state, name) => {
|
|
|
3900
4170
|
};
|
|
3901
4171
|
|
|
3902
4172
|
const handleClickScrollToTop = state => {
|
|
4173
|
+
if (state.selectedTab === Changelog) {
|
|
4174
|
+
if (state.changelogScrollTop === 0) {
|
|
4175
|
+
return state;
|
|
4176
|
+
}
|
|
4177
|
+
return {
|
|
4178
|
+
...state,
|
|
4179
|
+
changelogScrollTop: 0
|
|
4180
|
+
};
|
|
4181
|
+
}
|
|
3903
4182
|
const {
|
|
3904
4183
|
readmeScrollTop
|
|
3905
4184
|
} = state;
|
|
@@ -4033,6 +4312,16 @@ const getCurrentColorTheme = async () => {
|
|
|
4033
4312
|
return '';
|
|
4034
4313
|
};
|
|
4035
4314
|
|
|
4315
|
+
const getErrorMessage$1 = error => {
|
|
4316
|
+
if (error instanceof Error && error.message) {
|
|
4317
|
+
return error.message;
|
|
4318
|
+
}
|
|
4319
|
+
if (typeof error === 'string' && error) {
|
|
4320
|
+
return error;
|
|
4321
|
+
}
|
|
4322
|
+
return 'Unknown error';
|
|
4323
|
+
};
|
|
4324
|
+
|
|
4036
4325
|
const getExtensionIdFromUri = uri => {
|
|
4037
4326
|
const id = uri.slice('extension-detail://'.length);
|
|
4038
4327
|
return id;
|
|
@@ -4045,6 +4334,47 @@ const getExtensionUri = (uri, platform, origin) => {
|
|
|
4045
4334
|
return new URL(uri, origin).href;
|
|
4046
4335
|
};
|
|
4047
4336
|
|
|
4337
|
+
const segmentRegex = /^[\w.-]+$/;
|
|
4338
|
+
const getRepositoryUrl = extension => {
|
|
4339
|
+
if (!extension || !hasProperty(extension, 'repository')) {
|
|
4340
|
+
return '';
|
|
4341
|
+
}
|
|
4342
|
+
const {
|
|
4343
|
+
repository
|
|
4344
|
+
} = extension;
|
|
4345
|
+
if (typeof repository === 'string') {
|
|
4346
|
+
return repository;
|
|
4347
|
+
}
|
|
4348
|
+
if (repository && hasProperty(repository, 'url') && typeof repository.url === 'string') {
|
|
4349
|
+
return repository.url;
|
|
4350
|
+
}
|
|
4351
|
+
return '';
|
|
4352
|
+
};
|
|
4353
|
+
const getGithubRepository = extension => {
|
|
4354
|
+
const rawUrl = getRepositoryUrl(extension).replace(/^git\+/, '');
|
|
4355
|
+
try {
|
|
4356
|
+
const url = new URL(rawUrl);
|
|
4357
|
+
if (url.protocol !== 'https:' || url.hostname.toLowerCase() !== 'github.com' || url.username || url.password || url.port) {
|
|
4358
|
+
return undefined;
|
|
4359
|
+
}
|
|
4360
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
4361
|
+
if (segments.length !== 2) {
|
|
4362
|
+
return undefined;
|
|
4363
|
+
}
|
|
4364
|
+
const owner = segments[0];
|
|
4365
|
+
const repository = segments[1].replace(/\.git$/i, '');
|
|
4366
|
+
if (!owner || !repository || !segmentRegex.test(owner) || !segmentRegex.test(repository)) {
|
|
4367
|
+
return undefined;
|
|
4368
|
+
}
|
|
4369
|
+
return {
|
|
4370
|
+
owner,
|
|
4371
|
+
repository
|
|
4372
|
+
};
|
|
4373
|
+
} catch {
|
|
4374
|
+
return undefined;
|
|
4375
|
+
}
|
|
4376
|
+
};
|
|
4377
|
+
|
|
4048
4378
|
const getLinkProtectionEnabled = async () => {
|
|
4049
4379
|
try {
|
|
4050
4380
|
const setting = await getPreference('application.linkProtectionEnabled');
|
|
@@ -4507,23 +4837,8 @@ const formatCreated = (created, now = Date.now()) => {
|
|
|
4507
4837
|
return relativeTimeFormat.format(value, unit);
|
|
4508
4838
|
};
|
|
4509
4839
|
|
|
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
|
-
}
|
|
4840
|
+
const formatLastUpdated = (lastUpdated, now = Date.now()) => {
|
|
4841
|
+
return formatCreated(lastUpdated, now);
|
|
4527
4842
|
};
|
|
4528
4843
|
|
|
4529
4844
|
const getSizeEntries = (showSizeLink, displaySize, extensionUri) => {
|
|
@@ -4704,6 +5019,16 @@ const parseCreated = extension => {
|
|
|
4704
5019
|
return timestamp;
|
|
4705
5020
|
};
|
|
4706
5021
|
|
|
5022
|
+
const secondsToMillisecondsThreshold = 10_000_000_000;
|
|
5023
|
+
const normalizeTimestamp = timestamp => {
|
|
5024
|
+
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
5025
|
+
return null;
|
|
5026
|
+
}
|
|
5027
|
+
if (timestamp < secondsToMillisecondsThreshold) {
|
|
5028
|
+
return timestamp * 1000;
|
|
5029
|
+
}
|
|
5030
|
+
return timestamp;
|
|
5031
|
+
};
|
|
4707
5032
|
const parseLastUpdated = extension => {
|
|
4708
5033
|
if (!extension || typeof extension !== 'object') {
|
|
4709
5034
|
return null;
|
|
@@ -4715,16 +5040,14 @@ const parseLastUpdated = extension => {
|
|
|
4715
5040
|
return null;
|
|
4716
5041
|
}
|
|
4717
5042
|
if (typeof lastUpdated === 'number') {
|
|
4718
|
-
|
|
4719
|
-
return lastUpdated;
|
|
4720
|
-
}
|
|
4721
|
-
return null;
|
|
5043
|
+
return normalizeTimestamp(lastUpdated);
|
|
4722
5044
|
}
|
|
4723
5045
|
if (typeof lastUpdated === 'string') {
|
|
4724
|
-
const
|
|
4725
|
-
if (Number.isFinite(
|
|
4726
|
-
return
|
|
5046
|
+
const numericTimestamp = Number(lastUpdated);
|
|
5047
|
+
if (Number.isFinite(numericTimestamp)) {
|
|
5048
|
+
return normalizeTimestamp(numericTimestamp);
|
|
4727
5049
|
}
|
|
5050
|
+
return normalizeTimestamp(Date.parse(lastUpdated));
|
|
4728
5051
|
}
|
|
4729
5052
|
return null;
|
|
4730
5053
|
};
|
|
@@ -4777,7 +5100,7 @@ const restoreState = savedState => {
|
|
|
4777
5100
|
const isEnabled = tab => {
|
|
4778
5101
|
return tab.enabled;
|
|
4779
5102
|
};
|
|
4780
|
-
const
|
|
5103
|
+
const loadContentInternal = async (state, platform, savedState, isTest = false) => {
|
|
4781
5104
|
if (isTest) {
|
|
4782
5105
|
savedState = undefined;
|
|
4783
5106
|
}
|
|
@@ -4839,7 +5162,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4839
5162
|
} = restoreState(savedState);
|
|
4840
5163
|
const features = getFeatures(selectedFeature || Theme, extension);
|
|
4841
5164
|
const hasFeatures = features.length > 0;
|
|
4842
|
-
const
|
|
5165
|
+
const hasGithubReleases = Boolean(getGithubRepository(extension));
|
|
5166
|
+
const tabs = getTabs(selectedTab, hasReadme, hasFeatures, hasChangelog || hasGithubReleases);
|
|
4843
5167
|
const enabledTabs = tabs.filter(isEnabled);
|
|
4844
5168
|
const sizeValue = getViewletSize(width || 0);
|
|
4845
5169
|
const showSizeLink = platform !== Web$1;
|
|
@@ -4872,6 +5196,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4872
5196
|
disabled,
|
|
4873
5197
|
displaySize,
|
|
4874
5198
|
downloadCount,
|
|
5199
|
+
errorMessage: '',
|
|
5200
|
+
errorTitle: '',
|
|
4875
5201
|
extension,
|
|
4876
5202
|
extensionId,
|
|
4877
5203
|
extensionUri,
|
|
@@ -4908,6 +5234,21 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4908
5234
|
tabs: enabledTabs
|
|
4909
5235
|
};
|
|
4910
5236
|
};
|
|
5237
|
+
const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
5238
|
+
try {
|
|
5239
|
+
return await loadContentInternal(state, platform, savedState, isTest);
|
|
5240
|
+
} catch (error) {
|
|
5241
|
+
const extensionId = getExtensionIdFromUri(state.uri);
|
|
5242
|
+
const errorMessage = error instanceof ExtensionNotFoundError ? extensionNotAvailable(extensionId) : unableToLoadExtensionWithError(getErrorMessage$1(error));
|
|
5243
|
+
return {
|
|
5244
|
+
...state,
|
|
5245
|
+
errorMessage,
|
|
5246
|
+
errorTitle: unableToLoadExtension(),
|
|
5247
|
+
extensionId,
|
|
5248
|
+
initial: false
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5251
|
+
};
|
|
4911
5252
|
|
|
4912
5253
|
const handleExtensionsChanged = async state => {
|
|
4913
5254
|
const {
|
|
@@ -4927,6 +5268,10 @@ const handleExtensionsStatusUpdate = async state => {
|
|
|
4927
5268
|
};
|
|
4928
5269
|
};
|
|
4929
5270
|
|
|
5271
|
+
const handleHeaderContextMenu = async state => {
|
|
5272
|
+
return state;
|
|
5273
|
+
};
|
|
5274
|
+
|
|
4930
5275
|
const handleIconError = state => {
|
|
4931
5276
|
const {
|
|
4932
5277
|
assetDir,
|
|
@@ -5057,6 +5402,13 @@ const handleResourceLinkClick = async (state, href) => {
|
|
|
5057
5402
|
|
|
5058
5403
|
const handleScroll = (state, scrollTop, scrollSource = Script) => {
|
|
5059
5404
|
const newScrollTop = Math.max(0, scrollTop);
|
|
5405
|
+
if (state.selectedTab === Changelog) {
|
|
5406
|
+
return {
|
|
5407
|
+
...state,
|
|
5408
|
+
changelogScrollTop: newScrollTop,
|
|
5409
|
+
scrollSource
|
|
5410
|
+
};
|
|
5411
|
+
}
|
|
5060
5412
|
return {
|
|
5061
5413
|
...state,
|
|
5062
5414
|
readmeScrollTop: newScrollTop,
|
|
@@ -5083,6 +5435,39 @@ const handleTabFocus = (state, name) => {
|
|
|
5083
5435
|
};
|
|
5084
5436
|
};
|
|
5085
5437
|
|
|
5438
|
+
const escapeMarkdown = value => {
|
|
5439
|
+
return value.replaceAll('\\', '\\\\').replaceAll('[', '\\[').replaceAll(']', '\\]');
|
|
5440
|
+
};
|
|
5441
|
+
const getPublishedText = publishedAt => {
|
|
5442
|
+
if (!publishedAt) {
|
|
5443
|
+
return 'Publication date unavailable';
|
|
5444
|
+
}
|
|
5445
|
+
const date = new Date(publishedAt);
|
|
5446
|
+
if (Number.isNaN(date.getTime())) {
|
|
5447
|
+
return 'Publication date unavailable';
|
|
5448
|
+
}
|
|
5449
|
+
return `Published ${date.toLocaleDateString(undefined, {
|
|
5450
|
+
dateStyle: 'long'
|
|
5451
|
+
})}`;
|
|
5452
|
+
};
|
|
5453
|
+
const getGithubReleasesMarkdown = (releases, githubRepository) => {
|
|
5454
|
+
if (releases.length === 0) {
|
|
5455
|
+
return `# Changelog\n\nNo GitHub releases were found for **${escapeMarkdown(githubRepository.owner)}/${escapeMarkdown(githubRepository.repository)}**.`;
|
|
5456
|
+
}
|
|
5457
|
+
return releases.map(release => {
|
|
5458
|
+
const title = release.name || release.tagName;
|
|
5459
|
+
const body = release.body.trim() || '_No release notes were provided._';
|
|
5460
|
+
return `# [${escapeMarkdown(title)}](${release.htmlUrl})\n\n${getPublishedText(release.publishedAt)} · \`${release.tagName}\`\n\n${body}`;
|
|
5461
|
+
}).join('\n\n---\n\n');
|
|
5462
|
+
};
|
|
5463
|
+
|
|
5464
|
+
class GithubReleasesError extends Error {
|
|
5465
|
+
constructor(message) {
|
|
5466
|
+
super(message);
|
|
5467
|
+
this.name = 'GithubReleasesError';
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
|
|
5086
5471
|
const error = async error => {
|
|
5087
5472
|
// TODO send message to error worker or log worker
|
|
5088
5473
|
// @ts-ignore
|
|
@@ -5103,21 +5488,206 @@ const loadChangelogContent = async path => {
|
|
|
5103
5488
|
}
|
|
5104
5489
|
};
|
|
5105
5490
|
|
|
5491
|
+
const pageSize = 100;
|
|
5492
|
+
const maximumReleases = 100;
|
|
5493
|
+
const getErrorMessage = async response => {
|
|
5494
|
+
try {
|
|
5495
|
+
const body = await response.clone().json();
|
|
5496
|
+
if (body && hasProperty(body, 'message') && typeof body.message === 'string') {
|
|
5497
|
+
return body.message;
|
|
5498
|
+
}
|
|
5499
|
+
} catch {
|
|
5500
|
+
// The status code still provides a useful error when the error body is invalid.
|
|
5501
|
+
}
|
|
5502
|
+
return '';
|
|
5503
|
+
};
|
|
5504
|
+
const getHttpError = async response => {
|
|
5505
|
+
const apiMessage = await getErrorMessage(response);
|
|
5506
|
+
const rateLimitRemaining = response.headers.get('x-ratelimit-remaining');
|
|
5507
|
+
if (response.status === 429 || response.status === 403 && (rateLimitRemaining === '0' || /rate limit/i.test(apiMessage))) {
|
|
5508
|
+
const resetValue = Number(response.headers.get('x-ratelimit-reset'));
|
|
5509
|
+
const retry = Number.isFinite(resetValue) && resetValue > 0 ? ` Try again after ${new Date(resetValue * 1000).toLocaleString()}.` : ' Please try again later.';
|
|
5510
|
+
return new GithubReleasesError(`GitHub API rate limit exceeded.${retry}`);
|
|
5511
|
+
}
|
|
5512
|
+
if (response.status === 404) {
|
|
5513
|
+
return new GithubReleasesError('The GitHub repository could not be found or is not publicly reachable.');
|
|
5514
|
+
}
|
|
5515
|
+
if (response.status >= 500) {
|
|
5516
|
+
return new GithubReleasesError(`GitHub is temporarily unavailable (server error ${response.status}). Please try again later.`);
|
|
5517
|
+
}
|
|
5518
|
+
if (response.status >= 400) {
|
|
5519
|
+
const detail = apiMessage ? `: ${apiMessage}` : '';
|
|
5520
|
+
return new GithubReleasesError(`GitHub rejected the releases request (${response.status} ${response.statusText || 'Client Error'})${detail}.`);
|
|
5521
|
+
}
|
|
5522
|
+
return new GithubReleasesError(`GitHub returned an unexpected HTTP status (${response.status}).`);
|
|
5523
|
+
};
|
|
5524
|
+
const parseNullableString = value => {
|
|
5525
|
+
if (typeof value === 'string') {
|
|
5526
|
+
return value;
|
|
5527
|
+
}
|
|
5528
|
+
if (value === null) {
|
|
5529
|
+
return '';
|
|
5530
|
+
}
|
|
5531
|
+
return undefined;
|
|
5532
|
+
};
|
|
5533
|
+
const parseRelease = value => {
|
|
5534
|
+
if (!value || !hasProperty(value, 'body') || !hasProperty(value, 'html_url') || !hasProperty(value, 'name') || !hasProperty(value, 'published_at') || !hasProperty(value, 'tag_name')) {
|
|
5535
|
+
return undefined;
|
|
5536
|
+
}
|
|
5537
|
+
const body = parseNullableString(value.body);
|
|
5538
|
+
const name = parseNullableString(value.name);
|
|
5539
|
+
const publishedAt = parseNullableString(value.published_at);
|
|
5540
|
+
if (body === undefined || name === undefined || publishedAt === undefined || typeof value.html_url !== 'string' || typeof value.tag_name !== 'string') {
|
|
5541
|
+
return undefined;
|
|
5542
|
+
}
|
|
5543
|
+
try {
|
|
5544
|
+
const htmlUrl = new URL(value.html_url);
|
|
5545
|
+
if (htmlUrl.protocol !== 'https:' || htmlUrl.hostname !== 'github.com') {
|
|
5546
|
+
return undefined;
|
|
5547
|
+
}
|
|
5548
|
+
} catch {
|
|
5549
|
+
return undefined;
|
|
5550
|
+
}
|
|
5551
|
+
return {
|
|
5552
|
+
body,
|
|
5553
|
+
htmlUrl: value.html_url,
|
|
5554
|
+
name,
|
|
5555
|
+
publishedAt,
|
|
5556
|
+
tagName: value.tag_name
|
|
5557
|
+
};
|
|
5558
|
+
};
|
|
5559
|
+
const parsePage = async response => {
|
|
5560
|
+
let value;
|
|
5561
|
+
try {
|
|
5562
|
+
value = await response.json();
|
|
5563
|
+
} catch {
|
|
5564
|
+
throw new GithubReleasesError('GitHub returned an invalid response instead of JSON release data. Please try again later.');
|
|
5565
|
+
}
|
|
5566
|
+
if (!Array.isArray(value)) {
|
|
5567
|
+
throw new GithubReleasesError('GitHub returned invalid release data. Please try again later.');
|
|
5568
|
+
}
|
|
5569
|
+
const releases = value.map(parseRelease);
|
|
5570
|
+
if (releases.some(release => !release)) {
|
|
5571
|
+
throw new GithubReleasesError('GitHub returned invalid release data. Please try again later.');
|
|
5572
|
+
}
|
|
5573
|
+
return releases;
|
|
5574
|
+
};
|
|
5575
|
+
const loadGithubReleases = async ({
|
|
5576
|
+
owner,
|
|
5577
|
+
repository
|
|
5578
|
+
}) => {
|
|
5579
|
+
const releases = [];
|
|
5580
|
+
for (let page = 1;; page++) {
|
|
5581
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/releases?per_page=${pageSize}&page=${page}`;
|
|
5582
|
+
let response;
|
|
5583
|
+
try {
|
|
5584
|
+
response = await request(url);
|
|
5585
|
+
} catch {
|
|
5586
|
+
throw new GithubReleasesError('GitHub releases could not be loaded because GitHub is not reachable. Check your connection and try again.');
|
|
5587
|
+
}
|
|
5588
|
+
if (!response.ok) {
|
|
5589
|
+
throw await getHttpError(response);
|
|
5590
|
+
}
|
|
5591
|
+
const pageReleases = await parsePage(response);
|
|
5592
|
+
const remaining = maximumReleases - releases.length;
|
|
5593
|
+
if (pageReleases.length > remaining) {
|
|
5594
|
+
releases.push(...pageReleases.slice(0, remaining));
|
|
5595
|
+
return {
|
|
5596
|
+
isTruncated: true,
|
|
5597
|
+
releases
|
|
5598
|
+
};
|
|
5599
|
+
}
|
|
5600
|
+
releases.push(...pageReleases);
|
|
5601
|
+
if (pageReleases.length < pageSize) {
|
|
5602
|
+
return {
|
|
5603
|
+
isTruncated: false,
|
|
5604
|
+
releases
|
|
5605
|
+
};
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
};
|
|
5609
|
+
|
|
5610
|
+
const releaseBatchSize = 100;
|
|
5611
|
+
const mergeMarkdownVirtualDoms = chunks => {
|
|
5612
|
+
let root;
|
|
5613
|
+
const children = [];
|
|
5614
|
+
for (const chunk of chunks) {
|
|
5615
|
+
const [chunkRoot, ...chunkChildren] = chunk;
|
|
5616
|
+
if (!chunkRoot) {
|
|
5617
|
+
continue;
|
|
5618
|
+
}
|
|
5619
|
+
root = root ? {
|
|
5620
|
+
...root,
|
|
5621
|
+
childCount: root.childCount + chunkRoot.childCount
|
|
5622
|
+
} : chunkRoot;
|
|
5623
|
+
children.push(...chunkChildren);
|
|
5624
|
+
}
|
|
5625
|
+
return root ? [root, ...children] : [];
|
|
5626
|
+
};
|
|
5627
|
+
const renderGithubReleases = async (result, githubRepository, languages, locationProtocol) => {
|
|
5628
|
+
const chunks = [];
|
|
5629
|
+
const {
|
|
5630
|
+
isTruncated,
|
|
5631
|
+
releases
|
|
5632
|
+
} = result;
|
|
5633
|
+
const releaseGroups = releases.length === 0 ? [[]] : Array.from({
|
|
5634
|
+
length: Math.ceil(releases.length / releaseBatchSize)
|
|
5635
|
+
}, (_, index) => {
|
|
5636
|
+
return releases.slice(index * releaseBatchSize, (index + 1) * releaseBatchSize);
|
|
5637
|
+
});
|
|
5638
|
+
const baseUrl = `https://github.com/${githubRepository.owner}/${githubRepository.repository}/blob/HEAD/`;
|
|
5639
|
+
for (const [index, releaseGroup] of releaseGroups.entries()) {
|
|
5640
|
+
const limitMessage = index === 0 && isTruncated ? `> Showing the newest ${releases.length} GitHub releases. Older releases are not displayed to keep the editor responsive.\n\n` : '';
|
|
5641
|
+
const markdown = limitMessage + getGithubReleasesMarkdown(releaseGroup, githubRepository);
|
|
5642
|
+
const html = await renderMarkdown(markdown, {
|
|
5643
|
+
baseUrl,
|
|
5644
|
+
languages,
|
|
5645
|
+
linksExternal: true,
|
|
5646
|
+
locationProtocol
|
|
5647
|
+
});
|
|
5648
|
+
chunks.push([...(await getMarkdownVirtualDom(html))]);
|
|
5649
|
+
}
|
|
5650
|
+
return addScrollToTopVirtualDom(mergeMarkdownVirtualDoms(chunks));
|
|
5651
|
+
};
|
|
5106
5652
|
const selectTabChangelog = async state => {
|
|
5107
5653
|
const {
|
|
5108
5654
|
baseUrl,
|
|
5655
|
+
extension,
|
|
5109
5656
|
extensionUri,
|
|
5110
5657
|
languages,
|
|
5111
5658
|
locationProtocol,
|
|
5112
5659
|
tabs
|
|
5113
5660
|
} = state;
|
|
5114
|
-
const
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5661
|
+
const githubRepository = getGithubRepository(extension);
|
|
5662
|
+
let changelogDom;
|
|
5663
|
+
if (githubRepository) {
|
|
5664
|
+
try {
|
|
5665
|
+
const result = await loadGithubReleases(githubRepository);
|
|
5666
|
+
changelogDom = await renderGithubReleases(result, githubRepository, languages, locationProtocol);
|
|
5667
|
+
} catch (error) {
|
|
5668
|
+
const message = error instanceof GithubReleasesError ? error.message : 'GitHub releases could not be loaded. Please try again later.';
|
|
5669
|
+
const html = await renderMarkdown(`# Changelog\n\n${message}`, {
|
|
5670
|
+
baseUrl,
|
|
5671
|
+
languages,
|
|
5672
|
+
linksExternal: true,
|
|
5673
|
+
locationProtocol
|
|
5674
|
+
});
|
|
5675
|
+
changelogDom = await getMarkdownVirtualDom(html, {
|
|
5676
|
+
scrollToTopEnabled: true
|
|
5677
|
+
});
|
|
5678
|
+
}
|
|
5679
|
+
} else {
|
|
5680
|
+
const changelogContent = await loadChangelogContent(extensionUri);
|
|
5681
|
+
const html = await renderMarkdown(changelogContent, {
|
|
5682
|
+
baseUrl,
|
|
5683
|
+
languages,
|
|
5684
|
+
linksExternal: true,
|
|
5685
|
+
locationProtocol
|
|
5686
|
+
});
|
|
5687
|
+
changelogDom = await getMarkdownVirtualDom(html, {
|
|
5688
|
+
scrollToTopEnabled: true
|
|
5689
|
+
});
|
|
5690
|
+
}
|
|
5121
5691
|
const newTabs = tabs.map(tab => {
|
|
5122
5692
|
return {
|
|
5123
5693
|
...tab,
|
|
@@ -5391,7 +5961,8 @@ const getChangelogVirtualDom = changelogDom => {
|
|
|
5391
5961
|
// TODO set tabpanel role
|
|
5392
5962
|
return [{
|
|
5393
5963
|
childCount: 1,
|
|
5394
|
-
className: Changelog$1
|
|
5964
|
+
className: `${ExtensionDetailPanel} ${Changelog$1}`,
|
|
5965
|
+
role: Panel,
|
|
5395
5966
|
type: Div
|
|
5396
5967
|
}, ...changelogDom];
|
|
5397
5968
|
};
|
|
@@ -5695,6 +6266,31 @@ const getExtensionDetailContentVirtualDom = (sanitizedReadmeHtml, selectedTab, w
|
|
|
5695
6266
|
}
|
|
5696
6267
|
};
|
|
5697
6268
|
|
|
6269
|
+
const getExtensionDetailErrorVirtualDom = (title, message) => {
|
|
6270
|
+
return [{
|
|
6271
|
+
childCount: 1,
|
|
6272
|
+
className: mergeClassNames(Viewlet, ExtensionDetail, ExtensionDetailError),
|
|
6273
|
+
type: Div
|
|
6274
|
+
}, {
|
|
6275
|
+
childCount: 3,
|
|
6276
|
+
className: ExtensionDetailErrorCard,
|
|
6277
|
+
role: 'alert',
|
|
6278
|
+
type: Div
|
|
6279
|
+
}, {
|
|
6280
|
+
childCount: 0,
|
|
6281
|
+
className: mergeClassNames(MaskIcon, MaskIconWarning, ExtensionDetailErrorIcon),
|
|
6282
|
+
type: Div
|
|
6283
|
+
}, {
|
|
6284
|
+
childCount: 1,
|
|
6285
|
+
className: ExtensionDetailErrorTitle,
|
|
6286
|
+
type: H1
|
|
6287
|
+
}, text(title), {
|
|
6288
|
+
childCount: 1,
|
|
6289
|
+
className: ExtensionDetailErrorMessage,
|
|
6290
|
+
type: P
|
|
6291
|
+
}, text(message)];
|
|
6292
|
+
};
|
|
6293
|
+
|
|
5698
6294
|
const getExtensionDetailDescriptionVirtualDom = description => {
|
|
5699
6295
|
return [{
|
|
5700
6296
|
childCount: 1,
|
|
@@ -5807,6 +6403,7 @@ const getExtensionDetailHeaderVirtualDom = (name, iconSrc, description, badge, b
|
|
|
5807
6403
|
}, getExtensionDetailIconVirtualDom(iconSrc), {
|
|
5808
6404
|
childCount: 3,
|
|
5809
6405
|
className: ExtensionDetailHeaderDetails,
|
|
6406
|
+
onContextMenu: HandleHeaderContextMenu,
|
|
5810
6407
|
type: Div
|
|
5811
6408
|
}, ...getExtensionDetailNameVirtualDom(name, badge), ...getExtensionDetailDescriptionVirtualDom(description), ...getExtensionDetailHeaderActionsVirtualDom(buttonDefs, settingsButtonEnabled)];
|
|
5812
6409
|
return dom;
|
|
@@ -5863,6 +6460,9 @@ const getClassNames = size => {
|
|
|
5863
6460
|
};
|
|
5864
6461
|
|
|
5865
6462
|
const getExtensionDetailVirtualDom = (newState, selectedTab) => {
|
|
6463
|
+
if (newState.errorMessage) {
|
|
6464
|
+
return getExtensionDetailErrorVirtualDom(newState.errorTitle, newState.errorMessage);
|
|
6465
|
+
}
|
|
5866
6466
|
// TODO move this to view model so that rendering occurs like
|
|
5867
6467
|
// 1. state
|
|
5868
6468
|
// 2. view model
|
|
@@ -6029,6 +6629,10 @@ const renderEventListeners = () => {
|
|
|
6029
6629
|
name: HandleImageContextMenu,
|
|
6030
6630
|
params: ['handleImageContextMenu', ClientX, ClientY],
|
|
6031
6631
|
preventDefault: true
|
|
6632
|
+
}, {
|
|
6633
|
+
name: HandleHeaderContextMenu,
|
|
6634
|
+
params: ['handleHeaderContextMenu'],
|
|
6635
|
+
preventDefault: true
|
|
6032
6636
|
}, {
|
|
6033
6637
|
name: HandleReadmeScroll,
|
|
6034
6638
|
params: ['handleScroll', 'event.target.scrollTop', User],
|
|
@@ -6141,6 +6745,7 @@ const commandMap = {
|
|
|
6141
6745
|
'ExtensionDetail.handleExtensionsChanged': wrapCommand(handleExtensionsChanged),
|
|
6142
6746
|
'ExtensionDetail.handleExtensionsStatusUpdate': wrapCommand(handleExtensionsStatusUpdate),
|
|
6143
6747
|
'ExtensionDetail.handleFeaturesClick': wrapCommand(handleClickFeatures),
|
|
6748
|
+
'ExtensionDetail.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
|
|
6144
6749
|
'ExtensionDetail.handleIconError': wrapCommand(handleIconError),
|
|
6145
6750
|
'ExtensionDetail.handleImageContextMenu': wrapCommand(handleImageContextMenu),
|
|
6146
6751
|
'ExtensionDetail.handleMouseEnterEnable': wrapCommand(handleMouseEnterEnable),
|
|
@@ -6157,9 +6762,11 @@ const commandMap = {
|
|
|
6157
6762
|
'ExtensionDetail.hideSizeLink': wrapCommand(hideSizeLink),
|
|
6158
6763
|
'ExtensionDetail.initialize': initialize,
|
|
6159
6764
|
'ExtensionDetail.loadContent2': wrapCommand(loadContent2),
|
|
6765
|
+
'ExtensionDetail.mockGithubApi': wrapCommand(handleMockGithubApi),
|
|
6160
6766
|
'ExtensionDetail.openImageInNewTab': wrapCommand(openImageInNewTab),
|
|
6161
6767
|
'ExtensionDetail.render2': render2,
|
|
6162
6768
|
'ExtensionDetail.renderEventListeners': renderEventListeners,
|
|
6769
|
+
'ExtensionDetail.resetGithubApiMock': wrapCommand(handleResetGithubApiMock),
|
|
6163
6770
|
'ExtensionDetail.resize': wrapCommand(resize),
|
|
6164
6771
|
'ExtensionDetail.saveState': wrapGetter(saveState),
|
|
6165
6772
|
'ExtensionDetail.selectTab': wrapCommand(selectTab),
|