Merge pull request #702 from quantified-uncertainty/components-hooks
useSquiggle and useSquigglePartial hooks - components refactorings
This commit is contained in:
commit
feda8997a6
|
@ -1,5 +1,5 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FC } from "react";
|
import React, { FC, useMemo } from "react";
|
||||||
import AceEditor from "react-ace";
|
import AceEditor from "react-ace";
|
||||||
|
|
||||||
import "ace-builds/src-noconflict/mode-golang";
|
import "ace-builds/src-noconflict/mode-golang";
|
||||||
|
@ -21,14 +21,15 @@ export const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
showGutter = false,
|
showGutter = false,
|
||||||
height,
|
height,
|
||||||
}) => {
|
}) => {
|
||||||
let lineCount = value.split("\n").length;
|
const lineCount = value.split("\n").length;
|
||||||
let id = _.uniqueId();
|
const id = useMemo(() => _.uniqueId(), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AceEditor
|
<AceEditor
|
||||||
value={value}
|
value={value}
|
||||||
mode="golang"
|
mode="golang"
|
||||||
theme="github"
|
theme="github"
|
||||||
width={"100%"}
|
width="100%"
|
||||||
fontSize={14}
|
fontSize={14}
|
||||||
height={String(height) + "px"}
|
height={String(height) + "px"}
|
||||||
minLines={oneLine ? lineCount : undefined}
|
minLines={oneLine ? lineCount : undefined}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
run,
|
|
||||||
errorValueToString,
|
|
||||||
squiggleExpression,
|
squiggleExpression,
|
||||||
bindings,
|
bindings,
|
||||||
environment,
|
environment,
|
||||||
|
@ -9,253 +7,11 @@ import {
|
||||||
defaultImports,
|
defaultImports,
|
||||||
defaultBindings,
|
defaultBindings,
|
||||||
defaultEnvironment,
|
defaultEnvironment,
|
||||||
declaration,
|
|
||||||
} from "@quri/squiggle-lang";
|
} from "@quri/squiggle-lang";
|
||||||
import { NumberShower } from "./NumberShower";
|
import { FunctionChartSettings } from "./FunctionChart";
|
||||||
import { DistributionChart } from "./DistributionChart";
|
import { useSquiggle } from "../lib/hooks";
|
||||||
import { ErrorAlert } from "./Alert";
|
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
|
||||||
import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
|
import { SquiggleItem } from "./SquiggleItem";
|
||||||
|
|
||||||
function getRange<a>(x: declaration<a>) {
|
|
||||||
let first = x.args[0];
|
|
||||||
switch (first.tag) {
|
|
||||||
case "Float": {
|
|
||||||
return { floats: { min: first.value.min, max: first.value.max } };
|
|
||||||
}
|
|
||||||
case "Date": {
|
|
||||||
return { time: { min: first.value.min, max: first.value.max } };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
|
|
||||||
let range = getRange(x);
|
|
||||||
let min = range.floats ? range.floats.min : 0;
|
|
||||||
let max = range.floats ? range.floats.max : 10;
|
|
||||||
return {
|
|
||||||
start: min,
|
|
||||||
stop: max,
|
|
||||||
count: 20,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VariableBoxProps {
|
|
||||||
heading: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
showTypes: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const VariableBox: React.FC<VariableBoxProps> = ({
|
|
||||||
heading = "Error",
|
|
||||||
children,
|
|
||||||
showTypes = false,
|
|
||||||
}) => {
|
|
||||||
if (showTypes) {
|
|
||||||
return (
|
|
||||||
<div className="bg-white border border-grey-200 m-2">
|
|
||||||
<div className="border-b border-grey-200 p-3">
|
|
||||||
<header className="font-mono">{heading}</header>
|
|
||||||
</div>
|
|
||||||
<div className="p-3">{children}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <div>{children}</div>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface SquiggleItemProps {
|
|
||||||
/** The input string for squiggle */
|
|
||||||
expression: squiggleExpression;
|
|
||||||
width?: number;
|
|
||||||
height: number;
|
|
||||||
/** Whether to show a summary of statistics for distributions */
|
|
||||||
showSummary: boolean;
|
|
||||||
/** Whether to show type information */
|
|
||||||
showTypes: boolean;
|
|
||||||
/** Whether to show users graph controls (scale etc) */
|
|
||||||
showControls: boolean;
|
|
||||||
/** Settings for displaying functions */
|
|
||||||
chartSettings: FunctionChartSettings;
|
|
||||||
/** Environment for further function executions */
|
|
||||||
environment: environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|
||||||
expression,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
showSummary,
|
|
||||||
showTypes = false,
|
|
||||||
showControls = false,
|
|
||||||
chartSettings,
|
|
||||||
environment,
|
|
||||||
}) => {
|
|
||||||
switch (expression.tag) {
|
|
||||||
case "number":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Number" showTypes={showTypes}>
|
|
||||||
<div className="font-semibold text-slate-600">
|
|
||||||
<NumberShower precision={3} number={expression.value} />
|
|
||||||
</div>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "distribution": {
|
|
||||||
let distType = expression.value.type();
|
|
||||||
return (
|
|
||||||
<VariableBox
|
|
||||||
heading={`Distribution (${distType})`}
|
|
||||||
showTypes={showTypes}
|
|
||||||
>
|
|
||||||
{distType === "Symbolic" && showTypes ? (
|
|
||||||
<div>{expression.value.toString()}</div>
|
|
||||||
) : null}
|
|
||||||
<DistributionChart
|
|
||||||
distribution={expression.value}
|
|
||||||
height={height}
|
|
||||||
width={width}
|
|
||||||
showSummary={showSummary}
|
|
||||||
showControls={showControls}
|
|
||||||
/>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case "string":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="String" showTypes={showTypes}>
|
|
||||||
<span className="text-slate-400">"</span>
|
|
||||||
<span className="text-slate-600 font-semibold">
|
|
||||||
{expression.value}
|
|
||||||
</span>
|
|
||||||
<span className="text-slate-400">"</span>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "boolean":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Boolean" showTypes={showTypes}>
|
|
||||||
{expression.value.toString()}
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "symbol":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Symbol" showTypes={showTypes}>
|
|
||||||
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
|
|
||||||
<span className="text-slate-600">{expression.value}</span>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "call":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Call" showTypes={showTypes}>
|
|
||||||
{expression.value}
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "array":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Array" showTypes={showTypes}>
|
|
||||||
{expression.value.map((r, i) => (
|
|
||||||
<div key={i} className="flex pt-1">
|
|
||||||
<div className="flex-none bg-slate-100 rounded-sm px-1">
|
|
||||||
<header className="text-slate-400 font-mono">{i}</header>
|
|
||||||
</div>
|
|
||||||
<div className="px-2 mb-2 grow">
|
|
||||||
<SquiggleItem
|
|
||||||
key={i}
|
|
||||||
expression={r}
|
|
||||||
width={width !== undefined ? width - 20 : width}
|
|
||||||
height={50}
|
|
||||||
showTypes={showTypes}
|
|
||||||
showControls={showControls}
|
|
||||||
chartSettings={chartSettings}
|
|
||||||
environment={environment}
|
|
||||||
showSummary={showSummary}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "record":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Record" showTypes={showTypes}>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{Object.entries(expression.value).map(([key, r]) => (
|
|
||||||
<div key={key} className="flex space-x-2">
|
|
||||||
<div className="flex-none">
|
|
||||||
<header className="text-slate-500 font-mono">{key}:</header>
|
|
||||||
</div>
|
|
||||||
<div className="px-2 grow bg-gray-50 border border-gray-100 rounded-sm">
|
|
||||||
<SquiggleItem
|
|
||||||
expression={r}
|
|
||||||
width={width !== undefined ? width - 20 : width}
|
|
||||||
height={height / 3}
|
|
||||||
showTypes={showTypes}
|
|
||||||
showSummary={showSummary}
|
|
||||||
showControls={showControls}
|
|
||||||
chartSettings={chartSettings}
|
|
||||||
environment={environment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "arraystring":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Array String" showTypes={showTypes}>
|
|
||||||
{expression.value.map((r) => `"${r}"`).join(", ")}
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "date":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Date" showTypes={showTypes}>
|
|
||||||
{expression.value.toDateString()}
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "timeDuration": {
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Time Duration" showTypes={showTypes}>
|
|
||||||
<NumberShower precision={3} number={expression.value} />
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case "lambda":
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Function" showTypes={showTypes}>
|
|
||||||
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value.parameters.join(
|
|
||||||
","
|
|
||||||
)})`}</div>
|
|
||||||
<FunctionChart
|
|
||||||
fn={expression.value}
|
|
||||||
chartSettings={chartSettings}
|
|
||||||
height={height}
|
|
||||||
environment={{
|
|
||||||
sampleCount: environment.sampleCount / 10,
|
|
||||||
xyPointLength: environment.xyPointLength / 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
case "lambdaDeclaration": {
|
|
||||||
return (
|
|
||||||
<VariableBox heading="Function Declaration" showTypes={showTypes}>
|
|
||||||
<FunctionChart
|
|
||||||
fn={expression.value.fn}
|
|
||||||
chartSettings={getChartSettings(expression.value)}
|
|
||||||
height={height}
|
|
||||||
environment={{
|
|
||||||
sampleCount: environment.sampleCount / 10,
|
|
||||||
xyPointLength: environment.xyPointLength / 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</VariableBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return <>Should be unreachable</>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface SquiggleChartProps {
|
export interface SquiggleChartProps {
|
||||||
/** The input string for squiggle */
|
/** The input string for squiggle */
|
||||||
|
@ -266,8 +22,8 @@ export interface SquiggleChartProps {
|
||||||
environment?: environment;
|
environment?: environment;
|
||||||
/** If the result is a function, where the function starts, ends and the amount of stops */
|
/** If the result is a function, where the function starts, ends and the amount of stops */
|
||||||
chartSettings?: FunctionChartSettings;
|
chartSettings?: FunctionChartSettings;
|
||||||
/** When the environment changes */
|
/** When the squiggle code gets reevaluated */
|
||||||
onChange?(expr: squiggleExpression): void;
|
onChange?(expr: squiggleExpression | undefined): void;
|
||||||
/** CSS width of the element */
|
/** CSS width of the element */
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
@ -275,7 +31,7 @@ export interface SquiggleChartProps {
|
||||||
bindings?: bindings;
|
bindings?: bindings;
|
||||||
/** JS imported parameters */
|
/** JS imported parameters */
|
||||||
jsImports?: jsImports;
|
jsImports?: jsImports;
|
||||||
/** Whether to show a summary of the distirbution */
|
/** Whether to show a summary of the distribution */
|
||||||
showSummary?: boolean;
|
showSummary?: boolean;
|
||||||
/** Whether to show type information about returns, default false */
|
/** Whether to show type information about returns, default false */
|
||||||
showTypes?: boolean;
|
showTypes?: boolean;
|
||||||
|
@ -283,12 +39,13 @@ export interface SquiggleChartProps {
|
||||||
showControls?: boolean;
|
showControls?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultOnChange = () => {};
|
||||||
const defaultChartSettings = { start: 0, stop: 10, count: 20 };
|
const defaultChartSettings = { start: 0, stop: 10, count: 20 };
|
||||||
|
|
||||||
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
||||||
squiggleString = "",
|
squiggleString = "",
|
||||||
environment,
|
environment,
|
||||||
onChange = () => {},
|
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
||||||
height = 200,
|
height = 200,
|
||||||
bindings = defaultBindings,
|
bindings = defaultBindings,
|
||||||
jsImports = defaultImports,
|
jsImports = defaultImports,
|
||||||
|
@ -298,28 +55,28 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
||||||
showControls = false,
|
showControls = false,
|
||||||
chartSettings = defaultChartSettings,
|
chartSettings = defaultChartSettings,
|
||||||
}) => {
|
}) => {
|
||||||
let expressionResult = run(squiggleString, bindings, environment, jsImports);
|
const { result } = useSquiggle({
|
||||||
if (expressionResult.tag !== "Ok") {
|
code: squiggleString,
|
||||||
return (
|
bindings,
|
||||||
<ErrorAlert heading={"Parse Error"}>
|
environment,
|
||||||
{errorValueToString(expressionResult.value)}
|
jsImports,
|
||||||
</ErrorAlert>
|
onChange,
|
||||||
);
|
});
|
||||||
|
|
||||||
|
if (result.tag !== "Ok") {
|
||||||
|
return <SquiggleErrorAlert error={result.value} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let e = environment ?? defaultEnvironment;
|
|
||||||
let expression = expressionResult.value;
|
|
||||||
onChange(expression);
|
|
||||||
return (
|
return (
|
||||||
<SquiggleItem
|
<SquiggleItem
|
||||||
expression={expression}
|
expression={result.value}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
showSummary={showSummary}
|
showSummary={showSummary}
|
||||||
showTypes={showTypes}
|
showTypes={showTypes}
|
||||||
showControls={showControls}
|
showControls={showControls}
|
||||||
chartSettings={chartSettings}
|
chartSettings={chartSettings}
|
||||||
environment={e}
|
environment={environment ?? defaultEnvironment}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,39 +1,51 @@
|
||||||
import * as React from "react";
|
import React, { useState } from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
import { SquiggleChart } from "./SquiggleChart";
|
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import { CodeEditor } from "./CodeEditor";
|
||||||
import type {
|
import {
|
||||||
squiggleExpression,
|
squiggleExpression,
|
||||||
environment,
|
environment,
|
||||||
bindings,
|
bindings,
|
||||||
jsImports,
|
jsImports,
|
||||||
|
defaultEnvironment,
|
||||||
} from "@quri/squiggle-lang";
|
} from "@quri/squiggle-lang";
|
||||||
import {
|
import { defaultImports, defaultBindings } from "@quri/squiggle-lang";
|
||||||
runPartial,
|
|
||||||
errorValueToString,
|
|
||||||
defaultImports,
|
|
||||||
defaultBindings,
|
|
||||||
} from "@quri/squiggle-lang";
|
|
||||||
import { ErrorAlert } from "./Alert";
|
|
||||||
import { SquiggleContainer } from "./SquiggleContainer";
|
import { SquiggleContainer } from "./SquiggleContainer";
|
||||||
|
import { useSquiggle, useSquigglePartial } from "../lib/hooks";
|
||||||
|
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
|
||||||
|
import { SquiggleItem } from "./SquiggleItem";
|
||||||
|
|
||||||
|
const WrappedCodeEditor: React.FC<{
|
||||||
|
code: string;
|
||||||
|
setCode: (code: string) => void;
|
||||||
|
}> = ({ code, setCode }) => (
|
||||||
|
<div className="border border-grey-200 p-2 m-4">
|
||||||
|
<CodeEditor
|
||||||
|
value={code}
|
||||||
|
onChange={setCode}
|
||||||
|
oneLine={true}
|
||||||
|
showGutter={false}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export interface SquiggleEditorProps {
|
export interface SquiggleEditorProps {
|
||||||
/** The input string for squiggle */
|
/** The input string for squiggle */
|
||||||
initialSquiggleString?: string;
|
initialSquiggleString?: string;
|
||||||
/** If the output requires monte carlo sampling, the amount of samples */
|
/** The width of the element */
|
||||||
environment?: environment;
|
width?: number;
|
||||||
/** If the result is a function, where the function starts */
|
/** If the result is a function, where the function starts */
|
||||||
diagramStart?: number;
|
diagramStart?: number;
|
||||||
/** If the result is a function, where the function ends */
|
/** If the result is a function, where the function ends */
|
||||||
diagramStop?: number;
|
diagramStop?: number;
|
||||||
/** If the result is a function, how many points along the function it samples */
|
/** If the result is a function, how many points along the function it samples */
|
||||||
diagramCount?: number;
|
diagramCount?: number;
|
||||||
/** when the environment changes. Used again for notebook magic*/
|
/** When the environment changes. Used again for notebook magic */
|
||||||
onChange?(expr: squiggleExpression): void;
|
onChange?(expr: squiggleExpression | undefined): void;
|
||||||
/** The width of the element */
|
|
||||||
width?: number;
|
|
||||||
/** Previous variable declarations */
|
/** Previous variable declarations */
|
||||||
bindings?: bindings;
|
bindings?: bindings;
|
||||||
|
/** If the output requires monte carlo sampling, the amount of samples */
|
||||||
|
environment?: environment;
|
||||||
/** JS Imports */
|
/** JS Imports */
|
||||||
jsImports?: jsImports;
|
jsImports?: jsImports;
|
||||||
/** Whether to show detail about types of the returns, default false */
|
/** Whether to show detail about types of the returns, default false */
|
||||||
|
@ -44,169 +56,109 @@ export interface SquiggleEditorProps {
|
||||||
showSummary?: boolean;
|
showSummary?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
export const SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
||||||
initialSquiggleString = "",
|
initialSquiggleString = "",
|
||||||
width,
|
width,
|
||||||
environment,
|
|
||||||
diagramStart = 0,
|
diagramStart = 0,
|
||||||
diagramStop = 10,
|
diagramStop = 10,
|
||||||
diagramCount = 20,
|
diagramCount = 20,
|
||||||
onChange,
|
onChange,
|
||||||
bindings = defaultBindings,
|
bindings = defaultBindings,
|
||||||
|
environment,
|
||||||
jsImports = defaultImports,
|
jsImports = defaultImports,
|
||||||
showTypes = false,
|
showTypes = false,
|
||||||
showControls = false,
|
showControls = false,
|
||||||
showSummary = false,
|
showSummary = false,
|
||||||
}: SquiggleEditorProps) => {
|
}: SquiggleEditorProps) => {
|
||||||
const [expression, setExpression] = React.useState(initialSquiggleString);
|
const [code, setCode] = useState(initialSquiggleString);
|
||||||
|
|
||||||
|
const { result, observableRef } = useSquiggle({
|
||||||
|
code,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
jsImports,
|
||||||
|
onChange,
|
||||||
|
});
|
||||||
|
|
||||||
const chartSettings = {
|
const chartSettings = {
|
||||||
start: diagramStart,
|
start: diagramStart,
|
||||||
stop: diagramStop,
|
stop: diagramStop,
|
||||||
count: diagramCount,
|
count: diagramCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquiggleContainer>
|
<div ref={observableRef}>
|
||||||
<div>
|
<SquiggleContainer>
|
||||||
<div className="border border-grey-200 p-2 m-4">
|
<WrappedCodeEditor code={code} setCode={setCode} />
|
||||||
<CodeEditor
|
{result.tag === "Ok" ? (
|
||||||
value={expression}
|
<SquiggleItem
|
||||||
onChange={setExpression}
|
expression={result.value}
|
||||||
oneLine={true}
|
width={width}
|
||||||
showGutter={false}
|
height={200}
|
||||||
height={20}
|
showSummary={showSummary}
|
||||||
|
showTypes={showTypes}
|
||||||
|
showControls={showControls}
|
||||||
|
chartSettings={chartSettings}
|
||||||
|
environment={environment ?? defaultEnvironment}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
<SquiggleChart
|
<SquiggleErrorAlert error={result.value} />
|
||||||
width={width}
|
)}
|
||||||
environment={environment}
|
</SquiggleContainer>
|
||||||
squiggleString={expression}
|
</div>
|
||||||
chartSettings={chartSettings}
|
|
||||||
onChange={onChange}
|
|
||||||
bindings={bindings}
|
|
||||||
jsImports={jsImports}
|
|
||||||
showTypes={showTypes}
|
|
||||||
showControls={showControls}
|
|
||||||
showSummary={showSummary}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SquiggleContainer>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
|
export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
|
||||||
let parent = document.createElement("div");
|
const parent = document.createElement("div");
|
||||||
ReactDOM.render(
|
ReactDOM.render(<SquiggleEditor {...props} />, parent);
|
||||||
<SquiggleEditor
|
|
||||||
{...props}
|
|
||||||
onChange={(expr) => {
|
|
||||||
// Typescript complains on two levels here.
|
|
||||||
// - Div elements don't have a value property
|
|
||||||
// - Even if it did (like it was an input element), it would have to
|
|
||||||
// be a string
|
|
||||||
//
|
|
||||||
// Which are reasonable in most web contexts.
|
|
||||||
//
|
|
||||||
// However we're using observable, neither of those things have to be
|
|
||||||
// true there. div elements can contain the value property, and can have
|
|
||||||
// the value be any datatype they wish.
|
|
||||||
//
|
|
||||||
// This is here to get the 'viewof' part of:
|
|
||||||
// viewof env = cell('normal(0,1)')
|
|
||||||
// to work
|
|
||||||
// @ts-ignore
|
|
||||||
parent.value = expr;
|
|
||||||
|
|
||||||
parent.dispatchEvent(new CustomEvent("input"));
|
|
||||||
if (props.onChange) props.onChange(expr);
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
parent
|
|
||||||
);
|
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SquigglePartialProps {
|
export interface SquigglePartialProps {
|
||||||
/** The input string for squiggle */
|
/** The input string for squiggle */
|
||||||
initialSquiggleString?: string;
|
initialSquiggleString?: string;
|
||||||
/** If the output requires monte carlo sampling, the amount of samples */
|
|
||||||
environment?: environment;
|
|
||||||
/** If the result is a function, where the function starts */
|
|
||||||
diagramStart?: number;
|
|
||||||
/** If the result is a function, where the function ends */
|
|
||||||
diagramStop?: number;
|
|
||||||
/** If the result is a function, how many points along the function it samples */
|
|
||||||
diagramCount?: number;
|
|
||||||
/** when the environment changes. Used again for notebook magic*/
|
/** when the environment changes. Used again for notebook magic*/
|
||||||
onChange?(expr: bindings): void;
|
onChange?(expr: bindings | undefined): void;
|
||||||
/** Previously declared variables */
|
/** Previously declared variables */
|
||||||
bindings?: bindings;
|
bindings?: bindings;
|
||||||
|
/** If the output requires monte carlo sampling, the amount of samples */
|
||||||
|
environment?: environment;
|
||||||
/** Variables imported from js */
|
/** Variables imported from js */
|
||||||
jsImports?: jsImports;
|
jsImports?: jsImports;
|
||||||
/** Whether to give users access to graph controls */
|
|
||||||
showControls?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export let SquigglePartial: React.FC<SquigglePartialProps> = ({
|
export const SquigglePartial: React.FC<SquigglePartialProps> = ({
|
||||||
initialSquiggleString = "",
|
initialSquiggleString = "",
|
||||||
onChange,
|
onChange,
|
||||||
bindings = defaultBindings,
|
bindings = defaultBindings,
|
||||||
environment,
|
environment,
|
||||||
jsImports = defaultImports,
|
jsImports = defaultImports,
|
||||||
}: SquigglePartialProps) => {
|
}: SquigglePartialProps) => {
|
||||||
const [expression, setExpression] = React.useState(initialSquiggleString);
|
const [code, setCode] = useState(initialSquiggleString);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
|
||||||
|
|
||||||
const runSquiggleAndUpdateBindings = () => {
|
const { result, observableRef } = useSquigglePartial({
|
||||||
const squiggleResult = runPartial(
|
code,
|
||||||
expression,
|
bindings,
|
||||||
bindings,
|
environment,
|
||||||
environment,
|
jsImports,
|
||||||
jsImports
|
onChange,
|
||||||
);
|
});
|
||||||
if (squiggleResult.tag === "Ok") {
|
|
||||||
if (onChange) onChange(squiggleResult.value);
|
|
||||||
setError(null);
|
|
||||||
} else {
|
|
||||||
setError(errorValueToString(squiggleResult.value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(runSquiggleAndUpdateBindings, [expression]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquiggleContainer>
|
<div ref={observableRef}>
|
||||||
<div>
|
<SquiggleContainer>
|
||||||
<div className="border border-grey-200 p-2 m-4">
|
<WrappedCodeEditor code={code} setCode={setCode} />
|
||||||
<CodeEditor
|
{result.tag !== "Ok" ? (
|
||||||
value={expression}
|
<SquiggleErrorAlert error={result.value} />
|
||||||
onChange={setExpression}
|
|
||||||
oneLine={true}
|
|
||||||
showGutter={false}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{error !== null ? (
|
|
||||||
<ErrorAlert heading="Error">{error}</ErrorAlert>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</SquiggleContainer>
|
||||||
</SquiggleContainer>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function renderSquigglePartialToDom(props: SquigglePartialProps) {
|
export function renderSquigglePartialToDom(props: SquigglePartialProps) {
|
||||||
let parent = document.createElement("div");
|
const parent = document.createElement("div");
|
||||||
ReactDOM.render(
|
ReactDOM.render(<SquigglePartial {...props} />, parent);
|
||||||
<SquigglePartial
|
|
||||||
{...props}
|
|
||||||
onChange={(bindings) => {
|
|
||||||
// @ts-ignore
|
|
||||||
parent.value = bindings;
|
|
||||||
|
|
||||||
parent.dispatchEvent(new CustomEvent("input"));
|
|
||||||
if (props.onChange) props.onChange(bindings);
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
parent
|
|
||||||
);
|
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
11
packages/components/src/components/SquiggleErrorAlert.tsx
Normal file
11
packages/components/src/components/SquiggleErrorAlert.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { errorValue, errorValueToString } from "@quri/squiggle-lang";
|
||||||
|
import React from "react";
|
||||||
|
import { ErrorAlert } from "./Alert";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
error: errorValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
|
||||||
|
return <ErrorAlert heading="Error">{errorValueToString(error)}</ErrorAlert>;
|
||||||
|
};
|
250
packages/components/src/components/SquiggleItem.tsx
Normal file
250
packages/components/src/components/SquiggleItem.tsx
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
squiggleExpression,
|
||||||
|
environment,
|
||||||
|
declaration,
|
||||||
|
} from "@quri/squiggle-lang";
|
||||||
|
import { NumberShower } from "./NumberShower";
|
||||||
|
import { DistributionChart } from "./DistributionChart";
|
||||||
|
import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
|
||||||
|
|
||||||
|
function getRange<a>(x: declaration<a>) {
|
||||||
|
const first = x.args[0];
|
||||||
|
switch (first.tag) {
|
||||||
|
case "Float": {
|
||||||
|
return { floats: { min: first.value.min, max: first.value.max } };
|
||||||
|
}
|
||||||
|
case "Date": {
|
||||||
|
return { time: { min: first.value.min, max: first.value.max } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
|
||||||
|
const range = getRange(x);
|
||||||
|
const min = range.floats ? range.floats.min : 0;
|
||||||
|
const max = range.floats ? range.floats.max : 10;
|
||||||
|
return {
|
||||||
|
start: min,
|
||||||
|
stop: max,
|
||||||
|
count: 20,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VariableBoxProps {
|
||||||
|
heading: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
showTypes: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VariableBox: React.FC<VariableBoxProps> = ({
|
||||||
|
heading = "Error",
|
||||||
|
children,
|
||||||
|
showTypes = false,
|
||||||
|
}) => {
|
||||||
|
if (showTypes) {
|
||||||
|
return (
|
||||||
|
<div className="bg-white border border-grey-200 m-2">
|
||||||
|
<div className="border-b border-grey-200 p-3">
|
||||||
|
<header className="font-mono">{heading}</header>
|
||||||
|
</div>
|
||||||
|
<div className="p-3">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div>{children}</div>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface SquiggleItemProps {
|
||||||
|
/** The input string for squiggle */
|
||||||
|
expression: squiggleExpression;
|
||||||
|
width?: number;
|
||||||
|
height: number;
|
||||||
|
/** Whether to show a summary of statistics for distributions */
|
||||||
|
showSummary: boolean;
|
||||||
|
/** Whether to show type information */
|
||||||
|
showTypes: boolean;
|
||||||
|
/** Whether to show users graph controls (scale etc) */
|
||||||
|
showControls: boolean;
|
||||||
|
/** Settings for displaying functions */
|
||||||
|
chartSettings: FunctionChartSettings;
|
||||||
|
/** Environment for further function executions */
|
||||||
|
environment: environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
||||||
|
expression,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
showSummary,
|
||||||
|
showTypes = false,
|
||||||
|
showControls = false,
|
||||||
|
chartSettings,
|
||||||
|
environment,
|
||||||
|
}) => {
|
||||||
|
switch (expression.tag) {
|
||||||
|
case "number":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Number" showTypes={showTypes}>
|
||||||
|
<div className="font-semibold text-slate-600">
|
||||||
|
<NumberShower precision={3} number={expression.value} />
|
||||||
|
</div>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "distribution": {
|
||||||
|
const distType = expression.value.type();
|
||||||
|
return (
|
||||||
|
<VariableBox
|
||||||
|
heading={`Distribution (${distType})`}
|
||||||
|
showTypes={showTypes}
|
||||||
|
>
|
||||||
|
{distType === "Symbolic" && showTypes ? (
|
||||||
|
<div>{expression.value.toString()}</div>
|
||||||
|
) : null}
|
||||||
|
<DistributionChart
|
||||||
|
distribution={expression.value}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
showSummary={showSummary}
|
||||||
|
showControls={showControls}
|
||||||
|
/>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "string":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="String" showTypes={showTypes}>
|
||||||
|
<span className="text-slate-400">"</span>
|
||||||
|
<span className="text-slate-600 font-semibold">
|
||||||
|
{expression.value}
|
||||||
|
</span>
|
||||||
|
<span className="text-slate-400">"</span>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "boolean":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Boolean" showTypes={showTypes}>
|
||||||
|
{expression.value.toString()}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "symbol":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Symbol" showTypes={showTypes}>
|
||||||
|
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
|
||||||
|
<span className="text-slate-600">{expression.value}</span>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "call":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Call" showTypes={showTypes}>
|
||||||
|
{expression.value}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "array":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Array" showTypes={showTypes}>
|
||||||
|
{expression.value.map((r, i) => (
|
||||||
|
<div key={i} className="flex pt-1">
|
||||||
|
<div className="flex-none bg-slate-100 rounded-sm px-1">
|
||||||
|
<header className="text-slate-400 font-mono">{i}</header>
|
||||||
|
</div>
|
||||||
|
<div className="px-2 mb-2 grow">
|
||||||
|
<SquiggleItem
|
||||||
|
key={i}
|
||||||
|
expression={r}
|
||||||
|
width={width !== undefined ? width - 20 : width}
|
||||||
|
height={50}
|
||||||
|
showTypes={showTypes}
|
||||||
|
showControls={showControls}
|
||||||
|
chartSettings={chartSettings}
|
||||||
|
environment={environment}
|
||||||
|
showSummary={showSummary}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "record":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Record" showTypes={showTypes}>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{Object.entries(expression.value).map(([key, r]) => (
|
||||||
|
<div key={key} className="flex space-x-2">
|
||||||
|
<div className="flex-none">
|
||||||
|
<header className="text-slate-500 font-mono">{key}:</header>
|
||||||
|
</div>
|
||||||
|
<div className="px-2 grow bg-gray-50 border border-gray-100 rounded-sm">
|
||||||
|
<SquiggleItem
|
||||||
|
expression={r}
|
||||||
|
width={width !== undefined ? width - 20 : width}
|
||||||
|
height={height / 3}
|
||||||
|
showTypes={showTypes}
|
||||||
|
showSummary={showSummary}
|
||||||
|
showControls={showControls}
|
||||||
|
chartSettings={chartSettings}
|
||||||
|
environment={environment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "arraystring":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Array String" showTypes={showTypes}>
|
||||||
|
{expression.value.map((r) => `"${r}"`).join(", ")}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "date":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Date" showTypes={showTypes}>
|
||||||
|
{expression.value.toDateString()}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "timeDuration": {
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Time Duration" showTypes={showTypes}>
|
||||||
|
<NumberShower precision={3} number={expression.value} />
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "lambda":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Function" showTypes={showTypes}>
|
||||||
|
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value.parameters.join(
|
||||||
|
","
|
||||||
|
)})`}</div>
|
||||||
|
<FunctionChart
|
||||||
|
fn={expression.value}
|
||||||
|
chartSettings={chartSettings}
|
||||||
|
height={height}
|
||||||
|
environment={{
|
||||||
|
sampleCount: environment.sampleCount / 10,
|
||||||
|
xyPointLength: environment.xyPointLength / 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "lambdaDeclaration": {
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Function Declaration" showTypes={showTypes}>
|
||||||
|
<FunctionChart
|
||||||
|
fn={expression.value.fn}
|
||||||
|
chartSettings={getChartSettings(expression.value)}
|
||||||
|
height={height}
|
||||||
|
environment={{
|
||||||
|
sampleCount: environment.sampleCount / 10,
|
||||||
|
xyPointLength: environment.xyPointLength / 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return <>Should be unreachable</>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -190,7 +190,7 @@ function Checkbox<T>({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SquigglePlayground: FC<PlaygroundProps> = ({
|
export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
initialSquiggleString = "",
|
initialSquiggleString = "",
|
||||||
height = 500,
|
height = 500,
|
||||||
showTypes = false,
|
showTypes = false,
|
||||||
|
@ -207,9 +207,9 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
sampleCount: 1000,
|
sampleCount: 1000,
|
||||||
xyPointLength: 1000,
|
xyPointLength: 1000,
|
||||||
chartHeight: 150,
|
chartHeight: 150,
|
||||||
showTypes: showTypes,
|
showTypes,
|
||||||
showControls: showControls,
|
showControls,
|
||||||
showSummary: showSummary,
|
showSummary,
|
||||||
leftSizePercent: 50,
|
leftSizePercent: 50,
|
||||||
showSettingsPage: false,
|
showSettingsPage: false,
|
||||||
diagramStart: 0,
|
diagramStart: 0,
|
||||||
|
@ -414,9 +414,9 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
height={vars.chartHeight}
|
height={vars.chartHeight}
|
||||||
showTypes={vars.showTypes}
|
showTypes={vars.showTypes}
|
||||||
showControls={vars.showControls}
|
showControls={vars.showControls}
|
||||||
|
showSummary={vars.showSummary}
|
||||||
bindings={defaultBindings}
|
bindings={defaultBindings}
|
||||||
jsImports={imports}
|
jsImports={imports}
|
||||||
showSummary={vars.showSummary}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -426,7 +426,6 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SquigglePlayground;
|
|
||||||
export function renderSquigglePlaygroundToDom(props: PlaygroundProps) {
|
export function renderSquigglePlaygroundToDom(props: PlaygroundProps) {
|
||||||
const parent = document.createElement("div");
|
const parent = document.createElement("div");
|
||||||
ReactDOM.render(<SquigglePlayground {...props} />, parent);
|
ReactDOM.render(<SquigglePlayground {...props} />, parent);
|
||||||
|
|
|
@ -6,7 +6,7 @@ export {
|
||||||
renderSquigglePartialToDom,
|
renderSquigglePartialToDom,
|
||||||
} from "./components/SquiggleEditor";
|
} from "./components/SquiggleEditor";
|
||||||
export {
|
export {
|
||||||
default as SquigglePlayground,
|
SquigglePlayground,
|
||||||
renderSquigglePlaygroundToDom,
|
renderSquigglePlaygroundToDom,
|
||||||
} from "./components/SquigglePlayground";
|
} from "./components/SquigglePlayground";
|
||||||
export { SquiggleContainer } from "./components/SquiggleContainer";
|
export { SquiggleContainer } from "./components/SquiggleContainer";
|
||||||
|
|
63
packages/components/src/lib/hooks.ts
Normal file
63
packages/components/src/lib/hooks.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import {
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
jsImports,
|
||||||
|
run,
|
||||||
|
runPartial,
|
||||||
|
} from "@quri/squiggle-lang";
|
||||||
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
|
|
||||||
|
type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = {
|
||||||
|
code: string;
|
||||||
|
bindings?: bindings;
|
||||||
|
jsImports?: jsImports;
|
||||||
|
environment?: environment;
|
||||||
|
onChange?: (expr: Extract<T, { tag: "Ok" }>["value"] | undefined) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>(
|
||||||
|
args: SquiggleArgs<T>,
|
||||||
|
f: (...args: Parameters<typeof run>) => T
|
||||||
|
) => {
|
||||||
|
// We're using observable, where div elements can have a `value` property:
|
||||||
|
// https://observablehq.com/@observablehq/introduction-to-views
|
||||||
|
//
|
||||||
|
// This is here to get the 'viewof' part of:
|
||||||
|
// viewof env = cell('normal(0,1)')
|
||||||
|
// to work
|
||||||
|
const ref = useRef<
|
||||||
|
HTMLDivElement & { value?: Extract<T, { tag: "Ok" }>["value"] }
|
||||||
|
>(null);
|
||||||
|
const result: T = useMemo<T>(
|
||||||
|
() => f(args.code, args.bindings, args.environment, args.jsImports),
|
||||||
|
[f, args.code, args.bindings, args.environment, args.jsImports]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
ref.current.value = result.tag === "Ok" ? result.value : undefined;
|
||||||
|
|
||||||
|
ref.current.dispatchEvent(new CustomEvent("input"));
|
||||||
|
}, [result]);
|
||||||
|
|
||||||
|
const { onChange } = args;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange?.(result.tag === "Ok" ? result.value : undefined);
|
||||||
|
}, [result, onChange]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
result, // squiggleExpression or externalBindings
|
||||||
|
observableRef: ref, // can be passed to outermost <div> if you want to use your component as an observablehq's view
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSquigglePartial = (
|
||||||
|
args: SquiggleArgs<ReturnType<typeof runPartial>>
|
||||||
|
) => {
|
||||||
|
return useSquiggleAny(args, runPartial);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSquiggle = (args: SquiggleArgs<ReturnType<typeof run>>) => {
|
||||||
|
return useSquiggleAny(args, run);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user