pxt-core 9.1.9 → 9.1.10

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.
@@ -345,8 +345,7 @@ var pxt;
345
345
  "codeStop": "<!-- stop -->",
346
346
  "autoOpen": "<!-- autoOpen -->",
347
347
  "autoexpandOff": "<!-- autoexpandOff -->",
348
- "preferredEditor": "<!-- preferredEditor -->",
349
- "tutorialCodeValidation": "<!-- tutorialCodeValidation -->"
348
+ "preferredEditor": "<!-- preferredEditor -->"
350
349
  };
351
350
  function replaceAll(replIn, x, y) {
352
351
  return replIn.split(x).join(y);
package/built/pxt.js CHANGED
@@ -106227,8 +106227,7 @@ var pxt;
106227
106227
  "codeStop": "<!-- stop -->",
106228
106228
  "autoOpen": "<!-- autoOpen -->",
106229
106229
  "autoexpandOff": "<!-- autoexpandOff -->",
106230
- "preferredEditor": "<!-- preferredEditor -->",
106231
- "tutorialCodeValidation": "<!-- tutorialCodeValidation -->"
106230
+ "preferredEditor": "<!-- preferredEditor -->"
106232
106231
  };
106233
106232
  function replaceAll(replIn, x, y) {
106234
106233
  return replIn.split(x).join(y);
@@ -118857,7 +118856,7 @@ var pxt;
118857
118856
  var pxt;
118858
118857
  (function (pxt) {
118859
118858
  var tutorial;
118860
- (function (tutorial) {
118859
+ (function (tutorial_1) {
118861
118860
  const _h2Regex = /^##[^#](.*)$([\s\S]*?)(?=^##[^#]|$(?![\r\n]))/gmi;
118862
118861
  const _h3Regex = /^###[^#](.*)$([\s\S]*?)(?=^###[^#]|$(?![\r\n]))/gmi;
118863
118862
  function parseTutorial(tutorialmd) {
@@ -118867,13 +118866,7 @@ var pxt;
118867
118866
  if (!steps)
118868
118867
  return undefined; // error parsing steps
118869
118868
  // collect code and infer editor
118870
- const { code, templateCode, editor, language, jres, assetJson, customTs, tutorialValidationRulesStr, simThemeJson } = computeBodyMetadata(body);
118871
- // parses tutorial rules string into a map of rules and enablement flag
118872
- let tutorialValidationRules;
118873
- if (metadata.tutorialCodeValidation) {
118874
- tutorialValidationRules = pxt.Util.jsonTryParse(tutorialValidationRulesStr);
118875
- categorizingValidationRules(tutorialValidationRules, title);
118876
- }
118869
+ const { code, templateCode, editor, language, jres, assetJson, customTs, simThemeJson } = computeBodyMetadata(body);
118877
118870
  // noDiffs legacy
118878
118871
  if (metadata.diffs === true // enabled in tutorial
118879
118872
  || (metadata.diffs !== false && metadata.noDiffs !== true // not disabled
@@ -118891,7 +118884,6 @@ var pxt;
118891
118884
  step.contentMd = stripHiddenSnippets(step.contentMd);
118892
118885
  step.headerContentMd = stripHiddenSnippets(step.headerContentMd);
118893
118886
  step.hintContentMd = stripHiddenSnippets(step.hintContentMd);
118894
- step.requiredBlockMd = stripHiddenSnippets(step.requiredBlockMd);
118895
118887
  });
118896
118888
  return {
118897
118889
  editor,
@@ -118905,17 +118897,16 @@ var pxt;
118905
118897
  jres,
118906
118898
  assetFiles,
118907
118899
  customTs,
118908
- tutorialValidationRules,
118909
118900
  globalBlockConfig,
118910
118901
  globalValidationConfig,
118911
118902
  simTheme
118912
118903
  };
118913
118904
  }
118914
- tutorial.parseTutorial = parseTutorial;
118905
+ tutorial_1.parseTutorial = parseTutorial;
118915
118906
  function getMetadataRegex() {
118916
- return /``` *(sim|block|blocks|filterblocks|spy|ghost|typescript|ts|js|javascript|template|python|jres|assetjson|customts|tutorialValidationRules|requiredTutorialBlock|simtheme)\s*\n([\s\S]*?)\n```/gmi;
118907
+ return /``` *(sim|block|blocks|filterblocks|spy|ghost|typescript|ts|js|javascript|template|python|jres|assetjson|customts|simtheme)\s*\n([\s\S]*?)\n```/gmi;
118917
118908
  }
118918
- tutorial.getMetadataRegex = getMetadataRegex;
118909
+ tutorial_1.getMetadataRegex = getMetadataRegex;
118919
118910
  function computeBodyMetadata(body) {
118920
118911
  // collect code and infer editor
118921
118912
  let editor = undefined;
@@ -118928,7 +118919,6 @@ var pxt;
118928
118919
  let assetJson;
118929
118920
  let customTs;
118930
118921
  let simThemeJson;
118931
- let tutorialValidationRulesStr;
118932
118922
  // Concatenate all blocks in separate code blocks and decompile so we can detect what blocks are used (for the toolbox)
118933
118923
  body
118934
118924
  .replace(/((?!.)\s)+/g, "\n")
@@ -118938,7 +118928,6 @@ var pxt;
118938
118928
  case "blocks":
118939
118929
  case "blockconfig.local":
118940
118930
  case "blockconfig.global":
118941
- case "requiredTutorialBlock":
118942
118931
  case "filterblocks":
118943
118932
  if (!checkTutorialEditor(pxt.BLOCKS_PROJECT_NAME))
118944
118933
  return undefined;
@@ -118973,9 +118962,6 @@ var pxt;
118973
118962
  customTs = m2;
118974
118963
  m2 = "";
118975
118964
  break;
118976
- case "tutorialValidationRules":
118977
- tutorialValidationRulesStr = m2;
118978
- break;
118979
118965
  }
118980
118966
  code.push(m1 == "python" ? `\n${m2}\n` : `{\n${m2}\n}`);
118981
118967
  idx++;
@@ -118991,7 +118977,6 @@ var pxt;
118991
118977
  jres,
118992
118978
  assetJson,
118993
118979
  customTs,
118994
- tutorialValidationRulesStr,
118995
118980
  simThemeJson
118996
118981
  };
118997
118982
  function checkTutorialEditor(expected) {
@@ -119102,7 +119087,7 @@ ${code}
119102
119087
  let stepInfo = [];
119103
119088
  markdown.replace(stepRegex, function (match, flags, step) {
119104
119089
  step = step.trim();
119105
- let { header, hint, requiredBlocks } = parseTutorialHint(step, metadata && metadata.explicitHints, metadata.tutorialCodeValidation);
119090
+ let { header, hint } = parseTutorialHint(step, metadata && metadata.explicitHints);
119106
119091
  const blockConfig = parseTutorialBlockConfig("local", step);
119107
119092
  const validationConfig = parseTutorialValidationConfig("local", step);
119108
119093
  // if title is not hidden ("{TITLE HERE}"), strip flags
@@ -119126,8 +119111,6 @@ ${code}
119126
119111
  info.resetDiff = true;
119127
119112
  if (hint)
119128
119113
  info.hintContentMd = hint;
119129
- if (metadata.tutorialCodeValidation && requiredBlocks)
119130
- info.requiredBlockMd = requiredBlocks;
119131
119114
  stepInfo.push(info);
119132
119115
  return "";
119133
119116
  });
@@ -119137,11 +119120,10 @@ ${code}
119137
119120
  }
119138
119121
  return stepInfo;
119139
119122
  }
119140
- function parseTutorialHint(step, explicitHints, tutorialCodeValidationEnabled) {
119123
+ function parseTutorialHint(step, explicitHints) {
119141
119124
  // remove hidden code sections
119142
119125
  step = stripHiddenSnippets(step);
119143
119126
  let header = step, hint;
119144
- let requiredBlocks;
119145
119127
  if (explicitHints) {
119146
119128
  // hint is explicitly set with hint syntax "#### ~ tutorialhint" and terminates at the next heading
119147
119129
  const hintTextRegex = /#+ ~ tutorialhint([\s\S]*)/i;
@@ -119157,17 +119139,9 @@ ${code}
119157
119139
  if (hintText && hintText.length > 2) {
119158
119140
  header = hintText[1].trim();
119159
119141
  hint = hintText[2].trim();
119160
- if (tutorialCodeValidationEnabled) {
119161
- let hintSnippet = hintText[2].trim();
119162
- hintSnippet = hintSnippet.replace(/``` *(requiredTutorialBlock)\s*\n([\s\S]*?)\n```/gmi, function (m0, m1, m2) {
119163
- requiredBlocks = `{\n${m2}\n}`;
119164
- return "";
119165
- });
119166
- hint = hintSnippet;
119167
- }
119168
119142
  }
119169
119143
  }
119170
- return { header, hint, requiredBlocks };
119144
+ return { header, hint };
119171
119145
  }
119172
119146
  function parseTutorialBlockConfig(scope, content) {
119173
119147
  let blockConfig = {
@@ -119200,17 +119174,6 @@ ${code}
119200
119174
  });
119201
119175
  return { validatorsMetadata: sectionedMetadata };
119202
119176
  }
119203
- function categorizingValidationRules(listOfRules, title) {
119204
- const ruleNames = Object.keys(listOfRules);
119205
- for (let i = 0; i < ruleNames.length; i++) {
119206
- const setValidationRule = {
119207
- ruleName: ruleNames[i],
119208
- enabled: listOfRules[ruleNames[i]] ? 'true' : 'false',
119209
- tutorial: title,
119210
- };
119211
- pxt.tickEvent('tutorial.validation.setValidationRules', setValidationRule);
119212
- }
119213
- }
119214
119177
  /* Remove hidden snippets from text */
119215
119178
  function stripHiddenSnippets(str) {
119216
119179
  if (!str)
@@ -119272,7 +119235,7 @@ ${code}
119272
119235
  }
119273
119236
  }
119274
119237
  }
119275
- tutorial.highlight = highlight;
119238
+ tutorial_1.highlight = highlight;
119276
119239
  function getTutorialOptions(md, tutorialId, filename, reportId, recipe) {
119277
119240
  var _a;
119278
119241
  const tutorialInfo = pxt.tutorial.parseTutorial(md);
@@ -119297,14 +119260,13 @@ ${code}
119297
119260
  jres: tutorialInfo.jres,
119298
119261
  assetFiles: tutorialInfo.assetFiles,
119299
119262
  customTs: tutorialInfo.customTs,
119300
- tutorialValidationRules: tutorialInfo.tutorialValidationRules,
119301
119263
  globalBlockConfig: tutorialInfo.globalBlockConfig,
119302
119264
  globalValidationConfig: tutorialInfo.globalValidationConfig,
119303
119265
  simTheme: tutorialInfo.simTheme,
119304
119266
  };
119305
119267
  return { options: tutorialOptions, editor: tutorialInfo.editor };
119306
119268
  }
119307
- tutorial.getTutorialOptions = getTutorialOptions;
119269
+ tutorial_1.getTutorialOptions = getTutorialOptions;
119308
119270
  function parseCachedTutorialInfo(json, id) {
119309
119271
  let cachedInfo = pxt.Util.jsonTryParse(json);
119310
119272
  if (!cachedInfo)
@@ -119325,7 +119287,7 @@ ${code}
119325
119287
  }
119326
119288
  }).catch((err) => { });
119327
119289
  }
119328
- tutorial.parseCachedTutorialInfo = parseCachedTutorialInfo;
119290
+ tutorial_1.parseCachedTutorialInfo = parseCachedTutorialInfo;
119329
119291
  function resolveLocalizedMarkdown(ghid, files, fileName) {
119330
119292
  // if non-default language, find localized file if any
119331
119293
  const mfn = (fileName || ghid.fileName || "README") + ".md";
@@ -119344,7 +119306,7 @@ ${code}
119344
119306
  md = md || files[mfn];
119345
119307
  return md;
119346
119308
  }
119347
- tutorial.resolveLocalizedMarkdown = resolveLocalizedMarkdown;
119309
+ tutorial_1.resolveLocalizedMarkdown = resolveLocalizedMarkdown;
119348
119310
  function parseAssetJson(json) {
119349
119311
  if (!json)
119350
119312
  return undefined;
@@ -119356,7 +119318,7 @@ ${code}
119356
119318
  [pxt.IMAGES_CODE]: files[pxt.IMAGES_CODE]
119357
119319
  };
119358
119320
  }
119359
- tutorial.parseAssetJson = parseAssetJson;
119321
+ tutorial_1.parseAssetJson = parseAssetJson;
119360
119322
  function parseSimThemeJson(json) {
119361
119323
  const pxtJson = pxt.Util.jsonTryParse(json);
119362
119324
  if (!pxtJson)
@@ -119370,225 +119332,32 @@ ${code}
119370
119332
  }
119371
119333
  return res;
119372
119334
  }
119373
- tutorial.parseSimThemeJson = parseSimThemeJson;
119374
- })(tutorial = pxt.tutorial || (pxt.tutorial = {}));
119375
- })(pxt || (pxt = {}));
119376
- var pxt;
119377
- (function (pxt) {
119378
- var tutorial;
119379
- (function (tutorial_1) {
119380
- /**
119381
- * Check the user's code to the map of tutorial validation rules from TutorialOptions and returns an array of TutorialRuleStatus
119382
- * @param tutorial the tutorial
119383
- * @param workspaceBlocks Blockly blocks used of workspace
119384
- * @param blockinfo Typescripts of the workspace
119385
- * @return A TutorialRuleStatus
119386
- */
119387
- async function validate(tutorial, workspaceBlocks, blockinfo) {
119388
- const listOfRules = tutorial.tutorialValidationRules;
119389
- let TutorialRuleStatuses = classifyRules(listOfRules);
119390
- // Check to if there are rules to valdiate and to see if there are blocks are in the workspace to compare to
119391
- if (TutorialRuleStatuses.length > 0 && workspaceBlocks.length > 0) {
119392
- // User blocks
119393
- const userBlockTypes = workspaceBlocks.map(b => b.type);
119394
- const usersBlockUsed = blockCount(userBlockTypes);
119395
- // Tutorial blocks
119396
- const { tutorialStepInfo, tutorialStep } = tutorial;
119397
- const step = tutorialStepInfo[tutorialStep];
119398
- const indexdb = await tutorialBlockList(tutorial, step);
119399
- const tutorialBlockUsed = extractBlockSnippet(tutorial, indexdb);
119400
- for (let i = 0; i < TutorialRuleStatuses.length; i++) {
119401
- let currRuleToValidate = TutorialRuleStatuses[i];
119402
- const ruleName = TutorialRuleStatuses[i].ruleName;
119403
- const isRuleEnabled = TutorialRuleStatuses[i].ruleTurnOn;
119404
- if (isRuleEnabled) {
119405
- switch (ruleName) {
119406
- case "exact":
119407
- currRuleToValidate = validateExactNumberOfBlocks(usersBlockUsed, tutorialBlockUsed, currRuleToValidate);
119408
- break;
119409
- case "atleast":
119410
- currRuleToValidate = validateAtleastOneBlocks(usersBlockUsed, tutorialBlockUsed, currRuleToValidate);
119411
- break;
119412
- case "required":
119413
- const requiredBlocksList = extractRequiredBlockSnippet(tutorial, indexdb);
119414
- currRuleToValidate = validateMeetRequiredBlocks(usersBlockUsed, requiredBlocksList, currRuleToValidate);
119415
- break;
119416
- }
119417
- }
119418
- }
119419
- }
119420
- return TutorialRuleStatuses;
119421
- }
119422
- tutorial_1.validate = validate;
119423
- /**
119424
- * Gives each rule from the markdown file a TutorialRuleStatus
119425
- * @param listOfRules a map of rules from makrdown file
119426
- * @return An array of TutorialRuleStatus
119427
- */
119428
- function classifyRules(listOfRules) {
119429
- let listOfRuleStatuses = [];
119430
- if (listOfRules != undefined) {
119431
- const ruleNames = Object.keys(listOfRules);
119432
- for (let i = 0; i < ruleNames.length; i++) {
119433
- const currRule = ruleNames[i];
119434
- const ruleVal = listOfRules[currRule];
119435
- const currRuleStatus = { ruleName: currRule, ruleTurnOn: ruleVal };
119436
- listOfRuleStatuses.push(currRuleStatus);
119437
- }
119438
- }
119439
- return listOfRuleStatuses;
119440
- }
119441
- /**
119442
- * Loops through an array of blocks and returns a map of blocks and the count for that block
119443
- * @param arr a string array of blocks
119444
- * @return a map <Block type, frequency>
119445
- */
119446
- function blockCount(arr) {
119447
- let frequencyMap = {};
119448
- for (let i = 0; i < arr.length; i++) {
119449
- if (!frequencyMap[arr[i]]) {
119450
- frequencyMap[arr[i]] = 0;
119451
- }
119452
- frequencyMap[arr[i]] = frequencyMap[arr[i]] + 1;
119453
- }
119454
- return frequencyMap;
119455
- }
119456
- /**
119457
- * Returns information from index database
119458
- * @param tutorial Typescripts of the workspace
119459
- * @param step the current tutorial step
119460
- * @return indexdb's tutorial code snippets
119461
- */
119462
- function tutorialBlockList(tutorial, step) {
119463
- return pxt.BrowserUtils.tutorialInfoDbAsync()
119464
- .then(db => db.getAsync(tutorial.tutorial, tutorial.tutorialCode)
119465
- .then(entry => {
119466
- if (entry === null || entry === void 0 ? void 0 : entry.snippets) {
119467
- return Promise.resolve(entry.snippets);
119468
- }
119469
- else {
119470
- return Promise.resolve(undefined);
119471
- }
119472
- }));
119335
+ tutorial_1.parseSimThemeJson = parseSimThemeJson;
119336
+ async function getTutorialHighlightedBlocks(tutorial) {
119337
+ const db = await pxt.BrowserUtils.tutorialInfoDbAsync();
119338
+ const entry = await db.getAsync(tutorial.tutorial, tutorial.tutorialCode);
119339
+ return entry === null || entry === void 0 ? void 0 : entry.highlightBlocks;
119473
119340
  }
119474
- /**
119475
- * Extract the tutorial blocks used from code snippet
119476
- * @param tutorial tutorial info
119477
- * @param indexdb database from index
119478
- * @return the tutorial blocks used for the current step
119479
- */
119480
- function extractBlockSnippet(tutorial, indexdb) {
119341
+ tutorial_1.getTutorialHighlightedBlocks = getTutorialHighlightedBlocks;
119342
+ function getTutorialStepHash(tutorial) {
119481
119343
  const { tutorialStepInfo, tutorialStep } = tutorial;
119482
- const body = tutorial.tutorialStepInfo[tutorialStep].hintContentMd;
119483
- let hintCode = "";
119484
- if (body != undefined) {
119485
- body.replace(/((?!.)\s)+/g, "\n").replace(/``` *(block|blocks)\s*\n([\s\S]*?)\n```/gmi, function (m0, m1, m2) {
119486
- hintCode = `{\n${m2}\n}`;
119487
- return "";
119488
- });
119489
- }
119490
- const snippetStepKey = pxt.BrowserUtils.getTutorialCodeHash([hintCode]);
119491
- let blockMap = {};
119492
- if (indexdb != undefined) {
119493
- blockMap = indexdb[snippetStepKey];
119494
- }
119495
- return blockMap;
119496
- }
119497
- /**
119498
- * Extract the required tutorial blocks from code snippet
119499
- * @param tutorial tutorial info
119500
- * @param indexdb database from index
119501
- * @return the tutorial blocks used for the current step
119502
- */
119503
- function extractRequiredBlockSnippet(tutorial, indexdb) {
119504
- const { tutorialStep } = tutorial;
119505
- const body = tutorial.tutorialStepInfo[tutorialStep].requiredBlockMd;
119506
- const snippetStepKey = pxt.BrowserUtils.getTutorialCodeHash([body]);
119507
- let blockMap = {};
119508
- if (indexdb != undefined) {
119509
- blockMap = indexdb[snippetStepKey];
119510
- }
119511
- return blockMap;
119512
- }
119513
- /**
119514
- * Strict Rule: Checks if the all required number of blocks for a tutorial step is used, returns a TutorialRuleStatus
119515
- * @param usersBlockUsed an array of strings
119516
- * @param tutorialBlockUsed the next available index
119517
- * @param currRule the current rule with its TutorialRuleStatus
119518
- * @return a tutorial rule status for currRule
119519
- */
119520
- function validateExactNumberOfBlocks(usersBlockUsed, tutorialBlockUsed, currRule) {
119521
- currRule.isStrict = true;
119522
- const userBlockKeys = Object.keys(usersBlockUsed);
119523
- let tutorialBlockKeys = [];
119524
- let blockIds = [];
119525
- if (tutorialBlockUsed != undefined) {
119526
- tutorialBlockKeys = Object.keys(tutorialBlockUsed);
119527
- }
119528
- let isValid = userBlockKeys.length >= tutorialBlockKeys.length; // user has enough blocks
119529
- const message = lf("These are the blocks you seem to be missing:");
119530
- for (let i = 0; i < tutorialBlockKeys.length; i++) {
119531
- let tutorialBlockKey = tutorialBlockKeys[i];
119532
- if (!usersBlockUsed[tutorialBlockKey] // user did not use a specific block or
119533
- || usersBlockUsed[tutorialBlockKey] < tutorialBlockUsed[tutorialBlockKey]) { // user did not use enough of a certain block
119534
- blockIds.push(tutorialBlockKey);
119535
- isValid = false;
119536
- }
119537
- }
119538
- currRule.ruleMessage = message;
119539
- currRule.ruleStatus = isValid;
119540
- currRule.blockIds = blockIds;
119541
- return currRule;
119542
- }
119543
- /**
119544
- * Passive Rule: Checks if the users has at least one block type for each rule
119545
- * @param usersBlockUsed an array of strings
119546
- * @param tutorialBlockUsed the next available index
119547
- * @param currRule the current rule with its TutorialRuleStatus
119548
- * @return a tutorial rule status for currRule
119549
- */
119550
- function validateAtleastOneBlocks(usersBlockUsed, tutorialBlockUsed, currRule) {
119551
- const userBlockKeys = Object.keys(usersBlockUsed);
119552
- const tutorialBlockKeys = Object.keys(tutorialBlockUsed !== null && tutorialBlockUsed !== void 0 ? tutorialBlockUsed : {});
119553
- let isValid = userBlockKeys.length >= tutorialBlockKeys.length; // user has enough blocks
119554
- for (let i = 0; i < tutorialBlockKeys.length; i++) {
119555
- let tutorialBlockKey = tutorialBlockKeys[i];
119556
- if (!usersBlockUsed[tutorialBlockKey]) { // user did not use a specific block
119557
- isValid = false;
119558
- break;
119559
- }
119560
- }
119561
- currRule.ruleStatus = isValid;
119562
- return currRule;
119563
- }
119564
- /**
119565
- * Strict Rule: Checks if the all required number of blocks for a tutorial step is used, returns a TutorialRuleStatus
119566
- * @param usersBlockUsed an array of strings
119567
- * @param tutorialBlockUsed the next available index
119568
- * @param currRule the current rule with its TutorialRuleStatus
119569
- * @return a tutorial rule status for currRule
119570
- */
119571
- function validateMeetRequiredBlocks(usersBlockUsed, requiredBlocks, currRule) {
119572
- currRule.isStrict = true;
119573
- const userBlockKeys = Object.keys(usersBlockUsed);
119574
- let requiredBlockKeys = [];
119575
- let blockIds = [];
119576
- if (requiredBlocks != undefined) {
119577
- requiredBlockKeys = Object.keys(requiredBlocks);
119578
- }
119579
- let isValid = true;
119580
- const message = lf("You are required to have the following block:");
119581
- for (let i = 0; i < requiredBlockKeys.length; i++) {
119582
- let requiredBlockKey = requiredBlockKeys[i];
119583
- if (!usersBlockUsed[requiredBlockKey]) {
119584
- blockIds.push(requiredBlockKey);
119585
- isValid = false;
119586
- }
119587
- }
119588
- currRule.ruleMessage = message;
119589
- currRule.ruleStatus = isValid;
119590
- currRule.blockIds = blockIds;
119591
- return currRule;
119344
+ const body = tutorialStepInfo[tutorialStep].hintContentMd;
119345
+ const codeSnippets = getBlockSnippetCode(body);
119346
+ return pxt.BrowserUtils.getTutorialCodeHash(codeSnippets);
119347
+ }
119348
+ tutorial_1.getTutorialStepHash = getTutorialStepHash;
119349
+ /** TODO: if this gets exported, we probably want to consider generalizing to 'parseMarkdownSnippets'
119350
+ * that returns { snippetName: string[] }; e.g.
119351
+ * { "blocks": ["block snippet 1", "block snippet 2"], "package": ["deplist 1"] }
119352
+ * need to cover getMetadataRegex + stripHiddenSnippets types, maybe more if any happen to be around.
119353
+ **/
119354
+ function getBlockSnippetCode(mdSnippet) {
119355
+ let hintCode = [];
119356
+ mdSnippet === null || mdSnippet === void 0 ? void 0 : mdSnippet.replace(/((?!.)\s)+/g, "\n").replace(/``` *(block|blocks)\s*\n([\s\S]*?)\n```/gim, function (m0, m1, m2) {
119357
+ hintCode.push(`{\n${m2}\n}`);
119358
+ return "";
119359
+ });
119360
+ return hintCode;
119592
119361
  }
119593
119362
  })(tutorial = pxt.tutorial || (pxt.tutorial = {}));
119594
119363
  })(pxt || (pxt = {}));
@@ -11520,6 +11520,42 @@ var pxt;
11520
11520
  blocks.initMathRoundBlock = initMathRoundBlock;
11521
11521
  })(blocks = pxt.blocks || (pxt.blocks = {}));
11522
11522
  })(pxt || (pxt = {}));
11523
+ var pxt;
11524
+ (function (pxt) {
11525
+ var blocks;
11526
+ (function (blocks) {
11527
+ function validateBlocksExist({ usedBlocks, requiredBlockCounts }) {
11528
+ let missingBlocks = [];
11529
+ let disabledBlocks = [];
11530
+ let insufficientBlocks = [];
11531
+ const userBlocksEnabledByType = usedBlocks === null || usedBlocks === void 0 ? void 0 : usedBlocks.reduce((acc, block) => {
11532
+ acc[block.type] = (acc[block.type] || 0) + (block.isEnabled() ? 1 : 0);
11533
+ return acc;
11534
+ }, {});
11535
+ for (const [requiredBlockId, requiredCount] of Object.entries(requiredBlockCounts || {})) {
11536
+ const countForBlock = userBlocksEnabledByType[requiredBlockId];
11537
+ if (countForBlock === undefined) {
11538
+ // user did not use a specific block
11539
+ missingBlocks.push(requiredBlockId);
11540
+ }
11541
+ else if (!countForBlock) {
11542
+ // all instances of block are disabled
11543
+ disabledBlocks.push(requiredBlockId);
11544
+ }
11545
+ else if (countForBlock < requiredCount) {
11546
+ // instances of block exists, but not enough.
11547
+ insufficientBlocks.push(requiredBlockId);
11548
+ }
11549
+ }
11550
+ return {
11551
+ missingBlocks,
11552
+ disabledBlocks,
11553
+ insufficientBlocks,
11554
+ };
11555
+ }
11556
+ blocks.validateBlocksExist = validateBlocksExist;
11557
+ })(blocks = pxt.blocks || (pxt.blocks = {}));
11558
+ })(pxt || (pxt = {}));
11523
11559
  var pxtblockly;
11524
11560
  (function (pxtblockly) {
11525
11561
  class FieldBase extends Blockly.Field {
@@ -392,6 +392,16 @@ declare namespace pxt.blocks {
392
392
  declare namespace pxt.blocks {
393
393
  function initMathRoundBlock(): void;
394
394
  }
395
+ declare namespace pxt.blocks {
396
+ function validateBlocksExist({ usedBlocks, requiredBlockCounts }: {
397
+ usedBlocks: Blockly.Block[];
398
+ requiredBlockCounts: pxt.Map<number>;
399
+ }): {
400
+ missingBlocks: string[];
401
+ disabledBlocks: string[];
402
+ insufficientBlocks: string[];
403
+ };
404
+ }
395
405
  declare namespace pxtblockly {
396
406
  abstract class FieldBase<U> extends Blockly.Field implements Blockly.FieldCustom {
397
407
  isFieldCustom_: true;
@@ -7958,6 +7958,42 @@ var pxt;
7958
7958
  blocks.initMathRoundBlock = initMathRoundBlock;
7959
7959
  })(blocks = pxt.blocks || (pxt.blocks = {}));
7960
7960
  })(pxt || (pxt = {}));
7961
+ var pxt;
7962
+ (function (pxt) {
7963
+ var blocks;
7964
+ (function (blocks) {
7965
+ function validateBlocksExist({ usedBlocks, requiredBlockCounts }) {
7966
+ let missingBlocks = [];
7967
+ let disabledBlocks = [];
7968
+ let insufficientBlocks = [];
7969
+ const userBlocksEnabledByType = usedBlocks === null || usedBlocks === void 0 ? void 0 : usedBlocks.reduce((acc, block) => {
7970
+ acc[block.type] = (acc[block.type] || 0) + (block.isEnabled() ? 1 : 0);
7971
+ return acc;
7972
+ }, {});
7973
+ for (const [requiredBlockId, requiredCount] of Object.entries(requiredBlockCounts || {})) {
7974
+ const countForBlock = userBlocksEnabledByType[requiredBlockId];
7975
+ if (countForBlock === undefined) {
7976
+ // user did not use a specific block
7977
+ missingBlocks.push(requiredBlockId);
7978
+ }
7979
+ else if (!countForBlock) {
7980
+ // all instances of block are disabled
7981
+ disabledBlocks.push(requiredBlockId);
7982
+ }
7983
+ else if (countForBlock < requiredCount) {
7984
+ // instances of block exists, but not enough.
7985
+ insufficientBlocks.push(requiredBlockId);
7986
+ }
7987
+ }
7988
+ return {
7989
+ missingBlocks,
7990
+ disabledBlocks,
7991
+ insufficientBlocks,
7992
+ };
7993
+ }
7994
+ blocks.validateBlocksExist = validateBlocksExist;
7995
+ })(blocks = pxt.blocks || (pxt.blocks = {}));
7996
+ })(pxt || (pxt = {}));
7961
7997
  var pxtblockly;
7962
7998
  (function (pxtblockly) {
7963
7999
  class FieldBase extends Blockly.Field {
@@ -256,7 +256,6 @@ declare namespace pxt.editor {
256
256
  showTutorialHint(): void;
257
257
  isTutorial(): boolean;
258
258
  onTutorialLoaded(): void;
259
- setTutorialCodeStatus(step: number, status: pxt.tutorial.TutorialRuleStatus[]): void;
260
259
  pokeUserActivity(): void;
261
260
  stopPokeUserActivity(): void;
262
261
  clearUserPoke(): void;
package/built/pxtlib.d.ts CHANGED
@@ -2983,24 +2983,8 @@ declare namespace pxt.tutorial {
2983
2983
  function resolveLocalizedMarkdown(ghid: pxt.github.ParsedRepo, files: pxt.Map<string>, fileName?: string): string;
2984
2984
  function parseAssetJson(json: string): pxt.Map<string>;
2985
2985
  function parseSimThemeJson(json: string): Partial<pxt.PackageConfig>;
2986
- }
2987
- declare namespace pxt.tutorial {
2988
- interface TutorialRuleStatus {
2989
- ruleName: string;
2990
- ruleTurnOn: boolean;
2991
- ruleStatus?: boolean;
2992
- ruleMessage?: string;
2993
- isStrict?: boolean;
2994
- blockIds?: string[];
2995
- }
2996
- /**
2997
- * Check the user's code to the map of tutorial validation rules from TutorialOptions and returns an array of TutorialRuleStatus
2998
- * @param tutorial the tutorial
2999
- * @param workspaceBlocks Blockly blocks used of workspace
3000
- * @param blockinfo Typescripts of the workspace
3001
- * @return A TutorialRuleStatus
3002
- */
3003
- function validate(tutorial: TutorialOptions, workspaceBlocks: Blockly.Block[], blockinfo: pxtc.BlocksInfo): Promise<TutorialRuleStatus[]>;
2986
+ function getTutorialHighlightedBlocks(tutorial: TutorialOptions): Promise<pxt.Map<pxt.Map<number>> | undefined>;
2987
+ function getTutorialStepHash(tutorial: TutorialOptions): string;
3004
2988
  }
3005
2989
  declare namespace ts.pxtc {
3006
2990
  function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string;