trying to simplify things a bit more... will get rid of 'onResolve'

This commit is contained in:
Joeri Exelmans 2025-05-13 16:33:25 +02:00
parent 897824e07d
commit 9c0c2dab90
7 changed files with 85 additions and 45 deletions

View file

@ -50,11 +50,11 @@ export function App() {
window.onkeydown = onKeyDown; window.onkeydown = onKeyDown;
}, []); }, []);
const commands = [ const commands: [string, string[], string][] = [
["call" , "[c] call" ], ["call" , ['c' ], "call" ],
["eval" , "[e] [Tab] [Enter] eval"], ["eval" , ['e','Tab','Enter' ], "eval" ],
["transform", "[t] [.] transform" ], ["transform", ['t', '.' ], "transform" ],
["let" , "[l] [=] let ... in ..." ], ["let" , ['l', '=', 'a' ], "let ... in ..."],
]; ];
const [highlighted, setHighlighted] = useState( const [highlighted, setHighlighted] = useState(
@ -74,8 +74,9 @@ export function App() {
<button disabled={future.length===0} onClick={onRedo}>Redo ({future.length}) [Ctrl+Shift+Z]</button> <button disabled={future.length===0} onClick={onRedo}>Redo ({future.length}) [Ctrl+Shift+Z]</button>
Commands: Commands:
{ {
commands.map(([_, descr], i) => commands.map(([_, keys, descr], i) =>
<span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}> <span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
{keys.map((key, j) => <kbd key={j}>{key}</kbd>)}
{descr} {descr}
</span>) </span>)
} }
@ -91,6 +92,8 @@ export function App() {
filter={() => true} filter={() => true}
/> />
</CommandContext> </CommandContext>
</main> </main>
<footer> <footer>

View file

@ -73,12 +73,26 @@
border-left-color: darkred; border-left-color: darkred;
} }
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam { .functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam {
background-color: rgb(95, 4, 4); /* background-color: rgb(95, 4, 4); */
color: white; background-color: pink;
/* color: white; */
color: black;
} }
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after { .functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after {
border-left-color: rgb(95, 4, 4); /* border-left-color: rgb(95, 4, 4); */
border-left-color: pink;
} }
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam {
/* background-color: rgb(95, 4, 4); */
background-color: pink;
/* color: white; */
color: black;
}
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam:after {
/* border-left-color: rgb(95, 4, 4); */
border-left-color: pink;
}
.functionBlock.unifyError > .functionParams > .outputParam { .functionBlock.unifyError > .functionParams > .outputParam {
background-color: pink; background-color: pink;

View file

@ -58,7 +58,7 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
} }
}; };
const onFnResolve = (fnState) => { const onFnResolve = (fnState) => {
if (input.resolved) { if (fnState && input.resolved) {
makeTheCall(input, fnState); makeTheCall(input, fnState);
} }
else { else {
@ -70,7 +70,7 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
} }
}; };
const onInputResolve = (inputState) => { const onInputResolve = (inputState) => {
if (fn.resolved) { if (fn.resolved && inputState) {
makeTheCall(inputState, fn); makeTheCall(inputState, fn);
} }
else { else {
@ -90,13 +90,14 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
} }
export function CallBlock({ state, setState, onResolve }: CallBlockProps) { export function CallBlock({ state, setState, onResolve }: CallBlockProps) {
const {unifyError, setFn, setInput, onFnResolve, onInputResolve, onInputCancel} const {unifyError, setFn, setInput, onFnResolve, onFnCancel, onInputResolve, onInputCancel}
= headlessCallBlock({ state, setState, onResolve }); = headlessCallBlock({ state, setState, onResolve });
return <span className={"functionBlock" + (unifyError ? " unifyError" : "")}> return <span className={"functionBlock" + (unifyError ? " unifyError" : "")}>
<FunctionHeader <FunctionHeader
fn={state.fn} fn={state.fn}
setFn={setFn} setFn={setFn}
onFnResolve={onFnResolve} onFnResolve={onFnResolve}
onFnCancel={onFnCancel}
input={state.input} /> input={state.input} />
<div className="functionParams"> <div className="functionParams">
<div className="outputParam"> <div className="outputParam">
@ -109,26 +110,29 @@ export function CallBlock({ state, setState, onResolve }: CallBlockProps) {
{/* Output (or Error) */} {/* Output (or Error) */}
{state.resolved && <Value dynamic={state.resolved} />} {state.resolved && <Value dynamic={state.resolved} />}
{ state.resolved && <>&#x2611;</>}
{unifyError && unifyError.toString()} {unifyError && unifyError.toString()}
</div> </div>
</div> </div>
</span>; </span>;
} }
function FunctionHeader({ fn, setFn, input, onFnResolve }) { function FunctionHeader({ fn, setFn, input, onFnResolve, onFnCancel }) {
if (fn.kind === "call") { if (fn.kind === "call") {
// if the function we're calling is itself the result of a function call, // if the function we're calling is itself the result of a function call,
// then we are anonymous, and so we don't draw a function name // then we are anonymous, and so we don't draw a function name
// recurse: // recurse:
const { const {
onFnResolve : onFnFnResolve onFnResolve : onFnFnResolve,
onFnCancel : onFnFnCancel,
} = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve}); } = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve});
return <FunctionHeader return <FunctionHeader
fn={fn.fn} fn={fn.fn}
setFn={fnFn => setFn({...fn, fn: fnFn})} setFn={fnFn => setFn({...fn, fn: fnFn})}
onFnResolve={onFnFnResolve} onFnResolve={onFnFnResolve}
onFnCancel={onFnFnCancel}
input={fn.input} />; input={fn.input} />;
} }
else { else {
@ -151,15 +155,15 @@ function FunctionHeader({ fn, setFn, input, onFnResolve }) {
} }
// end of recursion - draw function name // end of recursion - draw function name
return <div className="functionName"> return <span className="functionName">
&nbsp;&#119891;&#119899;&nbsp; &nbsp;&#119891;&#119899;&nbsp;
<Editor <Editor
state={fn} state={fn}
setState={setFn} setState={setFn}
onResolve={onFnResolve} onResolve={onFnResolve}
onCancel={() => {/*todo*/}} onCancel={onFnCancel}
filter={filterCompatibleFns} /> filter={filterCompatibleFns} />
</div>; </span>;
} }
} }

View file

@ -3,5 +3,5 @@
} }
.commandInput { .commandInput {
width: 160px; width: 90px;
} }

View file

@ -62,6 +62,10 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
}, [needCommand]); }, [needCommand]);
const onMyResolve = (editorState: EditorState) => { const onMyResolve = (editorState: EditorState) => {
setState(editorState); setState(editorState);
onResolve(editorState);
return;
if (editorState.resolved) { if (editorState.resolved) {
setNeedCommand(true); setNeedCommand(true);
} }
@ -74,8 +78,9 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
const globalContext = useContext(CommandContext); const globalContext = useContext(CommandContext);
const onCommand = (e: React.KeyboardEvent) => { const onCommand = (e: React.KeyboardEvent) => {
const type = getType(state.resolved); // const type = getType(state.resolved);
const commands = getCommands(type); // const commands = getCommands(type);
const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', '=', '.', 'c'];
if (!commands.includes(e.key)) { if (!commands.includes(e.key)) {
return; return;
} }
@ -172,19 +177,16 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
(state.resolved) (state.resolved)
? <div className="typeSignature"> ? <div className="typeSignature">
:: <Type type={getType(state.resolved)} /> :: <Type type={getType(state.resolved)} />
{ (needCommand)
? <input
ref={commandInputRef}
spellCheck={false}
className="editable commandInput"
placeholder={`<command: ${getShortCommands(getType(state.resolved))}>`}
onKeyDown={onCommand}
value={""}
onChange={() => {}} /> /* gets rid of React warning */
: <></>
}
</div> </div>
: <></> : <></>
} }
<input
ref={commandInputRef}
spellCheck={false}
className="editable commandInput"
placeholder={`<command>`}
onKeyDown={onCommand}
value={""}
onChange={() => {}} />
</>; </>;
} }

View file

@ -61,6 +61,8 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
}, [focus]); }, [focus]);
const onSelectSuggestion = ([name, dynamic]) => { const onSelectSuggestion = ([name, dynamic]) => {
console.log('resolving input block', text, '->', name);
onResolve({ onResolve({
kind: "input", kind: "input",
text: name, text: name,
@ -69,23 +71,10 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
}); });
}; };
const onInput = e => {
setText(e.target.value);
if (resolved) {
// un-resolve
onResolve({
kind: "input",
text: e.target.value,
resolved: undefined,
focus: true,
});
}
};
const getCaretPosition = () => { const getCaretPosition = () => {
return inputRef.current?.selectionStart || -1; return inputRef.current?.selectionStart || -1;
} }
// fired before onInput
const onKeyDown = (e: React.KeyboardEvent) => { const onKeyDown = (e: React.KeyboardEvent) => {
const fns = { const fns = {
Tab: () => { Tab: () => {
@ -141,6 +130,24 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
fns[e.key]?.(); fns[e.key]?.();
}; };
const onInput = e => {
const found = trie.get(env.name2dyn)(e.target.value);
if (found) {
console.log('resolving input block..', e.target.value);
onResolve({...state, text: e.target.value, resolved: found});
}
else {
if (resolved) {
// un-resolve
console.log('un-resolving input block..', e.target.value);
onResolve({...state, text: e.target.value, resolved: undefined});
}
else {
setText(e.target.value);
}
}
};
return <span> return <span>
<span className=""> <span className="">
{/* Dropdown suggestions */} {/* Dropdown suggestions */}
@ -164,6 +171,7 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
spellCheck={false}/> spellCheck={false}/>
{/* Single 'grey' suggestion */} {/* Single 'grey' suggestion */}
<span className="text-block suggest">{singleSuggestion}</span> <span className="text-block suggest">{singleSuggestion}</span>
{ resolved && <>&#x2611;</>}
</span> </span>
</span>; </span>;
} }

View file

@ -9,3 +9,12 @@ body {
font-style: normal; font-style: normal;
font-variation-settings: "wdth" 100; font-variation-settings: "wdth" 100;
} }
kbd {
border: 2px darkgrey;
color: rgb(63, 63, 63);
border-style: outset;
background-color: whitesmoke;
border-radius: 3px;
margin: 0 2px 0 2px;
}