Skip to content

Commit

Permalink
Merge pull request #30 from FluffyLabs/ui-finetuning
Browse files Browse the repository at this point in the history
Yet another set of UI improvements
  • Loading branch information
wkwiatek authored Aug 12, 2024
2 parents 9e7cdc1 + 1cb69db commit fad0cdc
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 107 deletions.
34 changes: 34 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-table": "^8.19.3",
"@typeberry/pvm": "0.0.1-18cd6dd",
"class-variance-authority": "^0.7.0",
Expand Down
153 changes: 104 additions & 49 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Registers } from "./components/Registers";
import { ExpectedState, InitialState, PageMapItem, Pvm, RegistersArray, Status } from "./types/pvm";

import { CurrentInstruction, initPvm, nextInstruction } from "./components/Debugger/debug";
import { RefreshCcw, StepForward } from "lucide-react";
import { Play, RefreshCcw, StepForward } from "lucide-react";
import { disassemblify } from "./pvm-packages/pvm/disassemblify";
import { Header } from "@/components/Header";
import { ProgramLoader } from "@/components/ProgramLoader";
Expand All @@ -19,6 +19,7 @@ import { Label } from "@/components/ui/label.tsx";
import { InstructionMode } from "@/components/Instructions/types.ts";
import { PvmSelect } from "@/components/PvmSelect";
import { NumeralSystemSwitch } from "@/components/NumeralSystemSwitch";
import { InitialLoadProgramCTA } from "@/components/InitialLoadProgramCTA";

function App() {
const [program, setProgram] = useState<number[]>([]);
Expand All @@ -35,18 +36,32 @@ function App() {
const [currentInstruction, setCurrentInstruction] = useState<CurrentInstruction>();
const [instructionMode, setInstructionMode] = useState<InstructionMode>(InstructionMode.ASM);
const [currentState, setCurrentState] = useState<ExpectedState>(initialState as ExpectedState);
const [previousState, setPreviousState] = useState<ExpectedState>(initialState as ExpectedState);

const [pvm, setPvm] = useState<Pvm>();
const [isDebugFinished, setIsDebugFinished] = useState(false);
const [isInitialCTA, setIsInitialCTA] = useState(true);

// const handleClick = () => {
// window.scrollTo(0, 0);
//
// const result = disassemblify(new Uint8Array(program));
// console.log(result);
// setProgramPreviewResult(result);
// // setProgramRunResult(result.programRunResult);
// };
useEffect(() => {
if (pvm) {
setCurrentStateFromPvm(pvm);
}
}, [pvm, currentInstruction]);

const setCurrentStateFromPvm = (pvm: Pvm) => {
const currentState = {
pc: pvm.getPC(),
regs: Array.from(pvm.getRegisters()) as RegistersArray,
gas: pvm.getGas(),
pageMap: pvm.getMemory() as unknown as PageMapItem[],
memory: pvm.getMemory(),
status: pvm.getStatus() as unknown as Status,
};
setCurrentState((prevState) => {
setPreviousState(prevState);
return currentState;
});
};

const handleFileUpload = ({ /*expected, */ initial, program }: ProgramUploadFileOutput) => {
// setExpectedResult(expected);
Expand All @@ -60,19 +75,6 @@ function App() {
setProgramPreviewResult(result);
};

useEffect(() => {
if (pvm) {
setCurrentState({
pc: pvm.getPC(),
regs: Array.from(pvm.getRegisters()) as RegistersArray,
gas: pvm.getGas(),
pageMap: pvm.getMemory() as unknown as PageMapItem[],
memory: pvm.getMemory(),
status: pvm.getStatus() as unknown as Status,
});
}
}, [pvm, currentInstruction]);

const onNext = () => {
if (!pvm) return;

Expand All @@ -87,6 +89,23 @@ function App() {
}
};

const handleRunProgram = () => {
if (!pvm) return;

pvm?.runProgram();

setIsProgramEditMode(false);
setIsDebugFinished(true);
setCurrentInstruction(programPreviewResult?.[0]);
setCurrentStateFromPvm(pvm);
};

const restartProgram = () => {
setIsDebugFinished(false);
setPvm(initPvm(program, initialState));
setCurrentState(initialState);
};

return (
<>
<Header />
Expand All @@ -100,15 +119,16 @@ function App() {
setIsDebugFinished(false);
setPvm(initPvm(program, initialState));
setCurrentState(initialState);
setCurrentInstruction(programPreviewResult?.[0]);
}}
>
<RefreshCcw className="w-3.5 mr-1.5" />
Restart
</Button>
{/*<Button className="mr-3" onClick={handleClick}>*/}
{/* <Play className="w-3.5 mr-1.5" />*/}
{/* Run*/}
{/*</Button>*/}
<Button className="mr-3" onClick={handleRunProgram} disabled={isDebugFinished}>
<Play className="w-3.5 mr-1.5" />
Run
</Button>
<Button className="mr-3" onClick={onNext} disabled={isDebugFinished}>
<StepForward className="w-3.5 mr-1.5" /> Step
</Button>
Expand All @@ -122,25 +142,49 @@ function App() {

<div className="grid auto-rows-fr grid-cols-[3fr_200px_3fr_3fr] gap-1.5 pt-2">
<div>
{isProgramEditMode && (
<>
<ProgramLoader program={program} setProgram={setProgram} />
</>
{isInitialCTA && (
<InitialLoadProgramCTA
onFileUpload={(uploadedProgram) => {
handleFileUpload(uploadedProgram);
setIsInitialCTA(false);
}}
onEditClick={() => {
setIsInitialCTA(false);
}}
/>
)}

{!isProgramEditMode && (
{!isInitialCTA && (
<>
<Instructions
programPreviewResult={programPreviewResult}
currentInstruction={currentInstruction}
instructionMode={instructionMode}
/>
{isProgramEditMode && (
<>
<ProgramLoader program={program} setProgram={setProgram} />
</>
)}

{!isProgramEditMode && (
<>
<Instructions
programPreviewResult={programPreviewResult}
currentInstruction={currentInstruction}
instructionMode={instructionMode}
/>
</>
)}
</>
)}
</div>

<div>
<Registers currentState={currentState} setCurrentState={setCurrentState} />
<Registers
currentState={isProgramEditMode ? initialState : currentState}
previousState={isProgramEditMode ? initialState : previousState}
onCurrentStateChange={(state) => {
setInitialState(state);
setCurrentState(state);
setPvm(initPvm(program, state));
}}
allowEditing={isProgramEditMode}
/>
</div>

<div>
Expand All @@ -158,19 +202,30 @@ function App() {
<div className="flex items-center justify-between">
<div>
{isProgramEditMode && <ProgramUpload onFileUpload={handleFileUpload} />}
{!isProgramEditMode && <Button onClick={() => setIsProgramEditMode(true)}>Edit program</Button>}
{!isProgramEditMode && (
<Button
onClick={() => {
restartProgram();
setIsProgramEditMode(true);
}}
>
Edit program
</Button>
)}
</div>
<div>
<div className="flex items-center space-x-2">
<Label htmlFor="instruction-mode">ASM</Label>
<Switch
id="instruction-mode"
onCheckedChange={(checked) =>
setInstructionMode(checked ? InstructionMode.BYTECODE : InstructionMode.ASM)
}
/>
<Label htmlFor="instruction-mode">Bytecode</Label>
</div>
{!isProgramEditMode && (
<div className="flex items-center space-x-2">
<Label htmlFor="instruction-mode">ASM</Label>
<Switch
id="instruction-mode"
onCheckedChange={(checked) =>
setInstructionMode(checked ? InstructionMode.BYTECODE : InstructionMode.ASM)
}
/>
<Label htmlFor="instruction-mode">Bytecode</Label>
</div>
)}
</div>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions src/components/InitialLoadProgramCTA/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ProgramUpload } from "@/components/ProgramUpload";
import { ProgramUploadFileOutput } from "@/components/ProgramUpload/types.ts";

export const InitialLoadProgramCTA = ({
onEditClick,
onFileUpload,
}: {
onEditClick: () => void;
onFileUpload: (val: ProgramUploadFileOutput) => void;
}) => {
return (
<div className="border-2 rounded-md h-full flex justify-center flex-col items-center gap-3" onClick={onEditClick}>
<p>Edit / paste raw machine program or ...</p>
<ProgramUpload onFileUpload={onFileUpload} />
</div>
);
};
26 changes: 16 additions & 10 deletions src/components/Instructions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const Instructions = ({
}) => {
const { numeralSystem } = useContext(NumeralSystemContext);

const isActive = (programRow: CurrentInstruction) => {
const isLastRunInstruction = (programRow: CurrentInstruction) => {
if (!currentInstruction) {
return false;
}
Expand All @@ -40,13 +40,17 @@ export const Instructions = ({
);
};

const isActive = (index: number) => {
return index === programPreviewResult?.findIndex((programRow) => isLastRunInstruction(programRow));
};

return (
<div className="font-mono overflow-auto scroll-auto border-2 rounded-md h-full">
<Table>
<TableBody>
{!!programPreviewResult?.length &&
programPreviewResult.map((programRow, i) => (
<TableRow className={classNames("hover:bg-gray-300", { "bg-gray-200": isActive(programRow) })} key={i}>
<TableRow className={classNames("hover:bg-gray-300", { "bg-gray-200": isActive(i - 1) })} key={i}>
{instructionMode === InstructionMode.BYTECODE && (
<TableCell className="p-1.5">
{programRow.instructionBytes && (
Expand All @@ -57,15 +61,17 @@ export const Instructions = ({
</TableCell>
)}
{instructionMode === InstructionMode.ASM && (
<TableCell className="p-1.5">
<span className="uppercase font-bold">{programRow.name}</span>
</TableCell>
<>
<TableCell className="p-1.5">
<span className="uppercase font-bold">{programRow.name}</span>
</TableCell>
<TableCell className="p-1.5">
<span className="">
{"args" in programRow && mapInstructionsArgsByType(programRow.args, numeralSystem)}
</span>
</TableCell>
</>
)}
<TableCell className="p-1.5">
<span className="">
{"args" in programRow && mapInstructionsArgsByType(programRow.args, numeralSystem)}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
Expand Down
22 changes: 15 additions & 7 deletions src/components/ProgramLoader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@ import { Textarea } from "@/components/ui/textarea.tsx";
import { useEffect, useState } from "react";
import classNames from "classnames";

export const ProgramLoader = ({ program, setProgram }: { onFileUpload?: (val: ProgramUploadFileOutput) => void; program: number[]; setProgram: (val: number[]) => void }) => {
export const ProgramLoader = ({
program,
setProgram,
}: {
onFileUpload?: (val: ProgramUploadFileOutput) => void;
program: number[];
setProgram: (val: number[]) => void;
}) => {
const [programInput, setProgramInput] = useState(JSON.stringify(program));
const [isInvalidProgram, setIsInvalidProgram] = useState(false);

useEffect(() => {
setProgramInput(JSON.stringify(program));
}, [program]);
// const handleFileUpload = (args: ProgramUploadFileOutput) => {
// setProgramInput(JSON.stringify(args.program));
// onFileUpload(args);
// };

return (
<div className={classNames("flex gap-1 flex-col border-2 rounded-md h-full", { "border-red-500": isInvalidProgram })}>
<div
className={classNames("flex gap-1 flex-col border-2 rounded-md h-full", { "border-red-500": isInvalidProgram })}
>
<div className="w-full h-full">
<Textarea
className={classNames("w-full h-full font-mono border-0", { "focus-visible:ring-3 focus-visible:outline-none active:outline-none": isInvalidProgram })}
autoFocus
className={classNames("w-full h-full font-mono border-0", {
"focus-visible:ring-3 focus-visible:outline-none active:outline-none": isInvalidProgram,
})}
id="program"
placeholder="Paste the program as an array of numbers"
value={programInput}
Expand Down
Loading

0 comments on commit fad0cdc

Please sign in to comment.