@lvce-editor/extension-detail-view 7.10.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,11 +4312,69 @@ 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;
|
|
4039
4328
|
};
|
|
4040
4329
|
|
|
4330
|
+
const getExtensionUri = (uri, platform, origin) => {
|
|
4331
|
+
if (platform !== Web || !uri) {
|
|
4332
|
+
return uri;
|
|
4333
|
+
}
|
|
4334
|
+
return new URL(uri, origin).href;
|
|
4335
|
+
};
|
|
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
|
+
|
|
4041
4378
|
const getLinkProtectionEnabled = async () => {
|
|
4042
4379
|
try {
|
|
4043
4380
|
const setting = await getPreference('application.linkProtectionEnabled');
|
|
@@ -4186,6 +4523,9 @@ const getIcon = (extension, platform, assetDir) => {
|
|
|
4186
4523
|
}
|
|
4187
4524
|
return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
|
|
4188
4525
|
}
|
|
4526
|
+
if (platform === Web) {
|
|
4527
|
+
return `${extension.path}/${extension.icon}`;
|
|
4528
|
+
}
|
|
4189
4529
|
return '';
|
|
4190
4530
|
};
|
|
4191
4531
|
|
|
@@ -4497,23 +4837,8 @@ const formatCreated = (created, now = Date.now()) => {
|
|
|
4497
4837
|
return relativeTimeFormat.format(value, unit);
|
|
4498
4838
|
};
|
|
4499
4839
|
|
|
4500
|
-
const formatLastUpdated = lastUpdated => {
|
|
4501
|
-
|
|
4502
|
-
return 'n/a';
|
|
4503
|
-
}
|
|
4504
|
-
try {
|
|
4505
|
-
const date = new Date(lastUpdated * 1000);
|
|
4506
|
-
if (Number.isNaN(date.getTime())) {
|
|
4507
|
-
return 'n/a';
|
|
4508
|
-
}
|
|
4509
|
-
return date.toLocaleDateString(undefined, {
|
|
4510
|
-
day: 'numeric',
|
|
4511
|
-
month: 'short',
|
|
4512
|
-
year: 'numeric'
|
|
4513
|
-
});
|
|
4514
|
-
} catch {
|
|
4515
|
-
return 'n/a';
|
|
4516
|
-
}
|
|
4840
|
+
const formatLastUpdated = (lastUpdated, now = Date.now()) => {
|
|
4841
|
+
return formatCreated(lastUpdated, now);
|
|
4517
4842
|
};
|
|
4518
4843
|
|
|
4519
4844
|
const getSizeEntries = (showSizeLink, displaySize, extensionUri) => {
|
|
@@ -4694,6 +5019,16 @@ const parseCreated = extension => {
|
|
|
4694
5019
|
return timestamp;
|
|
4695
5020
|
};
|
|
4696
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
|
+
};
|
|
4697
5032
|
const parseLastUpdated = extension => {
|
|
4698
5033
|
if (!extension || typeof extension !== 'object') {
|
|
4699
5034
|
return null;
|
|
@@ -4705,16 +5040,14 @@ const parseLastUpdated = extension => {
|
|
|
4705
5040
|
return null;
|
|
4706
5041
|
}
|
|
4707
5042
|
if (typeof lastUpdated === 'number') {
|
|
4708
|
-
|
|
4709
|
-
return lastUpdated;
|
|
4710
|
-
}
|
|
4711
|
-
return null;
|
|
5043
|
+
return normalizeTimestamp(lastUpdated);
|
|
4712
5044
|
}
|
|
4713
5045
|
if (typeof lastUpdated === 'string') {
|
|
4714
|
-
const
|
|
4715
|
-
if (Number.isFinite(
|
|
4716
|
-
return
|
|
5046
|
+
const numericTimestamp = Number(lastUpdated);
|
|
5047
|
+
if (Number.isFinite(numericTimestamp)) {
|
|
5048
|
+
return normalizeTimestamp(numericTimestamp);
|
|
4717
5049
|
}
|
|
5050
|
+
return normalizeTimestamp(Date.parse(lastUpdated));
|
|
4718
5051
|
}
|
|
4719
5052
|
return null;
|
|
4720
5053
|
};
|
|
@@ -4767,7 +5100,7 @@ const restoreState = savedState => {
|
|
|
4767
5100
|
const isEnabled = tab => {
|
|
4768
5101
|
return tab.enabled;
|
|
4769
5102
|
};
|
|
4770
|
-
const
|
|
5103
|
+
const loadContentInternal = async (state, platform, savedState, isTest = false) => {
|
|
4771
5104
|
if (isTest) {
|
|
4772
5105
|
savedState = undefined;
|
|
4773
5106
|
}
|
|
@@ -4789,13 +5122,14 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4789
5122
|
description,
|
|
4790
5123
|
downloadCount,
|
|
4791
5124
|
extensionId,
|
|
4792
|
-
extensionUri,
|
|
5125
|
+
extensionUri: unresolvedExtensionUri,
|
|
4793
5126
|
extensionVersion,
|
|
4794
5127
|
hasColorTheme,
|
|
4795
5128
|
iconSrc,
|
|
4796
5129
|
name,
|
|
4797
5130
|
rating
|
|
4798
5131
|
} = headerData;
|
|
5132
|
+
const extensionUri = getExtensionUri(unresolvedExtensionUri, platform, location.origin);
|
|
4799
5133
|
const readmeUrl = join(extensionUri, 'README.md');
|
|
4800
5134
|
const changelogUrl = join(extensionUri, 'CHANGELOG.md');
|
|
4801
5135
|
const [hasReadme, hasChangelog] = await Promise.all([existsFile(readmeUrl), existsFile(changelogUrl)]);
|
|
@@ -4814,7 +5148,7 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4814
5148
|
const detailsVirtualDom = await getMarkdownVirtualDom(readmeHtml, {
|
|
4815
5149
|
scrollToTopEnabled: true
|
|
4816
5150
|
});
|
|
4817
|
-
const isBuiltin = extension?.isBuiltin;
|
|
5151
|
+
const isBuiltin = extension?.isBuiltin || extension?.builtin || false;
|
|
4818
5152
|
const disabled = extension?.disabled;
|
|
4819
5153
|
const extensionColorThemeId = getColorThemeId(extension) || '';
|
|
4820
5154
|
const extensionColorThemeLabel = getColorThemeLabel(extension) || '';
|
|
@@ -4828,7 +5162,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4828
5162
|
} = restoreState(savedState);
|
|
4829
5163
|
const features = getFeatures(selectedFeature || Theme, extension);
|
|
4830
5164
|
const hasFeatures = features.length > 0;
|
|
4831
|
-
const
|
|
5165
|
+
const hasGithubReleases = Boolean(getGithubRepository(extension));
|
|
5166
|
+
const tabs = getTabs(selectedTab, hasReadme, hasFeatures, hasChangelog || hasGithubReleases);
|
|
4832
5167
|
const enabledTabs = tabs.filter(isEnabled);
|
|
4833
5168
|
const sizeValue = getViewletSize(width || 0);
|
|
4834
5169
|
const showSizeLink = platform !== Web$1;
|
|
@@ -4861,6 +5196,8 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4861
5196
|
disabled,
|
|
4862
5197
|
displaySize,
|
|
4863
5198
|
downloadCount,
|
|
5199
|
+
errorMessage: '',
|
|
5200
|
+
errorTitle: '',
|
|
4864
5201
|
extension,
|
|
4865
5202
|
extensionId,
|
|
4866
5203
|
extensionUri,
|
|
@@ -4897,6 +5234,21 @@ const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
|
4897
5234
|
tabs: enabledTabs
|
|
4898
5235
|
};
|
|
4899
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
|
+
};
|
|
4900
5252
|
|
|
4901
5253
|
const handleExtensionsChanged = async state => {
|
|
4902
5254
|
const {
|
|
@@ -4916,6 +5268,10 @@ const handleExtensionsStatusUpdate = async state => {
|
|
|
4916
5268
|
};
|
|
4917
5269
|
};
|
|
4918
5270
|
|
|
5271
|
+
const handleHeaderContextMenu = async state => {
|
|
5272
|
+
return state;
|
|
5273
|
+
};
|
|
5274
|
+
|
|
4919
5275
|
const handleIconError = state => {
|
|
4920
5276
|
const {
|
|
4921
5277
|
assetDir,
|
|
@@ -5046,6 +5402,13 @@ const handleResourceLinkClick = async (state, href) => {
|
|
|
5046
5402
|
|
|
5047
5403
|
const handleScroll = (state, scrollTop, scrollSource = Script) => {
|
|
5048
5404
|
const newScrollTop = Math.max(0, scrollTop);
|
|
5405
|
+
if (state.selectedTab === Changelog) {
|
|
5406
|
+
return {
|
|
5407
|
+
...state,
|
|
5408
|
+
changelogScrollTop: newScrollTop,
|
|
5409
|
+
scrollSource
|
|
5410
|
+
};
|
|
5411
|
+
}
|
|
5049
5412
|
return {
|
|
5050
5413
|
...state,
|
|
5051
5414
|
readmeScrollTop: newScrollTop,
|
|
@@ -5072,6 +5435,39 @@ const handleTabFocus = (state, name) => {
|
|
|
5072
5435
|
};
|
|
5073
5436
|
};
|
|
5074
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
|
+
|
|
5075
5471
|
const error = async error => {
|
|
5076
5472
|
// TODO send message to error worker or log worker
|
|
5077
5473
|
// @ts-ignore
|
|
@@ -5092,21 +5488,206 @@ const loadChangelogContent = async path => {
|
|
|
5092
5488
|
}
|
|
5093
5489
|
};
|
|
5094
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
|
+
};
|
|
5095
5652
|
const selectTabChangelog = async state => {
|
|
5096
5653
|
const {
|
|
5097
5654
|
baseUrl,
|
|
5098
5655
|
extension,
|
|
5656
|
+
extensionUri,
|
|
5099
5657
|
languages,
|
|
5100
5658
|
locationProtocol,
|
|
5101
5659
|
tabs
|
|
5102
5660
|
} = state;
|
|
5103
|
-
const
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
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
|
+
}
|
|
5110
5691
|
const newTabs = tabs.map(tab => {
|
|
5111
5692
|
return {
|
|
5112
5693
|
...tab,
|
|
@@ -5380,7 +5961,8 @@ const getChangelogVirtualDom = changelogDom => {
|
|
|
5380
5961
|
// TODO set tabpanel role
|
|
5381
5962
|
return [{
|
|
5382
5963
|
childCount: 1,
|
|
5383
|
-
className: Changelog$1
|
|
5964
|
+
className: `${ExtensionDetailPanel} ${Changelog$1}`,
|
|
5965
|
+
role: Panel,
|
|
5384
5966
|
type: Div
|
|
5385
5967
|
}, ...changelogDom];
|
|
5386
5968
|
};
|
|
@@ -5684,6 +6266,31 @@ const getExtensionDetailContentVirtualDom = (sanitizedReadmeHtml, selectedTab, w
|
|
|
5684
6266
|
}
|
|
5685
6267
|
};
|
|
5686
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
|
+
|
|
5687
6294
|
const getExtensionDetailDescriptionVirtualDom = description => {
|
|
5688
6295
|
return [{
|
|
5689
6296
|
childCount: 1,
|
|
@@ -5796,6 +6403,7 @@ const getExtensionDetailHeaderVirtualDom = (name, iconSrc, description, badge, b
|
|
|
5796
6403
|
}, getExtensionDetailIconVirtualDom(iconSrc), {
|
|
5797
6404
|
childCount: 3,
|
|
5798
6405
|
className: ExtensionDetailHeaderDetails,
|
|
6406
|
+
onContextMenu: HandleHeaderContextMenu,
|
|
5799
6407
|
type: Div
|
|
5800
6408
|
}, ...getExtensionDetailNameVirtualDom(name, badge), ...getExtensionDetailDescriptionVirtualDom(description), ...getExtensionDetailHeaderActionsVirtualDom(buttonDefs, settingsButtonEnabled)];
|
|
5801
6409
|
return dom;
|
|
@@ -5852,6 +6460,9 @@ const getClassNames = size => {
|
|
|
5852
6460
|
};
|
|
5853
6461
|
|
|
5854
6462
|
const getExtensionDetailVirtualDom = (newState, selectedTab) => {
|
|
6463
|
+
if (newState.errorMessage) {
|
|
6464
|
+
return getExtensionDetailErrorVirtualDom(newState.errorTitle, newState.errorMessage);
|
|
6465
|
+
}
|
|
5855
6466
|
// TODO move this to view model so that rendering occurs like
|
|
5856
6467
|
// 1. state
|
|
5857
6468
|
// 2. view model
|
|
@@ -6018,6 +6629,10 @@ const renderEventListeners = () => {
|
|
|
6018
6629
|
name: HandleImageContextMenu,
|
|
6019
6630
|
params: ['handleImageContextMenu', ClientX, ClientY],
|
|
6020
6631
|
preventDefault: true
|
|
6632
|
+
}, {
|
|
6633
|
+
name: HandleHeaderContextMenu,
|
|
6634
|
+
params: ['handleHeaderContextMenu'],
|
|
6635
|
+
preventDefault: true
|
|
6021
6636
|
}, {
|
|
6022
6637
|
name: HandleReadmeScroll,
|
|
6023
6638
|
params: ['handleScroll', 'event.target.scrollTop', User],
|
|
@@ -6130,6 +6745,7 @@ const commandMap = {
|
|
|
6130
6745
|
'ExtensionDetail.handleExtensionsChanged': wrapCommand(handleExtensionsChanged),
|
|
6131
6746
|
'ExtensionDetail.handleExtensionsStatusUpdate': wrapCommand(handleExtensionsStatusUpdate),
|
|
6132
6747
|
'ExtensionDetail.handleFeaturesClick': wrapCommand(handleClickFeatures),
|
|
6748
|
+
'ExtensionDetail.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
|
|
6133
6749
|
'ExtensionDetail.handleIconError': wrapCommand(handleIconError),
|
|
6134
6750
|
'ExtensionDetail.handleImageContextMenu': wrapCommand(handleImageContextMenu),
|
|
6135
6751
|
'ExtensionDetail.handleMouseEnterEnable': wrapCommand(handleMouseEnterEnable),
|
|
@@ -6146,9 +6762,11 @@ const commandMap = {
|
|
|
6146
6762
|
'ExtensionDetail.hideSizeLink': wrapCommand(hideSizeLink),
|
|
6147
6763
|
'ExtensionDetail.initialize': initialize,
|
|
6148
6764
|
'ExtensionDetail.loadContent2': wrapCommand(loadContent2),
|
|
6765
|
+
'ExtensionDetail.mockGithubApi': wrapCommand(handleMockGithubApi),
|
|
6149
6766
|
'ExtensionDetail.openImageInNewTab': wrapCommand(openImageInNewTab),
|
|
6150
6767
|
'ExtensionDetail.render2': render2,
|
|
6151
6768
|
'ExtensionDetail.renderEventListeners': renderEventListeners,
|
|
6769
|
+
'ExtensionDetail.resetGithubApiMock': wrapCommand(handleResetGithubApiMock),
|
|
6152
6770
|
'ExtensionDetail.resize': wrapCommand(resize),
|
|
6153
6771
|
'ExtensionDetail.saveState': wrapGetter(saveState),
|
|
6154
6772
|
'ExtensionDetail.selectTab': wrapCommand(selectTab),
|