Skip to content
60 changes: 60 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@emotion/react": "^11.1.5",
"@emotion/styled": "^11.1.5",
"@microbit/microbit-fs": "^0.9.1",
"@monaco-editor/react": "^4.1.0",
"@sentry/browser": "^6.2.3",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
Expand All @@ -30,10 +31,13 @@
"@types/node": "^14.14.35",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"ace-builds": "^1.4.12",
"dapjs": "2.2.0",
"file-saver": "^2.0.5",
"framer-motion": "^3.10.6",
"monaco-editor": "^0.23.0",
"react": "^17.0.2",
"react-ace": "^9.4.0",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-scripts": "4.0.3",
Expand Down
4 changes: 2 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ html,
body,
#root,
#root > div > .spaces-space {
height: var(--ios-vvh, 100vh);
max-height: var(--ios-vvh, 100vh);
height: var(--ios-vvh, -webkit-fill-available, 100vh);
max-height: var(--ios-vvh, -webkit-fill-available, 100vh);
}
1 change: 1 addition & 0 deletions src/editor/EditorArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const EditorArea = ({ filename, onSelectedFileChanged }: EditorAreaProps) => {
<Box flex="1 1 auto" height={0} position="relative">
<ZoomControls
size="lg"
display={["none", "none", "flex"]}
position="absolute"
top={0}
right={0}
Expand Down
31 changes: 31 additions & 0 deletions src/editor/ace/Ace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import AceEditor from "react-ace";

import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-kuroir";
import "ace-builds/src-noconflict/ext-language_tools";
import { EditorComponentProps } from "../editor";

const ptToPixelRatio = 96 / 72;

const Ace = ({ defaultValue, onChange, fontSize }: EditorComponentProps) => {
return (
<AceEditor
mode="python"
theme="kuroir"
tabSize={4}
height="100%"
width="100%"
enableBasicAutocompletion
fontSize={fontSize * ptToPixelRatio}
defaultValue={defaultValue}
onChange={onChange}
name="ace-editor"
editorProps={{ $blockScrolling: true }}
// This should really be part of the theme but they seem to need
// be quite painful to get up and running with.
style={{ backgroundColor: "var(--code-background)" }}
/>
);
};

export default Ace;
13 changes: 2 additions & 11 deletions src/editor/codemirror/CodeMirror.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EditorState } from "@codemirror/state";
import { Text } from "@codemirror/text";
import { EditorView } from "@codemirror/view";
import { useEffect, useMemo, useRef } from "react";
import { EditorComponentProps } from "../editor";
import { blocks, blocksCompartment } from "./blocks";
import "./CodeMirror.css";
import {
Expand All @@ -10,15 +10,6 @@ import {
themeExtensionsCompartment,
} from "./config";

interface CodeMirrorProps {
className?: string;
defaultValue: Text;
onChange: (doc: Text) => void;

fontSize: number;
highlightCodeStructure: boolean;
}

/**
* A React component for CodeMirror 6.
*
Expand All @@ -33,7 +24,7 @@ const CodeMirror = ({
onChange,
fontSize,
highlightCodeStructure,
}: CodeMirrorProps) => {
}: EditorComponentProps) => {
const elementRef = useRef<HTMLDivElement | null>(null);
const viewRef = useRef<EditorView | null>(null);

Expand Down
17 changes: 17 additions & 0 deletions src/editor/editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface TextDocument {
toString(): string;
}

/**
* Common interface for embedded editors.
*
* Useful while we're exploring options.
*/
export interface EditorComponentProps {
className?: string;
defaultValue: string;
onChange: (doc: TextDocument) => void;

fontSize: number;
highlightCodeStructure: boolean;
}
47 changes: 47 additions & 0 deletions src/editor/monaco/Monaco.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Editor, { Monaco, OnChange } from "@monaco-editor/react";
import { EditorComponentProps } from "../editor";

const ptToPixelRatio = 96 / 72;

const configureMonaco = (monaco: Monaco) => {
monaco.editor.defineTheme("microbit", {
base: "vs",
inherit: true,
rules: [],
colors: {
"editor.background": "#fffff8", // CSS variable doesn't work here
},
});
};

const OurMonaco = ({
defaultValue,
onChange,
fontSize,
}: EditorComponentProps) => {
const handleChange: OnChange = (value, event) => {
if (value) {
onChange(value);
}
};
return (
<Editor
beforeMount={configureMonaco}
height="100%"
width="100%"
defaultLanguage="python"
defaultValue={defaultValue}
onChange={handleChange}
theme="microbit"
options={{
dragAndDrop: true,
fontSize: ptToPixelRatio * fontSize,
minimap: {
enabled: false,
},
}}
/>
);
};

export default OurMonaco;
16 changes: 8 additions & 8 deletions src/project/project-hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Text } from "@codemirror/state";
import { useCallback, useEffect, useMemo, useState } from "react";
import useActionFeedback from "../common/use-action-feedback";
import { useDialogs } from "../common/use-dialogs";
import useIsUnmounted from "../common/use-is-unmounted";
import { useDevice } from "../device/device-hooks";
import { TextDocument } from "../editor/editor";
import { EVENT_PROJECT_UPDATED, Project } from "../fs/fs";
import { useFileSystem } from "../fs/fs-hooks";
import { VersionAction } from "../fs/storage";
import { useLogging } from "../logging/logging-hooks";
import { useDialogs } from "../common/use-dialogs";
import { ProjectActions } from "./project-actions";
import { useSelection } from "../workbench/use-selection";
import { ProjectActions } from "./project-actions";

/**
* Hook exposing the main UI actions.
Expand Down Expand Up @@ -64,10 +64,10 @@ export const useProject = (): Project => {
*/
export const useProjectFileText = (
filename: string
): [Text | undefined, (text: Text) => void] => {
): [string | undefined, (doc: TextDocument) => void] => {
const fs = useFileSystem();
const actionFeedback = useActionFeedback();
const [initialValue, setInitialValue] = useState<Text | undefined>();
const [initialValue, setInitialValue] = useState<string | undefined>();
const isUnmounted = useIsUnmounted();
useEffect(() => {
const loadData = async () => {
Expand All @@ -76,7 +76,7 @@ export const useProjectFileText = (
const { data } = await fs.read(filename);
const text = new TextDecoder().decode(data);
if (!isUnmounted()) {
setInitialValue(Text.of(text.split("\n")));
setInitialValue(text);
}
}
} catch (e) {
Expand All @@ -88,9 +88,9 @@ export const useProjectFileText = (
}, [fs, filename, actionFeedback, isUnmounted]);

const handleChange = useCallback(
(text: Text) => {
(text: TextDocument) => {
try {
const content = text.sliceString(0, undefined, "\n");
const content = text.toString();
fs.write(filename, content, VersionAction.MAINTAIN);
} catch (e) {
actionFeedback.unexpectedError(e);
Expand Down