@lvce-editor/extension-detail-view 3.20.0 → 3.22.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.
@@ -424,7 +424,7 @@ const create$4 = (method, params) => {
424
424
  };
425
425
  };
426
426
  const callbacks = Object.create(null);
427
- const set$2 = (id, fn) => {
427
+ const set$4 = (id, fn) => {
428
428
  callbacks[id] = fn;
429
429
  };
430
430
  const get$2 = id => {
@@ -443,7 +443,7 @@ const registerPromise = () => {
443
443
  resolve,
444
444
  promise
445
445
  } = Promise.withResolvers();
446
- set$2(id, resolve);
446
+ set$4(id, resolve);
447
447
  return {
448
448
  id,
449
449
  promise
@@ -886,6 +886,20 @@ const create$1 = () => {
886
886
  };
887
887
  };
888
888
  return wrapped;
889
+ },
890
+ diff(uid, modules, numbers) {
891
+ const {
892
+ oldState,
893
+ newState
894
+ } = states[uid];
895
+ const diffResult = [];
896
+ for (let i = 0; i < modules.length; i++) {
897
+ const fn = modules[i];
898
+ if (!fn(oldState, newState)) {
899
+ diffResult.push(numbers[i]);
900
+ }
901
+ }
902
+ return diffResult;
889
903
  }
890
904
  };
891
905
  };
@@ -897,7 +911,7 @@ const {
897
911
  wrapCommand
898
912
  } = create$1();
899
913
 
900
- const create = (uid, uri, width, height, platform, assetDir$1) => {
914
+ const create = (uid, uri, x, y, width, height, platform, assetDir$1) => {
901
915
  const state = {
902
916
  description: '',
903
917
  iconSrc: '',
@@ -964,124 +978,6 @@ const getCommandIds = () => {
964
978
  return commandIds;
965
979
  };
966
980
 
967
- const AdditionalDetails = 'AdditionalDetails';
968
- const AdditionalDetailsEntry = 'AdditionalDetailsEntry';
969
- const AdditionalDetailsTitle = 'AdditionalDetailsTitle';
970
- const Aside = 'Aside';
971
- const Button$1 = 'Button';
972
- const ButtonPrimary = 'ButtonPrimary';
973
- const DefaultMarkdown = 'DefaultMarkdown';
974
- const Categories = 'Categories';
975
- const Category = 'Category';
976
- const Changelog$1 = 'Changelog';
977
- const DefinitionListItem = 'DefinitionListItem';
978
- const DefinitionListItemHeading = 'DefinitionListItemHeading';
979
- const DefinitionListItemValue = 'DefinitionListItemValue';
980
- const ExtensionDetail = 'ExtensionDetail';
981
- const ExtensionDetailDescription = 'ExtensionDetailDescription';
982
- const ExtensionDetailHeader = 'ExtensionDetailHeader';
983
- const ExtensionDetailHeaderActions = 'ExtensionDetailHeaderActions';
984
- const ExtensionDetailHeaderDetails = 'ExtensionDetailHeaderDetails';
985
- const ExtensionDetailIcon = 'ExtensionDetailIcon';
986
- const ExtensionDetailName = 'ExtensionDetailName';
987
- const ExtensionDetailPanel = 'ExtensionDetailPanel';
988
- const ExtensionDetailTab = 'ExtensionDetailTab';
989
- const ExtensionDetailTabs = 'ExtensionDetailTabs';
990
- const ExtensionDetailTabSelected = 'ExtensionDetailTabSelected';
991
- const Feature = 'Feature';
992
- const FeatureContent = 'FeatureContent';
993
- const Features$1 = 'Features';
994
- const FeaturesList = 'FeaturesList';
995
- const FeatureWebView = 'FeatureWebView';
996
- const Large$1 = 'Large';
997
- const Sash = 'Sash';
998
- const SashVertical = 'SashVertical';
999
- const MoreInfo = 'MoreInfo';
1000
- const MoreInfoEntry = 'MoreInfoEntry';
1001
- const MoreInfoEntryKey = 'MoreInfoEntryKey';
1002
- const MoreInfoEntryOdd = 'MoreInfoEntryOdd';
1003
- const MoreInfoEntryValue = 'MoreInfoEntryValue';
1004
- const Normal$1 = 'Normal';
1005
- const Resource = 'Resource';
1006
- const Resources = 'Resources';
1007
- const Small$1 = 'Small';
1008
- const Table$1 = 'Table';
1009
- const TableCell = 'TableCell';
1010
- const TableHeading = 'TableHeading';
1011
- const Viewlet = 'Viewlet';
1012
-
1013
- const BYTE_UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
1014
- const BIBYTE_UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
1015
- const BIT_UNITS = ['b', 'kbit', 'Mbit', 'Gbit', 'Tbit', 'Pbit', 'Ebit', 'Zbit', 'Ybit'];
1016
- const BIBIT_UNITS = ['b', 'kibit', 'Mibit', 'Gibit', 'Tibit', 'Pibit', 'Eibit', 'Zibit', 'Yibit'];
1017
-
1018
- /*
1019
- Formats the given number using `Number#toLocaleString`.
1020
- - If locale is a string, the value is expected to be a locale-key (for example: `de`).
1021
- - If locale is true, the system default locale is used for translation.
1022
- - If no value for locale is specified, the number is returned unmodified.
1023
- */
1024
- const toLocaleString = (number, locale, options) => {
1025
- let result = number;
1026
- if (typeof locale === 'string' || Array.isArray(locale)) {
1027
- result = number.toLocaleString(locale, options);
1028
- } else if (locale === true || options !== undefined) {
1029
- result = number.toLocaleString(undefined, options);
1030
- }
1031
- return result;
1032
- };
1033
- function prettyBytes(number, options) {
1034
- if (!Number.isFinite(number)) {
1035
- throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
1036
- }
1037
- options = {
1038
- bits: false,
1039
- binary: false,
1040
- space: true,
1041
- ...options
1042
- };
1043
- const UNITS = options.bits ? options.binary ? BIBIT_UNITS : BIT_UNITS : options.binary ? BIBYTE_UNITS : BYTE_UNITS;
1044
- const separator = options.space ? ' ' : '';
1045
- if (options.signed && number === 0) {
1046
- return ` 0${separator}${UNITS[0]}`;
1047
- }
1048
- const isNegative = number < 0;
1049
- const prefix = isNegative ? '-' : options.signed ? '+' : '';
1050
- if (isNegative) {
1051
- number = -number;
1052
- }
1053
- let localeOptions;
1054
- if (options.minimumFractionDigits !== undefined) {
1055
- localeOptions = {
1056
- minimumFractionDigits: options.minimumFractionDigits
1057
- };
1058
- }
1059
- if (options.maximumFractionDigits !== undefined) {
1060
- localeOptions = {
1061
- maximumFractionDigits: options.maximumFractionDigits,
1062
- ...localeOptions
1063
- };
1064
- }
1065
- if (number < 1) {
1066
- const numberString = toLocaleString(number, options.locale, localeOptions);
1067
- return prefix + numberString + separator + UNITS[0];
1068
- }
1069
- const exponent = Math.min(Math.floor(options.binary ? Math.log(number) / Math.log(1024) : Math.log10(number) / 3), UNITS.length - 1);
1070
- number /= (options.binary ? 1024 : 1000) ** exponent;
1071
- if (!localeOptions) {
1072
- number = number.toPrecision(3);
1073
- }
1074
- const numberString = toLocaleString(Number(number), options.locale, localeOptions);
1075
- const unit = UNITS[exponent];
1076
- return prefix + numberString + separator + unit;
1077
- }
1078
-
1079
- const getDisplaySize = size => {
1080
- return prettyBytes(size, {
1081
- maximumFractionDigits: 1
1082
- });
1083
- };
1084
-
1085
981
  const emptyObject = {};
1086
982
  const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1087
983
  const i18nString = (key, placeholders = emptyObject) => {
@@ -1172,1331 +1068,1496 @@ const notImplemented = () => {
1172
1068
  return i18nString(NotImplemented);
1173
1069
  };
1174
1070
 
1175
- const A = 53;
1176
- const Button = 1;
1177
- const Div = 4;
1178
- const H1 = 5;
1179
- const H2 = 22;
1180
- const Img = 17;
1181
- const Pre = 51;
1182
- const Table = 9;
1183
- const TBody = 10;
1184
- const Td = 11;
1185
- const Th = 13;
1186
- const THead = 14;
1187
- const Tr = 15;
1071
+ const None = 0;
1188
1072
 
1189
- const mergeClassNames = (...classNames) => {
1190
- return classNames.filter(Boolean).join(' ');
1191
- };
1192
- const Text$1 = 12;
1193
- const text = data => {
1194
- return {
1195
- type: Text$1,
1196
- text: data,
1197
- childCount: 0
1198
- };
1073
+ const getCopyMenuEntry = () => ({
1074
+ id: 'copy',
1075
+ label: copy(),
1076
+ flags: None,
1077
+ command: 'ClipBoard.execCopy'
1078
+ });
1079
+
1080
+ const getImageMenuEntries = props => {
1081
+ if (!props.isImage) {
1082
+ return [];
1083
+ }
1084
+ return [{
1085
+ id: 'openImageInNewTab',
1086
+ label: openImageInNewTab(),
1087
+ flags: None,
1088
+ command: 'Open.openUrl',
1089
+ args: [props.url || '']
1090
+ }, {
1091
+ id: 'saveImageAs',
1092
+ label: saveImageAs(),
1093
+ flags: None,
1094
+ command: 'SaveFileAs.saveFileAs',
1095
+ args: ['image.png', props.url || '']
1096
+ }];
1199
1097
  };
1200
1098
 
1201
- const getChangelogVirtualDom = () => {
1202
- const notImplemented$1 = notImplemented();
1203
- // TODO set tabpanel role
1099
+ const getLinkMenuEntries = props => {
1100
+ if (!props.isLink) {
1101
+ return [];
1102
+ }
1204
1103
  return [{
1205
- type: Div,
1206
- className: Changelog$1,
1207
- childCount: 1
1208
- }, text(notImplemented$1)];
1104
+ id: 'openInNewTab',
1105
+ label: openInNewTab(),
1106
+ flags: None,
1107
+ command: 'Open.openUrl',
1108
+ args: [props.url || '']
1109
+ }];
1209
1110
  };
1210
1111
 
1211
- const TabList = 'tablist';
1212
- const Tab = 'tab';
1213
- const Panel = 'panel';
1112
+ const getMenuEntries = props => [...getLinkMenuEntries(props), ...getImageMenuEntries(props), getCopyMenuEntry()];
1214
1113
 
1215
- const getAdditionalDetailsEntryVirtualDom = (heading, items, renderer) => {
1216
- return [{
1217
- type: Div,
1218
- className: AdditionalDetailsEntry,
1219
- childCount: 2
1220
- }, {
1221
- type: Div,
1222
- className: AdditionalDetailsTitle,
1223
- childCount: 1
1224
- }, text(heading), ...renderer(items)];
1114
+ const handleClickDisable = async state => {
1115
+ return state;
1225
1116
  };
1226
1117
 
1227
- const getCategoryVirtualDom = category => {
1118
+ const selectFeature = async (state, name) => {
1119
+ if (!name) {
1120
+ return state;
1121
+ }
1228
1122
  const {
1229
- label
1230
- } = category;
1231
- return [{
1232
- type: Div,
1233
- className: Category,
1234
- childCount: 1
1235
- }, text(label)];
1123
+ features
1124
+ } = state;
1125
+ const newFeatures = features.map(feature => {
1126
+ if (feature.id === name) {
1127
+ return {
1128
+ ...feature,
1129
+ selected: true
1130
+ };
1131
+ }
1132
+ return {
1133
+ ...feature,
1134
+ selected: false
1135
+ };
1136
+ });
1137
+ return {
1138
+ ...state,
1139
+ selectedFeature: name,
1140
+ features: newFeatures
1141
+ };
1236
1142
  };
1237
1143
 
1238
- const getCategoriesDom = categories => {
1239
- return [{
1240
- type: Div,
1241
- className: Categories,
1242
- childCount: categories.length
1243
- }, ...categories.flatMap(getCategoryVirtualDom)];
1144
+ const handleClickFeatures = async (state, name) => {
1145
+ return selectFeature(state, name);
1244
1146
  };
1245
1147
 
1246
- const parentNode$1 = {
1247
- type: Div,
1248
- className: MoreInfoEntryKey,
1249
- childCount: 1
1148
+ const rpcs = Object.create(null);
1149
+ const set$3 = (id, rpc) => {
1150
+ rpcs[id] = rpc;
1250
1151
  };
1251
- const getMoreInfoEntryKeyVirtualDom = item => {
1252
- const {
1253
- key
1254
- } = item;
1255
- return [parentNode$1, text(key)];
1152
+ const get = id => {
1153
+ return rpcs[id];
1154
+ };
1155
+ const RendererWorker$1 = 1;
1156
+ const invoke$2 = (method, ...params) => {
1157
+ const rpc = get(RendererWorker$1);
1158
+ // @ts-ignore
1159
+ return rpc.invoke(method, ...params);
1160
+ };
1161
+ const set$2 = rpc => {
1162
+ set$3(RendererWorker$1, rpc);
1163
+ };
1164
+ const RendererWorker = {
1165
+ __proto__: null,
1166
+ invoke: invoke$2,
1167
+ set: set$2
1256
1168
  };
1257
1169
 
1258
- const getTag = (onClick, code) => {
1259
- if (onClick) {
1260
- return A;
1261
- }
1262
- if (code) {
1263
- // TODO use code tag
1264
- return Div;
1265
- }
1266
- return Div;
1267
- };
1268
- const getClassName = (onClick, code) => {
1269
- if (onClick) {
1270
- return MoreInfoEntryValue + ' Link';
1271
- }
1272
- if (code) {
1273
- return MoreInfoEntryValue + ' Code';
1274
- }
1275
- return MoreInfoEntryValue;
1276
- };
1277
- const getMoreInfoEntryValueVirtualDom = item => {
1170
+ const {
1171
+ invoke,
1172
+ set
1173
+ } = RendererWorker;
1174
+
1175
+ const handleClickSize = async state => {
1278
1176
  const {
1279
- value,
1280
- onClick,
1281
- code
1282
- } = item;
1283
- const type = getTag(onClick, code);
1284
- const className = getClassName(onClick, code);
1285
- return [{
1286
- type: type,
1287
- className,
1288
- childCount: 1,
1289
- onClick
1290
- }, text(value)];
1177
+ uri
1178
+ } = state.extension;
1179
+ await invoke('OpenNativeFolder.openNativeFolder', uri);
1180
+ return state;
1291
1181
  };
1292
1182
 
1293
- const parentNodeEven = {
1294
- type: Div,
1295
- className: MoreInfoEntry,
1296
- childCount: 2
1183
+ const handleClickUninstall = async state => {
1184
+ return state;
1297
1185
  };
1298
- const parentNodeOdd = {
1299
- type: Div,
1300
- className: mergeClassNames(MoreInfoEntry, MoreInfoEntryOdd),
1301
- childCount: 2
1186
+
1187
+ const extensionDefaultIcon = assetDir => {
1188
+ return `${assetDir}/icons/extensionDefaultIcon.png`;
1302
1189
  };
1303
- const getMoreInfoEntryVirtualDom = item => {
1304
- const {
1305
- odd
1306
- } = item;
1307
- const node = odd ? parentNodeOdd : parentNodeEven;
1308
- return [node, ...getMoreInfoEntryKeyVirtualDom(item), ...getMoreInfoEntryValueVirtualDom(item)];
1190
+ const extensionLanguageBasics = assetDir => {
1191
+ return `${assetDir}/icons/language-icon.svg`;
1192
+ };
1193
+ const extensionTheme = assetDir => {
1194
+ return `${assetDir}/icons/theme-icon.png`;
1309
1195
  };
1310
1196
 
1311
- const getMoreInfoVirtualDom = items => {
1312
- return [{
1313
- type: Div,
1314
- className: MoreInfo,
1315
- childCount: items.length
1316
- }, ...items.flatMap(getMoreInfoEntryVirtualDom)];
1197
+ const handleIconError = state => {
1198
+ const {
1199
+ iconSrc,
1200
+ assetDir
1201
+ } = state;
1202
+ if (iconSrc === extensionDefaultIcon(assetDir)) {
1203
+ return state;
1204
+ }
1205
+ return {
1206
+ ...state,
1207
+ iconSrc: extensionDefaultIcon(assetDir)
1208
+ };
1317
1209
  };
1318
1210
 
1319
- const resourceNode = {
1320
- // TODO use link with url
1321
- type: Div,
1322
- className: Resource,
1323
- childCount: 1
1211
+ const Changelog$1 = 'Changelog';
1212
+ const Commands = 'Commands';
1213
+ const Details = 'Details';
1214
+ const Features$1 = 'Features';
1215
+ const JsonValidation = 'JsonValidation';
1216
+ const ProgrammingLanguages = 'ProgrammingLanguages';
1217
+ const Settings = 'Settings';
1218
+ const WebViews = 'WebViews';
1219
+ const Theme = 'Theme';
1220
+
1221
+ const selectTabChangelog = async state => {
1222
+ return {
1223
+ ...state,
1224
+ selectedTab: Changelog$1
1225
+ };
1324
1226
  };
1325
- const getResourceVirtualDom = resource => {
1326
- const {
1327
- label
1328
- } = resource;
1329
- return [resourceNode, text(label)];
1227
+
1228
+ const selectTabDefault = async state => {
1229
+ return state;
1330
1230
  };
1331
1231
 
1332
- const getResourcesVirtualDom = resources => {
1333
- return [{
1334
- type: Div,
1335
- className: Resources,
1336
- childCount: resources.length
1337
- }, ...resources.flatMap(getResourceVirtualDom)];
1232
+ const selectTabDetails = async state => {
1233
+ // TODO load readmo markdown here
1234
+ return {
1235
+ ...state,
1236
+ selectedTab: Details
1237
+ };
1338
1238
  };
1339
1239
 
1340
- const getAdditionalDetailsVirtualDom = (showAdditionalDetails, firstHeading, entries, secondHeading, secondEntries, thirdHeading, categories, fourthHeading, resources) => {
1341
- if (!showAdditionalDetails) {
1342
- return [];
1240
+ const getThemeItemMarkdown = (heading, items) => {
1241
+ let markdown = '';
1242
+ if (items.length > 0) {
1243
+ markdown += `### ${heading}`;
1244
+ markdown += '\n\n';
1245
+ for (const item of items) {
1246
+ markdown += `- ${item.label}`;
1247
+ markdown += '\n';
1248
+ }
1343
1249
  }
1344
- return [{
1345
- type: Div,
1346
- className: Aside,
1347
- childCount: 1
1348
- }, {
1349
- type: Div,
1350
- className: AdditionalDetails,
1351
- tabIndex: 0,
1352
- childCount: 4
1353
- }, ...getAdditionalDetailsEntryVirtualDom(firstHeading, entries, getMoreInfoVirtualDom), ...getAdditionalDetailsEntryVirtualDom(secondHeading, secondEntries, getMoreInfoVirtualDom), ...getAdditionalDetailsEntryVirtualDom(thirdHeading, categories, getCategoriesDom), ...getAdditionalDetailsEntryVirtualDom(fourthHeading, resources, getResourcesVirtualDom)];
1250
+ return markdown;
1354
1251
  };
1355
1252
 
1356
- const HandleClickDisable = 'handleClickDisable';
1357
- const HandleClickSize = 'handleClickSize';
1358
- const HandleClickUninstall = 'handleClickUninstall';
1359
- const HandleFeaturesClick = 'handleFeaturesClick';
1360
- const HandleIconError = 'handleIconError';
1361
- const HandleReadmeContextMenu = 'handleReadmeContextMenu';
1362
- const HandleTabsClick = 'handleTabsClick';
1253
+ const getColorThemeMarkdown = themes => {
1254
+ const heading = 'Color Themes';
1255
+ return getThemeItemMarkdown(heading, themes);
1256
+ };
1257
+ const getIconThemeMarkdown = iconThemes => {
1258
+ const heading = 'File Icon Themes';
1259
+ return getThemeItemMarkdown(heading, iconThemes);
1260
+ };
1261
+ const getProductIconThemeMarkdown = iconThemes => {
1262
+ const heading = 'Product Icon Themes';
1263
+ return getThemeItemMarkdown(heading, iconThemes);
1264
+ };
1265
+ const getThemeMarkdown = (themes, iconThemes, productIconThemes) => {
1266
+ let markdown = '';
1267
+ markdown += getColorThemeMarkdown(themes);
1268
+ markdown += getIconThemeMarkdown(iconThemes);
1269
+ markdown += getProductIconThemeMarkdown(productIconThemes);
1270
+ return markdown;
1271
+ };
1363
1272
 
1364
- const getInstallationEntries = (displaySize, extensionId, extensionVersion) => {
1365
- const entries = [{
1366
- key: 'Identifier',
1367
- value: extensionId,
1368
- odd: true,
1369
- code: true
1370
- }, {
1371
- key: 'Version',
1372
- value: extensionVersion,
1373
- code: true
1374
- }, {
1375
- key: 'Last Updated',
1376
- value: 'n/a',
1377
- odd: true
1378
- }, {
1379
- key: 'Size',
1380
- value: displaySize,
1381
- onClick: HandleClickSize
1382
- }];
1383
- return entries;
1273
+ const renderMarkdown = async (markdown, options = {}) => {
1274
+ // @ts-ignore todo
1275
+ const html = await invoke('Markdown.renderMarkdown', markdown, options);
1276
+ return html;
1384
1277
  };
1385
1278
 
1386
- const RendererWorker = 1;
1279
+ const selectTab$1 = async state => {
1280
+ const {
1281
+ extension,
1282
+ baseUrl
1283
+ } = state;
1284
+ const {
1285
+ colorThemes,
1286
+ iconThemes,
1287
+ productIconThemes
1288
+ } = extension;
1289
+ // TODO do this depending on featured tab content
1290
+ const markdown = getThemeMarkdown(colorThemes || [], iconThemes || [], productIconThemes || []);
1291
+ const rendered = await renderMarkdown(markdown, {
1292
+ baseUrl
1293
+ });
1294
+ return {
1295
+ ...state,
1296
+ selectedTab: Features$1,
1297
+ selectedFeatureMarkdownDom: rendered
1298
+ };
1299
+ };
1387
1300
 
1388
- const rpcs = Object.create(null);
1389
- const set = (id, rpc) => {
1390
- rpcs[id] = rpc;
1301
+ const getSelectTabHandler = selectedTab => {
1302
+ switch (selectedTab) {
1303
+ case Details:
1304
+ return selectTabDetails;
1305
+ case Features$1:
1306
+ return selectTab$1;
1307
+ case Changelog$1:
1308
+ return selectTabChangelog;
1309
+ default:
1310
+ return selectTabDefault;
1311
+ }
1391
1312
  };
1392
- const get = id => {
1393
- return rpcs[id];
1313
+
1314
+ const selectTab = (state, name) => {
1315
+ const fn = getSelectTabHandler(name);
1316
+ return fn(state);
1394
1317
  };
1395
1318
 
1396
- const invoke = (method, ...params) => {
1397
- const rpc = get(RendererWorker);
1398
- return rpc.invoke(method, ...params);
1319
+ const handleTabsClick = (state, name) => {
1320
+ return selectTab(state, name);
1399
1321
  };
1400
1322
 
1401
- const getMarkdownVirtualDom = async html => {
1402
- string(html);
1403
- const dom = await invoke('Markdown.getVirtualDom', html);
1404
- return dom;
1323
+ const isLanguageBasicsExtension = extension => {
1324
+ return extension.name && extension.name.startsWith('Language Basics');
1405
1325
  };
1406
1326
 
1407
- const getMarketplaceEntries = () => {
1408
- return [{
1409
- key: 'Published',
1410
- value: 'n/a',
1411
- odd: true
1412
- }, {
1413
- key: 'Last Released',
1414
- value: 'n/a'
1415
- }];
1327
+ const isThemeExtension = extension => {
1328
+ return extension.name && extension.name.endsWith(' Theme');
1416
1329
  };
1417
1330
 
1418
- const getDetailsVirtualDom = async (sanitizedReadmeHtml, displaySize, extensionId, extensionVersion, width) => {
1419
- const firstHeading = 'Installation';
1420
- const entries = getInstallationEntries(displaySize, extensionId, extensionVersion);
1421
- const secondHeading = 'Marketplace';
1422
- const secondEntries = getMarketplaceEntries();
1423
- const thirdHeading = 'Categories';
1424
- const categories = [{
1425
- id: 'themes',
1426
- label: 'Themes'
1427
- }];
1428
- const fourthHeading = 'Resources';
1429
- const resources = [{
1430
- label: 'Marketplace',
1431
- url: '#'
1432
- }, {
1433
- label: 'Issues',
1434
- url: '#'
1435
- }, {
1436
- label: 'Repository',
1437
- url: '#'
1438
- }, {
1439
- label: 'License',
1440
- url: '#'
1441
- }];
1442
- const showAdditionalDetailsBreakpoint = 600;
1443
- const showAdditionalDetails = width > showAdditionalDetailsBreakpoint;
1444
- const childCount = showAdditionalDetails ? 2 : 1;
1445
- const dom = [{
1446
- type: Div,
1447
- className: ExtensionDetailPanel,
1448
- childCount: childCount,
1449
- role: Panel
1450
- }, ...(await getMarkdownVirtualDom(sanitizedReadmeHtml)), ...getAdditionalDetailsVirtualDom(showAdditionalDetails, firstHeading, entries, secondHeading, secondEntries, thirdHeading, categories, fourthHeading, resources)];
1451
- return dom;
1452
- };
1453
-
1454
- const Text = 1;
1455
- const Code = 2;
1331
+ const Web = 1;
1332
+ const Electron = 2;
1333
+ const Remote = 3;
1456
1334
 
1457
- const getCommandTableEntry = command => {
1458
- // TODO watch out for command being null/undefined/number/string/array
1459
- const {
1460
- id,
1461
- label
1462
- } = command;
1463
- return [{
1464
- type: Code,
1465
- value: id
1466
- }, {
1467
- type: Text,
1468
- value: label
1469
- }];
1335
+ const getIcon = (extension, platform, assetDir) => {
1336
+ if (!extension) {
1337
+ return extensionDefaultIcon(assetDir);
1338
+ }
1339
+ if (!extension.path || !extension.icon) {
1340
+ if (isLanguageBasicsExtension(extension)) {
1341
+ return extensionLanguageBasics(assetDir);
1342
+ }
1343
+ if (isThemeExtension(extension)) {
1344
+ return extensionTheme(assetDir);
1345
+ }
1346
+ return extensionDefaultIcon(assetDir);
1347
+ }
1348
+ if (platform === Remote || platform === Electron) {
1349
+ if (extension.builtin) {
1350
+ return `${assetDir}/extensions/${extension.id}/${extension.icon}`;
1351
+ }
1352
+ return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
1353
+ }
1354
+ return '';
1470
1355
  };
1471
1356
 
1472
- const getCommandTableEntries = extension => {
1473
- const commands = extension.commands || [];
1474
- const rows = commands.map(getCommandTableEntry);
1475
- const id$1 = id();
1476
- const label$1 = label();
1477
- return {
1478
- headings: [id$1, label$1],
1479
- rows
1480
- };
1357
+ const getDescription = extension => {
1358
+ if (!extension || !extension.description) {
1359
+ return 'n/a';
1360
+ }
1361
+ return extension.description;
1481
1362
  };
1482
1363
 
1483
- const getFeatureContentHeadingVirtualDom = heading => {
1484
- return [{
1485
- type: H1,
1486
- childCount: 1
1487
- }, text(heading)];
1364
+ const getName = extension => {
1365
+ if (extension && extension.name) {
1366
+ return extension.name;
1367
+ }
1368
+ if (extension && extension.id) {
1369
+ return extension.id;
1370
+ }
1371
+ return 'n/a';
1488
1372
  };
1489
1373
 
1490
- const getTableHeadingVirtualDom = heading => {
1491
- return [{
1492
- type: Th,
1493
- className: TableHeading,
1494
- childCount: 1
1495
- }, text(heading)];
1374
+ const getAllExtensions = async platform => {
1375
+ if (platform === Web) {
1376
+ return [];
1377
+ }
1378
+ return invoke('ExtensionManagement.getAllExtensions');
1379
+ };
1380
+ const getExtension$1 = async (id, platform) => {
1381
+ // TODO only ask one extension from renderer worker instead of all
1382
+ const allExtensions = await getAllExtensions(platform);
1383
+ for (const extension of allExtensions) {
1384
+ if (extension.id === id) {
1385
+ return extension;
1386
+ }
1387
+ }
1388
+ return undefined;
1496
1389
  };
1497
1390
 
1498
- const getCellCodeVirtualDom = value => {
1499
- return [{
1500
- type: Td,
1501
- className: TableCell,
1502
- childCount: 1
1503
- }, {
1504
- type: Div,
1505
- // TODO use code tag
1506
- childCount: 1
1507
- }, text(value)];
1391
+ const getExtensionNew = async id => {
1392
+ // @ts-ignore todo
1393
+ return invoke('ExtensionManagement.getExtension', id);
1394
+ };
1395
+ const getExtension = async (id, platform) => {
1396
+ try {
1397
+ return await getExtensionNew(id);
1398
+ } catch {
1399
+ return getExtension$1(id, platform);
1400
+ }
1508
1401
  };
1509
1402
 
1510
- const getCellTextVirtualDom = value => {
1511
- return [{
1512
- type: Td,
1513
- className: TableCell,
1514
- childCount: 1
1515
- }, text(value)];
1403
+ const getRemoteSrc = uri => {
1404
+ const src = `/remote${uri}`;
1405
+ return src;
1516
1406
  };
1517
1407
 
1518
- const getCellRenderer = type => {
1519
- switch (type) {
1520
- case Code:
1521
- return getCellCodeVirtualDom;
1522
- case Text:
1523
- return getCellTextVirtualDom;
1408
+ const getBaseUrl = (extensionPath, platform) => {
1409
+ switch (platform) {
1410
+ case Remote:
1411
+ case Electron:
1412
+ return getRemoteSrc(extensionPath + '/');
1524
1413
  default:
1525
- throw new Error(`unexpected cell type ${type}`);
1414
+ return extensionPath;
1526
1415
  }
1527
1416
  };
1528
1417
 
1529
- const getCellVirtualDom = entry => {
1530
- const {
1531
- value,
1532
- type
1533
- } = entry;
1534
- const fn = getCellRenderer(type);
1535
- return fn(value);
1418
+ const getExtensionIdFromUri = uri => {
1419
+ const id = uri.slice('extension-detail://'.length);
1420
+ return id;
1536
1421
  };
1537
1422
 
1538
- const getTableRowVirtualDom = entries => {
1539
- return [{
1540
- type: Tr,
1541
- childCount: entries.length
1542
- }, ...entries.flatMap(getCellVirtualDom)];
1423
+ const hasThemes = extension => {
1424
+ return extension && (extension.colorThemes || extension.iconThemes || extension.productIconThemes);
1543
1425
  };
1544
-
1545
- const getTableVirtualDom = tableInfo => {
1546
- const {
1547
- headings,
1548
- rows
1549
- } = tableInfo;
1550
- return [{
1551
- type: Table,
1552
- className: Table$1,
1553
- childCount: 2
1554
- }, {
1555
- type: THead,
1556
- childCount: 1
1557
- }, {
1558
- type: Tr,
1559
- childCount: headings.length
1560
- }, ...headings.flatMap(getTableHeadingVirtualDom), {
1561
- type: TBody,
1562
- childCount: rows.length
1563
- }, ...rows.flatMap(getTableRowVirtualDom)];
1426
+ const hasCommands = extension => {
1427
+ return extension && extension.commands;
1564
1428
  };
1565
-
1566
- // TODO have typed view-model
1567
- const getFeatureCommandsVirtualDom = extension => {
1568
- const heading = commands();
1569
- const tableInfo = getCommandTableEntries(extension);
1570
- return [{
1571
- type: Div,
1572
- className: FeatureContent,
1573
- childCount: 2
1574
- }, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
1429
+ const hasJsonValidation = extension => {
1430
+ return extension && extension.jsonValidation;
1575
1431
  };
1576
-
1577
- const getJsonValidationTableEntry = validation => {
1578
- const {
1579
- fileMatch,
1580
- schema
1581
- } = validation;
1432
+ const hasProgrammingLanguages = extension => {
1433
+ return extension && extension.programmingLanguages;
1434
+ };
1435
+ const hasSettings = extension => {
1436
+ return extension && extension.settings;
1437
+ };
1438
+ const hasWebViews = extension => {
1439
+ return extension && extension.webViews;
1440
+ };
1441
+ const ifElseFeature = (id, label, isEnabled, selectedFeature, extension) => {
1442
+ if (!isEnabled(extension)) {
1443
+ return [];
1444
+ }
1582
1445
  return [{
1583
- type: Code,
1584
- value: fileMatch
1585
- }, {
1586
- type: Code,
1587
- value: schema
1446
+ id,
1447
+ label,
1448
+ selected: selectedFeature === id
1588
1449
  }];
1589
1450
  };
1590
-
1591
- const getJsonValidationTableEntries = extension => {
1592
- const validations = extension.jsonValidation || [];
1593
- const rows = validations.map(getJsonValidationTableEntry);
1594
- return {
1595
- headings: [fileMatch(), schema()],
1596
- rows
1597
- };
1451
+ const getFeatures = (selectedFeature, extension) => {
1452
+ if (!selectedFeature) {
1453
+ selectedFeature = Theme;
1454
+ }
1455
+ const textTheme = theme();
1456
+ const textCommands = commands();
1457
+ const textJsonValidation = jsonValidation();
1458
+ const programmingLanguages$1 = programmingLanguages();
1459
+ const settings$1 = settings();
1460
+ const webViews$1 = webViews();
1461
+ const features = [...ifElseFeature(Theme, textTheme, hasThemes, selectedFeature, extension), ...ifElseFeature(Commands, textCommands, hasCommands, selectedFeature, extension), ...ifElseFeature(JsonValidation, textJsonValidation, hasJsonValidation, selectedFeature, extension), ...ifElseFeature(ProgrammingLanguages, programmingLanguages$1, hasProgrammingLanguages, selectedFeature, extension), ...ifElseFeature(Settings, settings$1, hasSettings, selectedFeature, extension), ...ifElseFeature(WebViews, webViews$1, hasWebViews, selectedFeature, extension)];
1462
+ return features;
1598
1463
  };
1599
1464
 
1600
- const parentNode = {
1601
- type: Div,
1602
- className: FeatureContent,
1603
- childCount: 2
1604
- };
1605
- const getFeatureJsonValidationVirtualDom = extension => {
1606
- const heading = jsonValidation();
1607
- const tableInfo = getJsonValidationTableEntries(extension);
1608
- return [parentNode, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
1465
+ const getFolderSize = async uri => {
1466
+ if (!uri) {
1467
+ throw new VError(`uri is required`);
1468
+ }
1469
+ try {
1470
+ // @ts-ignore todo
1471
+ return await invoke('FileSystem.getFolderSize', uri);
1472
+ } catch {
1473
+ return 0;
1474
+ }
1609
1475
  };
1610
1476
 
1611
- const getFeatureNotImplementedVirtualDom = () => {
1612
- const heading = 'Not implemented';
1613
- return [{
1614
- type: Div,
1615
- className: FeatureContent,
1616
- childCount: 1
1617
- }, {
1618
- type: H1,
1619
- childCount: 1
1620
- }, text(heading)];
1621
- };
1477
+ const Small$1 = 1;
1478
+ const Normal$1 = 2;
1479
+ const Large$1 = 3;
1622
1480
 
1623
- const getFeatureProgrammingLanguagesVirtualDom = () => {
1624
- const heading = programmingLanguages();
1625
- // TODO
1626
- return [{
1627
- type: Div,
1628
- className: FeatureContent,
1629
- childCount: 1
1630
- }, ...getFeatureContentHeadingVirtualDom(heading)];
1481
+ const getViewletSize = width => {
1482
+ if (width < 180) {
1483
+ return Small$1;
1484
+ }
1485
+ if (width < 768) {
1486
+ return Normal$1;
1487
+ }
1488
+ return Large$1;
1631
1489
  };
1632
1490
 
1633
- const getSettingsTableEntry = setting => {
1634
- const {
1635
- id,
1636
- label
1637
- } = setting;
1638
- // TODO watch out for null/undefined/number/string/array
1639
- return [{
1640
- type: Text,
1641
- value: id
1642
- }, {
1643
- type: Text,
1644
- value: label
1645
- }];
1491
+ const readFile = async uri => {
1492
+ if (uri.startsWith('http://') || uri.startsWith('https://')) {
1493
+ const response = await fetch(uri);
1494
+ if (!response.ok) {
1495
+ throw new Error(response.statusText);
1496
+ }
1497
+ const result = await response.text();
1498
+ return result;
1499
+ }
1500
+ return invoke('FileSystem.readFile', uri);
1646
1501
  };
1647
1502
 
1648
- const getSettingsTableEntries = extension => {
1649
- const settings = extension.settings || [];
1650
- const rows = settings.map(getSettingsTableEntry);
1651
- const textId = id();
1652
- const textLabel = label();
1653
- return {
1654
- headings: [textId, textLabel],
1655
- rows
1656
- };
1503
+ const ENOENT = 'ENOENT';
1504
+
1505
+ const isEnoentError = error => {
1506
+ return error && error.code === ENOENT;
1657
1507
  };
1658
1508
 
1659
- const getFeatureSettingsVirtualDom = extension => {
1660
- const heading = settings();
1661
- const tableInfo = getSettingsTableEntries(extension);
1662
- return [{
1663
- type: Div,
1664
- className: FeatureContent,
1665
- childCount: 2
1666
- }, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
1509
+ const join = (pathSeparator, ...parts) => {
1510
+ return parts.join(pathSeparator);
1667
1511
  };
1668
1512
 
1669
- const getVirtualDomChildCount = markdownDom => {
1670
- const max = markdownDom.length - 1;
1671
- let stack = [];
1672
- for (let i = max; i >= 0; i--) {
1673
- const element = markdownDom[i];
1674
- if (element.childCount > 0) {
1675
- stack = stack.slice(element.childCount);
1513
+ const loadReadmeContent = async path => {
1514
+ try {
1515
+ const readmeUrl = join('/', path, 'README.md');
1516
+ const readmeContent = await readFile(readmeUrl);
1517
+ return readmeContent;
1518
+ } catch (error) {
1519
+ if (isEnoentError(error)) {
1520
+ return '';
1676
1521
  }
1677
- stack.unshift(element);
1522
+ console.error(new VError(error, 'Failed to load Readme content'));
1523
+ return `${error}`;
1678
1524
  }
1679
- return stack.length;
1680
1525
  };
1681
1526
 
1682
- const getFeatureThemesVirtualDom = async themesHtml => {
1683
- const markdownDom = await getMarkdownVirtualDom(themesHtml);
1684
- const childCount = getVirtualDomChildCount(markdownDom);
1685
- const heading = theme();
1686
- return [{
1687
- type: Div,
1688
- className: FeatureContent,
1689
- childCount: 2
1690
- }, ...getFeatureContentHeadingVirtualDom(heading), {
1691
- type: Div,
1692
- className: DefaultMarkdown,
1693
- childCount
1694
- }, ...markdownDom];
1527
+ const getSavedSelectedFeature = savedState => {
1528
+ if (savedState && typeof savedState === 'object' && 'selectedFeature' in savedState && typeof savedState.selectedFeature === 'string') {
1529
+ return savedState.selectedFeature;
1530
+ }
1531
+ return Details;
1695
1532
  };
1696
1533
 
1697
- const toWebView = rawWebView => {
1698
- const {
1699
- id,
1700
- selector,
1701
- contentSecurityPolicy,
1702
- elements
1703
- } = rawWebView;
1704
- return {
1705
- id,
1706
- selectorString: JSON.stringify(selector),
1707
- contentSecurityPolicyString: JSON.stringify(contentSecurityPolicy),
1708
- elementsString: JSON.stringify(elements, null, 2)
1709
- };
1534
+ const getSavedSelectedTab = savedState => {
1535
+ if (savedState && typeof savedState === 'object' && 'selectedTab' in savedState && typeof savedState.selectedTab === 'string') {
1536
+ return savedState.selectedTab;
1537
+ }
1538
+ return Details;
1710
1539
  };
1711
1540
 
1712
- const getWebViews = extension => {
1713
- const rawWebViews = extension.webViews || [];
1714
- return rawWebViews.map(toWebView);
1541
+ const restoreState = savedState => {
1542
+ const selectedTab = getSavedSelectedTab(savedState);
1543
+ const selectedFeature = getSavedSelectedFeature(savedState);
1544
+ return {
1545
+ selectedFeature,
1546
+ selectedTab
1547
+ };
1715
1548
  };
1716
1549
 
1717
- const heading = {
1718
- type: H2,
1719
- className: DefinitionListItemHeading,
1720
- childCount: 1
1721
- };
1722
- const pre = {
1723
- type: Pre,
1724
- className: DefinitionListItemValue,
1725
- childCount: 1
1726
- };
1727
- const item = {
1728
- type: Div,
1729
- className: DefinitionListItem,
1730
- childCount: 2
1731
- };
1732
- const getWebViewVirtualDom = webView => {
1550
+ const loadContent = async (state, platform, savedState) => {
1733
1551
  const {
1734
- id: id$1,
1735
- selectorString,
1736
- contentSecurityPolicyString,
1737
- elementsString
1738
- } = webView;
1739
- const textId = id();
1740
- const textSelector = selector();
1741
- const textContentSecurityPolicy = contentSecurityPolicy();
1742
- const textElements = elements();
1743
- return [{
1744
- type: Div,
1745
- className: FeatureWebView,
1746
- childCount: 5
1747
- }, item, heading, text(textId), pre, text(id$1), item, heading, text(textSelector), pre, text(selectorString), item, heading, text(textContentSecurityPolicy), pre, text(contentSecurityPolicyString), item, heading, text(textElements), pre, text(elementsString)];
1552
+ uri,
1553
+ width,
1554
+ assetDir
1555
+ } = state;
1556
+ const id = getExtensionIdFromUri(uri);
1557
+ const extension = await getExtension(id, platform);
1558
+ const readmeContent = await loadReadmeContent(extension.path);
1559
+ const baseUrl = getBaseUrl(extension.path, platform);
1560
+ const readmeHtml = await renderMarkdown(readmeContent, {
1561
+ baseUrl
1562
+ });
1563
+ const sanitizedReadmeHtml = readmeHtml;
1564
+ const normalizedReadmeHtml = sanitizedReadmeHtml;
1565
+ const iconSrc = getIcon(extension, platform, assetDir);
1566
+ const description = getDescription(extension);
1567
+ const name = getName(extension);
1568
+ const size = getViewletSize(width);
1569
+ const {
1570
+ selectedFeature,
1571
+ selectedTab
1572
+ } = restoreState(savedState);
1573
+ const features = getFeatures(selectedFeature, extension);
1574
+ const extensionUri = extension.uri || extension.path;
1575
+ const folderSize = await getFolderSize(extensionUri);
1576
+ const entries = [{
1577
+ key: 'Identifier',
1578
+ value: 'abc'
1579
+ }, {
1580
+ key: 'Version',
1581
+ value: '1.9.5'
1582
+ }, {
1583
+ key: 'Last Updated',
1584
+ value: 'n/a'
1585
+ }];
1586
+ const secondEntries = [{
1587
+ key: 'Published',
1588
+ value: 'n/a'
1589
+ }, {
1590
+ key: 'Last Released',
1591
+ value: 'n/a'
1592
+ }];
1593
+ const categories = [{
1594
+ id: 'themes',
1595
+ label: 'Themes'
1596
+ }];
1597
+ const resources = [{
1598
+ label: 'Marketplace',
1599
+ url: '#'
1600
+ }, {
1601
+ label: 'Issues',
1602
+ url: '#'
1603
+ }, {
1604
+ label: 'Repository',
1605
+ url: '#'
1606
+ }, {
1607
+ label: 'License',
1608
+ url: '#'
1609
+ }];
1610
+ return {
1611
+ ...state,
1612
+ selectedTab,
1613
+ sanitizedReadmeHtml: normalizedReadmeHtml,
1614
+ iconSrc,
1615
+ name,
1616
+ description,
1617
+ sizeOnDisk: size,
1618
+ entries,
1619
+ secondEntries,
1620
+ categories,
1621
+ resources,
1622
+ extension,
1623
+ baseUrl,
1624
+ features,
1625
+ folderSize
1626
+ };
1748
1627
  };
1749
1628
 
1750
- const getFeatureWebViewsVirtualDom = extension => {
1751
- const webViews$1 = getWebViews(extension);
1752
- const heading = webViews();
1753
- return [{
1754
- type: Div,
1755
- className: FeatureContent,
1756
- childCount: 2
1757
- }, ...getFeatureContentHeadingVirtualDom(heading), {
1758
- type: Div,
1759
- childCount: webViews$1.length
1760
- }, ...webViews$1.flatMap(getWebViewVirtualDom)];
1629
+ const loadContent2 = async (state, savedState) => {
1630
+ return loadContent(state, state.platform, savedState);
1761
1631
  };
1762
1632
 
1633
+ const AdditionalDetails = 'AdditionalDetails';
1634
+ const AdditionalDetailsEntry = 'AdditionalDetailsEntry';
1635
+ const AdditionalDetailsTitle = 'AdditionalDetailsTitle';
1636
+ const Aside = 'Aside';
1637
+ const Button$1 = 'Button';
1638
+ const ButtonPrimary = 'ButtonPrimary';
1639
+ const DefaultMarkdown = 'DefaultMarkdown';
1640
+ const Categories = 'Categories';
1641
+ const Category = 'Category';
1763
1642
  const Changelog = 'Changelog';
1764
- const Commands = 'Commands';
1765
- const Details = 'Details';
1643
+ const DefinitionListItem = 'DefinitionListItem';
1644
+ const DefinitionListItemHeading = 'DefinitionListItemHeading';
1645
+ const DefinitionListItemValue = 'DefinitionListItemValue';
1646
+ const ExtensionDetail = 'ExtensionDetail';
1647
+ const ExtensionDetailDescription = 'ExtensionDetailDescription';
1648
+ const ExtensionDetailHeader = 'ExtensionDetailHeader';
1649
+ const ExtensionDetailHeaderActions = 'ExtensionDetailHeaderActions';
1650
+ const ExtensionDetailHeaderDetails = 'ExtensionDetailHeaderDetails';
1651
+ const ExtensionDetailIcon = 'ExtensionDetailIcon';
1652
+ const ExtensionDetailName = 'ExtensionDetailName';
1653
+ const ExtensionDetailPanel = 'ExtensionDetailPanel';
1654
+ const ExtensionDetailTab = 'ExtensionDetailTab';
1655
+ const ExtensionDetailTabs = 'ExtensionDetailTabs';
1656
+ const ExtensionDetailTabSelected = 'ExtensionDetailTabSelected';
1657
+ const Feature = 'Feature';
1658
+ const FeatureContent = 'FeatureContent';
1766
1659
  const Features = 'Features';
1767
- const JsonValidation = 'JsonValidation';
1768
- const ProgrammingLanguages = 'ProgrammingLanguages';
1769
- const Settings = 'Settings';
1770
- const WebViews = 'WebViews';
1771
- const Theme = 'Theme';
1660
+ const FeaturesList = 'FeaturesList';
1661
+ const FeatureWebView = 'FeatureWebView';
1662
+ const Large = 'Large';
1663
+ const Sash = 'Sash';
1664
+ const SashVertical = 'SashVertical';
1665
+ const MoreInfo = 'MoreInfo';
1666
+ const MoreInfoEntry = 'MoreInfoEntry';
1667
+ const MoreInfoEntryKey = 'MoreInfoEntryKey';
1668
+ const MoreInfoEntryOdd = 'MoreInfoEntryOdd';
1669
+ const MoreInfoEntryValue = 'MoreInfoEntryValue';
1670
+ const Normal = 'Normal';
1671
+ const Resource = 'Resource';
1672
+ const Resources = 'Resources';
1673
+ const Small = 'Small';
1674
+ const Table$1 = 'Table';
1675
+ const TableCell = 'TableCell';
1676
+ const TableHeading = 'TableHeading';
1677
+ const Viewlet = 'Viewlet';
1772
1678
 
1773
- const getFeatureContentVirtualDom = async (features, themesHtml, selectedFeature, extension) => {
1774
- switch (selectedFeature) {
1775
- case Theme:
1776
- return await getFeatureThemesVirtualDom(themesHtml);
1777
- case Commands:
1778
- return getFeatureCommandsVirtualDom(extension);
1779
- case JsonValidation:
1780
- return getFeatureJsonValidationVirtualDom(extension);
1781
- case ProgrammingLanguages:
1782
- return getFeatureProgrammingLanguagesVirtualDom();
1783
- case Settings:
1784
- return getFeatureSettingsVirtualDom(extension);
1785
- case WebViews:
1786
- return getFeatureWebViewsVirtualDom(extension);
1787
- default:
1788
- return getFeatureNotImplementedVirtualDom();
1679
+ const BYTE_UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
1680
+ const BIBYTE_UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
1681
+ const BIT_UNITS = ['b', 'kbit', 'Mbit', 'Gbit', 'Tbit', 'Pbit', 'Ebit', 'Zbit', 'Ybit'];
1682
+ const BIBIT_UNITS = ['b', 'kibit', 'Mibit', 'Gibit', 'Tibit', 'Pibit', 'Eibit', 'Zibit', 'Yibit'];
1683
+
1684
+ /*
1685
+ Formats the given number using `Number#toLocaleString`.
1686
+ - If locale is a string, the value is expected to be a locale-key (for example: `de`).
1687
+ - If locale is true, the system default locale is used for translation.
1688
+ - If no value for locale is specified, the number is returned unmodified.
1689
+ */
1690
+ const toLocaleString = (number, locale, options) => {
1691
+ let result = number;
1692
+ if (typeof locale === 'string' || Array.isArray(locale)) {
1693
+ result = number.toLocaleString(locale, options);
1694
+ } else if (locale === true || options !== undefined) {
1695
+ result = number.toLocaleString(undefined, options);
1696
+ }
1697
+ return result;
1698
+ };
1699
+ const log10 = numberOrBigInt => {
1700
+ if (typeof numberOrBigInt === 'number') {
1701
+ return Math.log10(numberOrBigInt);
1702
+ }
1703
+ const string = numberOrBigInt.toString(10);
1704
+ return string.length + Math.log10('0.' + string.slice(0, 15));
1705
+ };
1706
+ const log = numberOrBigInt => {
1707
+ if (typeof numberOrBigInt === 'number') {
1708
+ return Math.log(numberOrBigInt);
1709
+ }
1710
+ return log10(numberOrBigInt) * Math.log(10);
1711
+ };
1712
+ const divide = (numberOrBigInt, divisor) => {
1713
+ if (typeof numberOrBigInt === 'number') {
1714
+ return numberOrBigInt / divisor;
1715
+ }
1716
+ const integerPart = numberOrBigInt / BigInt(divisor);
1717
+ const remainder = numberOrBigInt % BigInt(divisor);
1718
+ return Number(integerPart) + Number(remainder) / divisor;
1719
+ };
1720
+ function prettyBytes(number, options) {
1721
+ if (typeof number !== 'bigint' && !Number.isFinite(number)) {
1722
+ throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
1723
+ }
1724
+ options = {
1725
+ bits: false,
1726
+ binary: false,
1727
+ space: true,
1728
+ ...options
1729
+ };
1730
+ const UNITS = options.bits ? options.binary ? BIBIT_UNITS : BIT_UNITS : options.binary ? BIBYTE_UNITS : BYTE_UNITS;
1731
+ const separator = options.space ? ' ' : '';
1732
+ if (options.signed && (typeof number === 'number' ? number === 0 : number === 0n)) {
1733
+ return ` 0${separator}${UNITS[0]}`;
1734
+ }
1735
+ const isNegative = number < 0;
1736
+ const prefix = isNegative ? '-' : options.signed ? '+' : '';
1737
+ if (isNegative) {
1738
+ number = -number;
1739
+ }
1740
+ let localeOptions;
1741
+ if (options.minimumFractionDigits !== undefined) {
1742
+ localeOptions = {
1743
+ minimumFractionDigits: options.minimumFractionDigits
1744
+ };
1789
1745
  }
1746
+ if (options.maximumFractionDigits !== undefined) {
1747
+ localeOptions = {
1748
+ maximumFractionDigits: options.maximumFractionDigits,
1749
+ ...localeOptions
1750
+ };
1751
+ }
1752
+ if (number < 1) {
1753
+ const numberString = toLocaleString(number, options.locale, localeOptions);
1754
+ return prefix + numberString + separator + UNITS[0];
1755
+ }
1756
+ const exponent = Math.min(Math.floor(options.binary ? log(number) / Math.log(1024) : log10(number) / 3), UNITS.length - 1);
1757
+ number = divide(number, (options.binary ? 1024 : 1000) ** exponent);
1758
+ if (!localeOptions) {
1759
+ number = number.toPrecision(3);
1760
+ }
1761
+ const numberString = toLocaleString(Number(number), options.locale, localeOptions);
1762
+ const unit = UNITS[exponent];
1763
+ return prefix + numberString + separator + unit;
1764
+ }
1765
+
1766
+ const getDisplaySize = size => {
1767
+ return prettyBytes(size, {
1768
+ maximumFractionDigits: 1
1769
+ });
1790
1770
  };
1791
1771
 
1792
- const getFeatureListItemVirtualDom = feature => {
1793
- const {
1794
- label,
1795
- selected,
1796
- id
1797
- } = feature;
1798
- const className = selected ? 'Feature FeatureSelected' : Feature;
1799
- return [{
1800
- // TODO use role list item or tab
1801
- type: Button,
1802
- name: id,
1803
- className,
1804
- childCount: 1
1805
- }, text(label)];
1772
+ const A = 53;
1773
+ const Button = 1;
1774
+ const Div = 4;
1775
+ const H1 = 5;
1776
+ const H2 = 22;
1777
+ const Img = 17;
1778
+ const Pre = 51;
1779
+ const Table = 9;
1780
+ const TBody = 10;
1781
+ const Td = 11;
1782
+ const Th = 13;
1783
+ const THead = 14;
1784
+ const Tr = 15;
1785
+
1786
+ const mergeClassNames = (...classNames) => {
1787
+ return classNames.filter(Boolean).join(' ');
1788
+ };
1789
+ const Text$1 = 12;
1790
+ const text = data => {
1791
+ return {
1792
+ type: Text$1,
1793
+ text: data,
1794
+ childCount: 0
1795
+ };
1806
1796
  };
1807
1797
 
1808
- const getFeatureListVirtualDom = features => {
1798
+ const getChangelogVirtualDom = () => {
1799
+ const notImplemented$1 = notImplemented();
1800
+ // TODO set tabpanel role
1809
1801
  return [{
1810
- // TODO use either list or tabs role
1811
1802
  type: Div,
1812
- className: FeaturesList,
1813
- childCount: features.length,
1814
- onClick: HandleFeaturesClick
1815
- }, ...features.flatMap(getFeatureListItemVirtualDom)];
1803
+ className: Changelog,
1804
+ childCount: 1
1805
+ }, text(notImplemented$1)];
1816
1806
  };
1817
1807
 
1818
- const getFeaturesVirtualDom = async (features, themesHtml, selectedFeature, extension) => {
1819
- if (features.length === 0) {
1820
- const none$1 = none();
1821
- return [{
1822
- type: Div,
1823
- className: Features$1,
1824
- childCount: 3
1825
- }, text(none$1)];
1826
- }
1808
+ const TabList = 'tablist';
1809
+ const Tab = 'tab';
1810
+ const Panel = 'panel';
1811
+
1812
+ const getAdditionalDetailsEntryVirtualDom = (heading, items, renderer) => {
1827
1813
  return [{
1828
1814
  type: Div,
1829
- className: Features$1,
1830
- childCount: 3
1831
- }, ...getFeatureListVirtualDom(features), {
1815
+ className: AdditionalDetailsEntry,
1816
+ childCount: 2
1817
+ }, {
1832
1818
  type: Div,
1833
- className: mergeClassNames(Sash, SashVertical),
1834
- childCount: 0
1835
- }, ...(await getFeatureContentVirtualDom(features, themesHtml, selectedFeature, extension))];
1836
- };
1837
-
1838
- const getExtensionDetailContentVirtualDom = async (sanitizedReadmeHtml, themesHtml, selectedTab, features, displaySize, extensionId, extensionVersion, selectedFeature, extension, width) => {
1839
- switch (selectedTab) {
1840
- case Details:
1841
- return await getDetailsVirtualDom(sanitizedReadmeHtml, displaySize, extensionId, extensionVersion, width);
1842
- case Features:
1843
- return await getFeaturesVirtualDom(features, themesHtml, selectedFeature, extension);
1844
- case Changelog:
1845
- return getChangelogVirtualDom();
1846
- default:
1847
- return [];
1848
- }
1819
+ className: AdditionalDetailsTitle,
1820
+ childCount: 1
1821
+ }, text(heading), ...renderer(items)];
1849
1822
  };
1850
1823
 
1851
- const getButtonVirtualDom = (message, onClick) => {
1824
+ const getCategoryVirtualDom = category => {
1825
+ const {
1826
+ label
1827
+ } = category;
1852
1828
  return [{
1853
- type: Button,
1854
- className: mergeClassNames(Button$1, ButtonPrimary),
1855
- onClick,
1829
+ type: Div,
1830
+ className: Category,
1856
1831
  childCount: 1
1857
- }, text(message)];
1832
+ }, text(label)];
1858
1833
  };
1859
1834
 
1860
- const getExtensionDetailHeaderActionsVirtualDom = () => {
1861
- const dom = [{
1835
+ const getCategoriesDom = categories => {
1836
+ return [{
1862
1837
  type: Div,
1863
- className: ExtensionDetailHeaderActions,
1864
- childCount: 2
1865
- }, ...getButtonVirtualDom('Disable', HandleClickDisable), ...getButtonVirtualDom('Uninstall', HandleClickUninstall)];
1866
- return dom;
1838
+ className: Categories,
1839
+ childCount: categories.length
1840
+ }, ...categories.flatMap(getCategoryVirtualDom)];
1867
1841
  };
1868
1842
 
1869
- const getExtensionDetailHeaderVirtualDom = extensionDetail => {
1843
+ const parentNode$1 = {
1844
+ type: Div,
1845
+ className: MoreInfoEntryKey,
1846
+ childCount: 1
1847
+ };
1848
+ const getMoreInfoEntryKeyVirtualDom = item => {
1870
1849
  const {
1871
- name,
1872
- iconSrc,
1873
- description
1874
- } = extensionDetail;
1875
- const dom = [{
1876
- type: Div,
1877
- className: ExtensionDetailHeader,
1878
- childCount: 2
1879
- }, {
1880
- type: Img,
1881
- className: ExtensionDetailIcon,
1882
- alt: '',
1883
- draggable: false,
1884
- childCount: 0,
1885
- src: iconSrc
1886
- }, {
1887
- type: Div,
1888
- className: ExtensionDetailHeaderDetails,
1889
- childCount: 3
1890
- }, {
1891
- type: Div,
1892
- className: ExtensionDetailName,
1893
- childCount: 1
1894
- }, text(name), {
1895
- type: Div,
1896
- className: ExtensionDetailDescription,
1897
- childCount: 1
1898
- }, text(description), ...getExtensionDetailHeaderActionsVirtualDom()];
1899
- return dom;
1850
+ key
1851
+ } = item;
1852
+ return [parentNode$1, text(key)];
1900
1853
  };
1901
1854
 
1902
- const hasThemes = extension => {
1903
- return extension && (extension.colorThemes || extension.iconThemes || extension.productIconThemes);
1904
- };
1905
- const hasCommands = extension => {
1906
- return extension && extension.commands;
1907
- };
1908
- const hasJsonValidation = extension => {
1909
- return extension && extension.jsonValidation;
1910
- };
1911
- const hasProgrammingLanguages = extension => {
1912
- return extension && extension.programmingLanguages;
1913
- };
1914
- const hasSettings = extension => {
1915
- return extension && extension.settings;
1916
- };
1917
- const hasWebViews = extension => {
1918
- return extension && extension.webViews;
1919
- };
1920
- const ifElseFeature = (id, label, isEnabled, selectedFeature, extension) => {
1921
- if (!isEnabled(extension)) {
1922
- return [];
1855
+ const getTag = (onClick, code) => {
1856
+ if (onClick) {
1857
+ return A;
1923
1858
  }
1924
- return [{
1925
- id,
1926
- label,
1927
- selected: selectedFeature === id
1928
- }];
1929
- };
1930
- const getFeatures = (selectedFeature, extension) => {
1931
- if (!selectedFeature) {
1932
- selectedFeature = Theme;
1859
+ if (code) {
1860
+ // TODO use code tag
1861
+ return Div;
1933
1862
  }
1934
- const textTheme = theme();
1935
- const textCommands = commands();
1936
- const textJsonValidation = jsonValidation();
1937
- const programmingLanguages$1 = programmingLanguages();
1938
- const settings$1 = settings();
1939
- const webViews$1 = webViews();
1940
- const features = [...ifElseFeature(Theme, textTheme, hasThemes, selectedFeature, extension), ...ifElseFeature(Commands, textCommands, hasCommands, selectedFeature, extension), ...ifElseFeature(JsonValidation, textJsonValidation, hasJsonValidation, selectedFeature, extension), ...ifElseFeature(ProgrammingLanguages, programmingLanguages$1, hasProgrammingLanguages, selectedFeature, extension), ...ifElseFeature(Settings, settings$1, hasSettings, selectedFeature, extension), ...ifElseFeature(WebViews, webViews$1, hasWebViews, selectedFeature, extension)];
1941
- return features;
1863
+ return Div;
1942
1864
  };
1943
-
1944
- const getTabs = selectedTab => {
1945
- const tabs = [{
1946
- label: 'Details',
1947
- name: Details,
1948
- selected: selectedTab === Details
1949
- }, {
1950
- label: 'Features',
1951
- name: Features,
1952
- selected: selectedTab === Features
1953
- }, {
1954
- label: 'Changelog',
1955
- name: Changelog,
1956
- selected: selectedTab === Changelog
1957
- }];
1958
- return tabs;
1865
+ const getClassName = (onClick, code) => {
1866
+ if (onClick) {
1867
+ return MoreInfoEntryValue + ' Link';
1868
+ }
1869
+ if (code) {
1870
+ return MoreInfoEntryValue + ' Code';
1871
+ }
1872
+ return MoreInfoEntryValue;
1959
1873
  };
1960
-
1961
- const selectedClassName = mergeClassNames(ExtensionDetailTab, ExtensionDetailTabSelected);
1962
- const defaultClassName = ExtensionDetailTab;
1963
- const getTabVirtualDom = tab => {
1874
+ const getMoreInfoEntryValueVirtualDom = item => {
1964
1875
  const {
1965
- label,
1966
- selected,
1967
- name
1968
- } = tab;
1969
- const className = selected ? selectedClassName : defaultClassName;
1970
- const ariaSelected = selected ? 'true' : 'false';
1876
+ value,
1877
+ onClick,
1878
+ code
1879
+ } = item;
1880
+ const type = getTag(onClick, code);
1881
+ const className = getClassName(onClick, code);
1971
1882
  return [{
1972
- type: Button,
1973
- role: Tab,
1974
- name,
1883
+ type: type,
1975
1884
  className,
1976
1885
  childCount: 1,
1977
- tabIndex: -1,
1978
- ariaSelected
1979
- }, text(label)];
1886
+ onClick
1887
+ }, text(value)];
1980
1888
  };
1981
1889
 
1982
- const getTabsVirtualDom = tabs => {
1983
- return [{
1984
- type: Div,
1985
- className: ExtensionDetailTabs,
1986
- childCount: tabs.length,
1987
- role: TabList,
1988
- onClick: HandleTabsClick,
1989
- tabIndex: 0
1990
- }, ...tabs.flatMap(getTabVirtualDom)];
1890
+ const parentNodeEven = {
1891
+ type: Div,
1892
+ className: MoreInfoEntry,
1893
+ childCount: 2
1991
1894
  };
1992
-
1993
- const Small = 1;
1994
- const Normal = 2;
1995
- const Large = 3;
1996
-
1997
- const getViewletSize = width => {
1998
- if (width < 180) {
1999
- return Small;
2000
- }
2001
- if (width < 768) {
2002
- return Normal;
2003
- }
2004
- return Large;
1895
+ const parentNodeOdd = {
1896
+ type: Div,
1897
+ className: mergeClassNames(MoreInfoEntry, MoreInfoEntryOdd),
1898
+ childCount: 2
2005
1899
  };
2006
-
2007
- const getClassNames = size => {
2008
- switch (size) {
2009
- case Small:
2010
- return Small$1;
2011
- case Normal:
2012
- return Normal$1;
2013
- case Large:
2014
- return Large$1;
2015
- default:
2016
- return '';
2017
- }
1900
+ const getMoreInfoEntryVirtualDom = item => {
1901
+ const {
1902
+ odd
1903
+ } = item;
1904
+ const node = odd ? parentNodeOdd : parentNodeEven;
1905
+ return [node, ...getMoreInfoEntryKeyVirtualDom(item), ...getMoreInfoEntryValueVirtualDom(item)];
2018
1906
  };
2019
1907
 
2020
- const getExtensionDetailVirtualDom = async (extensionDetail, sanitizedReadmeHtml, selectedTab, newState) => {
2021
- // TODO move this to view model so that rendering occurs like
2022
- // 1. state
2023
- // 2. view model
2024
- // 3. virtual dom
2025
- // 4. dom
2026
- const themesHtml = newState?.selectedFeatureMarkdownDom || '';
2027
- const selectedFeature = newState?.selectedFeature || '';
2028
- const extension = newState?.extension || {};
2029
- const features = getFeatures(selectedFeature, extension);
2030
- const size = newState.folderSize || 0;
2031
- const extensionId = newState?.extension?.id || 'n/a';
2032
- const extensionVersion = newState?.extension?.version || 'n/a';
2033
- const displaySize = getDisplaySize(size);
2034
- const width = newState?.width || 500;
2035
- const tabs = getTabs(selectedTab);
2036
- const sizeValue = getViewletSize(newState?.width || 0);
2037
- const sizeClass = getClassNames(sizeValue);
2038
- const dom = [{
1908
+ const getMoreInfoVirtualDom = items => {
1909
+ return [{
2039
1910
  type: Div,
2040
- className: mergeClassNames(Viewlet, ExtensionDetail, sizeClass),
2041
- childCount: 3
2042
- }, ...getExtensionDetailHeaderVirtualDom(extensionDetail), ...getTabsVirtualDom(tabs), ...(await getExtensionDetailContentVirtualDom(sanitizedReadmeHtml, themesHtml, selectedTab, features, displaySize, extensionId, extensionVersion, selectedFeature, extension, width))];
2043
- return dom;
1911
+ className: MoreInfo,
1912
+ childCount: items.length
1913
+ }, ...items.flatMap(getMoreInfoEntryVirtualDom)];
2044
1914
  };
2045
1915
 
2046
- const getExtensionDetailVirtualDom2 = async uid => {
1916
+ const resourceNode = {
1917
+ // TODO use link with url
1918
+ type: Div,
1919
+ className: Resource,
1920
+ childCount: 1
1921
+ };
1922
+ const getResourceVirtualDom = resource => {
2047
1923
  const {
2048
- newState
2049
- } = get$1(uid);
2050
- return await getExtensionDetailVirtualDom(newState, newState.sanitizedReadmeHtml, newState.selectedTab, newState);
1924
+ label
1925
+ } = resource;
1926
+ return [resourceNode, text(label)];
2051
1927
  };
2052
1928
 
2053
- const None = 0;
2054
-
2055
- const getCopyMenuEntry = () => ({
2056
- id: 'copy',
2057
- label: copy(),
2058
- flags: None,
2059
- command: 'ClipBoard.execCopy'
2060
- });
2061
-
2062
- const getImageMenuEntries = props => {
2063
- if (!props.isImage) {
2064
- return [];
2065
- }
1929
+ const getResourcesVirtualDom = resources => {
2066
1930
  return [{
2067
- id: 'openImageInNewTab',
2068
- label: openImageInNewTab(),
2069
- flags: None,
2070
- command: 'Open.openUrl',
2071
- args: [props.url || '']
2072
- }, {
2073
- id: 'saveImageAs',
2074
- label: saveImageAs(),
2075
- flags: None,
2076
- command: 'SaveFileAs.saveFileAs',
2077
- args: ['image.png', props.url || '']
2078
- }];
1931
+ type: Div,
1932
+ className: Resources,
1933
+ childCount: resources.length
1934
+ }, ...resources.flatMap(getResourceVirtualDom)];
2079
1935
  };
2080
1936
 
2081
- const getLinkMenuEntries = props => {
2082
- if (!props.isLink) {
1937
+ const getAdditionalDetailsVirtualDom = (showAdditionalDetails, firstHeading, entries, secondHeading, secondEntries, thirdHeading, categories, fourthHeading, resources) => {
1938
+ if (!showAdditionalDetails) {
2083
1939
  return [];
2084
1940
  }
2085
1941
  return [{
2086
- id: 'openInNewTab',
2087
- label: openInNewTab(),
2088
- flags: None,
2089
- command: 'Open.openUrl',
2090
- args: [props.url || '']
2091
- }];
1942
+ type: Div,
1943
+ className: Aside,
1944
+ childCount: 1
1945
+ }, {
1946
+ type: Div,
1947
+ className: AdditionalDetails,
1948
+ tabIndex: 0,
1949
+ childCount: 4
1950
+ }, ...getAdditionalDetailsEntryVirtualDom(firstHeading, entries, getMoreInfoVirtualDom), ...getAdditionalDetailsEntryVirtualDom(secondHeading, secondEntries, getMoreInfoVirtualDom), ...getAdditionalDetailsEntryVirtualDom(thirdHeading, categories, getCategoriesDom), ...getAdditionalDetailsEntryVirtualDom(fourthHeading, resources, getResourcesVirtualDom)];
2092
1951
  };
2093
1952
 
2094
- const getMenuEntries = props => [...getLinkMenuEntries(props), ...getImageMenuEntries(props), getCopyMenuEntry()];
2095
-
2096
- const handleClickDisable = async state => {
2097
- return state;
2098
- };
1953
+ const HandleClickDisable = 'handleClickDisable';
1954
+ const HandleClickSize = 'handleClickSize';
1955
+ const HandleClickUninstall = 'handleClickUninstall';
1956
+ const HandleFeaturesClick = 'handleFeaturesClick';
1957
+ const HandleIconError = 'handleIconError';
1958
+ const HandleReadmeContextMenu = 'handleReadmeContextMenu';
1959
+ const HandleTabsClick = 'handleTabsClick';
2099
1960
 
2100
- const selectFeature = async (state, name) => {
2101
- if (!name) {
2102
- return state;
2103
- }
2104
- const {
2105
- features
2106
- } = state;
2107
- const newFeatures = features.map(feature => {
2108
- if (feature.id === name) {
2109
- return {
2110
- ...feature,
2111
- selected: true
2112
- };
2113
- }
2114
- return {
2115
- ...feature,
2116
- selected: false
2117
- };
2118
- });
2119
- return {
2120
- ...state,
2121
- selectedFeature: name,
2122
- features: newFeatures
2123
- };
1961
+ const getInstallationEntries = (displaySize, extensionId, extensionVersion) => {
1962
+ const entries = [{
1963
+ key: 'Identifier',
1964
+ value: extensionId,
1965
+ odd: true,
1966
+ code: true
1967
+ }, {
1968
+ key: 'Version',
1969
+ value: extensionVersion,
1970
+ code: true
1971
+ }, {
1972
+ key: 'Last Updated',
1973
+ value: 'n/a',
1974
+ odd: true
1975
+ }, {
1976
+ key: 'Size',
1977
+ value: displaySize,
1978
+ onClick: HandleClickSize
1979
+ }];
1980
+ return entries;
2124
1981
  };
2125
1982
 
2126
- const handleClickFeatures = async (state, name) => {
2127
- return selectFeature(state, name);
1983
+ const getMarkdownVirtualDom = async html => {
1984
+ string(html);
1985
+ // @ts-ignore todo
1986
+ const dom = await invoke('Markdown.getVirtualDom', html);
1987
+ return dom;
2128
1988
  };
2129
1989
 
2130
- const handleClickSize = async state => {
2131
- const {
2132
- uri
2133
- } = state.extension;
2134
- await invoke('OpenNativeFolder.openNativeFolder', uri);
2135
- return state;
1990
+ const getMarketplaceEntries = () => {
1991
+ return [{
1992
+ key: 'Published',
1993
+ value: 'n/a',
1994
+ odd: true
1995
+ }, {
1996
+ key: 'Last Released',
1997
+ value: 'n/a'
1998
+ }];
2136
1999
  };
2137
2000
 
2138
- const handleClickUninstall = async state => {
2139
- return state;
2001
+ const getDetailsVirtualDom = async (sanitizedReadmeHtml, displaySize, extensionId, extensionVersion, width) => {
2002
+ const firstHeading = 'Installation';
2003
+ const entries = getInstallationEntries(displaySize, extensionId, extensionVersion);
2004
+ const secondHeading = 'Marketplace';
2005
+ const secondEntries = getMarketplaceEntries();
2006
+ const thirdHeading = 'Categories';
2007
+ const categories = [{
2008
+ id: 'themes',
2009
+ label: 'Themes'
2010
+ }];
2011
+ const fourthHeading = 'Resources';
2012
+ const resources = [{
2013
+ label: 'Marketplace',
2014
+ url: '#'
2015
+ }, {
2016
+ label: 'Issues',
2017
+ url: '#'
2018
+ }, {
2019
+ label: 'Repository',
2020
+ url: '#'
2021
+ }, {
2022
+ label: 'License',
2023
+ url: '#'
2024
+ }];
2025
+ const showAdditionalDetailsBreakpoint = 600;
2026
+ const showAdditionalDetails = width > showAdditionalDetailsBreakpoint;
2027
+ const childCount = showAdditionalDetails ? 2 : 1;
2028
+ const dom = [{
2029
+ type: Div,
2030
+ className: ExtensionDetailPanel,
2031
+ childCount: childCount,
2032
+ role: Panel
2033
+ }, ...(await getMarkdownVirtualDom(sanitizedReadmeHtml)), ...getAdditionalDetailsVirtualDom(showAdditionalDetails, firstHeading, entries, secondHeading, secondEntries, thirdHeading, categories, fourthHeading, resources)];
2034
+ return dom;
2140
2035
  };
2141
2036
 
2142
- const extensionDefaultIcon = assetDir => {
2143
- return `${assetDir}/icons/extensionDefaultIcon.png`;
2144
- };
2145
- const extensionLanguageBasics = assetDir => {
2146
- return `${assetDir}/icons/language-icon.svg`;
2147
- };
2148
- const extensionTheme = assetDir => {
2149
- return `${assetDir}/icons/theme-icon.png`;
2150
- };
2037
+ const Text = 1;
2038
+ const Code = 2;
2151
2039
 
2152
- const handleIconError = state => {
2040
+ const getCommandTableEntry = command => {
2041
+ // TODO watch out for command being null/undefined/number/string/array
2153
2042
  const {
2154
- iconSrc,
2155
- assetDir
2156
- } = state;
2157
- if (iconSrc === extensionDefaultIcon(assetDir)) {
2158
- return state;
2159
- }
2160
- return {
2161
- ...state,
2162
- iconSrc: extensionDefaultIcon(assetDir)
2163
- };
2043
+ id,
2044
+ label
2045
+ } = command;
2046
+ return [{
2047
+ type: Code,
2048
+ value: id
2049
+ }, {
2050
+ type: Text,
2051
+ value: label
2052
+ }];
2164
2053
  };
2165
2054
 
2166
- const selectTabChangelog = async state => {
2055
+ const getCommandTableEntries = extension => {
2056
+ const commands = extension.commands || [];
2057
+ const rows = commands.map(getCommandTableEntry);
2058
+ const id$1 = id();
2059
+ const label$1 = label();
2167
2060
  return {
2168
- ...state,
2169
- selectedTab: Changelog
2061
+ headings: [id$1, label$1],
2062
+ rows
2170
2063
  };
2171
2064
  };
2172
2065
 
2173
- const selectTabDefault = async state => {
2174
- return state;
2066
+ const getFeatureContentHeadingVirtualDom = heading => {
2067
+ return [{
2068
+ type: H1,
2069
+ childCount: 1
2070
+ }, text(heading)];
2175
2071
  };
2176
2072
 
2177
- const selectTabDetails = async state => {
2178
- // TODO load readmo markdown here
2179
- return {
2180
- ...state,
2181
- selectedTab: Details
2182
- };
2073
+ const getTableHeadingVirtualDom = heading => {
2074
+ return [{
2075
+ type: Th,
2076
+ className: TableHeading,
2077
+ childCount: 1
2078
+ }, text(heading)];
2183
2079
  };
2184
2080
 
2185
- const getThemeItemMarkdown = (heading, items) => {
2186
- let markdown = '';
2187
- if (items.length > 0) {
2188
- markdown += `### ${heading}`;
2189
- markdown += '\n\n';
2190
- for (const item of items) {
2191
- markdown += `- ${item.label}`;
2192
- markdown += '\n';
2193
- }
2194
- }
2195
- return markdown;
2081
+ const getCellCodeVirtualDom = value => {
2082
+ return [{
2083
+ type: Td,
2084
+ className: TableCell,
2085
+ childCount: 1
2086
+ }, {
2087
+ type: Div,
2088
+ // TODO use code tag
2089
+ childCount: 1
2090
+ }, text(value)];
2196
2091
  };
2197
2092
 
2198
- const getColorThemeMarkdown = themes => {
2199
- const heading = 'Color Themes';
2200
- return getThemeItemMarkdown(heading, themes);
2201
- };
2202
- const getIconThemeMarkdown = iconThemes => {
2203
- const heading = 'File Icon Themes';
2204
- return getThemeItemMarkdown(heading, iconThemes);
2205
- };
2206
- const getProductIconThemeMarkdown = iconThemes => {
2207
- const heading = 'Product Icon Themes';
2208
- return getThemeItemMarkdown(heading, iconThemes);
2209
- };
2210
- const getThemeMarkdown = (themes, iconThemes, productIconThemes) => {
2211
- let markdown = '';
2212
- markdown += getColorThemeMarkdown(themes);
2213
- markdown += getIconThemeMarkdown(iconThemes);
2214
- markdown += getProductIconThemeMarkdown(productIconThemes);
2215
- return markdown;
2093
+ const getCellTextVirtualDom = value => {
2094
+ return [{
2095
+ type: Td,
2096
+ className: TableCell,
2097
+ childCount: 1
2098
+ }, text(value)];
2216
2099
  };
2217
2100
 
2218
- const renderMarkdown = async (markdown, options = {}) => {
2219
- const html = await invoke('Markdown.renderMarkdown', markdown, options);
2220
- return html;
2101
+ const getCellRenderer = type => {
2102
+ switch (type) {
2103
+ case Code:
2104
+ return getCellCodeVirtualDom;
2105
+ case Text:
2106
+ return getCellTextVirtualDom;
2107
+ default:
2108
+ throw new Error(`unexpected cell type ${type}`);
2109
+ }
2221
2110
  };
2222
2111
 
2223
- const selectTab$1 = async state => {
2224
- const {
2225
- extension,
2226
- baseUrl
2227
- } = state;
2112
+ const getCellVirtualDom = entry => {
2228
2113
  const {
2229
- colorThemes,
2230
- iconThemes,
2231
- productIconThemes
2232
- } = extension;
2233
- // TODO do this depending on featured tab content
2234
- const markdown = getThemeMarkdown(colorThemes || [], iconThemes || [], productIconThemes || []);
2235
- const rendered = await renderMarkdown(markdown, {
2236
- baseUrl
2237
- });
2238
- return {
2239
- ...state,
2240
- selectedTab: Features,
2241
- selectedFeatureMarkdownDom: rendered
2242
- };
2114
+ value,
2115
+ type
2116
+ } = entry;
2117
+ const fn = getCellRenderer(type);
2118
+ return fn(value);
2243
2119
  };
2244
2120
 
2245
- const getSelectTabHandler = selectedTab => {
2246
- switch (selectedTab) {
2247
- case Details:
2248
- return selectTabDetails;
2249
- case Features:
2250
- return selectTab$1;
2251
- case Changelog:
2252
- return selectTabChangelog;
2253
- default:
2254
- return selectTabDefault;
2255
- }
2121
+ const getTableRowVirtualDom = entries => {
2122
+ return [{
2123
+ type: Tr,
2124
+ childCount: entries.length
2125
+ }, ...entries.flatMap(getCellVirtualDom)];
2256
2126
  };
2257
2127
 
2258
- const selectTab = (state, name) => {
2259
- const fn = getSelectTabHandler(name);
2260
- return fn(state);
2128
+ const getTableVirtualDom = tableInfo => {
2129
+ const {
2130
+ headings,
2131
+ rows
2132
+ } = tableInfo;
2133
+ return [{
2134
+ type: Table,
2135
+ className: Table$1,
2136
+ childCount: 2
2137
+ }, {
2138
+ type: THead,
2139
+ childCount: 1
2140
+ }, {
2141
+ type: Tr,
2142
+ childCount: headings.length
2143
+ }, ...headings.flatMap(getTableHeadingVirtualDom), {
2144
+ type: TBody,
2145
+ childCount: rows.length
2146
+ }, ...rows.flatMap(getTableRowVirtualDom)];
2261
2147
  };
2262
2148
 
2263
- const handleTabsClick = (state, name) => {
2264
- return selectTab(state, name);
2149
+ // TODO have typed view-model
2150
+ const getFeatureCommandsVirtualDom = extension => {
2151
+ const heading = commands();
2152
+ const tableInfo = getCommandTableEntries(extension);
2153
+ return [{
2154
+ type: Div,
2155
+ className: FeatureContent,
2156
+ childCount: 2
2157
+ }, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
2265
2158
  };
2266
2159
 
2267
- const isLanguageBasicsExtension = extension => {
2268
- return extension.name && extension.name.startsWith('Language Basics');
2160
+ const getJsonValidationTableEntry = validation => {
2161
+ const {
2162
+ fileMatch,
2163
+ schema
2164
+ } = validation;
2165
+ return [{
2166
+ type: Code,
2167
+ value: fileMatch
2168
+ }, {
2169
+ type: Code,
2170
+ value: schema
2171
+ }];
2269
2172
  };
2270
2173
 
2271
- const isThemeExtension = extension => {
2272
- return extension.name && extension.name.endsWith(' Theme');
2174
+ const getJsonValidationTableEntries = extension => {
2175
+ const validations = extension.jsonValidation || [];
2176
+ const rows = validations.map(getJsonValidationTableEntry);
2177
+ return {
2178
+ headings: [fileMatch(), schema()],
2179
+ rows
2180
+ };
2273
2181
  };
2274
2182
 
2275
- const Web = 1;
2276
- const Electron = 2;
2277
- const Remote = 3;
2278
-
2279
- const getIcon = (extension, platform, assetDir) => {
2280
- if (!extension) {
2281
- return extensionDefaultIcon(assetDir);
2282
- }
2283
- if (!extension.path || !extension.icon) {
2284
- if (isLanguageBasicsExtension(extension)) {
2285
- return extensionLanguageBasics(assetDir);
2286
- }
2287
- if (isThemeExtension(extension)) {
2288
- return extensionTheme(assetDir);
2289
- }
2290
- return extensionDefaultIcon(assetDir);
2291
- }
2292
- if (platform === Remote || platform === Electron) {
2293
- if (extension.builtin) {
2294
- return `${assetDir}/extensions/${extension.id}/${extension.icon}`;
2295
- }
2296
- return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
2297
- }
2298
- return '';
2183
+ const parentNode = {
2184
+ type: Div,
2185
+ className: FeatureContent,
2186
+ childCount: 2
2299
2187
  };
2300
-
2301
- const getDescription = extension => {
2302
- if (!extension || !extension.description) {
2303
- return 'n/a';
2304
- }
2305
- return extension.description;
2188
+ const getFeatureJsonValidationVirtualDom = extension => {
2189
+ const heading = jsonValidation();
2190
+ const tableInfo = getJsonValidationTableEntries(extension);
2191
+ return [parentNode, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
2306
2192
  };
2307
2193
 
2308
- const getName = extension => {
2309
- if (extension && extension.name) {
2310
- return extension.name;
2311
- }
2312
- if (extension && extension.id) {
2313
- return extension.id;
2314
- }
2315
- return 'n/a';
2194
+ const getFeatureNotImplementedVirtualDom = () => {
2195
+ const heading = 'Not implemented';
2196
+ return [{
2197
+ type: Div,
2198
+ className: FeatureContent,
2199
+ childCount: 1
2200
+ }, {
2201
+ type: H1,
2202
+ childCount: 1
2203
+ }, text(heading)];
2316
2204
  };
2317
2205
 
2318
- const getAllExtensions = async platform => {
2319
- if (platform === Web) {
2320
- return [];
2321
- }
2322
- return invoke('ExtensionManagement.getAllExtensions');
2323
- };
2324
- const getExtension$1 = async (id, platform) => {
2325
- // TODO only ask one extension from renderer worker instead of all
2326
- const allExtensions = await getAllExtensions(platform);
2327
- for (const extension of allExtensions) {
2328
- if (extension.id === id) {
2329
- return extension;
2330
- }
2331
- }
2332
- return undefined;
2206
+ const getFeatureProgrammingLanguagesVirtualDom = () => {
2207
+ const heading = programmingLanguages();
2208
+ // TODO
2209
+ return [{
2210
+ type: Div,
2211
+ className: FeatureContent,
2212
+ childCount: 1
2213
+ }, ...getFeatureContentHeadingVirtualDom(heading)];
2333
2214
  };
2334
2215
 
2335
- const getExtensionNew = async id => {
2336
- return invoke('ExtensionManagement.getExtension', id);
2337
- };
2338
- const getExtension = async (id, platform) => {
2339
- try {
2340
- return await getExtensionNew(id);
2341
- } catch {
2342
- return getExtension$1(id, platform);
2343
- }
2216
+ const getSettingsTableEntry = setting => {
2217
+ const {
2218
+ id,
2219
+ label
2220
+ } = setting;
2221
+ // TODO watch out for null/undefined/number/string/array
2222
+ return [{
2223
+ type: Text,
2224
+ value: id
2225
+ }, {
2226
+ type: Text,
2227
+ value: label
2228
+ }];
2344
2229
  };
2345
2230
 
2346
- const getRemoteSrc = uri => {
2347
- const src = `/remote${uri}`;
2348
- return src;
2231
+ const getSettingsTableEntries = extension => {
2232
+ const settings = extension.settings || [];
2233
+ const rows = settings.map(getSettingsTableEntry);
2234
+ const textId = id();
2235
+ const textLabel = label();
2236
+ return {
2237
+ headings: [textId, textLabel],
2238
+ rows
2239
+ };
2349
2240
  };
2350
2241
 
2351
- const getBaseUrl = (extensionPath, platform) => {
2352
- switch (platform) {
2353
- case Remote:
2354
- case Electron:
2355
- return getRemoteSrc(extensionPath + '/');
2356
- default:
2357
- return extensionPath;
2358
- }
2242
+ const getFeatureSettingsVirtualDom = extension => {
2243
+ const heading = settings();
2244
+ const tableInfo = getSettingsTableEntries(extension);
2245
+ return [{
2246
+ type: Div,
2247
+ className: FeatureContent,
2248
+ childCount: 2
2249
+ }, ...getFeatureContentHeadingVirtualDom(heading), ...getTableVirtualDom(tableInfo)];
2359
2250
  };
2360
2251
 
2361
- const getFolderSize = async uri => {
2362
- try {
2363
- return await invoke('FileSystem.getFolderSize', uri);
2364
- } catch {
2365
- return 0;
2252
+ const getVirtualDomChildCount = markdownDom => {
2253
+ const max = markdownDom.length - 1;
2254
+ let stack = [];
2255
+ for (let i = max; i >= 0; i--) {
2256
+ const element = markdownDom[i];
2257
+ if (element.childCount > 0) {
2258
+ stack = stack.slice(element.childCount);
2259
+ }
2260
+ stack.unshift(element);
2366
2261
  }
2262
+ return stack.length;
2367
2263
  };
2368
2264
 
2369
- const readFile = async uri => {
2370
- return invoke('FileSystem.readFile', uri);
2265
+ const getFeatureThemesVirtualDom = async themesHtml => {
2266
+ const markdownDom = await getMarkdownVirtualDom(themesHtml);
2267
+ const childCount = getVirtualDomChildCount(markdownDom);
2268
+ const heading = theme();
2269
+ return [{
2270
+ type: Div,
2271
+ className: FeatureContent,
2272
+ childCount: 2
2273
+ }, ...getFeatureContentHeadingVirtualDom(heading), {
2274
+ type: Div,
2275
+ className: DefaultMarkdown,
2276
+ childCount
2277
+ }, ...markdownDom];
2371
2278
  };
2372
2279
 
2373
- const ENOENT = 'ENOENT';
2374
-
2375
- const isEnoentError = error => {
2376
- return error && error.code === ENOENT;
2280
+ const toWebView = rawWebView => {
2281
+ const {
2282
+ id,
2283
+ selector,
2284
+ contentSecurityPolicy,
2285
+ elements
2286
+ } = rawWebView;
2287
+ return {
2288
+ id,
2289
+ selectorString: JSON.stringify(selector),
2290
+ contentSecurityPolicyString: JSON.stringify(contentSecurityPolicy),
2291
+ elementsString: JSON.stringify(elements, null, 2)
2292
+ };
2377
2293
  };
2378
2294
 
2379
- const join = (pathSeparator, ...parts) => {
2380
- return parts.join(pathSeparator);
2295
+ const getWebViews = extension => {
2296
+ const rawWebViews = extension.webViews || [];
2297
+ return rawWebViews.map(toWebView);
2381
2298
  };
2382
2299
 
2383
- const loadReadmeContent = async path => {
2384
- try {
2385
- const readmeUrl = join('/', path, 'README.md');
2386
- const readmeContent = await readFile(readmeUrl);
2387
- return readmeContent;
2388
- } catch (error) {
2389
- if (isEnoentError(error)) {
2390
- return '';
2391
- }
2392
- console.error(new VError(error, 'Failed to load Readme content'));
2393
- return `${error}`;
2394
- }
2300
+ const heading = {
2301
+ type: H2,
2302
+ className: DefinitionListItemHeading,
2303
+ childCount: 1
2395
2304
  };
2396
-
2397
- const getSavedSelectedFeature = savedState => {
2398
- if (savedState && typeof savedState === 'object' && 'selectedFeature' in savedState && typeof savedState.selectedFeature === 'string') {
2399
- return savedState.selectedFeature;
2400
- }
2401
- return Details;
2305
+ const pre = {
2306
+ type: Pre,
2307
+ className: DefinitionListItemValue,
2308
+ childCount: 1
2309
+ };
2310
+ const item = {
2311
+ type: Div,
2312
+ className: DefinitionListItem,
2313
+ childCount: 2
2314
+ };
2315
+ const getWebViewVirtualDom = webView => {
2316
+ const {
2317
+ id: id$1,
2318
+ selectorString,
2319
+ contentSecurityPolicyString,
2320
+ elementsString
2321
+ } = webView;
2322
+ const textId = id();
2323
+ const textSelector = selector();
2324
+ const textContentSecurityPolicy = contentSecurityPolicy();
2325
+ const textElements = elements();
2326
+ return [{
2327
+ type: Div,
2328
+ className: FeatureWebView,
2329
+ childCount: 5
2330
+ }, item, heading, text(textId), pre, text(id$1), item, heading, text(textSelector), pre, text(selectorString), item, heading, text(textContentSecurityPolicy), pre, text(contentSecurityPolicyString), item, heading, text(textElements), pre, text(elementsString)];
2402
2331
  };
2403
2332
 
2404
- const getSavedSelectedTab = savedState => {
2405
- if (savedState && typeof savedState === 'object' && 'selectedTab' in savedState && typeof savedState.selectedTab === 'string') {
2406
- return savedState.selectedTab;
2407
- }
2408
- return Details;
2333
+ const getFeatureWebViewsVirtualDom = extension => {
2334
+ const webViews$1 = getWebViews(extension);
2335
+ const heading = webViews();
2336
+ return [{
2337
+ type: Div,
2338
+ className: FeatureContent,
2339
+ childCount: 2
2340
+ }, ...getFeatureContentHeadingVirtualDom(heading), {
2341
+ type: Div,
2342
+ childCount: webViews$1.length
2343
+ }, ...webViews$1.flatMap(getWebViewVirtualDom)];
2409
2344
  };
2410
2345
 
2411
- const restoreState = savedState => {
2412
- const selectedTab = getSavedSelectedTab(savedState);
2413
- const selectedFeature = getSavedSelectedFeature(savedState);
2414
- return {
2415
- selectedFeature,
2416
- selectedTab
2417
- };
2346
+ const getFeatureContentVirtualDom = async (features, themesHtml, selectedFeature, extension) => {
2347
+ switch (selectedFeature) {
2348
+ case Theme:
2349
+ return await getFeatureThemesVirtualDom(themesHtml);
2350
+ case Commands:
2351
+ return getFeatureCommandsVirtualDom(extension);
2352
+ case JsonValidation:
2353
+ return getFeatureJsonValidationVirtualDom(extension);
2354
+ case ProgrammingLanguages:
2355
+ return getFeatureProgrammingLanguagesVirtualDom();
2356
+ case Settings:
2357
+ return getFeatureSettingsVirtualDom(extension);
2358
+ case WebViews:
2359
+ return getFeatureWebViewsVirtualDom(extension);
2360
+ default:
2361
+ return getFeatureNotImplementedVirtualDom();
2362
+ }
2418
2363
  };
2419
2364
 
2420
- const loadContent = async (state, platform, savedState) => {
2365
+ const getFeatureListItemVirtualDom = feature => {
2421
2366
  const {
2422
- uri,
2423
- width,
2424
- assetDir
2425
- } = state;
2426
- const id = uri.slice('extension-detail://'.length);
2427
- const extension = await getExtension(id, platform);
2428
- const readmeContent = await loadReadmeContent(extension.path);
2429
- const baseUrl = getBaseUrl(extension.path, platform);
2430
- const readmeHtml = await renderMarkdown(readmeContent, {
2431
- baseUrl
2432
- });
2433
- const sanitizedReadmeHtml = readmeHtml;
2434
- const normalizedReadmeHtml = sanitizedReadmeHtml;
2435
- const iconSrc = getIcon(extension, platform, assetDir);
2436
- const description = getDescription(extension);
2437
- const name = getName(extension);
2438
- const size = getViewletSize(width);
2367
+ label,
2368
+ selected,
2369
+ id
2370
+ } = feature;
2371
+ const className = selected ? 'Feature FeatureSelected' : Feature;
2372
+ return [{
2373
+ // TODO use role list item or tab
2374
+ type: Button,
2375
+ name: id,
2376
+ className,
2377
+ childCount: 1
2378
+ }, text(label)];
2379
+ };
2380
+
2381
+ const getFeatureListVirtualDom = features => {
2382
+ return [{
2383
+ // TODO use either list or tabs role
2384
+ type: Div,
2385
+ className: FeaturesList,
2386
+ childCount: features.length,
2387
+ onClick: HandleFeaturesClick
2388
+ }, ...features.flatMap(getFeatureListItemVirtualDom)];
2389
+ };
2390
+
2391
+ const getFeaturesVirtualDom = async (features, themesHtml, selectedFeature, extension) => {
2392
+ if (features.length === 0) {
2393
+ const none$1 = none();
2394
+ return [{
2395
+ type: Div,
2396
+ className: Features,
2397
+ childCount: 3
2398
+ }, text(none$1)];
2399
+ }
2400
+ return [{
2401
+ type: Div,
2402
+ className: Features,
2403
+ childCount: 3
2404
+ }, ...getFeatureListVirtualDom(features), {
2405
+ type: Div,
2406
+ className: mergeClassNames(Sash, SashVertical),
2407
+ childCount: 0
2408
+ }, ...(await getFeatureContentVirtualDom(features, themesHtml, selectedFeature, extension))];
2409
+ };
2410
+
2411
+ const getExtensionDetailContentVirtualDom = async (sanitizedReadmeHtml, themesHtml, selectedTab, features, displaySize, extensionId, extensionVersion, selectedFeature, extension, width) => {
2412
+ switch (selectedTab) {
2413
+ case Details:
2414
+ return await getDetailsVirtualDom(sanitizedReadmeHtml, displaySize, extensionId, extensionVersion, width);
2415
+ case Features$1:
2416
+ return await getFeaturesVirtualDom(features, themesHtml, selectedFeature, extension);
2417
+ case Changelog$1:
2418
+ return getChangelogVirtualDom();
2419
+ default:
2420
+ return [];
2421
+ }
2422
+ };
2423
+
2424
+ const getButtonVirtualDom = (message, onClick) => {
2425
+ return [{
2426
+ type: Button,
2427
+ className: mergeClassNames(Button$1, ButtonPrimary),
2428
+ onClick,
2429
+ childCount: 1
2430
+ }, text(message)];
2431
+ };
2432
+
2433
+ const getExtensionDetailHeaderActionsVirtualDom = () => {
2434
+ const dom = [{
2435
+ type: Div,
2436
+ className: ExtensionDetailHeaderActions,
2437
+ childCount: 2
2438
+ }, ...getButtonVirtualDom('Disable', HandleClickDisable), ...getButtonVirtualDom('Uninstall', HandleClickUninstall)];
2439
+ return dom;
2440
+ };
2441
+
2442
+ const getExtensionDetailHeaderVirtualDom = extensionDetail => {
2439
2443
  const {
2440
- selectedFeature,
2441
- selectedTab
2442
- } = restoreState(savedState);
2443
- const features = getFeatures(selectedFeature, extension);
2444
- const folderSize = await getFolderSize(extension.uri);
2445
- const entries = [{
2446
- key: 'Identifier',
2447
- value: 'abc'
2448
- }, {
2449
- key: 'Version',
2450
- value: '1.9.5'
2444
+ name,
2445
+ iconSrc,
2446
+ description
2447
+ } = extensionDetail;
2448
+ const dom = [{
2449
+ type: Div,
2450
+ className: ExtensionDetailHeader,
2451
+ childCount: 2
2451
2452
  }, {
2452
- key: 'Last Updated',
2453
- value: 'n/a'
2454
- }];
2455
- const secondEntries = [{
2456
- key: 'Published',
2457
- value: 'n/a'
2453
+ type: Img,
2454
+ className: ExtensionDetailIcon,
2455
+ alt: '',
2456
+ draggable: false,
2457
+ childCount: 0,
2458
+ src: iconSrc
2458
2459
  }, {
2459
- key: 'Last Released',
2460
- value: 'n/a'
2461
- }];
2462
- const categories = [{
2463
- id: 'themes',
2464
- label: 'Themes'
2465
- }];
2466
- const resources = [{
2467
- label: 'Marketplace',
2468
- url: '#'
2460
+ type: Div,
2461
+ className: ExtensionDetailHeaderDetails,
2462
+ childCount: 3
2469
2463
  }, {
2470
- label: 'Issues',
2471
- url: '#'
2464
+ type: Div,
2465
+ className: ExtensionDetailName,
2466
+ childCount: 1
2467
+ }, text(name), {
2468
+ type: Div,
2469
+ className: ExtensionDetailDescription,
2470
+ childCount: 1
2471
+ }, text(description), ...getExtensionDetailHeaderActionsVirtualDom()];
2472
+ return dom;
2473
+ };
2474
+
2475
+ const getTabs = selectedTab => {
2476
+ const tabs = [{
2477
+ label: 'Details',
2478
+ name: Details,
2479
+ selected: selectedTab === Details
2472
2480
  }, {
2473
- label: 'Repository',
2474
- url: '#'
2481
+ label: 'Features',
2482
+ name: Features$1,
2483
+ selected: selectedTab === Features$1
2475
2484
  }, {
2476
- label: 'License',
2477
- url: '#'
2485
+ label: 'Changelog',
2486
+ name: Changelog$1,
2487
+ selected: selectedTab === Changelog$1
2478
2488
  }];
2479
- return {
2480
- ...state,
2481
- selectedTab,
2482
- sanitizedReadmeHtml: normalizedReadmeHtml,
2483
- iconSrc,
2489
+ return tabs;
2490
+ };
2491
+
2492
+ const selectedClassName = mergeClassNames(ExtensionDetailTab, ExtensionDetailTabSelected);
2493
+ const defaultClassName = ExtensionDetailTab;
2494
+ const getTabVirtualDom = tab => {
2495
+ const {
2496
+ label,
2497
+ selected,
2498
+ name
2499
+ } = tab;
2500
+ const className = selected ? selectedClassName : defaultClassName;
2501
+ const ariaSelected = selected ? 'true' : 'false';
2502
+ return [{
2503
+ type: Button,
2504
+ role: Tab,
2484
2505
  name,
2485
- description,
2486
- sizeOnDisk: size,
2487
- entries,
2488
- secondEntries,
2489
- categories,
2490
- resources,
2491
- extension,
2492
- baseUrl,
2493
- features,
2494
- folderSize
2495
- };
2506
+ className,
2507
+ childCount: 1,
2508
+ tabIndex: -1,
2509
+ ariaSelected
2510
+ }, text(label)];
2496
2511
  };
2497
2512
 
2498
- const loadContent2 = async (state, savedState) => {
2499
- return loadContent(state, state.platform, savedState);
2513
+ const getTabsVirtualDom = tabs => {
2514
+ return [{
2515
+ type: Div,
2516
+ className: ExtensionDetailTabs,
2517
+ childCount: tabs.length,
2518
+ role: TabList,
2519
+ onClick: HandleTabsClick,
2520
+ tabIndex: 0
2521
+ }, ...tabs.flatMap(getTabVirtualDom)];
2522
+ };
2523
+
2524
+ const getClassNames = size => {
2525
+ switch (size) {
2526
+ case Small$1:
2527
+ return Small;
2528
+ case Normal$1:
2529
+ return Normal;
2530
+ case Large$1:
2531
+ return Large;
2532
+ default:
2533
+ return '';
2534
+ }
2535
+ };
2536
+
2537
+ const getExtensionDetailVirtualDom = async (extensionDetail, sanitizedReadmeHtml, selectedTab, newState) => {
2538
+ // TODO move this to view model so that rendering occurs like
2539
+ // 1. state
2540
+ // 2. view model
2541
+ // 3. virtual dom
2542
+ // 4. dom
2543
+ const themesHtml = newState?.selectedFeatureMarkdownDom || '';
2544
+ const selectedFeature = newState?.selectedFeature || '';
2545
+ const extension = newState?.extension || {};
2546
+ const features = getFeatures(selectedFeature, extension);
2547
+ const size = newState.folderSize || 0;
2548
+ const extensionId = newState?.extension?.id || 'n/a';
2549
+ const extensionVersion = newState?.extension?.version || 'n/a';
2550
+ const displaySize = getDisplaySize(size);
2551
+ const width = newState?.width || 500;
2552
+ const tabs = getTabs(selectedTab);
2553
+ const sizeValue = getViewletSize(newState?.width || 0);
2554
+ const sizeClass = getClassNames(sizeValue);
2555
+ const dom = [{
2556
+ type: Div,
2557
+ className: mergeClassNames(Viewlet, ExtensionDetail, sizeClass),
2558
+ childCount: 3
2559
+ }, ...getExtensionDetailHeaderVirtualDom(extensionDetail), ...getTabsVirtualDom(tabs), ...(await getExtensionDetailContentVirtualDom(sanitizedReadmeHtml, themesHtml, selectedTab, features, displaySize, extensionId, extensionVersion, selectedFeature, extension, width))];
2560
+ return dom;
2500
2561
  };
2501
2562
 
2502
2563
  const renderDom = async (oldState, newState) => {
@@ -2606,17 +2667,14 @@ const commandMap = {
2606
2667
  'ExtensionDetail.resize': resize,
2607
2668
  'ExtensionDetail.saveState': saveState,
2608
2669
  'ExtensionDetail.selectTab': wrapCommand(selectTab),
2609
- 'ExtensionDetail.terminate': terminate,
2610
- // deprecated
2611
- 'ExtensionDetail.getVirtualDom2': getExtensionDetailVirtualDom2,
2612
- 'ExtensionDetail.loadContent': wrapCommand(loadContent)
2670
+ 'ExtensionDetail.terminate': terminate
2613
2671
  };
2614
2672
 
2615
2673
  const listen = async () => {
2616
2674
  const rpc = await WebWorkerRpcClient.create({
2617
2675
  commandMap: commandMap
2618
2676
  });
2619
- set(RendererWorker, rpc);
2677
+ set(rpc);
2620
2678
  };
2621
2679
 
2622
2680
  const main = async () => {