Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion package-lock.json

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

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@
"vscode-languageserver-protocol": "^3.16.0",
"web-vitals": "^1.1.1",
"xterm": "4.14.1",
"xterm-addon-fit": "^0.5.0"
"xterm-addon-fit": "^0.5.0",
"y-codemirror.next": "^0.3.5",
"y-indexeddb": "^9.0.12",
"y-protocols": "^1.0.6",
"yjs": "^13.6.27"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
Expand Down
24 changes: 15 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import SettingsProvider from "./settings/settings";
import BeforeUnloadDirtyCheck from "./workbench/BeforeUnloadDirtyCheck";
import { SelectionProvider } from "./workbench/use-selection";
import Workbench from "./workbench/Workbench";
import { ProjectStorageProvider } from "./project-persistence/ProjectStorageProvider";
import ProjectPageRouting from "./ProjectPageRouting";

const isMockDeviceMode = () =>
// We use a cookie set from the e2e tests. Avoids having separate test and live builds.
Expand Down Expand Up @@ -79,15 +81,19 @@ const App = () => {
<SearchProvider>
<SelectionProvider>
<DialogProvider>
<RouterProvider>
<ConsentProvider>
<ProjectDropTarget>
<ActiveEditorProvider>
<Workbench />
</ActiveEditorProvider>
</ProjectDropTarget>
</ConsentProvider>
</RouterProvider>
<ProjectStorageProvider>
<RouterProvider>
<ProjectPageRouting>
<ConsentProvider>
<ProjectDropTarget>
<ActiveEditorProvider>
<Workbench />
</ActiveEditorProvider>
</ProjectDropTarget>
</ConsentProvider>
</ProjectPageRouting>
</RouterProvider>
</ProjectStorageProvider>
</DialogProvider>
</SelectionProvider>
</SearchProvider>
Expand Down
34 changes: 34 additions & 0 deletions src/ProjectPageRouting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ReactNode, useEffect } from "react";
import { useRouterState } from "./router-hooks";
import ProjectBrowser from "./project/ProjectBrowser";
import { useProjectList } from "./project-persistence/project-list-hooks";
import { usePersistentProject } from "./project-persistence/persistent-project-hooks";

interface ProjectPageRoutingProps {
children: ReactNode;
}
const ProjectPageRouting = ({ children }: ProjectPageRoutingProps) => {
const [{ tab }] = useRouterState();
const { projectList, restoreStoredProject } = useProjectList();
const { projectId } = usePersistentProject();

useEffect(() => {
if (!projectId && projectList) {
const restoreState = async () => {
const restoredProject = await restoreStoredProject(projectList[0].id);
if (!restoredProject && typeof tab !== "undefined") {
history.replaceState(null, "", "/");
window.dispatchEvent(new PopStateEvent("popstate"));
}
};
void restoreState();
}
}, [projectId, projectList, restoreStoredProject, tab]);

if (typeof tab === "undefined") {
return <ProjectBrowser />;
}
return children;
};

export default ProjectPageRouting;
17 changes: 16 additions & 1 deletion src/editor/EditorContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useSettings } from "../settings/settings";
import { WorkbenchSelection } from "../workbench/use-selection";
import Editor from "./codemirror/CodeMirror";
import ModuleOverlay from "./ModuleOverlay";
import { usePersistentProject } from "../project-persistence/persistent-project-hooks";
import * as Y from "yjs";

interface EditorContainerProps {
selection: WorkbenchSelection;
Expand All @@ -25,23 +27,36 @@ const EditorContainer = ({ selection }: EditorContainerProps) => {
}, [setSettings, settings]);
// Note fileInfo is not updated for ordinary text edits.
const [fileInfo, onFileChange] = useProjectFileText(selection.file);
const { ydoc, awareness } = usePersistentProject();

const ytext = ydoc?.getMap("files").get(selection.file) as Y.Text;

if (ytext === null) return null;
if (fileInfo === undefined) {
return null;
}

awareness!.setLocalStateField("user", {
name: "micro:bit tester",
color: "yellow",
});

// TODO: represent fileInfo in project?

return fileInfo.isThirdPartyModule &&
!settings.allowEditingThirdPartyModules ? (
<ModuleOverlay moduleData={fileInfo.moduleData} />
) : (
<Editor
defaultValue={fileInfo.initialValue}
selection={selection}
onChange={onFileChange}
fontSize={settings.fontSize}
codeStructureOption={settings.codeStructureHighlight}
parameterHelpOption={settings.parameterHelp}
warnOnV2OnlyFeatures={settings.warnForApiUnsupportedByDevice}
disableV2OnlyFeaturesWarning={disableV2OnlyFeaturesWarning}
awareness={awareness!}
text={ytext}
/>
);
};
Expand Down
26 changes: 19 additions & 7 deletions src/editor/codemirror/CodeMirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ import { lintGutter } from "./lint/lint";
import { codeStructure } from "./structure-highlighting";
import themeExtensions from "./themeExtensions";
import { useDevice } from "../../device/device-hooks";
import * as Y from "yjs";
import { Awareness } from 'y-protocols/awareness.js'
import { yCollab } from "y-codemirror.next";

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

text: Y.Text;
awareness: Awareness;
selection: WorkbenchSelection;
fontSize: number;
codeStructureOption: CodeStructureOption;
Expand All @@ -64,9 +67,10 @@ interface CodeMirrorProps {
* (e.g. based on the file being edited).
*/
const CodeMirror = ({
defaultValue,
className,
onChange,
text,
awareness,
selection,
fontSize,
codeStructureOption,
Expand All @@ -87,6 +91,7 @@ const CodeMirror = ({
const [sessionSettings, setSessionSettings] = useSessionSettings();
const { apiReferenceMap } = useDocumentation();
const device = useDevice();
const textRef = useRef<Y.Text>();

// Reset undo/redo events on file change.
useEffect(() => {
Expand All @@ -108,8 +113,14 @@ const CodeMirror = ({
);

useEffect(() => {
const initializing = !viewRef.current;
let initializing = !viewRef.current;
// Recreate if the text doc changes
if (!initializing && textRef.current !== text) {
elementRef.current?.replaceChildren();
initializing = true;
}
if (initializing) {
textRef.current = text;
const notify = EditorView.updateListener.of((update) => {
if (update.docChanged) {
onChange(update.state.sliceDoc(0));
Expand All @@ -121,8 +132,9 @@ const CodeMirror = ({
}
});
const state = EditorState.create({
doc: defaultValue,
doc: text.toString(),
extensions: [
yCollab(text, awareness),
notify,
editorConfig,
// Extension requires external state.
Expand Down Expand Up @@ -165,14 +177,13 @@ const CodeMirror = ({
state,
parent: elementRef.current!,
});

viewRef.current = view;
setActiveEditor(new EditorActions(view, logging, actionFeedback, intl));
}
}, [
awareness,
actionFeedback,
client,
defaultValue,
intl,
logging,
onChange,
Expand All @@ -186,6 +197,7 @@ const CodeMirror = ({
apiReferenceMap,
device,
disableV2OnlyFeaturesWarning,
text,
]);
useEffect(() => {
// Do this separately as we don't want to destroy the view whenever options needed for initialization change.
Expand Down
12 changes: 12 additions & 0 deletions src/fs/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,18 @@ export class FileSystem extends TypedEventTarget<EventMap> {
await this.replaceCommon(project.projectName);
}

async replaceWithMultipleStrings(project: {
files: Record<string, string>,
projectName: string
}): Promise<void> {
const fs = await this.initialize();
fs.ls().forEach((f) => fs.remove(f));
for (const key in project.files) {
fs.write(key, new TextEncoder().encode(project.files[key]));
}
await this.replaceCommon(project.projectName);
}

async replaceWithHexContents(
projectName: string,
hex: string
Expand Down
Loading