mirror of
https://github.com/scratchfoundation/scratch-desktop.git
synced 2024-12-31 10:02:25 -05:00
Merge pull request #338 from aoneill01/feature/high-contrast
Feature/high contrast
This commit is contained in:
commit
7bc88deb19
13 changed files with 8070 additions and 3537 deletions
|
@ -209,4 +209,7 @@ commands:
|
|||
steps:
|
||||
- run:
|
||||
command: npm run distDev
|
||||
environment: *clear_context
|
||||
environment:
|
||||
<<: *clear_context
|
||||
# increased Node memory needed for macOS build
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
|
|
@ -22,7 +22,7 @@ Let's assume that you want to make a new release, version `3.999.0`, correspondi
|
|||
1. `cd scratch-desktop`
|
||||
2. `git pull --all --tags`
|
||||
3. `git checkout develop`
|
||||
4. `npm install --save-dev 'scratch-gui@github:LLK/scratch-gui#scratch-desktop-v3.999.0'`
|
||||
4. `npm install --save-dev 'scratch-gui@github:scratchfoundation/scratch-gui#scratch-desktop-v3.999.0'`
|
||||
5. `git add package.json package-lock.json`
|
||||
6. Make sure the app works, the diffs look reasonable, etc.
|
||||
7. `git commit -m "bump scratch-gui to scratch-desktop-v3.999.0"`
|
||||
|
|
11335
package-lock.json
generated
11335
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -32,6 +32,8 @@
|
|||
"@babel/plugin-proposal-object-rest-spread": "^7.9.6",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.8.3",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5",
|
||||
"@babel/plugin-transform-optional-chaining": "^7.22.6",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"async": "^3.2.0",
|
||||
|
@ -40,8 +42,8 @@
|
|||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-react-intl": "^7.5.7",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"electron": "^15.3.1",
|
||||
"electron-builder": "^22.13.1",
|
||||
"electron": "^25.2.0",
|
||||
"electron-builder": "^24.4.0",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-notarize": "^1.1.1",
|
||||
"electron-store": "^8.0.1",
|
||||
|
@ -66,7 +68,7 @@
|
|||
"react-redux": "5.1.2",
|
||||
"redux": "3.7.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"scratch-gui": "github:LLK/scratch-gui#scratch-desktop-v3.29.0",
|
||||
"scratch-gui": "github:scratchfoundation/scratch-gui#scratch-desktop-v3.30.1",
|
||||
"uuid": "^8.0.0",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-merge": "4.2.2"
|
||||
|
|
|
@ -230,6 +230,52 @@ const createPrivacyWindow = () => {
|
|||
return window;
|
||||
};
|
||||
|
||||
const createUsbWindow = () => {
|
||||
const window = createWindow({
|
||||
width: 400,
|
||||
height: 300,
|
||||
parent: _windows.main,
|
||||
search: 'route=usb',
|
||||
modal: true,
|
||||
frame: false
|
||||
});
|
||||
|
||||
// Filters from navigator.usb.requestDevice do not appear to be available here.
|
||||
// Hard code to micro:bit since that is the only device that currently uses this api.
|
||||
const getIsMicroBit = device => device.vendorId === 0x0d28 && device.productId === 0x0204;
|
||||
let deviceList = [];
|
||||
let selectedDeviceCallback;
|
||||
|
||||
_windows.main.webContents.session.on('select-usb-device', (event, details, callback) => {
|
||||
deviceList = details.deviceList.filter(getIsMicroBit);
|
||||
selectedDeviceCallback = callback;
|
||||
|
||||
window.webContents.send('usb-device-list', deviceList);
|
||||
window.show();
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
_windows.main.webContents.session.on('usb-device-added', (_event, device) => {
|
||||
if (!getIsMicroBit(device)) return;
|
||||
deviceList.push(device);
|
||||
window.webContents.send('usb-device-list', deviceList);
|
||||
});
|
||||
|
||||
_windows.main.webContents.session.on('usb-device-removed', (_event, device) => {
|
||||
if (!getIsMicroBit(device)) return;
|
||||
deviceList = deviceList.filter(existing => existing.deviceId !== device.deviceId);
|
||||
window.webContents.send('usb-device-list', deviceList);
|
||||
});
|
||||
|
||||
ipcMain.on('usb-device-selected', (_event, message) => {
|
||||
selectedDeviceCallback(message);
|
||||
window.hide();
|
||||
});
|
||||
|
||||
return window;
|
||||
};
|
||||
|
||||
const getIsProjectSave = downloadItem => {
|
||||
switch (downloadItem.getMimeType()) {
|
||||
case 'application/x.scratch.sb3':
|
||||
|
@ -398,6 +444,8 @@ app.on('ready', () => {
|
|||
event.preventDefault();
|
||||
_windows.privacy.hide();
|
||||
});
|
||||
|
||||
_windows.usb = createUsbWindow();
|
||||
});
|
||||
|
||||
ipcMain.on('open-about-window', () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
html, body {
|
||||
background-color: #4D97FF;
|
||||
background-color: #855CD6;
|
||||
color: white;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: bolder;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
background-color: #4D97FF;
|
||||
background-color: #855CD6;
|
||||
}
|
||||
.splash {
|
||||
color: white;
|
||||
|
|
|
@ -24,6 +24,9 @@ case 'about':
|
|||
case 'privacy':
|
||||
routeModulePromise = import('./privacy.jsx');
|
||||
break;
|
||||
case 'usb':
|
||||
routeModulePromise = import('./usb.jsx');
|
||||
break;
|
||||
}
|
||||
|
||||
routeModulePromise.then(routeModule => {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
html, body {
|
||||
background-color: #4D97FF;
|
||||
background-color: #855CD6;
|
||||
color: white;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a:active, a:hover, a:link, a:visited {
|
||||
color: #855CD6;
|
||||
}
|
||||
|
||||
.privacyBox {
|
||||
background-color: white;
|
||||
color: #575e75;
|
||||
|
|
75
src/renderer/usb.css
Normal file
75
src/renderer/usb.css
Normal file
|
@ -0,0 +1,75 @@
|
|||
html, body {
|
||||
background-color: white;
|
||||
color: #575E75;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
line-height: 150%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body, :global(#app), main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(#app) {
|
||||
box-sizing: border-box;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.devices {
|
||||
border: 1px solid hsla(0, 0%, 0%, 0.15);
|
||||
flex-grow: 1;
|
||||
margin-block: 1em;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.device.selected {
|
||||
background-color: #855CD6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.device input[type="radio"] {
|
||||
/* Hide the radio button but keep it accessible for keyboard */
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.device label {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
padding-inline: .5em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: .25rem;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
margin: 0.25rem;
|
||||
padding: 0.6rem 0.75rem;
|
||||
}
|
||||
|
||||
.cancelButton {
|
||||
background: white;
|
||||
border: 1px solid #855CD6;
|
||||
color: #855CD6;
|
||||
}
|
||||
|
||||
.connectButton {
|
||||
background: #855CD6;
|
||||
border: 1px solid #855CD6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.connectButton:disabled {
|
||||
opacity: 50%;
|
||||
}
|
70
src/renderer/usb.jsx
Normal file
70
src/renderer/usb.jsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {ipcRenderer} from 'electron';
|
||||
|
||||
import styles from './usb.css';
|
||||
|
||||
const UsbElement = () => {
|
||||
const [deviceList, setDeviceList] = useState([]);
|
||||
const [selectedDeviceId, setSelectedDeviceId] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (_event, usbDeviceList) => {
|
||||
setDeviceList(usbDeviceList);
|
||||
if (!usbDeviceList.some(device => device.deviceId === selectedDeviceId)) {
|
||||
setSelectedDeviceId(null);
|
||||
}
|
||||
};
|
||||
|
||||
ipcRenderer.on('usb-device-list', listener);
|
||||
|
||||
return () => ipcRenderer.removeListener('usb-device-list', listener);
|
||||
}, []);
|
||||
|
||||
const selectHandler = deviceId => () => {
|
||||
setSelectedDeviceId(deviceId);
|
||||
};
|
||||
|
||||
const deviceHandler = deviceId => () => {
|
||||
ipcRenderer.send('usb-device-selected', deviceId);
|
||||
setSelectedDeviceId(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<main>
|
||||
Select your USB device:
|
||||
<fieldset
|
||||
className={styles.devices}
|
||||
>
|
||||
{deviceList.map(device => (
|
||||
<div
|
||||
className={`${styles.device} ${selectedDeviceId === device.deviceId ? styles.selected : ''}`}
|
||||
key={device.deviceId}
|
||||
>
|
||||
<input
|
||||
checked={selectedDeviceId === device.deviceId}
|
||||
id={`device-${device.deviceId}`}
|
||||
name="usbDevice"
|
||||
onChange={selectHandler(device.deviceId)}
|
||||
type="radio"
|
||||
value={device.deviceId}
|
||||
/>
|
||||
<label htmlFor={`device-${device.deviceId}`}>{device.productName}</label>
|
||||
</div>
|
||||
))}
|
||||
</fieldset>
|
||||
<div className={styles.buttons}>
|
||||
<button
|
||||
className={styles.cancelButton}
|
||||
onClick={deviceHandler(null)}
|
||||
>Cancel</button>
|
||||
<button
|
||||
className={styles.connectButton}
|
||||
disabled={!selectedDeviceId}
|
||||
onClick={deviceHandler(selectedDeviceId)}
|
||||
>Connect</button>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default <UsbElement />;
|
|
@ -24,7 +24,9 @@ const makeConfig = function (defaultConfig, options) {
|
|||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-async-to-generator',
|
||||
'@babel/plugin-proposal-object-rest-spread'
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-transform-nullish-coalescing-operator',
|
||||
'@babel/plugin-transform-optional-chaining'
|
||||
],
|
||||
presets: [
|
||||
['@babel/preset-env', {targets: {electron: electronVersion}}]
|
||||
|
@ -101,6 +103,15 @@ const makeConfig = function (defaultConfig, options) {
|
|||
options: {
|
||||
outputPath: 'static/assets/'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.hex$/,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 16 * 1024
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -20,19 +20,31 @@ module.exports = defaultConfig =>
|
|||
/node_modules[\\/]+@vernier[\\/]+godirect/
|
||||
],
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([{
|
||||
from: path.join(getModulePath('scratch-blocks'), 'media'),
|
||||
to: 'static/blocks-media'
|
||||
}]),
|
||||
new CopyWebpackPlugin([{
|
||||
from: 'extension-worker.{js,js.map}',
|
||||
context: path.join(getModulePath('scratch-vm'), 'dist', 'web')
|
||||
}]),
|
||||
new CopyWebpackPlugin([{
|
||||
from: path.join(getModulePath('scratch-gui'), 'src', 'lib', 'libraries', '*.json'),
|
||||
to: 'static/libraries',
|
||||
flatten: true
|
||||
}])
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.join(getModulePath('scratch-blocks'), 'media'),
|
||||
to: 'static/blocks-media/default'
|
||||
},
|
||||
{
|
||||
from: path.join(getModulePath('scratch-blocks'), 'media'),
|
||||
to: 'static/blocks-media/high-contrast'
|
||||
},
|
||||
{
|
||||
from: path.join(getModulePath('scratch-gui'),
|
||||
'src', 'lib', 'themes', 'high-contrast', 'blocks-media'),
|
||||
to: 'static/blocks-media/high-contrast',
|
||||
force: true
|
||||
},
|
||||
{
|
||||
from: 'extension-worker.{js,js.map}',
|
||||
context: path.join(getModulePath('scratch-vm'), 'dist', 'web')
|
||||
},
|
||||
{
|
||||
from: path.join(getModulePath('scratch-gui'), 'src', 'lib', 'libraries', '*.json'),
|
||||
to: 'static/libraries',
|
||||
flatten: true
|
||||
}
|
||||
])
|
||||
]
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue