Skip to content

Commit

Permalink
Local FS UI (#419)
Browse files Browse the repository at this point in the history

---------

Co-authored-by: David Souther <[email protected]>
  • Loading branch information
netalondon and DavidSouther authored Sep 10, 2024
1 parent ceb954b commit e0dbf0d
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 212 deletions.
6 changes: 4 additions & 2 deletions components/src/chips/memory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const Memory = forwardRef(
showClear = true,
onChange = undefined,
onClear = undefined,
loadTooltip = undefined,
}: {
name?: string;
className?: string;
Expand All @@ -212,6 +213,7 @@ export const Memory = forwardRef(
showClear?: boolean;
onChange?: () => void;
onClear?: () => void;
loadTooltip?: { value: string; placement: string };
},
ref,
) => {
Expand Down Expand Up @@ -306,8 +308,8 @@ export const Memory = forwardRef(
<button
onClick={doLoad}
className="flex-0"
data-tooltip={"Load file"}
data-placement="bottom"
data-tooltip={loadTooltip?.value ?? "Load file"}
data-placement={loadTooltip?.placement ?? "bottom"}
>
{/* <Icon name="upload_file" /> */}
📂
Expand Down
5 changes: 0 additions & 5 deletions components/src/stores/vm.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export interface VmSim {

export interface VMTestSim {
highlight: Span | undefined;
path: string;
}

export interface VmPageState {
Expand Down Expand Up @@ -163,9 +162,6 @@ export function makeVmStore(
}
state.controls.error = error;
},
setPath(state: VmPageState, path: string) {
state.test.path = path;
},
update(state: VmPageState) {
state.vm = reduceVMTest(test, dispatch, setStatus, showHighlight);
state.test.highlight = test.currentStep?.span;
Expand Down Expand Up @@ -203,7 +199,6 @@ export function makeVmStore(
},
test: {
highlight: undefined,
path: "/",
},
files: {
vm: "",
Expand Down
2 changes: 2 additions & 0 deletions web/src/languages/tst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const TstLanguage: monaco.languages.IMonarchLanguage = {

keywords: [
"output-list",
"compare-to",
"output-file",
"set",
"eval",
"output",
Expand Down
77 changes: 50 additions & 27 deletions web/src/pages/asm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Panel } from "../shell/panel";
import { LOADING } from "@nand2tetris/components/messages.js";
import { ROM } from "@nand2tetris/simulator/cpu/memory";
import { Link } from "react-router-dom";
import { isPath } from "src/shell/file_select";
import { AppContext } from "../App.context";
import { PageContext } from "../Page.context";
import URLs from "../urls";
Expand All @@ -21,6 +22,7 @@ export const Asm = () => {
const { filePicker } = useContext(AppContext);
const { stores, setTool } = useContext(PageContext);
const { state, actions, dispatch } = stores.asm;
const { fs, localFsRoot } = useContext(BaseContext);

const sourceCursorPos = useRef(0);
const resultCursorPos = useRef(0);
Expand Down Expand Up @@ -62,15 +64,32 @@ export const Asm = () => {
const path = await filePicker.select({ suffix: ".asm" });
setStatus(LOADING);
requestAnimationFrame(async () => {
await actions.loadAsm(path);
await actions.loadAsm(path.path);
setStatus("");
dispatch.current({
action: "setTitle",
payload: path.split("/").pop() ?? "",
payload: path.path.split("/").pop() ?? "",
});
});
};

const loadCompare = async () => {
const filesRef = await filePicker.selectAllowLocal({ suffix: "hack" });
if (isPath(filesRef)) {
const cmp = await fs.readFile(filesRef.path);
dispatch.current({
action: "setCmp",
payload: { cmp, name: filesRef.path.split("/").pop() },
});
} else {
const file = Array.isArray(filesRef) ? filesRef[0] : filesRef;
dispatch.current({
action: "setCmp",
payload: { cmp: file.content, name: file.name },
});
}
};

const { setStatus } = useContext(BaseContext);

const downloadAsm = () =>
Expand Down Expand Up @@ -151,15 +170,17 @@ export const Asm = () => {
/>
)}
</div>
<fieldset role="group">
<button
data-tooltip="Download"
data-placement="left"
onClick={downloadAsm}
>
⬇️
</button>
</fieldset>
{!localFsRoot && (
<fieldset role="group">
<button
data-tooltip="Download"
data-placement="left"
onClick={downloadAsm}
>
⬇️
</button>
</fieldset>
)}
</>
}
>
Expand Down Expand Up @@ -217,13 +238,15 @@ export const Asm = () => {
>
↩️
</button>
<button
data-tooltip="Download"
data-placement="left"
onClick={downloadHack}
>
⬇️
</button>
{!localFsRoot && (
<button
data-tooltip="Download"
data-placement="left"
onClick={downloadHack}
>
⬇️
</button>
)}
</fieldset>
</div>
</>
Expand Down Expand Up @@ -278,11 +301,13 @@ export const Asm = () => {
<Trans>Compare Code</Trans>
{state.compareName && `: ${state.compareName}`}
</div>
<div>
<fieldset role="group">
<button onClick={compare}>Compare</button>
</fieldset>
</div>
<fieldset role="group">
<button onClick={loadCompare}>📂</button>
</fieldset>
<div className="flex-1" />
<fieldset role="group">
<button onClick={compare}>Compare</button>
</fieldset>
</>
}
>
Expand All @@ -292,11 +317,9 @@ export const Asm = () => {
highlightType={state.compareError ? "error" : "highlight"}
alwaysRecenter={false}
onChange={function (source: string): void {
dispatch.current({
action: "setCmp",
payload: { cmp: source },
});
return;
}}
disabled={true}
onCursorPositionChange={(index) => {
if (index == resultCursorPos.current) {
return;
Expand Down
53 changes: 32 additions & 21 deletions web/src/pages/chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export const Chip = () => {
const hdlToCompile = state.controls.usingBuiltin
? files.hdl
: files.hdl ?? hdl;

await actions.updateFiles({
hdl: hdlToCompile,
tst: files.tst ?? tst,
Expand Down Expand Up @@ -142,12 +141,16 @@ export const Chip = () => {

const loadFile = async () => {
const path = await filePicker.select({ suffix: "hdl" });
actions.loadChip(path);
actions.loadChip(path.path);
};

const selectors = (
<>
<fieldset role="group">
<fieldset
role="group"
data-tooltip="Open an HDL file using this menu"
data-placement="bottom"
>
<select
value={state.controls.project}
onChange={({ target: { value } }) => {
Expand All @@ -157,7 +160,7 @@ export const Chip = () => {
>
{state.controls.projects.map((project) => (
<option key={project} value={project}>
{project}
{`Project ${project}`}
</option>
))}
</select>
Expand Down Expand Up @@ -206,7 +209,13 @@ export const Chip = () => {
)}
{selectors}
<fieldset role="group">
<button onClick={loadFile}>📂</button>
<button
data-tooltip="Open an HDL file directly"
data-placement="left"
onClick={loadFile}
>
📂
</button>
</fieldset>
</>
}
Expand All @@ -224,7 +233,7 @@ export const Chip = () => {
}}
grammar={HDL.parser}
language={"hdl"}
disabled={state.controls.usingBuiltin}
disabled={state.controls.usingBuiltin || state.controls.chipName == ""}
/>
</Panel>
);
Expand Down Expand Up @@ -309,21 +318,23 @@ export const Chip = () => {
{state.sim.invalid ? (
<Trans>Syntax errors in the HDL code or test</Trans>
) : (
<>
<PinContext.Provider value={pinResetDispatcher}>
<FullPinout
sim={state.sim}
toggle={actions.toggle}
setInputValid={setInputValid}
hideInternal={state.controls.usingBuiltin}
/>
</PinContext.Provider>
{visualizations.length > 0 && (
<Accordian summary={<Trans>Visualization</Trans>} open={true}>
<main>{visualizations.map(([_, v]) => v)}</main>
</Accordian>
)}
</>
state.controls.chipName != "" && (
<>
<PinContext.Provider value={pinResetDispatcher}>
<FullPinout
sim={state.sim}
toggle={actions.toggle}
setInputValid={setInputValid}
hideInternal={state.controls.usingBuiltin}
/>
</PinContext.Provider>
{visualizations.length > 0 && (
<Accordian summary={<Trans>Visualization</Trans>} open={true}>
<main>{visualizations.map(([_, v]) => v)}</main>
</Accordian>
)}
</>
)
)}
</Panel>
);
Expand Down
65 changes: 46 additions & 19 deletions web/src/pages/compiler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import URLs from "../urls";
import "./compiler.scss";

export const Compiler = () => {
const { setStatus } = useContext(BaseContext);
const { setStatus, canUpgradeFs } = useContext(BaseContext);
const { tracking } = useContext(AppContext);
const { stores, setTool } = useContext(PageContext);
const { state, dispatch, actions } = stores.compiler;

const [selected, setSelected] = useState(0);
const [suppressStatus, setSuppressStatus] = useState(false);
const [editable, setEditable] = useState(false);

const redirectRef = useRef<HTMLAnchorElement>(null);

Expand Down Expand Up @@ -57,28 +58,46 @@ export const Compiler = () => {
setSelected(Object.keys(state.files).indexOf(state.selected));
}, [state.selected]);

const uploadFiles = async () => {
const handle = await openNand2TetrisDirectory();
const fs = new FileSystem(new FileSystemAccessFileSystemAdapter(handle));

dispatch.current({
action: "setTitle",
payload: `${handle.name} / *.jack`,
});
const loadRef = useRef<HTMLInputElement>(null);

const empty =
(await fs.scandir("/")).filter(
(entry) => entry.isFile() && entry.name.endsWith(".jack"),
).length == 0;
const uploadFiles = async () => {
if (canUpgradeFs) {
const handle = await openNand2TetrisDirectory();
const fs = new FileSystem(new FileSystemAccessFileSystemAdapter(handle));
const empty =
(await fs.scandir("/")).filter(
(entry) => entry.isFile() && entry.name.endsWith(".jack"),
).length == 0;

if (empty) {
setStatus("No .jack files in the selected folder");
setSuppressStatus(true);
if (empty) {
setStatus("No .jack files in the selected folder");
setSuppressStatus(true);
} else {
setStatus("");
actions.loadProject(fs, `${handle.name} / *.jack`);
setEditable(true);
}
} else {
setStatus("");
setSuppressStatus(false);
loadRef.current?.click();
setEditable(false);
}
};

const onLoad = async () => {
if (
!loadRef.current ||
!loadRef.current?.files ||
loadRef.current.files?.length == 0
) {
return;
}
actions.loadProject(fs, `${handle.name} / *.jack`);
const files: Record<string, string> = {};
for (const file of loadRef.current.files) {
if (file.name.endsWith(".jack")) {
files[file.name.replace(".jack", "")] = await file.text();
}
}
actions.loadFiles(files);
};

const compileAll = (): VmFile[] => {
Expand Down Expand Up @@ -147,6 +166,13 @@ export const Compiler = () => {

return (
<div className="Page CompilerPage grid">
<input
type="file"
ref={loadRef}
webkitdirectory=""
onChange={onLoad}
style={{ display: "none" }}
></input>
<Link
ref={redirectRef}
to={URLs["vm"].href}
Expand Down Expand Up @@ -221,6 +247,7 @@ export const Compiler = () => {
}}
error={state.compiled[file].error}
language={"jack"}
disabled={!editable}
/>
</Tab>
))}
Expand Down
Loading

0 comments on commit e0dbf0d

Please sign in to comment.