diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx
index 295dcf1d9fd..38500878daf 100644
--- a/packages/scratch-gui/src/containers/blocks.jsx
+++ b/packages/scratch-gui/src/containers/blocks.jsx
@@ -393,6 +393,47 @@ class Blocks extends React.Component {
const dom = this.ScratchBlocks.Xml.textToDom(data.xml);
try {
this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace);
+
+ // When we converted blocks from Ruby, update top block positions.
+ if (this.props.vm.editingTarget) {
+ const blocks = this.props.vm.editingTarget.blocks;
+ const scripts = blocks.getScripts();
+ let fromRuby = false;
+ for (let i = 0; i < scripts.length; i++) {
+ const topBlockId = scripts[i];
+ const topBlock = blocks.getBlock(topBlockId);
+ if (typeof topBlock.x === 'undefined' || typeof topBlock.y === 'undefined') {
+ fromRuby = true;
+ break;
+ }
+ }
+ if (fromRuby) {
+ this.workspace.cleanUp();
+
+ // Re-calculate the position of the comments.
+ this.workspace.getTopComments(false).forEach(comment => {
+ if (comment.blockId) {
+ const block = this.workspace.getBlockById(comment.blockId);
+ if (block) {
+ const blockXY = block.getRelativeToSurfaceXY();
+ const blockHW = block.getHeightWidth();
+ const rtl = this.workspace.RTL;
+ if (rtl) {
+ comment.setAnchorLocation(
+ blockXY.x - (blockHW.width / 2),
+ blockXY.y + (blockHW.height / 2)
+ );
+ } else {
+ comment.setAnchorLocation(
+ blockXY.x + (blockHW.width / 2),
+ blockXY.y + (blockHW.height / 2)
+ );
+ }
+ }
+ }
+ });
+ }
+ }
} catch (error) {
// The workspace is likely incomplete. What did update should be
// functional.
diff --git a/packages/scratch-gui/src/containers/extension-library.jsx b/packages/scratch-gui/src/containers/extension-library.jsx
index ab9a2bbe45e..6e6b990bb08 100644
--- a/packages/scratch-gui/src/containers/extension-library.jsx
+++ b/packages/scratch-gui/src/containers/extension-library.jsx
@@ -2,7 +2,8 @@ import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import VM from '@scratch/scratch-vm';
-import {defineMessages, injectIntl} from 'react-intl';
+import {connect} from 'react-redux';
+import {defineMessages, injectIntl, FormattedMessage} from 'react-intl';
import intlShape from '../lib/intlShape.js';
import extensionLibraryContent from '../lib/libraries/extensions/index.jsx';
@@ -10,6 +11,10 @@ import extensionLibraryContent from '../lib/libraries/extensions/index.jsx';
import LibraryComponent from '../components/library/library.jsx';
import extensionIcon from '../components/action-menu/icon--sprite.svg';
+import {toggleShowAllExtensions} from '../reducers/extension-filter';
+
+import styles from './extension-library.css';
+
const messages = defineMessages({
extensionTitle: {
defaultMessage: 'Choose an Extension',
@@ -26,10 +31,25 @@ const messages = defineMessages({
class ExtensionLibrary extends React.PureComponent {
constructor (props) {
super(props);
+ extensionLibraryContent.forEach(extension => {
+ if (extension.setFormatMessage) {
+ extension.setFormatMessage(this.props.intl.formatMessage);
+ }
+ if (extension.translationMap) {
+ Object.assign(
+ this.props.intl.messages,
+ extension.translationMap[this.props.intl.locale]
+ );
+ }
+ });
bindAll(this, [
- 'handleItemSelect'
+ 'handleItemSelect',
+ 'handleToggleShowAllExtensions'
]);
}
+ handleToggleShowAllExtensions (event) {
+ this.props.onToggleShowAllExtensions(event.target.checked);
+ }
handleItemSelect (item) {
const id = item.extensionId;
let url = item.extensionURL ? item.extensionURL : id;
@@ -48,14 +68,58 @@ class ExtensionLibrary extends React.PureComponent {
}
}
render () {
- const extensionLibraryThumbnailData = extensionLibraryContent.map(extension => ({
- rawURL: extension.iconURL || extensionIcon,
- ...extension
- }));
+ const query = new URLSearchParams(window.location.search);
+ const extensionsParam = query.get('extensions') || '';
+ const showMeshV2 = extensionsParam.split(',').includes('meshV2');
+
+ const showAllExtensionsParam = query.get('showAllExtensions');
+ const showAllExtensions = showAllExtensionsParam === 'true' ? true :
+ showAllExtensionsParam === 'false' ? false :
+ this.props.showAllExtensions;
+
+ const extensionLibraryThumbnailData = extensionLibraryContent
+ .filter(extension => {
+ if (extension.extensionId === 'meshV2' && !showMeshV2) {
+ return false;
+ }
+ if (!showAllExtensions && extension.defaultHidden) {
+ return false;
+ }
+ return true;
+ })
+ .map(extension => ({
+ rawURL: extension.iconURL || extensionIcon,
+ ...extension
+ }));
+
+ const checkboxLabel = this.props.intl.formatMessage({
+ defaultMessage: 'Show all extensions',
+ description: 'Checkbox label to show all extensions including hidden ones',
+ id: 'gui.extensionLibrary.showAllExtensions'
+ });
+
+ const headerActions = (
+
+ );
+
return (
({
+ showAllExtensions: state.scratchGui.extensionFilter.showAllExtensions
+});
+
+const mapDispatchToProps = dispatch => ({
+ onToggleShowAllExtensions: showAll => dispatch(toggleShowAllExtensions(showAll))
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(injectIntl(ExtensionLibrary));
diff --git a/packages/scratch-gui/src/containers/gui.jsx b/packages/scratch-gui/src/containers/gui.jsx
index d84fab6f7f2..ffbda7ba753 100644
--- a/packages/scratch-gui/src/containers/gui.jsx
+++ b/packages/scratch-gui/src/containers/gui.jsx
@@ -16,7 +16,8 @@ import {
activateTab,
BLOCKS_TAB_INDEX,
COSTUMES_TAB_INDEX,
- SOUNDS_TAB_INDEX
+ SOUNDS_TAB_INDEX,
+ RUBY_TAB_INDEX
} from '../reducers/editor-tab';
import {
@@ -178,6 +179,7 @@ const mapStateToProps = (state, ownProps) => {
platform: ownProps.platform,
projectId: state.scratchGui.projectState.projectId,
soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX,
+ rubyTabVisible: state.scratchGui.editorTab.activeTabIndex === RUBY_TAB_INDEX,
targetIsStage: (
state.scratchGui.targets.stage &&
state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
diff --git a/packages/scratch-gui/src/locales/ja-Hira.js b/packages/scratch-gui/src/locales/ja-Hira.js
index 0628ae61881..ef53ffe1c53 100644
--- a/packages/scratch-gui/src/locales/ja-Hira.js
+++ b/packages/scratch-gui/src/locales/ja-Hira.js
@@ -1,6 +1,11 @@
export default {
'gui.modal.reload': 'さいよみこみ',
'gui.menuBar.meshV2': 'メッシュ',
+ 'gui.sharedMessages.backdrop': 'はいけい{index}',
+ 'gui.sharedMessages.costume': 'コスチューム{index}',
+ 'gui.sharedMessages.pop': 'ポップ',
+ 'gui.sharedMessages.sprite': 'スプライト{index}',
+ 'gui.sharedMessages.loadFromComputerTitle': 'コンピューターからよみこむ',
'gui.menuBar.loadFromUrl': 'Scratchからよみこむ',
'gui.urlLoader.loadError': 'プロジェクトURLのよみこみにしっぱいしました。',
'gui.urlLoader.invalidUrl': 'ゆうこうなScratchプロジェクトURLをにゅうりょくしてください。',
diff --git a/packages/scratch-gui/src/locales/ja.js b/packages/scratch-gui/src/locales/ja.js
index c84548fff25..3ef57d44c89 100644
--- a/packages/scratch-gui/src/locales/ja.js
+++ b/packages/scratch-gui/src/locales/ja.js
@@ -3,6 +3,11 @@ export default {
'gui.modal.stop': '中止',
'gui.menuBar.loadFromUrl': 'Scratchから読み込む',
'gui.menuBar.meshV2': 'メッシュ',
+ 'gui.sharedMessages.backdrop': '背景{index}',
+ 'gui.sharedMessages.costume': 'コスチューム{index}',
+ 'gui.sharedMessages.pop': 'ポップ',
+ 'gui.sharedMessages.sprite': 'スプライト{index}',
+ 'gui.sharedMessages.loadFromComputerTitle': 'コンピューターから読み込む',
'gui.menuBar.loadFromGoogleDrive': 'Google ドライブから読み込む',
'gui.menuBar.saveToGoogleDrive': 'Googleドライブにコピーを保存...',
'gui.menuBar.saveDirectlyToGoogleDrive': 'Googleドライブに直ちに保存',
diff --git a/packages/scratch-gui/webpack.config.js b/packages/scratch-gui/webpack.config.js
index b00966cbc7f..30d9bf4c4c4 100644
--- a/packages/scratch-gui/webpack.config.js
+++ b/packages/scratch-gui/webpack.config.js
@@ -274,7 +274,7 @@ const buildWithPwaConfig = buildConfig.clone()
exclude: [
/\.DS_Store/
],
- maximumFileSizeToCacheInBytes: 32 * 1024 * 1024
+ maximumFileSizeToCacheInBytes: 64 * 1024 * 1024
})
)
.addPlugin(