improve layout, store program in url

* fix the command list being way too tall when there are few states
* move the output to the top, maybe where it's like this (wrapping):
+-----+
|1 2 3|
|4 15 |
|16 32|
+-----+
* store and load program from url (maybe make own hook?)
This commit is contained in:
mehbark 2023-04-17 16:33:51 -04:00
parent 1b54cd0d12
commit 4cb13e0a16
5 changed files with 15527 additions and 15256 deletions

30682
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -40,5 +40,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"react-use": "^17.4.0"
}
}

View file

@ -22,44 +22,65 @@ html {
color: var(--fg);
background-color: var(--bg);
resize: none;
outline: 1px solid var(--fg);
border: none;
border: 1px solid var(--fg);
}
.program:focus-within {
filter: brightness(1.2);
outline: none;
}
.info {
z-index: -413;
display: grid;
width: 50%;
grid-template-areas: "ops stack" "states states";
grid-template-rows: max-content;
}
.ops,
/* .ops,
.states {
width: 50vw;
}
} */
.ops {
/* position: fixed; */
/* top: 0;
left: 50%; */
min-height: 25vh;
margin-bottom: 0;
background-color: var(--bg);
border: 1px solid var(--fg);
border-left: none;
grid-area: ops;
padding: 5px;
}
.states {
overflow: scroll;
grid-area: states;
}
.state {
border-right: 1px solid var(--fg);
border-bottom: 1px solid var(--fg);
/* margin: 5px; */
padding-left: 5px;
}
.state > * {
/* .state > * {
margin-left: 5px;
}
} */
.stack {
display: block;
border: 1px solid var(--fg);
border-left: none;
grid-area: stack;
padding: 5px;
}
.stack::before {
content: "Stack: ";
}
/* later i can do .op.active and .state.active if necessary */
@ -76,6 +97,10 @@ html {
white-space: pre;
}
.output {
display: flex;
}
/* https://www.joshwcomeau.com/css/custom-css-reset/ */
/*
1. Use a more-intuitive box-sizing model.

View file

@ -1,16 +1,24 @@
/* eslint-disable eqeqeq */
import React, { useState } from "react";
import { useHash } from "react-use";
import TextArea from "./TextArea";
import "./App.css";
export default function App() {
let [input, setInput] = useState("");
let [hash, setHash] = useHash();
let input = decodeURI(hash.slice(1));
function setInput(s: string) {
setHash(encodeURI(s));
}
let [activeStateIdx, setActiveStateIdx] = useState(0);
let parsed = somes(input.split(/[^a-z0-9-]+/).map(parse_op));
let states = steps(INITIAL_STATE, parsed);
return (
<>
<textarea
onChange={e => setInput(e.target.value)}
<TextArea
onChange={(e: any) => setInput(e.target.value)}
value={input}
className="program"
title={`\
@ -26,14 +34,15 @@ type Op =
| "del"
| "cmp"
| "swp";`}
></textarea>
></TextArea>
<div className="info">
<Ops
parsed={parsed}
ip={(states[activeStateIdx] ?? INITIAL_STATE).ip}
ip={states[activeStateIdx]?.ip ?? 0}
// // hack
// top={document.getElementById("input")?.clientHeight ?? 0}
/>
<Stack stack={states[activeStateIdx]?.stack ?? []} />
<States
states={states}
activeStateIdx={activeStateIdx}
@ -131,13 +140,13 @@ function State({
{...props}
>
<div className="ip">Ip: {state.ip}</div>
<span>Stack:</span> <Stack stack={state.stack} />
{/* <span>Stack:</span> <Stack stack={state.stack} /> */}
<span>Output:</span> <Output output={state.output} />
</div>
);
}
function Stack({ stack }: { stack: number[] }): JSX.Element {
function Stack({ stack, ...props }: { stack: number[] }): JSX.Element {
let ns = [...stack];
ns.reverse();
return (
@ -146,6 +155,7 @@ function Stack({ stack }: { stack: number[] }): JSX.Element {
children={ns.map((item, i) => (
<StackItem item={item} key={i} />
))}
{...props}
/>
);
}
@ -164,7 +174,7 @@ function Output({ output }: { output: number[] }): JSX.Element {
<div
className="output"
children={output.map((item, i) => (
<StackItem item={item} key={i} />
<span key={i}>{String.fromCharCode(item)}</span>
))}
></div>
);
@ -219,8 +229,9 @@ function parse_int(n: string, radix?: number): Option<number> {
}
function parse_op(op: string): Option<Op> {
if (parse_int(op)) {
return parse_int(op);
let maybe_int = parse_int(op);
if (is_some(maybe_int)) {
return maybe_int;
}
switch (op) {
case "add":
@ -235,6 +246,7 @@ function parse_op(op: string): Option<Op> {
case "swp":
return op;
}
console.log(op);
}
type State = {
@ -249,7 +261,7 @@ const INITIAL_STATE: State = {
output: [],
};
function steps(state: State, ops: Op[], limit = 100, num_steps = 0): State[] {
function steps(state: State, ops: Op[], limit = 300, num_steps = 0): State[] {
if (!ops[state.ip] || num_steps > limit) {
return [state];
}

25
src/TextArea.tsx Normal file
View file

@ -0,0 +1,25 @@
// @ts-nocheck
// https://stackoverflow.com/a/68928267/18571336
import React, { useEffect, useRef, useState } from "react";
function TextArea(props) {
const { value, onChange, ...rest } = props;
const [cursor, setCursor] = useState(null);
const ref = useRef(null);
useEffect(() => {
const input = ref.current;
if (input) input.setSelectionRange(cursor, cursor);
}, [ref, cursor, value]);
const handleChange = e => {
setCursor(e.target.selectionStart);
onChange && onChange(e);
};
return (
<textarea ref={ref} value={value} onChange={handleChange} {...rest} />
);
}
export default TextArea;