Web-Ide mit aufgenommen

This commit is contained in:
Riwoldt
2026-04-09 14:14:56 +02:00
parent 64816c45cc
commit 15cfaf332d
489 changed files with 186891 additions and 0 deletions
@@ -0,0 +1,160 @@
import { makeVisualizationsWithId } from "@nand2tetris/components/chips/visualizations.js";
import { Clockface } from "@nand2tetris/components/clockface.js";
import { FullPinout } from "@nand2tetris/components/pinout.js";
import { useChipPageStore } from "@nand2tetris/components/stores/chip.store.js";
import * as Not from "@nand2tetris/projects/project_01/01_not.js";
import { VSCodeButton, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react";
import {
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { VSCodeContext } from "./vscode";
function App() {
const { state, actions, dispatch } = useChipPageStore();
const { api } = useContext(VSCodeContext);
const [hdl, setHdl] = useState(Not.hdl);
const [loaded, setLoaded] = useState(false);
const compile = useCallback(
async (hdl: string) => {
setHdl(hdl);
await actions.updateFiles({ hdl, tst: "// No test", cmp: "" });
},
[setHdl, actions],
);
const onMessage = useCallback(
(
event: MessageEvent<
Partial<{ nand2tetris: boolean; hdl: string; chipName: string }>
>,
) => {
if (!event.data?.nand2tetris) return;
if (event.data.hdl) compile(event.data.hdl ?? "");
if (event.data.chipName)
dispatch.current({
action: "updateChip",
payload: {
chipName: event.data.chipName,
},
});
setLoaded(true);
},
[compile, dispatch],
);
useEffect(() => {
window.addEventListener("message", onMessage);
return () => {
window.removeEventListener("message", onMessage);
};
}, [onMessage]);
useEffect(() => {
api.postMessage({ nand2tetris: true, ready: true });
}, [api]);
const [useBuiltin, setUseBuiltin] = useState(false);
const toggleUseBuiltin = async () => {
if (useBuiltin) {
compile(hdl);
setUseBuiltin(false);
} else {
actions.toggleBuiltin();
setUseBuiltin(true);
}
};
const clockActions = useMemo(
() => ({
toggle() {
actions.clock();
},
reset() {
actions.reset();
},
}),
[actions],
);
const chipButtons = state.controls.error ? (
<p>{state.controls.error?.message}</p>
) : (
<>
<VSCodeCheckbox onChange={toggleUseBuiltin}>Use Builtin</VSCodeCheckbox>
<fieldset role="group">
<VSCodeButton
onClick={actions.eval}
onKeyDown={actions.eval}
disabled={!state.sim.pending}
>
Eval
</VSCodeButton>
<VSCodeButton
onClick={clockActions.toggle}
style={{ maxWidth: "initial" }}
disabled={!state.sim.clocked}
>
Clock:{"\u00a0"}
<Clockface />
</VSCodeButton>
<VSCodeButton
onClick={clockActions.reset}
style={{ maxWidth: "initial" }}
disabled={!state.sim.clocked}
>
Reset
</VSCodeButton>
</fieldset>
</>
);
const visualizations: [string, ReactNode][] = makeVisualizationsWithId({
parts: state.sim.chip,
});
const pinsPanel = (
<>
<h3>Chip {state.controls.chipName}</h3>
{chipButtons}
{state.sim.invalid ? (
<p>Invalid Chip</p>
) : (
<>
<FullPinout
sim={state.sim}
toggle={actions.toggle}
setInputValid={() => {
console.log("TODO: Handle Input Valid");
}}
/>
<h4>Visualizations</h4>
{visualizations.length > 0 ? (
visualizations.map(([p, v]) => <div key={p}>{v}</div>)
) : (
<p>None</p>
)}
</>
)}
{/* DEBUG */}
<textarea style={{ display: "none" }}>{hdl}</textarea>
</>
);
return loaded ? (
pinsPanel
) : (
<>
<h3>HDL</h3>
<p>Open an HDL chip to begin</p>
</>
);
}
export default App;
@@ -0,0 +1,47 @@
import {
FileSystem,
ObjectFileSystemAdapter,
} from "@davidsouther/jiffies/lib/esm/fs.js";
import { useDialog } from "@nand2tetris/components/dialog";
import { BaseContext } from "@nand2tetris/components/stores/base.context.js";
import * as Not from "@nand2tetris/projects/project_01/01_not.js";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { StatusSeverity } from "@nand2tetris/components/stores/base.context";
const baseContext: BaseContext = {
fs: new FileSystem(
new ObjectFileSystemAdapter({ "projects/01/Not.hdl": Not.hdl }),
),
canUpgradeFs: false,
async upgradeFs() {},
closeFs() {},
storage: {},
status: { message: "", severity: "INFO" },
setStatus: (status: string | { message: string; severity?: StatusSeverity }): void => {
// api.postMessage({ nand2tetris: true, showMessage: status });
if (typeof status === "string") {
console.log(status);
} else {
console.log(`${status.severity}: ${status.message}`);
}
},
permissionPrompt: {} as ReturnType<typeof useDialog>,
// eslint-disable-next-line @typescript-eslint/no-empty-function
async requestPermission() {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
loadFs() {},
};
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
root.render(
<React.StrictMode>
<BaseContext.Provider value={baseContext}>
<App />
</BaseContext.Provider>
</React.StrictMode>,
);
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";
@@ -0,0 +1,6 @@
import { createContext } from "react";
import type { WebviewApi } from "vscode-webview";
const api: WebviewApi<unknown> = acquireVsCodeApi?.();
export const VSCodeContext = createContext({ api });