toolbar: drop open and saveAs buttons

These have been replace by the explorer.
This commit is contained in:
David Lechner
2022-03-12 15:45:36 -06:00
parent a2ccd3afdb
commit cf8438c689
13 changed files with 4 additions and 672 deletions
-40
View File
@@ -1,40 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import React, { useContext } from 'react';
import { useDispatch } from 'react-redux';
import * as notificationActions from '../notifications/actions';
import { pythonFileExtension } from '../pybricksMicropython/lib';
import OpenFileButton, { OpenFileButtonProps } from '../toolbar/OpenFileButton';
import { TooltipId } from '../toolbar/i18n';
import { EditorContext } from './Editor';
import * as editorActions from './actions';
import openIcon from './open.svg';
type OpenButtonProps = Pick<OpenFileButtonProps, 'id'>;
const OpenButton: React.FunctionComponent<OpenButtonProps> = (props) => {
const { editor } = useContext(EditorContext);
const dispatch = useDispatch();
return (
<OpenFileButton
fileExtension={pythonFileExtension}
tooltip={TooltipId.Open}
icon={openIcon}
enabled={editor !== null}
onFile={(data) => dispatch(editorActions.open(data))}
onReject={(file) =>
dispatch(
notificationActions.add(
'error',
`'${file.name}' is not a valid python file.`,
),
)
}
{...props}
/>
);
};
export default OpenButton;
-31
View File
@@ -1,31 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import React, { useContext } from 'react';
import { useDispatch } from 'react-redux';
import * as editorActions from '../editor/actions';
import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton';
import { TooltipId } from '../toolbar/i18n';
import { EditorContext } from './Editor';
import downloadIcon from './save.svg';
type SaveAsButtonProps = Pick<ActionButtonProps, 'id'> &
Pick<ActionButtonProps, 'keyboardShortcut'>;
const SaveAsButton: React.FunctionComponent<SaveAsButtonProps> = (props) => {
const { editor } = useContext(EditorContext);
const dispatch = useDispatch();
return (
<ActionButton
tooltip={TooltipId.SaveAs}
icon={downloadIcon}
enabled={editor !== null}
onAction={() => dispatch(editorActions.saveAs())}
{...props}
/>
);
};
export default SaveAsButton;
-31
View File
@@ -1,31 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import { createAction } from '../actions';
/**
* Creates an action to save the current file
*/
export const saveAs = createAction(() => ({
type: 'editor.action.saveAs',
}));
/** Action that indicates saving a file succeeded. */
export const didSaveAs = createAction(() => ({
type: 'editor.action.didSaveAs',
}));
/** Action that indicates saving a file failed. */
export const didFailToSaveAs = createAction((err: Error) => ({
type: 'editor.action.didFailToSaveAs',
err,
}));
/**
* Creates an action to save a file
* @param data The file data
*/
export const open = createAction((data: ArrayBuffer) => ({
type: 'editor.action.open',
data,
}));
-146
View File
@@ -1,146 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="50"
height="50"
viewBox="0 0 26.458333 26.458334"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="open.svg"
inkscape:export-filename="button.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<linearGradient
id="light-circle-fill"
osb:paint="solid"
gradientTransform="translate(-111.65281,-194.6407)">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop5207" />
</linearGradient>
<linearGradient
id="icon-contents"
osb:paint="solid"
gradientTransform="matrix(0.78378378,0,0,0.78378378,-217.21909,-864.32335)">
<stop
style="stop-color:#3880b7;stop-opacity:1;"
offset="0"
id="stop5179" />
</linearGradient>
<linearGradient
id="light-circle-edge"
osb:paint="solid"
gradientTransform="matrix(0.04571075,0,0,0.26284445,-63.602396,-483.67016)">
<stop
style="stop-color:#b7b7b7;stop-opacity:1;"
offset="0"
id="stop5173" />
</linearGradient>
<linearGradient
id="dark-back"
osb:paint="solid"
gradientTransform="translate(1195.9167,-717.54999)">
<stop
style="stop-color:#333333;stop-opacity:1;"
offset="0"
id="stop1439" />
</linearGradient>
<linearGradient
id="dark-front"
osb:paint="solid"
gradientTransform="translate(4643.556,-4.2349475e-6)">
<stop
style="stop-color:#d4d4d4;stop-opacity:1;"
offset="0"
id="stop1389" />
</linearGradient>
<linearGradient
id="light-front"
osb:paint="solid"
gradientTransform="translate(-313.01005,-269.76128)">
<stop
style="stop-color:#4e4e4e;stop-opacity:1;"
offset="0"
id="stop1184" />
</linearGradient>
<linearGradient
id="light-back"
osb:paint="solid"
gradientTransform="translate(-78.252472)">
<stop
style="stop-color:#eeeeee;stop-opacity:1;"
offset="0"
id="stop1172" />
</linearGradient>
<linearGradient
id="light-circle-fill-3"
osb:paint="solid"
gradientTransform="matrix(1.4086066e-4,-8.1325944e-5,8.8105755e-5,1.5260364e-4,2250.622,3765.5708)">
<stop
style="stop-color:#0088ce;stop-opacity:1;"
offset="0"
id="stop5207-6" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="14.566286"
inkscape:cy="-4.9193288"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1853"
inkscape:window-height="1145"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1"
units="px"
inkscape:measure-start="0,0"
inkscape:measure-end="0,0"
inkscape:snap-global="false"
showguides="true"
inkscape:pagecheckerboard="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.83952707;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0,283.0985 v -10.61134 h 4.6381475 4.638146 l 1.2359985,1.23674 1.235998,1.23674 h 3.842999 5.132843 v 3.04238 3.04239 h 2.770299 c 1.523663,0 2.770297,0.0169 2.770297,0.0373 0,0.0205 -1.130907,2.86443 -2.513131,6.31974 l -2.513134,6.28238 -9.974309,0.0125 L 0,293.70973 Z m 21.446718,4.37796 c 0.908972,-2.27189 1.665144,-4.16968 1.680384,-4.21729 0.02631,-0.0822 0.885319,-0.0866 -7.288433,-0.0866 H 7.2326845 l -1.6812488,4.20244 c -0.9246856,2.31136 -1.668944,4.21476 -1.6539081,4.2298 0.01504,0.015 3.8881179,0.0217 8.6068474,0.0148 l 7.289666,-0.0125 z m -8.649134,-6.43105 H 18.498 v -1.95406 -1.95404 h -3.199894 -4.489738 l -1.2359985,-1.23675 -1.235998,-1.23673 H 5.2812523 2.226133 l 0.00232,7.65542 0.00232,7.65541 1.7882473,-4.46462 1.7882472,-4.46463 h 6.9902555 z"
id="path857"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccsssccccccscccccccccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

-125
View File
@@ -1,125 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import FileSaver from 'file-saver';
import { mock } from 'jest-mock-extended';
import { monaco } from 'react-monaco-editor';
import { AsyncSaga } from '../../test';
import { didFailToSaveAs, didSaveAs, open, saveAs } from './actions';
import editor from './sagas';
jest.mock('react-monaco-editor');
jest.mock('file-saver');
test('open', async () => {
const mockEditor = mock<monaco.editor.ICodeEditor>();
const saga = new AsyncSaga(editor, { editor: mockEditor });
const data = new Uint8Array().buffer;
saga.put(open(data));
expect(mockEditor.setValue).toBeCalled();
await saga.end();
});
describe('saveAs', () => {
test('web file system api can succeed', async () => {
const mockEditor = mock<monaco.editor.ICodeEditor>();
const saga = new AsyncSaga(editor, { editor: mockEditor });
// window.showSaveFilePicker is not defined in the test environment
// so we can't use spyOn().
const mockWriteable = mock<FileSystemWritableFileStream>();
const originalShowSaveFilePicker = window.showSaveFilePicker;
window.showSaveFilePicker = jest.fn().mockResolvedValue(
mock<FileSystemFileHandle>({
createWritable: jest.fn().mockResolvedValue(mockWriteable),
}),
);
saga.put(saveAs());
expect(mockEditor.getValue).toHaveBeenCalled();
const action = await saga.take();
expect(action).toEqual(didSaveAs());
expect(window.showSaveFilePicker).toHaveBeenCalled();
expect(mockWriteable.write).toHaveBeenCalled();
expect(mockWriteable.close).toHaveBeenCalled();
await saga.end();
window.showSaveFilePicker = originalShowSaveFilePicker;
});
test('web file system api can fail', async () => {
const mockEditor = mock<monaco.editor.ICodeEditor>();
const saga = new AsyncSaga(editor, { editor: mockEditor });
// window.showSaveFilePicker is not defined in the test environment
// so we can't use spyOn().
const testError = new Error('test error');
const originalShowSaveFilePicker = window.showSaveFilePicker;
window.showSaveFilePicker = jest.fn().mockResolvedValue(
mock<FileSystemFileHandle>({
createWritable: jest.fn().mockRejectedValue(testError),
}),
);
saga.put(saveAs());
expect(mockEditor.getValue).toHaveBeenCalled();
const action = await saga.take();
expect(action).toEqual(didFailToSaveAs(testError));
expect(window.showSaveFilePicker).toHaveBeenCalled();
await saga.end();
window.showSaveFilePicker = originalShowSaveFilePicker;
});
test('fallback can succeed', async () => {
const mockEditor = mock<monaco.editor.ICodeEditor>();
const saga = new AsyncSaga(editor, { editor: mockEditor });
const mockFileSaverSaveAs = jest.spyOn(FileSaver, 'saveAs');
saga.put(saveAs());
expect(mockEditor.getValue).toHaveBeenCalled();
const action = await saga.take();
expect(action).toEqual(didSaveAs());
expect(mockFileSaverSaveAs).toHaveBeenCalled();
await saga.end();
mockFileSaverSaveAs.mockRestore();
});
test('fallback can fail', async () => {
const mockEditor = mock<monaco.editor.ICodeEditor>();
const saga = new AsyncSaga(editor, { editor: mockEditor });
const testError = new Error('test error');
const mockFileSaverSaveAs = jest
.spyOn(FileSaver, 'saveAs')
.mockImplementation(() => {
throw testError;
});
saga.put(saveAs());
expect(mockEditor.getValue).toHaveBeenCalled();
const action = await saga.take();
expect(action).toEqual(didFailToSaveAs(testError));
expect(mockFileSaverSaveAs).toHaveBeenCalled();
await saga.end();
mockFileSaverSaveAs.mockRestore();
});
});
-83
View File
@@ -1,83 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import FileSaver from 'file-saver';
import { call, getContext, put, takeEvery } from 'typed-redux-saga/macro';
import { pythonFileExtension, pythonFileMimeType } from '../pybricksMicropython/lib';
import { ensureError } from '../utils';
import { EditorType } from './Editor';
import { didFailToSaveAs, didSaveAs, open, saveAs } from './actions';
/**
* Partial saga context type for context used in the editor sagas.
*/
export type EditorSagaContext = { editor: EditorType };
const decoder = new TextDecoder();
function* handleOpen(action: ReturnType<typeof open>): Generator {
const editor = yield* getContext<EditorType>('editor');
// istanbul ignore next: it is a bug to dispatch this action with no current editor
if (editor === null) {
console.error('open: No current editor');
return;
}
const text = decoder.decode(action.data);
editor.setValue(text);
}
function* handleSaveAs(): Generator {
const editor = yield* getContext<EditorType>('editor');
// istanbul ignore next: it is a bug to dispatch this action with no current editor
if (editor === null) {
console.error('saveAs: No current editor');
return;
}
const data = editor.getValue();
const blob = new Blob([data], { type: `${pythonFileMimeType};charset=utf-8` });
if (window.showSaveFilePicker) {
// This uses https://wicg.github.io/file-system-access which is not
// available in all browsers
try {
const handle = yield* call(() =>
window.showSaveFilePicker({
suggestedName: 'main.py',
types: [
{
accept: { [pythonFileMimeType]: pythonFileExtension },
// TODO: translate description
description: 'Python Files',
},
],
}),
);
const writeable = yield* call(() => handle.createWritable());
yield* call(() => writeable.write(blob));
yield* call(() => writeable.close());
} catch (err) {
yield* put(didFailToSaveAs(ensureError(err)));
return;
}
} else {
// this is a fallback to use the standard browser download mechanism
try {
FileSaver.saveAs(blob, 'main.py');
} catch (err) {
yield* put(didFailToSaveAs(ensureError(err)));
return;
}
}
yield* put(didSaveAs());
}
export default function* (): Generator {
yield* takeEvery(open, handleOpen);
yield* takeEvery(saveAs, handleSaveAs);
}
-184
View File
@@ -1,184 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="50"
height="50"
viewBox="0 0 26.458333 26.458334"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="download.svg"
inkscape:export-filename="button.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<linearGradient
id="light-circle-fill"
osb:paint="solid"
gradientTransform="matrix(0.28001736,0,0,0.23860295,-25.635992,341.90119)">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop5207" />
</linearGradient>
<linearGradient
id="icon-contents"
osb:paint="solid"
gradientTransform="matrix(0.78378378,0,0,0.78378378,-217.21909,-864.32335)">
<stop
style="stop-color:#3880b7;stop-opacity:1;"
offset="0"
id="stop5179" />
</linearGradient>
<linearGradient
id="light-circle-edge"
osb:paint="solid"
gradientTransform="matrix(0.04571075,0,0,0.26284445,-63.602396,-483.67016)">
<stop
style="stop-color:#b7b7b7;stop-opacity:1;"
offset="0"
id="stop5173" />
</linearGradient>
<linearGradient
id="dark-back"
osb:paint="solid"
gradientTransform="translate(1195.9167,-717.54999)">
<stop
style="stop-color:#333333;stop-opacity:1;"
offset="0"
id="stop1439" />
</linearGradient>
<linearGradient
id="dark-front"
osb:paint="solid"
gradientTransform="translate(4643.556,-4.2349475e-6)">
<stop
style="stop-color:#d4d4d4;stop-opacity:1;"
offset="0"
id="stop1389" />
</linearGradient>
<linearGradient
id="light-front"
osb:paint="solid"
gradientTransform="translate(-313.01005,-269.76128)">
<stop
style="stop-color:#4e4e4e;stop-opacity:1;"
offset="0"
id="stop1184" />
</linearGradient>
<linearGradient
id="light-back"
osb:paint="solid"
gradientTransform="translate(-78.252472)">
<stop
style="stop-color:#eeeeee;stop-opacity:1;"
offset="0"
id="stop1172" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#light-circle-fill"
id="linearGradient5201"
x1="48.865211"
y1="96.215172"
x2="57.67136"
y2="96.215172"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.4926212,0,0,2.4926212,-119.48235,43.562502)" />
<linearGradient
id="light-circle-fill-6"
osb:paint="solid"
gradientTransform="matrix(1.4086066e-4,-8.1325943e-5,8.8105753e-5,1.5260364e-4,2183.2035,3758.7078)">
<stop
style="stop-color:#0088ce;stop-opacity:1;"
offset="0"
id="stop5207-2" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="32"
inkscape:cx="23.424389"
inkscape:cy="35.145846"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1853"
inkscape:window-height="1145"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1"
units="px"
inkscape:measure-start="0,0"
inkscape:measure-end="0,0"
inkscape:snap-global="false"
showguides="true"
inkscape:pagecheckerboard="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="path919"
d="m 3.6391222,283.39048 v -9.65619 h 6.9396798 8.87582 c 1.811001,1.81136 1.68586,1.68657 3.496881,3.49792 v 8.87531 6.93915 H 13.295302 3.6391222 Z"
style="fill:none;fill-opacity:0.50267382;stroke:url(#linearGradient5201);stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="out.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<rect
ry="0"
rx="0"
y="287.20477"
x="8.4382458"
height="6.0376749"
width="9.2444725"
id="rect915"
style="fill:url(#light-circle-fill);fill-opacity:1;stroke:none;stroke-width:1.39999986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="out.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<rect
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="out.png"
style="fill:url(#light-circle-fill);fill-opacity:1;stroke:none;stroke-width:1.39999986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect838"
width="9.2444725"
height="5.1447062"
x="8.4382458"
y="277.1506"
rx="0"
ry="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.9 KiB

-3
View File
@@ -16,7 +16,6 @@ import {
BleDeviceFailToConnectReasonType,
didFailToConnect as bleDidFailToConnect,
} from '../ble/actions';
import { didFailToSaveAs } from '../editor/actions';
import { explorerDeleteFile, explorerDidFailToImportFiles } from '../explorer/actions';
import {
fileStorageDeleteFile,
@@ -111,7 +110,6 @@ test.each([
didFailToFinish(FailToFinishReasonType.Unknown, new Error('test error')),
appDidCheckForUpdate(false),
bleDIServiceDidReceiveFirmwareRevision('3.0.0'),
didFailToSaveAs(new DOMException('test message', 'NotAllowedError')),
fileStorageDidFailToInitialize(new Error('test error')),
fileStorageDidFailToReadFile('test.file', new Error('test error')),
fileStorageDidFailToWriteFile('test.file', new Error('test error')),
@@ -138,7 +136,6 @@ test.each([
serviceWorkerDidSucceed(),
appDidCheckForUpdate(true),
bleDIServiceDidReceiveFirmwareRevision(firmwareVersion),
didFailToSaveAs(new DOMException('test message', 'AbortError')),
fileStorageDidFailToExportFile(
'test.file',
new DOMException('test message', 'AbortError'),
-13
View File
@@ -17,7 +17,6 @@ import {
BleDeviceFailToConnectReasonType,
didFailToConnect as bleDeviceDidFailToConnect,
} from '../ble/actions';
import { didFailToSaveAs } from '../editor/actions';
import { explorerDeleteFile, explorerDidFailToImportFiles } from '../explorer/actions';
import {
fileStorageDeleteFile,
@@ -239,17 +238,6 @@ function* showBootloaderDidFailToConnectError(
}
}
function* showEditorFailToSaveFile(
action: ReturnType<typeof didFailToSaveAs>,
): Generator {
if (action.err.name === 'AbortError') {
// user clicked cancel button - not an error
return;
}
yield* showUnexpectedError(MessageId.EditorFailedToSaveFile, action.err);
}
function* showFlashFirmwareError(
action: ReturnType<typeof didFailToFinish>,
): Generator {
@@ -494,7 +482,6 @@ function* showExplorerFailToImportFiles(
export default function* (): Generator {
yield* takeEvery(bleDeviceDidFailToConnect, showBleDeviceDidFailToConnectError);
yield* takeEvery(bootloaderDidFailToConnect, showBootloaderDidFailToConnectError);
yield* takeEvery(didFailToSaveAs, showEditorFailToSaveFile);
yield* takeEvery(didFailToFinish, showFlashFirmwareError);
yield* takeEvery(didCompile, dismissCompilerError);
yield* takeEvery(didFailToCompile, showCompilerError);
+2 -4
View File
@@ -6,7 +6,7 @@ import { didStart } from './app/actions';
import app from './app/sagas';
import blePybricksService from './ble-pybricks-service/sagas';
import ble from './ble/sagas';
import editor, { EditorSagaContext } from './editor/sagas';
import { EditorType } from './editor/Editor';
import errorLog from './error-log/sagas';
import explorer from './explorer/sagas';
import fileStorage from './fileStorage/sagas';
@@ -29,7 +29,6 @@ export default function* (): Generator {
fileStorage(),
lwp3BootloaderBle(),
lwp3BootloaderProtocol(),
editor(),
errorLog(),
explorer(),
flashFirmware(),
@@ -46,7 +45,6 @@ export default function* (): Generator {
/**
* Combined type for all saga contexts.
*/
export type RootSagaContext = EditorSagaContext &
FirmwareSagaContext &
export type RootSagaContext = { editor: EditorType } & FirmwareSagaContext &
NotificationSagaContext &
TerminalSagaContext;
-6
View File
@@ -3,8 +3,6 @@
import { ButtonGroup } from '@blueprintjs/core';
import React, { useState } from 'react';
import OpenButton from '../editor/OpenButton';
import SaveAsButton from '../editor/SaveAsButton';
import FlashButton from '../firmware/FlashButton';
import BluetoothButton from '../hub/BluetoothButton';
import ReplButton from '../hub/ReplButton';
@@ -25,10 +23,6 @@ const Toolbar: React.VFC = (_props) => {
onContextMenu={preventBrowserNativeContextMenu}
className="pb-toolbar"
>
<ButtonGroup className="pb-toolbar-group pb-align-left">
<OpenButton id="open" />
<SaveAsButton id="saveAs" />
</ButtonGroup>
<ButtonGroup className="pb-toolbar-group pb-align-left">
<RunButton id="run" keyboardShortcut="F5" />
<StopButton id="stop" keyboardShortcut="F6" />
-2
View File
@@ -1,6 +1,4 @@
{
"open": { "tooltip": "Open file" },
"saveAs": { "tooltip": "Download file" },
"stop": { "tooltip": "Stop everything" },
"run": {
"action": { "tooltip": "Download and run this program" },
+2 -4
View File
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2021 The Pybricks Authors
// Copyright (c) 2020-2022 The Pybricks Authors
//
// Button translation keys.
// Toolbar button translation keys.
export enum TooltipId {
Open = 'open.tooltip',
SaveAs = 'saveAs.tooltip',
Run = 'run.action.tooltip',
RunProgress = 'run.progress.tooltip',
Stop = 'stop.tooltip',