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.
- package/built/backendutils.js +1 -2
- package/built/pxt.js +38 -269
- package/built/pxtblockly.js +36 -0
- package/built/pxtblocks.d.ts +10 -0
- package/built/pxtblocks.js +36 -0
- package/built/pxteditor.d.ts +0 -1
- package/built/pxtlib.d.ts +2 -18
- package/built/pxtlib.js +38 -269
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtblockly.js +2 -2
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/semantic.css +1 -1
- package/localtypings/pxtarget.d.ts +5 -18
- package/package.json +1 -1
- package/theme/pxt.less +0 -1
- package/theme/themes/pxt/globals/site.variables +0 -6
- package/theme/tutorial-sidebar.less +4 -0
- package/theme/tutorial.less +0 -10
- package/theme/tutorialCodeValidation.less +0 -73
package/built/backendutils.js
CHANGED
|
@@ -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 (
|
|
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,
|
|
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
|
-
|
|
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|
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
119374
|
-
|
|
119375
|
-
|
|
119376
|
-
|
|
119377
|
-
|
|
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
|
-
|
|
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 =
|
|
119483
|
-
|
|
119484
|
-
|
|
119485
|
-
|
|
119486
|
-
|
|
119487
|
-
|
|
119488
|
-
|
|
119489
|
-
|
|
119490
|
-
|
|
119491
|
-
|
|
119492
|
-
|
|
119493
|
-
|
|
119494
|
-
|
|
119495
|
-
|
|
119496
|
-
|
|
119497
|
-
|
|
119498
|
-
|
|
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 = {}));
|
package/built/pxtblockly.js
CHANGED
|
@@ -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 {
|
package/built/pxtblocks.d.ts
CHANGED
|
@@ -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;
|
package/built/pxtblocks.js
CHANGED
|
@@ -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 {
|
package/built/pxteditor.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|