pxt-core 8.4.2 → 8.4.3
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 -0
- package/built/cli.js +83 -75
- package/built/pxt.js +1264 -176
- package/built/pxtblockly.js +323 -40
- package/built/pxtblocks.d.ts +30 -7
- package/built/pxtblocks.js +324 -41
- package/built/pxtlib.d.ts +91 -5
- package/built/pxtlib.js +1173 -98
- package/built/pxtsim.js +8 -3
- package/built/server.js +4 -0
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/multiplayer/css/main.2dd69ed8.css +4 -0
- package/built/web/multiplayer/js/main.f3b8f930.js +2 -0
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +1 -1
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +2 -2
- package/built/web/react-common-authcode.css +4 -6993
- package/built/web/react-common-multiplayer.css +13 -0
- package/built/web/react-common-skillmap.css +1 -1
- package/built/web/rtlreact-common-authcode.css +13 -0
- package/built/web/rtlreact-common-multiplayer.css +13 -0
- package/built/web/rtlreact-common-skillmap.css +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/semantic.css +1 -1
- package/built/web/skillmap/js/main.a6cf40e1.chunk.js +1 -0
- package/common-docs/identity/sign-in.md +17 -3
- package/common-docs/static/music-editor/apple.png +0 -0
- package/common-docs/static/music-editor/burger.png +0 -0
- package/common-docs/static/music-editor/cake.png +0 -0
- package/common-docs/static/music-editor/car.png +0 -0
- package/common-docs/static/music-editor/cat.png +0 -0
- package/common-docs/static/music-editor/cherry.png +0 -0
- package/common-docs/static/music-editor/clam.png +0 -0
- package/common-docs/static/music-editor/computer.png +0 -0
- package/common-docs/static/music-editor/crab.png +0 -0
- package/common-docs/static/music-editor/dog.png +0 -0
- package/common-docs/static/music-editor/duck.png +0 -0
- package/common-docs/static/music-editor/egg.png +0 -0
- package/common-docs/static/music-editor/explosion.png +0 -0
- package/common-docs/static/music-editor/fish.png +0 -0
- package/common-docs/static/music-editor/ice-cream.png +0 -0
- package/common-docs/static/music-editor/lemon.png +0 -0
- package/common-docs/static/music-editor/metronomeWorker.js +35 -0
- package/common-docs/static/music-editor/snake.png +0 -0
- package/common-docs/static/music-editor/star.png +0 -0
- package/common-docs/static/music-editor/strawberry.png +0 -0
- package/common-docs/static/music-editor/taco.png +0 -0
- package/common-docs/static/music-editor/treble-clef.svg +1 -0
- package/package.json +4 -2
- package/react-common/components/controls/Input.tsx +7 -3
- package/react-common/styles/controls/Button.less +9 -0
- package/react-common/styles/react-common-authcode-core.less +1 -1
- package/react-common/styles/react-common-authcode.less +1 -1
- package/react-common/styles/react-common-multiplayer-core.less +10 -0
- package/react-common/styles/react-common-multiplayer.less +12 -0
- package/theme/highcontrast.less +6 -0
- package/theme/music-editor/EditControls.less +22 -0
- package/theme/music-editor/MusicEditor.less +25 -0
- package/theme/music-editor/Note.less +16 -0
- package/theme/music-editor/NoteGroup.less +7 -0
- package/theme/music-editor/PlaybackControls.less +55 -0
- package/theme/music-editor/ScrollableWorkspace.less +3 -0
- package/theme/music-editor/Staff.less +31 -0
- package/theme/music-editor/Track.less +0 -0
- package/theme/music-editor/TrackSelector.less +48 -0
- package/theme/music-editor/Workspace.less +3 -0
- package/theme/pxt.less +1 -0
- package/theme/tutorial-sidebar.less +3 -0
- package/webapp/public/multiplayer.html +1 -0
- package/webapp/public/skillmap.html +1 -1
- package/built/web/skillmap/js/main.6eec9e0f.chunk.js +0 -1
package/built/pxtblocks.js
CHANGED
|
@@ -2401,6 +2401,7 @@ var pxt;
|
|
|
2401
2401
|
registerFieldEditor('melody', pxtblockly.FieldCustomMelody);
|
|
2402
2402
|
registerFieldEditor('soundeffect', pxtblockly.FieldSoundEffect);
|
|
2403
2403
|
registerFieldEditor('autocomplete', pxtblockly.FieldAutoComplete);
|
|
2404
|
+
registerFieldEditor('musiceditor', pxtblockly.FieldMusicEditor);
|
|
2404
2405
|
}
|
|
2405
2406
|
blocks.initFieldEditors = initFieldEditors;
|
|
2406
2407
|
function registerFieldEditor(selector, field, validator) {
|
|
@@ -8022,50 +8023,159 @@ var pxtblockly;
|
|
|
8022
8023
|
const project = pxt.react.getTilemapProject();
|
|
8023
8024
|
pxt.sprite.addMissingTilemapTilesAndReferences(project, this.asset);
|
|
8024
8025
|
break;
|
|
8026
|
+
case "song" /* pxt.AssetType.Song */:
|
|
8027
|
+
editorKind = "music-editor";
|
|
8028
|
+
params.temporaryAssets = pxtblockly.getTemporaryAssets(this.sourceBlock_.workspace, "song" /* pxt.AssetType.Song */);
|
|
8029
|
+
pxtblockly.setMelodyEditorOpen(this.sourceBlock_, true);
|
|
8030
|
+
break;
|
|
8031
|
+
}
|
|
8032
|
+
if (this.isFullscreen()) {
|
|
8033
|
+
this.showEditorFullscreen(editorKind, params);
|
|
8034
|
+
}
|
|
8035
|
+
else {
|
|
8036
|
+
this.showEditorInWidgetDiv(editorKind, params);
|
|
8025
8037
|
}
|
|
8038
|
+
}
|
|
8039
|
+
showEditorFullscreen(editorKind, params) {
|
|
8026
8040
|
const fv = pxt.react.getFieldEditorView(editorKind, this.asset, params);
|
|
8027
8041
|
if (this.undoRedoState) {
|
|
8028
8042
|
fv.restorePersistentData(this.undoRedoState);
|
|
8029
8043
|
}
|
|
8030
8044
|
pxt.react.getTilemapProject().pushUndo();
|
|
8031
8045
|
fv.onHide(() => {
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8046
|
+
this.onFieldEditorHide(fv);
|
|
8047
|
+
});
|
|
8048
|
+
fv.show();
|
|
8049
|
+
}
|
|
8050
|
+
showEditorInWidgetDiv(editorKind, params) {
|
|
8051
|
+
let bbox;
|
|
8052
|
+
// This is due to the changes in https://github.com/microsoft/pxt-blockly/pull/289
|
|
8053
|
+
// which caused the widgetdiv to jump around if any fields underneath changed size
|
|
8054
|
+
let widgetOwner = {
|
|
8055
|
+
getScaledBBox: () => bbox
|
|
8056
|
+
};
|
|
8057
|
+
Blockly.WidgetDiv.show(widgetOwner, this.sourceBlock_.RTL, () => {
|
|
8058
|
+
if (document.activeElement && document.activeElement.tagName === "INPUT")
|
|
8059
|
+
document.activeElement.blur();
|
|
8060
|
+
fv.hide();
|
|
8061
|
+
widgetDiv.classList.remove("sound-effect-editor-widget");
|
|
8062
|
+
widgetDiv.style.transform = "";
|
|
8063
|
+
widgetDiv.style.position = "";
|
|
8064
|
+
widgetDiv.style.left = "";
|
|
8065
|
+
widgetDiv.style.top = "";
|
|
8066
|
+
widgetDiv.style.width = "";
|
|
8067
|
+
widgetDiv.style.height = "";
|
|
8068
|
+
widgetDiv.style.opacity = "";
|
|
8069
|
+
widgetDiv.style.transition = "";
|
|
8070
|
+
widgetDiv.style.alignItems = "";
|
|
8071
|
+
this.onFieldEditorHide(fv);
|
|
8072
|
+
});
|
|
8073
|
+
const widgetDiv = Blockly.WidgetDiv.DIV;
|
|
8074
|
+
const fv = pxt.react.getFieldEditorView(editorKind, this.asset, params, widgetDiv);
|
|
8075
|
+
const block = this.sourceBlock_;
|
|
8076
|
+
const bounds = block.getBoundingRectangle();
|
|
8077
|
+
const coord = pxtblockly.workspaceToScreenCoordinates(block.workspace, new Blockly.utils.Coordinate(bounds.right, bounds.top));
|
|
8078
|
+
const animationDistance = 20;
|
|
8079
|
+
const left = coord.x - 400;
|
|
8080
|
+
const top = coord.y + 60 - animationDistance;
|
|
8081
|
+
widgetDiv.style.opacity = "0";
|
|
8082
|
+
widgetDiv.classList.add("sound-effect-editor-widget");
|
|
8083
|
+
widgetDiv.style.position = "absolute";
|
|
8084
|
+
widgetDiv.style.left = left + "px";
|
|
8085
|
+
widgetDiv.style.top = top + "px";
|
|
8086
|
+
widgetDiv.style.width = "50rem";
|
|
8087
|
+
widgetDiv.style.height = "34.25rem";
|
|
8088
|
+
widgetDiv.style.display = "flex";
|
|
8089
|
+
widgetDiv.style.alignItems = "center";
|
|
8090
|
+
widgetDiv.style.transition = "transform 0.25s ease 0s, opacity 0.25s ease 0s";
|
|
8091
|
+
widgetDiv.style.borderRadius = "";
|
|
8092
|
+
fv.onHide(() => {
|
|
8093
|
+
Blockly.WidgetDiv.hideIfOwner(widgetOwner);
|
|
8094
|
+
});
|
|
8095
|
+
fv.show();
|
|
8096
|
+
const divBounds = widgetDiv.getBoundingClientRect();
|
|
8097
|
+
const injectDivBounds = block.workspace.getInjectionDiv().getBoundingClientRect();
|
|
8098
|
+
if (divBounds.height > injectDivBounds.height) {
|
|
8099
|
+
widgetDiv.style.height = "";
|
|
8100
|
+
widgetDiv.style.top = `calc(1rem - ${animationDistance}px)`;
|
|
8101
|
+
widgetDiv.style.bottom = `calc(1rem + ${animationDistance}px)`;
|
|
8102
|
+
}
|
|
8103
|
+
else {
|
|
8104
|
+
if (divBounds.bottom > injectDivBounds.bottom || divBounds.top < injectDivBounds.top) {
|
|
8105
|
+
// This editor is pretty tall, so just center vertically on the inject div
|
|
8106
|
+
widgetDiv.style.top = (injectDivBounds.top + (injectDivBounds.height / 2) - (divBounds.height / 2)) - animationDistance + "px";
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8109
|
+
const toolboxWidth = block.workspace.getToolbox().getWidth();
|
|
8110
|
+
const workspaceLeft = injectDivBounds.left + toolboxWidth;
|
|
8111
|
+
if (divBounds.width > injectDivBounds.width - toolboxWidth) {
|
|
8112
|
+
widgetDiv.style.width = "";
|
|
8113
|
+
widgetDiv.style.left = "1rem";
|
|
8114
|
+
widgetDiv.style.right = "1rem";
|
|
8115
|
+
}
|
|
8116
|
+
else {
|
|
8117
|
+
// Check to see if we are bleeding off the right side of the canvas
|
|
8118
|
+
if (divBounds.left + divBounds.width >= injectDivBounds.right) {
|
|
8119
|
+
// If so, try and place to the left of the block instead of the right
|
|
8120
|
+
const blockLeft = pxtblockly.workspaceToScreenCoordinates(block.workspace, new Blockly.utils.Coordinate(bounds.left, bounds.top));
|
|
8121
|
+
if (blockLeft.x - divBounds.width - 20 > workspaceLeft) {
|
|
8122
|
+
widgetDiv.style.left = (blockLeft.x - divBounds.width - 20) + "px";
|
|
8064
8123
|
}
|
|
8065
|
-
|
|
8124
|
+
else {
|
|
8125
|
+
// As a last resort, just center on the inject div
|
|
8126
|
+
widgetDiv.style.left = (workspaceLeft + ((injectDivBounds.width - toolboxWidth) / 2) - divBounds.width / 2) + "px";
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
else if (divBounds.left < injectDivBounds.left) {
|
|
8130
|
+
widgetDiv.style.left = workspaceLeft + "px";
|
|
8066
8131
|
}
|
|
8132
|
+
}
|
|
8133
|
+
const finalDimensions = widgetDiv.getBoundingClientRect();
|
|
8134
|
+
bbox = new Blockly.utils.Rect(finalDimensions.top, finalDimensions.bottom, finalDimensions.left, finalDimensions.right);
|
|
8135
|
+
requestAnimationFrame(() => {
|
|
8136
|
+
widgetDiv.style.opacity = "1";
|
|
8137
|
+
widgetDiv.style.transform = `translateY(${animationDistance}px)`;
|
|
8067
8138
|
});
|
|
8068
|
-
|
|
8139
|
+
}
|
|
8140
|
+
onFieldEditorHide(fv) {
|
|
8141
|
+
var _a;
|
|
8142
|
+
const result = fv.getResult();
|
|
8143
|
+
const project = pxt.react.getTilemapProject();
|
|
8144
|
+
if (this.asset.type === "song" /* pxt.AssetType.Song */) {
|
|
8145
|
+
pxtblockly.setMelodyEditorOpen(this.sourceBlock_, false);
|
|
8146
|
+
}
|
|
8147
|
+
if (result) {
|
|
8148
|
+
const old = this.getValue();
|
|
8149
|
+
if (pxt.assetEquals(this.asset, result))
|
|
8150
|
+
return;
|
|
8151
|
+
const oldId = isTemporaryAsset(this.asset) ? null : this.asset.id;
|
|
8152
|
+
let newId = isTemporaryAsset(result) ? null : result.id;
|
|
8153
|
+
if (!oldId && newId === this.sourceBlock_.id) {
|
|
8154
|
+
// The temporary assets we create just use the block id as the id; give it something
|
|
8155
|
+
// a little nicer
|
|
8156
|
+
result.id = project.generateNewID(result.type);
|
|
8157
|
+
newId = result.id;
|
|
8158
|
+
}
|
|
8159
|
+
this.pendingEdit = true;
|
|
8160
|
+
if ((_a = result.meta) === null || _a === void 0 ? void 0 : _a.displayName)
|
|
8161
|
+
this.disposeOfTemporaryAsset();
|
|
8162
|
+
this.asset = result;
|
|
8163
|
+
const lastRevision = project.revision();
|
|
8164
|
+
this.onEditorClose(this.asset);
|
|
8165
|
+
this.updateAssetListener();
|
|
8166
|
+
this.updateAssetMeta();
|
|
8167
|
+
this.redrawPreview();
|
|
8168
|
+
this.undoRedoState = fv.getPersistentData();
|
|
8169
|
+
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
|
8170
|
+
const event = new BlocklyTilemapChange(this.sourceBlock_, 'field', this.name, old, this.getValue(), lastRevision, project.revision());
|
|
8171
|
+
if (oldId !== newId) {
|
|
8172
|
+
event.oldAssetId = oldId;
|
|
8173
|
+
event.newAssetId = newId;
|
|
8174
|
+
}
|
|
8175
|
+
Blockly.Events.fire(event);
|
|
8176
|
+
}
|
|
8177
|
+
this.pendingEdit = false;
|
|
8178
|
+
}
|
|
8069
8179
|
}
|
|
8070
8180
|
render_() {
|
|
8071
8181
|
if (this.isGreyBlock && !this.textElement_) {
|
|
@@ -8163,12 +8273,17 @@ var pxtblockly;
|
|
|
8163
8273
|
case "tilemap" /* pxt.AssetType.Tilemap */:
|
|
8164
8274
|
dataURI = pxtblockly.tilemapToImageURI(this.asset.data, PREVIEW_WIDTH, this.lightMode);
|
|
8165
8275
|
break;
|
|
8276
|
+
case "song" /* pxt.AssetType.Song */:
|
|
8277
|
+
dataURI = pxtblockly.songToDataURI(this.asset.song, 60, 20, this.lightMode);
|
|
8278
|
+
break;
|
|
8279
|
+
}
|
|
8280
|
+
if (dataURI) {
|
|
8281
|
+
const img = new svg.Image()
|
|
8282
|
+
.src(dataURI)
|
|
8283
|
+
.at(X_PADDING + BG_PADDING, Y_PADDING + BG_PADDING)
|
|
8284
|
+
.size(PREVIEW_WIDTH, PREVIEW_WIDTH);
|
|
8285
|
+
this.fieldGroup_.appendChild(img.el);
|
|
8166
8286
|
}
|
|
8167
|
-
const img = new svg.Image()
|
|
8168
|
-
.src(dataURI)
|
|
8169
|
-
.at(X_PADDING + BG_PADDING, Y_PADDING + BG_PADDING)
|
|
8170
|
-
.size(PREVIEW_WIDTH, PREVIEW_WIDTH);
|
|
8171
|
-
this.fieldGroup_.appendChild(img.el);
|
|
8172
8287
|
}
|
|
8173
8288
|
}
|
|
8174
8289
|
parseValueText(newText) {
|
|
@@ -8259,6 +8374,9 @@ var pxtblockly;
|
|
|
8259
8374
|
pxt.react.getTilemapProject().addChangeListener(this.asset, this.assetChangeListener);
|
|
8260
8375
|
}
|
|
8261
8376
|
}
|
|
8377
|
+
isFullscreen() {
|
|
8378
|
+
return true;
|
|
8379
|
+
}
|
|
8262
8380
|
}
|
|
8263
8381
|
pxtblockly.FieldAssetEditor = FieldAssetEditor;
|
|
8264
8382
|
function isTemporaryAsset(asset) {
|
|
@@ -10325,13 +10443,13 @@ var pxtblockly;
|
|
|
10325
10443
|
this.prevString = this.getValue();
|
|
10326
10444
|
// The webapp listens to this event and stops the simulator so that you don't get the melody
|
|
10327
10445
|
// playing twice (once in the editor and once when the code runs in the sim)
|
|
10328
|
-
|
|
10446
|
+
pxtblockly.setMelodyEditorOpen(this.sourceBlock_, true);
|
|
10329
10447
|
Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_, () => {
|
|
10330
10448
|
this.onEditorClose();
|
|
10331
10449
|
// revert all style attributes for dropdown div
|
|
10332
10450
|
pxt.BrowserUtils.removeClass(contentDiv, "melody-content-div");
|
|
10333
10451
|
pxt.BrowserUtils.removeClass(contentDiv.parentElement, "melody-editor-dropdown");
|
|
10334
|
-
|
|
10452
|
+
pxtblockly.setMelodyEditorOpen(this.sourceBlock_, false);
|
|
10335
10453
|
});
|
|
10336
10454
|
}
|
|
10337
10455
|
getValue() {
|
|
@@ -10999,6 +11117,116 @@ var pxtblockly;
|
|
|
10999
11117
|
return "#DCDCDC";
|
|
11000
11118
|
}
|
|
11001
11119
|
})(pxtblockly || (pxtblockly = {}));
|
|
11120
|
+
/// <reference path="../../built/pxtlib.d.ts" />
|
|
11121
|
+
/// <reference path="./field_asset.ts" />
|
|
11122
|
+
var pxtblockly;
|
|
11123
|
+
(function (pxtblockly) {
|
|
11124
|
+
var svg = pxt.svgUtil;
|
|
11125
|
+
const PREVIEW_HEIGHT = 32;
|
|
11126
|
+
const X_PADDING = 5;
|
|
11127
|
+
const Y_PADDING = 1;
|
|
11128
|
+
const BG_PADDING = 4;
|
|
11129
|
+
const BG_HEIGHT = BG_PADDING * 2 + PREVIEW_HEIGHT;
|
|
11130
|
+
const TOTAL_HEIGHT = Y_PADDING * 2 + BG_PADDING * 2 + PREVIEW_HEIGHT;
|
|
11131
|
+
class FieldMusicEditor extends pxtblockly.FieldAssetEditor {
|
|
11132
|
+
getAssetType() {
|
|
11133
|
+
return "song" /* pxt.AssetType.Song */;
|
|
11134
|
+
}
|
|
11135
|
+
createNewAsset(text) {
|
|
11136
|
+
const project = pxt.react.getTilemapProject();
|
|
11137
|
+
if (text) {
|
|
11138
|
+
const asset = pxt.lookupProjectAssetByTSReference(text, project);
|
|
11139
|
+
if (asset)
|
|
11140
|
+
return asset;
|
|
11141
|
+
}
|
|
11142
|
+
if (this.getBlockData()) {
|
|
11143
|
+
return project.lookupAsset("song" /* pxt.AssetType.Song */, this.getBlockData());
|
|
11144
|
+
}
|
|
11145
|
+
let song;
|
|
11146
|
+
if (text) {
|
|
11147
|
+
const match = /^\s*hex\s*`([a-fA-F0-9]+)`\s*(?:;?)\s*$/.exec(text);
|
|
11148
|
+
if (match) {
|
|
11149
|
+
song = pxt.assets.music.decodeSongFromHex(match[1]);
|
|
11150
|
+
}
|
|
11151
|
+
}
|
|
11152
|
+
else {
|
|
11153
|
+
song = pxt.assets.music.getEmptySong(2);
|
|
11154
|
+
}
|
|
11155
|
+
if (!song) {
|
|
11156
|
+
this.isGreyBlock = true;
|
|
11157
|
+
this.valueText = text;
|
|
11158
|
+
return undefined;
|
|
11159
|
+
}
|
|
11160
|
+
else {
|
|
11161
|
+
// Restore all of the unused tracks
|
|
11162
|
+
pxt.assets.music.inflateSong(song);
|
|
11163
|
+
}
|
|
11164
|
+
const newAsset = {
|
|
11165
|
+
internalID: -1,
|
|
11166
|
+
id: this.sourceBlock_.id,
|
|
11167
|
+
type: "song" /* pxt.AssetType.Song */,
|
|
11168
|
+
meta: {},
|
|
11169
|
+
song
|
|
11170
|
+
};
|
|
11171
|
+
return newAsset;
|
|
11172
|
+
}
|
|
11173
|
+
render_() {
|
|
11174
|
+
super.render_();
|
|
11175
|
+
if (!this.isGreyBlock) {
|
|
11176
|
+
this.size_.height = TOTAL_HEIGHT;
|
|
11177
|
+
this.size_.width = X_PADDING * 2 + BG_PADDING * 2 + this.previewWidth();
|
|
11178
|
+
}
|
|
11179
|
+
}
|
|
11180
|
+
getValueText() {
|
|
11181
|
+
if (this.asset && !this.isTemporaryAsset()) {
|
|
11182
|
+
return pxt.getTSReferenceForAsset(this.asset);
|
|
11183
|
+
}
|
|
11184
|
+
return this.asset ? `hex\`${pxt.assets.music.encodeSongToHex(this.asset.song)}\`` : "";
|
|
11185
|
+
}
|
|
11186
|
+
parseFieldOptions(opts) {
|
|
11187
|
+
return {};
|
|
11188
|
+
}
|
|
11189
|
+
isFullscreen() {
|
|
11190
|
+
return false;
|
|
11191
|
+
}
|
|
11192
|
+
redrawPreview() {
|
|
11193
|
+
var _a;
|
|
11194
|
+
if (!this.fieldGroup_)
|
|
11195
|
+
return;
|
|
11196
|
+
pxsim.U.clear(this.fieldGroup_);
|
|
11197
|
+
if (this.isGreyBlock) {
|
|
11198
|
+
super.redrawPreview();
|
|
11199
|
+
return;
|
|
11200
|
+
}
|
|
11201
|
+
const totalWidth = X_PADDING * 2 + BG_PADDING * 2 + this.previewWidth();
|
|
11202
|
+
const bg = new svg.Rect()
|
|
11203
|
+
.at(X_PADDING, Y_PADDING)
|
|
11204
|
+
.size(BG_PADDING * 2 + this.previewWidth(), BG_HEIGHT)
|
|
11205
|
+
.setClass("blocklySpriteField")
|
|
11206
|
+
.stroke("#898989", 1)
|
|
11207
|
+
.corner(4);
|
|
11208
|
+
this.fieldGroup_.appendChild(bg.el);
|
|
11209
|
+
if (this.asset) {
|
|
11210
|
+
const dataURI = pxtblockly.songToDataURI(this.asset.song, this.previewWidth(), PREVIEW_HEIGHT, this.lightMode);
|
|
11211
|
+
if (dataURI) {
|
|
11212
|
+
const img = new svg.Image()
|
|
11213
|
+
.src(dataURI)
|
|
11214
|
+
.at(X_PADDING + BG_PADDING, Y_PADDING + BG_PADDING)
|
|
11215
|
+
.size(this.previewWidth(), PREVIEW_HEIGHT);
|
|
11216
|
+
this.fieldGroup_.appendChild(img.el);
|
|
11217
|
+
}
|
|
11218
|
+
}
|
|
11219
|
+
if (((_a = this.size_) === null || _a === void 0 ? void 0 : _a.width) != totalWidth) {
|
|
11220
|
+
this.forceRerender();
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
previewWidth() {
|
|
11224
|
+
const measures = this.asset ? this.asset.song.measures : 2;
|
|
11225
|
+
return measures * PREVIEW_HEIGHT;
|
|
11226
|
+
}
|
|
11227
|
+
}
|
|
11228
|
+
pxtblockly.FieldMusicEditor = FieldMusicEditor;
|
|
11229
|
+
})(pxtblockly || (pxtblockly = {}));
|
|
11002
11230
|
/// <reference path="../../localtypings/pxtblockly.d.ts" />
|
|
11003
11231
|
var pxtblockly;
|
|
11004
11232
|
(function (pxtblockly) {
|
|
@@ -13534,6 +13762,54 @@ var pxtblockly;
|
|
|
13534
13762
|
return canvas.toDataURL();
|
|
13535
13763
|
}
|
|
13536
13764
|
pxtblockly.tilemapToImageURI = tilemapToImageURI;
|
|
13765
|
+
function songToDataURI(song, width, height, lightMode, maxMeasures) {
|
|
13766
|
+
const colors = pxt.appTarget.runtime.palette.slice();
|
|
13767
|
+
const canvas = document.createElement("canvas");
|
|
13768
|
+
canvas.width = width;
|
|
13769
|
+
canvas.height = height;
|
|
13770
|
+
let context;
|
|
13771
|
+
if (lightMode) {
|
|
13772
|
+
context = canvas.getContext("2d", { alpha: false });
|
|
13773
|
+
context.fillStyle = "#dedede";
|
|
13774
|
+
context.fillRect(0, 0, width, height);
|
|
13775
|
+
}
|
|
13776
|
+
else {
|
|
13777
|
+
context = canvas.getContext("2d");
|
|
13778
|
+
}
|
|
13779
|
+
const trackColors = [
|
|
13780
|
+
5,
|
|
13781
|
+
11,
|
|
13782
|
+
5,
|
|
13783
|
+
4,
|
|
13784
|
+
2,
|
|
13785
|
+
6,
|
|
13786
|
+
14,
|
|
13787
|
+
2,
|
|
13788
|
+
5,
|
|
13789
|
+
1, // explosion
|
|
13790
|
+
];
|
|
13791
|
+
maxMeasures = maxMeasures || song.measures;
|
|
13792
|
+
const cellWidth = Math.max(Math.floor(width / (song.beatsPerMeasure * maxMeasures * 2)), 1);
|
|
13793
|
+
const cellsShown = Math.floor(width / cellWidth);
|
|
13794
|
+
const cellHeight = Math.max(Math.floor(height / 12), 1);
|
|
13795
|
+
const notesShown = Math.floor(height / cellHeight);
|
|
13796
|
+
for (const track of song.tracks) {
|
|
13797
|
+
for (const noteEvent of track.notes) {
|
|
13798
|
+
const col = Math.floor(noteEvent.startTick / (song.ticksPerBeat / 2));
|
|
13799
|
+
if (col > cellsShown)
|
|
13800
|
+
break;
|
|
13801
|
+
for (const note of noteEvent.notes) {
|
|
13802
|
+
const row = 12 - (note % 12);
|
|
13803
|
+
if (row > notesShown)
|
|
13804
|
+
continue;
|
|
13805
|
+
context.fillStyle = colors[trackColors[track.id || song.tracks.indexOf(track)]];
|
|
13806
|
+
context.fillRect(col * cellWidth, row * cellHeight, cellWidth, cellHeight);
|
|
13807
|
+
}
|
|
13808
|
+
}
|
|
13809
|
+
}
|
|
13810
|
+
return canvas.toDataURL();
|
|
13811
|
+
}
|
|
13812
|
+
pxtblockly.songToDataURI = songToDataURI;
|
|
13537
13813
|
function deleteTilesetTileIfExists(ws, tile) {
|
|
13538
13814
|
const existing = ws.getVariablesOfType(pxt.sprite.BLOCKLY_TILESET_TYPE);
|
|
13539
13815
|
for (const model of existing) {
|
|
@@ -13669,10 +13945,17 @@ var pxtblockly;
|
|
|
13669
13945
|
case "animation" /* pxt.AssetType.Animation */:
|
|
13670
13946
|
return getAllFields(workspace, field => field instanceof pxtblockly.FieldAnimationEditor && field.isTemporaryAsset())
|
|
13671
13947
|
.map(f => f.ref.getAsset());
|
|
13948
|
+
case "song" /* pxt.AssetType.Song */:
|
|
13949
|
+
return getAllFields(workspace, field => field instanceof pxtblockly.FieldMusicEditor && field.isTemporaryAsset())
|
|
13950
|
+
.map(f => f.ref.getAsset());
|
|
13672
13951
|
default: return [];
|
|
13673
13952
|
}
|
|
13674
13953
|
}
|
|
13675
13954
|
pxtblockly.getTemporaryAssets = getTemporaryAssets;
|
|
13955
|
+
function setMelodyEditorOpen(block, isOpen) {
|
|
13956
|
+
Blockly.Events.fire(new Blockly.Events.Ui(block, "melody-editor", !isOpen, isOpen));
|
|
13957
|
+
}
|
|
13958
|
+
pxtblockly.setMelodyEditorOpen = setMelodyEditorOpen;
|
|
13676
13959
|
function workspaceToScreenCoordinates(ws, wsCoordinates) {
|
|
13677
13960
|
// The position in pixels relative to the origin of the
|
|
13678
13961
|
// main workspace.
|
package/built/pxtlib.d.ts
CHANGED
|
@@ -99,6 +99,7 @@ declare namespace pxt.auth {
|
|
|
99
99
|
protected abstract onProfileDeleted(userId: string): Promise<void>;
|
|
100
100
|
protected abstract onApiError(err: any): Promise<void>;
|
|
101
101
|
protected abstract onStateCleared(): Promise<void>;
|
|
102
|
+
authTokenAsync(): Promise<string>;
|
|
102
103
|
/**
|
|
103
104
|
* Starts the process of authenticating the user against the given identity
|
|
104
105
|
* provider. Upon success the backend will write an http-only session cookie
|
|
@@ -177,6 +178,7 @@ declare namespace pxt.auth {
|
|
|
177
178
|
function hasIdentity(): boolean;
|
|
178
179
|
function enableAuth(enabled?: boolean): void;
|
|
179
180
|
function userName(user: pxt.auth.UserProfile): string;
|
|
181
|
+
function firstName(user: pxt.auth.UserProfile): string;
|
|
180
182
|
function userInitials(user: pxt.auth.UserProfile): string;
|
|
181
183
|
function generateUserProfilePicDataUrl(profile: pxt.auth.UserProfile): void;
|
|
182
184
|
/**
|
|
@@ -554,6 +556,7 @@ declare namespace pxt {
|
|
|
554
556
|
asseteditorUrl?: string;
|
|
555
557
|
skillmapUrl?: string;
|
|
556
558
|
authcodeUrl?: string;
|
|
559
|
+
multiplayerUrl?: string;
|
|
557
560
|
isStatic?: boolean;
|
|
558
561
|
verprefix?: string;
|
|
559
562
|
}
|
|
@@ -1613,6 +1616,8 @@ declare namespace pxt.sprite {
|
|
|
1613
1616
|
const IMAGE_PREFIX = "image";
|
|
1614
1617
|
const ANIMATION_NAMESPACE = "myAnimations";
|
|
1615
1618
|
const ANIMATION_PREFIX = "anim";
|
|
1619
|
+
const SONG_NAMESPACE = "mySongs";
|
|
1620
|
+
const SONG_PREFIX = "song";
|
|
1616
1621
|
interface Coord {
|
|
1617
1622
|
x: number;
|
|
1618
1623
|
y: number;
|
|
@@ -1764,6 +1769,66 @@ declare namespace pxt.storage.shared {
|
|
|
1764
1769
|
function setAsync(container: string, key: string, val: any): Promise<void>;
|
|
1765
1770
|
function delAsync(container: string, key: string): Promise<void>;
|
|
1766
1771
|
}
|
|
1772
|
+
declare namespace pxt.assets.music {
|
|
1773
|
+
interface Instrument {
|
|
1774
|
+
waveform: number;
|
|
1775
|
+
ampEnvelope: Envelope;
|
|
1776
|
+
pitchEnvelope?: Envelope;
|
|
1777
|
+
ampLFO?: LFO;
|
|
1778
|
+
pitchLFO?: LFO;
|
|
1779
|
+
octave?: number;
|
|
1780
|
+
}
|
|
1781
|
+
interface Envelope {
|
|
1782
|
+
attack: number;
|
|
1783
|
+
decay: number;
|
|
1784
|
+
sustain: number;
|
|
1785
|
+
release: number;
|
|
1786
|
+
amplitude: number;
|
|
1787
|
+
}
|
|
1788
|
+
interface LFO {
|
|
1789
|
+
frequency: number;
|
|
1790
|
+
amplitude: number;
|
|
1791
|
+
}
|
|
1792
|
+
interface Song {
|
|
1793
|
+
measures: number;
|
|
1794
|
+
beatsPerMeasure: number;
|
|
1795
|
+
beatsPerMinute: number;
|
|
1796
|
+
ticksPerBeat: number;
|
|
1797
|
+
tracks: Track[];
|
|
1798
|
+
}
|
|
1799
|
+
interface Track {
|
|
1800
|
+
instrument: Instrument;
|
|
1801
|
+
id: number;
|
|
1802
|
+
name?: string;
|
|
1803
|
+
iconURI?: string;
|
|
1804
|
+
drums?: DrumInstrument[];
|
|
1805
|
+
notes: NoteEvent[];
|
|
1806
|
+
}
|
|
1807
|
+
interface NoteEvent {
|
|
1808
|
+
notes: number[];
|
|
1809
|
+
startTick: number;
|
|
1810
|
+
endTick: number;
|
|
1811
|
+
}
|
|
1812
|
+
interface DrumSoundStep {
|
|
1813
|
+
waveform: number;
|
|
1814
|
+
frequency: number;
|
|
1815
|
+
volume: number;
|
|
1816
|
+
duration: number;
|
|
1817
|
+
}
|
|
1818
|
+
interface DrumInstrument {
|
|
1819
|
+
startFrequency: number;
|
|
1820
|
+
startVolume: number;
|
|
1821
|
+
steps: DrumSoundStep[];
|
|
1822
|
+
}
|
|
1823
|
+
function renderInstrument(instrument: Instrument, noteFrequency: number, gateLength: number, volume: number): Uint8Array;
|
|
1824
|
+
function renderDrumInstrument(sound: DrumInstrument, volume: number): Uint8Array;
|
|
1825
|
+
function encodeSongToHex(song: Song): string;
|
|
1826
|
+
function decodeSongFromHex(hex: string): Song;
|
|
1827
|
+
function cloneSong(song: Song): Song;
|
|
1828
|
+
function songEquals(a: Song, b: Song): boolean;
|
|
1829
|
+
function inflateSong(song: pxt.assets.music.Song): void;
|
|
1830
|
+
function getEmptySong(measures: number): pxt.assets.music.Song;
|
|
1831
|
+
}
|
|
1767
1832
|
declare namespace pxt {
|
|
1768
1833
|
class Package {
|
|
1769
1834
|
id: string;
|
|
@@ -2607,11 +2672,13 @@ declare namespace pxt {
|
|
|
2607
2672
|
export const IMAGE_MIME_TYPE = "image/x-mkcd-f4";
|
|
2608
2673
|
export const TILEMAP_MIME_TYPE = "application/mkcd-tilemap";
|
|
2609
2674
|
export const ANIMATION_MIME_TYPE = "application/mkcd-animation";
|
|
2675
|
+
export const SONG_MIME_TYPE = "application/mkcd-song";
|
|
2610
2676
|
export const enum AssetType {
|
|
2611
2677
|
Image = "image",
|
|
2612
2678
|
Tile = "tile",
|
|
2613
2679
|
Tilemap = "tilemap",
|
|
2614
|
-
Animation = "animation"
|
|
2680
|
+
Animation = "animation",
|
|
2681
|
+
Song = "song"
|
|
2615
2682
|
}
|
|
2616
2683
|
export interface AssetMetadata {
|
|
2617
2684
|
displayName?: string;
|
|
@@ -2623,7 +2690,7 @@ declare namespace pxt {
|
|
|
2623
2690
|
blockId: string;
|
|
2624
2691
|
fieldName: string;
|
|
2625
2692
|
}
|
|
2626
|
-
export type Asset = ProjectImage | Tile | Animation | ProjectTilemap;
|
|
2693
|
+
export type Asset = ProjectImage | Tile | Animation | ProjectTilemap | Song;
|
|
2627
2694
|
export interface BaseAsset {
|
|
2628
2695
|
internalID: number;
|
|
2629
2696
|
id: string;
|
|
@@ -2661,6 +2728,10 @@ declare namespace pxt {
|
|
|
2661
2728
|
type: AssetType.Tilemap;
|
|
2662
2729
|
data: pxt.sprite.TilemapData;
|
|
2663
2730
|
}
|
|
2731
|
+
export interface Song extends BaseAsset {
|
|
2732
|
+
type: AssetType.Song;
|
|
2733
|
+
song: assets.music.Song;
|
|
2734
|
+
}
|
|
2664
2735
|
export interface TilemapSnapshot {
|
|
2665
2736
|
revision: number;
|
|
2666
2737
|
projectTilemaps?: ProjectTilemap[];
|
|
@@ -2674,6 +2745,7 @@ declare namespace pxt {
|
|
|
2674
2745
|
tilemaps: AssetCollection<ProjectTilemap>;
|
|
2675
2746
|
images: AssetCollection<ProjectImage>;
|
|
2676
2747
|
animations: AssetCollection<Animation>;
|
|
2748
|
+
songs: AssetCollection<Song>;
|
|
2677
2749
|
}
|
|
2678
2750
|
interface AssetSnapshotDiff {
|
|
2679
2751
|
beforeRevision: number;
|
|
@@ -2682,6 +2754,7 @@ declare namespace pxt {
|
|
|
2682
2754
|
tilemaps: AssetCollectionDiff<ProjectTilemap>;
|
|
2683
2755
|
images: AssetCollectionDiff<ProjectImage>;
|
|
2684
2756
|
animations: AssetCollectionDiff<Animation>;
|
|
2757
|
+
songs: AssetCollectionDiff<Song>;
|
|
2685
2758
|
}
|
|
2686
2759
|
interface AssetUpdateListener {
|
|
2687
2760
|
internalID: number;
|
|
@@ -2734,6 +2807,7 @@ declare namespace pxt {
|
|
|
2734
2807
|
getProjectTiles(tileWidth: number, createIfMissing: boolean): TileSet | null;
|
|
2735
2808
|
createNewTile(data: pxt.sprite.BitmapData, id?: string, displayName?: string): Tile;
|
|
2736
2809
|
createNewProjectImage(data: pxt.sprite.BitmapData, displayName?: string): ProjectImage;
|
|
2810
|
+
createNewSong(data: pxt.assets.music.Song, displayName?: string): Song;
|
|
2737
2811
|
updateTile(tile: pxt.Tile): Tile;
|
|
2738
2812
|
deleteTile(id: string): void;
|
|
2739
2813
|
getProjectTilesetJRes(): Map<any>;
|
|
@@ -2775,6 +2849,10 @@ declare namespace pxt {
|
|
|
2775
2849
|
* assets.animation`shortId`
|
|
2776
2850
|
* assets.animation`displayName`
|
|
2777
2851
|
*
|
|
2852
|
+
* SONGS:
|
|
2853
|
+
* assets.song`shortId`
|
|
2854
|
+
* assets.song`displayName`
|
|
2855
|
+
*
|
|
2778
2856
|
* TILEMAPS:
|
|
2779
2857
|
* tilemap`shortId`
|
|
2780
2858
|
*
|
|
@@ -2787,36 +2865,43 @@ declare namespace pxt {
|
|
|
2787
2865
|
lookupAsset(assetType: AssetType.Tile, name: string): Tile;
|
|
2788
2866
|
lookupAsset(assetType: AssetType.Tilemap, name: string): ProjectTilemap;
|
|
2789
2867
|
lookupAsset(assetType: AssetType.Animation, name: string): Animation;
|
|
2868
|
+
lookupAsset(assetType: AssetType.Song, name: string): Song;
|
|
2790
2869
|
lookupAsset(assetType: AssetType, name: string): Asset;
|
|
2791
2870
|
lookupAssetByName(assetType: AssetType.Image, name: string): ProjectImage;
|
|
2792
2871
|
lookupAssetByName(assetType: AssetType.Tile, name: string): Tile;
|
|
2793
2872
|
lookupAssetByName(assetType: AssetType.Tilemap, name: string): ProjectTilemap;
|
|
2794
2873
|
lookupAssetByName(assetType: AssetType.Animation, name: string): Animation;
|
|
2874
|
+
lookupAssetByName(assetType: AssetType.Song, name: string): Song;
|
|
2795
2875
|
lookupAssetByName(assetType: AssetType, name: string): Asset;
|
|
2796
2876
|
getAssets(type: AssetType.Image): ProjectImage[];
|
|
2797
2877
|
getAssets(type: AssetType.Tile): Tile[];
|
|
2798
2878
|
getAssets(type: AssetType.Tilemap): ProjectTilemap[];
|
|
2799
2879
|
getAssets(type: AssetType.Animation): Animation[];
|
|
2880
|
+
getAssets(type: AssetType.Song): Song[];
|
|
2800
2881
|
getAssets(type: AssetType): Asset[];
|
|
2801
2882
|
getGalleryAssets(type: AssetType.Image): ProjectImage[];
|
|
2802
2883
|
getGalleryAssets(type: AssetType.Tile): Tile[];
|
|
2803
2884
|
getGalleryAssets(type: AssetType.Tilemap): ProjectTilemap[];
|
|
2804
2885
|
getGalleryAssets(type: AssetType.Animation): Animation[];
|
|
2886
|
+
getGalleryAssets(type: AssetType.Song): Song[];
|
|
2805
2887
|
getGalleryAssets(type: AssetType): Asset[];
|
|
2806
2888
|
lookupBlockAsset(assetType: AssetType.Image, blockID: string): ProjectImage;
|
|
2807
2889
|
lookupBlockAsset(assetType: AssetType.Tile, blockID: string): Tile;
|
|
2808
2890
|
lookupBlockAsset(assetType: AssetType.Tilemap, blockID: string): ProjectTilemap;
|
|
2809
2891
|
lookupBlockAsset(assetType: AssetType.Animation, blockID: string): Animation;
|
|
2892
|
+
lookupBlockAsset(assetType: AssetType.Song, blockID: string): Song;
|
|
2810
2893
|
lookupBlockAsset(assetType: AssetType, blockID: string): Asset;
|
|
2811
2894
|
updateAsset(asset: ProjectImage): ProjectImage;
|
|
2812
2895
|
updateAsset(asset: Tile): Tile;
|
|
2813
2896
|
updateAsset(asset: ProjectTilemap): ProjectTilemap;
|
|
2814
2897
|
updateAsset(asset: Animation): Animation;
|
|
2898
|
+
updateAsset(asset: Song): Song;
|
|
2815
2899
|
updateAsset(asset: Asset): Asset;
|
|
2816
2900
|
duplicateAsset(asset: ProjectImage, displayName?: string): ProjectImage;
|
|
2817
2901
|
duplicateAsset(asset: Tile, displayName?: string): Tile;
|
|
2818
2902
|
duplicateAsset(asset: ProjectTilemap, displayName?: string): ProjectTilemap;
|
|
2819
2903
|
duplicateAsset(asset: Animation, displayName?: string): Animation;
|
|
2904
|
+
duplicateAsset(asset: Song, displayName?: string): Song;
|
|
2820
2905
|
duplicateAsset(asset: Asset, displayName?: string): Asset;
|
|
2821
2906
|
removeAsset(asset: Asset): void;
|
|
2822
2907
|
addChangeListener(asset: Asset, listener: () => void): void;
|
|
@@ -2827,12 +2912,13 @@ declare namespace pxt {
|
|
|
2827
2912
|
removeInactiveBlockAssets(activeBlockIDs: string[]): void;
|
|
2828
2913
|
protected generateImage(entry: JRes, type: AssetType.Image): ProjectImage;
|
|
2829
2914
|
protected generateImage(entry: JRes, type: AssetType.Tile): Tile;
|
|
2915
|
+
protected generateSong(entry: JRes): Song;
|
|
2830
2916
|
protected generateAnimation(entry: JRes): [Animation, boolean];
|
|
2831
|
-
protected inflateAnimation(animation: Animation, assets: (Tile | ProjectImage | Animation)[]): Animation;
|
|
2917
|
+
protected inflateAnimation(animation: Animation, assets: (Tile | ProjectImage | Animation | Song)[]): Animation;
|
|
2832
2918
|
generateNewID(type: AssetType): string;
|
|
2833
2919
|
protected generateNewIDInternal(type: AssetType, varPrefix: string, namespaceString?: string): string;
|
|
2834
2920
|
protected onChange(): void;
|
|
2835
|
-
protected readImages(allJRes: Map<JRes>, isProjectFile?: boolean): (Tile | ProjectImage | Animation)[];
|
|
2921
|
+
protected readImages(allJRes: Map<JRes>, isProjectFile?: boolean): (Tile | ProjectImage | Animation | Song)[];
|
|
2836
2922
|
protected cleanupTemporaryAssets(): void;
|
|
2837
2923
|
}
|
|
2838
2924
|
export function emitTilemapsFromJRes(jres: pxt.Map<JRes>): string;
|
|
@@ -2845,7 +2931,7 @@ declare namespace pxt {
|
|
|
2845
2931
|
type: string;
|
|
2846
2932
|
name: string;
|
|
2847
2933
|
};
|
|
2848
|
-
export function lookupProjectAssetByTSReference(ts: string, project: TilemapProject): Tile | ProjectImage | Animation | ProjectTilemap;
|
|
2934
|
+
export function lookupProjectAssetByTSReference(ts: string, project: TilemapProject): Tile | ProjectImage | Animation | ProjectTilemap | Song;
|
|
2849
2935
|
export function getDefaultAssetDisplayName(type: pxt.AssetType): string;
|
|
2850
2936
|
export function getShortIDForAsset(asset: pxt.Asset): string;
|
|
2851
2937
|
export {};
|