diff --git a/pxteditor/index.ts b/pxteditor/index.ts index 2f56bb4fd8c0..72ca0becced3 100644 --- a/pxteditor/index.ts +++ b/pxteditor/index.ts @@ -12,6 +12,7 @@ export * from "./monaco-fields/field_tilemap"; export * from "./monaco-fields/field_musiceditor"; export * from "./monaco-fields/field_soundEffect"; export * from "./monaco-fields/field_sprite"; +export * from "./monaco-fields/field_pianoroll"; export * from "./monaco-fields/field_animation"; export * from "./monaco-fields/field_react"; diff --git a/pxteditor/monaco-fields/field_musiceditor.ts b/pxteditor/monaco-fields/field_musiceditor.ts index 7186eb817b60..6f236e7351e8 100644 --- a/pxteditor/monaco-fields/field_musiceditor.ts +++ b/pxteditor/monaco-fields/field_musiceditor.ts @@ -71,12 +71,7 @@ export class MonacoSongEditor extends MonacoReactFieldEditor { } let out = pxt.getTSReferenceForAsset(result, this.isPython); if (!this.isAsset) { - if (this.isPython) { - out = `music.create_song(${out})`; - } - else { - out = `music.createSong(${out})`; - } + out = this.wrapCreateSong(out); } return out; } @@ -100,6 +95,15 @@ export class MonacoSongEditor extends MonacoReactFieldEditor { blocksInfo: this.host.blocksInfo() }; } + + protected wrapCreateSong(text: string): string { + if (this.isPython) { + return `music.create_song(${text})`; + } + else { + return `music.createSong(${text})`; + } + } } function createFakeAsset(song: pxt.assets.music.Song): pxt.Song { diff --git a/pxteditor/monaco-fields/field_pianoroll.ts b/pxteditor/monaco-fields/field_pianoroll.ts new file mode 100644 index 000000000000..d3bd5eda6c56 --- /dev/null +++ b/pxteditor/monaco-fields/field_pianoroll.ts @@ -0,0 +1,53 @@ +import { MonacoSongEditor } from "./field_musiceditor"; +import { registerMonacoFieldEditor } from "./monacoFieldEditor"; + +const fieldEditorId = "piano-roll-editor"; + +export class MonacoPianoRollEditor extends MonacoSongEditor { + protected wrapCreateSong(text: string): string { + if (this.isPython) { + return `pianoRoll.create_song(${text})`; + } + else { + return `pianoRoll.createSong(${text})`; + } + } + + protected getOptions(): any { + const opts = super.getOptions(); + opts.showTimeSignature = true; + opts.showSnapControls = true; + return opts; + } + + protected getFieldEditorId() { + return fieldEditorId; + } +} + +const regexes = [ + // typescript + "pianoRoll\\s*\\.\\s*createSong\\s*\\(\\s*hex`[a-fA-F0-9\\s\\n]*`\\s*\\)", + + // python + 'pianoRoll\\s*\\.\\s*create_song\\s*\\(\\s*hex\\s*\\(\\s*"""[a-fA-F0-9\\s\\n]*"""\\s*\\)\\s*\\)', + 'pianoRoll\\s*\\.\\s*createSong\\s*\\(\\s*hex\\s*\\(\\s*"""[a-fA-F0-9\\s\\n]*"""\\s*\\)\\s*\\)', +]; + +const searchString = regexes.map(r => `(?:${r})`).join("|"); + +export const pianoRollEditorDefinition: pxt.editor.MonacoFieldEditorDefinition = { + id: fieldEditorId, + foldMatches: true, + glyphCssClass: "fas fa-music sprite-focus-hover", + heightInPixels: 510, + matcher: { + searchString: searchString, + isRegex: true, + matchCase: true, + matchWholeWord: false + }, + proto: MonacoPianoRollEditor +}; + +registerMonacoFieldEditor(fieldEditorId, pianoRollEditorDefinition); \ No newline at end of file diff --git a/pxtlib/music.ts b/pxtlib/music.ts index 8e21dd169105..197a76015511 100644 --- a/pxtlib/music.ts +++ b/pxtlib/music.ts @@ -799,7 +799,7 @@ namespace pxt.assets.music { }, { - name: lf("booming kick"), + name: lf("boom kick"), startFrequency: 100, startVolume: 1024, steps: [{ diff --git a/pxtlib/tilemap.ts b/pxtlib/tilemap.ts index 653bdf328699..306cf9abc22d 100644 --- a/pxtlib/tilemap.ts +++ b/pxtlib/tilemap.ts @@ -843,8 +843,10 @@ namespace pxt { * @param skipIDs string[] a list of string ids (block id, asset id, or file name) to ignore **/ public isAssetUsed(asset: Asset, files?: pxt.Map<{content: string}>, skipIDs?: string[]): boolean { - let blockIds = asset.meta?.blockIDs?.filter(id => !skipIDs || skipIDs?.indexOf(id) < 0) || []; - if (blockIds.length > 0) return true; + if (!asset.meta?.displayName || !files) { + let blockIds = asset.meta?.blockIDs?.filter(id => !skipIDs || skipIDs?.indexOf(id) < 0) || []; + if (blockIds.length > 0) return true; + } if (asset.type == pxt.AssetType.Tile) { for (const tm of this.getAssets(AssetType.Tilemap)) { @@ -857,74 +859,88 @@ namespace pxt { } if (files) { + const config = U.jsonTryParse(files["pxt.json"]?.content) as pxt.PackageConfig; + + const filesToCheck: string[] = []; + + if (config?.files) { + for (const file of config.files) { + if (file.endsWith(".g.ts")) continue; + if (skipIDs && (skipIDs.indexOf(file) !== -1)) continue; + + if (file.endsWith(".ts") && file !== pxt.MAIN_TS || file.endsWith(".py") && file !== pxt.MAIN_PY) { + filesToCheck.push(file); + } + } + } + + if (config?.preferredEditor === pxt.BLOCKS_PROJECT_NAME) { + filesToCheck.push(pxt.MAIN_BLOCKS); + } + else if (config?.preferredEditor === pxt.JAVASCRIPT_PROJECT_NAME) { + filesToCheck.push(pxt.MAIN_TS); + } + else if (config?.preferredEditor === pxt.PYTHON_PROJECT_NAME) { + filesToCheck.push(pxt.MAIN_PY); + } + + const references: string[] = []; + const shortId = Util.escapeForRegex(getShortIDForAsset(asset)); const displayName = Util.escapeForRegex(asset.meta?.displayName) || ""; - let assetTsRefs: string; - switch (asset.type) { - case pxt.AssetType.Tile: - assetTsRefs = `myTiles.${shortId}|assets.tile\`${shortId}\``; - if (displayName) assetTsRefs += `|assets.tile\`${displayName}\``; - break; - case pxt.AssetType.Tilemap: - assetTsRefs = `tilemap\`${shortId}\``; - break; - case pxt.AssetType.Animation: - assetTsRefs = `assets.animation\`${shortId}\``; - if (displayName) assetTsRefs += `|assets.animation\`${displayName}\``; - break; - case pxt.AssetType.Song: - assetTsRefs = `assets.song\`${shortId}\``; - if (displayName) assetTsRefs += `|assets.song\`${displayName}\``; - break; - case pxt.AssetType.Json: - assetTsRefs = `assets.json\`${shortId}\``; - if (displayName) assetTsRefs += `|assets.json\`${displayName}\``; - break; - default: - assetTsRefs = `assets.image\`${shortId}\``; - if (displayName) assetTsRefs += `|assets.image\`${displayName}\``; - break; + const addNamespaceReference = (namespace: string, fun: string, id: string) => { + references.push(`${namespace}\\s*\\.\\s*${fun}\`\\s*${id}\\s*\``); } - const assetTsRegex = new RegExp(assetTsRefs, "gm"); - let assetPyRefs: string; switch (asset.type) { case pxt.AssetType.Tile: - assetPyRefs = `myTiles.${shortId}|assets.tile\("""${shortId}"""\)`; - if (displayName) assetPyRefs += `|assets.tile\("""${displayName}"""\)`; + references.push(`myTiles\\s*\\.\\s*${shortId}\\s*`); + + addNamespaceReference("assets", "tile", shortId); + if (displayName) addNamespaceReference("assets", "tile", displayName); break; case pxt.AssetType.Tilemap: - assetPyRefs = `assets.tilemap\("""${shortId}"""\)`; + references.push(`tilemap\`\\s*${shortId}\\s*\``); + addNamespaceReference("assets", "tilemap", shortId); + if (displayName) { + references.push(`tilemap\`\\s*${displayName}\\s*\``); + addNamespaceReference("assets", "tilemap", displayName); + } break; case pxt.AssetType.Animation: - assetPyRefs = `assets.animation\("""${shortId}"""\)`; - if (displayName) assetPyRefs += `|assets.animation\("""${displayName}"""\)`; + addNamespaceReference("assets", "animation", shortId); + if (displayName) addNamespaceReference("assets", "animation", displayName); break; case pxt.AssetType.Song: - assetPyRefs = `assets.song\("""${shortId}"""\)`; - if (displayName) assetPyRefs += `|assets.song\("""${displayName}"""\)`; + addNamespaceReference("assets", "song", shortId); + if (displayName) addNamespaceReference("assets", "song", displayName); break; case pxt.AssetType.Json: - assetPyRefs = `assets.json\("""${shortId}"""\)`; - if (displayName) assetPyRefs += `|assets.json\("""${displayName}"""\)`; + addNamespaceReference("assets", "json", shortId); + if (displayName) addNamespaceReference("assets", "json", displayName); break; default: - assetPyRefs = `assets.image\("""${shortId}"""\)`; - if (displayName) assetPyRefs += `|assets.image\("""${displayName}"""\)`; + addNamespaceReference("assets", "image", shortId); + if (displayName) addNamespaceReference("assets", "image", displayName); break; } - const assetPyRegex = new RegExp(assetPyRefs, "gm"); - for (let filename of Object.keys(files)) { - if (skipIDs?.indexOf(filename) >= 0) continue; + const regex = new RegExp(references.map(r => `(?:${r})`).join("|")); + const pyRegex = new RegExp(references.map(r => `(?:${r})`).join("|").replace(/`/g, '"""')); + + for (const file of filesToCheck) { + const content = files[file].content; - const f = files[filename]; - // Match .ts files that are not generated (.g.ts) - if (filename.match(/((?!\.g).{2}|^.{0,1})\.ts$/i)) { - if (f.content.match(assetTsRegex)) return true; - } else if (filename.endsWith(".py")) { - if (f.content.match(assetPyRegex)) return true; + if (file.endsWith(".py")) { + if (pyRegex.test(content)) { + return true; + } + } + else { + if (regex.test(content)) { + return true; + } } } } diff --git a/theme/monaco.less b/theme/monaco.less index 52dae8b79a42..ad67da5d4640 100644 --- a/theme/monaco.less +++ b/theme/monaco.less @@ -19,6 +19,8 @@ .monacoToolboxDiv { height: 100%; + /* Prevent large category lists from expanding the page scroll bounds. */ + contain: paint; } .monacoEditorRightArea { diff --git a/theme/music-editor/gallery.less b/theme/music-editor/gallery.less index 42c94ab0d4c4..73432cb2e583 100644 --- a/theme/music-editor/gallery.less +++ b/theme/music-editor/gallery.less @@ -1,6 +1,8 @@ .image-editor-gallery.song { display: flex; flex-direction: column; + flex-wrap: balance; + align-items: center; align-content: center; justify-content: unset; padding: 0.5rem; @@ -15,11 +17,12 @@ padding: 0.5rem; width: 100%; height: 4rem; + flex-shrink: 0; background-color: var(--pxt-neutral-background1); color: var(--pxt-neutral-foreground1); border-radius: 0.25rem; - max-width: 500px; + max-width: 440px; gap: 1rem; .song-gallery-item-name { @@ -94,4 +97,10 @@ outline-offset: 5px; } } +} + +@media @tabletAndBelow { + .image-editor-gallery.song { + flex-wrap: nowrap; + } } \ No newline at end of file diff --git a/theme/piano-roll/piano-roll.less b/theme/piano-roll/piano-roll.less index 2fbe4b3718a6..e45219d9d232 100644 --- a/theme/piano-roll/piano-roll.less +++ b/theme/piano-roll/piano-roll.less @@ -272,6 +272,9 @@ padding-right: 0.25rem; background-color: var(--white-key-color); color: var(--key-text-color); + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; &.active, &.playing {