pxt-core 7.4.9 → 7.4.13

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.
Files changed (59) hide show
  1. package/built/cli.js +94 -83
  2. package/built/pxt.js +180 -135
  3. package/built/pxtblockly.js +1509 -1508
  4. package/built/pxtblocks.d.ts +11 -4
  5. package/built/pxtblocks.js +126 -138
  6. package/built/pxtlib.d.ts +6 -2
  7. package/built/pxtlib.js +86 -52
  8. package/built/target.js +1 -1
  9. package/built/web/blockly.css +1 -1
  10. package/built/web/main.js +1 -1
  11. package/built/web/pxtapp.js +1 -1
  12. package/built/web/pxtasseteditor.js +1 -1
  13. package/built/web/pxtblockly.js +53 -1
  14. package/built/web/pxtblocks.js +1 -1
  15. package/built/web/pxtembed.js +53 -1
  16. package/built/web/pxtlib.js +1 -1
  17. package/built/web/pxtworker.js +1 -1
  18. package/built/web/react-common-skillmap.css +1 -0
  19. package/built/web/rtlblockly.css +1 -1
  20. package/built/web/rtlreact-common-skillmap.css +1 -0
  21. package/built/web/rtlsemantic.css +1 -1
  22. package/built/web/semantic.css +1 -1
  23. package/built/web/skillmap/js/main.b96caef3.chunk.js +1 -0
  24. package/docfiles/tracking.html +1 -1
  25. package/localtypings/blockly.d.ts +12268 -7535
  26. package/localtypings/pxtarget.d.ts +1 -0
  27. package/localtypings/pxtblockly.d.ts +37 -0
  28. package/package.json +6 -3
  29. package/react-common/components/Checkbox.tsx +25 -0
  30. package/react-common/components/Notification.tsx +82 -0
  31. package/react-common/components/profile/Badge.tsx +33 -0
  32. package/react-common/components/profile/BadgeInfo.tsx +74 -0
  33. package/react-common/components/profile/BadgeList.tsx +67 -0
  34. package/react-common/components/profile/Profile.tsx +42 -0
  35. package/react-common/components/profile/UserNotification.tsx +32 -0
  36. package/react-common/components/profile/UserPane.tsx +64 -0
  37. package/react-common/components/types.d.ts +29 -0
  38. package/react-common/components/util.tsx +35 -0
  39. package/{built/web/react-common.css → react-common/styles/profile/profile.less} +1 -0
  40. package/react-common/styles/react-common-skillmap-core.less +10 -0
  41. package/react-common/styles/react-common-skillmap.less +12 -0
  42. package/react-common/styles/react-common.less +1 -0
  43. package/react-common/tsconfig.json +36 -0
  44. package/theme/blockly-core.less +38 -13
  45. package/theme/common-components.less +7 -0
  46. package/theme/common.less +1 -1
  47. package/theme/highcontrast.less +4 -0
  48. package/theme/melodyeditor.less +2 -2
  49. package/theme/print.less +1 -1
  50. package/theme/pxt.less +2 -0
  51. package/theme/toolbox.less +1 -0
  52. package/theme/tutorial-sidebar.less +64 -6
  53. package/webapp/public/blockly/blockly_compressed.js +1271 -1288
  54. package/webapp/public/blockly/blocks_compressed.js +47 -65
  55. package/webapp/public/blockly/msg/js/en.js +8 -17
  56. package/webapp/public/blockly/msg/json/en.json +6 -15
  57. package/webapp/public/blockly/plugins.js +57 -0
  58. package/webapp/public/skillmap.html +2 -2
  59. package/built/web/skillmap/js/main.ea4b3e23.chunk.js +0 -1
package/built/pxtlib.d.ts CHANGED
@@ -129,8 +129,11 @@ declare namespace pxt.auth {
129
129
  username?: string;
130
130
  avatarUrl?: string;
131
131
  }): Promise<boolean>;
132
- private prefPatchOps;
133
- patchUserPreferencesAsync(ops: ts.pxtc.jsonPatch.PatchOperation | ts.pxtc.jsonPatch.PatchOperation[], immediate?: boolean): Promise<SetPrefResult>;
132
+ private patchQueue;
133
+ patchUserPreferencesAsync(patchOps: ts.pxtc.jsonPatch.PatchOperation | ts.pxtc.jsonPatch.PatchOperation[], opts?: {
134
+ immediate?: boolean;
135
+ filter?: (op: ts.pxtc.jsonPatch.PatchOperation) => boolean;
136
+ }): Promise<SetPrefResult>;
134
137
  hasUserId(): boolean;
135
138
  private fetchUserAsync;
136
139
  private setUserProfileAsync;
@@ -302,6 +305,7 @@ declare namespace ts.pxtc.Util {
302
305
  export function groupBy<T>(arr: T[], f: (t: T) => string): pxt.Map<T[]>;
303
306
  export function toDictionary<T>(arr: T[], f: (t: T) => string): pxt.Map<T>;
304
307
  export function toSet<T>(arr: T[], f: (t: T) => string): pxt.Map<boolean>;
308
+ export function deepCopy(src: any): any;
305
309
  export interface ArrayLike<T> {
306
310
  [index: number]: T;
307
311
  length: number;
package/built/pxtlib.js CHANGED
@@ -199,7 +199,7 @@ var pxt;
199
199
  constructor() {
200
200
  this.initialUserPreferences_ = undefined;
201
201
  this.initialAuthCheck_ = undefined;
202
- this.prefPatchOps = [];
202
+ this.patchQueue = [];
203
203
  pxt.Util.assert(!_client);
204
204
  // Set global instance.
205
205
  _client = this;
@@ -389,63 +389,85 @@ var pxt;
389
389
  }
390
390
  return result.success;
391
391
  }
392
- async patchUserPreferencesAsync(ops, immediate = false) {
393
- ops = Array.isArray(ops) ? ops : [ops];
394
- ops = ops.filter(op => !!op);
392
+ async patchUserPreferencesAsync(patchOps, opts = {}) {
393
+ const defaultSuccessAsync = async () => ({ success: true, res: await this.userPreferencesAsync() });
394
+ patchOps = Array.isArray(patchOps) ? patchOps : [patchOps];
395
+ patchOps = patchOps.filter(op => !!op);
396
+ if (!patchOps.length) {
397
+ return await defaultSuccessAsync();
398
+ }
399
+ const patchDiff = (pSrc, ops, filter) => {
400
+ // Apply patches to pDst and return the diff as a set of new patch ops.
401
+ const pDst = pxt.U.deepCopy(pSrc);
402
+ ts.pxtc.jsonPatch.patchInPlace(pDst, ops);
403
+ let diff = ts.pxtc.jsonPatch.diff(pSrc, pDst);
404
+ // Run caller-provided filter
405
+ if (diff.length && filter) {
406
+ diff = diff.filter(filter);
407
+ }
408
+ return diff;
409
+ };
410
+ // Process incoming patch operations to produce a more fine-grained set of diffs. Incoming patches may be overly destructive
411
+ // Apply the patch in isolation and get the diff from original
395
412
  const curPref = await this.userPreferencesAsync();
396
- if (!ops.length) {
397
- return { success: true, res: curPref };
413
+ const diff = patchDiff(curPref, patchOps, opts.filter);
414
+ if (!diff.length) {
415
+ return await defaultSuccessAsync();
398
416
  }
399
- ts.pxtc.jsonPatch.patchInPlace(curPref, ops);
417
+ // Apply the new diff to the current state
418
+ ts.pxtc.jsonPatch.patchInPlace(curPref, diff);
400
419
  await this.setUserPreferencesAsync(curPref);
401
- // If we're not logged in, non-persistent local state is all we'll use
420
+ // If the user is not logged in, non-persistent local state is all we'll use (no sync to cloud)
402
421
  if (!await this.loggedInAsync()) {
403
- return { success: true, res: curPref };
422
+ return await defaultSuccessAsync();
404
423
  }
405
- // If the user is logged in, save to cloud, but debounce the api call as this can be called frequently from skillmaps
406
- // Replace matching patches in the queue
407
- ops.forEach((incoming, iIncoming) => {
408
- this.prefPatchOps.some((existing, iExisting) => {
409
- if (!ts.pxtc.jsonPatch.opsAreEqual(existing, incoming))
410
- return false;
411
- // Patches are equivalent, replace in queue
412
- this.prefPatchOps[iExisting] = incoming;
413
- // Clear from incoming so we don't add it below
414
- ops[iIncoming] = null;
415
- return true;
416
- });
417
- });
418
- // Add remaining ops to the queue
419
- ops.filter(op => !!op).forEach(op => this.prefPatchOps.push(op));
424
+ // If the user is logged in, sync to cloud, but debounce the api call as this can be called frequently from skillmaps
425
+ // Queue the patch for sync with backend
426
+ this.patchQueue.push({ ops: patchOps, filter: opts.filter });
420
427
  clearTimeout(debouncePreferencesChangedTimeout);
421
- const savePrefs = async () => {
428
+ const syncPrefs = async () => {
422
429
  debouncePreferencesChangedStarted = 0;
423
- // Clear queued patch ops before send.
424
- const prefPatchOps = this.prefPatchOps;
425
- this.prefPatchOps = [];
426
- const result = await this.apiAsync('/api/user/preferences', prefPatchOps, 'PATCH');
427
- if (result.success) {
428
- pxt.debug("Updating local user preferences w/ cloud data after result of POST");
429
- // Set user profile from returned value so we stay in-sync
430
- this.setUserPreferencesAsync(result.resp);
430
+ if (!this.patchQueue.length) {
431
+ return await defaultSuccessAsync();
432
+ }
433
+ // Fetch latest prefs from remote
434
+ const getResult = await this.apiAsync('/api/user/preferences');
435
+ if (!getResult.success) {
436
+ pxt.reportError("identity", "failed to fetch preferences for patch", getResult);
437
+ return { success: false, res: undefined };
438
+ }
439
+ // Apply queued patches to the remote state in isolation and develop a final diff to send to the backend
440
+ const remotePrefs = pxt.U.deepCopy(getResult.resp) || auth.DEFAULT_USER_PREFERENCES();
441
+ const patchQueue = this.patchQueue;
442
+ this.patchQueue = []; // Reset the queue
443
+ patchQueue.forEach(patch => {
444
+ const diff = patchDiff(remotePrefs, patch.ops, patch.filter);
445
+ ts.pxtc.jsonPatch.patchInPlace(remotePrefs, diff);
446
+ });
447
+ // Diff the original and patched remote states to get a final set of patch operations
448
+ const finalOps = pxtc.jsonPatch.diff(getResult.resp, remotePrefs);
449
+ const patchResult = await this.apiAsync('/api/user/preferences', finalOps, 'PATCH');
450
+ if (patchResult.success) {
451
+ // Set user profile from returned value so we stay in sync
452
+ this.setUserPreferencesAsync(patchResult.resp);
431
453
  }
432
454
  else {
433
- pxt.reportError("identity", "update preferences failed", result);
455
+ pxt.reportError("identity", "failed to patch preferences", patchResult);
434
456
  }
435
- return { success: result.success, res: result.resp };
457
+ return { success: patchResult.success, res: patchResult.resp };
436
458
  };
437
- if (immediate) {
438
- return await savePrefs();
459
+ if (opts.immediate) {
460
+ return await syncPrefs();
439
461
  }
440
462
  else {
441
463
  if (!debouncePreferencesChangedStarted) {
442
464
  debouncePreferencesChangedStarted = pxt.U.now();
443
465
  }
444
466
  if (PREFERENCES_DEBOUNCE_MAX_MS < pxt.U.now() - debouncePreferencesChangedStarted) {
445
- return await savePrefs();
467
+ return await syncPrefs();
446
468
  }
447
469
  else {
448
- debouncePreferencesChangedTimeout = setTimeout(savePrefs, PREFERENCES_DEBOUNCE_MS);
470
+ debouncePreferencesChangedTimeout = setTimeout(syncPrefs, PREFERENCES_DEBOUNCE_MS);
449
471
  return { success: false, res: undefined }; // This needs to be implemented correctly to return a promise with the debouncer
450
472
  }
451
473
  }
@@ -1528,6 +1550,18 @@ var ts;
1528
1550
  return r;
1529
1551
  }
1530
1552
  Util.toSet = toSet;
1553
+ function deepCopy(src) {
1554
+ if (typeof src !== "object" || src === null) {
1555
+ return src;
1556
+ }
1557
+ const dst = Array.isArray(src) ? [] : {};
1558
+ for (const key in src) {
1559
+ const value = src[key];
1560
+ dst[key] = deepCopy(value);
1561
+ }
1562
+ return dst;
1563
+ }
1564
+ Util.deepCopy = deepCopy;
1531
1565
  function toArray(a) {
1532
1566
  if (Array.isArray(a)) {
1533
1567
  return a;
@@ -3947,7 +3981,7 @@ var pxt;
3947
3981
  'pxt_controls_for': {
3948
3982
  name: pxt.Util.lf("a loop that repeats the number of times you say"),
3949
3983
  tooltip: pxt.Util.lf("Have the variable '{0}' take on the values from 0 to the end number, counting by 1, and do the specified blocks."),
3950
- url: 'blocks/loops/for',
3984
+ url: '/blocks/loops/for',
3951
3985
  category: 'loops',
3952
3986
  block: {
3953
3987
  message0: pxt.Util.lf("for %1 from 0 to %2"),
@@ -3958,7 +3992,7 @@ var pxt;
3958
3992
  'controls_simple_for': {
3959
3993
  name: pxt.Util.lf("a loop that repeats the number of times you say"),
3960
3994
  tooltip: pxt.Util.lf("Have the variable '{0}' take on the values from 0 to the end number, counting by 1, and do the specified blocks."),
3961
- url: 'blocks/loops/for',
3995
+ url: '/blocks/loops/for',
3962
3996
  category: 'loops',
3963
3997
  block: {
3964
3998
  message0: pxt.Util.lf("for %1 from 0 to %2"),
@@ -3969,7 +4003,7 @@ var pxt;
3969
4003
  'pxt_controls_for_of': {
3970
4004
  name: pxt.Util.lf("a loop that repeats for each value in an array"),
3971
4005
  tooltip: pxt.Util.lf("Have the variable '{0}' take the value of each item in the array one by one, and do the specified blocks."),
3972
- url: 'blocks/loops/for-of',
4006
+ url: '/blocks/loops/for-of',
3973
4007
  category: 'loops',
3974
4008
  block: {
3975
4009
  message0: pxt.Util.lf("for element %1 of %2"),
@@ -3980,7 +4014,7 @@ var pxt;
3980
4014
  'controls_for_of': {
3981
4015
  name: pxt.Util.lf("a loop that repeats for each value in an array"),
3982
4016
  tooltip: pxt.Util.lf("Have the variable '{0}' take the value of each item in the array one by one, and do the specified blocks."),
3983
- url: 'blocks/loops/for-of',
4017
+ url: '/blocks/loops/for-of',
3984
4018
  category: 'loops',
3985
4019
  block: {
3986
4020
  message0: pxt.Util.lf("for element %1 of %2"),
@@ -4265,7 +4299,7 @@ var pxt;
4265
4299
  'text': {
4266
4300
  name: pxt.Util.lf("a piece of text"),
4267
4301
  tooltip: pxt.Util.lf("A letter, word, or line of text."),
4268
- url: 'types/string',
4302
+ url: '/types/string',
4269
4303
  category: 'text',
4270
4304
  block: {
4271
4305
  search: pxt.Util.lf("a piece of text") // Only used for search; this string is not surfaced in the block's text
@@ -4274,7 +4308,7 @@ var pxt;
4274
4308
  'text_length': {
4275
4309
  name: pxt.Util.lf("number of characters in the string"),
4276
4310
  tooltip: pxt.Util.lf("Returns the number of letters (including spaces) in the provided text."),
4277
- url: 'reference/text/length',
4311
+ url: '/reference/text/length',
4278
4312
  category: 'text',
4279
4313
  block: {
4280
4314
  TEXT_LENGTH_TITLE: pxt.Util.lf("length of %1")
@@ -4283,7 +4317,7 @@ var pxt;
4283
4317
  'text_join': {
4284
4318
  name: pxt.Util.lf("join items to create text"),
4285
4319
  tooltip: pxt.Util.lf("Create a piece of text by joining together any number of items."),
4286
- url: 'reference/text/join',
4320
+ url: '/reference/text/join',
4287
4321
  category: 'text',
4288
4322
  block: {
4289
4323
  TEXT_JOIN_TITLE_CREATEWITH: pxt.Util.lf("join")
@@ -4292,7 +4326,7 @@ var pxt;
4292
4326
  'procedures_defnoreturn': {
4293
4327
  name: pxt.Util.lf("define the function"),
4294
4328
  tooltip: pxt.Util.lf("Create a function."),
4295
- url: 'types/function/define',
4329
+ url: '/types/function/define',
4296
4330
  category: 'functions',
4297
4331
  block: {
4298
4332
  PROCEDURES_DEFNORETURN_TITLE: pxt.Util.lf("function"),
@@ -4302,7 +4336,7 @@ var pxt;
4302
4336
  'procedures_callnoreturn': {
4303
4337
  name: pxt.Util.lf("call the function"),
4304
4338
  tooltip: pxt.Util.lf("Call the user-defined function."),
4305
- url: 'types/function/call',
4339
+ url: '/types/function/call',
4306
4340
  category: 'functions',
4307
4341
  block: {
4308
4342
  PROCEDURES_CALLNORETURN_TITLE: pxt.Util.lf("call function")
@@ -4311,7 +4345,7 @@ var pxt;
4311
4345
  'function_return': {
4312
4346
  name: pxt.Util.lf("return a value from within a function"),
4313
4347
  tooltip: pxt.Util.lf("Return a value from within a user-defined function."),
4314
- url: 'types/function/return',
4348
+ url: '/types/function/return',
4315
4349
  category: 'functions',
4316
4350
  block: {
4317
4351
  message_with_value: pxt.Util.lf("return %1"),
@@ -4321,7 +4355,7 @@ var pxt;
4321
4355
  'function_definition': {
4322
4356
  name: pxt.Util.lf("define the function"),
4323
4357
  tooltip: pxt.Util.lf("Create a function."),
4324
- url: 'types/function/define',
4358
+ url: '/types/function/define',
4325
4359
  category: 'functions',
4326
4360
  block: {
4327
4361
  FUNCTIONS_EDIT_OPTION: pxt.Util.lf("Edit Function")
@@ -4330,7 +4364,7 @@ var pxt;
4330
4364
  'function_call': {
4331
4365
  name: pxt.Util.lf("call the function"),
4332
4366
  tooltip: pxt.Util.lf("Call the user-defined function."),
4333
- url: 'types/function/call',
4367
+ url: '/types/function/call',
4334
4368
  category: 'functions',
4335
4369
  block: {
4336
4370
  FUNCTIONS_CALL_TITLE: pxt.Util.lf("call"),
@@ -4340,7 +4374,7 @@ var pxt;
4340
4374
  'function_call_output': {
4341
4375
  name: pxt.Util.lf("call the function with a return value"),
4342
4376
  tooltip: pxt.Util.lf("Call the user-defined function with a return value."),
4343
- url: 'types/function/call',
4377
+ url: '/types/function/call',
4344
4378
  category: 'functions',
4345
4379
  block: {}
4346
4380
  }