From b2d4021455dedc85eb222f323c086f293fd6998b Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Thu, 9 Feb 2023 11:49:11 -0500 Subject: [PATCH 01/21] feat: store color theme in state --- src/components/gui/gui.jsx | 7 +- src/containers/blocks.jsx | 34 ++------ src/lib/make-toolbox-xml.js | 84 ++++++++++++-------- src/lib/themes/__mocks__/dark-mode.js | 8 ++ src/lib/themes/__mocks__/default-colors.js | 11 +++ src/lib/themes/dark-mode.js | 66 ++++++++++++++++ src/lib/themes/default-colors.js | 90 ++++++++++++++++++++++ src/lib/themes/high-contrast.js | 87 +++++++++++++++++++++ src/lib/themes/index.js | 31 ++++++++ src/reducers/gui.js | 3 + src/reducers/theme.js | 25 ++++++ test/unit/util/themes.test.js | 22 ++++++ 12 files changed, 410 insertions(+), 58 deletions(-) create mode 100644 src/lib/themes/__mocks__/dark-mode.js create mode 100644 src/lib/themes/__mocks__/default-colors.js create mode 100644 src/lib/themes/dark-mode.js create mode 100644 src/lib/themes/default-colors.js create mode 100644 src/lib/themes/high-contrast.js create mode 100644 src/lib/themes/index.js create mode 100644 src/reducers/theme.js create mode 100644 test/unit/util/themes.test.js diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index a44604898..53330d3b7 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -117,6 +117,7 @@ const GUIComponent = props => { stageSizeMode, targetIsStage, telemetryModalVisible, + theme, tipsLibraryVisible, vm, ...componentProps @@ -303,10 +304,12 @@ const GUIComponent = props => { canUseCloud={canUseCloud} grow={1} isVisible={blocksTabVisible} + key={theme} options={{ media: `${basePath}static/blocks-media/` }} stageSize={stageSize} + theme={theme} vm={vm} /> @@ -422,6 +425,7 @@ GUIComponent.propTypes = { stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)), targetIsStage: PropTypes.bool, telemetryModalVisible: PropTypes.bool, + theme: PropTypes.string, tipsLibraryVisible: PropTypes.bool, vm: PropTypes.instanceOf(VM).isRequired }; @@ -448,7 +452,8 @@ GUIComponent.defaultProps = { const mapStateToProps = state => ({ // This is the button's mode, as opposed to the actual current state - stageSizeMode: state.scratchGui.stageSize.stageSize + stageSizeMode: state.scratchGui.stageSize.stageSize, + theme: state.scratchGui.theme.theme }); export default injectIntl(connect( diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 5756249f7..cb998b929 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -18,6 +18,7 @@ import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; import defineDynamicBlock from '../lib/define-dynamic-block'; +import {getColorsForTheme, themes} from '../lib/themes'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -94,7 +95,7 @@ class Blocks extends React.Component { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options, - {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} + {rtl: this.props.isRtl, toolbox: this.props.toolboxXML, colours: getColorsForTheme(this.props.theme)} ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); @@ -351,7 +352,8 @@ class Blocks extends React.Component { return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, targetCostumes[targetCostumes.length - 1].name, stageCostumes[stageCostumes.length - 1].name, - targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' + targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '', + getColorsForTheme(this.props.theme) ); } catch { return null; @@ -620,22 +622,11 @@ Blocks.propTypes = { wheel: PropTypes.bool, startScale: PropTypes.number }), - colours: PropTypes.shape({ - workspace: PropTypes.string, - flyout: PropTypes.string, - toolbox: PropTypes.string, - toolboxSelected: PropTypes.string, - scrollbar: PropTypes.string, - scrollbarHover: PropTypes.string, - insertionMarker: PropTypes.string, - insertionMarkerOpacity: PropTypes.number, - fieldShadow: PropTypes.string, - dragShadowOpacity: PropTypes.number - }), comments: PropTypes.bool, collapse: PropTypes.bool }), stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, + theme: PropTypes.oneOf(themes), toolboxXML: PropTypes.string, updateMetrics: PropTypes.func, updateToolboxState: PropTypes.func, @@ -656,18 +647,6 @@ Blocks.defaultOptions = { length: 2, colour: '#ddd' }, - colours: { - workspace: '#F9F9F9', - flyout: '#F9F9F9', - toolbox: '#FFFFFF', - toolboxSelected: '#E9EEF2', - scrollbar: '#CECDCE', - scrollbarHover: '#CECDCE', - insertionMarker: '#000000', - insertionMarkerOpacity: 0.2, - fieldShadow: 'rgba(255, 255, 255, 0.3)', - dragShadowOpacity: 0.6 - }, comments: true, collapse: false, sounds: false @@ -675,7 +654,8 @@ Blocks.defaultOptions = { Blocks.defaultProps = { isVisible: true, - options: Blocks.defaultOptions + options: Blocks.defaultOptions, + theme: 'standard' }; const mapStateToProps = state => ({ diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js index 0b618979d..8d356e442 100644 --- a/src/lib/make-toolbox-xml.js +++ b/src/lib/make-toolbox-xml.js @@ -1,17 +1,19 @@ import ScratchBlocks from 'scratch-blocks'; +import {defaultColors} from './themes'; const categorySeparator = ''; const blockSeparator = ''; // At default scale, about 28px /* eslint-disable no-unused-vars */ -const motion = function (isInitialSetup, isStage, targetId) { +const motion = function (isInitialSetup, isStage, targetId, colors) { const stageSelected = ScratchBlocks.ScratchMsgs.translate( 'MOTION_STAGE_SELECTED', 'Stage selected: no motion blocks' ); + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + ${isStage ? ` ` : ` @@ -151,11 +153,12 @@ const xmlEscape = function (unsafe) { }); }; -const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName) { +const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName, colors) { const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!'); const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...'); + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + ${isStage ? '' : ` @@ -288,9 +291,10 @@ const looks = function (isInitialSetup, isStage, targetId, costumeName, backdrop `; }; -const sound = function (isInitialSetup, isStage, targetId, soundName) { +const sound = function (isInitialSetup, isStage, targetId, soundName, colors) { + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + @@ -343,9 +347,10 @@ const sound = function (isInitialSetup, isStage, targetId, soundName) { `; }; -const events = function (isInitialSetup, isStage) { +const events = function (isInitialSetup, isStage, targetId, colors) { + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + @@ -382,9 +387,14 @@ const events = function (isInitialSetup, isStage) { `; }; -const control = function (isInitialSetup, isStage) { +const control = function (isInitialSetup, isStage, targetId, colors) { + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + @@ -429,10 +439,15 @@ const control = function (isInitialSetup, isStage) { `; }; -const sensing = function (isInitialSetup, isStage) { +const sensing = function (isInitialSetup, isStage, targetId, colors) { const name = ScratchBlocks.ScratchMsgs.translate('SENSING_ASK_TEXT', 'What\'s your name?'); + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + ${isStage ? '' : ` @@ -504,12 +519,17 @@ const sensing = function (isInitialSetup, isStage) { `; }; -const operators = function (isInitialSetup) { +const operators = function (isInitialSetup, isStage, targetId, colors) { const apple = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_APPLE', 'apple'); const banana = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_BANANA', 'banana'); const letter = ScratchBlocks.ScratchMsgs.translate('OPERATORS_LETTEROF_APPLE', 'a'); + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` - + @@ -691,25 +711,27 @@ const operators = function (isInitialSetup) { `; }; -const variables = function () { +const variables = function (isInitialSetup, isStage, targetId, colors) { + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` `; }; -const myBlocks = function () { +const myBlocks = function (isInitialSetup, isStage, targetId, colors) { + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` `; @@ -732,10 +754,11 @@ const xmlClose = ''; * @param {?string} costumeName - The name of the default selected costume dropdown. * @param {?string} backdropName - The name of the default selected backdrop dropdown. * @param {?string} soundName - The name of the default selected sound dropdown. + * @param {?object} colors - The colors for the theme. * @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox. */ const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categoriesXML = [], - costumeName = '', backdropName = '', soundName = '') { + costumeName = '', backdropName = '', soundName = '', colors = defaultColors) { isStage = isInitialSetup || isStage; const gap = [categorySeparator]; @@ -753,15 +776,16 @@ const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categ } // return `undefined` }; - const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId); - const looksXML = moveCategory('looks') || looks(isInitialSetup, isStage, targetId, costumeName, backdropName); - const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName); - const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId); - const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId); - const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId); - const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId); - const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId); - const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId); + const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId, colors.motion); + const looksXML = moveCategory('looks') || + looks(isInitialSetup, isStage, targetId, costumeName, backdropName, colors.looks); + const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName, colors.sounds); + const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId, colors.event); + const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId, colors.control); + const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId, colors.sensing); + const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId, colors.operators); + const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId, colors.data); + const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId, colors.more); const everything = [ xmlOpen, diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/__mocks__/dark-mode.js new file mode 100644 index 000000000..c3faf8d54 --- /dev/null +++ b/src/lib/themes/__mocks__/dark-mode.js @@ -0,0 +1,8 @@ +const blockColors = { + motion: { + primary: '#AAAAAA' + }, + text: '#BBBBBB' +}; + +export default blockColors; diff --git a/src/lib/themes/__mocks__/default-colors.js b/src/lib/themes/__mocks__/default-colors.js new file mode 100644 index 000000000..f382f38aa --- /dev/null +++ b/src/lib/themes/__mocks__/default-colors.js @@ -0,0 +1,11 @@ +const blockColors = { + motion: { + primary: '#111111', + secondary: '#222222', + tertiary: '#333333' + }, + text: '#444444', + workspace: '#555555' +}; + +export default blockColors; diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark-mode.js new file mode 100644 index 000000000..d9fc37a57 --- /dev/null +++ b/src/lib/themes/dark-mode.js @@ -0,0 +1,66 @@ +const blockColors = { + motion: { + primary: '#0F1E33', + secondary: '#4C4C4C', + tertiary: '#4C97FF' + }, + looks: { + primary: '#1E1433', + secondary: '#4C4C4C', + tertiary: '#9966FF' + }, + sounds: { + primary: '#291329', + secondary: '#4C4C4C', + tertiary: '#CF63CF' + }, + control: { + primary: '#332205', + secondary: '#4C4C4C', + tertiary: '#FFAB19' + }, + event: { + primary: '#332600', + secondary: '#4C4C4C', + tertiary: '#FFBF00' + }, + sensing: { + primary: '#12232A', + secondary: '#4C4C4C', + tertiary: '#5CB1D6' + }, + pen: { + primary: '#03251C', + secondary: '#4C4C4C', + tertiary: '#0fBD8C' + }, + operators: { + primary: '#112611', + secondary: '#4C4C4C', + tertiary: '#59C059' + }, + data: { + primary: '#331C05', + secondary: '#4C4C4C', + tertiary: '#FF8C1A' + }, + data_lists: { + primary: '#331405', + secondary: '#4C4C4C', + tertiary: '#FF661A' + }, + more: { + primary: '#331419', + secondary: '#4C4C4C', + tertiary: '#FF6680' + }, + text: '#E5E5E5', + workspace: '#121212', + toolboxSelected: '#4C4C4C', + toolboxText: '#E5E5E5', + toolbox: '#121212', + flyout: '#121212', + textField: '#4C4C4C' +}; + +export default blockColors; diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default-colors.js new file mode 100644 index 000000000..e9f9cde3c --- /dev/null +++ b/src/lib/themes/default-colors.js @@ -0,0 +1,90 @@ +const blockColors = { + motion: { + primary: '#4C97FF', + secondary: '#4280D7', + tertiary: '#3373CC' + }, + looks: { + primary: '#9966FF', + secondary: '#855CD6', + tertiary: '#774DCB' + }, + sounds: { + primary: '#CF63CF', + secondary: '#C94FC9', + tertiary: '#BD42BD' + }, + control: { + primary: '#FFAB19', + secondary: '#EC9C13', + tertiary: '#CF8B17' + }, + event: { + primary: '#FFBF00', + secondary: '#E6AC00', + tertiary: '#CC9900' + }, + sensing: { + primary: '#5CB1D6', + secondary: '#47A8D1', + tertiary: '#2E8EB8' + }, + pen: { + primary: '#0fBD8C', + secondary: '#0DA57A', + tertiary: '#0B8E69' + }, + operators: { + primary: '#59C059', + secondary: '#46B946', + tertiary: '#389438' + }, + data: { + primary: '#FF8C1A', + secondary: '#FF8000', + tertiary: '#DB6E00' + }, + // This is not a new category, but rather for differentiation + // between lists and scalar variables. + data_lists: { + primary: '#FF661A', + secondary: '#FF5500', + tertiary: '#E64D00' + }, + more: { + primary: '#FF6680', + secondary: '#FF4D6A', + tertiary: '#FF3355' + }, + text: '#575E75', + workspace: '#F9F9F9', + toolboxHover: '#4C97FF', + toolboxSelected: '#E9EEF2', + toolboxText: '#575E75', + toolbox: '#FFFFFF', + flyout: '#F9F9F9', + scrollbar: '#CECDCE', + scrollbarHover: '#CECDCE', + textField: '#FFFFFF', + insertionMarker: '#000000', + insertionMarkerOpacity: 0.2, + dragShadowOpacity: 0.6, + stackGlow: '#FFF200', + stackGlowSize: 4, + stackGlowOpacity: 1, + replacementGlow: '#FFFFFF', + replacementGlowSize: 2, + replacementGlowOpacity: 1, + colourPickerStroke: '#FFFFFF', + // CSS colours: support RGBA + fieldShadow: 'rgba(255, 255, 255, 0.3)', + dropDownShadow: 'rgba(0, 0, 0, .3)', + numPadBackground: '#547AB2', + numPadBorder: '#435F91', + numPadActiveBackground: '#435F91', + numPadText: 'white', // Do not use hex here, it cannot be inlined with data-uri SVG + valueReportBackground: '#FFFFFF', + valueReportBorder: '#AAAAAA' +}; + +export default blockColors; diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js new file mode 100644 index 000000000..1c185556f --- /dev/null +++ b/src/lib/themes/high-contrast.js @@ -0,0 +1,87 @@ +const blockColors = { + motion: { + primary: '#80B5FF', + secondary: '#B3D2FF', + tertiary: '#3373CC', + // this field is not used in the "classic" Scratch 3 color scheme + // If we add "modes" we'd have to handle what happens when this field is not defined. + quaternary: '#CCE1FF' + }, + looks: { + primary: '#CCB3FF', + secondary: '#DDCCFF', + tertiary: '#774DCB', + quaternary: '#EEE5FF' + }, + sounds: { + primary: '#E19DE1', + secondary: '#FFB3FF', + tertiary: '#BD42BD', + quaternary: '#FFCCFF' + + }, + control: { + primary: '#FFBE4C', + secondary: '#FFDA99', + tertiary: '#CF8B17', + quaternary: '#FFE3B3' + }, + event: { + primary: '#FFD966', + secondary: '#FFECB3', + tertiary: '#CC9900', + quaternary: '#FFF2CC' + }, + sensing: { + primary: '#85C4E0', + secondary: '#AED8EA', + tertiary: '#2E8EB8', + quaternary: '#C2E2F0' + }, + pen: { + primary: '#13ECAF', + secondary: '#75F0CD', + tertiary: '#0B8E69', + quaternary: '#A3F5DE' + }, + operators: { + primary: '#7ECE7E', + secondary: '#B5E3B5', + tertiary: '#389438', + quaternary: '#DAF1DA' + }, + data: { + primary: '#FFA54C', + secondary: '#FFCC99', + tertiary: '#DB6E00', + quaternary: '#FFE5CC' + }, + // This is not a new category, but rather for differentiation + // between lists and scalar variables. + data_lists: { + primary: '#FF9966', + secondary: '#FFCAB0', // I don't think this is used, b/c we don't have any droppable fields in list blocks + tertiary: '#E64D00', + quaternary: '#FFDDCC' + }, + more: { + primary: '#FF99AA', + secondary: '#FFCCD5', + tertiary: '#FF3355', + quaternary: '#FFE5EA' + }, + // For productionizing: we should look into making this field be the color of the text on the blocks + // e.g. move and steps in move (10) steps, and rename this property below for controlling the text inside + // of inputs e.g. 10 + text: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] + + toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75) + // The color that the category menu label (e.g. 'motion', 'looks', etc.) changes to on hover + toolboxHover: '#3373CC', + insertionMarker: '#000000', + insertionMarkerOpacity: 0.2, + fieldShadow: 'rgba(255, 255, 255, 0.3)', + dragShadowOpacity: 0.6 +}; + +export default blockColors; diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js new file mode 100644 index 000000000..e1fa87f71 --- /dev/null +++ b/src/lib/themes/index.js @@ -0,0 +1,31 @@ +import defaultsDeep from 'lodash.defaultsdeep'; + +import darkMode from './dark-mode'; +import highContrast from './high-contrast'; +import defaultColors from './default-colors'; + +const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors); + +const themeMap = { + 'dark-mode': mergeWithDefaults(darkMode), + 'high-contrast': mergeWithDefaults(highContrast), + 'standard': defaultColors +}; + +const getColorsForTheme = theme => { + const colors = themeMap[theme]; + + if (!colors) { + throw new Error(`Undefined theme ${theme}`); + } + + return colors; +}; + +const themes = Object.keys(themeMap); + +export { + defaultColors, + getColorsForTheme, + themes +}; diff --git a/src/reducers/gui.js b/src/reducers/gui.js index 378a3203c..2e70c40e1 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -21,6 +21,7 @@ import fontsLoadedReducer, {fontsLoadedInitialState} from './fonts-loaded'; import restoreDeletionReducer, {restoreDeletionInitialState} from './restore-deletion'; import stageSizeReducer, {stageSizeInitialState} from './stage-size'; import targetReducer, {targetsInitialState} from './targets'; +import themeReducer, {themeInitialState} from './theme'; import timeoutReducer, {timeoutInitialState} from './timeout'; import toolboxReducer, {toolboxInitialState} from './toolbox'; import vmReducer, {vmInitialState} from './vm'; @@ -55,6 +56,7 @@ const guiInitialState = { fontsLoaded: fontsLoadedInitialState, restoreDeletion: restoreDeletionInitialState, targets: targetsInitialState, + theme: themeInitialState, timeout: timeoutInitialState, toolbox: toolboxInitialState, vm: vmInitialState, @@ -154,6 +156,7 @@ const guiReducer = combineReducers({ fontsLoaded: fontsLoadedReducer, restoreDeletion: restoreDeletionReducer, targets: targetReducer, + theme: themeReducer, timeout: timeoutReducer, toolbox: toolboxReducer, vm: vmReducer, diff --git a/src/reducers/theme.js b/src/reducers/theme.js new file mode 100644 index 000000000..5d05eff00 --- /dev/null +++ b/src/reducers/theme.js @@ -0,0 +1,25 @@ +const SET_THEME = 'scratch-gui/theme/SET_THEME'; + +const initialState = { + theme: 'standard' +}; + +const reducer = (state = initialState, action) => { + switch (action.type) { + case SET_THEME: + return {...state, theme: action.theme}; + default: + return state; + } +}; + +const setTheme = theme => ({ + type: SET_THEME, + theme +}); + +export { + reducer as default, + initialState as themeInitialState, + setTheme +}; diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js new file mode 100644 index 000000000..d281add0c --- /dev/null +++ b/test/unit/util/themes.test.js @@ -0,0 +1,22 @@ +import {defaultColors, getColorsForTheme} from '../../../src/lib/themes'; + +jest.mock('../../../src/lib/themes/default-colors'); +jest.mock('../../../src/lib/themes/dark-mode'); + +describe('themes', () => { + test('provides the default theme colors', () => { + expect(defaultColors.motion.primary).toEqual('#111111'); + }); + + test('returns the dark mode', () => { + const colors = getColorsForTheme('dark-mode'); + + expect(colors.motion.primary).toEqual('#AAAAAA'); + }); + + test('uses default theme colors when not specified', () => { + const colors = getColorsForTheme('dark-mode'); + + expect(colors.motion.secondary).toEqual('#222222'); + }); +}); From 9af1272530147d6b009182ca8fc4c5b6d00617b1 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Wed, 15 Feb 2023 13:46:39 -0500 Subject: [PATCH 02/21] feat: use updated names for text colors --- src/lib/themes/dark-mode.js | 3 ++- src/lib/themes/default-colors.js | 3 ++- src/lib/themes/high-contrast.js | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark-mode.js index d9fc37a57..707f6a33c 100644 --- a/src/lib/themes/dark-mode.js +++ b/src/lib/themes/dark-mode.js @@ -54,7 +54,8 @@ const blockColors = { secondary: '#4C4C4C', tertiary: '#FF6680' }, - text: '#E5E5E5', + text: 'rgba(255, 255, 255, .7)', + textFieldText: '#E5E5E5', workspace: '#121212', toolboxSelected: '#4C4C4C', toolboxText: '#E5E5E5', diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default-colors.js index e9f9cde3c..e7567573e 100644 --- a/src/lib/themes/default-colors.js +++ b/src/lib/themes/default-colors.js @@ -56,7 +56,7 @@ const blockColors = { secondary: '#FF4D6A', tertiary: '#FF3355' }, - text: '#575E75', + text: '#FFFFFF', workspace: '#F9F9F9', toolboxHover: '#4C97FF', toolboxSelected: '#E9EEF2', @@ -66,6 +66,7 @@ const blockColors = { scrollbar: '#CECDCE', scrollbarHover: '#CECDCE', textField: '#FFFFFF', + textFieldText: '#575E75', insertionMarker: '#000000', insertionMarkerOpacity: 0.2, dragShadowOpacity: 0.6, diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js index 1c185556f..d7abd0674 100644 --- a/src/lib/themes/high-contrast.js +++ b/src/lib/themes/high-contrast.js @@ -73,8 +73,8 @@ const blockColors = { // For productionizing: we should look into making this field be the color of the text on the blocks // e.g. move and steps in move (10) steps, and rename this property below for controlling the text inside // of inputs e.g. 10 - text: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] - + text: '#000000', + textFieldText: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75) // The color that the category menu label (e.g. 'motion', 'looks', etc.) changes to on hover toolboxHover: '#3373CC', From 563dffb34ae831c8cfaeed1dbbf27644c520aca8 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Thu, 16 Feb 2023 17:14:54 -0500 Subject: [PATCH 03/21] feat: color theming for monitors based on block category --- src/components/monitor/default-monitor.jsx | 10 +++- src/components/monitor/large-monitor.jsx | 10 +++- .../monitor/list-monitor-scroller.jsx | 11 +++- src/components/monitor/list-monitor.jsx | 5 +- src/components/monitor/monitor.css | 5 -- src/components/monitor/monitor.jsx | 35 +++++++----- src/components/monitor/slider-monitor.jsx | 10 +++- src/containers/monitor.jsx | 6 ++ src/lib/themes/high-contrast.js | 3 - test/unit/components/monitor-list.test.jsx | 3 + test/unit/components/monitor.test.jsx | 55 +++++++++++++++++++ 11 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 test/unit/components/monitor.test.jsx diff --git a/src/components/monitor/default-monitor.jsx b/src/components/monitor/default-monitor.jsx index 992014775..3368519dd 100644 --- a/src/components/monitor/default-monitor.jsx +++ b/src/components/monitor/default-monitor.jsx @@ -10,7 +10,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => (
{value}
@@ -19,7 +22,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => ( ); DefaultMonitor.propTypes = { - categoryColor: PropTypes.string.isRequired, + categoryColor: PropTypes.shape({ + background: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired, label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.string, diff --git a/src/components/monitor/large-monitor.jsx b/src/components/monitor/large-monitor.jsx index 5101ad0a5..64ac32640 100644 --- a/src/components/monitor/large-monitor.jsx +++ b/src/components/monitor/large-monitor.jsx @@ -6,7 +6,10 @@ const LargeMonitor = ({categoryColor, value}) => (
{value}
@@ -14,7 +17,10 @@ const LargeMonitor = ({categoryColor, value}) => ( ); LargeMonitor.propTypes = { - categoryColor: PropTypes.string, + categoryColor: PropTypes.shape({ + background: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired, value: PropTypes.oneOfType([ PropTypes.string, PropTypes.number diff --git a/src/components/monitor/list-monitor-scroller.jsx b/src/components/monitor/list-monitor-scroller.jsx index cfcd6a248..ea0de244e 100644 --- a/src/components/monitor/list-monitor-scroller.jsx +++ b/src/components/monitor/list-monitor-scroller.jsx @@ -41,7 +41,10 @@ class ListMonitorScroller extends React.Component {
{this.props.draggable && this.props.activeIndex === index ? ( @@ -51,6 +54,7 @@ class ListMonitorScroller extends React.Component { autoComplete={false} className={classNames(styles.listInput, 'no-drag')} spellCheck={false} + style={{color: this.props.categoryColor.text}} type="text" value={this.props.activeValue} onBlur={this.props.onDeactivate} @@ -97,7 +101,10 @@ class ListMonitorScroller extends React.Component { ListMonitorScroller.propTypes = { activeIndex: PropTypes.number, activeValue: PropTypes.string, - categoryColor: PropTypes.string, + categoryColor: PropTypes.shape({ + background: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired, draggable: PropTypes.bool, height: PropTypes.number, onActivate: PropTypes.func, diff --git a/src/components/monitor/list-monitor.jsx b/src/components/monitor/list-monitor.jsx index e3422e5b3..4bd9a2563 100644 --- a/src/components/monitor/list-monitor.jsx +++ b/src/components/monitor/list-monitor.jsx @@ -54,7 +54,10 @@ const ListMonitor = ({draggable, label, width, height, value, onResizeMouseDown, ListMonitor.propTypes = { activeIndex: PropTypes.number, - categoryColor: PropTypes.string.isRequired, + categoryColor: PropTypes.shape({ + background: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired, draggable: PropTypes.bool.isRequired, height: PropTypes.number, label: PropTypes.string.isRequired, diff --git a/src/components/monitor/monitor.css b/src/components/monitor/monitor.css index 0947db638..a4011457a 100644 --- a/src/components/monitor/monitor.css +++ b/src/components/monitor/monitor.css @@ -42,7 +42,6 @@ align-items: center; min-width: 40px; text-align: center; - color: white; margin: 0 5px; border-radius: calc($space / 2); padding: 0 2px; @@ -55,7 +54,6 @@ min-width: 3rem; padding: 0.1rem 0.25rem; text-align: center; - color: white; font-size: 1rem; white-space: pre-wrap; transform: translateZ(0); /* Fixes flickering in Safari */ @@ -115,7 +113,6 @@ .list-value { min-width: 40px; text-align: left; - color: white; margin: 0 3px; border-radius: calc($space / 2); border: 1px solid $ui-black-transparent; @@ -161,7 +158,6 @@ padding: 3px 5px; border: 0; background: none; - color: $ui-white; outline: none; font-size: 0.75rem; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; @@ -174,7 +170,6 @@ padding: 0; padding-right: 5px; cursor: pointer; - color: $ui-white; } .add-button { diff --git a/src/components/monitor/monitor.jsx b/src/components/monitor/monitor.jsx index ddb2f2e57..d9ceb9269 100644 --- a/src/components/monitor/monitor.jsx +++ b/src/components/monitor/monitor.jsx @@ -10,17 +10,19 @@ import DefaultMonitor from './default-monitor.jsx'; import LargeMonitor from './large-monitor.jsx'; import SliderMonitor from '../../containers/slider-monitor.jsx'; import ListMonitor from '../../containers/list-monitor.jsx'; +import {getColorsForTheme} from '../../lib/themes/index.js'; import styles from './monitor.css'; -const categories = { - data: '#FF8C1A', - sensing: '#5CB1D6', - sound: '#CF63CF', - looks: '#9966FF', - motion: '#4C97FF', - list: '#FC662C', - extension: '#0FBD8C' +// Map category name to color name used in scratch-blocks Blockly.Colours +const categoryColorMap = { + data: 'data', + sensing: 'sensing', + sound: 'sounds', + looks: 'looks', + motion: 'motion', + list: 'data_lists', + extension: 'pen' }; const modes = { @@ -30,6 +32,14 @@ const modes = { list: ListMonitor }; +const getCategoryColor = (theme, category) => { + const colors = getColorsForTheme(theme); + return { + background: colors[categoryColorMap[category]].primary, + text: colors.text + }; +}; + const MonitorComponent = props => ( ( onDoubleClick={props.mode === 'list' || !props.draggable ? null : props.onNextMode} > {React.createElement(modes[props.mode], { - categoryColor: categories[props.category], + categoryColor: getCategoryColor(props.theme, props.category), ...props })} @@ -122,12 +132,10 @@ const MonitorComponent = props => ( ); -MonitorComponent.categories = categories; - const monitorModes = Object.keys(modes); MonitorComponent.propTypes = { - category: PropTypes.oneOf(Object.keys(categories)), + category: PropTypes.oneOf(Object.keys(categoryColorMap)), componentRef: PropTypes.func.isRequired, draggable: PropTypes.bool.isRequired, label: PropTypes.string.isRequired, @@ -140,7 +148,8 @@ MonitorComponent.propTypes = { onSetModeToDefault: PropTypes.func, onSetModeToLarge: PropTypes.func, onSetModeToSlider: PropTypes.func, - onSliderPromptOpen: PropTypes.func + onSliderPromptOpen: PropTypes.func, + theme: PropTypes.string.isRequired }; MonitorComponent.defaultProps = { diff --git a/src/components/monitor/slider-monitor.jsx b/src/components/monitor/slider-monitor.jsx index 0271b140a..2bda8f593 100644 --- a/src/components/monitor/slider-monitor.jsx +++ b/src/components/monitor/slider-monitor.jsx @@ -12,7 +12,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli
{value}
@@ -33,7 +36,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli ); SliderMonitor.propTypes = { - categoryColor: PropTypes.string.isRequired, + categoryColor: PropTypes.shape({ + background: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired, isDiscrete: PropTypes.bool, label: PropTypes.string.isRequired, max: PropTypes.number, diff --git a/src/containers/monitor.jsx b/src/containers/monitor.jsx index b952a1836..eee63392e 100644 --- a/src/containers/monitor.jsx +++ b/src/containers/monitor.jsx @@ -213,6 +213,7 @@ class Monitor extends React.Component { min={this.props.min} mode={this.props.mode} targetId={this.props.targetId} + theme={this.props.theme} width={this.props.width} onDragEnd={this.handleDragEnd} onExport={isList ? this.handleExport : null} @@ -250,6 +251,7 @@ Monitor.propTypes = { resizeMonitorRect: PropTypes.func.isRequired, spriteName: PropTypes.string, // eslint-disable-line react/no-unused-prop-types targetId: PropTypes.string, + theme: PropTypes.string, toolboxXML: PropTypes.string, // eslint-disable-line react/no-unused-prop-types value: PropTypes.oneOfType([ PropTypes.string, @@ -264,8 +266,12 @@ Monitor.propTypes = { x: PropTypes.number, y: PropTypes.number }; +Monitor.defaultProps = { + theme: 'standard' +}; const mapStateToProps = state => ({ monitorLayout: state.scratchGui.monitorLayout, + theme: state.scratchGui.theme.theme, // render on toolbox updates since changes to the blocks could affect monitor labels, i.e. updated locale toolboxXML: state.scratchGui.toolbox.toolboxXML, vm: state.scratchGui.vm diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js index d7abd0674..dc77490b5 100644 --- a/src/lib/themes/high-contrast.js +++ b/src/lib/themes/high-contrast.js @@ -70,9 +70,6 @@ const blockColors = { tertiary: '#FF3355', quaternary: '#FFE5EA' }, - // For productionizing: we should look into making this field be the color of the text on the blocks - // e.g. move and steps in move (10) steps, and rename this property below for controlling the text inside - // of inputs e.g. 10 text: '#000000', textFieldText: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75) diff --git a/test/unit/components/monitor-list.test.jsx b/test/unit/components/monitor-list.test.jsx index 051632ea4..5a3cdf4b2 100644 --- a/test/unit/components/monitor-list.test.jsx +++ b/test/unit/components/monitor-list.test.jsx @@ -11,6 +11,9 @@ describe('MonitorListComponent', () => { monitors: {}, savedMonitorPositions: {} }, + theme: { + theme: 'standard' + }, toolbox: { toolboxXML: '' }, diff --git a/test/unit/components/monitor.test.jsx b/test/unit/components/monitor.test.jsx new file mode 100644 index 000000000..a60ca0923 --- /dev/null +++ b/test/unit/components/monitor.test.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import {shallow} from 'enzyme'; +import DefaultMonitor from '../../../src/components/monitor/default-monitor'; +import Monitor from '../../../src/components/monitor/monitor'; + +jest.mock('../../../src/lib/themes/default-colors'); +jest.mock('../../../src/lib/themes/dark-mode'); + +describe('Monitor Component', () => { + test('it selects the correct colors based on default theme', () => { + const noop = () => {}; + + const wrapper = shallow(); + + const defaultMonitor = wrapper.find(DefaultMonitor); + + // selects colors from mock value in src/lib/themes/__mocks__/default-colors.js + expect(defaultMonitor.props().categoryColor).toEqual({background: '#111111', text: '#444444'}); + }); + + test('it selects the correct colors based on dark mode theme', () => { + const noop = () => {}; + + const wrapper = shallow(); + + const defaultMonitor = wrapper.find(DefaultMonitor); + + // selects colors from mock value in src/lib/themes/__mocks__/dark-mode.js + expect(defaultMonitor.props().categoryColor).toEqual({background: '#AAAAAA', text: '#BBBBBB'}); + }); +}); From 63d43fbc7865bb943f299d15d43dc8b4a1a30549 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 17 Feb 2023 16:30:55 -0500 Subject: [PATCH 04/21] feat: apply color theme to extension categories and blocks --- src/containers/blocks.jsx | 12 +++-- src/lib/themes/__mocks__/dark-mode.js | 5 ++ src/lib/themes/__mocks__/default-colors.js | 5 ++ src/lib/themes/blockHelpers.js | 61 ++++++++++++++++++++++ src/lib/themes/index.js | 4 +- src/reducers/theme.js | 4 +- test/unit/util/themes.test.js | 41 ++++++++++++++- 7 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 src/lib/themes/blockHelpers.js diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index cb998b929..14e9677c3 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -18,7 +18,8 @@ import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; import defineDynamicBlock from '../lib/define-dynamic-block'; -import {getColorsForTheme, themes} from '../lib/themes'; +import {DEFAULT_THEME, getColorsForTheme, themes} from '../lib/themes'; +import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../lib/themes/blockHelpers'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -348,7 +349,10 @@ class Blocks extends React.Component { const stageCostumes = stage.getCostumes(); const targetCostumes = target.getCostumes(); const targetSounds = target.getSounds(); - const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target); + const dynamicBlocksXML = injectExtensionCategoryColors( + this.props.vm.runtime.getBlocksXML(target), + this.props.theme + ); return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, targetCostumes[targetCostumes.length - 1].name, stageCostumes[stageCostumes.length - 1].name, @@ -432,7 +436,7 @@ class Blocks extends React.Component { if (blockInfo.info && blockInfo.info.isDynamic) { dynamicBlocksInfo.push(blockInfo); } else if (blockInfo.json) { - staticBlocksJson.push(blockInfo.json); + staticBlocksJson.push(injectExtensionBlockColors(blockInfo.json, this.props.theme)); } // otherwise it's a non-block entry such as '---' }); @@ -655,7 +659,7 @@ Blocks.defaultOptions = { Blocks.defaultProps = { isVisible: true, options: Blocks.defaultOptions, - theme: 'standard' + theme: DEFAULT_THEME }; const mapStateToProps = state => ({ diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/__mocks__/dark-mode.js index c3faf8d54..793270dd6 100644 --- a/src/lib/themes/__mocks__/dark-mode.js +++ b/src/lib/themes/__mocks__/dark-mode.js @@ -2,6 +2,11 @@ const blockColors = { motion: { primary: '#AAAAAA' }, + pen: { + primary: '#FFFFFF', + secondary: '#EEEEEE', + tertiary: '#DDDDDD' + }, text: '#BBBBBB' }; diff --git a/src/lib/themes/__mocks__/default-colors.js b/src/lib/themes/__mocks__/default-colors.js index f382f38aa..f5e7aa48c 100644 --- a/src/lib/themes/__mocks__/default-colors.js +++ b/src/lib/themes/__mocks__/default-colors.js @@ -4,6 +4,11 @@ const blockColors = { secondary: '#222222', tertiary: '#333333' }, + pen: { + primary: '#121212', + secondary: '#232323', + tertiary: '#343434' + }, text: '#444444', workspace: '#555555' }; diff --git a/src/lib/themes/blockHelpers.js b/src/lib/themes/blockHelpers.js new file mode 100644 index 000000000..4071dfb59 --- /dev/null +++ b/src/lib/themes/blockHelpers.js @@ -0,0 +1,61 @@ +import {DEFAULT_THEME, getColorsForTheme} from '.'; + +// scratch-blocks colours has a pen property that scratch-gui uses for all extensions +const getExtensionColors = theme => getColorsForTheme(theme).pen; + +/** + * Applies extension color theme to categories. + * No changes are applied if called with the default theme, allowing extensions to provide their own colors. + * These colors are not seen if the category provides a blockIconURI. + * @param {Array.} dynamicBlockXML - XML for each category of extension blocks, returned from getBlocksXML + * in the vm runtime. + * @param {string} theme - Theme name + * @returns {Array.} Dynamic block XML updated with colors. + */ +const injectExtensionCategoryColors = (dynamicBlockXML, theme) => { + // Don't do any manipulation for the default theme + if (theme === DEFAULT_THEME) return dynamicBlockXML; + + const extensionColors = getExtensionColors(theme); + const parser = new DOMParser(); + const serializer = new XMLSerializer(); + + return dynamicBlockXML.map(extension => { + const dom = parser.parseFromString(extension.xml, 'text/xml'); + + dom.documentElement.setAttribute('colour', extensionColors.primary); + // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. + dom.documentElement.setAttribute('secondaryColour', extensionColors.tertiary); + + return { + ...extension, + xml: serializer.serializeToString(dom) + }; + }); +}; + +/** + * Applies extension color theme to static block json. + * No changes are applied if called with the default theme, allowing extensions to provide their own colors. + * @param {object} blockInfoJson - Static block json + * @param {string} theme - Theme name + * @returns {object} Block info json with updated colors. The original blockInfoJson is not modified. + */ +const injectExtensionBlockColors = (blockInfoJson, theme) => { + // Don't do any manipulation for the default theme + if (theme === DEFAULT_THEME) return blockInfoJson; + + const extensionColors = getExtensionColors(theme); + + return { + ...blockInfoJson, + colour: extensionColors.primary, + colourSecondary: extensionColors.secondary, + colourTertiary: extensionColors.tertiary + }; +}; + +export { + injectExtensionBlockColors, + injectExtensionCategoryColors +}; diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index e1fa87f71..bd1edcb5b 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -4,12 +4,13 @@ import darkMode from './dark-mode'; import highContrast from './high-contrast'; import defaultColors from './default-colors'; +const DEFAULT_THEME = 'standard'; const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors); const themeMap = { 'dark-mode': mergeWithDefaults(darkMode), 'high-contrast': mergeWithDefaults(highContrast), - 'standard': defaultColors + [DEFAULT_THEME]: defaultColors }; const getColorsForTheme = theme => { @@ -25,6 +26,7 @@ const getColorsForTheme = theme => { const themes = Object.keys(themeMap); export { + DEFAULT_THEME, defaultColors, getColorsForTheme, themes diff --git a/src/reducers/theme.js b/src/reducers/theme.js index 5d05eff00..63ddabc0e 100644 --- a/src/reducers/theme.js +++ b/src/reducers/theme.js @@ -1,7 +1,9 @@ +import {DEFAULT_THEME} from '../lib/themes'; + const SET_THEME = 'scratch-gui/theme/SET_THEME'; const initialState = { - theme: 'standard' + theme: DEFAULT_THEME }; const reducer = (state = initialState, action) => { diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index d281add0c..8bf70ebe5 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -1,4 +1,5 @@ -import {defaultColors, getColorsForTheme} from '../../../src/lib/themes'; +import {defaultColors, DEFAULT_THEME, getColorsForTheme} from '../../../src/lib/themes'; +import {injectExtensionBlockColors} from '../../../src/lib/themes/blockHelpers'; jest.mock('../../../src/lib/themes/default-colors'); jest.mock('../../../src/lib/themes/dark-mode'); @@ -19,4 +20,42 @@ describe('themes', () => { expect(colors.motion.secondary).toEqual('#222222'); }); + + test('updates extension blocks based on theme', () => { + const blockInfoJson = { + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }; + + const updated = injectExtensionBlockColors(blockInfoJson, 'dark-mode'); + + expect(updated).toEqual({ + type: 'dummy_block', + colour: '#FFFFFF', + colourSecondary: '#EEEEEE', + colourTertiary: '#DDDDDD' + }); + // The original value was not modified + expect(blockInfoJson.colour).toBe('#0FBD8C'); + }); + + test('bypasses updates if using the default theme', () => { + const blockInfoJson = { + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }; + + const updated = injectExtensionBlockColors(blockInfoJson, DEFAULT_THEME); + + expect(updated).toEqual({ + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }); + }); }); From d2c5d6211c1b2e1052e85fc1e7e924a1cce92bc9 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 24 Feb 2023 09:52:41 -0500 Subject: [PATCH 05/21] chore: use constant for default theme --- src/containers/monitor.jsx | 3 ++- test/unit/components/monitor-list.test.jsx | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/containers/monitor.jsx b/src/containers/monitor.jsx index eee63392e..f45c2d1ef 100644 --- a/src/containers/monitor.jsx +++ b/src/containers/monitor.jsx @@ -9,6 +9,7 @@ import {addMonitorRect, getInitialPosition, resizeMonitorRect, removeMonitorRect import {getVariable, setVariableValue} from '../lib/variable-utils'; import importCSV from '../lib/import-csv'; import downloadBlob from '../lib/download-blob'; +import {DEFAULT_THEME} from '../lib/themes'; import SliderPrompt from './slider-prompt.jsx'; import {connect} from 'react-redux'; @@ -267,7 +268,7 @@ Monitor.propTypes = { y: PropTypes.number }; Monitor.defaultProps = { - theme: 'standard' + theme: DEFAULT_THEME }; const mapStateToProps = state => ({ monitorLayout: state.scratchGui.monitorLayout, diff --git a/test/unit/components/monitor-list.test.jsx b/test/unit/components/monitor-list.test.jsx index 5a3cdf4b2..b9576d464 100644 --- a/test/unit/components/monitor-list.test.jsx +++ b/test/unit/components/monitor-list.test.jsx @@ -1,9 +1,10 @@ import React from 'react'; -import {mountWithIntl} from '../../helpers/intl-helpers.jsx'; -import MonitorList from '../../../src/components/monitor-list/monitor-list.jsx'; import {OrderedMap} from 'immutable'; import configureStore from 'redux-mock-store'; import {Provider} from 'react-redux'; +import {mountWithIntl} from '../../helpers/intl-helpers.jsx'; +import MonitorList from '../../../src/components/monitor-list/monitor-list.jsx'; +import {DEFAULT_THEME} from '../../../src/lib/themes'; describe('MonitorListComponent', () => { const store = configureStore()({scratchGui: { @@ -12,7 +13,7 @@ describe('MonitorListComponent', () => { savedMonitorPositions: {} }, theme: { - theme: 'standard' + theme: DEFAULT_THEME }, toolbox: { toolboxXML: '' From 5b6903c7f03d8d3775b51e758e7cfc7c118e94d1 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 24 Feb 2023 10:34:36 -0500 Subject: [PATCH 06/21] feat: switch color theme based on operating system preferences --- src/containers/gui.jsx | 4 +- src/lib/system-preferences-hoc.jsx | 63 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/lib/system-preferences-hoc.jsx diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index c10df0fb3..30d41072f 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -36,6 +36,7 @@ import storage from '../lib/storage'; import vmListenerHOC from '../lib/vm-listener-hoc.jsx'; import vmManagerHOC from '../lib/vm-manager-hoc.jsx'; import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx'; +import systemPreferencesHOC from '../lib/system-preferences-hoc.jsx'; import GUIComponent from '../components/gui/gui.jsx'; import {setIsScratchDesktop} from '../lib/isScratchDesktop.js'; @@ -183,7 +184,8 @@ const WrappedGui = compose( vmListenerHOC, vmManagerHOC, SBFileUploaderHOC, - cloudManagerHOC + cloudManagerHOC, + systemPreferencesHOC )(ConnectedGUI); WrappedGui.setAppElement = ReactModal.setAppElement; diff --git a/src/lib/system-preferences-hoc.jsx b/src/lib/system-preferences-hoc.jsx new file mode 100644 index 000000000..0190af4c0 --- /dev/null +++ b/src/lib/system-preferences-hoc.jsx @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {connect} from 'react-redux'; +import {DEFAULT_THEME} from './themes'; +import {setTheme} from '../reducers/theme'; + +const prefersDarkQuery = '(prefers-color-scheme: dark)'; +const prefersHighContrastQuery = '(prefers-contrast: more)'; + +const getTheme = () => { + const highContrast = window.matchMedia(prefersHighContrastQuery).matches; + + if (highContrast) return 'high-contrast'; + + const dark = window.matchMedia(prefersDarkQuery).matches; + + if (dark) return 'dark-mode'; + + return DEFAULT_THEME; +}; + +const systemPreferencesHOC = function (WrappedComponent) { + class SystemPreferences extends React.Component { + componentDidMount () { + this.props.onSetTheme(getTheme()); + this.preferencesListener = () => this.props.onSetTheme(getTheme()); + this.highContrastMatchMedia = window.matchMedia(prefersHighContrastQuery); + this.highContrastMatchMedia.addEventListener('change', this.preferencesListener); + this.darkMatchMedia = window.matchMedia(prefersDarkQuery); + this.darkMatchMedia.addEventListener('change', this.preferencesListener); + } + + componentWillUnmount () { + this.highContrastMatchMedia.removeEventListener('change', this.preferencesListener); + this.darkMatchMedia.removeEventListener('change', this.preferencesListener); + } + + render () { + const { + /* eslint-disable no-unused-vars */ + onSetTheme, + /* eslint-enable no-unused-vars */ + ...props + } = this.props; + return ; + } + } + + SystemPreferences.propTypes = { + onSetTheme: PropTypes.func + }; + + const mapDispatchToProps = dispatch => ({ + onSetTheme: theme => dispatch(setTheme(theme)) + }); + + return connect( + null, + mapDispatchToProps + )(SystemPreferences); +}; + +export default systemPreferencesHOC; From f61402d84e92c7b7c72c2ab36044606567d7b47d Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Mon, 27 Feb 2023 12:08:34 -0500 Subject: [PATCH 07/21] test: create a test for updating extension category colors --- test/unit/util/themes.test.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index 8bf70ebe5..3ff15dabc 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -1,10 +1,20 @@ import {defaultColors, DEFAULT_THEME, getColorsForTheme} from '../../../src/lib/themes'; -import {injectExtensionBlockColors} from '../../../src/lib/themes/blockHelpers'; +import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../../../src/lib/themes/blockHelpers'; jest.mock('../../../src/lib/themes/default-colors'); jest.mock('../../../src/lib/themes/dark-mode'); describe('themes', () => { + let serializeToString; + + beforeEach(() => { + serializeToString = jest.fn(() => 'mocked xml'); + + global.XMLSerializer = () => ({ + serializeToString + }); + }); + test('provides the default theme colors', () => { expect(defaultColors.motion.primary).toEqual('#111111'); }); @@ -58,4 +68,20 @@ describe('themes', () => { colourTertiary: '#0B8E69' }); }); + + test('updates extension category based on theme', () => { + const dynamicBlockXML = [ + { + id: 'pen', + xml: '' + } + ]; + + injectExtensionCategoryColors(dynamicBlockXML, 'dark-mode'); + + // XMLSerializer is not available outside the browser. + // Verify the mocked XMLSerializer.serializeToString is called with updated colors. + expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('colour')).toBe('#FFFFFF'); + expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('secondaryColour')).toBe('#DDDDDD'); + }); }); From 8533704cdbc291cfc60ff38bd5c23f4fd11f7a5e Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Thu, 2 Mar 2023 15:19:30 -0500 Subject: [PATCH 08/21] chore: config changes to support new quaternary color --- src/lib/blocks.js | 3 +++ src/lib/themes/blockHelpers.js | 3 ++- src/lib/themes/dark-mode.js | 36 +++++++++++++++++++++----------- src/lib/themes/default-colors.js | 36 +++++++++++++++++++++----------- src/lib/themes/high-contrast.js | 5 ++--- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/lib/blocks.js b/src/lib/blocks.js index 809a77ce2..f1dbbf8b7 100644 --- a/src/lib/blocks.js +++ b/src/lib/blocks.js @@ -24,6 +24,7 @@ export default function (vm) { colour: colors.secondary, colourSecondary: colors.secondary, colourTertiary: colors.tertiary, + colourQuaternary: colors.quaternary, outputShape: ScratchBlocks.OUTPUT_SHAPE_ROUND }; }; @@ -43,6 +44,7 @@ export default function (vm) { colour: colors.primary, colourSecondary: colors.secondary, colourTertiary: colors.tertiary, + colourQuaternary: colors.quaternary, extensions: ['shape_hat'] }; }; @@ -69,6 +71,7 @@ export default function (vm) { colour: ScratchBlocks.Colours.sensing.primary, colourSecondary: ScratchBlocks.Colours.sensing.secondary, colourTertiary: ScratchBlocks.Colours.sensing.tertiary, + colourQuaternary: ScratchBlocks.Colours.sensing.quaternary, outputShape: ScratchBlocks.OUTPUT_SHAPE_ROUND }; }; diff --git a/src/lib/themes/blockHelpers.js b/src/lib/themes/blockHelpers.js index 4071dfb59..baf0cb0f9 100644 --- a/src/lib/themes/blockHelpers.js +++ b/src/lib/themes/blockHelpers.js @@ -51,7 +51,8 @@ const injectExtensionBlockColors = (blockInfoJson, theme) => { ...blockInfoJson, colour: extensionColors.primary, colourSecondary: extensionColors.secondary, - colourTertiary: extensionColors.tertiary + colourTertiary: extensionColors.tertiary, + colourQuaternary: extensionColors.quaternary }; }; diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark-mode.js index 707f6a33c..6842c8b39 100644 --- a/src/lib/themes/dark-mode.js +++ b/src/lib/themes/dark-mode.js @@ -2,57 +2,68 @@ const blockColors = { motion: { primary: '#0F1E33', secondary: '#4C4C4C', - tertiary: '#4C97FF' + tertiary: '#4C97FF', + quaternary: '#4C97FF' }, looks: { primary: '#1E1433', secondary: '#4C4C4C', - tertiary: '#9966FF' + tertiary: '#9966FF', + quaternary: '#9966FF' }, sounds: { primary: '#291329', secondary: '#4C4C4C', - tertiary: '#CF63CF' + tertiary: '#CF63CF', + quaternary: '#CF63CF' }, control: { primary: '#332205', secondary: '#4C4C4C', - tertiary: '#FFAB19' + tertiary: '#FFAB19', + quaternary: '#FFAB19' }, event: { primary: '#332600', secondary: '#4C4C4C', - tertiary: '#FFBF00' + tertiary: '#FFBF00', + quaternary: '#FFBF00' }, sensing: { primary: '#12232A', secondary: '#4C4C4C', - tertiary: '#5CB1D6' + tertiary: '#5CB1D6', + quaternary: '#5CB1D6' }, pen: { primary: '#03251C', secondary: '#4C4C4C', - tertiary: '#0fBD8C' + tertiary: '#0fBD8C', + quaternary: '#0fBD8C' }, operators: { primary: '#112611', secondary: '#4C4C4C', - tertiary: '#59C059' + tertiary: '#59C059', + quaternary: '#59C059' }, data: { primary: '#331C05', secondary: '#4C4C4C', - tertiary: '#FF8C1A' + tertiary: '#FF8C1A', + quaternary: '#FF8C1A' }, data_lists: { primary: '#331405', secondary: '#4C4C4C', - tertiary: '#FF661A' + tertiary: '#FF661A', + quaternary: '#FF661A' }, more: { primary: '#331419', secondary: '#4C4C4C', - tertiary: '#FF6680' + tertiary: '#FF6680', + quaternary: '#FF6680' }, text: 'rgba(255, 255, 255, .7)', textFieldText: '#E5E5E5', @@ -61,7 +72,8 @@ const blockColors = { toolboxText: '#E5E5E5', toolbox: '#121212', flyout: '#121212', - textField: '#4C4C4C' + textField: '#4C4C4C', + menuHover: 'rgba(255, 255, 255, 0.3)' }; export default blockColors; diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default-colors.js index e7567573e..1b53026c5 100644 --- a/src/lib/themes/default-colors.js +++ b/src/lib/themes/default-colors.js @@ -2,59 +2,70 @@ const blockColors = { motion: { primary: '#4C97FF', secondary: '#4280D7', - tertiary: '#3373CC' + tertiary: '#3373CC', + quaternary: '#3373CC' }, looks: { primary: '#9966FF', secondary: '#855CD6', - tertiary: '#774DCB' + tertiary: '#774DCB', + quaternary: '#774DCB' }, sounds: { primary: '#CF63CF', secondary: '#C94FC9', - tertiary: '#BD42BD' + tertiary: '#BD42BD', + quaternary: '#BD42BD' }, control: { primary: '#FFAB19', secondary: '#EC9C13', - tertiary: '#CF8B17' + tertiary: '#CF8B17', + quaternary: '#CF8B17' }, event: { primary: '#FFBF00', secondary: '#E6AC00', - tertiary: '#CC9900' + tertiary: '#CC9900', + quaternary: '#CC9900' }, sensing: { primary: '#5CB1D6', secondary: '#47A8D1', - tertiary: '#2E8EB8' + tertiary: '#2E8EB8', + quaternary: '#2E8EB8' }, pen: { primary: '#0fBD8C', secondary: '#0DA57A', - tertiary: '#0B8E69' + tertiary: '#0B8E69', + quaternary: '#0B8E69' }, operators: { primary: '#59C059', secondary: '#46B946', - tertiary: '#389438' + tertiary: '#389438', + quaternary: '#389438' }, data: { primary: '#FF8C1A', secondary: '#FF8000', - tertiary: '#DB6E00' + tertiary: '#DB6E00', + quaternary: '#DB6E00' }, // This is not a new category, but rather for differentiation // between lists and scalar variables. data_lists: { primary: '#FF661A', secondary: '#FF5500', - tertiary: '#E64D00' + tertiary: '#E64D00', + quaternary: '#E64D00' }, more: { primary: '#FF6680', secondary: '#FF4D6A', - tertiary: '#FF3355' + tertiary: '#FF3355', + quaternary: '#FF3355' }, text: '#FFFFFF', workspace: '#F9F9F9', @@ -85,7 +96,8 @@ const blockColors = { numPadActiveBackground: '#435F91', numPadText: 'white', // Do not use hex here, it cannot be inlined with data-uri SVG valueReportBackground: '#FFFFFF', - valueReportBorder: '#AAAAAA' + valueReportBorder: '#AAAAAA', + menuHover: 'rgba(0, 0, 0, 0.2)' }; export default blockColors; diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js index dc77490b5..e8f7c0121 100644 --- a/src/lib/themes/high-contrast.js +++ b/src/lib/themes/high-contrast.js @@ -3,8 +3,6 @@ const blockColors = { primary: '#80B5FF', secondary: '#B3D2FF', tertiary: '#3373CC', - // this field is not used in the "classic" Scratch 3 color scheme - // If we add "modes" we'd have to handle what happens when this field is not defined. quaternary: '#CCE1FF' }, looks: { @@ -78,7 +76,8 @@ const blockColors = { insertionMarker: '#000000', insertionMarkerOpacity: 0.2, fieldShadow: 'rgba(255, 255, 255, 0.3)', - dragShadowOpacity: 0.6 + dragShadowOpacity: 0.6, + menuHover: 'rgba(255, 255, 255, 0.3)' }; export default blockColors; From 11560b84376e9803713684f4bf5399bbda3908f1 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Wed, 8 Mar 2023 17:02:26 -0500 Subject: [PATCH 09/21] feat: show theme picker in menu --- package-lock.json | 112 ++++++++++++++++-- package.json | 1 + src/components/gui/gui.jsx | 4 + .../language-selector/language-selector.css | 2 +- src/components/menu-bar/menu-bar.css | 28 +++-- src/components/menu-bar/menu-bar.jsx | 94 +++++++++++---- src/components/menu-bar/theme-menu.css | 28 +++++ src/components/menu-bar/theme-menu.jsx | 91 ++++++++++++++ src/components/menu/menu.css | 3 +- src/css/units.css | 1 - src/lib/system-preferences-hoc.jsx | 28 +++-- src/lib/themes/high-contrast.svg | 17 +++ src/lib/themes/index.js | 54 +++++++-- src/lib/themes/standard.svg | 17 +++ src/reducers/menus.js | 12 +- src/reducers/theme.js | 35 +++++- 16 files changed, 454 insertions(+), 73 deletions(-) create mode 100644 src/components/menu-bar/theme-menu.css create mode 100644 src/components/menu-bar/theme-menu.jsx create mode 100644 src/lib/themes/high-contrast.svg create mode 100644 src/lib/themes/standard.svg diff --git a/package-lock.json b/package-lock.json index 8ecae424c..3283671b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "bowser": "1.9.4", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", + "cookie": "^0.5.0", "copy-webpack-plugin": "6.4.1", "core-js": "2.5.7", "css-loader": "^1.0.0", @@ -10408,10 +10409,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true, + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -13505,6 +13505,15 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "node_modules/express/node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -14434,6 +14443,7 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/ansi-regex": { @@ -14441,6 +14451,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14451,6 +14462,7 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/are-we-there-yet": { @@ -14458,6 +14470,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "delegates": "^1.0.0", @@ -14469,6 +14482,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/brace-expansion": { @@ -14476,6 +14490,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -14487,6 +14502,7 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/code-point-at": { @@ -14494,6 +14510,7 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14504,6 +14521,7 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/console-control-strings": { @@ -14511,6 +14529,7 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/core-util-is": { @@ -14518,14 +14537,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "ms": "^2.1.1" @@ -14536,6 +14556,7 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=4.0.0" @@ -14546,6 +14567,7 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/detect-libc": { @@ -14553,6 +14575,7 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "inBundle": true, + "license": "Apache-2.0", "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" @@ -14566,6 +14589,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "minipass": "^2.2.1" @@ -14576,6 +14600,7 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/gauge": { @@ -14583,6 +14608,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "aproba": "^1.0.3", @@ -14600,6 +14626,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -14618,6 +14645,7 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/iconv-lite": { @@ -14625,6 +14653,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -14638,6 +14667,7 @@ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "minimatch": "^3.0.4" @@ -14648,6 +14678,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "once": "^1.3.0", @@ -14659,13 +14690,24 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "inBundle": true, + "license": "ISC", "optional": true }, + "node_modules/fsevents/node_modules/ini": { + "version": "1.3.5", + "inBundle": true, + "license": "ISC", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/fsevents/node_modules/is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "number-is-nan": "^1.0.0" @@ -14679,6 +14721,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/minimatch": { @@ -14686,6 +14729,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -14699,6 +14743,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/minipass": { @@ -14706,6 +14751,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "safe-buffer": "^5.1.2", @@ -14717,6 +14763,7 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "minipass": "^2.2.1" @@ -14726,8 +14773,8 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "minimist": "0.0.8" @@ -14741,6 +14788,7 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/needle": { @@ -14748,6 +14796,7 @@ "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "debug": "^4.1.0", @@ -14765,8 +14814,8 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", - "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future", "inBundle": true, + "license": "BSD-3-Clause", "optional": true, "dependencies": { "detect-libc": "^1.0.2", @@ -14789,6 +14838,7 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "abbrev": "1", @@ -14803,6 +14853,7 @@ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/npm-packlist": { @@ -14810,6 +14861,7 @@ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "ignore-walk": "^3.0.1", @@ -14821,6 +14873,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "are-we-there-yet": "~1.1.2", @@ -14834,6 +14887,7 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14844,6 +14898,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14854,6 +14909,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "wrappy": "1" @@ -14864,6 +14920,7 @@ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14874,6 +14931,7 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14884,6 +14942,7 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "os-homedir": "^1.0.0", @@ -14895,6 +14954,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14905,6 +14965,7 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/rc": { @@ -14912,6 +14973,7 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "inBundle": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "optional": true, "dependencies": { "deep-extend": "^0.6.0", @@ -14928,6 +14990,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/readable-stream": { @@ -14935,6 +14998,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "core-util-is": "~1.0.0", @@ -14951,6 +15015,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "glob": "^7.1.3" @@ -14964,6 +15029,7 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/safer-buffer": { @@ -14971,6 +15037,7 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/sax": { @@ -14978,6 +15045,7 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/semver": { @@ -14985,6 +15053,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "inBundle": true, + "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -14995,6 +15064,7 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/signal-exit": { @@ -15002,6 +15072,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/string_decoder": { @@ -15009,6 +15080,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "safe-buffer": "~5.1.0" @@ -15019,6 +15091,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "code-point-at": "^1.0.0", @@ -15034,6 +15107,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "inBundle": true, + "license": "MIT", "optional": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -15047,6 +15121,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "inBundle": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -15057,6 +15132,7 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "chownr": "^1.1.1", @@ -15076,6 +15152,7 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "inBundle": true, + "license": "MIT", "optional": true }, "node_modules/fsevents/node_modules/wide-align": { @@ -15083,6 +15160,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "inBundle": true, + "license": "ISC", "optional": true, "dependencies": { "string-width": "^1.0.2 || 2" @@ -15093,6 +15171,7 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/fsevents/node_modules/yallist": { @@ -15100,6 +15179,7 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "inBundle": true, + "license": "ISC", "optional": true }, "node_modules/function-bind": { @@ -39144,10 +39224,9 @@ } }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", @@ -41611,6 +41690,12 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -42532,6 +42617,11 @@ "bundled": true, "optional": true }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", diff --git a/package.json b/package.json index abfb05839..ef7070619 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "bowser": "1.9.4", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", + "cookie": "0.5.0", "copy-webpack-plugin": "6.4.1", "core-js": "2.5.7", "css-loader": "^1.0.0", diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 53330d3b7..360c725b7 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -67,6 +67,7 @@ const GUIComponent = props => { blocksTabVisible, cardsVisible, canChangeLanguage, + canChangeTheme, canCreateNew, canEditTitle, canManageFiles, @@ -213,6 +214,7 @@ const GUIComponent = props => { authorThumbnailUrl={authorThumbnailUrl} authorUsername={authorUsername} canChangeLanguage={canChangeLanguage} + canChangeTheme={canChangeTheme} canCreateCopy={canCreateCopy} canCreateNew={canCreateNew} canEditTitle={canEditTitle} @@ -377,6 +379,7 @@ GUIComponent.propTypes = { basePath: PropTypes.string, blocksTabVisible: PropTypes.bool, canChangeLanguage: PropTypes.bool, + canChangeTheme: PropTypes.bool, canCreateCopy: PropTypes.bool, canCreateNew: PropTypes.bool, canEditTitle: PropTypes.bool, @@ -434,6 +437,7 @@ GUIComponent.defaultProps = { backpackVisible: false, basePath: './', canChangeLanguage: true, + canChangeTheme: true, canCreateNew: false, canEditTitle: false, canManageFiles: true, diff --git a/src/components/language-selector/language-selector.css b/src/components/language-selector/language-selector.css index 013e340dd..d5fb68618 100644 --- a/src/components/language-selector/language-selector.css +++ b/src/components/language-selector/language-selector.css @@ -4,7 +4,7 @@ /* Position the language select over the language icon, and make it transparent */ .language-select { position: absolute; - width: $language-selector-width; + width: 100%; height: $menu-bar-height; opacity: 0; user-select: none; diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index 78c639c15..a5fbf5bac 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -27,7 +27,8 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: $menu-bar-standard-font-size; font-weight: bold; - background-color: $motion-primary; + /* background-color: $motion-primary; */ + background-color: #774DCB; color: $ui-white; } @@ -52,16 +53,17 @@ .language-icon { height: 1.5rem; vertical-align: middle; -} - -.language-caret { - margin: 0 .125rem; + margin-inline-end: .125rem; } .language-menu { box-sizing: border-box; display: inline-flex; - width: $language-selector-width; +} + +.language-label { + margin-inline-start: .25rem; + margin-inline-end: .5rem; } .menu-bar-item { @@ -106,10 +108,6 @@ padding: 0 0.75rem; } -.menu-bar-item.language-menu { - padding: 0 0.5rem; -} - .menu-bar-menu { margin-top: $menu-bar-height; z-index: $z-index-menu-bar; @@ -224,3 +222,13 @@ margin: 0.5rem; vertical-align: middle; } + +@media only screen and (max-width: 1024px) { + .language-label, .tutorials-label { + display: none; + } + + .help-icon { + margin-right: 0; + } +} diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index 126f0d460..b3cb50987 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -8,6 +8,7 @@ import bowser from 'bowser'; import React from 'react'; import VM from 'scratch-vm'; +import locales from 'scratch-l10n'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; @@ -28,6 +29,7 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; +import ThemeMenu from './theme-menu.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -58,8 +60,12 @@ import { languageMenuOpen, openLoginMenu, closeLoginMenu, - loginMenuOpen + loginMenuOpen, + openThemeMenu, + closeThemeMenu, + themeMenuOpen } from '../../reducers/menus'; +import {setTheme} from '../../reducers/theme.js'; import collectMetadata from '../../lib/collect-metadata'; @@ -76,6 +82,7 @@ import aboutIcon from './icon--about.svg'; import scratchLogo from './scratch-logo.svg'; import sharedMessages from '../../lib/shared-messages'; +import {DEFAULT_THEME, HIGH_CONTRAST_THEME} from '../../lib/themes/index.js'; const ariaMessages = defineMessages({ language: { @@ -161,6 +168,7 @@ class MenuBar extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'handleChangeTheme', 'handleClickNew', 'handleClickRemix', 'handleClickSave', @@ -256,6 +264,19 @@ class MenuBar extends React.Component { this.props.onClickLanguage(e); } } + handleChangeTheme (theme) { + this.props.onChangeTheme(theme); + this.props.onRequestCloseTheme(); + + let systemPreferencesTheme = DEFAULT_THEME; + const prefersHighContrastQuery = '(prefers-contrast: more)'; + if (window.matchMedia(prefersHighContrastQuery).matches) { + systemPreferencesTheme = HIGH_CONTRAST_THEME; + } + + // Remove the cookie if it matches the system preferences. Otherwise store the theme in the cookie. + document.cookie = `scratchtheme=${systemPreferencesTheme === theme ? '' : theme}`; + } restoreOptionMessage (deletedItem) { switch (deletedItem) { case 'Sprite': @@ -402,18 +423,26 @@ class MenuBar extends React.Component { {(this.props.canChangeLanguage) && (
-
- - -
+ + {locales[this.props.currentLocale].name} +
)} + {(this.props.canChangeTheme) && ()} {(this.props.canManageFiles) && (
- -
- - -
- {this.props.canEditTitle ? (
) : [])}
+ +
+ + + + +
+ {/* show the proper UI in the account menu, given whether the user is @@ -761,6 +792,7 @@ MenuBar.propTypes = { authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), autoUpdateProject: PropTypes.func, canChangeLanguage: PropTypes.bool, + canChangeTheme: PropTypes.bool, canCreateCopy: PropTypes.bool, canCreateNew: PropTypes.bool, canEditTitle: PropTypes.bool, @@ -770,6 +802,7 @@ MenuBar.propTypes = { canShare: PropTypes.bool, className: PropTypes.string, confirmReadyToReplaceProject: PropTypes.func, + currentLocale: PropTypes.string.isRequired, editMenuOpen: PropTypes.bool, enableCommunity: PropTypes.bool, fileMenuOpen: PropTypes.bool, @@ -782,6 +815,7 @@ MenuBar.propTypes = { locale: PropTypes.string.isRequired, loginMenuOpen: PropTypes.bool, logo: PropTypes.string, + onChangeTheme: PropTypes.func, onClickAbout: PropTypes.oneOfType([ PropTypes.func, // button mode: call this callback when the About button is clicked PropTypes.arrayOf( // menu mode: list of items in the About menu @@ -812,6 +846,8 @@ MenuBar.propTypes = { onRequestCloseFile: PropTypes.func, onRequestCloseLanguage: PropTypes.func, onRequestCloseLogin: PropTypes.func, + onRequestCloseTheme: PropTypes.func, + onRequestOpenTheme: PropTypes.func, onSeeCommunity: PropTypes.func, onShare: PropTypes.func, onStartSelectingFileUpload: PropTypes.func, @@ -821,6 +857,8 @@ MenuBar.propTypes = { sessionExists: PropTypes.bool, shouldSaveBeforeTransition: PropTypes.func, showComingSoon: PropTypes.bool, + theme: PropTypes.string, + themeMenuOpen: PropTypes.bool, userOwnsProject: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired @@ -837,6 +875,7 @@ const mapStateToProps = (state, ownProps) => { return { aboutMenuOpen: aboutMenuOpen(state), accountMenuOpen: accountMenuOpen(state), + currentLocale: state.locales.locale, fileMenuOpen: fileMenuOpen(state), editMenuOpen: editMenuOpen(state), isRtl: state.locales.isRtl, @@ -847,6 +886,8 @@ const mapStateToProps = (state, ownProps) => { loginMenuOpen: loginMenuOpen(state), projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', + theme: state.scratchGui.theme.theme, + themeMenuOpen: themeMenuOpen(state), username: user ? user.username : null, userOwnsProject: ownProps.authorUsername && user && (ownProps.authorUsername === user.username), @@ -869,11 +910,14 @@ const mapDispatchToProps = dispatch => ({ onRequestCloseLogin: () => dispatch(closeLoginMenu()), onRequestOpenAbout: () => dispatch(openAboutMenu()), onRequestCloseAbout: () => dispatch(closeAboutMenu()), + onRequestOpenTheme: () => dispatch(openThemeMenu()), + onRequestCloseTheme: () => dispatch(closeThemeMenu()), onClickNew: needSave => dispatch(requestNewProject(needSave)), onClickRemix: () => dispatch(remixProject()), onClickSave: () => dispatch(manualUpdateProject()), onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()), - onSeeCommunity: () => dispatch(setPlayer(true)) + onSeeCommunity: () => dispatch(setPlayer(true)), + onChangeTheme: theme => dispatch(setTheme(theme)) }); export default compose( diff --git a/src/components/menu-bar/theme-menu.css b/src/components/menu-bar/theme-menu.css new file mode 100644 index 000000000..24fcec18a --- /dev/null +++ b/src/components/menu-bar/theme-menu.css @@ -0,0 +1,28 @@ +@import "../../css/colors.css"; + +.theme-label { + margin-inline-start: .25rem; + margin-inline-end: .5rem; +} + +.theme-icon { + height: 1.5rem; + vertical-align: middle; + margin-inline-end: .25rem; +} + +.theme-option { + display: flex; + align-items: center; + gap: .5rem; +} + +.theme-active { + background-color: $ui-black-transparent; +} + +@media only screen and (max-width: 1024px) { + .theme-label { + display: none; + } +} diff --git a/src/components/menu-bar/theme-menu.jsx b/src/components/menu-bar/theme-menu.jsx new file mode 100644 index 000000000..d9ca0b1a5 --- /dev/null +++ b/src/components/menu-bar/theme-menu.jsx @@ -0,0 +1,91 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {FormattedMessage} from 'react-intl'; +import classNames from 'classnames'; + +import MenuBarMenu from './menu-bar-menu.jsx'; +import {DEFAULT_THEME, HIGH_CONTRAST_THEME, themeMap} from '../../lib/themes'; +import {MenuItem, MenuSection} from '../menu/menu.jsx'; + +import dropdownCaret from './dropdown-caret.svg'; + +import styles from './theme-menu.css'; +import menuBarStyles from './menu-bar.css'; + +const ThemeMenuItem = props => { + const themeInfo = themeMap[props.theme]; + + return ( + +
+ + +
+
); +}; + +ThemeMenuItem.propTypes = { + isRtl: PropTypes.bool, + isSelected: PropTypes.bool, + onClick: PropTypes.func, + theme: PropTypes.string +}; + +const ThemeMenu = props => { + const enabledThemes = [DEFAULT_THEME, HIGH_CONTRAST_THEME]; + const themeInfo = themeMap[props.theme]; + + return (
+ + + + + + + + {enabledThemes.map(enabledTheme => ( + props.onChange(enabledTheme)} + theme={enabledTheme} + />) + )} + + +
); +}; + +ThemeMenu.propTypes = { + className: PropTypes.string, + isRtl: PropTypes.bool, + onChange: PropTypes.func, + onRequestClose: PropTypes.func, + onRequestOpen: PropTypes.func, + theme: PropTypes.string, + themeMenuOpen: PropTypes.bool +}; + +export default ThemeMenu; diff --git a/src/components/menu/menu.css b/src/components/menu/menu.css index c48f0d25d..d671d1381 100644 --- a/src/components/menu/menu.css +++ b/src/components/menu/menu.css @@ -4,7 +4,8 @@ position: absolute; border: 1px solid $ui-black-transparent; border-radius: 0 0 8px 8px; - background-color: $motion-primary; + /* background-color: $motion-primary; */ + background-color: #774DCB; padding: 0; margin: 0; min-width: 186px; diff --git a/src/css/units.css b/src/css/units.css index 0b00f3234..587385fc0 100644 --- a/src/css/units.css +++ b/src/css/units.css @@ -6,7 +6,6 @@ $space: 0.5rem; $sprites-per-row: 5; $menu-bar-height: 3rem; -$language-selector-width: 3rem; $sprite-info-height: 6rem; $stage-menu-height: 2.75rem; diff --git a/src/lib/system-preferences-hoc.jsx b/src/lib/system-preferences-hoc.jsx index 0190af4c0..1f2cf6f91 100644 --- a/src/lib/system-preferences-hoc.jsx +++ b/src/lib/system-preferences-hoc.jsx @@ -1,20 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; +import cookie from 'cookie'; + import {DEFAULT_THEME} from './themes'; import {setTheme} from '../reducers/theme'; -const prefersDarkQuery = '(prefers-color-scheme: dark)'; +// Dark mode is not yet supported +// const prefersDarkQuery = '(prefers-color-scheme: dark)'; const prefersHighContrastQuery = '(prefers-contrast: more)'; const getTheme = () => { - const highContrast = window.matchMedia(prefersHighContrastQuery).matches; - - if (highContrast) return 'high-contrast'; - - const dark = window.matchMedia(prefersDarkQuery).matches; - - if (dark) return 'dark-mode'; + if (window.matchMedia(prefersHighContrastQuery).matches) return 'high-contrast'; return DEFAULT_THEME; }; @@ -22,17 +19,22 @@ const getTheme = () => { const systemPreferencesHOC = function (WrappedComponent) { class SystemPreferences extends React.Component { componentDidMount () { - this.props.onSetTheme(getTheme()); - this.preferencesListener = () => this.props.onSetTheme(getTheme()); + // this.props.onSetTheme(getTheme()); + this.preferencesListener = () => { + const obj = cookie.parse(document.cookie) || {}; + const themeCookie = obj.scratchtheme; + + // Only use system preferences if there is not a specified cookie. + if (themeCookie) return; + + this.props.onSetTheme(getTheme()); + } this.highContrastMatchMedia = window.matchMedia(prefersHighContrastQuery); this.highContrastMatchMedia.addEventListener('change', this.preferencesListener); - this.darkMatchMedia = window.matchMedia(prefersDarkQuery); - this.darkMatchMedia.addEventListener('change', this.preferencesListener); } componentWillUnmount () { this.highContrastMatchMedia.removeEventListener('change', this.preferencesListener); - this.darkMatchMedia.removeEventListener('change', this.preferencesListener); } render () { diff --git a/src/lib/themes/high-contrast.svg b/src/lib/themes/high-contrast.svg new file mode 100644 index 000000000..4bdf2fa04 --- /dev/null +++ b/src/lib/themes/high-contrast.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index bd1edcb5b..81151406f 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -1,33 +1,73 @@ import defaultsDeep from 'lodash.defaultsdeep'; +import {defineMessages} from 'react-intl'; import darkMode from './dark-mode'; import highContrast from './high-contrast'; import defaultColors from './default-colors'; +import defaultIcon from './standard.svg'; +import highContrastIcon from './high-contrast.svg'; + const DEFAULT_THEME = 'standard'; +const HIGH_CONTRAST_THEME = 'high-contrast'; +const DARK_THEME = 'dark-mode'; + const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors); +const messages = defineMessages({ + [DEFAULT_THEME]: { + id: 'gui.theme.default', + defaultMessage: 'Default', + description: 'label for default theme' + }, + [DARK_THEME]: { + id: 'gui.theme.dark', + defaultMessage: 'Dark', + description: 'label for dark mode theme' + }, + [HIGH_CONTRAST_THEME]: { + id: 'gui.theme.highContrast', + defaultMessage: 'High Text Contrast', + description: 'label for high constast theme' + } +}); + const themeMap = { - 'dark-mode': mergeWithDefaults(darkMode), - 'high-contrast': mergeWithDefaults(highContrast), - [DEFAULT_THEME]: defaultColors + [DEFAULT_THEME]: { + colors: defaultColors, + label: messages[DEFAULT_THEME], + icon: defaultIcon + }, + [DARK_THEME]: { + colors: mergeWithDefaults(darkMode), + label: messages[DARK_THEME] + }, + [HIGH_CONTRAST_THEME]: { + colors: mergeWithDefaults(highContrast), + label: messages[HIGH_CONTRAST_THEME], + icon: highContrastIcon + } }; const getColorsForTheme = theme => { - const colors = themeMap[theme]; + const themeInfo = themeMap[theme]; - if (!colors) { + if (!themeInfo) { throw new Error(`Undefined theme ${theme}`); } - return colors; + return themeInfo.colors; }; const themes = Object.keys(themeMap); export { DEFAULT_THEME, + DARK_THEME, + HIGH_CONTRAST_THEME, defaultColors, getColorsForTheme, - themes + messages, + themes, + themeMap }; diff --git a/src/lib/themes/standard.svg b/src/lib/themes/standard.svg new file mode 100644 index 000000000..8f54974f6 --- /dev/null +++ b/src/lib/themes/standard.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/reducers/menus.js b/src/reducers/menus.js index fc3687f5a..c8c4a6945 100644 --- a/src/reducers/menus.js +++ b/src/reducers/menus.js @@ -7,6 +7,7 @@ const MENU_FILE = 'fileMenu'; const MENU_EDIT = 'editMenu'; const MENU_LANGUAGE = 'languageMenu'; const MENU_LOGIN = 'loginMenu'; +const MENU_THEME = 'themeMenu'; const initialState = { @@ -15,7 +16,8 @@ const initialState = { [MENU_FILE]: false, [MENU_EDIT]: false, [MENU_LANGUAGE]: false, - [MENU_LOGIN]: false + [MENU_LOGIN]: false, + [MENU_THEME]: false }; const reducer = function (state, action) { @@ -59,6 +61,9 @@ const languageMenuOpen = state => state.scratchGui.menus[MENU_LANGUAGE]; const openLoginMenu = () => openMenu(MENU_LOGIN); const closeLoginMenu = () => closeMenu(MENU_LOGIN); const loginMenuOpen = state => state.scratchGui.menus[MENU_LOGIN]; +const openThemeMenu = () => openMenu(MENU_THEME); +const closeThemeMenu = () => closeMenu(MENU_THEME); +const themeMenuOpen = state => state.scratchGui.menus[MENU_THEME]; export { reducer as default, @@ -80,5 +85,8 @@ export { languageMenuOpen, openLoginMenu, closeLoginMenu, - loginMenuOpen + loginMenuOpen, + openThemeMenu, + closeThemeMenu, + themeMenuOpen }; diff --git a/src/reducers/theme.js b/src/reducers/theme.js index 63ddabc0e..0c8664332 100644 --- a/src/reducers/theme.js +++ b/src/reducers/theme.js @@ -1,9 +1,24 @@ -import {DEFAULT_THEME} from '../lib/themes'; +import cookie from 'cookie'; + +import {DEFAULT_THEME, HIGH_CONTRAST_THEME} from '../lib/themes'; const SET_THEME = 'scratch-gui/theme/SET_THEME'; +const detectTheme = () => { + const obj = cookie.parse(document.cookie) || {}; + const themeCookie = obj.scratchtheme; + + if ([DEFAULT_THEME, HIGH_CONTRAST_THEME].includes(themeCookie)) return themeCookie; + + // No cookie set. Fall back to system preferences + const prefersHighContrastQuery = '(prefers-contrast: more)'; + if (window.matchMedia(prefersHighContrastQuery).matches) return HIGH_CONTRAST_THEME; + + return DEFAULT_THEME; +}; + const initialState = { - theme: DEFAULT_THEME + theme: detectTheme() }; const reducer = (state = initialState, action) => { @@ -25,3 +40,19 @@ export { initialState as themeInitialState, setTheme }; + + +const prefersDarkQuery = '(prefers-color-scheme: dark)'; +const prefersHighContrastQuery = '(prefers-contrast: more)'; + +const getTheme = () => { + const highContrast = window.matchMedia(prefersHighContrastQuery).matches; + + if (highContrast) return 'high-contrast'; + + const dark = window.matchMedia(prefersDarkQuery).matches; + + if (dark) return 'dark-mode'; + + return DEFAULT_THEME; +}; From b1c6ac272108fb93d4dfcc4b685305302c915adf Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 10 Mar 2023 11:14:46 -0500 Subject: [PATCH 10/21] test: add tests for theme picker --- README.md | 7 +- src/components/menu-bar/menu-bar.css | 20 ++- src/components/menu-bar/menu-bar.jsx | 13 +- src/components/menu-bar/theme-menu.css | 17 ++- src/components/menu-bar/theme-menu.jsx | 14 +- src/components/menu/menu.css | 3 +- src/containers/blocks.jsx | 4 +- src/css/colors.css | 2 + src/lib/system-preferences-hoc.jsx | 19 +-- src/lib/themes/index.js | 8 +- src/lib/themes/themePersistance.js | 44 +++++++ src/reducers/theme.js | 33 +---- test/helpers/selenium-helper.js | 1 + test/integration/menu-bar.test.js | 27 ++++ test/unit/components/menu-bar.test.jsx | 5 +- test/unit/components/monitor.test.jsx | 5 +- test/unit/util/themes.test.js | 171 ++++++++++++++++--------- 17 files changed, 246 insertions(+), 147 deletions(-) create mode 100644 src/lib/themes/themePersistance.js diff --git a/README.md b/README.md index 4477a140c..da45b9ce9 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,6 @@ $(npm bin)/jest --runInBand test/unit/components/button.test.jsx ``` #### Running integration tests - Integration tests use a headless browser to manipulate the actual HTML and javascript that the repo produces. You will not see this activity (though you can hear it when sounds are played!). @@ -129,6 +128,12 @@ If you want to watch the browser as it runs the test, rather than running headle USE_HEADLESS=no $(npm bin)/jest --runInBand test/integration/backpack.test.js ``` +_Note: If you are seeing failed tests related to `chromedriver` being incompatible with your version of Chrome, you may need to update `chromedriver` with:_ + +```bash +npm install chromedriver@{version} +``` + ## Troubleshooting ### Ignoring optional dependencies diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index a5fbf5bac..786108d5d 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -27,8 +27,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: $menu-bar-standard-font-size; font-weight: bold; - /* background-color: $motion-primary; */ - background-color: #774DCB; + background-color: $looks-tertiary; color: $ui-white; } @@ -53,7 +52,11 @@ .language-icon { height: 1.5rem; vertical-align: middle; - margin-inline-end: .125rem; + margin: 0 .125rem 0 0; +} + +[dir="rtl"] .language-icon { + margin: 0 0 0 .125rem; } .language-menu { @@ -62,8 +65,15 @@ } .language-label { - margin-inline-start: .25rem; - margin-inline-end: .5rem; + max-width: 7rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin: 0 .5rem 0 .25rem; +} + +[dir="rtl"] .language-label { + margin: 0 .25rem 0 .5rem; } .menu-bar-item { diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index b3cb50987..0043ae96b 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -82,7 +82,7 @@ import aboutIcon from './icon--about.svg'; import scratchLogo from './scratch-logo.svg'; import sharedMessages from '../../lib/shared-messages'; -import {DEFAULT_THEME, HIGH_CONTRAST_THEME} from '../../lib/themes/index.js'; +import {persistTheme} from '../../lib/themes/themePersistance'; const ariaMessages = defineMessages({ language: { @@ -267,15 +267,7 @@ class MenuBar extends React.Component { handleChangeTheme (theme) { this.props.onChangeTheme(theme); this.props.onRequestCloseTheme(); - - let systemPreferencesTheme = DEFAULT_THEME; - const prefersHighContrastQuery = '(prefers-contrast: more)'; - if (window.matchMedia(prefersHighContrastQuery).matches) { - systemPreferencesTheme = HIGH_CONTRAST_THEME; - } - - // Remove the cookie if it matches the system preferences. Otherwise store the theme in the cookie. - document.cookie = `scratchtheme=${systemPreferencesTheme === theme ? '' : theme}`; + persistTheme(theme); } restoreOptionMessage (deletedItem) { switch (deletedItem) { @@ -435,7 +427,6 @@ class MenuBar extends React.Component { )} {(this.props.canChangeTheme) && ( { const themeInfo = themeMap[props.theme]; @@ -46,6 +54,7 @@ const ThemeMenu = props => { onMouseUp={props.onRequestOpen} > {props.intl.formatMessage(messages.colorTheme)} @@ -80,6 +89,7 @@ const ThemeMenu = props => { ThemeMenu.propTypes = { className: PropTypes.string, + intl: intlShape.isRequired, isRtl: PropTypes.bool, onChange: PropTypes.func, onRequestClose: PropTypes.func, @@ -88,4 +98,4 @@ ThemeMenu.propTypes = { themeMenuOpen: PropTypes.bool }; -export default ThemeMenu; +export default injectIntl(ThemeMenu); diff --git a/src/components/menu/menu.css b/src/components/menu/menu.css index d671d1381..109e1ab5b 100644 --- a/src/components/menu/menu.css +++ b/src/components/menu/menu.css @@ -4,8 +4,7 @@ position: absolute; border: 1px solid $ui-black-transparent; border-radius: 0 0 8px 8px; - /* background-color: $motion-primary; */ - background-color: #774DCB; + background-color: $looks-tertiary; padding: 0; margin: 0; min-width: 186px; diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 14e9677c3..ae5e175e7 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -18,7 +18,7 @@ import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; import defineDynamicBlock from '../lib/define-dynamic-block'; -import {DEFAULT_THEME, getColorsForTheme, themes} from '../lib/themes'; +import {DEFAULT_THEME, getColorsForTheme, themeMap} from '../lib/themes'; import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../lib/themes/blockHelpers'; import {connect} from 'react-redux'; @@ -630,7 +630,7 @@ Blocks.propTypes = { collapse: PropTypes.bool }), stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, - theme: PropTypes.oneOf(themes), + theme: PropTypes.oneOf(Object.keys(themeMap)), toolboxXML: PropTypes.string, updateMetrics: PropTypes.func, updateToolboxState: PropTypes.func, diff --git a/src/css/colors.css b/src/css/colors.css index 6f38510e2..5f14fd307 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -19,6 +19,8 @@ $motion-tertiary: hsla(215, 60%, 50%, 1); /* #3373CC */ $motion-transparent: hsla(215, 100%, 65%, 0.35); /* 35% transparent version of motion-primary */ $motion-light-transparent: hsla(215, 100%, 65%, 0.15); /* 15% transparent version of motion-primary */ +$looks-tertiary: hsla(260, 54%, 54%, 1); /* #774DCB */ + $red-primary: hsla(20, 100%, 55%, 1); /* #FF661A */ $red-tertiary: hsla(20, 100%, 45%, 1); /* #E64D00 */ diff --git a/src/lib/system-preferences-hoc.jsx b/src/lib/system-preferences-hoc.jsx index 1f2cf6f91..c38cd9f37 100644 --- a/src/lib/system-preferences-hoc.jsx +++ b/src/lib/system-preferences-hoc.jsx @@ -1,34 +1,19 @@ import React from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; -import cookie from 'cookie'; -import {DEFAULT_THEME} from './themes'; import {setTheme} from '../reducers/theme'; +import {detectTheme} from './themes/themePersistance'; // Dark mode is not yet supported // const prefersDarkQuery = '(prefers-color-scheme: dark)'; const prefersHighContrastQuery = '(prefers-contrast: more)'; -const getTheme = () => { - if (window.matchMedia(prefersHighContrastQuery).matches) return 'high-contrast'; - - return DEFAULT_THEME; -}; - const systemPreferencesHOC = function (WrappedComponent) { class SystemPreferences extends React.Component { componentDidMount () { - // this.props.onSetTheme(getTheme()); - this.preferencesListener = () => { - const obj = cookie.parse(document.cookie) || {}; - const themeCookie = obj.scratchtheme; + this.preferencesListener = () => this.props.onSetTheme(detectTheme()); - // Only use system preferences if there is not a specified cookie. - if (themeCookie) return; - - this.props.onSetTheme(getTheme()); - } this.highContrastMatchMedia = window.matchMedia(prefersHighContrastQuery); this.highContrastMatchMedia.addEventListener('change', this.preferencesListener); } diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index 81151406f..000960e4a 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -8,9 +8,9 @@ import defaultColors from './default-colors'; import defaultIcon from './standard.svg'; import highContrastIcon from './high-contrast.svg'; -const DEFAULT_THEME = 'standard'; +const DEFAULT_THEME = 'default'; const HIGH_CONTRAST_THEME = 'high-contrast'; -const DARK_THEME = 'dark-mode'; +const DARK_THEME = 'dark'; const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors); @@ -59,15 +59,11 @@ const getColorsForTheme = theme => { return themeInfo.colors; }; -const themes = Object.keys(themeMap); - export { DEFAULT_THEME, DARK_THEME, HIGH_CONTRAST_THEME, defaultColors, getColorsForTheme, - messages, - themes, themeMap }; diff --git a/src/lib/themes/themePersistance.js b/src/lib/themes/themePersistance.js new file mode 100644 index 000000000..c94ff337a --- /dev/null +++ b/src/lib/themes/themePersistance.js @@ -0,0 +1,44 @@ +import cookie from 'cookie'; + +import {DEFAULT_THEME, HIGH_CONTRAST_THEME} from '.'; + +const PREFERS_HIGH_CONTRAST_QUERY = '(prefers-contrast: more)'; +const COOKIE_KEY = 'scratchtheme'; + +// Dark mode isn't enabled yet +const isValidTheme = theme => [DEFAULT_THEME, HIGH_CONTRAST_THEME].includes(theme); + +const systemPreferencesTheme = () => { + if (window.matchMedia && window.matchMedia(PREFERS_HIGH_CONTRAST_QUERY).matches) return HIGH_CONTRAST_THEME; + + return DEFAULT_THEME; +}; + +const detectTheme = () => { + const obj = cookie.parse(document.cookie) || {}; + const themeCookie = obj.scratchtheme; + + if (isValidTheme(themeCookie)) return themeCookie; + + // No cookie set. Fall back to system preferences + return systemPreferencesTheme(); +}; + +const persistTheme = theme => { + if (!isValidTheme(theme)) { + throw new Error(`Invalid theme: ${theme}`); + } + + if (systemPreferencesTheme() === theme) { + // Clear the cookie to represent using the system preferences + document.cookie = `${COOKIE_KEY}=`; + return; + } + + document.cookie = `${COOKIE_KEY}=${theme}`; +}; + +export { + detectTheme, + persistTheme +}; diff --git a/src/reducers/theme.js b/src/reducers/theme.js index 0c8664332..4330f94da 100644 --- a/src/reducers/theme.js +++ b/src/reducers/theme.js @@ -1,22 +1,7 @@ -import cookie from 'cookie'; - -import {DEFAULT_THEME, HIGH_CONTRAST_THEME} from '../lib/themes'; +import {detectTheme} from '../lib/themes/themePersistance'; const SET_THEME = 'scratch-gui/theme/SET_THEME'; -const detectTheme = () => { - const obj = cookie.parse(document.cookie) || {}; - const themeCookie = obj.scratchtheme; - - if ([DEFAULT_THEME, HIGH_CONTRAST_THEME].includes(themeCookie)) return themeCookie; - - // No cookie set. Fall back to system preferences - const prefersHighContrastQuery = '(prefers-contrast: more)'; - if (window.matchMedia(prefersHighContrastQuery).matches) return HIGH_CONTRAST_THEME; - - return DEFAULT_THEME; -}; - const initialState = { theme: detectTheme() }; @@ -40,19 +25,3 @@ export { initialState as themeInitialState, setTheme }; - - -const prefersDarkQuery = '(prefers-color-scheme: dark)'; -const prefersHighContrastQuery = '(prefers-contrast: more)'; - -const getTheme = () => { - const highContrast = window.matchMedia(prefersHighContrastQuery).matches; - - if (highContrast) return 'high-contrast'; - - const dark = window.matchMedia(prefersDarkQuery).matches; - - if (dark) return 'dark-mode'; - - return DEFAULT_THEME; -}; diff --git a/test/helpers/selenium-helper.js b/test/helpers/selenium-helper.js index 7ce274322..2e314e09c 100644 --- a/test/helpers/selenium-helper.js +++ b/test/helpers/selenium-helper.js @@ -48,6 +48,7 @@ class SeleniumHelper { reportedValue: '*[@class="blocklyDropDownContent"]', soundsTab: "*[@id='react-tabs-5']", spriteTile: '*[starts-with(@class,"react-contextmenu-wrapper")]', + menuBar: '*[contains(@class,"menu-bar_menu-bar_")]', monitors: '*[starts-with(@class,"stage_monitor-wrapper")]', contextMenu: '*[starts-with(@class,"react-contextmenu")]' }; diff --git a/test/integration/menu-bar.test.js b/test/integration/menu-bar.test.js index c83f1022d..e2e1853ec 100644 --- a/test/integration/menu-bar.test.js +++ b/test/integration/menu-bar.test.js @@ -97,4 +97,31 @@ describe('Menu bar settings', () => { .accept(); await findByText('project1-sprite'); }); + + test('Theme picker shows themes', async () => { + await loadUri(uri); + await clickXpath('//img[@alt="Color theme"]'); + + await findByText('Default', scope.menuBar); + await findByText('High Text Contrast', scope.menuBar); + }); + + test('Theme picker switches to high text contrast', async () => { + await loadUri(uri); + await clickXpath('//img[@alt="Color theme"]'); + await clickText('High Text Contrast', scope.menuBar); + + // There is a tiny delay for the color theme to be applied to the categories. + await driver.wait(async () => { + const motionCategoryDiv = await findByXpath( + '//div[contains(@class, "scratchCategoryMenuItem") and ' + + 'contains(@class, "scratchCategoryId-motion")]/*[1]'); + const color = await motionCategoryDiv.getCssValue('background-color'); + + // Documentation for getCssValue says it depends on how the browser + // returns the value. Locally I am seeing 'rgba(128, 181, 255, 1)', + // but this is a bit flexible just in case. + return /128,\s?181,\s?255/.test(color) || color.includes('80B5FF'); + }, 5000, 'Motion category color does not match high text contrast theme'); + }); }); diff --git a/test/unit/components/menu-bar.test.jsx b/test/unit/components/menu-bar.test.jsx index 3aaadb9de..5eff1514b 100644 --- a/test/unit/components/menu-bar.test.jsx +++ b/test/unit/components/menu-bar.test.jsx @@ -3,12 +3,12 @@ import {mountWithIntl} from '../../helpers/intl-helpers'; import MenuBar from '../../../src/components/menu-bar/menu-bar'; import {menuInitialState} from '../../../src/reducers/menus'; import {LoadingState} from '../../../src/reducers/project-state'; +import {DEFAULT_THEME} from '../../../src/lib/themes'; import configureStore from 'redux-mock-store'; import {Provider} from 'react-redux'; import VM from 'scratch-vm'; - describe('MenuBar Component', () => { const store = configureStore()({ locales: { @@ -20,6 +20,9 @@ describe('MenuBar Component', () => { projectState: { loadingState: LoadingState.NOT_LOADED }, + theme: { + theme: DEFAULT_THEME + }, vm: new VM() } }); diff --git a/test/unit/components/monitor.test.jsx b/test/unit/components/monitor.test.jsx index a60ca0923..0602f2453 100644 --- a/test/unit/components/monitor.test.jsx +++ b/test/unit/components/monitor.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import {shallow} from 'enzyme'; import DefaultMonitor from '../../../src/components/monitor/default-monitor'; import Monitor from '../../../src/components/monitor/monitor'; +import {DARK_THEME, DEFAULT_THEME} from '../../../src/lib/themes'; jest.mock('../../../src/lib/themes/default-colors'); jest.mock('../../../src/lib/themes/dark-mode'); @@ -21,7 +22,7 @@ describe('Monitor Component', () => { onDragEnd={noop} // eslint-disable-next-line react/jsx-no-bind onNextMode={noop} - theme="standard" + theme={DEFAULT_THEME} />); const defaultMonitor = wrapper.find(DefaultMonitor); @@ -44,7 +45,7 @@ describe('Monitor Component', () => { onDragEnd={noop} // eslint-disable-next-line react/jsx-no-bind onNextMode={noop} - theme="dark-mode" + theme={DARK_THEME} />); const defaultMonitor = wrapper.find(DefaultMonitor); diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index 3ff15dabc..575f7e886 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -1,5 +1,12 @@ -import {defaultColors, DEFAULT_THEME, getColorsForTheme} from '../../../src/lib/themes'; +import { + DARK_THEME, + defaultColors, + DEFAULT_THEME, + getColorsForTheme, + HIGH_CONTRAST_THEME +} from '../../../src/lib/themes'; import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../../../src/lib/themes/blockHelpers'; +import {detectTheme, persistTheme} from '../../../src/lib/themes/themePersistance'; jest.mock('../../../src/lib/themes/default-colors'); jest.mock('../../../src/lib/themes/dark-mode'); @@ -7,81 +14,119 @@ jest.mock('../../../src/lib/themes/dark-mode'); describe('themes', () => { let serializeToString; - beforeEach(() => { - serializeToString = jest.fn(() => 'mocked xml'); + describe('core functionality', () => { + test('provides the default theme colors', () => { + expect(defaultColors.motion.primary).toEqual('#111111'); + }); - global.XMLSerializer = () => ({ - serializeToString + test('returns the dark mode', () => { + const colors = getColorsForTheme(DARK_THEME); + + expect(colors.motion.primary).toEqual('#AAAAAA'); + }); + + test('uses default theme colors when not specified', () => { + const colors = getColorsForTheme(DARK_THEME); + + expect(colors.motion.secondary).toEqual('#222222'); }); }); - test('provides the default theme colors', () => { - expect(defaultColors.motion.primary).toEqual('#111111'); - }); + describe('block helpers', () => { + beforeEach(() => { + serializeToString = jest.fn(() => 'mocked xml'); - test('returns the dark mode', () => { - const colors = getColorsForTheme('dark-mode'); - - expect(colors.motion.primary).toEqual('#AAAAAA'); - }); - - test('uses default theme colors when not specified', () => { - const colors = getColorsForTheme('dark-mode'); - - expect(colors.motion.secondary).toEqual('#222222'); - }); - - test('updates extension blocks based on theme', () => { - const blockInfoJson = { - type: 'dummy_block', - colour: '#0FBD8C', - colourSecondary: '#0DA57A', - colourTertiary: '#0B8E69' - }; - - const updated = injectExtensionBlockColors(blockInfoJson, 'dark-mode'); - - expect(updated).toEqual({ - type: 'dummy_block', - colour: '#FFFFFF', - colourSecondary: '#EEEEEE', - colourTertiary: '#DDDDDD' + global.XMLSerializer = () => ({ + serializeToString + }); }); - // The original value was not modified - expect(blockInfoJson.colour).toBe('#0FBD8C'); - }); - test('bypasses updates if using the default theme', () => { - const blockInfoJson = { - type: 'dummy_block', - colour: '#0FBD8C', - colourSecondary: '#0DA57A', - colourTertiary: '#0B8E69' - }; + test('updates extension blocks based on theme', () => { + const blockInfoJson = { + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }; - const updated = injectExtensionBlockColors(blockInfoJson, DEFAULT_THEME); + const updated = injectExtensionBlockColors(blockInfoJson, DARK_THEME); - expect(updated).toEqual({ - type: 'dummy_block', - colour: '#0FBD8C', - colourSecondary: '#0DA57A', - colourTertiary: '#0B8E69' + expect(updated).toEqual({ + type: 'dummy_block', + colour: '#FFFFFF', + colourSecondary: '#EEEEEE', + colourTertiary: '#DDDDDD' + }); + // The original value was not modified + expect(blockInfoJson.colour).toBe('#0FBD8C'); + }); + + test('bypasses updates if using the default theme', () => { + const blockInfoJson = { + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }; + + const updated = injectExtensionBlockColors(blockInfoJson, DEFAULT_THEME); + + expect(updated).toEqual({ + type: 'dummy_block', + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }); + }); + + test('updates extension category based on theme', () => { + const dynamicBlockXML = [ + { + id: 'pen', + xml: '' + } + ]; + + injectExtensionCategoryColors(dynamicBlockXML, DARK_THEME); + + // XMLSerializer is not available outside the browser. + // Verify the mocked XMLSerializer.serializeToString is called with updated colors. + expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('colour')).toBe('#FFFFFF'); + expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('secondaryColour')).toBe('#DDDDDD'); }); }); - test('updates extension category based on theme', () => { - const dynamicBlockXML = [ - { - id: 'pen', - xml: '' - } - ]; + describe('theme persistance', () => { + test('returns the theme stored in a cookie', () => { + window.document.cookie = `scratchtheme=${HIGH_CONTRAST_THEME}`; - injectExtensionCategoryColors(dynamicBlockXML, 'dark-mode'); + const theme = detectTheme(); - // XMLSerializer is not available outside the browser. - // Verify the mocked XMLSerializer.serializeToString is called with updated colors. - expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('colour')).toBe('#FFFFFF'); - expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('secondaryColour')).toBe('#DDDDDD'); + expect(theme).toEqual(HIGH_CONTRAST_THEME); + }); + + test('returns the system theme when no cookie', () => { + window.document.cookie = 'scratchtheme='; + + const theme = detectTheme(); + + expect(theme).toEqual(DEFAULT_THEME); + }); + + test('persists theme to cookie', () => { + window.document.cookie = 'scratchtheme='; + + persistTheme(HIGH_CONTRAST_THEME); + + expect(window.document.cookie).toEqual(`scratchtheme=${HIGH_CONTRAST_THEME}`); + }); + + test('clears theme when matching system preferences', () => { + window.document.cookie = `scratchtheme=${HIGH_CONTRAST_THEME}`; + + persistTheme(DEFAULT_THEME); + + expect(window.document.cookie).toEqual('scratchtheme='); + }); }); }); From a77c0076fbdbf07af2242c490d280b620cdc5400 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Mon, 13 Mar 2023 14:14:19 -0400 Subject: [PATCH 11/21] chore: tweak menu styling --- README.md | 1 + src/components/menu-bar/check.svg | 3 +++ src/components/menu-bar/menu-bar.css | 8 +++++++- src/components/menu-bar/menu-bar.jsx | 28 ++++++++++++++------------ src/components/menu-bar/theme-menu.css | 13 ++++++++++++ src/components/menu-bar/theme-menu.jsx | 6 +++++- 6 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 src/components/menu-bar/check.svg diff --git a/README.md b/README.md index da45b9ce9..77aeb5f09 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ $(npm bin)/jest --runInBand test/unit/components/button.test.jsx ``` #### Running integration tests + Integration tests use a headless browser to manipulate the actual HTML and javascript that the repo produces. You will not see this activity (though you can hear it when sounds are played!). diff --git a/src/components/menu-bar/check.svg b/src/components/menu-bar/check.svg new file mode 100644 index 000000000..afe11c097 --- /dev/null +++ b/src/components/menu-bar/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index 786108d5d..f77d8a27e 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -115,6 +115,7 @@ } .file-group .menu-bar-item { + margin: 0 .25rem; padding: 0 0.75rem; } @@ -166,6 +167,7 @@ } .account-info-group .menu-bar-item { + margin: 0 .25rem; padding: 0 0.75rem; } @@ -175,7 +177,11 @@ } .help-icon { - margin-right: 0.35rem; + margin: 0 .25rem 0 0; +} + +[dir="rtl"] .help-icon { + margin: 0 0 0 .25rem; } .account-nav-menu, .mystuff-button { diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index 0043ae96b..29d9f2e89 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -434,6 +434,7 @@ class MenuBar extends React.Component { theme={this.props.theme} themeMenuOpen={this.props.themeMenuOpen} />)} + {(this.props.canManageFiles) && (
-
- - - - +
+
+ + + + +
-
{/* show the proper UI in the account menu, given whether the user is diff --git a/src/components/menu-bar/theme-menu.css b/src/components/menu-bar/theme-menu.css index 8bd2dad7e..7c79945c2 100644 --- a/src/components/menu-bar/theme-menu.css +++ b/src/components/menu-bar/theme-menu.css @@ -32,6 +32,19 @@ background-color: $ui-black-transparent; } +.check { + margin: 0 .25rem 0 0; + visibility: hidden; +} + +[dir="rtl"] .check { + margin: 0 0 0 .25rem; +} + +.selected { + visibility: visible; +} + @media only screen and (max-width: 1024px) { .theme-label { display: none; diff --git a/src/components/menu-bar/theme-menu.jsx b/src/components/menu-bar/theme-menu.jsx index 607cce902..afd58aa53 100644 --- a/src/components/menu-bar/theme-menu.jsx +++ b/src/components/menu-bar/theme-menu.jsx @@ -8,6 +8,7 @@ import {DEFAULT_THEME, HIGH_CONTRAST_THEME, themeMap} from '../../lib/themes'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import dropdownCaret from './dropdown-caret.svg'; +import check from './check.svg'; import styles from './theme-menu.css'; import menuBarStyles from './menu-bar.css'; @@ -27,9 +28,12 @@ const ThemeMenuItem = props => {
+
From 0dca3bfe28676158202044947a7f8f6896a7a06e Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 17 Mar 2023 10:28:59 -0400 Subject: [PATCH 12/21] feat: use high contrast icons --- src/components/gui/gui.jsx | 3 +- src/components/record-modal/record-modal.css | 3 +- src/containers/blocks.jsx | 6 +-- src/lib/themes/__mocks__/dark-mode.js | 7 +++- src/lib/themes/__mocks__/default-colors.js | 4 +- src/lib/themes/blockHelpers.js | 40 ++++++++++++++++--- src/lib/themes/dark-mode.js | 7 +++- src/lib/themes/default-colors.js | 4 +- src/lib/themes/high-contrast.js | 28 ++++++++++++- src/lib/themes/index.js | 22 +++++++--- .../comment-arrow-down.svg | 10 +++++ .../comment-arrow-up.svg | 10 +++++ .../blocks-media-high-contrast/delete-x.svg | 10 +++++ .../dropdown-arrow.svg | 1 + .../blocks-media-high-contrast/eyedropper.svg | 22 ++++++++++ .../icons/arrow_button.svg | 1 + static/blocks-media-high-contrast/repeat.svg | 13 ++++++ .../rotate-left.svg | 1 + .../rotate-right.svg | 1 + static/blocks-media-high-contrast/zoom-in.svg | 5 +++ .../blocks-media-high-contrast/zoom-out.svg | 5 +++ .../blocks-media-high-contrast/zoom-reset.svg | 6 +++ test/unit/util/themes.test.js | 8 ++-- webpack.config.js | 9 +++++ 24 files changed, 201 insertions(+), 25 deletions(-) create mode 100644 static/blocks-media-high-contrast/comment-arrow-down.svg create mode 100644 static/blocks-media-high-contrast/comment-arrow-up.svg create mode 100644 static/blocks-media-high-contrast/delete-x.svg create mode 100644 static/blocks-media-high-contrast/dropdown-arrow.svg create mode 100644 static/blocks-media-high-contrast/eyedropper.svg create mode 100644 static/blocks-media-high-contrast/icons/arrow_button.svg create mode 100644 static/blocks-media-high-contrast/repeat.svg create mode 100644 static/blocks-media-high-contrast/rotate-left.svg create mode 100644 static/blocks-media-high-contrast/rotate-right.svg create mode 100644 static/blocks-media-high-contrast/zoom-in.svg create mode 100644 static/blocks-media-high-contrast/zoom-out.svg create mode 100644 static/blocks-media-high-contrast/zoom-reset.svg diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 360c725b7..bb8585d1a 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -33,6 +33,7 @@ import TelemetryModal from '../telemetry-modal/telemetry-modal.jsx'; import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants'; import {resolveStageSize} from '../../lib/screen-utils'; +import {themeMap} from '../../lib/themes'; import styles from './gui.css'; import addExtensionIcon from './icon--extensions.svg'; @@ -308,7 +309,7 @@ const GUIComponent = props => { isVisible={blocksTabVisible} key={theme} options={{ - media: `${basePath}static/blocks-media/` + media: `${basePath}static/${themeMap[theme].blocksMediaFolder}/` }} stageSize={stageSize} theme={theme} diff --git a/src/components/record-modal/record-modal.css b/src/components/record-modal/record-modal.css index 41fc90a96..42f00649a 100644 --- a/src/components/record-modal/record-modal.css +++ b/src/components/record-modal/record-modal.css @@ -106,7 +106,8 @@ text-align: center; } -.record-button { +/* Increase specificity to make sure this wins out over normalize.css */ +svg.record-button { overflow: visible; } diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index ae5e175e7..f890f7984 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -19,7 +19,7 @@ import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; import defineDynamicBlock from '../lib/define-dynamic-block'; import {DEFAULT_THEME, getColorsForTheme, themeMap} from '../lib/themes'; -import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../lib/themes/blockHelpers'; +import {injectExtensionBlockTheme, injectExtensionCategoryTheme} from '../lib/themes/blockHelpers'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -349,7 +349,7 @@ class Blocks extends React.Component { const stageCostumes = stage.getCostumes(); const targetCostumes = target.getCostumes(); const targetSounds = target.getSounds(); - const dynamicBlocksXML = injectExtensionCategoryColors( + const dynamicBlocksXML = injectExtensionCategoryTheme( this.props.vm.runtime.getBlocksXML(target), this.props.theme ); @@ -436,7 +436,7 @@ class Blocks extends React.Component { if (blockInfo.info && blockInfo.info.isDynamic) { dynamicBlocksInfo.push(blockInfo); } else if (blockInfo.json) { - staticBlocksJson.push(injectExtensionBlockColors(blockInfo.json, this.props.theme)); + staticBlocksJson.push(injectExtensionBlockTheme(blockInfo.json, this.props.theme)); } // otherwise it's a non-block entry such as '---' }); diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/__mocks__/dark-mode.js index 793270dd6..b3bcd65fd 100644 --- a/src/lib/themes/__mocks__/dark-mode.js +++ b/src/lib/themes/__mocks__/dark-mode.js @@ -10,4 +10,9 @@ const blockColors = { text: '#BBBBBB' }; -export default blockColors; +const extensions = {}; + +export { + blockColors, + extensions +} diff --git a/src/lib/themes/__mocks__/default-colors.js b/src/lib/themes/__mocks__/default-colors.js index f5e7aa48c..d3b3bc94b 100644 --- a/src/lib/themes/__mocks__/default-colors.js +++ b/src/lib/themes/__mocks__/default-colors.js @@ -13,4 +13,6 @@ const blockColors = { workspace: '#555555' }; -export default blockColors; +export { + blockColors +}; diff --git a/src/lib/themes/blockHelpers.js b/src/lib/themes/blockHelpers.js index baf0cb0f9..72ef02237 100644 --- a/src/lib/themes/blockHelpers.js +++ b/src/lib/themes/blockHelpers.js @@ -1,4 +1,4 @@ -import {DEFAULT_THEME, getColorsForTheme} from '.'; +import {DEFAULT_THEME, getColorsForTheme, themeMap} from '.'; // scratch-blocks colours has a pen property that scratch-gui uses for all extensions const getExtensionColors = theme => getColorsForTheme(theme).pen; @@ -12,11 +12,12 @@ const getExtensionColors = theme => getColorsForTheme(theme).pen; * @param {string} theme - Theme name * @returns {Array.} Dynamic block XML updated with colors. */ -const injectExtensionCategoryColors = (dynamicBlockXML, theme) => { +const injectExtensionCategoryTheme = (dynamicBlockXML, theme) => { // Don't do any manipulation for the default theme if (theme === DEFAULT_THEME) return dynamicBlockXML; const extensionColors = getExtensionColors(theme); + const extensionIcons = themeMap[theme].extensions; const parser = new DOMParser(); const serializer = new XMLSerializer(); @@ -27,6 +28,10 @@ const injectExtensionCategoryColors = (dynamicBlockXML, theme) => { // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. dom.documentElement.setAttribute('secondaryColour', extensionColors.tertiary); + if (extensionIcons[extension.id] && extensionIcons[extension.id].menuIconURI) { + dom.documentElement.setAttribute('iconURI', extensionIcons[extension.id].menuIconURI); + } + return { ...extension, xml: serializer.serializeToString(dom) @@ -34,6 +39,29 @@ const injectExtensionCategoryColors = (dynamicBlockXML, theme) => { }); }; +const isScratchExtension = blockInfoJson => (blockInfoJson.extensions || []).includes('scratch_extension'); + +const injectBlockIcons = (blockInfoJson, theme) => { + if (!blockInfoJson.args0 || blockInfoJson.args0[0].type !== 'field_image') return blockInfoJson; + + const extensionIcons = themeMap[theme].extensions; + const extension = blockInfoJson.type.substring(0, blockInfoJson.type.indexOf('_')); + + if (!extensionIcons[extension] || !extensionIcons[extension].blockIconURI) return blockInfoJson; + + return { + ...blockInfoJson, + args0: blockInfoJson.args0.map((value, index) => { + if (index !== 0) return value; + + return { + ...value, + src: extensionIcons[extension].blockIconURI + }; + }) + }; +}; + /** * Applies extension color theme to static block json. * No changes are applied if called with the default theme, allowing extensions to provide their own colors. @@ -41,14 +69,14 @@ const injectExtensionCategoryColors = (dynamicBlockXML, theme) => { * @param {string} theme - Theme name * @returns {object} Block info json with updated colors. The original blockInfoJson is not modified. */ -const injectExtensionBlockColors = (blockInfoJson, theme) => { +const injectExtensionBlockTheme = (blockInfoJson, theme) => { // Don't do any manipulation for the default theme if (theme === DEFAULT_THEME) return blockInfoJson; const extensionColors = getExtensionColors(theme); return { - ...blockInfoJson, + ...injectBlockIcons(blockInfoJson, theme), colour: extensionColors.primary, colourSecondary: extensionColors.secondary, colourTertiary: extensionColors.tertiary, @@ -57,6 +85,6 @@ const injectExtensionBlockColors = (blockInfoJson, theme) => { }; export { - injectExtensionBlockColors, - injectExtensionCategoryColors + injectExtensionBlockTheme, + injectExtensionCategoryTheme }; diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark-mode.js index 6842c8b39..d1e865163 100644 --- a/src/lib/themes/dark-mode.js +++ b/src/lib/themes/dark-mode.js @@ -76,4 +76,9 @@ const blockColors = { menuHover: 'rgba(255, 255, 255, 0.3)' }; -export default blockColors; +const extensions = {}; + +export { + blockColors, + extensions +}; diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default-colors.js index 1b53026c5..3a754dca3 100644 --- a/src/lib/themes/default-colors.js +++ b/src/lib/themes/default-colors.js @@ -100,4 +100,6 @@ const blockColors = { menuHover: 'rgba(0, 0, 0, 0.2)' }; -export default blockColors; +export { + blockColors +}; diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js index e8f7c0121..16a09283f 100644 --- a/src/lib/themes/high-contrast.js +++ b/src/lib/themes/high-contrast.js @@ -80,4 +80,30 @@ const blockColors = { menuHover: 'rgba(255, 255, 255, 0.3)' }; -export default blockColors; +const extensions = { + music: { + // eslint-disable-next-line max-len + blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI2LjQzMzggMzBDMjkuNTEzIDMwIDMxLjYzNjYgMjguMTU2OSAzMS4xNzkgMjUuODc0QzI4LjI5OTQgMTMuMDQzNiAyNy40MjU2IDkuODUxMzQgMjcuOTQ1NCA5LjYwNTQ3QzI4LjMxMjUgOS40MzE4MyAyOS4zNzQ2IDEwLjcyNzYgMzAuOTE2MiAxMS4xMzU5QzM0LjY0MTYgMTIuMTEyNiA0MC4yNzQyIDYuNDA3NTEgMzQuNTY1NSA3LjI5MTg2QzMyLjMyMjQgNy42MzkgMzAuMTU4NyA2LjIzODM2IDI4LjQ4NzkgNS4xNTY3OUMyNS45MDcyIDMuNDg2MiAyNC41MDI0IDIuNTc2ODQgMjUuNzk3NCAxMC4wNDUxQzI2LjQzNDkgMTMuNzAzNSAyNi45Njk3IDE2LjMyMzEgMjcuMzQ3NSAxOC4xNzM5QzI3Ljk5NTcgMjEuMzQ5NCAyOC4xODE5IDIyLjI2MTYgMjcuNjMyNSAyMi4yNzQxQzI3LjMzMjEgMjIuMTUyIDI3LjAzMjcgMjIuMDU3OCAyNi42OTU3IDIxLjk3MjRDMjYuMDg4IDIxLjgyMjUgMjUuNDUxNiAyMS43MzgyIDI0LjgwNTMgMjEuNzM4MkMyMS43MjcxIDIxLjczODIgMTkuNjAzNiAyMy41ODkyIDIwLjA2MjEgMjUuODc0QzIwLjUxMDcgMjguMTU2OSAyMy4zNjQ2IDMwIDI2LjQzMzggMzBaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNOS40Mzg2MSAzNi4wMDAxQzEyLjUwNjUgMzYuMDAwMSAxNC42MzAyIDM0LjE0OCAxNC4xODE3IDMxLjg2NDJDMTEuMzAzMiAxOS4wMzM2IDEwLjQyOTkgMTUuODQxNiAxMC45NDk2IDE1LjU5NThDMTEuMzE2NiAxNS40MjIyIDEyLjM3ODMgMTYuNzE3NyAxMy45MTkgMTcuMTI2QzE3LjY0MjggMTguMTEzNiAyMy4yNzI5IDEyLjM5ODUgMTcuNTY2NyAxMy4yOTE4QzE1LjMyNDggMTMuNjM4NiAxMy4xNjIzIDEyLjIzODIgMTEuNDkyMiAxMS4xNTY5QzguOTEyMzcgOS40ODYzNiA3LjUwNzk3IDguNTc2OTggOC44MDI1MSAxNi4wNDUxQzkuNDQyOTQgMTkuNzI4NiA5Ljk3OTggMjIuMzU5NCAxMC4zNTggMjQuMjEyNUMxMC45OTEyIDI3LjMxNTQgMTEuMTc5NSAyOC4yMzg0IDEwLjY2NDQgMjguMjczMkM5Ljc4NDMyIDI3LjkyNTggOC44MTIzOSAyNy43MjkzIDcuODEwODIgMjcuNzI5M0M0LjczNCAyNy43MjkzIDIuNjExMzQgMjkuNTgwNCAzLjA1OTc4IDMxLjg2NDJDMy41MTgwOSAzNC4xNDggNi4zNzA2OSAzNi4wMDAxIDkuNDM4NjEgMzYuMDAwMVoiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=' + }, + pen: { + // eslint-disable-next-line max-len + blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04Ljc1MjkgMzQuNjAyNUw0LjUwMTkgMzYuMzgwNUw2LjI4NTkgMzIuMTQ0NUM3LjUwMzkgMjkuMjUzNSA5LjE5MjkgMjYuNzIyNSAxMS4zMTU5IDI0LjYwNzVMMzEuMDY2OSA0LjkyOTVDMzEuOTExOSA0LjA4NzUgMzMuNzE2OSA0LjUyMDUgMzUuMDk3OSA1Ljg5NjVDMzYuNDc4OSA3LjI3MjUgMzYuOTEzOSA5LjA3MDUgMzYuMDY3OSA5LjkxMjVMMTYuMzE3OSAyOS41OTA1QzE0LjE5NDkgMzEuNzA1NSAxMS42NTM5IDMzLjM4ODUgOC43NTI5IDM0LjYwMjVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOC43NTI5IDM0LjYwMjVMNC41MDE5IDM2LjM4MDVMNi4yODU5IDMyLjE0NDVDNy41MDM5IDI5LjI1MzUgOS4xOTI5IDI2LjcyMjUgMTEuMzE1OSAyNC42MDc1TDMxLjA2NjkgNC45Mjk1QzMxLjkxMTkgNC4wODc1IDMzLjcxNjkgNC41MjA1IDM1LjA5NzkgNS44OTY1QzM2LjQ3ODkgNy4yNzI1IDM2LjkxMzkgOS4wNzA1IDM2LjA2NzkgOS45MTI1TDE2LjMxNzkgMjkuNTkwNUMxNC4xOTQ5IDMxLjcwNTUgMTEuNjUzOSAzMy4zODg1IDguNzUyOSAzNC42MDI1IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjkuNDA5MiA2LjExMTNDMjkuNDA5MiA2LjExMTMgMjQuOTYwMiAzLjczMjMgMjEuMjA4MiAxMS44ODIzQzE5LjQ3NDIgMTUuNjQ4MyAxNi44NTgyIDEzLjQyODMgMTYuODU4MiAxMy40MjgzIiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTM2LjQyMDkgOC44MjUxQzM2LjQyMDkgOS4yODgxIDM2LjI3OTkgOS42OTgxIDM1Ljk4NzkgOS45ODkxTDI2LjY1MjkgMTkuMjkwMUMyNi45MzQ5IDE4Ljk5OTEgMjcuMDYzOSAxOC42MjIxIDI3LjA2MzkgMTguMTY5MUMyNy4wNjM5IDE3LjI5NjEgMjYuNTU1OSAxNi4yMDcxIDI1LjY1NjkgMTUuMzAyMUMyNC4yOTQ5IDEzLjk0NDEgMjIuNTA5OSAxMy41MDIxIDIxLjY1NDkgMTQuMzExMUwzMC45ODk5IDUuMDEwMUMzMS44MzM5IDQuMTY5MSAzMy42NDA5IDQuNjAwMSAzNS4wMjQ5IDUuOTY5MUMzNS45MjI5IDYuODc0MSAzNi40MjA5IDcuOTUyMSAzNi40MjA5IDguODI1MVoiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZD0iTTM2LjQyMDkgOC44MjUxQzM2LjQyMDkgOS4yODgxIDM2LjI3OTkgOS42OTgxIDM1Ljk4NzkgOS45ODkxTDI2LjY1MjkgMTkuMjkwMUMyNi45MzQ5IDE4Ljk5OTEgMjcuMDYzOSAxOC42MjIxIDI3LjA2MzkgMTguMTY5MUMyNy4wNjM5IDE3LjI5NjEgMjYuNTU1OSAxNi4yMDcxIDI1LjY1NjkgMTUuMzAyMUMyNC4yOTQ5IDEzLjk0NDEgMjIuNTA5OSAxMy41MDIxIDIxLjY1NDkgMTQuMzExMUwzMC45ODk5IDUuMDEwMUMzMS44MzM5IDQuMTY5MSAzMy42NDA5IDQuNjAwMSAzNS4wMjQ5IDUuOTY5MUMzNS45MjI5IDYuODc0MSAzNi40MjA5IDcuOTUyMSAzNi40MjA5IDguODI1MSIgc3Ryb2tlPSIjMEI4RTY5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC41MTQ2IDMzLjc3MzlDOS45NDE2IDM0LjA3NTkgOS4zNTc2IDM0LjM0NDkgOC43NTA2IDM0LjYwMzlMNC40OTk2IDM2LjM4MTlMNi4yODU2IDMyLjE0NjlDNi41NDM2IDMxLjU0MjkgNi44MTQ2IDMwLjk2MDkgNy4xMTg2IDMwLjM4OTlDNy44MDk2IDMwLjU3MjkgOC41Njc2IDMxLjAxNDkgOS4yMjc2IDMxLjY3MTlDOS44ODY2IDMyLjMyOTkgMTAuMzI5NiAzMy4wODM5IDEwLjUxNDYgMzMuNzczOVoiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZD0iTTEwLjUxNDYgMzMuNzczOUM5Ljk0MTYgMzQuMDc1OSA5LjM1NzYgMzQuMzQ0OSA4Ljc1MDYgMzQuNjAzOUw0LjQ5OTYgMzYuMzgxOUw2LjI4NTYgMzIuMTQ2OUM2LjU0MzYgMzEuNTQyOSA2LjgxNDYgMzAuOTYwOSA3LjExODYgMzAuMzg5OUM3LjgwOTYgMzAuNTcyOSA4LjU2NzYgMzEuMDE0OSA5LjIyNzYgMzEuNjcxOUM5Ljg4NjYgMzIuMzI5OSAxMC4zMjk2IDMzLjA4MzkgMTAuNTE0NiAzMy43NzM5IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8ZyBvcGFjaXR5PSIwLjE1Ij4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zNi40OTggOC43NDg1QzM2LjQ5OCA5LjIxMTUgMzYuMzU3IDkuNjIxNSAzNi4wNjUgOS45MTI1TDE2LjMyMyAyOS41OTI1QzE0LjE5MiAzMS43MDQ1IDExLjY1IDMzLjM4NTUgOC43NTEgMzQuNjAzNUw0LjUgMzYuMzgxNUw1LjQ3NCAzNC4wNjQ1TDcuMzk5IDMzLjI1NjVDMTAuMjk4IDMyLjAzODUgMTIuODQgMzAuMzU3NSAxNC45NzEgMjguMjQ1NUwzNC43MTMgOC41NjU1QzM1LjAwNSA4LjI3NDUgMzUuMTQ1IDcuODY0NSAzNS4xNDUgNy40MDE1QzM1LjE0NSA2Ljc1NDUgMzQuODc1IDYuMDAwNSAzNC4zNjYgNS4yNzg1QzM0LjYxNSA1LjQ1MDUgMzQuODY0IDUuNjU1NSAzNS4xMDIgNS44OTI1QzM2IDYuNzk3NSAzNi40OTggNy44NzU1IDM2LjQ5OCA4Ljc0ODVaIiBmaWxsPSIjMEI4RTY5Ii8+CjxwYXRoIGQ9Ik0zNi40OTggOC43NDg1QzM2LjQ5OCA5LjIxMTUgMzYuMzU3IDkuNjIxNSAzNi4wNjUgOS45MTI1TDE2LjMyMyAyOS41OTI1QzE0LjE5MiAzMS43MDQ1IDExLjY1IDMzLjM4NTUgOC43NTEgMzQuNjAzNUw0LjUgMzYuMzgxNUw1LjQ3NCAzNC4wNjQ1TDcuMzk5IDMzLjI1NjVDMTAuMjk4IDMyLjAzODUgMTIuODQgMzAuMzU3NSAxNC45NzEgMjguMjQ1NUwzNC43MTMgOC41NjU1QzM1LjAwNSA4LjI3NDUgMzUuMTQ1IDcuODY0NSAzNS4xNDUgNy40MDE1QzM1LjE0NSA2Ljc1NDUgMzQuODc1IDYuMDAwNSAzNC4zNjYgNS4yNzg1QzM0LjYxNSA1LjQ1MDUgMzQuODY0IDUuNjU1NSAzNS4xMDIgNS44OTI1QzM2IDYuNzk3NSAzNi40OTggNy44NzU1IDM2LjQ5OCA4Ljc0ODUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjwvZz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xOC40NTAyIDEyLjgzMUMxOC40NTAyIDEzLjMzIDE4LjA0NjIgMTMuNzM1IDE3LjU0NjIgMTMuNzM1QzE3LjA0NzIgMTMuNzM1IDE2LjY0MTIgMTMuMzMgMTYuNjQxMiAxMi44MzFDMTYuNjQxMiAxMi4zMzEgMTcuMDQ3MiAxMS45MjcgMTcuNTQ2MiAxMS45MjdDMTguMDQ2MiAxMS45MjcgMTguNDUwMiAxMi4zMzEgMTguNDUwMiAxMi44MzFaIiBmaWxsPSIjMEI4RTY5IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K' + }, + text2speech: { + // eslint-disable-next-line max-len + blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2IDIxLjY2OTNWMzQuMjgzNEMxNiAzNS44MTUxIDE0IDM2LjUzNTkgMTIuOCAzNS41NDQ4TDEwLjIgMzMuMzgyNEM5LjIgMzIuNTcxNSA4IDMyLjIxMTEgNi43IDMyLjIxMTFINi4zQzUgMzIuMjExMSA0IDMxLjMxMDEgNCAzMC4xMzg4VjI1LjkwNDFDNCAyNC43MzI4IDUgMjMuODMxOCA2LjMgMjMuODMxOEg2LjdDOCAyMy44MzE4IDkuMiAyMy4zODEzIDEwLjEgMjIuNjYwNEwxMi44IDIwLjQ5OEMxNCAxOS40MTY4IDE2IDIwLjIyNzcgMTYgMjEuNjY5M1oiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0yNCA0QzE5LjU4MTcgNCAxNiA3LjU4MTcyIDE2IDEyQzE2IDE1LjExNDcgMTcuNzc5OSAxNy44MTM2IDIwLjM3ODEgMTkuMTM1MUMyMC4yMDk1IDIwLjkwODcgMTkuNjU2NCAyMS42NjU1IDE5LjMwNDIgMjIuMTQ3M0MxOS4xMjY1IDIyLjM5MDQgMTkgMjIuNTYzNSAxOSAyMi43NjE5QzE5IDIzLjQyODYgMTkuNjY2NyAyMy40Mjg2IDE5LjY2NjcgMjMuNDI4NkMyMC42MTMyIDIzLjQyODYgMjMuNTgxMyAyMi4yNjIzIDI1LjQwOTcgMjBIMjhDMzIuNDE4MyAyMCAzNiAxNi40MTgzIDM2IDEyQzM2IDcuNTgxNzIgMzIuNDE4MyA0IDI4IDRIMjRaIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMEI4RTY5Ii8+Cjwvc3ZnPgo=' + }, + translate: { + // eslint-disable-next-line max-len + blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggNUM1Ljc5MDg2IDUgNCA2Ljc5MDg2IDQgOVYxN0M0IDE5LjIwOTEgNS43OTA4NiAyMSA4IDIxSDguMjUyNzdDOC4wMTY0OCAyMi4xNzI4IDcuNTkyODMgMjIuNzUyNCA3LjMwNDIzIDIzLjE0NzNDNy4xMjY1MSAyMy4zOTA0IDcgMjMuNTYzNSA3IDIzLjc2MTlDNyAyNC40Mjg2IDcuNjY2NjcgMjQuNDI4NiA3LjY2NjY3IDI0LjQyODZDOC42MTMyNCAyNC40Mjg2IDExLjU4MTMgMjMuMjYyMyAxMy40MDk3IDIxSDIwQzIyLjIwOTEgMjEgMjQgMTkuMjA5MSAyNCAxN1Y5QzI0IDYuNzkwODYgMjIuMjA5MSA1IDIwIDVIOFoiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwQjhFNjkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy40MjE4IDcuOTEzMDJDMTMuNTUyMSA3LjgwMTUzIDEzLjcyMzkgNy43NSAxMy45MDY3IDcuNzVDMTQuMDkyOCA3Ljc1IDE0LjI2NyA3LjgwMDM2IDE0LjM5OTQgNy45MTE1QzE0LjUzNTQgOC4wMjU2MSAxNC42MDY3IDguMTg3NDggMTQuNjA2NyA4LjM2MjE2VjkuNTI4NDFIMTguNzAzNkMxOC44Nzk5IDkuNTI4NDEgMTkuMDI3MiA5LjYxMDE3IDE5LjEyMjkgOS43Mzk1N0MxOS4yMTM0IDkuODYyMDEgMTkuMjUgMTAuMDE0NiAxOS4yNSAxMC4xNjI2QzE5LjI1IDEwLjMxMzYgMTkuMjE0NyAxMC40NzAyIDE5LjEyOTMgMTAuNTk3MUMxOS4wMzk1IDEwLjczMDYgMTguODkyNyAxMC44Mjk3IDE4LjcwMzYgMTAuODI5N0gxNy4xMDEyQzE2LjY2MDggMTIuMjgwMiAxNS45NDgyIDEzLjYzODEgMTQuOTI3IDE0Ljc4MTlDMTUuMDYwNiAxNC44OTE2IDE1LjE4OSAxNC45OTIxIDE1LjMyMTMgMTUuMDgyNkwxNS4zMjc5IDE1LjA4NzFDMTYuMzIxOSAxNS44MTQgMTcuNTQ3MiAxNi4zODUgMTguNzM1MSAxNi43NjI5QzE5LjAzOTUgMTYuODU5IDE5LjIyODIgMTcuMTAzNiAxOS4yMjgyIDE3LjQwNzNDMTkuMjI4MiAxNy40OTM2IDE5LjIxMTIgMTcuNTc3IDE5LjE4MDYgMTcuNjU2MUMxOS4xMzIgMTcuODA1NSAxOS4wNTU1IDE3LjkzNDUgMTguOTQyOCAxOC4wMjY1QzE4LjgyNTEgMTguMTIyNiAxOC42ODc0IDE4LjE2MjQgMTguNTUgMTguMTYyNEMxOC40NDgxIDE4LjE2MjQgMTguMzQyNyAxOC4xNDc3IDE4LjIyNiAxOC4xMDQ3QzE2Ljc5OTcgMTcuNjM1OSAxNS41Nzc3IDE3LjAyMDEgMTQuNDMzIDE2LjEyMjVMMTQuNDMwOSAxNi4xMjA4QzE0LjQxNDIgMTYuMTA3NCAxNC4zOTc1IDE2LjA5NDEgMTQuMzgwOSAxNi4wODA3QzE0LjIzOTEgMTUuOTY3MyAxNC4wOTc1IDE1Ljg1MzggMTMuOTU5MiAxNS43MzM1QzEzLjgwNTUgMTUuODY1NyAxMy42NTIyIDE1Ljk4OTggMTMuNDg2NyAxNi4xMTQxQzEyLjI3NzIgMTcuMDMyMyAxMS4xMjMgMTcuNjAyNyA5Ljc0NTUgMTguMTEzOUw5LjczODE3IDE4LjExNjZDOS42NDc4MSAxOC4xNDcgOS41NTQ4NiAxOC4xNjI0IDkuNDYxMTIgMTguMTYyNEM5LjMxOTgyIDE4LjE2MjQgOS4xNzk5MSAxOC4xMjY3IDkuMDU4NTggMTguMDQyQzguOTM4MDggMTcuOTU3OSA4Ljg1MjUxIDE3LjgzNzIgOC43OTk0NyAxNy42OTQ1QzguNzY3MjYgMTcuNjEzIDguNzUgMTcuNTI3OCA4Ljc1IDE3LjQ0MDJDOC43NSAxNy4xMjA3IDguOTc2NDEgMTYuODkyMSA5LjIzODg0IDE2Ljc5MDNMOS4yNDk0OSAxNi43ODYyQzEwLjQ2NzkgMTYuMzc2NSAxMS42MTQgMTUuODUxNSAxMi44Nzk3IDE0Ljg2NkMxMi45MTQ5IDE0LjgzNSAxMi45NTA5IDE0LjgwNTUgMTIuOTg1NCAxNC43Nzc5QzExLjk4NDUgMTMuNjMzNyAxMS4yODM0IDEyLjI2ODkgMTAuODQ0MiAxMC44Mjk3SDkuMzA3NUM5LjExNjIyIDEwLjgyOTcgOC45NjY4NyAxMC43MzM2IDguODc1ODMgMTAuNTk0NkM4Ljc5MTQ0IDEwLjQ2NTcgOC43NjA4OCAxMC4zMDg3IDguNzYwODggMTAuMTYyNkM4Ljc2MDg4IDEwLjAxOTkgOC43OTIzNSA5Ljg2Njc0IDguODgyMSA5Ljc0MjEyQzguOTc5MDYgOS42MDc0OCA5LjEyOTEgOS41Mjg0MSA5LjMwNzUgOS41Mjg0MUgxMy4yMTc5VjguMzYyMTZDMTMuMjE3OSA4LjE4ODA1IDEzLjI4ODYgOC4wMjcgMTMuNDIxOCA3LjkxMzAyWk0xMi4yMzg2IDEwLjgyOTdDMTIuNjA5MSAxMS45MzE2IDEzLjIwNTYgMTIuOTU4OCAxMy45NjI2IDEzLjgzNzdDMTQuNzI1NyAxMi45NTkzIDE1LjMwNTEgMTEuOTM4OSAxNS42NDkzIDEwLjgyOTdIMTIuMjM4NloiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMiAxNkMzNC4yMDkxIDE2IDM2IDE3Ljc5MDkgMzYgMjBWMjhDMzYgMzAuMjA5MSAzNC4yMDkxIDMyIDMyIDMySDMxLjc0NzJDMzEuOTgzNSAzMy4xNzI4IDMyLjQwNzIgMzMuNzUyNCAzMi42OTU4IDM0LjE0NzNDMzIuODczNSAzNC4zOTA0IDMzIDM0LjU2MzUgMzMgMzQuNzYxOUMzMyAzNS40Mjg2IDMyLjMzMzMgMzUuNDI4NiAzMi4zMzMzIDM1LjQyODZDMzEuMzg2OCAzNS40Mjg2IDI4LjQxODcgMzQuMjYyMyAyNi41OTAzIDMySDIwQzE3Ljc5MDkgMzIgMTYgMzAuMjA5MSAxNiAyOFYyMEMxNiAxNy43OTA5IDE3Ljc5MDkgMTYgMjAgMTZIMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzEuNzQ3MiAzMkwzMS4yNTcxIDMyLjA5ODhMMzEuMTM2NCAzMS41SDMxLjc0NzJWMzJaTTMyLjY5NTggMzQuMTQ3M0wzMy4wOTk0IDMzLjg1MjJIMzMuMDk5NEwzMi42OTU4IDM0LjE0NzNaTTI2LjU5MDMgMzJWMzEuNUgyNi44MjkxTDI2Ljk3OTIgMzEuNjg1N0wyNi41OTAzIDMyWk0zNS41IDIwQzM1LjUgMTguMDY3IDMzLjkzMyAxNi41IDMyIDE2LjVWMTUuNUMzNC40ODUzIDE1LjUgMzYuNSAxNy41MTQ3IDM2LjUgMjBIMzUuNVpNMzUuNSAyOFYyMEgzNi41VjI4SDM1LjVaTTMyIDMxLjVDMzMuOTMzIDMxLjUgMzUuNSAyOS45MzMgMzUuNSAyOEgzNi41QzM2LjUgMzAuNDg1MyAzNC40ODUzIDMyLjUgMzIgMzIuNVYzMS41Wk0zMS43NDcyIDMxLjVIMzJWMzIuNUgzMS43NDcyVjMxLjVaTTMyLjI5MjEgMzQuNDQyM0MzMS45ODM4IDM0LjAyMDYgMzEuNTEzOCAzMy4zNzI3IDMxLjI1NzEgMzIuMDk4OEwzMi4yMzc0IDMxLjkwMTJDMzIuNDUzMyAzMi45NzI5IDMyLjgzMDUgMzMuNDg0MyAzMy4wOTk0IDMzLjg1MjJMMzIuMjkyMSAzNC40NDIzWk0zMi41IDM0Ljc2MTlDMzIuNSAzNC43NzU1IDMyLjUwNjEgMzQuNzY2NiAzMi40NzU4IDM0LjcxMTNDMzIuNDQxIDM0LjY0NzUgMzIuMzg2OSAzNC41NzIxIDMyLjI5MjEgMzQuNDQyM0wzMy4wOTk0IDMzLjg1MjJDMzMuMTgyMyAzMy45NjU2IDMzLjI4MDQgMzQuMDk4MyAzMy4zNTMyIDM0LjIzMTVDMzMuNDMwNyAzNC4zNzMxIDMzLjUgMzQuNTUgMzMuNSAzNC43NjE5SDMyLjVaTTMyLjMzMzMgMzUuNDI4NkMzMi4zMzMzIDM0LjkyODYgMzIuMzMzMSAzNC45Mjg2IDMyLjMzMjkgMzQuOTI4NkMzMi4zMzI4IDM0LjkyODYgMzIuMzMyNSAzNC45Mjg2IDMyLjMzMjQgMzQuOTI4NkMzMi4zMzIxIDM0LjkyODYgMzIuMzMxOCAzNC45Mjg2IDMyLjMzMTUgMzQuOTI4NkMzMi4zMzA5IDM0LjkyODYgMzIuMzMwNCAzNC45Mjg2IDMyLjMyOTggMzQuOTI4NkMzMi4zMjg4IDM0LjkyODYgMzIuMzI3OSAzNC45Mjg2IDMyLjMyNzIgMzQuOTI4NkMzMi4zMjU3IDM0LjkyODYgMzIuMzI0NyAzNC45Mjg3IDMyLjMyNDIgMzQuOTI4N0MzMi4zMjMxIDM0LjkyODcgMzIuMzI0IDM0LjkyODcgMzIuMzI2NCAzNC45Mjg1QzMyLjMzMTMgMzQuOTI4MiAzMi4zNDE2IDM0LjkyNzIgMzIuMzU1MyAzNC45MjVDMzIuMzg1MSAzNC45MiAzMi40MTcxIDM0LjkxMSAzMi40NDMxIDM0Ljg5OEMzMi40NjcxIDM0Ljg4NiAzMi40NzU5IDM0Ljg3NTkgMzIuNDc5OCAzNC44N0MzMi40ODE2IDM0Ljg2NzIgMzIuNSAzNC44NDExIDMyLjUgMzQuNzYxOUgzMy41QzMzLjUgMzUuMDE2MSAzMy40MzUgMzUuMjM5OSAzMy4zMTE5IDM1LjQyNDdDMzMuMTkwOCAzNS42MDYzIDMzLjAzMjkgMzUuNzIxMSAzMi44OTAzIDM1Ljc5MjVDMzIuNzQ5NiAzNS44NjI4IDMyLjYxNDkgMzUuODk1NSAzMi41MTk3IDM1LjkxMTRDMzIuNDcwOSAzNS45MTk1IDMyLjQyOTEgMzUuOTIzNyAzMi4zOTc2IDM1LjkyNkMzMi4zODE4IDM1LjkyNzEgMzIuMzY4MyAzNS45Mjc4IDMyLjM1NzUgMzUuOTI4MUMzMi4zNTIxIDM1LjkyODMgMzIuMzQ3MyAzNS45Mjg0IDMyLjM0MzMgMzUuOTI4NUMzMi4zNDEzIDM1LjkyODUgMzIuMzM5NCAzNS45Mjg1IDMyLjMzNzggMzUuOTI4NkMzMi4zMzcgMzUuOTI4NiAzMi4zMzYyIDM1LjkyODYgMzIuMzM1NCAzNS45Mjg2QzMyLjMzNTEgMzUuOTI4NiAzMi4zMzQ3IDM1LjkyODYgMzIuMzM0MyAzNS45Mjg2QzMyLjMzNDIgMzUuOTI4NiAzMi4zMzM5IDM1LjkyODYgMzIuMzMzOCAzNS45Mjg2QzMyLjMzMzYgMzUuOTI4NiAzMi4zMzMzIDM1LjkyODYgMzIuMzMzMyAzNS40Mjg2Wk0yNi45NzkyIDMxLjY4NTdDMjcuODM5NyAzMi43NTA0IDI4Ljk3OTEgMzMuNTY4MyAzMC4wMjU4IDM0LjEyMDRDMzAuNTQ3NSAzNC4zOTU2IDMxLjAzODYgMzQuNjAwNyAzMS40NSAzNC43MzZDMzEuODcyNyAzNC44NzUgMzIuMTc2IDM0LjkyODYgMzIuMzMzMyAzNC45Mjg2VjM1LjkyODZDMzIuMDE3MyAzNS45Mjg2IDMxLjU5NDcgMzUuODM2MyAzMS4xMzc2IDM1LjY4NkMzMC42NjkgMzUuNTMxOSAzMC4xMjY0IDM1LjMwNDEgMjkuNTU5MiAzNS4wMDQ5QzI4LjQyODEgMzQuNDA4MyAyNy4xNjkzIDMzLjUxMTkgMjYuMjAxNSAzMi4zMTQzTDI2Ljk3OTIgMzEuNjg1N1pNMjAgMzEuNUgyNi41OTAzVjMyLjVIMjBWMzEuNVpNMTYuNSAyOEMxNi41IDI5LjkzMyAxOC4wNjcgMzEuNSAyMCAzMS41VjMyLjVDMTcuNTE0NyAzMi41IDE1LjUgMzAuNDg1MyAxNS41IDI4SDE2LjVaTTE2LjUgMjBWMjhIMTUuNVYyMEgxNi41Wk0yMCAxNi41QzE4LjA2NyAxNi41IDE2LjUgMTguMDY3IDE2LjUgMjBIMTUuNUMxNS41IDE3LjUxNDcgMTcuNTE0NyAxNS41IDIwIDE1LjVWMTYuNVpNMzIgMTYuNUgyMFYxNS41SDMyVjE2LjVaIiBmaWxsPSIjMEI4RTY5Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjQuOTIyNiAyNS4wMjAxSDI3LjA0M0wyNi4wMTE1IDIyLjAxNzJIMjUuOTg4NUwyNC45MjI2IDI1LjAyMDFaTTI1LjA5NDYgMjBIMjYuOTM5OEwzMCAyOC4xODM0SDI4LjEzMThMMjcuNTEyOSAyNi4zNjFIMjQuNDUyN0wyMy44MTA5IDI4LjE4MzRIMjJMMjUuMDk0NiAyMFoiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMzMuMDMzIDEyLjg3NjNDMzIuODMzNSAxMy4wNzEzIDMyLjU2MzUgMTMuMTgxMyAzMi4yODMgMTMuMTgxM0MzMi4wMDM0IDEzLjE4MTMgMzEuNzMzNCAxMy4wNzEzIDMxLjUzMjkgMTIuODc2M0wyOC44Nzg0IDEwLjIxNjJDMjguNTczNCA5LjkxMTIyIDI4LjQ4MzQgOS40NTYyMSAyOC42NDg0IDkuMDYxMkMyOC44MTM0IDguNjY2MTkgMjkuMTkzNCA4LjQxMTE5IDI5LjYyMzQgOC40MTExOUgzMC41Nzg0QzMwLjU1MzQgOC4xODExOSAzMC40ODg0IDcuOTIxMTggMzAuMzc4NCA3LjY0NjE4QzMwLjM0MzkgNy41NjYxNyAzMC4zMDg0IDcuNDg2MTcgMzAuMjY4NCA3LjQwNjE3QzMwLjIxMzQgNy4zMTYxNyAzMC4yMTg5IDcuMjkxMTcgMzAuMTQzNCA3LjE4MTE3QzMwLjAyMzQgNy4wMDExNiAyOS45MTM0IDYuODY2MTYgMjkuNzgyOSA2LjcxMTE2QzI5LjUxODQgNi40MjYxNSAyOS4xOTg0IDYuMTgxMTUgMjguODU4NCA2LjAwMTE1QzI4LjUxMzQgNS44MjExNCAyOC4xNTM0IDUuNzExMTQgMjcuODMzNCA1LjY1MTE0QzI3LjUxODQgNS41OTYxNCAyNy4yMjg0IDUuNTkxMTQgMjcuMDU4NCA1LjU5MTE0QzI2Ljk3MzQgNS41ODYxNCAyNi44NjM0IDUuNjA2MTQgMjYuODA4NCA1LjYxMTE0QzI2Ljc0ODQgNS42MTYxNCAyNi43MTM0IDUuNjIxMTQgMjYuNzEzNCA1LjYyMTE0QzI2LjM1ODMgNS42NTYxNCAyNi4wMzgzIDUuMzk2MTQgMjYuMDAzMyA1LjA0MTEzQzI1Ljk3MzMgNC43NDExMiAyNi4xNDgzIDQuNDcxMTIgMjYuNDEzMyA0LjM3MTEyQzI2LjQxMzMgNC4zNzExMiAyNi40NDgzIDQuMzU2MTIgMjYuNTAzMyA0LjMzNjEyQzI2LjU2ODMgNC4zMTYxMiAyNi42Mjg0IDQuMjgxMTIgMjYuNzY4NCA0LjI0MTEyQzI3LjA0ODQgNC4xNTYxMSAyNy40MDM0IDQuMDY2MTEgMjcuODY4NCA0LjAyMTExQzI4LjMyODQgMy45ODExMSAyOC44ODM0IDMuOTkxMTEgMjkuNDY4OSA0LjExMTExQzMwLjA1MzQgNC4yMzYxMiAzMC42Njg0IDQuNDcxMTIgMzEuMjM4NCA0LjgxMTEzQzMxLjUwODQgNC45ODYxMyAzMS43OTg0IDUuMTkxMTMgMzIuMDIzNCA1LjM5MTE0QzMyLjEyMzQgNS40NjYxNCAzMi4yOTM1IDUuNjM2MTQgMzIuMzkzNSA1Ljc0MTE0QzMyLjUwODUgNS44NjExNCAzMi42MTM1IDUuOTgxMTUgMzIuNzE5IDYuMTA2MTVDMzMuMTIzNSA2LjYwNjE2IDMzLjQyMzUgNy4xNDYxNyAzMy42MTg1IDcuNjM2MThDMzMuNzMzNSA3LjkxNjE4IDMzLjgxMzUgOC4xODExOSAzMy44NzM1IDguNDExMTlIMzQuOTQzNUMzNS4zNzM1IDguNDExMTkgMzUuNzUzNSA4LjY2NjE5IDM1LjkxODUgOS4wNjEyQzM2LjA4MzUgOS40NTYyMSAzNS45OTM1IDkuOTExMjIgMzUuNjg4NSAxMC4yMTYyTDMzLjAzMyAxMi44NzYzWiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02Ljk2NzA0IDI3LjEyNUM3LjE2NjU0IDI2LjkzIDcuNDM2NTQgMjYuODIgNy43MTcwNSAyNi44MkM3Ljk5NjU1IDI2LjgyIDguMjY2NTYgMjYuOTMgOC40NjcwNiAyNy4xMjVMMTEuMTIxNiAyOS43ODUxQzExLjQyNjYgMzAuMDkwMSAxMS41MTY2IDMwLjU0NTEgMTEuMzUxNiAzMC45NDAxQzExLjE4NjYgMzEuMzM1MSAxMC44MDY2IDMxLjU5MDEgMTAuMzc2NiAzMS41OTAxSDkuNDIxNThDOS40NDY1OCAzMS44MjAxIDkuNTExNTggMzIuMDgwMSA5LjYyMTU4IDMyLjM1NTFDOS42NTYwOCAzMi40MzUxIDkuNjkxNTggMzIuNTE1MSA5LjczMTU4IDMyLjU5NTFDOS43ODY1OSAzMi42ODUxIDkuNzgxMDkgMzIuNzEwMSA5Ljg1NjU5IDMyLjgyMDFDOS45NzY1OSAzMy4wMDAxIDEwLjA4NjYgMzMuMTM1MSAxMC4yMTcxIDMzLjI5MDFDMTAuNDgxNiAzMy41NzUxIDEwLjgwMTYgMzMuODIwMSAxMS4xNDE2IDM0LjAwMDFDMTEuNDg2NiAzNC4xODAxIDExLjg0NjYgMzQuMjkwMSAxMi4xNjY2IDM0LjM1MDFDMTIuNDgxNiAzNC40MDUxIDEyLjc3MTYgMzQuNDEwMSAxMi45NDE2IDM0LjQxMDFDMTMuMDI2NiAzNC40MTUxIDEzLjEzNjYgMzQuMzk1MSAxMy4xOTE2IDM0LjM5MDFDMTMuMjUxNiAzNC4zODUxIDEzLjI4NjYgMzQuMzgwMSAxMy4yODY2IDM0LjM4MDFDMTMuNjQxNyAzNC4zNDUxIDEzLjk2MTcgMzQuNjA1MSAxMy45OTY3IDM0Ljk2MDJDMTQuMDI2NyAzNS4yNjAyIDEzLjg1MTcgMzUuNTMwMiAxMy41ODY3IDM1LjYzMDJDMTMuNTg2NyAzNS42MzAyIDEzLjU1MTcgMzUuNjQ1MiAxMy40OTY3IDM1LjY2NTJDMTMuNDMxNyAzNS42ODUyIDEzLjM3MTYgMzUuNzIwMiAxMy4yMzE2IDM1Ljc2MDJDMTIuOTUxNiAzNS44NDUyIDEyLjU5NjYgMzUuOTM1MiAxMi4xMzE2IDM1Ljk4MDJDMTEuNjcxNiAzNi4wMjAyIDExLjExNjYgMzYuMDEwMiAxMC41MzExIDM1Ljg5MDJDOS45NDY1OSAzNS43NjUyIDkuMzMxNTggMzUuNTMwMiA4Ljc2MTU3IDM1LjE5MDJDOC40OTE1NiAzNS4wMTUyIDguMjAxNTYgMzQuODEwMSA3Ljk3NjU1IDM0LjYxMDFDNy44NzY1NSAzNC41MzUxIDcuNzA2NTUgMzQuMzY1MSA3LjYwNjU1IDM0LjI2MDFDNy40OTE1NCAzNC4xNDAxIDcuMzg2NTQgMzQuMDIwMSA3LjI4MTA0IDMzLjg5NTFDNi44NzY1MyAzMy4zOTUxIDYuNTc2NTMgMzIuODU1MSA2LjM4MTUzIDMyLjM2NTFDNi4yNjY1MiAzMi4wODUxIDYuMTg2NTIgMzEuODIwMSA2LjEyNjUyIDMxLjU5MDFINS4wNTY1QzQuNjI2NDkgMzEuNTkwMSA0LjI0NjQ5IDMxLjMzNTEgNC4wODE0OCAzMC45NDAxQzMuOTE2NDggMzAuNTQ1MSA0LjAwNjQ4IDMwLjA5MDEgNC4zMTE0OSAyOS43ODUxTDYuOTY3MDQgMjcuMTI1WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==' + }, + videoSensing: { + // eslint-disable-next-line max-len + blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBvcGFjaXR5PSIwLjI1IiBjeD0iMzIiIGN5PSIyNiIgcj0iNCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxjaXJjbGUgb3BhY2l0eT0iMC41IiBjeD0iMzIiIGN5PSIyMiIgcj0iNCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxjaXJjbGUgb3BhY2l0eT0iMC43NSIgY3g9IjMyIiBjeT0iMTgiIHI9IjQiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIzMiIgY3k9IjE0IiByPSI0IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMEI4RTY5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTE3IDE3LjVMMjIuNCAxNEMyMi45IDEzLjcgMjMuNSAxMy44IDIzLjggMTQuM0MyMy45IDE0LjUgMjQgMTQuNyAyNCAxNC44VjI1LjFDMjQgMjUuNyAyMy41IDI2LjEgMjMgMjYuMUMyMi44IDI2LjEgMjIuNiAyNiAyMi41IDI1LjlMMTcgMjIuNlYyNEMxNyAyNi4yIDE1LjIgMjguMSAxMyAyOC4xSDQuMUMxLjggMjggMCAyNi4yIDAgMjRWMTYuMUMwIDEzLjggMS44IDEyIDQuMSAxMkgxM0MxNS4yIDEyIDE3IDEzLjggMTcgMTYuMVYxNy41WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==' + } +}; + +export { + blockColors, + extensions +}; diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index 000960e4a..d77a7a6f3 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -1,9 +1,15 @@ import defaultsDeep from 'lodash.defaultsdeep'; import {defineMessages} from 'react-intl'; -import darkMode from './dark-mode'; -import highContrast from './high-contrast'; -import defaultColors from './default-colors'; +import { + blockColors as darkModeBlockColors, + extensions as darkModeExtensions +} from './dark-mode'; +import { + blockColors as highContrastBlockColors, + extensions as highContrastExtensions +} from './high-contrast'; +import {blockColors as defaultColors} from './default-colors'; import defaultIcon from './standard.svg'; import highContrastIcon from './high-contrast.svg'; @@ -34,16 +40,22 @@ const messages = defineMessages({ const themeMap = { [DEFAULT_THEME]: { + blocksMediaFolder: 'blocks-media', colors: defaultColors, + extensions: {}, label: messages[DEFAULT_THEME], icon: defaultIcon }, [DARK_THEME]: { - colors: mergeWithDefaults(darkMode), + blocksMediaFolder: 'blocks-media', + colors: mergeWithDefaults(darkModeBlockColors), + extensions: darkModeExtensions, label: messages[DARK_THEME] }, [HIGH_CONTRAST_THEME]: { - colors: mergeWithDefaults(highContrast), + blocksMediaFolder: 'blocks-media-high-contrast', + colors: mergeWithDefaults(highContrastBlockColors), + extensions: highContrastExtensions, label: messages[HIGH_CONTRAST_THEME], icon: highContrastIcon } diff --git a/static/blocks-media-high-contrast/comment-arrow-down.svg b/static/blocks-media-high-contrast/comment-arrow-down.svg new file mode 100644 index 000000000..9753f40b0 --- /dev/null +++ b/static/blocks-media-high-contrast/comment-arrow-down.svg @@ -0,0 +1,10 @@ + + + + dropdown-caret-up + Created with Sketch. + + + + + diff --git a/static/blocks-media-high-contrast/comment-arrow-up.svg b/static/blocks-media-high-contrast/comment-arrow-up.svg new file mode 100644 index 000000000..a118883f4 --- /dev/null +++ b/static/blocks-media-high-contrast/comment-arrow-up.svg @@ -0,0 +1,10 @@ + + + + dropdown-caret-down + Created with Sketch. + + + + + diff --git a/static/blocks-media-high-contrast/delete-x.svg b/static/blocks-media-high-contrast/delete-x.svg new file mode 100644 index 000000000..46d898a9a --- /dev/null +++ b/static/blocks-media-high-contrast/delete-x.svg @@ -0,0 +1,10 @@ + + + + delete-x + Created with Sketch. + + + + + diff --git a/static/blocks-media-high-contrast/dropdown-arrow.svg b/static/blocks-media-high-contrast/dropdown-arrow.svg new file mode 100644 index 000000000..585e5cc4f --- /dev/null +++ b/static/blocks-media-high-contrast/dropdown-arrow.svg @@ -0,0 +1 @@ +dropdown-arrow \ No newline at end of file diff --git a/static/blocks-media-high-contrast/eyedropper.svg b/static/blocks-media-high-contrast/eyedropper.svg new file mode 100644 index 000000000..c7a7cfc9a --- /dev/null +++ b/static/blocks-media-high-contrast/eyedropper.svg @@ -0,0 +1,22 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/blocks-media-high-contrast/icons/arrow_button.svg b/static/blocks-media-high-contrast/icons/arrow_button.svg new file mode 100644 index 000000000..5fa8ffee2 --- /dev/null +++ b/static/blocks-media-high-contrast/icons/arrow_button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blocks-media-high-contrast/repeat.svg b/static/blocks-media-high-contrast/repeat.svg new file mode 100644 index 000000000..70d02b909 --- /dev/null +++ b/static/blocks-media-high-contrast/repeat.svg @@ -0,0 +1,13 @@ + + + + +repeat + + diff --git a/static/blocks-media-high-contrast/rotate-left.svg b/static/blocks-media-high-contrast/rotate-left.svg new file mode 100644 index 000000000..eb120afa4 --- /dev/null +++ b/static/blocks-media-high-contrast/rotate-left.svg @@ -0,0 +1 @@ +rotate-clockwise \ No newline at end of file diff --git a/static/blocks-media-high-contrast/rotate-right.svg b/static/blocks-media-high-contrast/rotate-right.svg new file mode 100644 index 000000000..960c00bbe --- /dev/null +++ b/static/blocks-media-high-contrast/rotate-right.svg @@ -0,0 +1 @@ +rotate-counter-clockwise \ No newline at end of file diff --git a/static/blocks-media-high-contrast/zoom-in.svg b/static/blocks-media-high-contrast/zoom-in.svg new file mode 100644 index 000000000..6395eb2d4 --- /dev/null +++ b/static/blocks-media-high-contrast/zoom-in.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/blocks-media-high-contrast/zoom-out.svg b/static/blocks-media-high-contrast/zoom-out.svg new file mode 100644 index 000000000..3dc33cd6e --- /dev/null +++ b/static/blocks-media-high-contrast/zoom-out.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/blocks-media-high-contrast/zoom-reset.svg b/static/blocks-media-high-contrast/zoom-reset.svg new file mode 100644 index 000000000..2e358b0d7 --- /dev/null +++ b/static/blocks-media-high-contrast/zoom-reset.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index 575f7e886..d12139e20 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -5,7 +5,7 @@ import { getColorsForTheme, HIGH_CONTRAST_THEME } from '../../../src/lib/themes'; -import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../../../src/lib/themes/blockHelpers'; +import {injectExtensionBlockTheme, injectExtensionCategoryTheme} from '../../../src/lib/themes/blockHelpers'; import {detectTheme, persistTheme} from '../../../src/lib/themes/themePersistance'; jest.mock('../../../src/lib/themes/default-colors'); @@ -49,7 +49,7 @@ describe('themes', () => { colourTertiary: '#0B8E69' }; - const updated = injectExtensionBlockColors(blockInfoJson, DARK_THEME); + const updated = injectExtensionBlockTheme(blockInfoJson, DARK_THEME); expect(updated).toEqual({ type: 'dummy_block', @@ -69,7 +69,7 @@ describe('themes', () => { colourTertiary: '#0B8E69' }; - const updated = injectExtensionBlockColors(blockInfoJson, DEFAULT_THEME); + const updated = injectExtensionBlockTheme(blockInfoJson, DEFAULT_THEME); expect(updated).toEqual({ type: 'dummy_block', @@ -87,7 +87,7 @@ describe('themes', () => { } ]; - injectExtensionCategoryColors(dynamicBlockXML, DARK_THEME); + injectExtensionCategoryTheme(dynamicBlockXML, DARK_THEME); // XMLSerializer is not available outside the browser. // Verify the mocked XMLSerializer.serializeToString is called with updated colors. diff --git a/webpack.config.js b/webpack.config.js index 168faa396..a59eb263d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -171,6 +171,15 @@ module.exports = [ { from: 'node_modules/scratch-blocks/media', to: 'static/blocks-media' + }, + { + from: 'node_modules/scratch-blocks/media', + to: 'static/blocks-media-high-contrast' + }, + { + from: 'static/blocks-media-high-contrast', + to: 'static/blocks-media-high-contrast', + force: true } ] }), From ee1deb86a85da8101bf339be5b29ab2efa1ffcf1 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 17 Mar 2023 12:32:12 -0400 Subject: [PATCH 13/21] test: add unit tests for updating block icons --- src/lib/themes/__mocks__/dark-mode.js | 8 ++++-- src/lib/themes/blockHelpers.js | 30 +++++++++++++++++------ test/unit/util/themes.test.js | 35 ++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/__mocks__/dark-mode.js index b3bcd65fd..6d1b817db 100644 --- a/src/lib/themes/__mocks__/dark-mode.js +++ b/src/lib/themes/__mocks__/dark-mode.js @@ -10,9 +10,13 @@ const blockColors = { text: '#BBBBBB' }; -const extensions = {}; +const extensions = { + pen: { + blockIconURI: 'darkPenIcon' + } +}; export { blockColors, extensions -} +}; diff --git a/src/lib/themes/blockHelpers.js b/src/lib/themes/blockHelpers.js index 72ef02237..b201241ee 100644 --- a/src/lib/themes/blockHelpers.js +++ b/src/lib/themes/blockHelpers.js @@ -1,5 +1,17 @@ import {DEFAULT_THEME, getColorsForTheme, themeMap} from '.'; +const getBlockIconURI = extensionIcons => { + if (!extensionIcons) return null; + + return extensionIcons.blockIconURI || extensionIcons.menuIconURI; +}; + +const getCategoryIconURI = extensionIcons => { + if (!extensionIcons) return null; + + return extensionIcons.menuIconURI || extensionIcons.blockIconURI; +}; + // scratch-blocks colours has a pen property that scratch-gui uses for all extensions const getExtensionColors = theme => getColorsForTheme(theme).pen; @@ -28,8 +40,9 @@ const injectExtensionCategoryTheme = (dynamicBlockXML, theme) => { // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. dom.documentElement.setAttribute('secondaryColour', extensionColors.tertiary); - if (extensionIcons[extension.id] && extensionIcons[extension.id].menuIconURI) { - dom.documentElement.setAttribute('iconURI', extensionIcons[extension.id].menuIconURI); + const categoryIconURI = getCategoryIconURI(extensionIcons[extension.id]); + if (categoryIconURI) { + dom.documentElement.setAttribute('iconURI', categoryIconURI); } return { @@ -39,15 +52,16 @@ const injectExtensionCategoryTheme = (dynamicBlockXML, theme) => { }); }; -const isScratchExtension = blockInfoJson => (blockInfoJson.extensions || []).includes('scratch_extension'); - const injectBlockIcons = (blockInfoJson, theme) => { - if (!blockInfoJson.args0 || blockInfoJson.args0[0].type !== 'field_image') return blockInfoJson; + // Block icons are the first element of `args0` + if (!blockInfoJson.args0 || blockInfoJson.args0.length < 1 || + blockInfoJson.args0[0].type !== 'field_image') return blockInfoJson; const extensionIcons = themeMap[theme].extensions; - const extension = blockInfoJson.type.substring(0, blockInfoJson.type.indexOf('_')); + const extensionId = blockInfoJson.type.substring(0, blockInfoJson.type.indexOf('_')); + const blockIconURI = getBlockIconURI(extensionIcons[extensionId]); - if (!extensionIcons[extension] || !extensionIcons[extension].blockIconURI) return blockInfoJson; + if (!blockIconURI) return blockInfoJson; return { ...blockInfoJson, @@ -56,7 +70,7 @@ const injectBlockIcons = (blockInfoJson, theme) => { return { ...value, - src: extensionIcons[extension].blockIconURI + src: blockIconURI }; }) }; diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index d12139e20..b074c4abc 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -41,7 +41,7 @@ describe('themes', () => { }); }); - test('updates extension blocks based on theme', () => { + test('updates extension block colors based on theme', () => { const blockInfoJson = { type: 'dummy_block', colour: '#0FBD8C', @@ -61,6 +61,38 @@ describe('themes', () => { expect(blockInfoJson.colour).toBe('#0FBD8C'); }); + test('updates extension block icon based on theme', () => { + const blockInfoJson = { + type: 'pen_block', + args0: [ + { + type: 'field_image', + src: 'original' + } + ], + colour: '#0FBD8C', + colourSecondary: '#0DA57A', + colourTertiary: '#0B8E69' + }; + + const updated = injectExtensionBlockTheme(blockInfoJson, DARK_THEME); + + expect(updated).toEqual({ + type: 'pen_block', + args0: [ + { + type: 'field_image', + src: 'darkPenIcon' + } + ], + colour: '#FFFFFF', + colourSecondary: '#EEEEEE', + colourTertiary: '#DDDDDD' + }); + // The original value was not modified + expect(blockInfoJson.args0[0].src).toBe('original'); + }); + test('bypasses updates if using the default theme', () => { const blockInfoJson = { type: 'dummy_block', @@ -93,6 +125,7 @@ describe('themes', () => { // Verify the mocked XMLSerializer.serializeToString is called with updated colors. expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('colour')).toBe('#FFFFFF'); expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('secondaryColour')).toBe('#DDDDDD'); + expect(serializeToString.mock.calls[0][0].documentElement.getAttribute('iconURI')).toBe('darkPenIcon'); }); }); From f8acdaea4eee99b6d335f145d4ae7b9fea724cc6 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Mon, 27 Mar 2023 11:54:55 -0400 Subject: [PATCH 14/21] chore: update how theme images are stored and served --- package-lock.json | 462 ++++++++++-------- package.json | 3 +- .../dark-mode.js => dark/__mocks__/index.js} | 0 .../themes/{dark-mode.js => dark/index.js} | 0 .../__mocks__/index.js} | 0 .../themes/{standard.svg => default/icon.svg} | 0 .../{default-colors.js => default/index.js} | 0 src/lib/themes/high-contrast.js | 109 ----- .../blocks-media}/comment-arrow-down.svg | 0 .../blocks-media}/comment-arrow-up.svg | 0 .../high-contrast/blocks-media}/delete-x.svg | 0 .../blocks-media}/dropdown-arrow.svg | 0 .../blocks-media}/eyedropper.svg | 0 .../blocks-media}/icons/arrow_button.svg | 0 .../high-contrast/blocks-media}/repeat.svg | 0 .../blocks-media}/rotate-left.svg | 0 .../blocks-media}/rotate-right.svg | 0 .../high-contrast/blocks-media}/zoom-in.svg | 0 .../high-contrast/blocks-media}/zoom-out.svg | 0 .../blocks-media}/zoom-reset.svg | 0 .../high-contrast/extensions/musicIcon.svg | 4 + .../high-contrast/extensions/penIcon.svg | 14 + .../extensions/text2speechIcon.svg | 4 + .../extensions/translateIcon.svg | 9 + .../extensions/videoSensingIcon.svg | 7 + .../icon.svg} | 0 src/lib/themes/high-contrast/index.js | 110 +++++ src/lib/themes/index.js | 8 +- test/unit/components/monitor.test.jsx | 4 +- test/unit/util/themes.test.js | 4 +- webpack.config.js | 8 +- 31 files changed, 410 insertions(+), 336 deletions(-) rename src/lib/themes/{__mocks__/dark-mode.js => dark/__mocks__/index.js} (100%) rename src/lib/themes/{dark-mode.js => dark/index.js} (100%) rename src/lib/themes/{__mocks__/default-colors.js => default/__mocks__/index.js} (100%) rename src/lib/themes/{standard.svg => default/icon.svg} (100%) rename src/lib/themes/{default-colors.js => default/index.js} (100%) delete mode 100644 src/lib/themes/high-contrast.js rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/comment-arrow-down.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/comment-arrow-up.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/delete-x.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/dropdown-arrow.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/eyedropper.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/icons/arrow_button.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/repeat.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/rotate-left.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/rotate-right.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/zoom-in.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/zoom-out.svg (100%) rename {static/blocks-media-high-contrast => src/lib/themes/high-contrast/blocks-media}/zoom-reset.svg (100%) create mode 100644 src/lib/themes/high-contrast/extensions/musicIcon.svg create mode 100644 src/lib/themes/high-contrast/extensions/penIcon.svg create mode 100644 src/lib/themes/high-contrast/extensions/text2speechIcon.svg create mode 100644 src/lib/themes/high-contrast/extensions/translateIcon.svg create mode 100644 src/lib/themes/high-contrast/extensions/videoSensingIcon.svg rename src/lib/themes/{high-contrast.svg => high-contrast/icon.svg} (100%) create mode 100644 src/lib/themes/high-contrast/index.js diff --git a/package-lock.json b/package-lock.json index 3283671b4..6f558c723 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,12 +15,11 @@ "bowser": "1.9.4", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", - "cookie": "^0.5.0", + "cookie": "0.5.0", "copy-webpack-plugin": "6.4.1", "core-js": "2.5.7", "css-loader": "^1.0.0", "es6-object-assign": "1.1.0", - "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", "immutable": "3.8.2", @@ -95,6 +94,7 @@ "eslint-plugin-import": "2.23.4", "eslint-plugin-jest": "22.17.0", "eslint-plugin-react": "7.24.0", + "file-loader": "6.2.0", "gh-pages": "3.2.3", "html-webpack-plugin": "3.2.0", "husky": "8.0.1", @@ -109,6 +109,7 @@ "selenium-webdriver": "3.6.0", "semantic-release": "19.0.5", "uglifyjs-webpack-plugin": "1.3.0", + "url-loader": "4.1.1", "web-audio-test-api": "0.5.2", "webpack": "4.46.0", "webpack-cli": "3.3.12", @@ -4869,12 +4870,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@commitlint/config-validator/node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -7386,10 +7381,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", - "dev": true + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -7810,14 +7804,18 @@ } }, "node_modules/ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ajv-errors": { @@ -7829,9 +7827,9 @@ } }, "node_modules/ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "peerDependencies": { "ajv": "^6.9.1" } @@ -8537,37 +8535,6 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, - "node_modules/babel-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/babel-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/babel-loader/node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -8577,12 +8544,6 @@ "node": ">= 4" } }, - "node_modules/babel-loader/node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "node_modules/babel-loader/node_modules/find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", @@ -10163,15 +10124,6 @@ "node": ">= 0.6" } }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/compression": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", @@ -10483,34 +10435,6 @@ "webpack": "^4.37.0 || ^5.0.0" } }, - "node_modules/copy-webpack-plugin/node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" - }, - "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/copy-webpack-plugin/node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -10574,11 +10498,6 @@ "node": ">= 4" } }, - "node_modules/copy-webpack-plugin/node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, "node_modules/copy-webpack-plugin/node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -13768,9 +13687,9 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.6", @@ -13969,18 +13888,64 @@ } }, "node_modules/file-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", - "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, "dependencies": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" }, "engines": { - "node": ">= 6.9.0 < 7.0.0 || >= 8.9.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/file-type": { @@ -16634,7 +16599,7 @@ "version": "1.3.7", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "devOptional": true + "dev": true }, "node_modules/inquirer": { "version": "6.5.2", @@ -19828,19 +19793,19 @@ } }, "node_modules/mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.40.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -29809,6 +29774,74 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/url-parse": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", @@ -34820,12 +34853,6 @@ "uri-js": "^4.2.2" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -36695,10 +36722,9 @@ } }, "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", - "dev": true + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "@types/json5": { "version": "0.0.29", @@ -37073,11 +37099,11 @@ } }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -37090,9 +37116,9 @@ "requires": {} }, "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "requires": {} }, "ansi-colors": { @@ -37642,43 +37668,12 @@ "schema-utils": "^2.6.5" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", @@ -39018,14 +39013,6 @@ "dev": true, "requires": { "mime-db": ">= 1.43.0 < 2" - }, - "dependencies": { - "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", - "dev": true - } } }, "compression": { @@ -39280,28 +39267,6 @@ "webpack-sources": "^1.4.3" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -39350,11 +39315,6 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -41890,9 +41850,9 @@ } }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.6", @@ -42058,12 +42018,43 @@ } }, "file-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", - "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, "file-type": { @@ -44158,7 +44149,7 @@ "version": "1.3.7", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "devOptional": true + "dev": true }, "inquirer": { "version": "6.5.2", @@ -46744,16 +46735,16 @@ "dev": true }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -54483,6 +54474,47 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, "url-parse": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", diff --git a/package.json b/package.json index ef7070619..37d4f8d6f 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "core-js": "2.5.7", "css-loader": "^1.0.0", "es6-object-assign": "1.1.0", - "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", "immutable": "3.8.2", @@ -122,6 +121,7 @@ "eslint-plugin-import": "2.23.4", "eslint-plugin-jest": "22.17.0", "eslint-plugin-react": "7.24.0", + "file-loader": "6.2.0", "gh-pages": "3.2.3", "html-webpack-plugin": "3.2.0", "husky": "8.0.1", @@ -136,6 +136,7 @@ "selenium-webdriver": "3.6.0", "semantic-release": "19.0.5", "uglifyjs-webpack-plugin": "1.3.0", + "url-loader": "4.1.1", "web-audio-test-api": "0.5.2", "webpack": "4.46.0", "webpack-cli": "3.3.12", diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/dark/__mocks__/index.js similarity index 100% rename from src/lib/themes/__mocks__/dark-mode.js rename to src/lib/themes/dark/__mocks__/index.js diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark/index.js similarity index 100% rename from src/lib/themes/dark-mode.js rename to src/lib/themes/dark/index.js diff --git a/src/lib/themes/__mocks__/default-colors.js b/src/lib/themes/default/__mocks__/index.js similarity index 100% rename from src/lib/themes/__mocks__/default-colors.js rename to src/lib/themes/default/__mocks__/index.js diff --git a/src/lib/themes/standard.svg b/src/lib/themes/default/icon.svg similarity index 100% rename from src/lib/themes/standard.svg rename to src/lib/themes/default/icon.svg diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default/index.js similarity index 100% rename from src/lib/themes/default-colors.js rename to src/lib/themes/default/index.js diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js deleted file mode 100644 index 16a09283f..000000000 --- a/src/lib/themes/high-contrast.js +++ /dev/null @@ -1,109 +0,0 @@ -const blockColors = { - motion: { - primary: '#80B5FF', - secondary: '#B3D2FF', - tertiary: '#3373CC', - quaternary: '#CCE1FF' - }, - looks: { - primary: '#CCB3FF', - secondary: '#DDCCFF', - tertiary: '#774DCB', - quaternary: '#EEE5FF' - }, - sounds: { - primary: '#E19DE1', - secondary: '#FFB3FF', - tertiary: '#BD42BD', - quaternary: '#FFCCFF' - - }, - control: { - primary: '#FFBE4C', - secondary: '#FFDA99', - tertiary: '#CF8B17', - quaternary: '#FFE3B3' - }, - event: { - primary: '#FFD966', - secondary: '#FFECB3', - tertiary: '#CC9900', - quaternary: '#FFF2CC' - }, - sensing: { - primary: '#85C4E0', - secondary: '#AED8EA', - tertiary: '#2E8EB8', - quaternary: '#C2E2F0' - }, - pen: { - primary: '#13ECAF', - secondary: '#75F0CD', - tertiary: '#0B8E69', - quaternary: '#A3F5DE' - }, - operators: { - primary: '#7ECE7E', - secondary: '#B5E3B5', - tertiary: '#389438', - quaternary: '#DAF1DA' - }, - data: { - primary: '#FFA54C', - secondary: '#FFCC99', - tertiary: '#DB6E00', - quaternary: '#FFE5CC' - }, - // This is not a new category, but rather for differentiation - // between lists and scalar variables. - data_lists: { - primary: '#FF9966', - secondary: '#FFCAB0', // I don't think this is used, b/c we don't have any droppable fields in list blocks - tertiary: '#E64D00', - quaternary: '#FFDDCC' - }, - more: { - primary: '#FF99AA', - secondary: '#FFCCD5', - tertiary: '#FF3355', - quaternary: '#FFE5EA' - }, - text: '#000000', - textFieldText: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] - toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75) - // The color that the category menu label (e.g. 'motion', 'looks', etc.) changes to on hover - toolboxHover: '#3373CC', - insertionMarker: '#000000', - insertionMarkerOpacity: 0.2, - fieldShadow: 'rgba(255, 255, 255, 0.3)', - dragShadowOpacity: 0.6, - menuHover: 'rgba(255, 255, 255, 0.3)' -}; - -const extensions = { - music: { - // eslint-disable-next-line max-len - blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI2LjQzMzggMzBDMjkuNTEzIDMwIDMxLjYzNjYgMjguMTU2OSAzMS4xNzkgMjUuODc0QzI4LjI5OTQgMTMuMDQzNiAyNy40MjU2IDkuODUxMzQgMjcuOTQ1NCA5LjYwNTQ3QzI4LjMxMjUgOS40MzE4MyAyOS4zNzQ2IDEwLjcyNzYgMzAuOTE2MiAxMS4xMzU5QzM0LjY0MTYgMTIuMTEyNiA0MC4yNzQyIDYuNDA3NTEgMzQuNTY1NSA3LjI5MTg2QzMyLjMyMjQgNy42MzkgMzAuMTU4NyA2LjIzODM2IDI4LjQ4NzkgNS4xNTY3OUMyNS45MDcyIDMuNDg2MiAyNC41MDI0IDIuNTc2ODQgMjUuNzk3NCAxMC4wNDUxQzI2LjQzNDkgMTMuNzAzNSAyNi45Njk3IDE2LjMyMzEgMjcuMzQ3NSAxOC4xNzM5QzI3Ljk5NTcgMjEuMzQ5NCAyOC4xODE5IDIyLjI2MTYgMjcuNjMyNSAyMi4yNzQxQzI3LjMzMjEgMjIuMTUyIDI3LjAzMjcgMjIuMDU3OCAyNi42OTU3IDIxLjk3MjRDMjYuMDg4IDIxLjgyMjUgMjUuNDUxNiAyMS43MzgyIDI0LjgwNTMgMjEuNzM4MkMyMS43MjcxIDIxLjczODIgMTkuNjAzNiAyMy41ODkyIDIwLjA2MjEgMjUuODc0QzIwLjUxMDcgMjguMTU2OSAyMy4zNjQ2IDMwIDI2LjQzMzggMzBaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNOS40Mzg2MSAzNi4wMDAxQzEyLjUwNjUgMzYuMDAwMSAxNC42MzAyIDM0LjE0OCAxNC4xODE3IDMxLjg2NDJDMTEuMzAzMiAxOS4wMzM2IDEwLjQyOTkgMTUuODQxNiAxMC45NDk2IDE1LjU5NThDMTEuMzE2NiAxNS40MjIyIDEyLjM3ODMgMTYuNzE3NyAxMy45MTkgMTcuMTI2QzE3LjY0MjggMTguMTEzNiAyMy4yNzI5IDEyLjM5ODUgMTcuNTY2NyAxMy4yOTE4QzE1LjMyNDggMTMuNjM4NiAxMy4xNjIzIDEyLjIzODIgMTEuNDkyMiAxMS4xNTY5QzguOTEyMzcgOS40ODYzNiA3LjUwNzk3IDguNTc2OTggOC44MDI1MSAxNi4wNDUxQzkuNDQyOTQgMTkuNzI4NiA5Ljk3OTggMjIuMzU5NCAxMC4zNTggMjQuMjEyNUMxMC45OTEyIDI3LjMxNTQgMTEuMTc5NSAyOC4yMzg0IDEwLjY2NDQgMjguMjczMkM5Ljc4NDMyIDI3LjkyNTggOC44MTIzOSAyNy43MjkzIDcuODEwODIgMjcuNzI5M0M0LjczNCAyNy43MjkzIDIuNjExMzQgMjkuNTgwNCAzLjA1OTc4IDMxLjg2NDJDMy41MTgwOSAzNC4xNDggNi4zNzA2OSAzNi4wMDAxIDkuNDM4NjEgMzYuMDAwMVoiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=' - }, - pen: { - // eslint-disable-next-line max-len - blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04Ljc1MjkgMzQuNjAyNUw0LjUwMTkgMzYuMzgwNUw2LjI4NTkgMzIuMTQ0NUM3LjUwMzkgMjkuMjUzNSA5LjE5MjkgMjYuNzIyNSAxMS4zMTU5IDI0LjYwNzVMMzEuMDY2OSA0LjkyOTVDMzEuOTExOSA0LjA4NzUgMzMuNzE2OSA0LjUyMDUgMzUuMDk3OSA1Ljg5NjVDMzYuNDc4OSA3LjI3MjUgMzYuOTEzOSA5LjA3MDUgMzYuMDY3OSA5LjkxMjVMMTYuMzE3OSAyOS41OTA1QzE0LjE5NDkgMzEuNzA1NSAxMS42NTM5IDMzLjM4ODUgOC43NTI5IDM0LjYwMjVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOC43NTI5IDM0LjYwMjVMNC41MDE5IDM2LjM4MDVMNi4yODU5IDMyLjE0NDVDNy41MDM5IDI5LjI1MzUgOS4xOTI5IDI2LjcyMjUgMTEuMzE1OSAyNC42MDc1TDMxLjA2NjkgNC45Mjk1QzMxLjkxMTkgNC4wODc1IDMzLjcxNjkgNC41MjA1IDM1LjA5NzkgNS44OTY1QzM2LjQ3ODkgNy4yNzI1IDM2LjkxMzkgOS4wNzA1IDM2LjA2NzkgOS45MTI1TDE2LjMxNzkgMjkuNTkwNUMxNC4xOTQ5IDMxLjcwNTUgMTEuNjUzOSAzMy4zODg1IDguNzUyOSAzNC42MDI1IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjkuNDA5MiA2LjExMTNDMjkuNDA5MiA2LjExMTMgMjQuOTYwMiAzLjczMjMgMjEuMjA4MiAxMS44ODIzQzE5LjQ3NDIgMTUuNjQ4MyAxNi44NTgyIDEzLjQyODMgMTYuODU4MiAxMy40MjgzIiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTM2LjQyMDkgOC44MjUxQzM2LjQyMDkgOS4yODgxIDM2LjI3OTkgOS42OTgxIDM1Ljk4NzkgOS45ODkxTDI2LjY1MjkgMTkuMjkwMUMyNi45MzQ5IDE4Ljk5OTEgMjcuMDYzOSAxOC42MjIxIDI3LjA2MzkgMTguMTY5MUMyNy4wNjM5IDE3LjI5NjEgMjYuNTU1OSAxNi4yMDcxIDI1LjY1NjkgMTUuMzAyMUMyNC4yOTQ5IDEzLjk0NDEgMjIuNTA5OSAxMy41MDIxIDIxLjY1NDkgMTQuMzExMUwzMC45ODk5IDUuMDEwMUMzMS44MzM5IDQuMTY5MSAzMy42NDA5IDQuNjAwMSAzNS4wMjQ5IDUuOTY5MUMzNS45MjI5IDYuODc0MSAzNi40MjA5IDcuOTUyMSAzNi40MjA5IDguODI1MVoiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZD0iTTM2LjQyMDkgOC44MjUxQzM2LjQyMDkgOS4yODgxIDM2LjI3OTkgOS42OTgxIDM1Ljk4NzkgOS45ODkxTDI2LjY1MjkgMTkuMjkwMUMyNi45MzQ5IDE4Ljk5OTEgMjcuMDYzOSAxOC42MjIxIDI3LjA2MzkgMTguMTY5MUMyNy4wNjM5IDE3LjI5NjEgMjYuNTU1OSAxNi4yMDcxIDI1LjY1NjkgMTUuMzAyMUMyNC4yOTQ5IDEzLjk0NDEgMjIuNTA5OSAxMy41MDIxIDIxLjY1NDkgMTQuMzExMUwzMC45ODk5IDUuMDEwMUMzMS44MzM5IDQuMTY5MSAzMy42NDA5IDQuNjAwMSAzNS4wMjQ5IDUuOTY5MUMzNS45MjI5IDYuODc0MSAzNi40MjA5IDcuOTUyMSAzNi40MjA5IDguODI1MSIgc3Ryb2tlPSIjMEI4RTY5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC41MTQ2IDMzLjc3MzlDOS45NDE2IDM0LjA3NTkgOS4zNTc2IDM0LjM0NDkgOC43NTA2IDM0LjYwMzlMNC40OTk2IDM2LjM4MTlMNi4yODU2IDMyLjE0NjlDNi41NDM2IDMxLjU0MjkgNi44MTQ2IDMwLjk2MDkgNy4xMTg2IDMwLjM4OTlDNy44MDk2IDMwLjU3MjkgOC41Njc2IDMxLjAxNDkgOS4yMjc2IDMxLjY3MTlDOS44ODY2IDMyLjMyOTkgMTAuMzI5NiAzMy4wODM5IDEwLjUxNDYgMzMuNzczOVoiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZD0iTTEwLjUxNDYgMzMuNzczOUM5Ljk0MTYgMzQuMDc1OSA5LjM1NzYgMzQuMzQ0OSA4Ljc1MDYgMzQuNjAzOUw0LjQ5OTYgMzYuMzgxOUw2LjI4NTYgMzIuMTQ2OUM2LjU0MzYgMzEuNTQyOSA2LjgxNDYgMzAuOTYwOSA3LjExODYgMzAuMzg5OUM3LjgwOTYgMzAuNTcyOSA4LjU2NzYgMzEuMDE0OSA5LjIyNzYgMzEuNjcxOUM5Ljg4NjYgMzIuMzI5OSAxMC4zMjk2IDMzLjA4MzkgMTAuNTE0NiAzMy43NzM5IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8ZyBvcGFjaXR5PSIwLjE1Ij4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zNi40OTggOC43NDg1QzM2LjQ5OCA5LjIxMTUgMzYuMzU3IDkuNjIxNSAzNi4wNjUgOS45MTI1TDE2LjMyMyAyOS41OTI1QzE0LjE5MiAzMS43MDQ1IDExLjY1IDMzLjM4NTUgOC43NTEgMzQuNjAzNUw0LjUgMzYuMzgxNUw1LjQ3NCAzNC4wNjQ1TDcuMzk5IDMzLjI1NjVDMTAuMjk4IDMyLjAzODUgMTIuODQgMzAuMzU3NSAxNC45NzEgMjguMjQ1NUwzNC43MTMgOC41NjU1QzM1LjAwNSA4LjI3NDUgMzUuMTQ1IDcuODY0NSAzNS4xNDUgNy40MDE1QzM1LjE0NSA2Ljc1NDUgMzQuODc1IDYuMDAwNSAzNC4zNjYgNS4yNzg1QzM0LjYxNSA1LjQ1MDUgMzQuODY0IDUuNjU1NSAzNS4xMDIgNS44OTI1QzM2IDYuNzk3NSAzNi40OTggNy44NzU1IDM2LjQ5OCA4Ljc0ODVaIiBmaWxsPSIjMEI4RTY5Ii8+CjxwYXRoIGQ9Ik0zNi40OTggOC43NDg1QzM2LjQ5OCA5LjIxMTUgMzYuMzU3IDkuNjIxNSAzNi4wNjUgOS45MTI1TDE2LjMyMyAyOS41OTI1QzE0LjE5MiAzMS43MDQ1IDExLjY1IDMzLjM4NTUgOC43NTEgMzQuNjAzNUw0LjUgMzYuMzgxNUw1LjQ3NCAzNC4wNjQ1TDcuMzk5IDMzLjI1NjVDMTAuMjk4IDMyLjAzODUgMTIuODQgMzAuMzU3NSAxNC45NzEgMjguMjQ1NUwzNC43MTMgOC41NjU1QzM1LjAwNSA4LjI3NDUgMzUuMTQ1IDcuODY0NSAzNS4xNDUgNy40MDE1QzM1LjE0NSA2Ljc1NDUgMzQuODc1IDYuMDAwNSAzNC4zNjYgNS4yNzg1QzM0LjYxNSA1LjQ1MDUgMzQuODY0IDUuNjU1NSAzNS4xMDIgNS44OTI1QzM2IDYuNzk3NSAzNi40OTggNy44NzU1IDM2LjQ5OCA4Ljc0ODUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjwvZz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xOC40NTAyIDEyLjgzMUMxOC40NTAyIDEzLjMzIDE4LjA0NjIgMTMuNzM1IDE3LjU0NjIgMTMuNzM1QzE3LjA0NzIgMTMuNzM1IDE2LjY0MTIgMTMuMzMgMTYuNjQxMiAxMi44MzFDMTYuNjQxMiAxMi4zMzEgMTcuMDQ3MiAxMS45MjcgMTcuNTQ2MiAxMS45MjdDMTguMDQ2MiAxMS45MjcgMTguNDUwMiAxMi4zMzEgMTguNDUwMiAxMi44MzFaIiBmaWxsPSIjMEI4RTY5IiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K' - }, - text2speech: { - // eslint-disable-next-line max-len - blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2IDIxLjY2OTNWMzQuMjgzNEMxNiAzNS44MTUxIDE0IDM2LjUzNTkgMTIuOCAzNS41NDQ4TDEwLjIgMzMuMzgyNEM5LjIgMzIuNTcxNSA4IDMyLjIxMTEgNi43IDMyLjIxMTFINi4zQzUgMzIuMjExMSA0IDMxLjMxMDEgNCAzMC4xMzg4VjI1LjkwNDFDNCAyNC43MzI4IDUgMjMuODMxOCA2LjMgMjMuODMxOEg2LjdDOCAyMy44MzE4IDkuMiAyMy4zODEzIDEwLjEgMjIuNjYwNEwxMi44IDIwLjQ5OEMxNCAxOS40MTY4IDE2IDIwLjIyNzcgMTYgMjEuNjY5M1oiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0yNCA0QzE5LjU4MTcgNCAxNiA3LjU4MTcyIDE2IDEyQzE2IDE1LjExNDcgMTcuNzc5OSAxNy44MTM2IDIwLjM3ODEgMTkuMTM1MUMyMC4yMDk1IDIwLjkwODcgMTkuNjU2NCAyMS42NjU1IDE5LjMwNDIgMjIuMTQ3M0MxOS4xMjY1IDIyLjM5MDQgMTkgMjIuNTYzNSAxOSAyMi43NjE5QzE5IDIzLjQyODYgMTkuNjY2NyAyMy40Mjg2IDE5LjY2NjcgMjMuNDI4NkMyMC42MTMyIDIzLjQyODYgMjMuNTgxMyAyMi4yNjIzIDI1LjQwOTcgMjBIMjhDMzIuNDE4MyAyMCAzNiAxNi40MTgzIDM2IDEyQzM2IDcuNTgxNzIgMzIuNDE4MyA0IDI4IDRIMjRaIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMEI4RTY5Ii8+Cjwvc3ZnPgo=' - }, - translate: { - // eslint-disable-next-line max-len - blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggNUM1Ljc5MDg2IDUgNCA2Ljc5MDg2IDQgOVYxN0M0IDE5LjIwOTEgNS43OTA4NiAyMSA4IDIxSDguMjUyNzdDOC4wMTY0OCAyMi4xNzI4IDcuNTkyODMgMjIuNzUyNCA3LjMwNDIzIDIzLjE0NzNDNy4xMjY1MSAyMy4zOTA0IDcgMjMuNTYzNSA3IDIzLjc2MTlDNyAyNC40Mjg2IDcuNjY2NjcgMjQuNDI4NiA3LjY2NjY3IDI0LjQyODZDOC42MTMyNCAyNC40Mjg2IDExLjU4MTMgMjMuMjYyMyAxMy40MDk3IDIxSDIwQzIyLjIwOTEgMjEgMjQgMTkuMjA5MSAyNCAxN1Y5QzI0IDYuNzkwODYgMjIuMjA5MSA1IDIwIDVIOFoiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwQjhFNjkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy40MjE4IDcuOTEzMDJDMTMuNTUyMSA3LjgwMTUzIDEzLjcyMzkgNy43NSAxMy45MDY3IDcuNzVDMTQuMDkyOCA3Ljc1IDE0LjI2NyA3LjgwMDM2IDE0LjM5OTQgNy45MTE1QzE0LjUzNTQgOC4wMjU2MSAxNC42MDY3IDguMTg3NDggMTQuNjA2NyA4LjM2MjE2VjkuNTI4NDFIMTguNzAzNkMxOC44Nzk5IDkuNTI4NDEgMTkuMDI3MiA5LjYxMDE3IDE5LjEyMjkgOS43Mzk1N0MxOS4yMTM0IDkuODYyMDEgMTkuMjUgMTAuMDE0NiAxOS4yNSAxMC4xNjI2QzE5LjI1IDEwLjMxMzYgMTkuMjE0NyAxMC40NzAyIDE5LjEyOTMgMTAuNTk3MUMxOS4wMzk1IDEwLjczMDYgMTguODkyNyAxMC44Mjk3IDE4LjcwMzYgMTAuODI5N0gxNy4xMDEyQzE2LjY2MDggMTIuMjgwMiAxNS45NDgyIDEzLjYzODEgMTQuOTI3IDE0Ljc4MTlDMTUuMDYwNiAxNC44OTE2IDE1LjE4OSAxNC45OTIxIDE1LjMyMTMgMTUuMDgyNkwxNS4zMjc5IDE1LjA4NzFDMTYuMzIxOSAxNS44MTQgMTcuNTQ3MiAxNi4zODUgMTguNzM1MSAxNi43NjI5QzE5LjAzOTUgMTYuODU5IDE5LjIyODIgMTcuMTAzNiAxOS4yMjgyIDE3LjQwNzNDMTkuMjI4MiAxNy40OTM2IDE5LjIxMTIgMTcuNTc3IDE5LjE4MDYgMTcuNjU2MUMxOS4xMzIgMTcuODA1NSAxOS4wNTU1IDE3LjkzNDUgMTguOTQyOCAxOC4wMjY1QzE4LjgyNTEgMTguMTIyNiAxOC42ODc0IDE4LjE2MjQgMTguNTUgMTguMTYyNEMxOC40NDgxIDE4LjE2MjQgMTguMzQyNyAxOC4xNDc3IDE4LjIyNiAxOC4xMDQ3QzE2Ljc5OTcgMTcuNjM1OSAxNS41Nzc3IDE3LjAyMDEgMTQuNDMzIDE2LjEyMjVMMTQuNDMwOSAxNi4xMjA4QzE0LjQxNDIgMTYuMTA3NCAxNC4zOTc1IDE2LjA5NDEgMTQuMzgwOSAxNi4wODA3QzE0LjIzOTEgMTUuOTY3MyAxNC4wOTc1IDE1Ljg1MzggMTMuOTU5MiAxNS43MzM1QzEzLjgwNTUgMTUuODY1NyAxMy42NTIyIDE1Ljk4OTggMTMuNDg2NyAxNi4xMTQxQzEyLjI3NzIgMTcuMDMyMyAxMS4xMjMgMTcuNjAyNyA5Ljc0NTUgMTguMTEzOUw5LjczODE3IDE4LjExNjZDOS42NDc4MSAxOC4xNDcgOS41NTQ4NiAxOC4xNjI0IDkuNDYxMTIgMTguMTYyNEM5LjMxOTgyIDE4LjE2MjQgOS4xNzk5MSAxOC4xMjY3IDkuMDU4NTggMTguMDQyQzguOTM4MDggMTcuOTU3OSA4Ljg1MjUxIDE3LjgzNzIgOC43OTk0NyAxNy42OTQ1QzguNzY3MjYgMTcuNjEzIDguNzUgMTcuNTI3OCA4Ljc1IDE3LjQ0MDJDOC43NSAxNy4xMjA3IDguOTc2NDEgMTYuODkyMSA5LjIzODg0IDE2Ljc5MDNMOS4yNDk0OSAxNi43ODYyQzEwLjQ2NzkgMTYuMzc2NSAxMS42MTQgMTUuODUxNSAxMi44Nzk3IDE0Ljg2NkMxMi45MTQ5IDE0LjgzNSAxMi45NTA5IDE0LjgwNTUgMTIuOTg1NCAxNC43Nzc5QzExLjk4NDUgMTMuNjMzNyAxMS4yODM0IDEyLjI2ODkgMTAuODQ0MiAxMC44Mjk3SDkuMzA3NUM5LjExNjIyIDEwLjgyOTcgOC45NjY4NyAxMC43MzM2IDguODc1ODMgMTAuNTk0NkM4Ljc5MTQ0IDEwLjQ2NTcgOC43NjA4OCAxMC4zMDg3IDguNzYwODggMTAuMTYyNkM4Ljc2MDg4IDEwLjAxOTkgOC43OTIzNSA5Ljg2Njc0IDguODgyMSA5Ljc0MjEyQzguOTc5MDYgOS42MDc0OCA5LjEyOTEgOS41Mjg0MSA5LjMwNzUgOS41Mjg0MUgxMy4yMTc5VjguMzYyMTZDMTMuMjE3OSA4LjE4ODA1IDEzLjI4ODYgOC4wMjcgMTMuNDIxOCA3LjkxMzAyWk0xMi4yMzg2IDEwLjgyOTdDMTIuNjA5MSAxMS45MzE2IDEzLjIwNTYgMTIuOTU4OCAxMy45NjI2IDEzLjgzNzdDMTQuNzI1NyAxMi45NTkzIDE1LjMwNTEgMTEuOTM4OSAxNS42NDkzIDEwLjgyOTdIMTIuMjM4NloiIGZpbGw9IiM0Qzk3RkYiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMiAxNkMzNC4yMDkxIDE2IDM2IDE3Ljc5MDkgMzYgMjBWMjhDMzYgMzAuMjA5MSAzNC4yMDkxIDMyIDMyIDMySDMxLjc0NzJDMzEuOTgzNSAzMy4xNzI4IDMyLjQwNzIgMzMuNzUyNCAzMi42OTU4IDM0LjE0NzNDMzIuODczNSAzNC4zOTA0IDMzIDM0LjU2MzUgMzMgMzQuNzYxOUMzMyAzNS40Mjg2IDMyLjMzMzMgMzUuNDI4NiAzMi4zMzMzIDM1LjQyODZDMzEuMzg2OCAzNS40Mjg2IDI4LjQxODcgMzQuMjYyMyAyNi41OTAzIDMySDIwQzE3Ljc5MDkgMzIgMTYgMzAuMjA5MSAxNiAyOFYyMEMxNiAxNy43OTA5IDE3Ljc5MDkgMTYgMjAgMTZIMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzEuNzQ3MiAzMkwzMS4yNTcxIDMyLjA5ODhMMzEuMTM2NCAzMS41SDMxLjc0NzJWMzJaTTMyLjY5NTggMzQuMTQ3M0wzMy4wOTk0IDMzLjg1MjJIMzMuMDk5NEwzMi42OTU4IDM0LjE0NzNaTTI2LjU5MDMgMzJWMzEuNUgyNi44MjkxTDI2Ljk3OTIgMzEuNjg1N0wyNi41OTAzIDMyWk0zNS41IDIwQzM1LjUgMTguMDY3IDMzLjkzMyAxNi41IDMyIDE2LjVWMTUuNUMzNC40ODUzIDE1LjUgMzYuNSAxNy41MTQ3IDM2LjUgMjBIMzUuNVpNMzUuNSAyOFYyMEgzNi41VjI4SDM1LjVaTTMyIDMxLjVDMzMuOTMzIDMxLjUgMzUuNSAyOS45MzMgMzUuNSAyOEgzNi41QzM2LjUgMzAuNDg1MyAzNC40ODUzIDMyLjUgMzIgMzIuNVYzMS41Wk0zMS43NDcyIDMxLjVIMzJWMzIuNUgzMS43NDcyVjMxLjVaTTMyLjI5MjEgMzQuNDQyM0MzMS45ODM4IDM0LjAyMDYgMzEuNTEzOCAzMy4zNzI3IDMxLjI1NzEgMzIuMDk4OEwzMi4yMzc0IDMxLjkwMTJDMzIuNDUzMyAzMi45NzI5IDMyLjgzMDUgMzMuNDg0MyAzMy4wOTk0IDMzLjg1MjJMMzIuMjkyMSAzNC40NDIzWk0zMi41IDM0Ljc2MTlDMzIuNSAzNC43NzU1IDMyLjUwNjEgMzQuNzY2NiAzMi40NzU4IDM0LjcxMTNDMzIuNDQxIDM0LjY0NzUgMzIuMzg2OSAzNC41NzIxIDMyLjI5MjEgMzQuNDQyM0wzMy4wOTk0IDMzLjg1MjJDMzMuMTgyMyAzMy45NjU2IDMzLjI4MDQgMzQuMDk4MyAzMy4zNTMyIDM0LjIzMTVDMzMuNDMwNyAzNC4zNzMxIDMzLjUgMzQuNTUgMzMuNSAzNC43NjE5SDMyLjVaTTMyLjMzMzMgMzUuNDI4NkMzMi4zMzMzIDM0LjkyODYgMzIuMzMzMSAzNC45Mjg2IDMyLjMzMjkgMzQuOTI4NkMzMi4zMzI4IDM0LjkyODYgMzIuMzMyNSAzNC45Mjg2IDMyLjMzMjQgMzQuOTI4NkMzMi4zMzIxIDM0LjkyODYgMzIuMzMxOCAzNC45Mjg2IDMyLjMzMTUgMzQuOTI4NkMzMi4zMzA5IDM0LjkyODYgMzIuMzMwNCAzNC45Mjg2IDMyLjMyOTggMzQuOTI4NkMzMi4zMjg4IDM0LjkyODYgMzIuMzI3OSAzNC45Mjg2IDMyLjMyNzIgMzQuOTI4NkMzMi4zMjU3IDM0LjkyODYgMzIuMzI0NyAzNC45Mjg3IDMyLjMyNDIgMzQuOTI4N0MzMi4zMjMxIDM0LjkyODcgMzIuMzI0IDM0LjkyODcgMzIuMzI2NCAzNC45Mjg1QzMyLjMzMTMgMzQuOTI4MiAzMi4zNDE2IDM0LjkyNzIgMzIuMzU1MyAzNC45MjVDMzIuMzg1MSAzNC45MiAzMi40MTcxIDM0LjkxMSAzMi40NDMxIDM0Ljg5OEMzMi40NjcxIDM0Ljg4NiAzMi40NzU5IDM0Ljg3NTkgMzIuNDc5OCAzNC44N0MzMi40ODE2IDM0Ljg2NzIgMzIuNSAzNC44NDExIDMyLjUgMzQuNzYxOUgzMy41QzMzLjUgMzUuMDE2MSAzMy40MzUgMzUuMjM5OSAzMy4zMTE5IDM1LjQyNDdDMzMuMTkwOCAzNS42MDYzIDMzLjAzMjkgMzUuNzIxMSAzMi44OTAzIDM1Ljc5MjVDMzIuNzQ5NiAzNS44NjI4IDMyLjYxNDkgMzUuODk1NSAzMi41MTk3IDM1LjkxMTRDMzIuNDcwOSAzNS45MTk1IDMyLjQyOTEgMzUuOTIzNyAzMi4zOTc2IDM1LjkyNkMzMi4zODE4IDM1LjkyNzEgMzIuMzY4MyAzNS45Mjc4IDMyLjM1NzUgMzUuOTI4MUMzMi4zNTIxIDM1LjkyODMgMzIuMzQ3MyAzNS45Mjg0IDMyLjM0MzMgMzUuOTI4NUMzMi4zNDEzIDM1LjkyODUgMzIuMzM5NCAzNS45Mjg1IDMyLjMzNzggMzUuOTI4NkMzMi4zMzcgMzUuOTI4NiAzMi4zMzYyIDM1LjkyODYgMzIuMzM1NCAzNS45Mjg2QzMyLjMzNTEgMzUuOTI4NiAzMi4zMzQ3IDM1LjkyODYgMzIuMzM0MyAzNS45Mjg2QzMyLjMzNDIgMzUuOTI4NiAzMi4zMzM5IDM1LjkyODYgMzIuMzMzOCAzNS45Mjg2QzMyLjMzMzYgMzUuOTI4NiAzMi4zMzMzIDM1LjkyODYgMzIuMzMzMyAzNS40Mjg2Wk0yNi45NzkyIDMxLjY4NTdDMjcuODM5NyAzMi43NTA0IDI4Ljk3OTEgMzMuNTY4MyAzMC4wMjU4IDM0LjEyMDRDMzAuNTQ3NSAzNC4zOTU2IDMxLjAzODYgMzQuNjAwNyAzMS40NSAzNC43MzZDMzEuODcyNyAzNC44NzUgMzIuMTc2IDM0LjkyODYgMzIuMzMzMyAzNC45Mjg2VjM1LjkyODZDMzIuMDE3MyAzNS45Mjg2IDMxLjU5NDcgMzUuODM2MyAzMS4xMzc2IDM1LjY4NkMzMC42NjkgMzUuNTMxOSAzMC4xMjY0IDM1LjMwNDEgMjkuNTU5MiAzNS4wMDQ5QzI4LjQyODEgMzQuNDA4MyAyNy4xNjkzIDMzLjUxMTkgMjYuMjAxNSAzMi4zMTQzTDI2Ljk3OTIgMzEuNjg1N1pNMjAgMzEuNUgyNi41OTAzVjMyLjVIMjBWMzEuNVpNMTYuNSAyOEMxNi41IDI5LjkzMyAxOC4wNjcgMzEuNSAyMCAzMS41VjMyLjVDMTcuNTE0NyAzMi41IDE1LjUgMzAuNDg1MyAxNS41IDI4SDE2LjVaTTE2LjUgMjBWMjhIMTUuNVYyMEgxNi41Wk0yMCAxNi41QzE4LjA2NyAxNi41IDE2LjUgMTguMDY3IDE2LjUgMjBIMTUuNUMxNS41IDE3LjUxNDcgMTcuNTE0NyAxNS41IDIwIDE1LjVWMTYuNVpNMzIgMTYuNUgyMFYxNS41SDMyVjE2LjVaIiBmaWxsPSIjMEI4RTY5Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjQuOTIyNiAyNS4wMjAxSDI3LjA0M0wyNi4wMTE1IDIyLjAxNzJIMjUuOTg4NUwyNC45MjI2IDI1LjAyMDFaTTI1LjA5NDYgMjBIMjYuOTM5OEwzMCAyOC4xODM0SDI4LjEzMThMMjcuNTEyOSAyNi4zNjFIMjQuNDUyN0wyMy44MTA5IDI4LjE4MzRIMjJMMjUuMDk0NiAyMFoiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMzMuMDMzIDEyLjg3NjNDMzIuODMzNSAxMy4wNzEzIDMyLjU2MzUgMTMuMTgxMyAzMi4yODMgMTMuMTgxM0MzMi4wMDM0IDEzLjE4MTMgMzEuNzMzNCAxMy4wNzEzIDMxLjUzMjkgMTIuODc2M0wyOC44Nzg0IDEwLjIxNjJDMjguNTczNCA5LjkxMTIyIDI4LjQ4MzQgOS40NTYyMSAyOC42NDg0IDkuMDYxMkMyOC44MTM0IDguNjY2MTkgMjkuMTkzNCA4LjQxMTE5IDI5LjYyMzQgOC40MTExOUgzMC41Nzg0QzMwLjU1MzQgOC4xODExOSAzMC40ODg0IDcuOTIxMTggMzAuMzc4NCA3LjY0NjE4QzMwLjM0MzkgNy41NjYxNyAzMC4zMDg0IDcuNDg2MTcgMzAuMjY4NCA3LjQwNjE3QzMwLjIxMzQgNy4zMTYxNyAzMC4yMTg5IDcuMjkxMTcgMzAuMTQzNCA3LjE4MTE3QzMwLjAyMzQgNy4wMDExNiAyOS45MTM0IDYuODY2MTYgMjkuNzgyOSA2LjcxMTE2QzI5LjUxODQgNi40MjYxNSAyOS4xOTg0IDYuMTgxMTUgMjguODU4NCA2LjAwMTE1QzI4LjUxMzQgNS44MjExNCAyOC4xNTM0IDUuNzExMTQgMjcuODMzNCA1LjY1MTE0QzI3LjUxODQgNS41OTYxNCAyNy4yMjg0IDUuNTkxMTQgMjcuMDU4NCA1LjU5MTE0QzI2Ljk3MzQgNS41ODYxNCAyNi44NjM0IDUuNjA2MTQgMjYuODA4NCA1LjYxMTE0QzI2Ljc0ODQgNS42MTYxNCAyNi43MTM0IDUuNjIxMTQgMjYuNzEzNCA1LjYyMTE0QzI2LjM1ODMgNS42NTYxNCAyNi4wMzgzIDUuMzk2MTQgMjYuMDAzMyA1LjA0MTEzQzI1Ljk3MzMgNC43NDExMiAyNi4xNDgzIDQuNDcxMTIgMjYuNDEzMyA0LjM3MTEyQzI2LjQxMzMgNC4zNzExMiAyNi40NDgzIDQuMzU2MTIgMjYuNTAzMyA0LjMzNjEyQzI2LjU2ODMgNC4zMTYxMiAyNi42Mjg0IDQuMjgxMTIgMjYuNzY4NCA0LjI0MTEyQzI3LjA0ODQgNC4xNTYxMSAyNy40MDM0IDQuMDY2MTEgMjcuODY4NCA0LjAyMTExQzI4LjMyODQgMy45ODExMSAyOC44ODM0IDMuOTkxMTEgMjkuNDY4OSA0LjExMTExQzMwLjA1MzQgNC4yMzYxMiAzMC42Njg0IDQuNDcxMTIgMzEuMjM4NCA0LjgxMTEzQzMxLjUwODQgNC45ODYxMyAzMS43OTg0IDUuMTkxMTMgMzIuMDIzNCA1LjM5MTE0QzMyLjEyMzQgNS40NjYxNCAzMi4yOTM1IDUuNjM2MTQgMzIuMzkzNSA1Ljc0MTE0QzMyLjUwODUgNS44NjExNCAzMi42MTM1IDUuOTgxMTUgMzIuNzE5IDYuMTA2MTVDMzMuMTIzNSA2LjYwNjE2IDMzLjQyMzUgNy4xNDYxNyAzMy42MTg1IDcuNjM2MThDMzMuNzMzNSA3LjkxNjE4IDMzLjgxMzUgOC4xODExOSAzMy44NzM1IDguNDExMTlIMzQuOTQzNUMzNS4zNzM1IDguNDExMTkgMzUuNzUzNSA4LjY2NjE5IDM1LjkxODUgOS4wNjEyQzM2LjA4MzUgOS40NTYyMSAzNS45OTM1IDkuOTExMjIgMzUuNjg4NSAxMC4yMTYyTDMzLjAzMyAxMi44NzYzWiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02Ljk2NzA0IDI3LjEyNUM3LjE2NjU0IDI2LjkzIDcuNDM2NTQgMjYuODIgNy43MTcwNSAyNi44MkM3Ljk5NjU1IDI2LjgyIDguMjY2NTYgMjYuOTMgOC40NjcwNiAyNy4xMjVMMTEuMTIxNiAyOS43ODUxQzExLjQyNjYgMzAuMDkwMSAxMS41MTY2IDMwLjU0NTEgMTEuMzUxNiAzMC45NDAxQzExLjE4NjYgMzEuMzM1MSAxMC44MDY2IDMxLjU5MDEgMTAuMzc2NiAzMS41OTAxSDkuNDIxNThDOS40NDY1OCAzMS44MjAxIDkuNTExNTggMzIuMDgwMSA5LjYyMTU4IDMyLjM1NTFDOS42NTYwOCAzMi40MzUxIDkuNjkxNTggMzIuNTE1MSA5LjczMTU4IDMyLjU5NTFDOS43ODY1OSAzMi42ODUxIDkuNzgxMDkgMzIuNzEwMSA5Ljg1NjU5IDMyLjgyMDFDOS45NzY1OSAzMy4wMDAxIDEwLjA4NjYgMzMuMTM1MSAxMC4yMTcxIDMzLjI5MDFDMTAuNDgxNiAzMy41NzUxIDEwLjgwMTYgMzMuODIwMSAxMS4xNDE2IDM0LjAwMDFDMTEuNDg2NiAzNC4xODAxIDExLjg0NjYgMzQuMjkwMSAxMi4xNjY2IDM0LjM1MDFDMTIuNDgxNiAzNC40MDUxIDEyLjc3MTYgMzQuNDEwMSAxMi45NDE2IDM0LjQxMDFDMTMuMDI2NiAzNC40MTUxIDEzLjEzNjYgMzQuMzk1MSAxMy4xOTE2IDM0LjM5MDFDMTMuMjUxNiAzNC4zODUxIDEzLjI4NjYgMzQuMzgwMSAxMy4yODY2IDM0LjM4MDFDMTMuNjQxNyAzNC4zNDUxIDEzLjk2MTcgMzQuNjA1MSAxMy45OTY3IDM0Ljk2MDJDMTQuMDI2NyAzNS4yNjAyIDEzLjg1MTcgMzUuNTMwMiAxMy41ODY3IDM1LjYzMDJDMTMuNTg2NyAzNS42MzAyIDEzLjU1MTcgMzUuNjQ1MiAxMy40OTY3IDM1LjY2NTJDMTMuNDMxNyAzNS42ODUyIDEzLjM3MTYgMzUuNzIwMiAxMy4yMzE2IDM1Ljc2MDJDMTIuOTUxNiAzNS44NDUyIDEyLjU5NjYgMzUuOTM1MiAxMi4xMzE2IDM1Ljk4MDJDMTEuNjcxNiAzNi4wMjAyIDExLjExNjYgMzYuMDEwMiAxMC41MzExIDM1Ljg5MDJDOS45NDY1OSAzNS43NjUyIDkuMzMxNTggMzUuNTMwMiA4Ljc2MTU3IDM1LjE5MDJDOC40OTE1NiAzNS4wMTUyIDguMjAxNTYgMzQuODEwMSA3Ljk3NjU1IDM0LjYxMDFDNy44NzY1NSAzNC41MzUxIDcuNzA2NTUgMzQuMzY1MSA3LjYwNjU1IDM0LjI2MDFDNy40OTE1NCAzNC4xNDAxIDcuMzg2NTQgMzQuMDIwMSA3LjI4MTA0IDMzLjg5NTFDNi44NzY1MyAzMy4zOTUxIDYuNTc2NTMgMzIuODU1MSA2LjM4MTUzIDMyLjM2NTFDNi4yNjY1MiAzMi4wODUxIDYuMTg2NTIgMzEuODIwMSA2LjEyNjUyIDMxLjU5MDFINS4wNTY1QzQuNjI2NDkgMzEuNTkwMSA0LjI0NjQ5IDMxLjMzNTEgNC4wODE0OCAzMC45NDAxQzMuOTE2NDggMzAuNTQ1MSA0LjAwNjQ4IDMwLjA5MDEgNC4zMTE0OSAyOS43ODUxTDYuOTY3MDQgMjcuMTI1WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==' - }, - videoSensing: { - // eslint-disable-next-line max-len - blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBvcGFjaXR5PSIwLjI1IiBjeD0iMzIiIGN5PSIyNiIgcj0iNCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxjaXJjbGUgb3BhY2l0eT0iMC41IiBjeD0iMzIiIGN5PSIyMiIgcj0iNCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzBCOEU2OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxjaXJjbGUgb3BhY2l0eT0iMC43NSIgY3g9IjMyIiBjeT0iMTgiIHI9IjQiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwQjhFNjkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIzMiIgY3k9IjE0IiByPSI0IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMEI4RTY5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTE3IDE3LjVMMjIuNCAxNEMyMi45IDEzLjcgMjMuNSAxMy44IDIzLjggMTQuM0MyMy45IDE0LjUgMjQgMTQuNyAyNCAxNC44VjI1LjFDMjQgMjUuNyAyMy41IDI2LjEgMjMgMjYuMUMyMi44IDI2LjEgMjIuNiAyNiAyMi41IDI1LjlMMTcgMjIuNlYyNEMxNyAyNi4yIDE1LjIgMjguMSAxMyAyOC4xSDQuMUMxLjggMjggMCAyNi4yIDAgMjRWMTYuMUMwIDEzLjggMS44IDEyIDQuMSAxMkgxM0MxNS4yIDEyIDE3IDEzLjggMTcgMTYuMVYxNy41WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==' - } -}; - -export { - blockColors, - extensions -}; diff --git a/static/blocks-media-high-contrast/comment-arrow-down.svg b/src/lib/themes/high-contrast/blocks-media/comment-arrow-down.svg similarity index 100% rename from static/blocks-media-high-contrast/comment-arrow-down.svg rename to src/lib/themes/high-contrast/blocks-media/comment-arrow-down.svg diff --git a/static/blocks-media-high-contrast/comment-arrow-up.svg b/src/lib/themes/high-contrast/blocks-media/comment-arrow-up.svg similarity index 100% rename from static/blocks-media-high-contrast/comment-arrow-up.svg rename to src/lib/themes/high-contrast/blocks-media/comment-arrow-up.svg diff --git a/static/blocks-media-high-contrast/delete-x.svg b/src/lib/themes/high-contrast/blocks-media/delete-x.svg similarity index 100% rename from static/blocks-media-high-contrast/delete-x.svg rename to src/lib/themes/high-contrast/blocks-media/delete-x.svg diff --git a/static/blocks-media-high-contrast/dropdown-arrow.svg b/src/lib/themes/high-contrast/blocks-media/dropdown-arrow.svg similarity index 100% rename from static/blocks-media-high-contrast/dropdown-arrow.svg rename to src/lib/themes/high-contrast/blocks-media/dropdown-arrow.svg diff --git a/static/blocks-media-high-contrast/eyedropper.svg b/src/lib/themes/high-contrast/blocks-media/eyedropper.svg similarity index 100% rename from static/blocks-media-high-contrast/eyedropper.svg rename to src/lib/themes/high-contrast/blocks-media/eyedropper.svg diff --git a/static/blocks-media-high-contrast/icons/arrow_button.svg b/src/lib/themes/high-contrast/blocks-media/icons/arrow_button.svg similarity index 100% rename from static/blocks-media-high-contrast/icons/arrow_button.svg rename to src/lib/themes/high-contrast/blocks-media/icons/arrow_button.svg diff --git a/static/blocks-media-high-contrast/repeat.svg b/src/lib/themes/high-contrast/blocks-media/repeat.svg similarity index 100% rename from static/blocks-media-high-contrast/repeat.svg rename to src/lib/themes/high-contrast/blocks-media/repeat.svg diff --git a/static/blocks-media-high-contrast/rotate-left.svg b/src/lib/themes/high-contrast/blocks-media/rotate-left.svg similarity index 100% rename from static/blocks-media-high-contrast/rotate-left.svg rename to src/lib/themes/high-contrast/blocks-media/rotate-left.svg diff --git a/static/blocks-media-high-contrast/rotate-right.svg b/src/lib/themes/high-contrast/blocks-media/rotate-right.svg similarity index 100% rename from static/blocks-media-high-contrast/rotate-right.svg rename to src/lib/themes/high-contrast/blocks-media/rotate-right.svg diff --git a/static/blocks-media-high-contrast/zoom-in.svg b/src/lib/themes/high-contrast/blocks-media/zoom-in.svg similarity index 100% rename from static/blocks-media-high-contrast/zoom-in.svg rename to src/lib/themes/high-contrast/blocks-media/zoom-in.svg diff --git a/static/blocks-media-high-contrast/zoom-out.svg b/src/lib/themes/high-contrast/blocks-media/zoom-out.svg similarity index 100% rename from static/blocks-media-high-contrast/zoom-out.svg rename to src/lib/themes/high-contrast/blocks-media/zoom-out.svg diff --git a/static/blocks-media-high-contrast/zoom-reset.svg b/src/lib/themes/high-contrast/blocks-media/zoom-reset.svg similarity index 100% rename from static/blocks-media-high-contrast/zoom-reset.svg rename to src/lib/themes/high-contrast/blocks-media/zoom-reset.svg diff --git a/src/lib/themes/high-contrast/extensions/musicIcon.svg b/src/lib/themes/high-contrast/extensions/musicIcon.svg new file mode 100644 index 000000000..34cf4e4f3 --- /dev/null +++ b/src/lib/themes/high-contrast/extensions/musicIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/lib/themes/high-contrast/extensions/penIcon.svg b/src/lib/themes/high-contrast/extensions/penIcon.svg new file mode 100644 index 000000000..d4436d58c --- /dev/null +++ b/src/lib/themes/high-contrast/extensions/penIcon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/lib/themes/high-contrast/extensions/text2speechIcon.svg b/src/lib/themes/high-contrast/extensions/text2speechIcon.svg new file mode 100644 index 000000000..2606c9cfb --- /dev/null +++ b/src/lib/themes/high-contrast/extensions/text2speechIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/lib/themes/high-contrast/extensions/translateIcon.svg b/src/lib/themes/high-contrast/extensions/translateIcon.svg new file mode 100644 index 000000000..e1fafc5be --- /dev/null +++ b/src/lib/themes/high-contrast/extensions/translateIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/lib/themes/high-contrast/extensions/videoSensingIcon.svg b/src/lib/themes/high-contrast/extensions/videoSensingIcon.svg new file mode 100644 index 000000000..95c2e90db --- /dev/null +++ b/src/lib/themes/high-contrast/extensions/videoSensingIcon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/lib/themes/high-contrast.svg b/src/lib/themes/high-contrast/icon.svg similarity index 100% rename from src/lib/themes/high-contrast.svg rename to src/lib/themes/high-contrast/icon.svg diff --git a/src/lib/themes/high-contrast/index.js b/src/lib/themes/high-contrast/index.js new file mode 100644 index 000000000..8a5dd9415 --- /dev/null +++ b/src/lib/themes/high-contrast/index.js @@ -0,0 +1,110 @@ +import musicIcon from './extensions/musicIcon.svg'; +import penIcon from './extensions/penIcon.svg'; +import text2speechIcon from './extensions/text2speechIcon.svg'; +import translateIcon from './extensions/translateIcon.svg'; +import videoSensingIcon from './extensions/videoSensingIcon.svg'; + +const blockColors = { + motion: { + primary: '#80B5FF', + secondary: '#B3D2FF', + tertiary: '#3373CC', + quaternary: '#CCE1FF' + }, + looks: { + primary: '#CCB3FF', + secondary: '#DDCCFF', + tertiary: '#774DCB', + quaternary: '#EEE5FF' + }, + sounds: { + primary: '#E19DE1', + secondary: '#FFB3FF', + tertiary: '#BD42BD', + quaternary: '#FFCCFF' + + }, + control: { + primary: '#FFBE4C', + secondary: '#FFDA99', + tertiary: '#CF8B17', + quaternary: '#FFE3B3' + }, + event: { + primary: '#FFD966', + secondary: '#FFECB3', + tertiary: '#CC9900', + quaternary: '#FFF2CC' + }, + sensing: { + primary: '#85C4E0', + secondary: '#AED8EA', + tertiary: '#2E8EB8', + quaternary: '#C2E2F0' + }, + pen: { + primary: '#13ECAF', + secondary: '#75F0CD', + tertiary: '#0B8E69', + quaternary: '#A3F5DE' + }, + operators: { + primary: '#7ECE7E', + secondary: '#B5E3B5', + tertiary: '#389438', + quaternary: '#DAF1DA' + }, + data: { + primary: '#FFA54C', + secondary: '#FFCC99', + tertiary: '#DB6E00', + quaternary: '#FFE5CC' + }, + // This is not a new category, but rather for differentiation + // between lists and scalar variables. + data_lists: { + primary: '#FF9966', + secondary: '#FFCAB0', // I don't think this is used, b/c we don't have any droppable fields in list blocks + tertiary: '#E64D00', + quaternary: '#FFDDCC' + }, + more: { + primary: '#FF99AA', + secondary: '#FFCCD5', + tertiary: '#FF3355', + quaternary: '#FFE5EA' + }, + text: '#000000', + textFieldText: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)] + toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75) + // The color that the category menu label (e.g. 'motion', 'looks', etc.) changes to on hover + toolboxHover: '#3373CC', + insertionMarker: '#000000', + insertionMarkerOpacity: 0.2, + fieldShadow: 'rgba(255, 255, 255, 0.3)', + dragShadowOpacity: 0.6, + menuHover: 'rgba(255, 255, 255, 0.3)' +}; + +const extensions = { + music: { + blockIconURI: musicIcon + }, + pen: { + blockIconURI: penIcon + }, + text2speech: { + blockIconURI: text2speechIcon + }, + translate: { + blockIconURI: translateIcon + }, + videoSensing: { + blockIconURI: videoSensingIcon + } +}; + +export { + blockColors, + extensions +}; diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index d77a7a6f3..6eee2244a 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -4,15 +4,15 @@ import {defineMessages} from 'react-intl'; import { blockColors as darkModeBlockColors, extensions as darkModeExtensions -} from './dark-mode'; +} from './dark'; import { blockColors as highContrastBlockColors, extensions as highContrastExtensions } from './high-contrast'; -import {blockColors as defaultColors} from './default-colors'; +import {blockColors as defaultColors} from './default'; -import defaultIcon from './standard.svg'; -import highContrastIcon from './high-contrast.svg'; +import defaultIcon from './default/icon.svg'; +import highContrastIcon from './high-contrast/icon.svg'; const DEFAULT_THEME = 'default'; const HIGH_CONTRAST_THEME = 'high-contrast'; diff --git a/test/unit/components/monitor.test.jsx b/test/unit/components/monitor.test.jsx index 0602f2453..e52fa603a 100644 --- a/test/unit/components/monitor.test.jsx +++ b/test/unit/components/monitor.test.jsx @@ -4,8 +4,8 @@ import DefaultMonitor from '../../../src/components/monitor/default-monitor'; import Monitor from '../../../src/components/monitor/monitor'; import {DARK_THEME, DEFAULT_THEME} from '../../../src/lib/themes'; -jest.mock('../../../src/lib/themes/default-colors'); -jest.mock('../../../src/lib/themes/dark-mode'); +jest.mock('../../../src/lib/themes/default'); +jest.mock('../../../src/lib/themes/dark'); describe('Monitor Component', () => { test('it selects the correct colors based on default theme', () => { diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js index b074c4abc..a815f9c59 100644 --- a/test/unit/util/themes.test.js +++ b/test/unit/util/themes.test.js @@ -8,8 +8,8 @@ import { import {injectExtensionBlockTheme, injectExtensionCategoryTheme} from '../../../src/lib/themes/blockHelpers'; import {detectTheme, persistTheme} from '../../../src/lib/themes/themePersistance'; -jest.mock('../../../src/lib/themes/default-colors'); -jest.mock('../../../src/lib/themes/dark-mode'); +jest.mock('../../../src/lib/themes/default'); +jest.mock('../../../src/lib/themes/dark'); describe('themes', () => { let serializeToString; diff --git a/webpack.config.js b/webpack.config.js index a59eb263d..34e4d7c5a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -113,8 +113,9 @@ module.exports = [ rules: base.module.rules.concat([ { test: /\.(svg|png|wav|gif|jpg)$/, - loader: 'file-loader', + loader: 'url-loader', options: { + limit: 2048, outputPath: 'static/assets/' } } @@ -177,7 +178,7 @@ module.exports = [ to: 'static/blocks-media-high-contrast' }, { - from: 'static/blocks-media-high-contrast', + from: 'src/lib/themes/high-contrast/blocks-media', to: 'static/blocks-media-high-contrast', force: true } @@ -223,8 +224,9 @@ module.exports = [ rules: base.module.rules.concat([ { test: /\.(svg|png|wav|gif|jpg)$/, - loader: 'file-loader', + loader: 'url-loader', options: { + limit: 2048, outputPath: 'static/assets/', publicPath: `${STATIC_PATH}/assets/` } From 413997483ffc794a81970af7376b04e816ab7610 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Mon, 20 Mar 2023 15:59:39 -0400 Subject: [PATCH 15/21] feat: update to purple accent color --- src/components/action-menu/action-menu.css | 11 ++++---- .../asset-panel/icon--sound-rtl.svg | 2 +- src/components/asset-panel/selector.css | 2 +- .../audio-trimmer/audio-trimmer.css | 8 +++--- src/components/audio-trimmer/icon--handle.svg | 4 +-- src/components/backpack/backpack.css | 2 +- .../browser-modal/browser-modal.css | 6 ++--- src/components/cards/card.css | 4 +-- src/components/close-button/close-button.css | 2 +- .../connection-modal/connection-modal.css | 26 +++++++++---------- src/components/context-menu/context-menu.css | 2 +- .../crash-message/crash-message.css | 6 +++++ .../custom-procedures/custom-procedures.css | 8 +++--- .../delete-button/delete-button.css | 4 +-- src/components/direction-picker/dial.css | 4 +-- .../direction-picker/icon--all-around.svg | 4 +-- .../direction-picker/icon--dial.svg | 2 +- .../direction-picker/icon--dont-rotate.svg | 4 +-- .../direction-picker/icon--handle.svg | 2 +- .../direction-picker/icon--left-right.svg | 8 +++--- src/components/drag-layer/drag-layer.css | 2 +- src/components/filter/filter.css | 2 +- src/components/filter/icon--filter.svg | 2 +- src/components/forms/input.css | 6 ++--- src/components/green-flag/green-flag.css | 4 +-- src/components/gui/gui.css | 6 ++--- src/components/gui/icon--code.svg | 4 +-- src/components/gui/icon--costumes.svg | 4 +-- src/components/gui/icon--sounds.svg | 4 +-- src/components/icon-button/icon-button.css | 4 +-- src/components/library-item/library-item.css | 2 +- src/components/library/library.css | 2 +- src/components/menu-bar/menu-bar.css | 2 +- src/components/modal/modal.css | 2 +- src/components/prompt/prompt.css | 4 +-- src/components/question/question.css | 4 +-- src/components/record-modal/icon--back.svg | 2 +- src/components/record-modal/icon--play.svg | 2 +- .../record-modal/icon--stop-playback.svg | 6 ++--- src/components/record-modal/record-modal.css | 8 +++--- .../slider-prompt/slider-prompt.css | 4 +-- .../sound-editor/icon--copy-to-new.svg | 4 +-- src/components/sound-editor/icon--copy.svg | 6 ++--- src/components/sound-editor/icon--delete.svg | 4 +-- src/components/sound-editor/icon--paste.svg | 4 +-- src/components/sound-editor/icon--redo.svg | 2 +- src/components/sound-editor/icon--undo.svg | 2 +- src/components/sound-editor/sound-editor.css | 2 +- src/components/spinner/spinner.css | 4 +-- src/components/sprite-info/icon--hide.svg | 4 +-- src/components/sprite-info/icon--show.svg | 4 +-- src/components/sprite-info/sprite-info.css | 6 ++--- .../sprite-selector-item.css | 8 +++--- .../stage-header/icon--large-stage.svg | 6 ++--- .../stage-header/icon--small-stage.svg | 6 ++--- .../stage-selector/stage-selector.css | 8 +++--- src/components/stage/stage.css | 4 +-- src/components/stop-all/stop-all.css | 2 +- src/components/tag-button/tag-button.css | 2 +- .../telemetry-modal/telemetry-modal.css | 14 +++++----- src/components/webgl-modal/webgl-modal.css | 8 +++--- src/css/colors.css | 5 ++-- 62 files changed, 150 insertions(+), 142 deletions(-) diff --git a/src/components/action-menu/action-menu.css b/src/components/action-menu/action-menu.css index 14dbb5df0..070c1713e 100644 --- a/src/components/action-menu/action-menu.css +++ b/src/components/action-menu/action-menu.css @@ -19,7 +19,7 @@ $more-button-size: 2.25rem; justify-content: center; cursor: pointer; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - background: $motion-primary; + background: $looks-tertiary; outline: none; border: none; transition: background-color 0.2s; @@ -45,14 +45,14 @@ button::-moz-focus-inner { border-radius: 100%; width: $main-button-size; height: $main-button-size; - box-shadow: 0 0 0 4px $motion-transparent; + box-shadow: 0 0 0 4px $looks-transparent; z-index: $z-index-add-button; transition: transform, box-shadow 0.5s; } .main-button:hover { transform: scale(1.1); - box-shadow: 0 0 0 6px $motion-transparent; + box-shadow: 0 0 0 6px $looks-transparent; } .main-icon { @@ -72,7 +72,8 @@ button::-moz-focus-inner { */ overflow-y: hidden; - background: $motion-tertiary; + background: $looks-tertiary-dark; + border-top-left-radius: $more-button-size; border-top-right-radius: $more-button-size; width: $more-button-size; @@ -115,7 +116,7 @@ button::-moz-focus-inner { .more-button { width: $more-button-size; height: $more-button-size; - background: $motion-tertiary; + background: $looks-tertiary-dark; } .more-icon { diff --git a/src/components/asset-panel/icon--sound-rtl.svg b/src/components/asset-panel/icon--sound-rtl.svg index adf850173..108f7aa1b 100644 --- a/src/components/asset-panel/icon--sound-rtl.svg +++ b/src/components/asset-panel/icon--sound-rtl.svg @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css index 6ff5510da..a50b1cc5f 100644 --- a/src/components/asset-panel/selector.css +++ b/src/components/asset-panel/selector.css @@ -20,7 +20,7 @@ align-items: center; justify-content: space-around; padding: 0.75rem 0; - color: $motion-primary; + color: $looks-tertiary; text-align: center; background: none; } diff --git a/src/components/audio-trimmer/audio-trimmer.css b/src/components/audio-trimmer/audio-trimmer.css index f3ce9fa42..b36ec82b9 100644 --- a/src/components/audio-trimmer/audio-trimmer.css +++ b/src/components/audio-trimmer/audio-trimmer.css @@ -41,7 +41,7 @@ $hover-scale: 1.25; } .selection-background { - background: $motion-primary; + background: $looks-tertiary; opacity: 0.5; } @@ -64,7 +64,7 @@ $hover-scale: 1.25; } .selector .trim-line { - border: 1px solid $motion-tertiary; + border: 1px solid $looks-tertiary; } .playhead-container { @@ -83,7 +83,7 @@ $hover-scale: 1.25; */ width: 100%; height: 100%; - border-left: 1px solid $motion-primary; + border-left: 1px solid $looks-tertiary; border-top: none; border-bottom: none; border-right: none; @@ -118,7 +118,7 @@ $hover-scale: 1.25; } .trimmer .trim-handle { - filter: hue-rotate(150deg); + filter: hue-rotate(-240deg) brightness(1.35); } .trim-handle img { diff --git a/src/components/audio-trimmer/icon--handle.svg b/src/components/audio-trimmer/icon--handle.svg index dad1ea751..bdfeafe20 100644 --- a/src/components/audio-trimmer/icon--handle.svg +++ b/src/components/audio-trimmer/icon--handle.svg @@ -8,9 +8,9 @@ - + - + diff --git a/src/components/backpack/backpack.css b/src/components/backpack/backpack.css index 25bc42d45..d89933954 100644 --- a/src/components/backpack/backpack.css +++ b/src/components/backpack/backpack.css @@ -88,7 +88,7 @@ } .more { - background: $motion-primary; + background: $looks-tertiary; color: $ui-white; border: none; outline: none; diff --git a/src/components/browser-modal/browser-modal.css b/src/components/browser-modal/browser-modal.css index 2f4145bf6..18c71e418 100644 --- a/src/components/browser-modal/browser-modal.css +++ b/src/components/browser-modal/browser-modal.css @@ -60,10 +60,10 @@ } .button-row button { - border: 1px solid $motion-primary; + border: 1px solid $looks-tertiary; border-radius: 0.25rem; padding: 0.5rem 2rem; - background: $motion-primary; + background: $looks-tertiary; color: white; font-weight: bold; font-size: 0.875rem; @@ -77,6 +77,6 @@ } .faq-link { - color: $motion-primary; + color: $looks-tertiary; text-decoration: none; } diff --git a/src/components/cards/card.css b/src/components/cards/card.css index ad712e339..5a3467e1e 100644 --- a/src/components/cards/card.css +++ b/src/components/cards/card.css @@ -214,7 +214,7 @@ } .deck-name { - color: $motion-primary; + color: $looks-tertiary; font-weight: bold; font-size: 0.85rem; margin: .625rem 0px; @@ -260,7 +260,7 @@ .see-all-button { cursor: pointer; padding: 0.5rem 1rem; - background-color: $motion-primary; + background-color: $looks-tertiary; color: white; font-weight: bold; border-radius: 0.25rem; diff --git a/src/components/close-button/close-button.css b/src/components/close-button/close-button.css index 48a18f88d..bcd081a00 100644 --- a/src/components/close-button/close-button.css +++ b/src/components/close-button/close-button.css @@ -28,7 +28,7 @@ .small { width: 0.825rem; height: 0.825rem; - background-color: $motion-primary; + background-color: $looks-tertiary; color: $ui-white; } diff --git a/src/components/connection-modal/connection-modal.css b/src/components/connection-modal/connection-modal.css index fde45e015..66b4fcdff 100644 --- a/src/components/connection-modal/connection-modal.css +++ b/src/components/connection-modal/connection-modal.css @@ -80,8 +80,8 @@ border-radius: 0.25rem; font-weight: 600; font-size: 0.85rem; - background: $motion-primary; - border: $motion-primary; + background: $looks-tertiary; + border: $looks-tertiary; color: white; cursor: pointer; } @@ -172,9 +172,9 @@ right: -15px; left: -15px; padding: 5px 5px; - background-color: $motion-primary; + background-color: $looks-tertiary; border-radius: 100%; - box-shadow: 0px 0px 0px 4px $motion-transparent; + box-shadow: 0px 0px 0px 4px $looks-transparent; /* animation: pulse-blue-ring 1s infinite ease-in-out alternate; */ animation: wiggle 0.5s infinite ease-in-out alternate; @@ -182,7 +182,7 @@ @keyframes pulse-blue-ring { 100% { - box-shadow: 0px 0px 0px 8px $motion-light-transparent; + box-shadow: 0px 0px 0px 8px $looks-light-transparent; } } @@ -208,9 +208,9 @@ .bluetooth-centered-icon { position: absolute; padding: 5px 5px; - background-color: $motion-primary; + background-color: $looks-tertiary; border-radius: 100%; - box-shadow: 0px 0px 0px 2px $motion-transparent; + box-shadow: 0px 0px 0px 2px $looks-transparent; } .peripheral-tile-widgets { @@ -220,7 +220,7 @@ .activityArea { height: 165px; - background-color: $motion-light-transparent; + background-color: $looks-light-transparent; display: flex; justify-content: center; align-items: center; @@ -297,7 +297,7 @@ .connection-button { padding: 0.6rem 0.75rem; border-radius: 0.5rem; - background: $motion-primary; + background: $looks-tertiary; color: white; font-weight: 600; font-size: 0.85rem; @@ -309,7 +309,7 @@ } .connection-button:disabled { - background: $motion-transparent; + background: $looks-transparent; } .segmented-button { @@ -390,7 +390,7 @@ display: flex; padding: 0.25rem 0.1rem; border-radius: 1rem; - background: $motion-light-transparent; + background: $looks-light-transparent; } .dots-holder-success { @@ -409,11 +409,11 @@ } .inactive-step-dot { - background: $motion-transparent; + background: $looks-transparent; } .active-step-dot { - background: $motion-primary; + background: $looks-tertiary; } .success-dot { diff --git a/src/components/context-menu/context-menu.css b/src/components/context-menu/context-menu.css index 7f21c92f7..55cd2afea 100644 --- a/src/components/context-menu/context-menu.css +++ b/src/components/context-menu/context-menu.css @@ -25,7 +25,7 @@ } .menu-item:hover { - background: $motion-primary; + background: $looks-tertiary; color: white; } diff --git a/src/components/crash-message/crash-message.css b/src/components/crash-message/crash-message.css index 605f7fdab..daac90c53 100644 --- a/src/components/crash-message/crash-message.css +++ b/src/components/crash-message/crash-message.css @@ -10,12 +10,18 @@ justify-content: center; align-items: center; } + .body { width: 35%; color: white; text-align: center; } +/* scratch-www was overriding the colors */ +.body p, .body h2 { + color: inherit; +} + .reloadButton { border: 1px solid $motion-primary; border-radius: 0.25rem; diff --git a/src/components/custom-procedures/custom-procedures.css b/src/components/custom-procedures/custom-procedures.css index 96cc55d15..8bf499d00 100644 --- a/src/components/custom-procedures/custom-procedures.css +++ b/src/components/custom-procedures/custom-procedures.css @@ -53,8 +53,8 @@ } .option-card:hover { - border: 2px solid $motion-primary; - box-shadow: 0px 0px 0px 4px $motion-transparent; + border: 2px solid $looks-tertiary; + box-shadow: 0px 0px 0px 4px $looks-transparent; } .option-card + .option-card { @@ -92,8 +92,8 @@ } .button-row button.ok-button { - background: $motion-primary; - border: $motion-primary; + background: $looks-tertiary; + border: $looks-tertiary; color: white; } diff --git a/src/components/delete-button/delete-button.css b/src/components/delete-button/delete-button.css index 6abca7ec0..f9535b395 100644 --- a/src/components/delete-button/delete-button.css +++ b/src/components/delete-button/delete-button.css @@ -18,8 +18,8 @@ overflow: hidden; /* Mask the icon animation */ width: 1.75rem; height: 1.75rem; - box-shadow: 0px 0px 0px 2px $motion-transparent; - background-color: $motion-primary; + box-shadow: 0px 0px 0px 2px $looks-transparent; + background-color: $looks-tertiary; color: $ui-white; border-radius: 50%; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; diff --git a/src/components/direction-picker/dial.css b/src/components/direction-picker/dial.css index ce9b7daea..ef1fc2e9d 100644 --- a/src/components/direction-picker/dial.css +++ b/src/components/direction-picker/dial.css @@ -35,7 +35,7 @@ $dial-size: 40px; } .gauge-path { - fill: $motion-transparent; - stroke: $motion-primary; + fill: $looks-transparent; + stroke: $looks-tertiary; stroke-width: 1px; } diff --git a/src/components/direction-picker/icon--all-around.svg b/src/components/direction-picker/icon--all-around.svg index 2412c0b23..06dd9a304 100644 --- a/src/components/direction-picker/icon--all-around.svg +++ b/src/components/direction-picker/icon--all-around.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/direction-picker/icon--dial.svg b/src/components/direction-picker/icon--dial.svg index d4aa8ed82..e2109dfb6 100644 --- a/src/components/direction-picker/icon--dial.svg +++ b/src/components/direction-picker/icon--dial.svg @@ -1 +1 @@ -dial-face \ No newline at end of file +dial-face diff --git a/src/components/direction-picker/icon--dont-rotate.svg b/src/components/direction-picker/icon--dont-rotate.svg index 4796c03af..f33effad5 100644 --- a/src/components/direction-picker/icon--dont-rotate.svg +++ b/src/components/direction-picker/icon--dont-rotate.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/direction-picker/icon--handle.svg b/src/components/direction-picker/icon--handle.svg index 8e5fee6e0..f95cf8443 100644 --- a/src/components/direction-picker/icon--handle.svg +++ b/src/components/direction-picker/icon--handle.svg @@ -1 +1 @@ -01_Dial Elements \ No newline at end of file +01_Dial Elements diff --git a/src/components/direction-picker/icon--left-right.svg b/src/components/direction-picker/icon--left-right.svg index 4525bd6e9..386c3cce0 100644 --- a/src/components/direction-picker/icon--left-right.svg +++ b/src/components/direction-picker/icon--left-right.svg @@ -6,9 +6,9 @@ - - - + + + - \ No newline at end of file + diff --git a/src/components/drag-layer/drag-layer.css b/src/components/drag-layer/drag-layer.css index 926c0ee9f..5fe1cf5f5 100644 --- a/src/components/drag-layer/drag-layer.css +++ b/src/components/drag-layer/drag-layer.css @@ -29,7 +29,7 @@ margin-top: -50%; padding: 0.25rem; - border: 2px solid $motion-primary; + border: 2px solid $looks-tertiary; background: $ui-white; border-radius: 0.5rem; diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index fffef4812..ad8ed36f4 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -35,7 +35,7 @@ } .filter:focus-within { - box-shadow: 0 0 0 .25rem $motion-transparent; + box-shadow: 0 0 0 .25rem $looks-transparent; } /* diff --git a/src/components/filter/icon--filter.svg b/src/components/filter/icon--filter.svg index 400bd6234..e00524981 100644 --- a/src/components/filter/icon--filter.svg +++ b/src/components/filter/icon--filter.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/forms/input.css b/src/components/forms/input.css index ee1010dbc..2d2c1677e 100644 --- a/src/components/forms/input.css +++ b/src/components/forms/input.css @@ -32,12 +32,12 @@ } .input-form:hover { - border-color: $motion-primary; + border-color: $looks-tertiary; } .input-form:focus { - border-color: $motion-primary; - box-shadow: 0 0 0 0.25rem $motion-transparent; + border-color: $looks-tertiary; + box-shadow: 0 0 0 0.25rem $looks-transparent; } .input-small { diff --git a/src/components/green-flag/green-flag.css b/src/components/green-flag/green-flag.css index aa4eb536d..3ca52f61e 100644 --- a/src/components/green-flag/green-flag.css +++ b/src/components/green-flag/green-flag.css @@ -11,9 +11,9 @@ } .green-flag:hover { - background-color: $motion-light-transparent; + background-color: $looks-light-transparent; } .green-flag.is-active { - background-color: $motion-transparent; + background-color: $looks-transparent; } diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css index eeb3989a1..fdea80336 100644 --- a/src/components/gui/gui.css +++ b/src/components/gui/gui.css @@ -124,7 +124,7 @@ .tab.is-selected { height: 90%; - color: $motion-primary; + color: $looks-tertiary; background-color: $ui-white; z-index: 4; /* Make sure selected is always above */ } @@ -236,9 +236,9 @@ left: 0; right: 0; z-index: $z-index-extension-button; - background: $motion-primary; + background: $looks-tertiary; - border: 1px solid $motion-primary; + border: 1px solid $looks-tertiary; box-sizing: content-box; /* To match scratch-block vertical toolbox borders */ } diff --git a/src/components/gui/icon--code.svg b/src/components/gui/icon--code.svg index 390e683b1..1626e2344 100644 --- a/src/components/gui/icon--code.svg +++ b/src/components/gui/icon--code.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/gui/icon--costumes.svg b/src/components/gui/icon--costumes.svg index de50ce39b..bd6914e77 100644 --- a/src/components/gui/icon--costumes.svg +++ b/src/components/gui/icon--costumes.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/gui/icon--sounds.svg b/src/components/gui/icon--sounds.svg index 0de67c537..d789e052e 100644 --- a/src/components/gui/icon--sounds.svg +++ b/src/components/gui/icon--sounds.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/icon-button/icon-button.css b/src/components/icon-button/icon-button.css index 002c817ae..7527804a8 100644 --- a/src/components/icon-button/icon-button.css +++ b/src/components/icon-button/icon-button.css @@ -7,7 +7,7 @@ cursor: pointer; font-size: 0.75rem; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - color: $motion-primary; + color: $looks-tertiary; border-radius: 0.5rem; } @@ -26,5 +26,5 @@ } .container:active { - background-color: $motion-light-transparent; + background-color: $looks-light-transparent; } diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index d4edd136d..33aba52a4 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -29,7 +29,7 @@ .library-item:hover { border-width: 2px; - border-color: $motion-primary; + border-color: $looks-tertiary; } .hidden { diff --git a/src/components/library/library.css b/src/components/library/library.css index 51287daa7..df13ec5f7 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -25,7 +25,7 @@ justify-content: flex-start; align-items: center; height: calc($library-filter-bar-height + 2rem); /* padding */ - background-color: $motion-transparent; + background-color: $looks-transparent; padding: 0 1rem; font-size: .875rem; } diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index f77d8a27e..741c1c071 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -125,7 +125,7 @@ } .feedback-link { - color: $motion-primary; + color: $looks-tertiary; text-decoration: none; } diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 1ed2425ce..89174bb24 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -67,7 +67,7 @@ $sides: 20rem; box-sizing: border-box; width: 100%; - background-color: $motion-primary; + background-color: $looks-tertiary; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1rem; diff --git a/src/components/prompt/prompt.css b/src/components/prompt/prompt.css index c12afc6b7..0b2d9482e 100644 --- a/src/components/prompt/prompt.css +++ b/src/components/prompt/prompt.css @@ -64,8 +64,8 @@ } .button-row button.ok-button { - background: $motion-primary; - border: $motion-primary; + background: $looks-tertiary; + border: $looks-tertiary; color: white; } diff --git a/src/components/question/question.css b/src/components/question/question.css index a0f8116ed..9e077584e 100644 --- a/src/components/question/question.css +++ b/src/components/question/question.css @@ -34,7 +34,7 @@ border-radius: 100%; color: white; - background: $motion-primary; + background: $looks-tertiary; } [dir="ltr"] .question-submit-button { @@ -60,7 +60,7 @@ } .question-input > input:focus { - box-shadow: 0px 0px 0px 3px $motion-transparent; + box-shadow: 0px 0px 0px 3px $looks-transparent; } .question-submit-button-icon { diff --git a/src/components/record-modal/icon--back.svg b/src/components/record-modal/icon--back.svg index 47d09bc7f..82f3b4efd 100644 --- a/src/components/record-modal/icon--back.svg +++ b/src/components/record-modal/icon--back.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/record-modal/icon--play.svg b/src/components/record-modal/icon--play.svg index 9494fccb8..e1871ff70 100644 --- a/src/components/record-modal/icon--play.svg +++ b/src/components/record-modal/icon--play.svg @@ -18,7 +18,7 @@ - + diff --git a/src/components/record-modal/icon--stop-playback.svg b/src/components/record-modal/icon--stop-playback.svg index bf7bb64be..bc01665db 100644 --- a/src/components/record-modal/icon--stop-playback.svg +++ b/src/components/record-modal/icon--stop-playback.svg @@ -18,11 +18,11 @@ - - + + - \ No newline at end of file + diff --git a/src/components/record-modal/record-modal.css b/src/components/record-modal/record-modal.css index 41fc90a96..edce007d5 100644 --- a/src/components/record-modal/record-modal.css +++ b/src/components/record-modal/record-modal.css @@ -47,7 +47,7 @@ } .playing-text { - color: $motion-primary; + color: $looks-tertiary; } .recording-text { @@ -89,12 +89,12 @@ border: 1px solid $ui-black-transparent; font-weight: 600; font-size: 0.85rem; - color: $motion-primary; + color: $looks-tertiary; } .button-row button.ok-button { - background: $motion-primary; - border: $motion-primary; + background: $looks-tertiary; + border: $looks-tertiary; color: white; } diff --git a/src/components/slider-prompt/slider-prompt.css b/src/components/slider-prompt/slider-prompt.css index c1ae4854a..0b643888c 100644 --- a/src/components/slider-prompt/slider-prompt.css +++ b/src/components/slider-prompt/slider-prompt.css @@ -41,8 +41,8 @@ } .button-row button.ok-button { - background: $motion-primary; - border: $motion-primary; + background: $looks-tertiary; + border: $looks-tertiary; color: white; } diff --git a/src/components/sound-editor/icon--copy-to-new.svg b/src/components/sound-editor/icon--copy-to-new.svg index d5f462117..8e08e6eeb 100644 --- a/src/components/sound-editor/icon--copy-to-new.svg +++ b/src/components/sound-editor/icon--copy-to-new.svg @@ -4,6 +4,6 @@ Sound/General/Copy to New Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/sound-editor/icon--copy.svg b/src/components/sound-editor/icon--copy.svg index 7bac62c1b..50e064a2f 100644 --- a/src/components/sound-editor/icon--copy.svg +++ b/src/components/sound-editor/icon--copy.svg @@ -4,11 +4,11 @@ Sound/General/Copy Created with Sketch. - + - + - \ No newline at end of file + diff --git a/src/components/sound-editor/icon--delete.svg b/src/components/sound-editor/icon--delete.svg index 8f662201e..a0a263d69 100644 --- a/src/components/sound-editor/icon--delete.svg +++ b/src/components/sound-editor/icon--delete.svg @@ -11,8 +11,8 @@ - + - \ No newline at end of file + diff --git a/src/components/sound-editor/icon--paste.svg b/src/components/sound-editor/icon--paste.svg index 53ca104c1..ce09f40d8 100644 --- a/src/components/sound-editor/icon--paste.svg +++ b/src/components/sound-editor/icon--paste.svg @@ -4,6 +4,6 @@ Sound/General/Paste Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/sound-editor/icon--redo.svg b/src/components/sound-editor/icon--redo.svg index fdc3b1780..aeeb00c5d 100644 --- a/src/components/sound-editor/icon--redo.svg +++ b/src/components/sound-editor/icon--redo.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sound-editor/icon--undo.svg b/src/components/sound-editor/icon--undo.svg index 54ff88ee4..f6b3fc7f8 100644 --- a/src/components/sound-editor/icon--undo.svg +++ b/src/components/sound-editor/icon--undo.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sound-editor/sound-editor.css b/src/components/sound-editor/sound-editor.css index 7a791e6ab..be11787a9 100644 --- a/src/components/sound-editor/sound-editor.css +++ b/src/components/sound-editor/sound-editor.css @@ -95,7 +95,7 @@ $border-radius: 0.25rem; height: 3rem; width: 3rem; outline: none; - background: $motion-primary; + background: $looks-tertiary; border-radius: 100%; border: 4px solid $ui-white-dim; cursor: pointer; diff --git a/src/components/spinner/spinner.css b/src/components/spinner/spinner.css index 16d323f5f..51c4c2706 100644 --- a/src/components/spinner/spinner.css +++ b/src/components/spinner/spinner.css @@ -83,8 +83,8 @@ } .spinner.primary { - border-color: $motion-transparent; + border-color: $looks-transparent; } .spinner.primary::after { - border-top-color: $motion-primary; + border-top-color: $looks-tertiary; } diff --git a/src/components/sprite-info/icon--hide.svg b/src/components/sprite-info/icon--hide.svg index 2fd66b830..4e9dc7a8c 100644 --- a/src/components/sprite-info/icon--hide.svg +++ b/src/components/sprite-info/icon--hide.svg @@ -5,10 +5,10 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/sprite-info/icon--show.svg b/src/components/sprite-info/icon--show.svg index fe54ed568..0eed3fd98 100644 --- a/src/components/sprite-info/icon--show.svg +++ b/src/components/sprite-info/icon--show.svg @@ -5,11 +5,11 @@ Created with Sketch. - + - \ No newline at end of file + diff --git a/src/components/sprite-info/sprite-info.css b/src/components/sprite-info/sprite-info.css index 93ca30f40..4ad343569 100644 --- a/src/components/sprite-info/sprite-info.css +++ b/src/components/sprite-info/sprite-info.css @@ -83,7 +83,7 @@ } .radio-first:focus { - border-color: $motion-primary; + border-color: $looks-tertiary; box-shadow: inset 0 0 0 -2px $ui-black-transparent; } @@ -107,7 +107,7 @@ } .radio-last:focus { - border-color: $motion-primary; + border-color: $looks-tertiary; box-shadow: inset 0 0 0 -2px $ui-black-transparent; } @@ -126,7 +126,7 @@ } .rotation-select:focus { - border-color: $motion-primary; + border-color: $looks-tertiary; box-shadow: inset 0 0 0 1px $ui-black-transparent); } diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css index dffc18902..1af628ca2 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.css +++ b/src/components/sprite-selector-item/sprite-selector-item.css @@ -23,13 +23,13 @@ } .sprite-selector-item.is-selected { - box-shadow: 0px 0px 0px 4px $motion-transparent; - border: 2px solid $motion-primary; + box-shadow: 0px 0px 0px 4px $looks-transparent; + border: 2px solid $looks-tertiary; background: $ui-white; } .sprite-selector-item:hover { - border: 2px solid $motion-primary; + border: 2px solid $looks-tertiary; background: $ui-white; } @@ -88,7 +88,7 @@ } .is-selected .sprite-info { - background: $motion-primary; + background: $looks-tertiary; color: $ui-white; } diff --git a/src/components/stage-header/icon--large-stage.svg b/src/components/stage-header/icon--large-stage.svg index ed4361ca3..e069a0951 100644 --- a/src/components/stage-header/icon--large-stage.svg +++ b/src/components/stage-header/icon--large-stage.svg @@ -5,12 +5,12 @@ Created with Sketch. - + - + - \ No newline at end of file + diff --git a/src/components/stage-header/icon--small-stage.svg b/src/components/stage-header/icon--small-stage.svg index 377939959..9ddc61cef 100644 --- a/src/components/stage-header/icon--small-stage.svg +++ b/src/components/stage-header/icon--small-stage.svg @@ -5,12 +5,12 @@ Created with Sketch. - + - + - \ No newline at end of file + diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css index 742898bd3..921c2a2a3 100644 --- a/src/components/stage-selector/stage-selector.css +++ b/src/components/stage-selector/stage-selector.css @@ -26,12 +26,12 @@ $header-height: calc($stage-menu-height - 2px); .stage-selector.is-selected { border-top-left-radius: .625rem; border-top-right-radius: .625rem; - border-color: $motion-primary; - box-shadow: 0px 0px 0px 4px $motion-transparent; + border-color: $looks-tertiary; + box-shadow: 0px 0px 0px 4px $looks-transparent; } .stage-selector:hover { - border-color: $motion-primary; + border-color: $looks-tertiary; } .header { @@ -60,7 +60,7 @@ $header-height: calc($stage-menu-height - 2px); } .stage-selector.is-selected .header { - background-color: $motion-primary; + background-color: $looks-tertiary; } .stage-selector.is-selected .header-title { diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css index cb8b4f630..e0e74cad8 100644 --- a/src/components/stage/stage.css +++ b/src/components/stage/stage.css @@ -108,8 +108,8 @@ stage doesn't "spill" out from under its rounded corners. instead, shift these o } .frame { - background: $motion-transparent; - border: 2px solid $motion-primary; + background: $looks-transparent; + border: 2px solid $looks-tertiary; border-radius: 0.5rem; animation-name: flash; animation-duration: 0.75s; diff --git a/src/components/stop-all/stop-all.css b/src/components/stop-all/stop-all.css index da0bcc7b3..ab89a5c70 100644 --- a/src/components/stop-all/stop-all.css +++ b/src/components/stop-all/stop-all.css @@ -10,7 +10,7 @@ } .stop-all:hover { - background-color: $motion-light-transparent; + background-color: $looks-light-transparent; } .stop-all { diff --git a/src/components/tag-button/tag-button.css b/src/components/tag-button/tag-button.css index b77004513..a84017ae0 100644 --- a/src/components/tag-button/tag-button.css +++ b/src/components/tag-button/tag-button.css @@ -3,7 +3,7 @@ .tag-button { padding: .625rem 1rem; - background: $motion-primary; + background: $looks-tertiary; border-radius: 1.375rem; color: $ui-white; height: $library-filter-bar-height; diff --git a/src/components/telemetry-modal/telemetry-modal.css b/src/components/telemetry-modal/telemetry-modal.css index 73f2d6a1c..50165c91a 100644 --- a/src/components/telemetry-modal/telemetry-modal.css +++ b/src/components/telemetry-modal/telemetry-modal.css @@ -35,7 +35,7 @@ $ui-gray: hsla(0, 0%, 95%, 1); .illustration { width: 100%; height: 123px; - background-color: $motion-primary; + background-color: $looks-tertiary; background-image: url('./telemetry-modal-header.png'); background-size: cover; } @@ -46,7 +46,7 @@ $ui-gray: hsla(0, 0%, 95%, 1); } .privacy-policy-link { - color: $motion-primary; + color: $looks-tertiary; text-decoration: none; } @@ -92,14 +92,14 @@ $ui-gray: hsla(0, 0%, 95%, 1); .radio-buttons input[type="radio"]:checked { transition: all .25s ease; background-color: $ui-white; - border: 1px solid $motion-primary; + border: 1px solid $looks-tertiary; } .radio-buttons input[type="radio"]:checked::after { display: block; margin: 0.125rem; border-radius: 50%; - background-color: $motion-primary; + background-color: $looks-tertiary; width: .625rem; height: .625rem; content: ""; @@ -127,11 +127,11 @@ $ui-gray: hsla(0, 0%, 95%, 1); } .button-row button { - border: 1px solid $motion-primary; + border: 1px solid $looks-tertiary; border-radius: 0.25rem; padding: 0.5rem 1.5rem; color: white; - background: $motion-primary; + background: $looks-tertiary; font-weight: bold; font-size: .875rem; cursor: pointer; @@ -139,7 +139,7 @@ $ui-gray: hsla(0, 0%, 95%, 1); .button-row button:hover { background: $extensions-primary; - box-shadow: 0 0 0 6px $motion-transparent; + box-shadow: 0 0 0 6px $looks-transparent; } .button-row button:disabled { diff --git a/src/components/webgl-modal/webgl-modal.css b/src/components/webgl-modal/webgl-modal.css index 85fcc0d01..04d85b3d9 100644 --- a/src/components/webgl-modal/webgl-modal.css +++ b/src/components/webgl-modal/webgl-modal.css @@ -29,7 +29,7 @@ .illustration { width: 100%; height: 208px; - background-color: $motion-primary; + background-color: $looks-tertiary; background-image: url('./unsupported.png'); background-size: cover; } @@ -54,10 +54,10 @@ } .button-row button { - border: 1px solid $motion-primary; + border: 1px solid $looks-tertiary; border-radius: 0.25rem; padding: 0.5rem 2rem; - background: $motion-primary; + background: $looks-tertiary; color: white; font-weight: bold; font-size: 0.875rem; @@ -70,6 +70,6 @@ } .faq-link { - color: $motion-primary; + color: $looks-tertiary; text-decoration: none; } diff --git a/src/css/colors.css b/src/css/colors.css index 5f14fd307..7089f45fd 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -16,10 +16,11 @@ $text-primary-transparent: hsla(225, 15%, 40%, 0.75); $motion-primary: hsla(215, 100%, 65%, 1); /* #4C97FF */ $motion-tertiary: hsla(215, 60%, 50%, 1); /* #3373CC */ -$motion-transparent: hsla(215, 100%, 65%, 0.35); /* 35% transparent version of motion-primary */ -$motion-light-transparent: hsla(215, 100%, 65%, 0.15); /* 15% transparent version of motion-primary */ $looks-tertiary: hsla(260, 54%, 54%, 1); /* #774DCB */ +$looks-transparent: hsla(260, 54%, 54%, 0.35); /* 35% transparent version of motion-tertiary */ +$looks-light-transparent: hsla(260, 54%, 54%, 0.15); /* 15% transparent version of motion-primary */ +$looks-tertiary-dark: hsla(263, 54%, 46%, 1); /* #6736B5 */ $red-primary: hsla(20, 100%, 55%, 1); /* #FF661A */ $red-tertiary: hsla(20, 100%, 45%, 1); /* #E64D00 */ From 7053aa66208d30fb5e33e693a3da10c0a1381ad2 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Tue, 28 Mar 2023 11:30:58 -0400 Subject: [PATCH 16/21] chore: clean up formatting --- src/components/action-menu/action-menu.css | 1 - src/components/asset-panel/icon--sound-rtl.svg | 2 +- src/css/colors.css | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/action-menu/action-menu.css b/src/components/action-menu/action-menu.css index 070c1713e..71d078576 100644 --- a/src/components/action-menu/action-menu.css +++ b/src/components/action-menu/action-menu.css @@ -73,7 +73,6 @@ button::-moz-focus-inner { overflow-y: hidden; background: $looks-tertiary-dark; - border-top-left-radius: $more-button-size; border-top-right-radius: $more-button-size; width: $more-button-size; diff --git a/src/components/asset-panel/icon--sound-rtl.svg b/src/components/asset-panel/icon--sound-rtl.svg index 108f7aa1b..adf850173 100644 --- a/src/components/asset-panel/icon--sound-rtl.svg +++ b/src/components/asset-panel/icon--sound-rtl.svg @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/css/colors.css b/src/css/colors.css index 7089f45fd..d4457ed6b 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -18,8 +18,8 @@ $motion-primary: hsla(215, 100%, 65%, 1); /* #4C97FF */ $motion-tertiary: hsla(215, 60%, 50%, 1); /* #3373CC */ $looks-tertiary: hsla(260, 54%, 54%, 1); /* #774DCB */ -$looks-transparent: hsla(260, 54%, 54%, 0.35); /* 35% transparent version of motion-tertiary */ -$looks-light-transparent: hsla(260, 54%, 54%, 0.15); /* 15% transparent version of motion-primary */ +$looks-transparent: hsla(260, 54%, 54%, 0.35); /* 35% transparent version of looks-tertiary */ +$looks-light-transparent: hsla(260, 54%, 54%, 0.15); /* 15% transparent version of looks-tertiary */ $looks-tertiary-dark: hsla(263, 54%, 46%, 1); /* #6736B5 */ $red-primary: hsla(20, 100%, 55%, 1); /* #FF661A */ From 8c4526d5f1b5f151d6a0acebb2310d10ed76e16b Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 31 Mar 2023 13:59:57 -0400 Subject: [PATCH 17/21] chore: update names of themes --- src/lib/themes/index.js | 8 ++++---- test/integration/menu-bar.test.js | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js index 6eee2244a..b9a1f1973 100644 --- a/src/lib/themes/index.js +++ b/src/lib/themes/index.js @@ -23,8 +23,8 @@ const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors); const messages = defineMessages({ [DEFAULT_THEME]: { id: 'gui.theme.default', - defaultMessage: 'Default', - description: 'label for default theme' + defaultMessage: 'Original', + description: 'label for original theme' }, [DARK_THEME]: { id: 'gui.theme.dark', @@ -33,8 +33,8 @@ const messages = defineMessages({ }, [HIGH_CONTRAST_THEME]: { id: 'gui.theme.highContrast', - defaultMessage: 'High Text Contrast', - description: 'label for high constast theme' + defaultMessage: 'High Contrast', + description: 'label for high theme' } }); diff --git a/test/integration/menu-bar.test.js b/test/integration/menu-bar.test.js index e2e1853ec..4c6b1fd4e 100644 --- a/test/integration/menu-bar.test.js +++ b/test/integration/menu-bar.test.js @@ -102,14 +102,14 @@ describe('Menu bar settings', () => { await loadUri(uri); await clickXpath('//img[@alt="Color theme"]'); - await findByText('Default', scope.menuBar); - await findByText('High Text Contrast', scope.menuBar); + await findByText('Original', scope.menuBar); + await findByText('High Contrast', scope.menuBar); }); - test('Theme picker switches to high text contrast', async () => { + test('Theme picker switches to high contrast', async () => { await loadUri(uri); await clickXpath('//img[@alt="Color theme"]'); - await clickText('High Text Contrast', scope.menuBar); + await clickText('High Contrast', scope.menuBar); // There is a tiny delay for the color theme to be applied to the categories. await driver.wait(async () => { @@ -122,6 +122,6 @@ describe('Menu bar settings', () => { // returns the value. Locally I am seeing 'rgba(128, 181, 255, 1)', // but this is a bit flexible just in case. return /128,\s?181,\s?255/.test(color) || color.includes('80B5FF'); - }, 5000, 'Motion category color does not match high text contrast theme'); + }, 5000, 'Motion category color does not match high contrast theme'); }); }); From bb00935e14169e936212d08be5ae3b4b6f26dc32 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 14 Apr 2023 16:47:15 -0400 Subject: [PATCH 18/21] feat: update button styles --- .../direction-picker/direction-picker.css | 30 +------ .../direction-picker/direction-picker.jsx | 63 ++++++-------- .../direction-picker/icon--left-right.svg | 10 +-- src/components/sound-editor/sound-editor.css | 9 +- src/components/sprite-info/sprite-info.css | 64 -------------- src/components/sprite-info/sprite-info.jsx | 70 ++++++--------- src/components/stage-header/stage-header.css | 32 ++----- src/components/stage-header/stage-header.jsx | 82 ++++++++---------- .../toggle-buttons/toggle-buttons.css | 85 +++++++++++++++++++ .../toggle-buttons/toggle-buttons.jsx | 52 ++++++++++++ src/containers/sprite-info.jsx | 18 +--- test/integration/stage-size.test.js | 4 +- 12 files changed, 245 insertions(+), 274 deletions(-) create mode 100644 src/components/toggle-buttons/toggle-buttons.css create mode 100644 src/components/toggle-buttons/toggle-buttons.jsx diff --git a/src/components/direction-picker/direction-picker.css b/src/components/direction-picker/direction-picker.css index 501ff0217..e39721317 100644 --- a/src/components/direction-picker/direction-picker.css +++ b/src/components/direction-picker/direction-picker.css @@ -1,32 +1,4 @@ -@import "../../css/colors.css"; - .button-row { - display: flex; - flex-direction: row; justify-content: center; - -} - -.icon-button { - margin: 0.25rem; - border: none; - background: none; - outline: none; - cursor: pointer; - user-select: none; -} - -.icon-button:active > img { - width: 20px; - height: 20px; - transform: scale(1.15); -} - -.icon-button > img { - transition: transform 0.1s; - filter: grayscale(100%); -} - -.icon-button.active > img { - filter: none; + margin-bottom: 1rem; } diff --git a/src/components/direction-picker/direction-picker.jsx b/src/components/direction-picker/direction-picker.jsx index 84a15db7a..cf29c416e 100644 --- a/src/components/direction-picker/direction-picker.jsx +++ b/src/components/direction-picker/direction-picker.jsx @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; import Popover from 'react-popover'; import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl'; @@ -8,6 +7,7 @@ import Label from '../forms/label.jsx'; import Input from '../forms/input.jsx'; import BufferedInputHOC from '../forms/buffered-input-hoc.jsx'; import Dial from './dial.jsx'; +import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx'; import styles from './direction-picker.css'; @@ -62,44 +62,29 @@ const DirectionPicker = props => ( direction={props.direction} onChange={props.onChangeDirection} /> -
- - - -
+ } isOpen={props.popoverOpen} diff --git a/src/components/direction-picker/icon--left-right.svg b/src/components/direction-picker/icon--left-right.svg index 386c3cce0..4d2cacb51 100644 --- a/src/components/direction-picker/icon--left-right.svg +++ b/src/components/direction-picker/icon--left-right.svg @@ -4,11 +4,9 @@ left-right-active Created with Sketch. - - - - - - + + + + diff --git a/src/components/sound-editor/sound-editor.css b/src/components/sound-editor/sound-editor.css index be11787a9..6e18d811c 100644 --- a/src/components/sound-editor/sound-editor.css +++ b/src/components/sound-editor/sound-editor.css @@ -79,11 +79,18 @@ $border-radius: 0.25rem; border: 1px solid $ui-black-transparent; cursor: pointer; font-size: 0.85rem; - transition: 0.2s; user-select: none; margin: 0px; } +.button:active { + background: $looks-transparent; +} + +.button:focus { + border-color: $looks-tertiary; +} + .button > img { flex-grow: 1; max-width: 100%; diff --git a/src/components/sprite-info/sprite-info.css b/src/components/sprite-info/sprite-info.css index 4ad343569..876acfd25 100644 --- a/src/components/sprite-info/sprite-info.css +++ b/src/components/sprite-info/sprite-info.css @@ -47,70 +47,6 @@ user-select: none; } -.icon { - width: 100%; - height: 100%; -} - -/* @todo: refactor radio divs to input */ -.radio-wrapper { - white-space: nowrap; /* make sure visibilty buttons don't wrap */ -} - -.radio { - filter: saturate(0); - cursor: pointer; -} -.radio.is-active { - filter: none; -} -.radio.is-disabled { - cursor: default; -} - -.radio-first { - border: 1px solid $ui-black-transparent; -} - -[dir="ltr"] .radio-first { - border-top-left-radius: $form-radius; - border-bottom-left-radius: $form-radius; -} - -[dir="rtl"] .radio-first { - border-top-right-radius: $form-radius; - border-bottom-right-radius: $form-radius; -} - -.radio-first:focus { - border-color: $looks-tertiary; - box-shadow: inset 0 0 0 -2px $ui-black-transparent; -} - -.radio-last { - border-bottom: 1px solid $ui-black-transparent; - border-top: 1px solid $ui-black-transparent; -} - -[dir="ltr"] .radio-last { - border-right: 1px solid $ui-black-transparent; - border-left: 1px solid $ui-black-transparent; - border-top-right-radius: $form-radius; - border-bottom-right-radius: $form-radius; -} - -[dir="rtl"] .radio-last { - border-left: 1px solid $ui-black-transparent; - border-right: 1px solid $ui-black-transparent; - border-top-left-radius: $form-radius; - border-bottom-left-radius: $form-radius; -} - -.radio-last:focus { - border-color: $looks-tertiary; - box-shadow: inset 0 0 0 -2px $ui-black-transparent; -} - .icon { width: 100%; height: 100%; diff --git a/src/components/sprite-info/sprite-info.jsx b/src/components/sprite-info/sprite-info.jsx index 09c94d016..6f0fcd1db 100644 --- a/src/components/sprite-info/sprite-info.jsx +++ b/src/components/sprite-info/sprite-info.jsx @@ -19,6 +19,7 @@ import xIcon from './icon--x.svg'; import yIcon from './icon--y.svg'; import showIcon from './icon--show.svg'; import hideIcon from './icon--hide.svg'; +import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx'; const BufferedInput = BufferedInputHOC(Input); @@ -27,6 +28,16 @@ const messages = defineMessages({ id: 'gui.SpriteInfo.spritePlaceholder', defaultMessage: 'Name', description: 'Placeholder text for sprite name' + }, + showSpriteAction: { + id: 'gui.SpriteInfo.showSpriteAction', + defaultMessage: 'Show sprite', + description: 'Tooltip for show sprite button' + }, + hideSpriteAction: { + id: 'gui.SpriteInfo.hideSpriteAction', + defaultMessage: 'Hide sprite', + description: 'Tooltip for hide sprite button' } }); @@ -185,46 +196,23 @@ class SpriteInfo extends React.Component { /> : null } -
-
- -
-
- -
-
+
) : ( - +
+ +
); header = ( @@ -110,40 +112,24 @@ const StageHeaderComponent = function (props) { [] ) : (
-
- -
-
- -
+
); header = ( diff --git a/src/components/toggle-buttons/toggle-buttons.css b/src/components/toggle-buttons/toggle-buttons.css new file mode 100644 index 000000000..c1abacafd --- /dev/null +++ b/src/components/toggle-buttons/toggle-buttons.css @@ -0,0 +1,85 @@ +@import "../../css/units.css"; +@import "../../css/colors.css"; + +.row { + display: flex; + flex-direction: row; +} + +.button { + margin: 0; + border: 1px solid $ui-black-transparent; + background: none; + outline: none; + cursor: pointer; + user-select: none; + position: relative; + width: 34px; + height: 34px; + display: flex; + justify-content: center; + align-items: center; +} + +.button > img { + display: block; + transition: transform 0.1s; + filter: grayscale(100%) opacity(.5); +} + +.button:not(:last-child) { + border-right: none; +} + +[dir="rtl"] .button:not(:last-child) { + border-left: none; + border-right: 1px solid $ui-black-transparent; +} + +.button:focus::before { + content: ""; + position: absolute; + top: -1px; + right: -1px; + bottom: -1px; + left: -1px; + border: 1px solid $looks-tertiary; +} + +/* round corners for first and last buttons */ + +.button:first-child, +[dir="rtl"] .button:last-child, +.button:first-child:focus::before, +[dir="rtl"] .button:last-child:focus::before { + border-radius: $form-radius 0 0 $form-radius; +} + +.button:last-child, +[dir="rtl"] .button:first-child, +.button:last-child:focus::before, +[dir="rtl"] .button:first-child:focus::before { + border-radius: 0 $form-radius $form-radius 0; +} + +/* selected button styling */ + +.button[aria-pressed='true'] { + background-color: $looks-light-transparent; +} + +.button[aria-pressed='true'] > img { + filter: none; +} + +/* pressed button styling */ + +.button:active { + background-color: $looks-transparent; +} + +/* disabled styling */ + +.disabled .button { + cursor: default; +} diff --git a/src/components/toggle-buttons/toggle-buttons.jsx b/src/components/toggle-buttons/toggle-buttons.jsx new file mode 100644 index 000000000..f8d1aabcc --- /dev/null +++ b/src/components/toggle-buttons/toggle-buttons.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import styles from './toggle-buttons.css'; + +const ToggleButtons = ({buttons, className, disabled}) => ( +
+ {buttons.map((button, index) => ( + + ))} +
+); + +ToggleButtons.propTypes = { + buttons: PropTypes.arrayOf(PropTypes.shape({ + title: PropTypes.string.isRequired, + handleClick: PropTypes.func.isRequired, + icon: PropTypes.string.isRequired, + iconClassName: PropTypes.string, + isSelected: PropTypes.bool + })), + className: PropTypes.string, + disabled: PropTypes.bool +}; + +ToggleButtons.defaultProps = { + disabled: false +}; + +export default ToggleButtons; diff --git a/src/containers/sprite-info.jsx b/src/containers/sprite-info.jsx index fe0a7267e..2499ed104 100644 --- a/src/containers/sprite-info.jsx +++ b/src/containers/sprite-info.jsx @@ -9,9 +9,7 @@ class SpriteInfo extends React.Component { super(props); bindAll(this, [ 'handleClickVisible', - 'handleClickNotVisible', - 'handlePressVisible', - 'handlePressNotVisible' + 'handleClickNotVisible' ]); } handleClickVisible (e) { @@ -22,26 +20,12 @@ class SpriteInfo extends React.Component { e.preventDefault(); this.props.onChangeVisibility(false); } - handlePressVisible (e) { - if (e.key === ' ' || e.key === 'Enter') { - e.preventDefault(); - this.props.onChangeVisibility(true); - } - } - handlePressNotVisible (e) { - if (e.key === ' ' || e.key === 'Enter') { - e.preventDefault(); - this.props.onChangeVisibility(false); - } - } render () { return ( ); } diff --git a/test/integration/stage-size.test.js b/test/integration/stage-size.test.js index 72de9c5bb..ca2359050 100644 --- a/test/integration/stage-size.test.js +++ b/test/integration/stage-size.test.js @@ -35,10 +35,10 @@ describe('Loading scratch gui', () => { await clickText('delete', scope.spriteTile); // Go to small stage mode - await clickXpath('//img[@alt="Switch to small stage"]'); + await clickXpath('//button[@title="Switch to small stage"]'); // Confirm app still working - await clickXpath('//img[@alt="Switch to large stage"]'); + await clickXpath('//button[@title="Switch to large stage"]'); const logs = await getLogs(); await expect(logs).toEqual([]); From 0b15a41c8feb7b6b6f527f00502fbb6b1ca57519 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Tue, 18 Apr 2023 17:17:09 -0400 Subject: [PATCH 19/21] test: unit test for toggle-buttons --- .../direction-picker/direction-picker.jsx | 2 +- src/components/sound-editor/sound-editor.css | 52 ++++++++++--------- src/components/stage-header/stage-header.css | 4 ++ src/components/stage-header/stage-header.jsx | 2 +- .../toggle-buttons/toggle-buttons.jsx | 1 + test/unit/components/toggle-buttons.test.jsx | 52 +++++++++++++++++++ 6 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 test/unit/components/toggle-buttons.test.jsx diff --git a/src/components/direction-picker/direction-picker.jsx b/src/components/direction-picker/direction-picker.jsx index cf29c416e..3f06f3dc4 100644 --- a/src/components/direction-picker/direction-picker.jsx +++ b/src/components/direction-picker/direction-picker.jsx @@ -6,8 +6,8 @@ import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-int import Label from '../forms/label.jsx'; import Input from '../forms/input.jsx'; import BufferedInputHOC from '../forms/buffered-input-hoc.jsx'; -import Dial from './dial.jsx'; import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx'; +import Dial from './dial.jsx'; import styles from './direction-picker.css'; diff --git a/src/components/sound-editor/sound-editor.css b/src/components/sound-editor/sound-editor.css index 6e18d811c..fcf2d15ee 100644 --- a/src/components/sound-editor/sound-editor.css +++ b/src/components/sound-editor/sound-editor.css @@ -81,14 +81,22 @@ $border-radius: 0.25rem; font-size: 0.85rem; user-select: none; margin: 0px; + position: relative; } .button:active { background: $looks-transparent; } -.button:focus { - border-color: $looks-tertiary; +.button:focus::before { + content: ""; + position: absolute; + top: -1px; + right: -1px; + bottom: -1px; + left: -1px; + border: 1px solid $looks-tertiary; + z-index: 1; } .button > img { @@ -198,33 +206,27 @@ $border-radius: 0.25rem; border-radius: 0; } -[dir="ltr"] .button-group .button { - border-left: none; -} -[dir="rtl"] .button-group .button { +.button:not(:last-child) { border-right: none; } -[dir="ltr"] .button-group .button:last-of-type { - border-top-right-radius: $border-radius; - border-bottom-right-radius: $border-radius; -} - -[dir="ltr"] .button-group .button:first-of-type { - border-left: 1px solid $ui-black-transparent; - border-top-left-radius: $border-radius; - border-bottom-left-radius: $border-radius; -} - -[dir="rtl"] .button-group .button:last-of-type { - border-top-left-radius: $border-radius; - border-bottom-left-radius: $border-radius; -} - -[dir="rtl"] .button-group .button:first-of-type { +[dir="rtl"] .button:not(:last-child) { + border-left: none; border-right: 1px solid $ui-black-transparent; - border-top-right-radius: $border-radius; - border-bottom-right-radius: $border-radius; +} + +.button-group .button:first-child, +[dir="rtl"] .button-group .button:last-child, +.button-group .button:first-child:focus::before, +[dir="rtl"] .button-group .button:last-child:focus::before { + border-radius: $form-radius 0 0 $form-radius; +} + +.button-group .button:last-child, +[dir="rtl"] .button-group .button:first-child, +.button-group .button:last-child:focus::before, +[dir="rtl"] .button-group .button:first-child:focus::before { + border-radius: 0 $form-radius $form-radius 0; } .button:disabled > img { diff --git a/src/components/stage-header/stage-header.css b/src/components/stage-header/stage-header.css index 7e654cb94..07ac21213 100644 --- a/src/components/stage-header/stage-header.css +++ b/src/components/stage-header/stage-header.css @@ -66,6 +66,10 @@ cursor: pointer; } +.stage-button:active { + background-color: $looks-transparent; +} + .stage-button-icon { width: 100%; height: 100%; diff --git a/src/components/stage-header/stage-header.jsx b/src/components/stage-header/stage-header.jsx index f0a572a4c..291acccae 100644 --- a/src/components/stage-header/stage-header.jsx +++ b/src/components/stage-header/stage-header.jsx @@ -6,6 +6,7 @@ import VM from 'scratch-vm'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; +import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx'; import Controls from '../../containers/controls.jsx'; import {getStageDimensions} from '../../lib/screen-utils'; import {STAGE_SIZE_MODES} from '../../lib/layout-constants'; @@ -17,7 +18,6 @@ import unFullScreenIcon from './icon--unfullscreen.svg'; import scratchLogo from '../menu-bar/scratch-logo.svg'; import styles from './stage-header.css'; -import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx'; const messages = defineMessages({ largeStageSizeMessage: { diff --git a/src/components/toggle-buttons/toggle-buttons.jsx b/src/components/toggle-buttons/toggle-buttons.jsx index f8d1aabcc..f09f60e86 100644 --- a/src/components/toggle-buttons/toggle-buttons.jsx +++ b/src/components/toggle-buttons/toggle-buttons.jsx @@ -22,6 +22,7 @@ const ToggleButtons = ({buttons, className, disabled}) => ( aria-label={button.title} aria-pressed={button.isSelected} onClick={button.handleClick} + disabled={disabled} > { + test('renders multiple buttons', () => { + const component = shallow( {}, + icon: 'Button 1 icon' + }, + { + title: 'Button 2', + handleClick: () => {}, + icon: 'Button 2 icon' + } + ]} + />); + + const buttons = component.find('button'); + + expect(buttons).toHaveLength(2); + expect(buttons.get(0).props.title).toBe('Button 1'); + expect(buttons.get(1).props.title).toBe('Button 2'); + }); + + test('calls correct click handler', () => { + const onClick1 = jest.fn(); + const onClick2 = jest.fn(); + const component = shallow(); + const button2 = component.find('button[title="Button 2"]'); + button2.simulate('click'); + + expect(onClick2).toHaveBeenCalled(); + expect(onClick1).not.toHaveBeenCalled(); + }); +}); From 068e24989ead3a23e6bad18bbd8d47e4d6e30787 Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Fri, 5 May 2023 16:09:54 -0400 Subject: [PATCH 20/21] fix: use looks secondary rather than tertiary for purple accent --- src/components/action-menu/action-menu.css | 6 +++--- src/components/asset-panel/selector.css | 2 +- src/components/audio-trimmer/audio-trimmer.css | 6 +++--- src/components/audio-trimmer/icon--handle.svg | 4 ++-- src/components/backpack/backpack.css | 2 +- src/components/browser-modal/browser-modal.css | 6 +++--- src/components/cards/card.css | 4 ++-- src/components/close-button/close-button.css | 2 +- src/components/connection-modal/connection-modal.css | 12 ++++++------ src/components/context-menu/context-menu.css | 2 +- .../custom-procedures/custom-procedures.css | 6 +++--- src/components/delete-button/delete-button.css | 2 +- src/components/direction-picker/dial.css | 2 +- src/components/direction-picker/icon--all-around.svg | 2 +- .../direction-picker/icon--dont-rotate.svg | 2 +- src/components/direction-picker/icon--handle.svg | 2 +- src/components/direction-picker/icon--left-right.svg | 6 +++--- src/components/drag-layer/drag-layer.css | 2 +- src/components/filter/icon--filter.svg | 2 +- src/components/forms/input.css | 4 ++-- src/components/gui/gui.css | 6 +++--- src/components/gui/icon--code.svg | 2 +- src/components/gui/icon--costumes.svg | 2 +- src/components/gui/icon--sounds.svg | 2 +- src/components/icon-button/icon-button.css | 2 +- src/components/library-item/library-item.css | 2 +- src/components/menu-bar/menu-bar.css | 4 ++-- src/components/menu/menu.css | 2 +- src/components/modal/modal.css | 2 +- src/components/prompt/prompt.css | 4 ++-- src/components/question/question.css | 2 +- src/components/record-modal/icon--back.svg | 2 +- src/components/record-modal/icon--play.svg | 2 +- src/components/record-modal/icon--stop-playback.svg | 2 +- src/components/record-modal/record-modal.css | 8 ++++---- src/components/slider-prompt/slider-prompt.css | 4 ++-- src/components/sound-editor/icon--copy-to-new.svg | 2 +- src/components/sound-editor/icon--copy.svg | 4 ++-- src/components/sound-editor/icon--delete.svg | 2 +- src/components/sound-editor/icon--paste.svg | 2 +- src/components/sound-editor/icon--redo.svg | 2 +- src/components/sound-editor/icon--undo.svg | 2 +- src/components/sound-editor/sound-editor.css | 4 ++-- src/components/spinner/spinner.css | 2 +- src/components/sprite-info/icon--hide.svg | 2 +- src/components/sprite-info/icon--show.svg | 2 +- src/components/sprite-info/sprite-info.css | 2 +- .../sprite-selector-item/sprite-selector-item.css | 6 +++--- src/components/stage-header/icon--large-stage.svg | 4 ++-- src/components/stage-header/icon--small-stage.svg | 4 ++-- src/components/stage-selector/stage-selector.css | 6 +++--- src/components/stage/stage.css | 2 +- src/components/tag-button/tag-button.css | 2 +- src/components/telemetry-modal/telemetry-modal.css | 12 ++++++------ src/components/toggle-buttons/toggle-buttons.css | 2 +- src/components/webgl-modal/webgl-modal.css | 8 ++++---- src/css/colors.css | 8 ++++---- 57 files changed, 102 insertions(+), 102 deletions(-) diff --git a/src/components/action-menu/action-menu.css b/src/components/action-menu/action-menu.css index 71d078576..03be98cac 100644 --- a/src/components/action-menu/action-menu.css +++ b/src/components/action-menu/action-menu.css @@ -19,7 +19,7 @@ $more-button-size: 2.25rem; justify-content: center; cursor: pointer; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - background: $looks-tertiary; + background: $looks-secondary; outline: none; border: none; transition: background-color 0.2s; @@ -72,7 +72,7 @@ button::-moz-focus-inner { */ overflow-y: hidden; - background: $looks-tertiary-dark; + background: $looks-secondary-dark; border-top-left-radius: $more-button-size; border-top-right-radius: $more-button-size; width: $more-button-size; @@ -115,7 +115,7 @@ button::-moz-focus-inner { .more-button { width: $more-button-size; height: $more-button-size; - background: $looks-tertiary-dark; + background: $looks-secondary-dark; } .more-icon { diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css index a50b1cc5f..712bdf342 100644 --- a/src/components/asset-panel/selector.css +++ b/src/components/asset-panel/selector.css @@ -20,7 +20,7 @@ align-items: center; justify-content: space-around; padding: 0.75rem 0; - color: $looks-tertiary; + color: $looks-secondary; text-align: center; background: none; } diff --git a/src/components/audio-trimmer/audio-trimmer.css b/src/components/audio-trimmer/audio-trimmer.css index b36ec82b9..9583558df 100644 --- a/src/components/audio-trimmer/audio-trimmer.css +++ b/src/components/audio-trimmer/audio-trimmer.css @@ -41,7 +41,7 @@ $hover-scale: 1.25; } .selection-background { - background: $looks-tertiary; + background: $looks-secondary; opacity: 0.5; } @@ -64,7 +64,7 @@ $hover-scale: 1.25; } .selector .trim-line { - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; } .playhead-container { @@ -83,7 +83,7 @@ $hover-scale: 1.25; */ width: 100%; height: 100%; - border-left: 1px solid $looks-tertiary; + border-left: 1px solid $looks-secondary; border-top: none; border-bottom: none; border-right: none; diff --git a/src/components/audio-trimmer/icon--handle.svg b/src/components/audio-trimmer/icon--handle.svg index bdfeafe20..69eb54a7e 100644 --- a/src/components/audio-trimmer/icon--handle.svg +++ b/src/components/audio-trimmer/icon--handle.svg @@ -8,9 +8,9 @@ - + - + diff --git a/src/components/backpack/backpack.css b/src/components/backpack/backpack.css index d89933954..98a35a5e8 100644 --- a/src/components/backpack/backpack.css +++ b/src/components/backpack/backpack.css @@ -88,7 +88,7 @@ } .more { - background: $looks-tertiary; + background: $looks-secondary; color: $ui-white; border: none; outline: none; diff --git a/src/components/browser-modal/browser-modal.css b/src/components/browser-modal/browser-modal.css index 18c71e418..5fa57bbec 100644 --- a/src/components/browser-modal/browser-modal.css +++ b/src/components/browser-modal/browser-modal.css @@ -60,10 +60,10 @@ } .button-row button { - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; border-radius: 0.25rem; padding: 0.5rem 2rem; - background: $looks-tertiary; + background: $looks-secondary; color: white; font-weight: bold; font-size: 0.875rem; @@ -77,6 +77,6 @@ } .faq-link { - color: $looks-tertiary; + color: $looks-secondary; text-decoration: none; } diff --git a/src/components/cards/card.css b/src/components/cards/card.css index 5a3467e1e..523323fff 100644 --- a/src/components/cards/card.css +++ b/src/components/cards/card.css @@ -214,7 +214,7 @@ } .deck-name { - color: $looks-tertiary; + color: $looks-secondary; font-weight: bold; font-size: 0.85rem; margin: .625rem 0px; @@ -260,7 +260,7 @@ .see-all-button { cursor: pointer; padding: 0.5rem 1rem; - background-color: $looks-tertiary; + background-color: $looks-secondary; color: white; font-weight: bold; border-radius: 0.25rem; diff --git a/src/components/close-button/close-button.css b/src/components/close-button/close-button.css index bcd081a00..2a023e290 100644 --- a/src/components/close-button/close-button.css +++ b/src/components/close-button/close-button.css @@ -28,7 +28,7 @@ .small { width: 0.825rem; height: 0.825rem; - background-color: $looks-tertiary; + background-color: $looks-secondary; color: $ui-white; } diff --git a/src/components/connection-modal/connection-modal.css b/src/components/connection-modal/connection-modal.css index 66b4fcdff..a97c4355d 100644 --- a/src/components/connection-modal/connection-modal.css +++ b/src/components/connection-modal/connection-modal.css @@ -80,8 +80,8 @@ border-radius: 0.25rem; font-weight: 600; font-size: 0.85rem; - background: $looks-tertiary; - border: $looks-tertiary; + background: $looks-secondary; + border: $looks-secondary; color: white; cursor: pointer; } @@ -172,7 +172,7 @@ right: -15px; left: -15px; padding: 5px 5px; - background-color: $looks-tertiary; + background-color: $looks-secondary; border-radius: 100%; box-shadow: 0px 0px 0px 4px $looks-transparent; /* animation: pulse-blue-ring 1s infinite ease-in-out alternate; */ @@ -208,7 +208,7 @@ .bluetooth-centered-icon { position: absolute; padding: 5px 5px; - background-color: $looks-tertiary; + background-color: $looks-secondary; border-radius: 100%; box-shadow: 0px 0px 0px 2px $looks-transparent; } @@ -297,7 +297,7 @@ .connection-button { padding: 0.6rem 0.75rem; border-radius: 0.5rem; - background: $looks-tertiary; + background: $looks-secondary; color: white; font-weight: 600; font-size: 0.85rem; @@ -413,7 +413,7 @@ } .active-step-dot { - background: $looks-tertiary; + background: $looks-secondary; } .success-dot { diff --git a/src/components/context-menu/context-menu.css b/src/components/context-menu/context-menu.css index 55cd2afea..5548e066b 100644 --- a/src/components/context-menu/context-menu.css +++ b/src/components/context-menu/context-menu.css @@ -25,7 +25,7 @@ } .menu-item:hover { - background: $looks-tertiary; + background: $looks-secondary; color: white; } diff --git a/src/components/custom-procedures/custom-procedures.css b/src/components/custom-procedures/custom-procedures.css index 8bf499d00..d54fd7d82 100644 --- a/src/components/custom-procedures/custom-procedures.css +++ b/src/components/custom-procedures/custom-procedures.css @@ -53,7 +53,7 @@ } .option-card:hover { - border: 2px solid $looks-tertiary; + border: 2px solid $looks-secondary; box-shadow: 0px 0px 0px 4px $looks-transparent; } @@ -92,8 +92,8 @@ } .button-row button.ok-button { - background: $looks-tertiary; - border: $looks-tertiary; + background: $looks-secondary; + border: $looks-secondary; color: white; } diff --git a/src/components/delete-button/delete-button.css b/src/components/delete-button/delete-button.css index f9535b395..692a9d352 100644 --- a/src/components/delete-button/delete-button.css +++ b/src/components/delete-button/delete-button.css @@ -19,7 +19,7 @@ width: 1.75rem; height: 1.75rem; box-shadow: 0px 0px 0px 2px $looks-transparent; - background-color: $looks-tertiary; + background-color: $looks-secondary; color: $ui-white; border-radius: 50%; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; diff --git a/src/components/direction-picker/dial.css b/src/components/direction-picker/dial.css index ef1fc2e9d..43ab7c7d0 100644 --- a/src/components/direction-picker/dial.css +++ b/src/components/direction-picker/dial.css @@ -36,6 +36,6 @@ $dial-size: 40px; .gauge-path { fill: $looks-transparent; - stroke: $looks-tertiary; + stroke: $looks-secondary; stroke-width: 1px; } diff --git a/src/components/direction-picker/icon--all-around.svg b/src/components/direction-picker/icon--all-around.svg index 06dd9a304..8ace45b7c 100644 --- a/src/components/direction-picker/icon--all-around.svg +++ b/src/components/direction-picker/icon--all-around.svg @@ -5,6 +5,6 @@ Created with Sketch. - + diff --git a/src/components/direction-picker/icon--dont-rotate.svg b/src/components/direction-picker/icon--dont-rotate.svg index f33effad5..222054e82 100644 --- a/src/components/direction-picker/icon--dont-rotate.svg +++ b/src/components/direction-picker/icon--dont-rotate.svg @@ -5,6 +5,6 @@ Created with Sketch. - + diff --git a/src/components/direction-picker/icon--handle.svg b/src/components/direction-picker/icon--handle.svg index f95cf8443..ea901a7a4 100644 --- a/src/components/direction-picker/icon--handle.svg +++ b/src/components/direction-picker/icon--handle.svg @@ -1 +1 @@ -01_Dial Elements +01_Dial Elements diff --git a/src/components/direction-picker/icon--left-right.svg b/src/components/direction-picker/icon--left-right.svg index 4d2cacb51..8565562d8 100644 --- a/src/components/direction-picker/icon--left-right.svg +++ b/src/components/direction-picker/icon--left-right.svg @@ -5,8 +5,8 @@ Created with Sketch. - - - + + + diff --git a/src/components/drag-layer/drag-layer.css b/src/components/drag-layer/drag-layer.css index 5fe1cf5f5..cb8885374 100644 --- a/src/components/drag-layer/drag-layer.css +++ b/src/components/drag-layer/drag-layer.css @@ -29,7 +29,7 @@ margin-top: -50%; padding: 0.25rem; - border: 2px solid $looks-tertiary; + border: 2px solid $looks-secondary; background: $ui-white; border-radius: 0.5rem; diff --git a/src/components/filter/icon--filter.svg b/src/components/filter/icon--filter.svg index e00524981..c60cc9172 100644 --- a/src/components/filter/icon--filter.svg +++ b/src/components/filter/icon--filter.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/forms/input.css b/src/components/forms/input.css index 2d2c1677e..ce0ab17b7 100644 --- a/src/components/forms/input.css +++ b/src/components/forms/input.css @@ -32,11 +32,11 @@ } .input-form:hover { - border-color: $looks-tertiary; + border-color: $looks-secondary; } .input-form:focus { - border-color: $looks-tertiary; + border-color: $looks-secondary; box-shadow: 0 0 0 0.25rem $looks-transparent; } diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css index fdea80336..2002e5bb2 100644 --- a/src/components/gui/gui.css +++ b/src/components/gui/gui.css @@ -124,7 +124,7 @@ .tab.is-selected { height: 90%; - color: $looks-tertiary; + color: $looks-secondary; background-color: $ui-white; z-index: 4; /* Make sure selected is always above */ } @@ -236,9 +236,9 @@ left: 0; right: 0; z-index: $z-index-extension-button; - background: $looks-tertiary; + background: $looks-secondary; - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; box-sizing: content-box; /* To match scratch-block vertical toolbox borders */ } diff --git a/src/components/gui/icon--code.svg b/src/components/gui/icon--code.svg index 1626e2344..c06998055 100644 --- a/src/components/gui/icon--code.svg +++ b/src/components/gui/icon--code.svg @@ -5,6 +5,6 @@ Created with Sketch. - + diff --git a/src/components/gui/icon--costumes.svg b/src/components/gui/icon--costumes.svg index bd6914e77..5111126f0 100644 --- a/src/components/gui/icon--costumes.svg +++ b/src/components/gui/icon--costumes.svg @@ -5,6 +5,6 @@ Created with Sketch. - + diff --git a/src/components/gui/icon--sounds.svg b/src/components/gui/icon--sounds.svg index d789e052e..d05c06309 100644 --- a/src/components/gui/icon--sounds.svg +++ b/src/components/gui/icon--sounds.svg @@ -5,6 +5,6 @@ Created with Sketch. - + diff --git a/src/components/icon-button/icon-button.css b/src/components/icon-button/icon-button.css index 7527804a8..5e6b4845e 100644 --- a/src/components/icon-button/icon-button.css +++ b/src/components/icon-button/icon-button.css @@ -7,7 +7,7 @@ cursor: pointer; font-size: 0.75rem; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - color: $looks-tertiary; + color: $looks-secondary; border-radius: 0.5rem; } diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index 33aba52a4..1a7d15933 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -29,7 +29,7 @@ .library-item:hover { border-width: 2px; - border-color: $looks-tertiary; + border-color: $looks-secondary; } .hidden { diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index 741c1c071..9e5d6724d 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -27,7 +27,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: $menu-bar-standard-font-size; font-weight: bold; - background-color: $looks-tertiary; + background-color: $looks-secondary; color: $ui-white; } @@ -125,7 +125,7 @@ } .feedback-link { - color: $looks-tertiary; + color: $looks-secondary; text-decoration: none; } diff --git a/src/components/menu/menu.css b/src/components/menu/menu.css index 109e1ab5b..e5d0a66ef 100644 --- a/src/components/menu/menu.css +++ b/src/components/menu/menu.css @@ -4,7 +4,7 @@ position: absolute; border: 1px solid $ui-black-transparent; border-radius: 0 0 8px 8px; - background-color: $looks-tertiary; + background-color: $looks-secondary; padding: 0; margin: 0; min-width: 186px; diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 89174bb24..6beaaf8f6 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -67,7 +67,7 @@ $sides: 20rem; box-sizing: border-box; width: 100%; - background-color: $looks-tertiary; + background-color: $looks-secondary; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1rem; diff --git a/src/components/prompt/prompt.css b/src/components/prompt/prompt.css index 0b2d9482e..71fb7fac7 100644 --- a/src/components/prompt/prompt.css +++ b/src/components/prompt/prompt.css @@ -64,8 +64,8 @@ } .button-row button.ok-button { - background: $looks-tertiary; - border: $looks-tertiary; + background: $looks-secondary; + border: $looks-secondary; color: white; } diff --git a/src/components/question/question.css b/src/components/question/question.css index 9e077584e..ca5c65913 100644 --- a/src/components/question/question.css +++ b/src/components/question/question.css @@ -34,7 +34,7 @@ border-radius: 100%; color: white; - background: $looks-tertiary; + background: $looks-secondary; } [dir="ltr"] .question-submit-button { diff --git a/src/components/record-modal/icon--back.svg b/src/components/record-modal/icon--back.svg index 82f3b4efd..aff0c4f68 100644 --- a/src/components/record-modal/icon--back.svg +++ b/src/components/record-modal/icon--back.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/record-modal/icon--play.svg b/src/components/record-modal/icon--play.svg index e1871ff70..14ddbbaf2 100644 --- a/src/components/record-modal/icon--play.svg +++ b/src/components/record-modal/icon--play.svg @@ -18,7 +18,7 @@ - + diff --git a/src/components/record-modal/icon--stop-playback.svg b/src/components/record-modal/icon--stop-playback.svg index bc01665db..c72c363e3 100644 --- a/src/components/record-modal/icon--stop-playback.svg +++ b/src/components/record-modal/icon--stop-playback.svg @@ -18,7 +18,7 @@ - + diff --git a/src/components/record-modal/record-modal.css b/src/components/record-modal/record-modal.css index 8086719f5..e59df3e91 100644 --- a/src/components/record-modal/record-modal.css +++ b/src/components/record-modal/record-modal.css @@ -47,7 +47,7 @@ } .playing-text { - color: $looks-tertiary; + color: $looks-secondary; } .recording-text { @@ -89,12 +89,12 @@ border: 1px solid $ui-black-transparent; font-weight: 600; font-size: 0.85rem; - color: $looks-tertiary; + color: $looks-secondary; } .button-row button.ok-button { - background: $looks-tertiary; - border: $looks-tertiary; + background: $looks-secondary; + border: $looks-secondary; color: white; } diff --git a/src/components/slider-prompt/slider-prompt.css b/src/components/slider-prompt/slider-prompt.css index 0b643888c..146b11551 100644 --- a/src/components/slider-prompt/slider-prompt.css +++ b/src/components/slider-prompt/slider-prompt.css @@ -41,8 +41,8 @@ } .button-row button.ok-button { - background: $looks-tertiary; - border: $looks-tertiary; + background: $looks-secondary; + border: $looks-secondary; color: white; } diff --git a/src/components/sound-editor/icon--copy-to-new.svg b/src/components/sound-editor/icon--copy-to-new.svg index 8e08e6eeb..4d228a177 100644 --- a/src/components/sound-editor/icon--copy-to-new.svg +++ b/src/components/sound-editor/icon--copy-to-new.svg @@ -4,6 +4,6 @@ Sound/General/Copy to New Created with Sketch. - + diff --git a/src/components/sound-editor/icon--copy.svg b/src/components/sound-editor/icon--copy.svg index 50e064a2f..b905e4342 100644 --- a/src/components/sound-editor/icon--copy.svg +++ b/src/components/sound-editor/icon--copy.svg @@ -4,10 +4,10 @@ Sound/General/Copy Created with Sketch. - + - + diff --git a/src/components/sound-editor/icon--delete.svg b/src/components/sound-editor/icon--delete.svg index a0a263d69..c7e096962 100644 --- a/src/components/sound-editor/icon--delete.svg +++ b/src/components/sound-editor/icon--delete.svg @@ -11,7 +11,7 @@ - + diff --git a/src/components/sound-editor/icon--paste.svg b/src/components/sound-editor/icon--paste.svg index ce09f40d8..51027762f 100644 --- a/src/components/sound-editor/icon--paste.svg +++ b/src/components/sound-editor/icon--paste.svg @@ -4,6 +4,6 @@ Sound/General/Paste Created with Sketch. - + diff --git a/src/components/sound-editor/icon--redo.svg b/src/components/sound-editor/icon--redo.svg index aeeb00c5d..c32f94494 100644 --- a/src/components/sound-editor/icon--redo.svg +++ b/src/components/sound-editor/icon--redo.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sound-editor/icon--undo.svg b/src/components/sound-editor/icon--undo.svg index f6b3fc7f8..44c9517b7 100644 --- a/src/components/sound-editor/icon--undo.svg +++ b/src/components/sound-editor/icon--undo.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sound-editor/sound-editor.css b/src/components/sound-editor/sound-editor.css index fcf2d15ee..e2e38a020 100644 --- a/src/components/sound-editor/sound-editor.css +++ b/src/components/sound-editor/sound-editor.css @@ -95,7 +95,7 @@ $border-radius: 0.25rem; right: -1px; bottom: -1px; left: -1px; - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; z-index: 1; } @@ -110,7 +110,7 @@ $border-radius: 0.25rem; height: 3rem; width: 3rem; outline: none; - background: $looks-tertiary; + background: $looks-secondary; border-radius: 100%; border: 4px solid $ui-white-dim; cursor: pointer; diff --git a/src/components/spinner/spinner.css b/src/components/spinner/spinner.css index 51c4c2706..48601b034 100644 --- a/src/components/spinner/spinner.css +++ b/src/components/spinner/spinner.css @@ -86,5 +86,5 @@ border-color: $looks-transparent; } .spinner.primary::after { - border-top-color: $looks-tertiary; + border-top-color: $looks-secondary; } diff --git a/src/components/sprite-info/icon--hide.svg b/src/components/sprite-info/icon--hide.svg index 4e9dc7a8c..347b4db5b 100644 --- a/src/components/sprite-info/icon--hide.svg +++ b/src/components/sprite-info/icon--hide.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sprite-info/icon--show.svg b/src/components/sprite-info/icon--show.svg index 0eed3fd98..d6fd9280d 100644 --- a/src/components/sprite-info/icon--show.svg +++ b/src/components/sprite-info/icon--show.svg @@ -5,7 +5,7 @@ Created with Sketch. - + diff --git a/src/components/sprite-info/sprite-info.css b/src/components/sprite-info/sprite-info.css index 876acfd25..364df9fce 100644 --- a/src/components/sprite-info/sprite-info.css +++ b/src/components/sprite-info/sprite-info.css @@ -62,7 +62,7 @@ } .rotation-select:focus { - border-color: $looks-tertiary; + border-color: $looks-secondary; box-shadow: inset 0 0 0 1px $ui-black-transparent); } diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css index 1af628ca2..81c48358b 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.css +++ b/src/components/sprite-selector-item/sprite-selector-item.css @@ -24,12 +24,12 @@ .sprite-selector-item.is-selected { box-shadow: 0px 0px 0px 4px $looks-transparent; - border: 2px solid $looks-tertiary; + border: 2px solid $looks-secondary; background: $ui-white; } .sprite-selector-item:hover { - border: 2px solid $looks-tertiary; + border: 2px solid $looks-secondary; background: $ui-white; } @@ -88,7 +88,7 @@ } .is-selected .sprite-info { - background: $looks-tertiary; + background: $looks-secondary; color: $ui-white; } diff --git a/src/components/stage-header/icon--large-stage.svg b/src/components/stage-header/icon--large-stage.svg index e069a0951..7b33554e7 100644 --- a/src/components/stage-header/icon--large-stage.svg +++ b/src/components/stage-header/icon--large-stage.svg @@ -5,10 +5,10 @@ Created with Sketch. - + - + diff --git a/src/components/stage-header/icon--small-stage.svg b/src/components/stage-header/icon--small-stage.svg index 9ddc61cef..aab6d5b55 100644 --- a/src/components/stage-header/icon--small-stage.svg +++ b/src/components/stage-header/icon--small-stage.svg @@ -5,10 +5,10 @@ Created with Sketch. - + - + diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css index 921c2a2a3..f93a0c344 100644 --- a/src/components/stage-selector/stage-selector.css +++ b/src/components/stage-selector/stage-selector.css @@ -26,12 +26,12 @@ $header-height: calc($stage-menu-height - 2px); .stage-selector.is-selected { border-top-left-radius: .625rem; border-top-right-radius: .625rem; - border-color: $looks-tertiary; + border-color: $looks-secondary; box-shadow: 0px 0px 0px 4px $looks-transparent; } .stage-selector:hover { - border-color: $looks-tertiary; + border-color: $looks-secondary; } .header { @@ -60,7 +60,7 @@ $header-height: calc($stage-menu-height - 2px); } .stage-selector.is-selected .header { - background-color: $looks-tertiary; + background-color: $looks-secondary; } .stage-selector.is-selected .header-title { diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css index e0e74cad8..a799e05ce 100644 --- a/src/components/stage/stage.css +++ b/src/components/stage/stage.css @@ -109,7 +109,7 @@ stage doesn't "spill" out from under its rounded corners. instead, shift these o .frame { background: $looks-transparent; - border: 2px solid $looks-tertiary; + border: 2px solid $looks-secondary; border-radius: 0.5rem; animation-name: flash; animation-duration: 0.75s; diff --git a/src/components/tag-button/tag-button.css b/src/components/tag-button/tag-button.css index a84017ae0..83c9ee9ca 100644 --- a/src/components/tag-button/tag-button.css +++ b/src/components/tag-button/tag-button.css @@ -3,7 +3,7 @@ .tag-button { padding: .625rem 1rem; - background: $looks-tertiary; + background: $looks-secondary; border-radius: 1.375rem; color: $ui-white; height: $library-filter-bar-height; diff --git a/src/components/telemetry-modal/telemetry-modal.css b/src/components/telemetry-modal/telemetry-modal.css index 50165c91a..d94f2f00c 100644 --- a/src/components/telemetry-modal/telemetry-modal.css +++ b/src/components/telemetry-modal/telemetry-modal.css @@ -35,7 +35,7 @@ $ui-gray: hsla(0, 0%, 95%, 1); .illustration { width: 100%; height: 123px; - background-color: $looks-tertiary; + background-color: $looks-secondary; background-image: url('./telemetry-modal-header.png'); background-size: cover; } @@ -46,7 +46,7 @@ $ui-gray: hsla(0, 0%, 95%, 1); } .privacy-policy-link { - color: $looks-tertiary; + color: $looks-secondary; text-decoration: none; } @@ -92,14 +92,14 @@ $ui-gray: hsla(0, 0%, 95%, 1); .radio-buttons input[type="radio"]:checked { transition: all .25s ease; background-color: $ui-white; - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; } .radio-buttons input[type="radio"]:checked::after { display: block; margin: 0.125rem; border-radius: 50%; - background-color: $looks-tertiary; + background-color: $looks-secondary; width: .625rem; height: .625rem; content: ""; @@ -127,11 +127,11 @@ $ui-gray: hsla(0, 0%, 95%, 1); } .button-row button { - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; border-radius: 0.25rem; padding: 0.5rem 1.5rem; color: white; - background: $looks-tertiary; + background: $looks-secondary; font-weight: bold; font-size: .875rem; cursor: pointer; diff --git a/src/components/toggle-buttons/toggle-buttons.css b/src/components/toggle-buttons/toggle-buttons.css index c1abacafd..d5b54e51b 100644 --- a/src/components/toggle-buttons/toggle-buttons.css +++ b/src/components/toggle-buttons/toggle-buttons.css @@ -43,7 +43,7 @@ right: -1px; bottom: -1px; left: -1px; - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; } /* round corners for first and last buttons */ diff --git a/src/components/webgl-modal/webgl-modal.css b/src/components/webgl-modal/webgl-modal.css index 04d85b3d9..43327f211 100644 --- a/src/components/webgl-modal/webgl-modal.css +++ b/src/components/webgl-modal/webgl-modal.css @@ -29,7 +29,7 @@ .illustration { width: 100%; height: 208px; - background-color: $looks-tertiary; + background-color: $looks-secondary; background-image: url('./unsupported.png'); background-size: cover; } @@ -54,10 +54,10 @@ } .button-row button { - border: 1px solid $looks-tertiary; + border: 1px solid $looks-secondary; border-radius: 0.25rem; padding: 0.5rem 2rem; - background: $looks-tertiary; + background: $looks-secondary; color: white; font-weight: bold; font-size: 0.875rem; @@ -70,6 +70,6 @@ } .faq-link { - color: $looks-tertiary; + color: $looks-secondary; text-decoration: none; } diff --git a/src/css/colors.css b/src/css/colors.css index d4457ed6b..13a861788 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -17,10 +17,10 @@ $text-primary-transparent: hsla(225, 15%, 40%, 0.75); $motion-primary: hsla(215, 100%, 65%, 1); /* #4C97FF */ $motion-tertiary: hsla(215, 60%, 50%, 1); /* #3373CC */ -$looks-tertiary: hsla(260, 54%, 54%, 1); /* #774DCB */ -$looks-transparent: hsla(260, 54%, 54%, 0.35); /* 35% transparent version of looks-tertiary */ -$looks-light-transparent: hsla(260, 54%, 54%, 0.15); /* 15% transparent version of looks-tertiary */ -$looks-tertiary-dark: hsla(263, 54%, 46%, 1); /* #6736B5 */ +$looks-secondary: hsla(260, 60%, 60%, 1); /* #855CD6 */ +$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-tertiary */ +$looks-light-transparent: hsla(260, 60%, 60%, 0.15); /* 15% transparent version of looks-tertiary */ +$looks-secondary-dark: hsla(260, 42%, 51%, 1); /* #714EB6 */ $red-primary: hsla(20, 100%, 55%, 1); /* #FF661A */ $red-tertiary: hsla(20, 100%, 45%, 1); /* #E64D00 */ From cfab4a2c9eb1fbbc3aecd4422b027b773246cecd Mon Sep 17 00:00:00 2001 From: Andy O'Neill Date: Tue, 9 May 2023 13:04:47 -0400 Subject: [PATCH 21/21] feat: prepare for color theme beta release BREAKING CHANGE: use purple as accent color across the editor --- .circleci/config.yml | 6 +++++ package-lock.json | 28 ++++++++++++------------ package.json | 4 ++-- release.config.js | 5 +++++ src/components/gui/gui.jsx | 3 +-- src/lib/themes/index.js | 6 ++--- webpack.config.js | 45 ++++++++++++++++---------------------- 7 files changed, 50 insertions(+), 47 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f386894d3..74624b633 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -225,6 +225,7 @@ workflows: ignore: - master - develop + - beta - /^hotfix\/.*/ push-translations: triggers: @@ -254,6 +255,7 @@ workflows: ignore: - master - develop + - beta - /^hotfix\/.*/ build-test-deploy: jobs: @@ -265,6 +267,7 @@ workflows: only: - master - develop + - beta - /^hotfix\/.*/ - lint: context: @@ -296,6 +299,7 @@ workflows: only: - master - develop + - beta - /^hotfix\/.*/ - store_dist: context: @@ -307,6 +311,7 @@ workflows: only: - master - develop + - beta - /^hotfix\/.*/ - deploy-npm: context: @@ -322,6 +327,7 @@ workflows: only: - master - develop + - beta - /^hotfix\/.*/ - deploy-gh-pages: context: diff --git a/package-lock.json b/package-lock.json index 6556e90d8..836223c1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,9 +58,9 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20221123180128", - "scratch-blocks": "0.1.0-prerelease.20230507065123", + "scratch-blocks": "0.2.0-prerelease.20230509131059", "scratch-l10n": "3.15.20230507032520", - "scratch-paint": "1.1.54", + "scratch-paint": "1.2.0-beta.1", "scratch-render": "0.1.0-prerelease.20230318150639", "scratch-render-fonts": "1.0.0-prerelease.20221102164332", "scratch-storage": "2.2.1", @@ -25876,9 +25876,9 @@ } }, "node_modules/scratch-blocks": { - "version": "0.1.0-prerelease.20230507065123", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20230507065123.tgz", - "integrity": "sha512-ESFkBv3EyyjXQbfgnbOXMCkLtlTzR1K+mPUANobSWim/evedSowx3l6uuqGVumEbo/9K2ullKyXQo7LDZDrvOw==", + "version": "0.2.0-prerelease.20230509131059", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.2.0-prerelease.20230509131059.tgz", + "integrity": "sha512-TPrMjmXhOh3AwlLJu3ggkMUwfHsUN8Y0tk7NnrLM02R0qtr32pgOHttBobXDya7R4Pu+HyfGSuxwtuJftb0MsQ==", "dependencies": { "exports-loader": "0.7.0", "google-closure-library": "20190301.0.0", @@ -25904,9 +25904,9 @@ } }, "node_modules/scratch-paint": { - "version": "1.1.54", - "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-1.1.54.tgz", - "integrity": "sha512-23fylk1yn74TEpthDX1wGpnVGLRDNuQALhTD+Hrq5b0yfWDW1U+Ane6MuA6zOR2noFRcby357rvCdKNXU9p6zQ==", + "version": "1.2.0-beta.1", + "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-1.2.0-beta.1.tgz", + "integrity": "sha512-YyVBRv5+ahJA02N0KKjalHUyJXKZOvQmpq658gswePsyU/GkcHByJCRQjd0iYWN8nJZlDEZrOExLuh+noTHjuA==", "dependencies": { "@scratch/paper": "0.11.20200728195508", "classnames": "2.2.5", @@ -51464,9 +51464,9 @@ } }, "scratch-blocks": { - "version": "0.1.0-prerelease.20230507065123", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20230507065123.tgz", - "integrity": "sha512-ESFkBv3EyyjXQbfgnbOXMCkLtlTzR1K+mPUANobSWim/evedSowx3l6uuqGVumEbo/9K2ullKyXQo7LDZDrvOw==", + "version": "0.2.0-prerelease.20230509131059", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.2.0-prerelease.20230509131059.tgz", + "integrity": "sha512-TPrMjmXhOh3AwlLJu3ggkMUwfHsUN8Y0tk7NnrLM02R0qtr32pgOHttBobXDya7R4Pu+HyfGSuxwtuJftb0MsQ==", "requires": { "exports-loader": "0.7.0", "google-closure-library": "20190301.0.0", @@ -51488,9 +51488,9 @@ } }, "scratch-paint": { - "version": "1.1.54", - "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-1.1.54.tgz", - "integrity": "sha512-23fylk1yn74TEpthDX1wGpnVGLRDNuQALhTD+Hrq5b0yfWDW1U+Ane6MuA6zOR2noFRcby357rvCdKNXU9p6zQ==", + "version": "1.2.0-beta.1", + "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-1.2.0-beta.1.tgz", + "integrity": "sha512-YyVBRv5+ahJA02N0KKjalHUyJXKZOvQmpq658gswePsyU/GkcHByJCRQjd0iYWN8nJZlDEZrOExLuh+noTHjuA==", "requires": { "@scratch/paper": "0.11.20200728195508", "classnames": "2.2.5", diff --git a/package.json b/package.json index 3bb780139..1d815fbe0 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,9 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20221123180128", - "scratch-blocks": "0.1.0-prerelease.20230507065123", + "scratch-blocks": "0.2.0-prerelease.20230509131059", "scratch-l10n": "3.15.20230507032520", - "scratch-paint": "1.1.54", + "scratch-paint": "1.2.0-beta.1", "scratch-render": "0.1.0-prerelease.20230318150639", "scratch-render-fonts": "1.0.0-prerelease.20221102164332", "scratch-storage": "2.2.1", diff --git a/release.config.js b/release.config.js index d4156eb15..416c2f5a1 100644 --- a/release.config.js +++ b/release.config.js @@ -8,6 +8,11 @@ module.exports = { { name: 'hotfix/REPLACE', // replace with actual hotfix branch name channel: 'hotfix' + }, + { + name: 'beta', + channel: 'beta', + prerelease: true } ] }; diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 9488f2d0f..f48e5a6ec 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -307,11 +307,10 @@ const GUIComponent = props => {