pxt-core 9.3.13 → 9.3.15
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/README.md +11 -0
- package/built/cli.js +7 -2
- package/built/pxt.js +57 -3
- package/built/pxtblockly.js +97 -57
- package/built/pxtblocks.d.ts +59 -21
- package/built/pxtblocks.js +97 -57
- package/built/pxtlib.d.ts +22 -0
- package/built/pxtlib.js +50 -1
- package/built/target.js +1 -1
- package/built/tests/blocksrunner.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/multiplayer/js/{main.78cecdcb.js → main.75ca8c58.js} +2 -2
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.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/runnerembed.js +1 -0
- package/built/web/semantic.css +1 -1
- package/built/web/skillmap/js/{main.8222bb34.js → main.236bd49e.js} +2 -2
- package/built/web/teachertool/css/main.e9386f28.css +1 -0
- package/built/web/teachertool/js/{main.3a94a341.js → main.8aa6604c.js} +2 -2
- package/localtypings/projectheader.d.ts +21 -0
- package/{built → localtypings}/pxteditor.d.ts +1095 -1090
- package/package.json +1 -1
- package/react-common/components/controls/MenuDropdown.tsx +5 -2
- package/react-common/components/util.tsx +1 -1
- package/theme/pxt.less +1 -0
- package/theme/themepacks.less +41 -0
- package/webapp/public/embed.js +1 -1
- package/webapp/public/multiplayer.html +1 -1
- package/webapp/public/skillmap.html +1 -1
- package/webapp/public/teachertool.html +1 -3
- package/built/pxteditor.js +0 -1834
- package/built/pxtrunner.d.ts +0 -151
- package/built/pxtrunner.js +0 -2626
- package/built/web/pxteditor.js +0 -1
- package/built/web/pxtrunner.js +0 -1
- package/built/web/teachertool/css/main.59776cd1.css +0 -1
package/README.md
CHANGED
|
@@ -114,6 +114,17 @@ npm install -g svgo
|
|
|
114
114
|
svgo svgicons/myicon.svg
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Shared Styling
|
|
118
|
+
|
|
119
|
+
When adding a CSS color or other style element that will be shared across editor targets (e.g. micro:bit, Arcade) and sub-applications (a.k.a. "CRAs", like skillmap, teachertool, etc.). Declare a CSS variable for it in `theme/themepacks.less`:
|
|
120
|
+
|
|
121
|
+
1. Add the new variable to the `:root` pseudo-class. Choose a reasonable default value according to the guidlines in the file.
|
|
122
|
+
2. Add the new variable to all theme classes defined in that file. At the time of this writing, only `theme-highcontrast` is defined. Choose a value that works well for the given theme.
|
|
123
|
+
3. Add the new variable to the theme overrides for each target. This will be done in the target repo's `theme/themepacks.less` file (e.g. pxt-microbit, pxt-arcade).
|
|
124
|
+
|
|
125
|
+
Variables declared this way will be available to CRAs at runtime, and they will be initialized with the override values defined by the target in which they're running.
|
|
126
|
+
|
|
127
|
+
|
|
117
128
|
### Documentation Highlighting
|
|
118
129
|
|
|
119
130
|
In the documentation, highlighting of code snippets uses highlight.js (hljs).
|
package/built/cli.js
CHANGED
|
@@ -2403,8 +2403,13 @@ async function buildAndWatchTargetAsync(includeSourceMaps, rebundle) {
|
|
|
2403
2403
|
}
|
|
2404
2404
|
else {
|
|
2405
2405
|
console.log("rebuilding css");
|
|
2406
|
-
|
|
2407
|
-
|
|
2406
|
+
try {
|
|
2407
|
+
await buildSemanticUIAsync();
|
|
2408
|
+
console.log("css build complete");
|
|
2409
|
+
}
|
|
2410
|
+
catch (e) {
|
|
2411
|
+
console.error("css build failed", e);
|
|
2412
|
+
}
|
|
2408
2413
|
}
|
|
2409
2414
|
return lessFiles;
|
|
2410
2415
|
};
|
package/built/pxt.js
CHANGED
|
@@ -99066,6 +99066,11 @@ var ts;
|
|
|
99066
99066
|
}
|
|
99067
99067
|
}
|
|
99068
99068
|
Util.fileReadAsTextAsync = fileReadAsTextAsync;
|
|
99069
|
+
function sanitizeFileName(name) {
|
|
99070
|
+
/* eslint-disable no-control-regex */
|
|
99071
|
+
return name.replace(/[()\\\/.,?*^:<>!;'#$%^&|"@+=«»°{}\[\]¾½¼³²¦¬¤¢£~¯¸`±\x00-\x1F]/g, '').trim().replace(/\s+/g, '-');
|
|
99072
|
+
}
|
|
99073
|
+
Util.sanitizeFileName = sanitizeFileName;
|
|
99069
99074
|
function repeatMap(n, fn) {
|
|
99070
99075
|
n = n || 0;
|
|
99071
99076
|
let r = [];
|
|
@@ -100104,7 +100109,7 @@ var ts;
|
|
|
100104
100109
|
"sk": { englishName: "Slovak", localizedName: "Slovenčina" },
|
|
100105
100110
|
"sl": { englishName: "Slovenian", localizedName: "Slovenski" },
|
|
100106
100111
|
"sq": { englishName: "Albanian", localizedName: "shqip" },
|
|
100107
|
-
"sr": { englishName: "Serbian (
|
|
100112
|
+
"sr": { englishName: "Serbian (Cyrillic)", localizedName: "Srpski" },
|
|
100108
100113
|
"su": { englishName: "Sundanese", localizedName: "ᮘᮞ ᮞᮥᮔ᮪ᮓ" },
|
|
100109
100114
|
"sv-SE": { englishName: "Swedish", localizedName: "Svenska" },
|
|
100110
100115
|
"sw": { englishName: "Swahili", localizedName: "Kiswahili" },
|
|
@@ -101617,6 +101622,7 @@ var pxt;
|
|
|
101617
101622
|
var blocks;
|
|
101618
101623
|
(function (blocks) {
|
|
101619
101624
|
const THIS_NAME = "this";
|
|
101625
|
+
blocks.showBlockIdInTooltip = false;
|
|
101620
101626
|
// The JS Math functions supported in the blocks. The order of this array
|
|
101621
101627
|
// determines the order of the dropdown in the math_js_op block
|
|
101622
101628
|
blocks.MATH_FUNCTIONS = {
|
|
@@ -102318,6 +102324,21 @@ var pxt;
|
|
|
102318
102324
|
}));
|
|
102319
102325
|
});
|
|
102320
102326
|
}
|
|
102327
|
+
if (pxt.blocks.showBlockIdInTooltip) {
|
|
102328
|
+
for (const id of Object.keys(_blockDefinitions)) {
|
|
102329
|
+
const tooltip = _blockDefinitions[id].tooltip;
|
|
102330
|
+
if (typeof tooltip === "object" && tooltip !== null) {
|
|
102331
|
+
for (const innerKey in tooltip) {
|
|
102332
|
+
if (tooltip.hasOwnProperty(innerKey)) {
|
|
102333
|
+
_blockDefinitions[id].tooltip[innerKey] = `${tooltip[innerKey]} (id: ${id})`;
|
|
102334
|
+
}
|
|
102335
|
+
}
|
|
102336
|
+
}
|
|
102337
|
+
else {
|
|
102338
|
+
_blockDefinitions[id].tooltip = `${_blockDefinitions[id].tooltip} (id: ${id})`;
|
|
102339
|
+
}
|
|
102340
|
+
}
|
|
102341
|
+
}
|
|
102321
102342
|
}
|
|
102322
102343
|
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
102323
102344
|
})(pxt || (pxt = {}));
|
|
@@ -103680,6 +103701,26 @@ var pxt;
|
|
|
103680
103701
|
}
|
|
103681
103702
|
}
|
|
103682
103703
|
BrowserUtils.legacyCopyText = legacyCopyText;
|
|
103704
|
+
/**
|
|
103705
|
+
* Sets the theme of the application by adding a class to the body. Themes
|
|
103706
|
+
* are defined in CSS variable packs. The default theme is defined in
|
|
103707
|
+
* `themes/themepacks.less`, in the `:root` pseudoclass. `highcontrast` is
|
|
103708
|
+
* also defined there. Target-specific themes are defined in the target
|
|
103709
|
+
* repo's `theme/themepack.less`.
|
|
103710
|
+
*/
|
|
103711
|
+
function setApplicationTheme(theme) {
|
|
103712
|
+
const body = document.body;
|
|
103713
|
+
const classes = body.classList;
|
|
103714
|
+
for (let i = 0; i < classes.length; i++) {
|
|
103715
|
+
if (/^theme-/.test(classes[i])) {
|
|
103716
|
+
body.classList.remove(classes[i]);
|
|
103717
|
+
}
|
|
103718
|
+
}
|
|
103719
|
+
if (theme) {
|
|
103720
|
+
body.classList.add(`theme-${theme}`);
|
|
103721
|
+
}
|
|
103722
|
+
}
|
|
103723
|
+
BrowserUtils.setApplicationTheme = setApplicationTheme;
|
|
103683
103724
|
})(BrowserUtils = pxt.BrowserUtils || (pxt.BrowserUtils = {}));
|
|
103684
103725
|
})(pxt || (pxt = {}));
|
|
103685
103726
|
var pxt;
|
|
@@ -117164,6 +117205,14 @@ var pxt;
|
|
|
117164
117205
|
})(svgUtil = pxt.svgUtil || (pxt.svgUtil = {}));
|
|
117165
117206
|
})(pxt || (pxt = {}));
|
|
117166
117207
|
var pxt;
|
|
117208
|
+
(function (pxt) {
|
|
117209
|
+
var editor;
|
|
117210
|
+
(function (editor) {
|
|
117211
|
+
editor.initExtensionsAsync = opts => Promise.resolve({});
|
|
117212
|
+
editor.initFieldExtensionsAsync = opts => Promise.resolve({});
|
|
117213
|
+
})(editor = pxt.editor || (pxt.editor = {}));
|
|
117214
|
+
})(pxt || (pxt = {}));
|
|
117215
|
+
var pxt;
|
|
117167
117216
|
(function (pxt) {
|
|
117168
117217
|
pxt.IMAGE_MIME_TYPE = "image/x-mkcd-f4";
|
|
117169
117218
|
pxt.TILEMAP_MIME_TYPE = "application/mkcd-tilemap";
|
|
@@ -162971,8 +163020,13 @@ async function buildAndWatchTargetAsync(includeSourceMaps, rebundle) {
|
|
|
162971
163020
|
}
|
|
162972
163021
|
else {
|
|
162973
163022
|
console.log("rebuilding css");
|
|
162974
|
-
|
|
162975
|
-
|
|
163023
|
+
try {
|
|
163024
|
+
await buildSemanticUIAsync();
|
|
163025
|
+
console.log("css build complete");
|
|
163026
|
+
}
|
|
163027
|
+
catch (e) {
|
|
163028
|
+
console.error("css build failed", e);
|
|
163029
|
+
}
|
|
162976
163030
|
}
|
|
162977
163031
|
return lessFiles;
|
|
162978
163032
|
};
|
package/built/pxtblockly.js
CHANGED
|
@@ -7919,7 +7919,7 @@ var pxt;
|
|
|
7919
7919
|
const hasHandlers = hasArrowFunction(fn);
|
|
7920
7920
|
block.setPreviousStatement(!(hasHandlers && !fn.attributes.handlerStatement) && fn.retType == "void");
|
|
7921
7921
|
block.setNextStatement(!(hasHandlers && !fn.attributes.handlerStatement) && fn.retType == "void");
|
|
7922
|
-
block.setTooltip(/^__/.test(fn.namespace) ? "" : fn.attributes.jsDoc);
|
|
7922
|
+
block.setTooltip((/^__/.test(fn.namespace) ? "" : fn.attributes.jsDoc) + (pxt.blocks.showBlockIdInTooltip ? " (id: '" + fn.attributes.blockId + "')" : ""));
|
|
7923
7923
|
function buildBlockFromDef(def, expanded = false) {
|
|
7924
7924
|
let anonIndex = 0;
|
|
7925
7925
|
let firstParam = !expanded && !!comp.thisParameter;
|
|
@@ -11525,66 +11525,56 @@ var pxt;
|
|
|
11525
11525
|
(function (pxt) {
|
|
11526
11526
|
var blocks;
|
|
11527
11527
|
(function (blocks) {
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11528
|
+
const maxConcurrentChecks = 4;
|
|
11529
|
+
async function runValidatorPlanAsync(usedBlocks, plan) {
|
|
11530
|
+
// Each plan can have multiple checks it needs to run.
|
|
11531
|
+
// Run all of them in parallel, and then check if the number of successes is greater than the specified threshold.
|
|
11532
|
+
// TBD if it's faster to run in parallel without short-circuiting once the threshold is reached, or if it's faster to run sequentially and short-circuit.
|
|
11533
|
+
const startTime = Date.now();
|
|
11534
|
+
const checkRuns = pxt.Util.promisePoolAsync(maxConcurrentChecks, plan.checks, async (check) => {
|
|
11535
|
+
switch (check.validator) {
|
|
11536
|
+
case "blocksExist":
|
|
11537
|
+
return runBlocksExistValidation(usedBlocks, check);
|
|
11538
|
+
case "blockCommentsExist":
|
|
11539
|
+
return runValidateBlockCommentsExist(usedBlocks, check);
|
|
11540
|
+
case "specificBlockCommentsExist":
|
|
11541
|
+
return runValidateSpecificBlockCommentsExist(usedBlocks, check);
|
|
11542
|
+
case "blocksInSetExist":
|
|
11543
|
+
return runBlocksInSetExistValidation(usedBlocks, check);
|
|
11544
|
+
default:
|
|
11545
|
+
pxt.debug(`Unrecognized validator: ${check.validator}`);
|
|
11546
|
+
return false;
|
|
11547
|
+
}
|
|
11548
|
+
});
|
|
11549
|
+
const results = await checkRuns;
|
|
11550
|
+
const successCount = results.filter((r) => r).length;
|
|
11551
|
+
const passed = successCount >= plan.threshold;
|
|
11552
|
+
pxt.tickEvent("validation.evaluation_complete", {
|
|
11553
|
+
plan: plan.name,
|
|
11554
|
+
durationMs: Date.now() - startTime,
|
|
11555
|
+
passed: `${passed}`,
|
|
11556
|
+
});
|
|
11557
|
+
return passed;
|
|
11551
11558
|
}
|
|
11552
|
-
blocks.
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
function getCriteria(data) {
|
|
11560
|
-
switch (data.criteriaId) {
|
|
11561
|
-
case "blockCheck":
|
|
11562
|
-
return new BlockCheckCriteria(data.displayText, data.blockRequirements);
|
|
11563
|
-
case "comment":
|
|
11564
|
-
return new CommentCriteria(data.displayText, data.count);
|
|
11565
|
-
default:
|
|
11566
|
-
console.error(`Unrecognized criteriaId: ${data.criteriaId}`);
|
|
11567
|
-
return null;
|
|
11568
|
-
}
|
|
11559
|
+
blocks.runValidatorPlanAsync = runValidatorPlanAsync;
|
|
11560
|
+
function runBlocksExistValidation(usedBlocks, inputs) {
|
|
11561
|
+
const blockResults = blocks.validateBlocksExist({ usedBlocks, requiredBlockCounts: inputs.blockCounts });
|
|
11562
|
+
const success = blockResults.disabledBlocks.length === 0 &&
|
|
11563
|
+
blockResults.missingBlocks.length === 0 &&
|
|
11564
|
+
blockResults.insufficientBlocks.length === 0;
|
|
11565
|
+
return success;
|
|
11569
11566
|
}
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
this.displayText = displayText;
|
|
11574
|
-
}
|
|
11567
|
+
function runValidateBlockCommentsExist(usedBlocks, inputs) {
|
|
11568
|
+
const blockResults = blocks.validateBlockCommentsExist({ usedBlocks, numRequired: inputs.count });
|
|
11569
|
+
return blockResults.passed;
|
|
11575
11570
|
}
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
super(displayText);
|
|
11580
|
-
this.blockRequirements = blockRequirements;
|
|
11581
|
-
}
|
|
11571
|
+
function runValidateSpecificBlockCommentsExist(usedBlocks, inputs) {
|
|
11572
|
+
const blockResults = blocks.validateSpecificBlockCommentsExist({ usedBlocks, blockType: inputs.blockType });
|
|
11573
|
+
return blockResults.passed;
|
|
11582
11574
|
}
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
this.count = count;
|
|
11587
|
-
}
|
|
11575
|
+
function runBlocksInSetExistValidation(usedBlocks, inputs) {
|
|
11576
|
+
const blockResults = blocks.validateBlocksInSetExist({ usedBlocks, blockIdsToCheck: inputs.blocks, count: inputs.count });
|
|
11577
|
+
return blockResults.passed;
|
|
11588
11578
|
}
|
|
11589
11579
|
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
11590
11580
|
})(pxt || (pxt = {}));
|
|
@@ -11624,6 +11614,56 @@ var pxt;
|
|
|
11624
11614
|
blocks.validateBlocksExist = validateBlocksExist;
|
|
11625
11615
|
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
11626
11616
|
})(pxt || (pxt = {}));
|
|
11617
|
+
var pxt;
|
|
11618
|
+
(function (pxt) {
|
|
11619
|
+
var blocks;
|
|
11620
|
+
(function (blocks) {
|
|
11621
|
+
// validates that a combination of blocks in the set satisfies the required count
|
|
11622
|
+
// returns the blocks that make the validator pass
|
|
11623
|
+
function validateBlocksInSetExist({ usedBlocks, blockIdsToCheck, count, requireUnique }) {
|
|
11624
|
+
const successfulBlocks = [];
|
|
11625
|
+
const enabledBlocks = usedBlocks.filter((block) => block.isEnabled());
|
|
11626
|
+
for (const block of blockIdsToCheck) {
|
|
11627
|
+
const blockInstances = enabledBlocks.filter((b) => b.type === block);
|
|
11628
|
+
if (requireUnique && blockInstances.length >= 1) {
|
|
11629
|
+
successfulBlocks.push(blockInstances[0]);
|
|
11630
|
+
}
|
|
11631
|
+
else {
|
|
11632
|
+
successfulBlocks.push(...blockInstances);
|
|
11633
|
+
}
|
|
11634
|
+
}
|
|
11635
|
+
return { successfulBlocks, passed: successfulBlocks.length >= count };
|
|
11636
|
+
}
|
|
11637
|
+
blocks.validateBlocksInSetExist = validateBlocksInSetExist;
|
|
11638
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
11639
|
+
})(pxt || (pxt = {}));
|
|
11640
|
+
var pxt;
|
|
11641
|
+
(function (pxt) {
|
|
11642
|
+
var blocks;
|
|
11643
|
+
(function (blocks) {
|
|
11644
|
+
// validates that one or more blocks comments are in the project
|
|
11645
|
+
// returns the blocks that have comments for teacher tool scenario
|
|
11646
|
+
function validateBlockCommentsExist({ usedBlocks, numRequired }) {
|
|
11647
|
+
const commentedBlocks = usedBlocks.filter((block) => !!block.getCommentText());
|
|
11648
|
+
return { commentedBlocks, passed: commentedBlocks.length >= numRequired };
|
|
11649
|
+
}
|
|
11650
|
+
blocks.validateBlockCommentsExist = validateBlockCommentsExist;
|
|
11651
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
11652
|
+
})(pxt || (pxt = {}));
|
|
11653
|
+
var pxt;
|
|
11654
|
+
(function (pxt) {
|
|
11655
|
+
var blocks;
|
|
11656
|
+
(function (blocks) {
|
|
11657
|
+
// validates that all of a specific block type have comments
|
|
11658
|
+
// returns the blocks that do not have comments for a tutorial validation scenario
|
|
11659
|
+
function validateSpecificBlockCommentsExist({ usedBlocks, blockType }) {
|
|
11660
|
+
const allSpecifcBlocks = usedBlocks.filter((block) => block.type === blockType);
|
|
11661
|
+
const uncommentedBlocks = allSpecifcBlocks.filter((block) => !block.getCommentText());
|
|
11662
|
+
return { uncommentedBlocks, passed: uncommentedBlocks.length === 0 };
|
|
11663
|
+
}
|
|
11664
|
+
blocks.validateSpecificBlockCommentsExist = validateSpecificBlockCommentsExist;
|
|
11665
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
11666
|
+
})(pxt || (pxt = {}));
|
|
11627
11667
|
var pxtblockly;
|
|
11628
11668
|
(function (pxtblockly) {
|
|
11629
11669
|
class FieldBase extends Blockly.Field {
|
package/built/pxtblocks.d.ts
CHANGED
|
@@ -393,30 +393,12 @@ declare namespace pxt.blocks {
|
|
|
393
393
|
function initMathRoundBlock(): void;
|
|
394
394
|
}
|
|
395
395
|
declare namespace pxt.blocks {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
constructor(criteria: RubricCriteria[]);
|
|
396
|
+
interface EvaluationResult {
|
|
397
|
+
result: boolean;
|
|
399
398
|
}
|
|
400
|
-
function parseRubric(rubric: string): Rubric;
|
|
401
399
|
}
|
|
402
400
|
declare namespace pxt.blocks {
|
|
403
|
-
|
|
404
|
-
blocks: string[];
|
|
405
|
-
count: number;
|
|
406
|
-
}
|
|
407
|
-
export interface CriteriaData {
|
|
408
|
-
criteriaId: string;
|
|
409
|
-
displayText: string;
|
|
410
|
-
blockRequirements?: BlockSet[];
|
|
411
|
-
count?: number;
|
|
412
|
-
}
|
|
413
|
-
export function getCriteria(data: CriteriaData): RubricCriteria;
|
|
414
|
-
export abstract class RubricCriteria {
|
|
415
|
-
displayText: string;
|
|
416
|
-
abstract criteriaId: string;
|
|
417
|
-
constructor(displayText: string);
|
|
418
|
-
}
|
|
419
|
-
export {};
|
|
401
|
+
function runValidatorPlanAsync(usedBlocks: Blockly.Block[], plan: ValidatorPlan): Promise<boolean>;
|
|
420
402
|
}
|
|
421
403
|
declare namespace pxt.blocks {
|
|
422
404
|
function validateBlocksExist({ usedBlocks, requiredBlockCounts }: {
|
|
@@ -428,6 +410,62 @@ declare namespace pxt.blocks {
|
|
|
428
410
|
insufficientBlocks: string[];
|
|
429
411
|
};
|
|
430
412
|
}
|
|
413
|
+
declare namespace pxt.blocks {
|
|
414
|
+
function validateBlocksInSetExist({ usedBlocks, blockIdsToCheck, count, requireUnique }: {
|
|
415
|
+
usedBlocks: Blockly.Block[];
|
|
416
|
+
blockIdsToCheck: string[];
|
|
417
|
+
count: number;
|
|
418
|
+
requireUnique?: boolean;
|
|
419
|
+
}): {
|
|
420
|
+
successfulBlocks: Blockly.Block[];
|
|
421
|
+
passed: boolean;
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
declare namespace pxt.blocks {
|
|
425
|
+
function validateBlockCommentsExist({ usedBlocks, numRequired }: {
|
|
426
|
+
usedBlocks: Blockly.Block[];
|
|
427
|
+
numRequired: number;
|
|
428
|
+
}): {
|
|
429
|
+
commentedBlocks: Blockly.Block[];
|
|
430
|
+
passed: boolean;
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
declare namespace pxt.blocks {
|
|
434
|
+
function validateSpecificBlockCommentsExist({ usedBlocks, blockType }: {
|
|
435
|
+
usedBlocks: Blockly.Block[];
|
|
436
|
+
blockType: string;
|
|
437
|
+
}): {
|
|
438
|
+
uncommentedBlocks: Blockly.Block[];
|
|
439
|
+
passed: boolean;
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
declare namespace pxt.blocks {
|
|
443
|
+
interface ValidatorPlan {
|
|
444
|
+
name: string;
|
|
445
|
+
threshold: number;
|
|
446
|
+
checks: ValidatorCheckBase[];
|
|
447
|
+
}
|
|
448
|
+
interface ValidatorCheckBase {
|
|
449
|
+
validator: string;
|
|
450
|
+
}
|
|
451
|
+
interface BlocksExistValidatorCheck extends ValidatorCheckBase {
|
|
452
|
+
validator: "blocksExist";
|
|
453
|
+
blockCounts: pxt.Map<number>;
|
|
454
|
+
}
|
|
455
|
+
interface BlockCommentsExistValidatorCheck extends ValidatorCheckBase {
|
|
456
|
+
validator: "blockCommentsExist";
|
|
457
|
+
count: number;
|
|
458
|
+
}
|
|
459
|
+
interface SpecificBlockCommentsExistValidatorCheck extends ValidatorCheckBase {
|
|
460
|
+
validator: "specificBlockCommentsExist";
|
|
461
|
+
blockType: string;
|
|
462
|
+
}
|
|
463
|
+
interface BlocksInSetExistValidatorCheck extends ValidatorCheckBase {
|
|
464
|
+
validator: "blocksInSetExist";
|
|
465
|
+
blocks: string[];
|
|
466
|
+
count: number;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
431
469
|
declare namespace pxtblockly {
|
|
432
470
|
abstract class FieldBase<U> extends Blockly.Field implements Blockly.FieldCustom {
|
|
433
471
|
isFieldCustom_: true;
|
package/built/pxtblocks.js
CHANGED
|
@@ -4357,7 +4357,7 @@ var pxt;
|
|
|
4357
4357
|
const hasHandlers = hasArrowFunction(fn);
|
|
4358
4358
|
block.setPreviousStatement(!(hasHandlers && !fn.attributes.handlerStatement) && fn.retType == "void");
|
|
4359
4359
|
block.setNextStatement(!(hasHandlers && !fn.attributes.handlerStatement) && fn.retType == "void");
|
|
4360
|
-
block.setTooltip(/^__/.test(fn.namespace) ? "" : fn.attributes.jsDoc);
|
|
4360
|
+
block.setTooltip((/^__/.test(fn.namespace) ? "" : fn.attributes.jsDoc) + (pxt.blocks.showBlockIdInTooltip ? " (id: '" + fn.attributes.blockId + "')" : ""));
|
|
4361
4361
|
function buildBlockFromDef(def, expanded = false) {
|
|
4362
4362
|
let anonIndex = 0;
|
|
4363
4363
|
let firstParam = !expanded && !!comp.thisParameter;
|
|
@@ -7963,66 +7963,56 @@ var pxt;
|
|
|
7963
7963
|
(function (pxt) {
|
|
7964
7964
|
var blocks;
|
|
7965
7965
|
(function (blocks) {
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7966
|
+
const maxConcurrentChecks = 4;
|
|
7967
|
+
async function runValidatorPlanAsync(usedBlocks, plan) {
|
|
7968
|
+
// Each plan can have multiple checks it needs to run.
|
|
7969
|
+
// Run all of them in parallel, and then check if the number of successes is greater than the specified threshold.
|
|
7970
|
+
// TBD if it's faster to run in parallel without short-circuiting once the threshold is reached, or if it's faster to run sequentially and short-circuit.
|
|
7971
|
+
const startTime = Date.now();
|
|
7972
|
+
const checkRuns = pxt.Util.promisePoolAsync(maxConcurrentChecks, plan.checks, async (check) => {
|
|
7973
|
+
switch (check.validator) {
|
|
7974
|
+
case "blocksExist":
|
|
7975
|
+
return runBlocksExistValidation(usedBlocks, check);
|
|
7976
|
+
case "blockCommentsExist":
|
|
7977
|
+
return runValidateBlockCommentsExist(usedBlocks, check);
|
|
7978
|
+
case "specificBlockCommentsExist":
|
|
7979
|
+
return runValidateSpecificBlockCommentsExist(usedBlocks, check);
|
|
7980
|
+
case "blocksInSetExist":
|
|
7981
|
+
return runBlocksInSetExistValidation(usedBlocks, check);
|
|
7982
|
+
default:
|
|
7983
|
+
pxt.debug(`Unrecognized validator: ${check.validator}`);
|
|
7984
|
+
return false;
|
|
7985
|
+
}
|
|
7986
|
+
});
|
|
7987
|
+
const results = await checkRuns;
|
|
7988
|
+
const successCount = results.filter((r) => r).length;
|
|
7989
|
+
const passed = successCount >= plan.threshold;
|
|
7990
|
+
pxt.tickEvent("validation.evaluation_complete", {
|
|
7991
|
+
plan: plan.name,
|
|
7992
|
+
durationMs: Date.now() - startTime,
|
|
7993
|
+
passed: `${passed}`,
|
|
7994
|
+
});
|
|
7995
|
+
return passed;
|
|
7989
7996
|
}
|
|
7990
|
-
blocks.
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
function getCriteria(data) {
|
|
7998
|
-
switch (data.criteriaId) {
|
|
7999
|
-
case "blockCheck":
|
|
8000
|
-
return new BlockCheckCriteria(data.displayText, data.blockRequirements);
|
|
8001
|
-
case "comment":
|
|
8002
|
-
return new CommentCriteria(data.displayText, data.count);
|
|
8003
|
-
default:
|
|
8004
|
-
console.error(`Unrecognized criteriaId: ${data.criteriaId}`);
|
|
8005
|
-
return null;
|
|
8006
|
-
}
|
|
7997
|
+
blocks.runValidatorPlanAsync = runValidatorPlanAsync;
|
|
7998
|
+
function runBlocksExistValidation(usedBlocks, inputs) {
|
|
7999
|
+
const blockResults = blocks.validateBlocksExist({ usedBlocks, requiredBlockCounts: inputs.blockCounts });
|
|
8000
|
+
const success = blockResults.disabledBlocks.length === 0 &&
|
|
8001
|
+
blockResults.missingBlocks.length === 0 &&
|
|
8002
|
+
blockResults.insufficientBlocks.length === 0;
|
|
8003
|
+
return success;
|
|
8007
8004
|
}
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
this.displayText = displayText;
|
|
8012
|
-
}
|
|
8005
|
+
function runValidateBlockCommentsExist(usedBlocks, inputs) {
|
|
8006
|
+
const blockResults = blocks.validateBlockCommentsExist({ usedBlocks, numRequired: inputs.count });
|
|
8007
|
+
return blockResults.passed;
|
|
8013
8008
|
}
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
super(displayText);
|
|
8018
|
-
this.blockRequirements = blockRequirements;
|
|
8019
|
-
}
|
|
8009
|
+
function runValidateSpecificBlockCommentsExist(usedBlocks, inputs) {
|
|
8010
|
+
const blockResults = blocks.validateSpecificBlockCommentsExist({ usedBlocks, blockType: inputs.blockType });
|
|
8011
|
+
return blockResults.passed;
|
|
8020
8012
|
}
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
this.count = count;
|
|
8025
|
-
}
|
|
8013
|
+
function runBlocksInSetExistValidation(usedBlocks, inputs) {
|
|
8014
|
+
const blockResults = blocks.validateBlocksInSetExist({ usedBlocks, blockIdsToCheck: inputs.blocks, count: inputs.count });
|
|
8015
|
+
return blockResults.passed;
|
|
8026
8016
|
}
|
|
8027
8017
|
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
8028
8018
|
})(pxt || (pxt = {}));
|
|
@@ -8062,6 +8052,56 @@ var pxt;
|
|
|
8062
8052
|
blocks.validateBlocksExist = validateBlocksExist;
|
|
8063
8053
|
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
8064
8054
|
})(pxt || (pxt = {}));
|
|
8055
|
+
var pxt;
|
|
8056
|
+
(function (pxt) {
|
|
8057
|
+
var blocks;
|
|
8058
|
+
(function (blocks) {
|
|
8059
|
+
// validates that a combination of blocks in the set satisfies the required count
|
|
8060
|
+
// returns the blocks that make the validator pass
|
|
8061
|
+
function validateBlocksInSetExist({ usedBlocks, blockIdsToCheck, count, requireUnique }) {
|
|
8062
|
+
const successfulBlocks = [];
|
|
8063
|
+
const enabledBlocks = usedBlocks.filter((block) => block.isEnabled());
|
|
8064
|
+
for (const block of blockIdsToCheck) {
|
|
8065
|
+
const blockInstances = enabledBlocks.filter((b) => b.type === block);
|
|
8066
|
+
if (requireUnique && blockInstances.length >= 1) {
|
|
8067
|
+
successfulBlocks.push(blockInstances[0]);
|
|
8068
|
+
}
|
|
8069
|
+
else {
|
|
8070
|
+
successfulBlocks.push(...blockInstances);
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
return { successfulBlocks, passed: successfulBlocks.length >= count };
|
|
8074
|
+
}
|
|
8075
|
+
blocks.validateBlocksInSetExist = validateBlocksInSetExist;
|
|
8076
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
8077
|
+
})(pxt || (pxt = {}));
|
|
8078
|
+
var pxt;
|
|
8079
|
+
(function (pxt) {
|
|
8080
|
+
var blocks;
|
|
8081
|
+
(function (blocks) {
|
|
8082
|
+
// validates that one or more blocks comments are in the project
|
|
8083
|
+
// returns the blocks that have comments for teacher tool scenario
|
|
8084
|
+
function validateBlockCommentsExist({ usedBlocks, numRequired }) {
|
|
8085
|
+
const commentedBlocks = usedBlocks.filter((block) => !!block.getCommentText());
|
|
8086
|
+
return { commentedBlocks, passed: commentedBlocks.length >= numRequired };
|
|
8087
|
+
}
|
|
8088
|
+
blocks.validateBlockCommentsExist = validateBlockCommentsExist;
|
|
8089
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
8090
|
+
})(pxt || (pxt = {}));
|
|
8091
|
+
var pxt;
|
|
8092
|
+
(function (pxt) {
|
|
8093
|
+
var blocks;
|
|
8094
|
+
(function (blocks) {
|
|
8095
|
+
// validates that all of a specific block type have comments
|
|
8096
|
+
// returns the blocks that do not have comments for a tutorial validation scenario
|
|
8097
|
+
function validateSpecificBlockCommentsExist({ usedBlocks, blockType }) {
|
|
8098
|
+
const allSpecifcBlocks = usedBlocks.filter((block) => block.type === blockType);
|
|
8099
|
+
const uncommentedBlocks = allSpecifcBlocks.filter((block) => !block.getCommentText());
|
|
8100
|
+
return { uncommentedBlocks, passed: uncommentedBlocks.length === 0 };
|
|
8101
|
+
}
|
|
8102
|
+
blocks.validateSpecificBlockCommentsExist = validateSpecificBlockCommentsExist;
|
|
8103
|
+
})(blocks = pxt.blocks || (pxt.blocks = {}));
|
|
8104
|
+
})(pxt || (pxt = {}));
|
|
8065
8105
|
var pxtblockly;
|
|
8066
8106
|
(function (pxtblockly) {
|
|
8067
8107
|
class FieldBase extends Blockly.Field {
|
package/built/pxtlib.d.ts
CHANGED
|
@@ -276,6 +276,7 @@ declare namespace ts.pxtc.Util {
|
|
|
276
276
|
export function blobReadAsDataURL(blob: Blob): Promise<string>;
|
|
277
277
|
export function fileReadAsBufferAsync(f: File): Promise<Uint8Array>;
|
|
278
278
|
export function fileReadAsTextAsync(f: File): Promise<string>;
|
|
279
|
+
export function sanitizeFileName(name: string): string;
|
|
279
280
|
export function repeatMap<T>(n: number, fn: (index: number) => T): T[];
|
|
280
281
|
export function listsEqual<T>(a: T[], b: T[]): boolean;
|
|
281
282
|
export function oops(msg?: string): Error;
|
|
@@ -632,6 +633,7 @@ declare namespace pxt {
|
|
|
632
633
|
function isOutputText(trg?: pxtc.CompileTarget): boolean;
|
|
633
634
|
}
|
|
634
635
|
declare namespace pxt.blocks {
|
|
636
|
+
let showBlockIdInTooltip: boolean;
|
|
635
637
|
const MATH_FUNCTIONS: {
|
|
636
638
|
unary: string[];
|
|
637
639
|
binary: string[];
|
|
@@ -888,6 +890,14 @@ declare namespace pxt.BrowserUtils {
|
|
|
888
890
|
export function setCookieLang(langId: string, docs?: boolean): void;
|
|
889
891
|
export function cacheBustingUrl(url: string): string;
|
|
890
892
|
export function legacyCopyText(element: HTMLInputElement | HTMLTextAreaElement): boolean;
|
|
893
|
+
/**
|
|
894
|
+
* Sets the theme of the application by adding a class to the body. Themes
|
|
895
|
+
* are defined in CSS variable packs. The default theme is defined in
|
|
896
|
+
* `themes/themepacks.less`, in the `:root` pseudoclass. `highcontrast` is
|
|
897
|
+
* also defined there. Target-specific themes are defined in the target
|
|
898
|
+
* repo's `theme/themepack.less`.
|
|
899
|
+
*/
|
|
900
|
+
export function setApplicationTheme(theme: string | undefined): void;
|
|
891
901
|
export {};
|
|
892
902
|
}
|
|
893
903
|
declare namespace pxt.cloud {
|
|
@@ -2710,6 +2720,18 @@ declare namespace pxt.svgUtil.helpers {
|
|
|
2710
2720
|
protected rePosition(): void;
|
|
2711
2721
|
}
|
|
2712
2722
|
}
|
|
2723
|
+
declare namespace pxt.editor {
|
|
2724
|
+
let initExtensionsAsync: (opts: pxt.editor.ExtensionOptions) => Promise<pxt.editor.ExtensionResult>;
|
|
2725
|
+
let initFieldExtensionsAsync: (opts: pxt.editor.FieldExtensionOptions) => Promise<pxt.editor.FieldExtensionResult>;
|
|
2726
|
+
interface ExtensionOptions {
|
|
2727
|
+
}
|
|
2728
|
+
interface FieldExtensionResult {
|
|
2729
|
+
}
|
|
2730
|
+
interface ExtensionResult {
|
|
2731
|
+
}
|
|
2732
|
+
interface FieldExtensionOptions {
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2713
2735
|
declare namespace pxt {
|
|
2714
2736
|
export const IMAGE_MIME_TYPE = "image/x-mkcd-f4";
|
|
2715
2737
|
export const TILEMAP_MIME_TYPE = "application/mkcd-tilemap";
|