pxt-core 9.3.13 → 9.3.14

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 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
- await buildSemanticUIAsync();
2407
- console.log("css build complete");
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
@@ -101617,6 +101617,7 @@ var pxt;
101617
101617
  var blocks;
101618
101618
  (function (blocks) {
101619
101619
  const THIS_NAME = "this";
101620
+ blocks.showBlockIdInTooltip = false;
101620
101621
  // The JS Math functions supported in the blocks. The order of this array
101621
101622
  // determines the order of the dropdown in the math_js_op block
101622
101623
  blocks.MATH_FUNCTIONS = {
@@ -102318,6 +102319,21 @@ var pxt;
102318
102319
  }));
102319
102320
  });
102320
102321
  }
102322
+ if (pxt.blocks.showBlockIdInTooltip) {
102323
+ for (const id of Object.keys(_blockDefinitions)) {
102324
+ const tooltip = _blockDefinitions[id].tooltip;
102325
+ if (typeof tooltip === "object" && tooltip !== null) {
102326
+ for (const innerKey in tooltip) {
102327
+ if (tooltip.hasOwnProperty(innerKey)) {
102328
+ _blockDefinitions[id].tooltip[innerKey] = `${tooltip[innerKey]} (id: ${id})`;
102329
+ }
102330
+ }
102331
+ }
102332
+ else {
102333
+ _blockDefinitions[id].tooltip = `${_blockDefinitions[id].tooltip} (id: ${id})`;
102334
+ }
102335
+ }
102336
+ }
102321
102337
  }
102322
102338
  })(blocks = pxt.blocks || (pxt.blocks = {}));
102323
102339
  })(pxt || (pxt = {}));
@@ -103680,6 +103696,26 @@ var pxt;
103680
103696
  }
103681
103697
  }
103682
103698
  BrowserUtils.legacyCopyText = legacyCopyText;
103699
+ /**
103700
+ * Sets the theme of the application by adding a class to the body. Themes
103701
+ * are defined in CSS variable packs. The default theme is defined in
103702
+ * `themes/themepacks.less`, in the `:root` pseudoclass. `highcontrast` is
103703
+ * also defined there. Target-specific themes are defined in the target
103704
+ * repo's `theme/themepack.less`.
103705
+ */
103706
+ function setApplicationTheme(theme) {
103707
+ const body = document.body;
103708
+ const classes = body.classList;
103709
+ for (let i = 0; i < classes.length; i++) {
103710
+ if (/^theme-/.test(classes[i])) {
103711
+ body.classList.remove(classes[i]);
103712
+ }
103713
+ }
103714
+ if (theme) {
103715
+ body.classList.add(`theme-${theme}`);
103716
+ }
103717
+ }
103718
+ BrowserUtils.setApplicationTheme = setApplicationTheme;
103683
103719
  })(BrowserUtils = pxt.BrowserUtils || (pxt.BrowserUtils = {}));
103684
103720
  })(pxt || (pxt = {}));
103685
103721
  var pxt;
@@ -162971,8 +163007,13 @@ async function buildAndWatchTargetAsync(includeSourceMaps, rebundle) {
162971
163007
  }
162972
163008
  else {
162973
163009
  console.log("rebuilding css");
162974
- await buildSemanticUIAsync();
162975
- console.log("css build complete");
163010
+ try {
163011
+ await buildSemanticUIAsync();
163012
+ console.log("css build complete");
163013
+ }
163014
+ catch (e) {
163015
+ console.error("css build failed", e);
163016
+ }
162976
163017
  }
162977
163018
  return lessFiles;
162978
163019
  };
@@ -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
- class Rubric {
11529
- constructor(criteria) {
11530
- this.criteria = criteria;
11531
- }
11532
- }
11533
- blocks.Rubric = Rubric;
11534
- function parseRubric(rubric) {
11535
- let rubricObj;
11536
- try {
11537
- rubricObj = JSON.parse(rubric);
11538
- }
11539
- catch (e) {
11540
- console.error(`Error parsing rubric! ${e}`);
11541
- return null;
11542
- }
11543
- if (!rubricObj.criteria) {
11544
- console.error(`No criteria found in rubric`);
11545
- return null;
11546
- }
11547
- const criteria = rubricObj.criteria.map((c) => {
11548
- return blocks.getCriteria(c);
11549
- }).filter((r) => !!r);
11550
- return new Rubric(criteria);
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.parseRubric = parseRubric;
11553
- })(blocks = pxt.blocks || (pxt.blocks = {}));
11554
- })(pxt || (pxt = {}));
11555
- var pxt;
11556
- (function (pxt) {
11557
- var blocks;
11558
- (function (blocks) {
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
- blocks.getCriteria = getCriteria;
11571
- class RubricCriteria {
11572
- constructor(displayText) {
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
- blocks.RubricCriteria = RubricCriteria;
11577
- class BlockCheckCriteria extends RubricCriteria {
11578
- constructor(displayText, blockRequirements) {
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
- class CommentCriteria extends RubricCriteria {
11584
- constructor(displayText, count) {
11585
- super(displayText);
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 {
@@ -393,30 +393,12 @@ declare namespace pxt.blocks {
393
393
  function initMathRoundBlock(): void;
394
394
  }
395
395
  declare namespace pxt.blocks {
396
- class Rubric {
397
- criteria: RubricCriteria[];
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
- interface BlockSet {
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;
@@ -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
- class Rubric {
7967
- constructor(criteria) {
7968
- this.criteria = criteria;
7969
- }
7970
- }
7971
- blocks.Rubric = Rubric;
7972
- function parseRubric(rubric) {
7973
- let rubricObj;
7974
- try {
7975
- rubricObj = JSON.parse(rubric);
7976
- }
7977
- catch (e) {
7978
- console.error(`Error parsing rubric! ${e}`);
7979
- return null;
7980
- }
7981
- if (!rubricObj.criteria) {
7982
- console.error(`No criteria found in rubric`);
7983
- return null;
7984
- }
7985
- const criteria = rubricObj.criteria.map((c) => {
7986
- return blocks.getCriteria(c);
7987
- }).filter((r) => !!r);
7988
- return new Rubric(criteria);
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.parseRubric = parseRubric;
7991
- })(blocks = pxt.blocks || (pxt.blocks = {}));
7992
- })(pxt || (pxt = {}));
7993
- var pxt;
7994
- (function (pxt) {
7995
- var blocks;
7996
- (function (blocks) {
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
- blocks.getCriteria = getCriteria;
8009
- class RubricCriteria {
8010
- constructor(displayText) {
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
- blocks.RubricCriteria = RubricCriteria;
8015
- class BlockCheckCriteria extends RubricCriteria {
8016
- constructor(displayText, blockRequirements) {
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
- class CommentCriteria extends RubricCriteria {
8022
- constructor(displayText, count) {
8023
- super(displayText);
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 {
@@ -304,6 +304,7 @@ declare namespace pxt.editor {
304
304
  blocksScreenshotAsync(pixelDensity?: number, encodeBlocks?: boolean): Promise<string>;
305
305
  renderBlocksAsync(req: EditorMessageRenderBlocksRequest): Promise<EditorMessageRenderBlocksResponse>;
306
306
  renderPythonAsync(req: EditorMessageRenderPythonRequest): Promise<EditorMessageRenderPythonResponse>;
307
+ getBlocks(): Blockly.Block[];
307
308
  toggleHighContrast(): void;
308
309
  setHighContrast(on: boolean): void;
309
310
  toggleGreenScreen(): void;
@@ -562,7 +563,7 @@ declare namespace pxt.editor {
562
563
  /**
563
564
  * Request action
564
565
  */
565
- action: "switchblocks" | "switchjavascript" | "startsimulator" | "restartsimulator" | "stopsimulator" | "hidesimulator" | "showsimulator" | "closeflyout" | "newproject" | "importproject" | "importtutorial" | "openheader" | "proxytosim" | "undo" | "redo" | "renderblocks" | "renderpython" | "setscale" | "startactivity" | "saveproject" | "unloadproject" | "shareproject" | "savelocalprojectstocloud" | "projectcloudstatus" | "requestprojectcloudstatus" | "convertcloudprojectstolocal" | "setlanguagerestriction" | "toggletrace" | "togglehighcontrast" | "sethighcontrast" | "togglegreenscreen" | "settracestate" | "setsimulatorfullscreen" | "print" | "pair" | "workspacesync" | "workspacereset" | "workspacesave" | "workspaceloaded" | "workspaceevent" | "workspacediagnostics" | "event" | "simevent" | "info" | "tutorialevent" | "editorcontentloaded" | ExtInitializeType | ExtDataStreamType | ExtUserCodeType | ExtReadCodeType | ExtWriteCodeType;
566
+ action: "switchblocks" | "switchjavascript" | "startsimulator" | "restartsimulator" | "stopsimulator" | "hidesimulator" | "showsimulator" | "closeflyout" | "newproject" | "importproject" | "importtutorial" | "openheader" | "proxytosim" | "undo" | "redo" | "renderblocks" | "renderpython" | "setscale" | "startactivity" | "saveproject" | "unloadproject" | "shareproject" | "savelocalprojectstocloud" | "projectcloudstatus" | "requestprojectcloudstatus" | "convertcloudprojectstolocal" | "setlanguagerestriction" | "toggletrace" | "togglehighcontrast" | "sethighcontrast" | "togglegreenscreen" | "settracestate" | "setsimulatorfullscreen" | "print" | "pair" | "workspacesync" | "workspacereset" | "workspacesave" | "workspaceloaded" | "workspaceevent" | "workspacediagnostics" | "event" | "simevent" | "info" | "tutorialevent" | "editorcontentloaded" | "runeval" | ExtInitializeType | ExtDataStreamType | ExtUserCodeType | ExtReadCodeType | ExtWriteCodeType;
566
567
  }
567
568
  /**
568
569
  * Request sent by the editor when a tick/error/expection is registered
@@ -710,6 +711,10 @@ declare namespace pxt.editor {
710
711
  snippetMode?: boolean;
711
712
  layout?: pxt.blocks.BlockLayout;
712
713
  }
714
+ interface EditorMessageRunEvalRequest extends EditorMessageRequest {
715
+ action: "runeval";
716
+ validatorPlan: pxt.blocks.ValidatorPlan;
717
+ }
713
718
  interface EditorMessageRenderBlocksResponse {
714
719
  svg: SVGSVGElement;
715
720
  xml: Promise<any>;
@@ -210,6 +210,18 @@ var pxt;
210
210
  });
211
211
  });
212
212
  }
213
+ case "runeval": {
214
+ const evalmsg = data;
215
+ const plan = evalmsg.validatorPlan;
216
+ return Promise.resolve()
217
+ .then(() => {
218
+ const blocks = projectView.getBlocks();
219
+ return pxt.blocks.runValidatorPlanAsync(blocks, plan);
220
+ })
221
+ .then(results => {
222
+ resp = { result: results };
223
+ });
224
+ }
213
225
  case "renderpython": {
214
226
  const rendermsg = data;
215
227
  return Promise.resolve()
package/built/pxtlib.d.ts CHANGED
@@ -632,6 +632,7 @@ declare namespace pxt {
632
632
  function isOutputText(trg?: pxtc.CompileTarget): boolean;
633
633
  }
634
634
  declare namespace pxt.blocks {
635
+ let showBlockIdInTooltip: boolean;
635
636
  const MATH_FUNCTIONS: {
636
637
  unary: string[];
637
638
  binary: string[];
@@ -888,6 +889,14 @@ declare namespace pxt.BrowserUtils {
888
889
  export function setCookieLang(langId: string, docs?: boolean): void;
889
890
  export function cacheBustingUrl(url: string): string;
890
891
  export function legacyCopyText(element: HTMLInputElement | HTMLTextAreaElement): boolean;
892
+ /**
893
+ * Sets the theme of the application by adding a class to the body. Themes
894
+ * are defined in CSS variable packs. The default theme is defined in
895
+ * `themes/themepacks.less`, in the `:root` pseudoclass. `highcontrast` is
896
+ * also defined there. Target-specific themes are defined in the target
897
+ * repo's `theme/themepack.less`.
898
+ */
899
+ export function setApplicationTheme(theme: string | undefined): void;
891
900
  export {};
892
901
  }
893
902
  declare namespace pxt.cloud {