Reorganize ui

This commit is contained in:
Yura Dupyn 2026-02-15 19:26:32 +01:00
parent e841106029
commit bf5eb54932
18 changed files with 31 additions and 28 deletions

View file

@ -0,0 +1,148 @@
import { createSignal, Show } from "solid-js";
import { Digith } from "src/ui/Digith";
import { useProgram } from "src/ui/ProgramProvider";
import { CodeEditor } from "src/ui/Component/CodeEditor";
import { sourceText } from "src/lang/parser/source_text";
import { Program } from "src/lang/program";
import { V, Validation, letValidate } from "src/ui/validation";
import { validateExprRaw, validateParamsRaw } from "./Helpers";
import { updateDigith } from "src/ui/Scrowl/scrowlStore";
import { DigithError } from "src/ui/Digith/DigithError";
type Input = {
raw_params: string,
raw_body: string,
}
const validator: Validation<Input, Program.UpdateFunction, DigithError> = letValidate(
(input) => ({
parameters: V.elseErr(validateParamsRaw(input.raw_params), err => ({
payload: { tag: "Parse", field: "params", err, src: sourceText(input.raw_params) },
ids: ["params"],
tags: ["footer"],
config: { title: "Parameters" },
})),
body: V.elseErr(validateExprRaw(input.raw_body), err => ({
payload: { tag: "Parse", field: "body", err, src: sourceText(input.raw_body) },
ids: ["body"],
tags: ["footer"],
config: { title: "Function Body" },
})),
}),
(fields, input) => {
return V.ok({
parameters: fields.parameters,
body: fields.body,
raw_parameters: input.raw_params,
raw_body: input.raw_body
});
}
);
// TODO: What about renaming?
export function FunctionDigith(props: { function: Digith.Function }) {
const program = useProgram();
const [params, setParams] = createSignal(props.function.raw_parameters);
const [body, setBody] = createSignal(props.function.raw_body);
const [errors, setErrors] = createSignal<DigithError[]>([]);
const isDirty = () =>
params() !== props.function.raw_parameters ||
body() !== props.function.raw_body;
function handleRedefine() {
setErrors([]);
const validRes = validator({ raw_params: params(), raw_body: body() });
if (validRes.tag === "errors") {
setErrors(validRes.errors);
return;
}
const updateData = validRes.value;
const progRes = Program.updateFunction(program, props.function.name, {
parameters: updateData.parameters,
body: updateData.body,
raw_parameters: updateData.raw_parameters,
raw_body: updateData.raw_body
});
if (progRes.tag === "error") {
setErrors([{
payload: { tag: "Program", err: progRes.error },
ids: ["program"],
tags: ["footer"],
config: { title: "Update Failed" },
}]);
return;
}
// reloading the digith
updateDigith(props.function.id, {
...props.function,
raw_parameters: updateData.raw_parameters,
raw_body: updateData.raw_body
});
}
return (
<article>
<header>
<strong>Fn</strong>
{/* Dirty Indicator / Status */}
<div>
<Show when={isDirty()} fallback={<span style={{color: "var(--pico-muted-color)"}}>Synced</span>}>
<span style={{color: "var(--pico-primary)"}}> Unsaved Changes</span>
</Show>
</div>
</header>
<div class="grid">
<label>
Name
<input
type="text"
value={props.function.name}
disabled
style={{ opacity: 0.7, cursor: "not-allowed" }}
/>
</label>
<label>
Parameters
<input
type="text"
value={params()}
onInput={(e) => setParams(e.currentTarget.value)}
/>
</label>
</div>
<label>Body</label>
<CodeEditor
value={body()}
onUpdate={setBody}
onRun={handleRedefine}
/>
<footer style={{ display: "flex", "align-items": "center", gap: "1rem" }}>
<button
onClick={handleRedefine}
disabled={!isDirty()}
class={isDirty() ? "" : "secondary outline"}
>
Redefine
</button>
</footer>
<div style={{ "margin-top": "1rem" }}>
<DigithError.ByTag errors={errors()} tag="footer" />
</div>
</article>
);
}

View file

@ -0,0 +1,24 @@
import { ParseError, parseExpr, parseFunctionName, parseFunctionParameters } from "src/lang/parser/parser";
import { sourceText } from "src/lang/parser/source_text";
import { Expr, FunctionName, ProductPattern } from "src/lang/expr";
import { V } from "src/ui/validation";
// === Parser wrappers ===
export function validateNameRaw(input: string): V<FunctionName, ParseError> {
const src = sourceText(input);
const res = parseFunctionName(src);
return res.tag === "ok" ? V.ok(res.value) : V.errors([res.error]);
};
export function validateParamsRaw(input: string): V<ProductPattern[], ParseError> {
const src = sourceText(input);
const res = parseFunctionParameters(src);
return res.tag === "ok" ? V.ok(res.value) : V.errors([res.error]);
};
export function validateExprRaw(input: string): V<Expr, ParseError> {
const src = sourceText(input);
const res = parseExpr(src);
return res.tag === "ok" ? V.ok(res.value) : V.errors([res.error]);
};

View file

@ -0,0 +1,127 @@
import { createSignal } from "solid-js";
import { Digith } from "src/ui/Digith";
import { useProgram } from "src/ui/ProgramProvider";
import { CodeEditor } from "src/ui/Component/CodeEditor";
import { sourceText } from "src/lang/parser/source_text";
import { Program } from "src/lang/program";
import { V, Validation, letValidate } from "src/ui/validation";
import { validateExprRaw, validateNameRaw, validateParamsRaw } from "./Helpers";
import { spawnFunctionDigith } from "src/ui/Scrowl/scrowlStore";
import { DigithError } from "src/ui/Digith/DigithError";
type Input = {
raw_name: string,
raw_params: string,
raw_body: string,
}
const validator: Validation<Input, Program.CreateFunction, DigithError> = letValidate(
(input) =>({
name: V.elseErr(validateNameRaw(input.raw_name), err =>({
payload: { tag: "Parse", err, src: sourceText(input.raw_name) },
ids: ["name"],
tags: ["footer"],
config: { title: "Function Name", display: "flat" },
})),
parameters: V.elseErr(validateParamsRaw(input.raw_params), err => ({
payload: { tag: "Parse", err, src: sourceText(input.raw_params) },
ids: ["params"],
tags: ["footer"],
config: { title: "Parameters", display: "flat" },
})),
body: V.elseErr(validateExprRaw(input.raw_body), err => ({
payload: { tag: "Parse", err, src: sourceText(input.raw_body) },
ids: ["body"],
tags: ["footer"],
config: { title: "Function Body", display: "flat" },
})),
}),
(fields, input) => {
const createFunction: Program.CreateFunction = {
name: fields.name,
parameters: fields.parameters,
body: fields.body,
raw_parameters: input.raw_params,
raw_body: input.raw_body,
};
return V.ok(createFunction);
})
export function NewFunctionDraftDigith(props: { draft: Digith.NewFunctionDraft }) {
const program = useProgram();
const [name, setName] = createSignal(props.draft.raw_name);
const [params, setParams] = createSignal(props.draft.raw_parameters);
const [body, setBody] = createSignal(props.draft.raw_body);
const [errors, setErrors] = createSignal<DigithError[]>([]);
function handleCommit() {
setErrors([]);
const validRes = validator({ raw_name: name(), raw_params: params(), raw_body: body() });
if (validRes.tag === "errors") {
setErrors(validRes.errors);
return;
}
const createFunction = validRes.value;
const programRes = Program.registerFunction(program, createFunction);
if (programRes.tag === "error") {
setErrors([{
payload: { tag: "Program", err: programRes.error },
ids: ["program"],
tags: ["footer"],
config: { title: "Registration Failed" },
}]);
return;
}
const fnName = programRes.value;
spawnFunctionDigith(program, fnName, props.draft.id);
};
return (
<article>
<header><strong>Fn (Draft)</strong></header>
<div class="grid">
<label>
Name
<input
type="text"
placeholder="my_func"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
/>
</label>
<label>
Parameters (comma separated)
<input
type="text"
placeholder="x, y"
value={params()}
onInput={(e) => setParams(e.currentTarget.value)}
/>
</label>
</div>
<label>Body</label>
<CodeEditor
value={body()}
onUpdate={setBody}
onRun={handleCommit}
/>
<footer>
<button class="primary" onClick={handleCommit}>Commit</button>
</footer>
<div style={{ "margin-top": "1rem" }}>
<DigithError.ByTag errors={errors()} tag="footer" />
</div>
</article>
);
}