Merge pull request #777 from quantified-uncertainty/pause-autoplay
Manual play mode
This commit is contained in:
		
						commit
						f2ccf574ed
					
				| 
						 | 
					@ -8,7 +8,8 @@
 | 
				
			||||||
    "@hookform/resolvers": "^2.9.3",
 | 
					    "@hookform/resolvers": "^2.9.3",
 | 
				
			||||||
    "@quri/squiggle-lang": "^0.2.8",
 | 
					    "@quri/squiggle-lang": "^0.2.8",
 | 
				
			||||||
    "@react-hook/size": "^2.1.2",
 | 
					    "@react-hook/size": "^2.1.2",
 | 
				
			||||||
    "clsx": "^1.2.0",
 | 
					    "clsx": "^1.1.1",
 | 
				
			||||||
 | 
					    "framer-motion": "^6.4.1",
 | 
				
			||||||
    "lodash": "^4.17.21",
 | 
					    "lodash": "^4.17.21",
 | 
				
			||||||
    "react": "^18.1.0",
 | 
					    "react": "^18.1.0",
 | 
				
			||||||
    "react-ace": "^10.1.0",
 | 
					    "react-ace": "^10.1.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import _ from "lodash";
 | 
					import _ from "lodash";
 | 
				
			||||||
import React, { FC, useMemo } from "react";
 | 
					import React, { FC, useMemo, useRef } 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";
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import "ace-builds/src-noconflict/theme-github";
 | 
				
			||||||
interface CodeEditorProps {
 | 
					interface CodeEditorProps {
 | 
				
			||||||
  value: string;
 | 
					  value: string;
 | 
				
			||||||
  onChange: (value: string) => void;
 | 
					  onChange: (value: string) => void;
 | 
				
			||||||
 | 
					  onSubmit?: () => void;
 | 
				
			||||||
  oneLine?: boolean;
 | 
					  oneLine?: boolean;
 | 
				
			||||||
  width?: number;
 | 
					  width?: number;
 | 
				
			||||||
  height: number;
 | 
					  height: number;
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,7 @@ interface CodeEditorProps {
 | 
				
			||||||
export const CodeEditor: FC<CodeEditorProps> = ({
 | 
					export const CodeEditor: FC<CodeEditorProps> = ({
 | 
				
			||||||
  value,
 | 
					  value,
 | 
				
			||||||
  onChange,
 | 
					  onChange,
 | 
				
			||||||
 | 
					  onSubmit,
 | 
				
			||||||
  oneLine = false,
 | 
					  oneLine = false,
 | 
				
			||||||
  showGutter = false,
 | 
					  showGutter = false,
 | 
				
			||||||
  height,
 | 
					  height,
 | 
				
			||||||
| 
						 | 
					@ -24,6 +26,10 @@ export const CodeEditor: FC<CodeEditorProps> = ({
 | 
				
			||||||
  const lineCount = value.split("\n").length;
 | 
					  const lineCount = value.split("\n").length;
 | 
				
			||||||
  const id = useMemo(() => _.uniqueId(), []);
 | 
					  const id = useMemo(() => _.uniqueId(), []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // this is necessary because AceEditor binds commands on mount, see https://github.com/securingsincity/react-ace/issues/684
 | 
				
			||||||
 | 
					  const onSubmitRef = useRef<typeof onSubmit | null>(null);
 | 
				
			||||||
 | 
					  onSubmitRef.current = onSubmit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <AceEditor
 | 
					    <AceEditor
 | 
				
			||||||
      value={value}
 | 
					      value={value}
 | 
				
			||||||
| 
						 | 
					@ -46,6 +52,13 @@ export const CodeEditor: FC<CodeEditorProps> = ({
 | 
				
			||||||
        enableBasicAutocompletion: false,
 | 
					        enableBasicAutocompletion: false,
 | 
				
			||||||
        enableLiveAutocompletion: false,
 | 
					        enableLiveAutocompletion: false,
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
 | 
					      commands={[
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: "submit",
 | 
				
			||||||
 | 
					          bindKey: { mac: "Cmd-Enter", win: "Ctrl-Enter" },
 | 
				
			||||||
 | 
					          exec: () => onSubmitRef.current?.(),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ]}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,57 +48,59 @@ export interface SquiggleChartProps {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultOnChange = () => {};
 | 
					const defaultOnChange = () => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
 | 
					export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
				
			||||||
  code = "",
 | 
					  ({
 | 
				
			||||||
  environment,
 | 
					    code = "",
 | 
				
			||||||
  onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
 | 
					 | 
				
			||||||
  height = 200,
 | 
					 | 
				
			||||||
  bindings = defaultBindings,
 | 
					 | 
				
			||||||
  jsImports = defaultImports,
 | 
					 | 
				
			||||||
  showSummary = false,
 | 
					 | 
				
			||||||
  width,
 | 
					 | 
				
			||||||
  showTypes = false,
 | 
					 | 
				
			||||||
  showControls = false,
 | 
					 | 
				
			||||||
  logX = false,
 | 
					 | 
				
			||||||
  expY = false,
 | 
					 | 
				
			||||||
  diagramStart = 0,
 | 
					 | 
				
			||||||
  diagramStop = 10,
 | 
					 | 
				
			||||||
  diagramCount = 100,
 | 
					 | 
				
			||||||
}) => {
 | 
					 | 
				
			||||||
  const result = useSquiggle({
 | 
					 | 
				
			||||||
    code,
 | 
					 | 
				
			||||||
    bindings,
 | 
					 | 
				
			||||||
    environment,
 | 
					    environment,
 | 
				
			||||||
    jsImports,
 | 
					    onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
 | 
				
			||||||
    onChange,
 | 
					    height = 200,
 | 
				
			||||||
  });
 | 
					    bindings = defaultBindings,
 | 
				
			||||||
 | 
					    jsImports = defaultImports,
 | 
				
			||||||
 | 
					    showSummary = false,
 | 
				
			||||||
 | 
					    width,
 | 
				
			||||||
 | 
					    showTypes = false,
 | 
				
			||||||
 | 
					    showControls = false,
 | 
				
			||||||
 | 
					    logX = false,
 | 
				
			||||||
 | 
					    expY = false,
 | 
				
			||||||
 | 
					    diagramStart = 0,
 | 
				
			||||||
 | 
					    diagramStop = 10,
 | 
				
			||||||
 | 
					    diagramCount = 100,
 | 
				
			||||||
 | 
					  }) => {
 | 
				
			||||||
 | 
					    const result = useSquiggle({
 | 
				
			||||||
 | 
					      code,
 | 
				
			||||||
 | 
					      bindings,
 | 
				
			||||||
 | 
					      environment,
 | 
				
			||||||
 | 
					      jsImports,
 | 
				
			||||||
 | 
					      onChange,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (result.tag !== "Ok") {
 | 
					    if (result.tag !== "Ok") {
 | 
				
			||||||
    return <SquiggleErrorAlert error={result.value} />;
 | 
					      return <SquiggleErrorAlert error={result.value} />;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let distributionPlotSettings = {
 | 
				
			||||||
 | 
					      showControls,
 | 
				
			||||||
 | 
					      showSummary,
 | 
				
			||||||
 | 
					      logX,
 | 
				
			||||||
 | 
					      expY,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let chartSettings = {
 | 
				
			||||||
 | 
					      start: diagramStart,
 | 
				
			||||||
 | 
					      stop: diagramStop,
 | 
				
			||||||
 | 
					      count: diagramCount,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <SquiggleItem
 | 
				
			||||||
 | 
					        expression={result.value}
 | 
				
			||||||
 | 
					        width={width}
 | 
				
			||||||
 | 
					        height={height}
 | 
				
			||||||
 | 
					        distributionPlotSettings={distributionPlotSettings}
 | 
				
			||||||
 | 
					        showTypes={showTypes}
 | 
				
			||||||
 | 
					        chartSettings={chartSettings}
 | 
				
			||||||
 | 
					        environment={environment ?? defaultEnvironment}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
  let distributionPlotSettings = {
 | 
					 | 
				
			||||||
    showControls,
 | 
					 | 
				
			||||||
    showSummary,
 | 
					 | 
				
			||||||
    logX,
 | 
					 | 
				
			||||||
    expY,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let chartSettings = {
 | 
					 | 
				
			||||||
    start: diagramStart,
 | 
					 | 
				
			||||||
    stop: diagramStop,
 | 
					 | 
				
			||||||
    count: diagramCount,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <SquiggleItem
 | 
					 | 
				
			||||||
      expression={result.value}
 | 
					 | 
				
			||||||
      width={width}
 | 
					 | 
				
			||||||
      height={height}
 | 
					 | 
				
			||||||
      distributionPlotSettings={distributionPlotSettings}
 | 
					 | 
				
			||||||
      showTypes={showTypes}
 | 
					 | 
				
			||||||
      chartSettings={chartSettings}
 | 
					 | 
				
			||||||
      environment={environment ?? defaultEnvironment}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,18 @@
 | 
				
			||||||
import React, { FC, Fragment, useState, useEffect } from "react";
 | 
					import React, { FC, useState, useEffect, useMemo } from "react";
 | 
				
			||||||
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
 | 
					import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
 | 
				
			||||||
import * as yup from "yup";
 | 
					import * as yup from "yup";
 | 
				
			||||||
import { useMaybeControlledValue } from "../lib/hooks";
 | 
					import { useMaybeControlledValue } from "../lib/hooks";
 | 
				
			||||||
import { yupResolver } from "@hookform/resolvers/yup";
 | 
					import { yupResolver } from "@hookform/resolvers/yup";
 | 
				
			||||||
import { Tab } from "@headlessui/react";
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ChartSquareBarIcon,
 | 
					  ChartSquareBarIcon,
 | 
				
			||||||
 | 
					  CheckCircleIcon,
 | 
				
			||||||
  CodeIcon,
 | 
					  CodeIcon,
 | 
				
			||||||
  CogIcon,
 | 
					  CogIcon,
 | 
				
			||||||
  CurrencyDollarIcon,
 | 
					  CurrencyDollarIcon,
 | 
				
			||||||
  EyeIcon,
 | 
					  EyeIcon,
 | 
				
			||||||
 | 
					  PauseIcon,
 | 
				
			||||||
 | 
					  PlayIcon,
 | 
				
			||||||
 | 
					  RefreshIcon,
 | 
				
			||||||
} from "@heroicons/react/solid";
 | 
					} from "@heroicons/react/solid";
 | 
				
			||||||
import clsx from "clsx";
 | 
					import clsx from "clsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +23,9 @@ import { CodeEditor } from "./CodeEditor";
 | 
				
			||||||
import { JsonEditor } from "./JsonEditor";
 | 
					import { JsonEditor } from "./JsonEditor";
 | 
				
			||||||
import { ErrorAlert, SuccessAlert } from "./Alert";
 | 
					import { ErrorAlert, SuccessAlert } from "./Alert";
 | 
				
			||||||
import { SquiggleContainer } from "./SquiggleContainer";
 | 
					import { SquiggleContainer } from "./SquiggleContainer";
 | 
				
			||||||
 | 
					import { Toggle } from "./ui/Toggle";
 | 
				
			||||||
 | 
					import { Checkbox } from "./ui/Checkbox";
 | 
				
			||||||
 | 
					import { StyledTab } from "./ui/StyledTab";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface PlaygroundProps {
 | 
					interface PlaygroundProps {
 | 
				
			||||||
  /** The initial squiggle string to put in the playground */
 | 
					  /** The initial squiggle string to put in the playground */
 | 
				
			||||||
| 
						 | 
					@ -44,104 +50,45 @@ interface PlaygroundProps {
 | 
				
			||||||
  showEditor?: boolean;
 | 
					  showEditor?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema = yup
 | 
					const schema = yup.object({}).shape({
 | 
				
			||||||
  .object()
 | 
					  sampleCount: yup
 | 
				
			||||||
  .shape({
 | 
					    .number()
 | 
				
			||||||
    sampleCount: yup
 | 
					    .required()
 | 
				
			||||||
      .number()
 | 
					    .positive()
 | 
				
			||||||
      .required()
 | 
					    .integer()
 | 
				
			||||||
      .positive()
 | 
					    .default(1000)
 | 
				
			||||||
      .integer()
 | 
					    .min(10)
 | 
				
			||||||
      .default(1000)
 | 
					    .max(1000000),
 | 
				
			||||||
      .min(10)
 | 
					  xyPointLength: yup
 | 
				
			||||||
      .max(1000000),
 | 
					    .number()
 | 
				
			||||||
    xyPointLength: yup
 | 
					    .required()
 | 
				
			||||||
      .number()
 | 
					    .positive()
 | 
				
			||||||
      .required()
 | 
					    .integer()
 | 
				
			||||||
      .positive()
 | 
					    .default(1000)
 | 
				
			||||||
      .integer()
 | 
					    .min(10)
 | 
				
			||||||
      .default(1000)
 | 
					    .max(10000),
 | 
				
			||||||
      .min(10)
 | 
					  chartHeight: yup.number().required().positive().integer().default(350),
 | 
				
			||||||
      .max(10000),
 | 
					  leftSizePercent: yup
 | 
				
			||||||
    chartHeight: yup.number().required().positive().integer().default(350),
 | 
					    .number()
 | 
				
			||||||
    leftSizePercent: yup
 | 
					    .required()
 | 
				
			||||||
      .number()
 | 
					    .positive()
 | 
				
			||||||
      .required()
 | 
					    .integer()
 | 
				
			||||||
      .positive()
 | 
					    .min(10)
 | 
				
			||||||
      .integer()
 | 
					    .max(100)
 | 
				
			||||||
      .min(10)
 | 
					    .default(50),
 | 
				
			||||||
      .max(100)
 | 
					  showTypes: yup.boolean().required(),
 | 
				
			||||||
      .default(50),
 | 
					  showControls: yup.boolean().required(),
 | 
				
			||||||
    showTypes: yup.boolean(),
 | 
					  showSummary: yup.boolean().required(),
 | 
				
			||||||
    showControls: yup.boolean(),
 | 
					  showEditor: yup.boolean().required(),
 | 
				
			||||||
    showSummary: yup.boolean(),
 | 
					  logX: yup.boolean().required(),
 | 
				
			||||||
    showEditor: yup.boolean(),
 | 
					  expY: yup.boolean().required(),
 | 
				
			||||||
    logX: yup.boolean(),
 | 
					  showSettingsPage: yup.boolean().default(false),
 | 
				
			||||||
    expY: yup.boolean(),
 | 
					  diagramStart: yup.number().required().positive().integer().default(0).min(0),
 | 
				
			||||||
    showSettingsPage: yup.boolean().default(false),
 | 
					  diagramStop: yup.number().required().positive().integer().default(10).min(0),
 | 
				
			||||||
    diagramStart: yup
 | 
					  diagramCount: yup.number().required().positive().integer().default(20).min(2),
 | 
				
			||||||
      .number()
 | 
					});
 | 
				
			||||||
      .required()
 | 
					 | 
				
			||||||
      .positive()
 | 
					 | 
				
			||||||
      .integer()
 | 
					 | 
				
			||||||
      .default(0)
 | 
					 | 
				
			||||||
      .min(0),
 | 
					 | 
				
			||||||
    diagramStop: yup
 | 
					 | 
				
			||||||
      .number()
 | 
					 | 
				
			||||||
      .required()
 | 
					 | 
				
			||||||
      .positive()
 | 
					 | 
				
			||||||
      .integer()
 | 
					 | 
				
			||||||
      .default(10)
 | 
					 | 
				
			||||||
      .min(0),
 | 
					 | 
				
			||||||
    diagramCount: yup
 | 
					 | 
				
			||||||
      .number()
 | 
					 | 
				
			||||||
      .required()
 | 
					 | 
				
			||||||
      .positive()
 | 
					 | 
				
			||||||
      .integer()
 | 
					 | 
				
			||||||
      .default(20)
 | 
					 | 
				
			||||||
      .min(2),
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  .required();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type StyledTabProps = {
 | 
					type FormFields = yup.InferType<typeof schema>;
 | 
				
			||||||
  name: string;
 | 
					 | 
				
			||||||
  icon: (props: React.ComponentProps<"svg">) => JSX.Element;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Tab key={name} as={Fragment}>
 | 
					 | 
				
			||||||
      {({ selected }) => (
 | 
					 | 
				
			||||||
        <button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
 | 
					 | 
				
			||||||
          <span
 | 
					 | 
				
			||||||
            className={clsx(
 | 
					 | 
				
			||||||
              "p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
 | 
					 | 
				
			||||||
              selected && "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <Icon
 | 
					 | 
				
			||||||
              className={clsx(
 | 
					 | 
				
			||||||
                "-ml-0.5 mr-2 h-4 w-4",
 | 
					 | 
				
			||||||
                selected
 | 
					 | 
				
			||||||
                  ? "text-slate-500"
 | 
					 | 
				
			||||||
                  : "text-gray-400 group-hover:text-gray-900"
 | 
					 | 
				
			||||||
              )}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <span
 | 
					 | 
				
			||||||
              className={clsx(
 | 
					 | 
				
			||||||
                selected
 | 
					 | 
				
			||||||
                  ? "text-gray-900"
 | 
					 | 
				
			||||||
                  : "text-gray-600 group-hover:text-gray-900"
 | 
					 | 
				
			||||||
              )}
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              {name}
 | 
					 | 
				
			||||||
            </span>
 | 
					 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </Tab>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({
 | 
					const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({
 | 
				
			||||||
  title,
 | 
					  title,
 | 
				
			||||||
| 
						 | 
					@ -182,27 +129,253 @@ function InputItem<T>({
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Checkbox<T>({
 | 
					const SamplingSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
 | 
				
			||||||
  name,
 | 
					 | 
				
			||||||
  label,
 | 
					 | 
				
			||||||
  register,
 | 
					  register,
 | 
				
			||||||
}: {
 | 
					}) => (
 | 
				
			||||||
  name: Path<T>;
 | 
					  <div className="space-y-6 p-3 max-w-xl">
 | 
				
			||||||
  label: string;
 | 
					    <div>
 | 
				
			||||||
  register: UseFormRegister<T>;
 | 
					      <InputItem
 | 
				
			||||||
}) {
 | 
					        name="sampleCount"
 | 
				
			||||||
  return (
 | 
					        type="number"
 | 
				
			||||||
    <label className="flex items-center">
 | 
					        label="Sample Count"
 | 
				
			||||||
      <input
 | 
					        register={register}
 | 
				
			||||||
        type="checkbox"
 | 
					 | 
				
			||||||
        {...register(name)}
 | 
					 | 
				
			||||||
        className="form-checkbox focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
 | 
					 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      {/* Clicking on the div makes the checkbox lose focus while mouse button is pressed, leading to annoying blinking; I couldn't figure out how to fix this. */}
 | 
					      <div className="mt-2">
 | 
				
			||||||
      <div className="ml-3 text-sm font-medium text-gray-700">{label}</div>
 | 
					        <Text>
 | 
				
			||||||
    </label>
 | 
					          How many samples to use for Monte Carlo simulations. This can
 | 
				
			||||||
 | 
					          occasionally be overridden by specific Squiggle programs.
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <InputItem
 | 
				
			||||||
 | 
					        name="xyPointLength"
 | 
				
			||||||
 | 
					        type="number"
 | 
				
			||||||
 | 
					        register={register}
 | 
				
			||||||
 | 
					        label="Coordinate Count (For PointSet Shapes)"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <div className="mt-2">
 | 
				
			||||||
 | 
					        <Text>
 | 
				
			||||||
 | 
					          When distributions are converted into PointSet shapes, we need to know
 | 
				
			||||||
 | 
					          how many coordinates to use.
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ViewSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
 | 
				
			||||||
 | 
					  register,
 | 
				
			||||||
 | 
					}) => (
 | 
				
			||||||
 | 
					  <div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
 | 
				
			||||||
 | 
					    <HeadedSection title="General Display Settings">
 | 
				
			||||||
 | 
					      <div className="space-y-4">
 | 
				
			||||||
 | 
					        <Checkbox
 | 
				
			||||||
 | 
					          name="showEditor"
 | 
				
			||||||
 | 
					          register={register}
 | 
				
			||||||
 | 
					          label="Show code editor on left"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <InputItem
 | 
				
			||||||
 | 
					          name="chartHeight"
 | 
				
			||||||
 | 
					          type="number"
 | 
				
			||||||
 | 
					          register={register}
 | 
				
			||||||
 | 
					          label="Chart Height (in pixels)"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Checkbox
 | 
				
			||||||
 | 
					          name="showTypes"
 | 
				
			||||||
 | 
					          register={register}
 | 
				
			||||||
 | 
					          label="Show information about displayed types"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </HeadedSection>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div className="pt-8">
 | 
				
			||||||
 | 
					      <HeadedSection title="Distribution Display Settings">
 | 
				
			||||||
 | 
					        <div className="space-y-2">
 | 
				
			||||||
 | 
					          <Checkbox
 | 
				
			||||||
 | 
					            register={register}
 | 
				
			||||||
 | 
					            name="logX"
 | 
				
			||||||
 | 
					            label="Show x scale logarithmically"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <Checkbox
 | 
				
			||||||
 | 
					            register={register}
 | 
				
			||||||
 | 
					            name="expY"
 | 
				
			||||||
 | 
					            label="Show y scale exponentially"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <Checkbox
 | 
				
			||||||
 | 
					            register={register}
 | 
				
			||||||
 | 
					            name="showControls"
 | 
				
			||||||
 | 
					            label="Show toggles to adjust scale of x and y axes"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <Checkbox
 | 
				
			||||||
 | 
					            register={register}
 | 
				
			||||||
 | 
					            name="showSummary"
 | 
				
			||||||
 | 
					            label="Show summary statistics"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </HeadedSection>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div className="pt-8">
 | 
				
			||||||
 | 
					      <HeadedSection title="Function Display Settings">
 | 
				
			||||||
 | 
					        <div className="space-y-6">
 | 
				
			||||||
 | 
					          <Text>
 | 
				
			||||||
 | 
					            When displaying functions of single variables that return numbers or
 | 
				
			||||||
 | 
					            distributions, we need to use defaults for the x-axis. We need to
 | 
				
			||||||
 | 
					            select a minimum and maximum value of x to sample, and a number n of
 | 
				
			||||||
 | 
					            the number of points to sample.
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <div className="space-y-4">
 | 
				
			||||||
 | 
					            <InputItem
 | 
				
			||||||
 | 
					              type="number"
 | 
				
			||||||
 | 
					              name="diagramStart"
 | 
				
			||||||
 | 
					              register={register}
 | 
				
			||||||
 | 
					              label="Min X Value"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <InputItem
 | 
				
			||||||
 | 
					              type="number"
 | 
				
			||||||
 | 
					              name="diagramStop"
 | 
				
			||||||
 | 
					              register={register}
 | 
				
			||||||
 | 
					              label="Max X Value"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <InputItem
 | 
				
			||||||
 | 
					              type="number"
 | 
				
			||||||
 | 
					              name="diagramCount"
 | 
				
			||||||
 | 
					              register={register}
 | 
				
			||||||
 | 
					              label="Points between X min and X max to sample"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </HeadedSection>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const InputVariablesSettings: React.FC<{
 | 
				
			||||||
 | 
					  initialImports: any; // TODO - any json type
 | 
				
			||||||
 | 
					  setImports: (imports: any) => void;
 | 
				
			||||||
 | 
					}> = ({ initialImports, setImports }) => {
 | 
				
			||||||
 | 
					  const [importString, setImportString] = useState(() =>
 | 
				
			||||||
 | 
					    JSON.stringify(initialImports)
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					  const [importsAreValid, setImportsAreValid] = useState(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onChange = (value: string) => {
 | 
				
			||||||
 | 
					    setImportString(value);
 | 
				
			||||||
 | 
					    let imports = {} as any;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      imports = JSON.parse(value);
 | 
				
			||||||
 | 
					      setImportsAreValid(true);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      setImportsAreValid(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setImports(imports);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="p-3 max-w-3xl">
 | 
				
			||||||
 | 
					      <HeadedSection title="Import Variables from JSON">
 | 
				
			||||||
 | 
					        <div className="space-y-6">
 | 
				
			||||||
 | 
					          <Text>
 | 
				
			||||||
 | 
					            You can import variables from JSON into your Squiggle code.
 | 
				
			||||||
 | 
					            Variables are accessed with dollar signs. For example, "timeNow"
 | 
				
			||||||
 | 
					            would be accessed as "$timeNow".
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <div className="border border-slate-200 mt-6 mb-2">
 | 
				
			||||||
 | 
					            <JsonEditor
 | 
				
			||||||
 | 
					              value={importString}
 | 
				
			||||||
 | 
					              onChange={onChange}
 | 
				
			||||||
 | 
					              oneLine={false}
 | 
				
			||||||
 | 
					              showGutter={true}
 | 
				
			||||||
 | 
					              height={150}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="p-1 pt-2">
 | 
				
			||||||
 | 
					            {importsAreValid ? (
 | 
				
			||||||
 | 
					              <SuccessAlert heading="Valid JSON" />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <ErrorAlert heading="Invalid JSON">
 | 
				
			||||||
 | 
					                You must use valid JSON in this editor.
 | 
				
			||||||
 | 
					              </ErrorAlert>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </HeadedSection>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RunControls: React.FC<{
 | 
				
			||||||
 | 
					  autorunMode: boolean;
 | 
				
			||||||
 | 
					  isRunning: boolean;
 | 
				
			||||||
 | 
					  isStale: boolean;
 | 
				
			||||||
 | 
					  onAutorunModeChange: (value: boolean) => void;
 | 
				
			||||||
 | 
					  run: () => void;
 | 
				
			||||||
 | 
					}> = ({ autorunMode, isRunning, isStale, onAutorunModeChange, run }) => {
 | 
				
			||||||
 | 
					  const CurrentPlayIcon = isRunning ? RefreshIcon : PlayIcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="flex space-x-1 items-center">
 | 
				
			||||||
 | 
					      {autorunMode ? null : (
 | 
				
			||||||
 | 
					        <button onClick={run}>
 | 
				
			||||||
 | 
					          <CurrentPlayIcon
 | 
				
			||||||
 | 
					            className={clsx(
 | 
				
			||||||
 | 
					              "w-8 h-8",
 | 
				
			||||||
 | 
					              isRunning && "animate-spin",
 | 
				
			||||||
 | 
					              isStale ? "text-indigo-500" : "text-gray-400"
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      <Toggle
 | 
				
			||||||
 | 
					        texts={["Autorun", "Paused"]}
 | 
				
			||||||
 | 
					        icons={[CheckCircleIcon, PauseIcon]}
 | 
				
			||||||
 | 
					        status={autorunMode}
 | 
				
			||||||
 | 
					        onChange={onAutorunModeChange}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useRunnerState = (code: string) => {
 | 
				
			||||||
 | 
					  const [autorunMode, setAutorunMode] = useState(true);
 | 
				
			||||||
 | 
					  const [renderedCode, setRenderedCode] = useState(code); // used in manual run mode only
 | 
				
			||||||
 | 
					  const [isRunning, setIsRunning] = useState(false); // used in manual run mode only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This part is tricky and fragile; we need to re-render first to make sure that the icon is spinning,
 | 
				
			||||||
 | 
					  // and only then evaluate the squiggle code (which freezes the UI).
 | 
				
			||||||
 | 
					  // Also note that `useEffect` execution order matters here.
 | 
				
			||||||
 | 
					  // Hopefully it'll all go away after we make squiggle code evaluation async.
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (renderedCode === code && isRunning) {
 | 
				
			||||||
 | 
					      // It's not possible to put this after `setRenderedCode(code)` below because React would apply
 | 
				
			||||||
 | 
					      // `setIsRunning` and `setRenderedCode` together and spinning icon will disappear immediately.
 | 
				
			||||||
 | 
					      setIsRunning(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [renderedCode, code, isRunning]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!autorunMode && isRunning) {
 | 
				
			||||||
 | 
					      setRenderedCode(code); // TODO - force run even if code hasn't changed
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [autorunMode, code, isRunning]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const run = () => {
 | 
				
			||||||
 | 
					    // The rest will be handled by useEffects above, but we need to update the spinner first.
 | 
				
			||||||
 | 
					    setIsRunning(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    run,
 | 
				
			||||||
 | 
					    renderedCode: autorunMode ? code : renderedCode,
 | 
				
			||||||
 | 
					    isRunning,
 | 
				
			||||||
 | 
					    autorunMode,
 | 
				
			||||||
 | 
					    setAutorunMode: (newValue: boolean) => {
 | 
				
			||||||
 | 
					      if (!newValue) setRenderedCode(code);
 | 
				
			||||||
 | 
					      setAutorunMode(newValue);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
					export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
  defaultCode = "",
 | 
					  defaultCode = "",
 | 
				
			||||||
| 
						 | 
					@ -222,9 +395,9 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
    defaultValue: defaultCode,
 | 
					    defaultValue: defaultCode,
 | 
				
			||||||
    onChange: onCodeChange,
 | 
					    onChange: onCodeChange,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  const [importString, setImportString] = useState("{}");
 | 
					
 | 
				
			||||||
  const [imports, setImports] = useState({});
 | 
					  const [imports, setImports] = useState({});
 | 
				
			||||||
  const [importsAreValid, setImportsAreValid] = useState(true);
 | 
					
 | 
				
			||||||
  const { register, control } = useForm({
 | 
					  const { register, control } = useForm({
 | 
				
			||||||
    resolver: yupResolver(schema),
 | 
					    resolver: yupResolver(schema),
 | 
				
			||||||
    defaultValues: {
 | 
					    defaultValues: {
 | 
				
			||||||
| 
						 | 
					@ -252,173 +425,20 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
    onSettingsChange?.(vars);
 | 
					    onSettingsChange?.(vars);
 | 
				
			||||||
  }, [vars, onSettingsChange]);
 | 
					  }, [vars, onSettingsChange]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const env: environment = {
 | 
					  const env: environment = useMemo(
 | 
				
			||||||
    sampleCount: Number(vars.sampleCount),
 | 
					    () => ({
 | 
				
			||||||
    xyPointLength: Number(vars.xyPointLength),
 | 
					      sampleCount: Number(vars.sampleCount),
 | 
				
			||||||
  };
 | 
					      xyPointLength: Number(vars.xyPointLength),
 | 
				
			||||||
  const getChangeJson = (r: string) => {
 | 
					    }),
 | 
				
			||||||
    setImportString(r);
 | 
					    [vars.sampleCount, vars.xyPointLength]
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      setImports(JSON.parse(r));
 | 
					 | 
				
			||||||
      setImportsAreValid(true);
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      setImportsAreValid(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const samplingSettings = (
 | 
					 | 
				
			||||||
    <div className="space-y-6 p-3 max-w-xl">
 | 
					 | 
				
			||||||
      <div>
 | 
					 | 
				
			||||||
        <InputItem
 | 
					 | 
				
			||||||
          name="sampleCount"
 | 
					 | 
				
			||||||
          type="number"
 | 
					 | 
				
			||||||
          label="Sample Count"
 | 
					 | 
				
			||||||
          register={register}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
        <div className="mt-2">
 | 
					 | 
				
			||||||
          <Text>
 | 
					 | 
				
			||||||
            How many samples to use for Monte Carlo simulations. This can
 | 
					 | 
				
			||||||
            occasionally be overridden by specific Squiggle programs.
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div>
 | 
					 | 
				
			||||||
        <InputItem
 | 
					 | 
				
			||||||
          name="xyPointLength"
 | 
					 | 
				
			||||||
          type="number"
 | 
					 | 
				
			||||||
          register={register}
 | 
					 | 
				
			||||||
          label="Coordinate Count (For PointSet Shapes)"
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
        <div className="mt-2">
 | 
					 | 
				
			||||||
          <Text>
 | 
					 | 
				
			||||||
            When distributions are converted into PointSet shapes, we need to
 | 
					 | 
				
			||||||
            know how many coordinates to use.
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const viewSettings = (
 | 
					  const { run, autorunMode, setAutorunMode, isRunning, renderedCode } =
 | 
				
			||||||
    <div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
 | 
					    useRunnerState(code);
 | 
				
			||||||
      <HeadedSection title="General Display Settings">
 | 
					 | 
				
			||||||
        <div className="space-y-4">
 | 
					 | 
				
			||||||
          <Checkbox
 | 
					 | 
				
			||||||
            name="showEditor"
 | 
					 | 
				
			||||||
            register={register}
 | 
					 | 
				
			||||||
            label="Show code editor on left"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
          <InputItem
 | 
					 | 
				
			||||||
            name="chartHeight"
 | 
					 | 
				
			||||||
            type="number"
 | 
					 | 
				
			||||||
            register={register}
 | 
					 | 
				
			||||||
            label="Chart Height (in pixels)"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
          <Checkbox
 | 
					 | 
				
			||||||
            name="showTypes"
 | 
					 | 
				
			||||||
            register={register}
 | 
					 | 
				
			||||||
            label="Show information about displayed types"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </HeadedSection>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div className="pt-8">
 | 
					 | 
				
			||||||
        <HeadedSection title="Distribution Display Settings">
 | 
					 | 
				
			||||||
          <div className="space-y-2">
 | 
					 | 
				
			||||||
            <Checkbox
 | 
					 | 
				
			||||||
              register={register}
 | 
					 | 
				
			||||||
              name="logX"
 | 
					 | 
				
			||||||
              label="Show x scale logarithmically"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <Checkbox
 | 
					 | 
				
			||||||
              register={register}
 | 
					 | 
				
			||||||
              name="expY"
 | 
					 | 
				
			||||||
              label="Show y scale exponentially"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <Checkbox
 | 
					 | 
				
			||||||
              register={register}
 | 
					 | 
				
			||||||
              name="showControls"
 | 
					 | 
				
			||||||
              label="Show toggles to adjust scale of x and y axes"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <Checkbox
 | 
					 | 
				
			||||||
              register={register}
 | 
					 | 
				
			||||||
              name="showSummary"
 | 
					 | 
				
			||||||
              label="Show summary statistics"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </HeadedSection>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div className="pt-8">
 | 
					 | 
				
			||||||
        <HeadedSection title="Function Display Settings">
 | 
					 | 
				
			||||||
          <div className="space-y-6">
 | 
					 | 
				
			||||||
            <Text>
 | 
					 | 
				
			||||||
              When displaying functions of single variables that return numbers
 | 
					 | 
				
			||||||
              or distributions, we need to use defaults for the x-axis. We need
 | 
					 | 
				
			||||||
              to select a minimum and maximum value of x to sample, and a number
 | 
					 | 
				
			||||||
              n of the number of points to sample.
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
            <div className="space-y-4">
 | 
					 | 
				
			||||||
              <InputItem
 | 
					 | 
				
			||||||
                type="number"
 | 
					 | 
				
			||||||
                name="diagramStart"
 | 
					 | 
				
			||||||
                register={register}
 | 
					 | 
				
			||||||
                label="Min X Value"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
              <InputItem
 | 
					 | 
				
			||||||
                type="number"
 | 
					 | 
				
			||||||
                name="diagramStop"
 | 
					 | 
				
			||||||
                register={register}
 | 
					 | 
				
			||||||
                label="Max X Value"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
              <InputItem
 | 
					 | 
				
			||||||
                type="number"
 | 
					 | 
				
			||||||
                name="diagramCount"
 | 
					 | 
				
			||||||
                register={register}
 | 
					 | 
				
			||||||
                label="Points between X min and X max to sample"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </HeadedSection>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const inputVariableSettings = (
 | 
					 | 
				
			||||||
    <div className="p-3 max-w-3xl">
 | 
					 | 
				
			||||||
      <HeadedSection title="Import Variables from JSON">
 | 
					 | 
				
			||||||
        <div className="space-y-6">
 | 
					 | 
				
			||||||
          <Text>
 | 
					 | 
				
			||||||
            You can import variables from JSON into your Squiggle code.
 | 
					 | 
				
			||||||
            Variables are accessed with dollar signs. For example, "timeNow"
 | 
					 | 
				
			||||||
            would be accessed as "$timeNow".
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
          <div className="border border-slate-200 mt-6 mb-2">
 | 
					 | 
				
			||||||
            <JsonEditor
 | 
					 | 
				
			||||||
              value={importString}
 | 
					 | 
				
			||||||
              onChange={getChangeJson}
 | 
					 | 
				
			||||||
              oneLine={false}
 | 
					 | 
				
			||||||
              showGutter={true}
 | 
					 | 
				
			||||||
              height={150}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div className="p-1 pt-2">
 | 
					 | 
				
			||||||
            {importsAreValid ? (
 | 
					 | 
				
			||||||
              <SuccessAlert heading="Valid JSON" />
 | 
					 | 
				
			||||||
            ) : (
 | 
					 | 
				
			||||||
              <ErrorAlert heading="Invalid JSON">
 | 
					 | 
				
			||||||
                You must use valid JSON in this editor.
 | 
					 | 
				
			||||||
              </ErrorAlert>
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </HeadedSection>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const squiggleChart = (
 | 
					  const squiggleChart = (
 | 
				
			||||||
    <SquiggleChart
 | 
					    <SquiggleChart
 | 
				
			||||||
      code={code}
 | 
					      code={renderedCode}
 | 
				
			||||||
      environment={env}
 | 
					      environment={env}
 | 
				
			||||||
      diagramStart={Number(vars.diagramStart)}
 | 
					      diagramStart={Number(vars.diagramStart)}
 | 
				
			||||||
      diagramStop={Number(vars.diagramStop)}
 | 
					      diagramStop={Number(vars.diagramStop)}
 | 
				
			||||||
| 
						 | 
					@ -437,8 +457,9 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
  const firstTab = vars.showEditor ? (
 | 
					  const firstTab = vars.showEditor ? (
 | 
				
			||||||
    <div className="border border-slate-200">
 | 
					    <div className="border border-slate-200">
 | 
				
			||||||
      <CodeEditor
 | 
					      <CodeEditor
 | 
				
			||||||
        value={code ?? ""}
 | 
					        value={code}
 | 
				
			||||||
        onChange={setCode}
 | 
					        onChange={setCode}
 | 
				
			||||||
 | 
					        onSubmit={run}
 | 
				
			||||||
        oneLine={false}
 | 
					        oneLine={false}
 | 
				
			||||||
        showGutter={true}
 | 
					        showGutter={true}
 | 
				
			||||||
        height={height - 1}
 | 
					        height={height - 1}
 | 
				
			||||||
| 
						 | 
					@ -449,12 +470,21 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const tabs = (
 | 
					  const tabs = (
 | 
				
			||||||
    <Tab.Panels>
 | 
					    <StyledTab.Panels>
 | 
				
			||||||
      <Tab.Panel>{firstTab}</Tab.Panel>
 | 
					      <StyledTab.Panel>{firstTab}</StyledTab.Panel>
 | 
				
			||||||
      <Tab.Panel>{samplingSettings}</Tab.Panel>
 | 
					      <StyledTab.Panel>
 | 
				
			||||||
      <Tab.Panel>{viewSettings}</Tab.Panel>
 | 
					        <SamplingSettings register={register} />
 | 
				
			||||||
      <Tab.Panel>{inputVariableSettings}</Tab.Panel>
 | 
					      </StyledTab.Panel>
 | 
				
			||||||
    </Tab.Panels>
 | 
					      <StyledTab.Panel>
 | 
				
			||||||
 | 
					        <ViewSettings register={register} />
 | 
				
			||||||
 | 
					      </StyledTab.Panel>
 | 
				
			||||||
 | 
					      <StyledTab.Panel>
 | 
				
			||||||
 | 
					        <InputVariablesSettings
 | 
				
			||||||
 | 
					          initialImports={imports}
 | 
				
			||||||
 | 
					          setImports={setImports}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </StyledTab.Panel>
 | 
				
			||||||
 | 
					    </StyledTab.Panels>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const withEditor = (
 | 
					  const withEditor = (
 | 
				
			||||||
| 
						 | 
					@ -468,20 +498,29 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SquiggleContainer>
 | 
					    <SquiggleContainer>
 | 
				
			||||||
      <Tab.Group>
 | 
					      <StyledTab.Group>
 | 
				
			||||||
        <div className="pb-4">
 | 
					        <div className="pb-4">
 | 
				
			||||||
          <Tab.List className="flex w-fit p-0.5 mt-2 rounded-md bg-slate-100 hover:bg-slate-200">
 | 
					          <div className="flex justify-between items-center mt-2">
 | 
				
			||||||
            <StyledTab
 | 
					            <StyledTab.List>
 | 
				
			||||||
              name={vars.showEditor ? "Code" : "Display"}
 | 
					              <StyledTab
 | 
				
			||||||
              icon={vars.showEditor ? CodeIcon : EyeIcon}
 | 
					                name={vars.showEditor ? "Code" : "Display"}
 | 
				
			||||||
 | 
					                icon={vars.showEditor ? CodeIcon : EyeIcon}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <StyledTab name="Sampling Settings" icon={CogIcon} />
 | 
				
			||||||
 | 
					              <StyledTab name="View Settings" icon={ChartSquareBarIcon} />
 | 
				
			||||||
 | 
					              <StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
 | 
				
			||||||
 | 
					            </StyledTab.List>
 | 
				
			||||||
 | 
					            <RunControls
 | 
				
			||||||
 | 
					              autorunMode={autorunMode}
 | 
				
			||||||
 | 
					              isStale={renderedCode !== code}
 | 
				
			||||||
 | 
					              run={run}
 | 
				
			||||||
 | 
					              isRunning={isRunning}
 | 
				
			||||||
 | 
					              onAutorunModeChange={setAutorunMode}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <StyledTab name="Sampling Settings" icon={CogIcon} />
 | 
					          </div>
 | 
				
			||||||
            <StyledTab name="View Settings" icon={ChartSquareBarIcon} />
 | 
					 | 
				
			||||||
            <StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
 | 
					 | 
				
			||||||
          </Tab.List>
 | 
					 | 
				
			||||||
          {vars.showEditor ? withEditor : withoutEditor}
 | 
					          {vars.showEditor ? withEditor : withoutEditor}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </Tab.Group>
 | 
					      </StyledTab.Group>
 | 
				
			||||||
    </SquiggleContainer>
 | 
					    </SquiggleContainer>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								packages/components/src/components/ui/Checkbox.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								packages/components/src/components/ui/Checkbox.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { Path, UseFormRegister } from "react-hook-form";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Checkbox<T>({
 | 
				
			||||||
 | 
					  name,
 | 
				
			||||||
 | 
					  label,
 | 
				
			||||||
 | 
					  register,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  name: Path<T>;
 | 
				
			||||||
 | 
					  label: string;
 | 
				
			||||||
 | 
					  register: UseFormRegister<T>;
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <label className="flex items-center">
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        type="checkbox"
 | 
				
			||||||
 | 
					        {...register(name)}
 | 
				
			||||||
 | 
					        className="form-checkbox focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      {/* Clicking on the div makes the checkbox lose focus while mouse button is pressed, leading to annoying blinking; I couldn't figure out how to fix this. */}
 | 
				
			||||||
 | 
					      <div className="ml-3 text-sm font-medium text-gray-700">{label}</div>
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								packages/components/src/components/ui/StyledTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								packages/components/src/components/ui/StyledTab.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					import React, { Fragment } from "react";
 | 
				
			||||||
 | 
					import { Tab } from "@headlessui/react";
 | 
				
			||||||
 | 
					import clsx from "clsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StyledTabProps = {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  icon: (props: React.ComponentProps<"svg">) => JSX.Element;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StyledTabType = React.FC<StyledTabProps> & {
 | 
				
			||||||
 | 
					  List: React.FC<{ children: React.ReactNode }>;
 | 
				
			||||||
 | 
					  Group: typeof Tab.Group;
 | 
				
			||||||
 | 
					  Panels: typeof Tab.Panels;
 | 
				
			||||||
 | 
					  Panel: typeof Tab.Panel;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const StyledTab: StyledTabType = ({ name, icon: Icon }) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Tab as={Fragment}>
 | 
				
			||||||
 | 
					      {({ selected }) => (
 | 
				
			||||||
 | 
					        <button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
 | 
				
			||||||
 | 
					          <span
 | 
				
			||||||
 | 
					            className={clsx(
 | 
				
			||||||
 | 
					              "p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
 | 
				
			||||||
 | 
					              selected && "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Icon
 | 
				
			||||||
 | 
					              className={clsx(
 | 
				
			||||||
 | 
					                "-ml-0.5 mr-2 h-4 w-4",
 | 
				
			||||||
 | 
					                selected
 | 
				
			||||||
 | 
					                  ? "text-slate-500"
 | 
				
			||||||
 | 
					                  : "text-gray-400 group-hover:text-gray-900"
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <span
 | 
				
			||||||
 | 
					              className={clsx(
 | 
				
			||||||
 | 
					                selected
 | 
				
			||||||
 | 
					                  ? "text-gray-900"
 | 
				
			||||||
 | 
					                  : "text-gray-600 group-hover:text-gray-900"
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              {name}
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </Tab>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					StyledTab.List = ({ children }) => (
 | 
				
			||||||
 | 
					  <Tab.List className="flex w-fit p-0.5 rounded-md bg-slate-100 hover:bg-slate-200">
 | 
				
			||||||
 | 
					    {children}
 | 
				
			||||||
 | 
					  </Tab.List>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					StyledTab.Group = Tab.Group;
 | 
				
			||||||
 | 
					StyledTab.Panels = Tab.Panels;
 | 
				
			||||||
 | 
					StyledTab.Panel = Tab.Panel;
 | 
				
			||||||
							
								
								
									
										41
									
								
								packages/components/src/components/ui/Toggle.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								packages/components/src/components/ui/Toggle.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					import clsx from "clsx";
 | 
				
			||||||
 | 
					import { motion } from "framer-motion";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IconType = (props: React.ComponentProps<"svg">) => JSX.Element;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					  status: boolean;
 | 
				
			||||||
 | 
					  onChange: (status: boolean) => void;
 | 
				
			||||||
 | 
					  texts: [string, string];
 | 
				
			||||||
 | 
					  icons: [IconType, IconType];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Toggle: React.FC<Props> = ({
 | 
				
			||||||
 | 
					  texts: [onText, offText],
 | 
				
			||||||
 | 
					  icons: [OnIcon, OffIcon],
 | 
				
			||||||
 | 
					  status,
 | 
				
			||||||
 | 
					  onChange,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					  const CurrentIcon = status ? OnIcon : OffIcon;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <motion.button
 | 
				
			||||||
 | 
					      layout
 | 
				
			||||||
 | 
					      transition={{ duration: 0.2 }}
 | 
				
			||||||
 | 
					      className={clsx(
 | 
				
			||||||
 | 
					        "rounded-full py-1 bg-indigo-500 text-white text-xs font-semibold flex items-center space-x-1",
 | 
				
			||||||
 | 
					        status ? "bg-indigo-500" : "bg-gray-400",
 | 
				
			||||||
 | 
					        status ? "pl-1 pr-3" : "pl-3 pr-1",
 | 
				
			||||||
 | 
					        !status && "flex-row-reverse space-x-reverse"
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      onClick={() => onChange(!status)}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <motion.div layout transition={{ duration: 0.2 }}>
 | 
				
			||||||
 | 
					        <CurrentIcon className="w-6 h-6" />
 | 
				
			||||||
 | 
					      </motion.div>
 | 
				
			||||||
 | 
					      <motion.span layout transition={{ duration: 0.2 }}>
 | 
				
			||||||
 | 
					        {status ? onText : offText}
 | 
				
			||||||
 | 
					      </motion.span>
 | 
				
			||||||
 | 
					    </motion.button>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										111
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								yarn.lock
									
									
									
									
									
								
							| 
						 | 
					@ -1802,6 +1802,18 @@
 | 
				
			||||||
    url-loader "^4.1.1"
 | 
					    url-loader "^4.1.1"
 | 
				
			||||||
    webpack "^5.72.1"
 | 
					    webpack "^5.72.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@emotion/is-prop-valid@^0.8.2":
 | 
				
			||||||
 | 
					  version "0.8.8"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
 | 
				
			||||||
 | 
					  integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@emotion/memoize" "0.7.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@emotion/memoize@0.7.4":
 | 
				
			||||||
 | 
					  version "0.7.4"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
 | 
				
			||||||
 | 
					  integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@eslint/eslintrc@^1.3.0":
 | 
					"@eslint/eslintrc@^1.3.0":
 | 
				
			||||||
  version "1.3.0"
 | 
					  version "1.3.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
 | 
					  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
 | 
				
			||||||
| 
						 | 
					@ -2237,6 +2249,59 @@
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
 | 
					  resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
 | 
				
			||||||
  integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
 | 
					  integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/animation@^10.10.1":
 | 
				
			||||||
 | 
					  version "10.10.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.10.1.tgz#d3d3ecb11c6d7507a104b080f31b07be9bc7056d"
 | 
				
			||||||
 | 
					  integrity sha512-iX839/Ir5wT7hVX0yCZYjcDhHAOkVR5hIhVBTf37qEUD693uVwrxC2i1BI9vMVPc1rIoFtftYjOtwoO9Oq/aog==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/easing" "^10.9.0"
 | 
				
			||||||
 | 
					    "@motionone/types" "^10.9.0"
 | 
				
			||||||
 | 
					    "@motionone/utils" "^10.9.0"
 | 
				
			||||||
 | 
					    tslib "^2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/dom@^10.11.0":
 | 
				
			||||||
 | 
					  version "10.11.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.11.0.tgz#dc2e46b0e21044cc9ae89a994cd6725eac09e48b"
 | 
				
			||||||
 | 
					  integrity sha512-ST56HBslkeoeDwqRFWafkD+JT5FEwlHqB2K2KGaQh6wo6zfKQ9xwjQcqgDiBv2gg6s8ycjHAdFS6dnMHQ5hXKw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/animation" "^10.10.1"
 | 
				
			||||||
 | 
					    "@motionone/generators" "^10.9.0"
 | 
				
			||||||
 | 
					    "@motionone/types" "^10.9.0"
 | 
				
			||||||
 | 
					    "@motionone/utils" "^10.9.0"
 | 
				
			||||||
 | 
					    hey-listen "^1.0.8"
 | 
				
			||||||
 | 
					    tslib "^2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/easing@^10.9.0":
 | 
				
			||||||
 | 
					  version "10.9.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.9.0.tgz#42910c70dd30e1bbba27a9612ce03bca022d0da7"
 | 
				
			||||||
 | 
					  integrity sha512-FYIr3HlQEb7aE5LOpY6BPQUaPyKeJt6VfGA+npy73+JIGqoVOjbrdZ1ZQxzTXqO76mG3UZvv1+twrDamRQsxFw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/utils" "^10.9.0"
 | 
				
			||||||
 | 
					    tslib "^2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/generators@^10.9.0":
 | 
				
			||||||
 | 
					  version "10.9.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.9.0.tgz#46f3e706f45a9566db7e0ce62d677c884813cdaa"
 | 
				
			||||||
 | 
					  integrity sha512-BOkHx4qQswJV+z/6k05qdvRENE4hG606NI5cIPTsLtSuksnRn83utuj/15VTNoFeYHuTdhwzxvIPvlPVayIGTg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/types" "^10.9.0"
 | 
				
			||||||
 | 
					    "@motionone/utils" "^10.9.0"
 | 
				
			||||||
 | 
					    tslib "^2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/types@^10.9.0":
 | 
				
			||||||
 | 
					  version "10.9.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.9.0.tgz#7caa9c1b746dd30bcf6104ce9c82a5a928385da2"
 | 
				
			||||||
 | 
					  integrity sha512-ZcEDfsrS2ym9+vExV7+59avkzEO/PLkNj16uaDvbWhi0Q/vOZ72j2LQTrtDLWVyZRIeUaB/i8DJP017Gj6UYQw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@motionone/utils@^10.9.0":
 | 
				
			||||||
 | 
					  version "10.9.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.9.0.tgz#a68fe41fe37e4365b07dfc676f014a0e43b8beb2"
 | 
				
			||||||
 | 
					  integrity sha512-5IgmwQ8TdH1HsQ9d2QZeBCu9+HkqjoYRYItRpmusoyiedPMZaKdU3pr3qFP5nbAj68Ww2sTUxgEZEOF20qJA6w==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/types" "^10.9.0"
 | 
				
			||||||
 | 
					    hey-listen "^1.0.8"
 | 
				
			||||||
 | 
					    tslib "^2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@mrmlnc/readdir-enhanced@^2.2.1":
 | 
					"@mrmlnc/readdir-enhanced@^2.2.1":
 | 
				
			||||||
  version "2.2.1"
 | 
					  version "2.2.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
 | 
					  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
 | 
				
			||||||
| 
						 | 
					@ -9149,6 +9214,27 @@ fragment-cache@^0.2.1:
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    map-cache "^0.2.2"
 | 
					    map-cache "^0.2.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					framer-motion@^6.4.1:
 | 
				
			||||||
 | 
					  version "6.4.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.4.1.tgz#d351a96d80bea5736af4175e6307bc63bb4daed1"
 | 
				
			||||||
 | 
					  integrity sha512-JiVnG3p0mO9yWxjNx3xxJaKP4hTWdm6oIbzfMJZEMtoOahSNoDTRZA/mFPzfVNj2foKwFPOLFv/lw8UUZjyUqA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@motionone/dom" "^10.11.0"
 | 
				
			||||||
 | 
					    framesync "6.0.1"
 | 
				
			||||||
 | 
					    hey-listen "^1.0.8"
 | 
				
			||||||
 | 
					    popmotion "11.0.3"
 | 
				
			||||||
 | 
					    style-value-types "5.0.0"
 | 
				
			||||||
 | 
					    tslib "^2.1.0"
 | 
				
			||||||
 | 
					  optionalDependencies:
 | 
				
			||||||
 | 
					    "@emotion/is-prop-valid" "^0.8.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					framesync@6.0.1:
 | 
				
			||||||
 | 
					  version "6.0.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20"
 | 
				
			||||||
 | 
					  integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    tslib "^2.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fresh@0.5.2:
 | 
					fresh@0.5.2:
 | 
				
			||||||
  version "0.5.2"
 | 
					  version "0.5.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
 | 
					  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
 | 
				
			||||||
| 
						 | 
					@ -9816,6 +9902,11 @@ he@^1.2.0:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
 | 
					  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
 | 
				
			||||||
  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 | 
					  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hey-listen@^1.0.8:
 | 
				
			||||||
 | 
					  version "1.0.8"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
 | 
				
			||||||
 | 
					  integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
highlight.js@^10.4.1, highlight.js@~10.7.0:
 | 
					highlight.js@^10.4.1, highlight.js@~10.7.0:
 | 
				
			||||||
  version "10.7.3"
 | 
					  version "10.7.3"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
 | 
					  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
 | 
				
			||||||
| 
						 | 
					@ -13390,6 +13481,16 @@ polished@^4.2.2:
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@babel/runtime" "^7.17.8"
 | 
					    "@babel/runtime" "^7.17.8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					popmotion@11.0.3:
 | 
				
			||||||
 | 
					  version "11.0.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9"
 | 
				
			||||||
 | 
					  integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    framesync "6.0.1"
 | 
				
			||||||
 | 
					    hey-listen "^1.0.8"
 | 
				
			||||||
 | 
					    style-value-types "5.0.0"
 | 
				
			||||||
 | 
					    tslib "^2.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
posix-character-classes@^0.1.0:
 | 
					posix-character-classes@^0.1.0:
 | 
				
			||||||
  version "0.1.1"
 | 
					  version "0.1.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
 | 
					  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
 | 
				
			||||||
| 
						 | 
					@ -16410,6 +16511,14 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    inline-style-parser "0.1.1"
 | 
					    inline-style-parser "0.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					style-value-types@5.0.0:
 | 
				
			||||||
 | 
					  version "5.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad"
 | 
				
			||||||
 | 
					  integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    hey-listen "^1.0.8"
 | 
				
			||||||
 | 
					    tslib "^2.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
stylehacks@^5.1.0:
 | 
					stylehacks@^5.1.0:
 | 
				
			||||||
  version "5.1.0"
 | 
					  version "5.1.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
 | 
					  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
 | 
				
			||||||
| 
						 | 
					@ -16967,7 +17076,7 @@ tslib@^1.8.1:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
 | 
					  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
 | 
				
			||||||
  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 | 
					  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
 | 
					tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0:
 | 
				
			||||||
  version "2.4.0"
 | 
					  version "2.4.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
 | 
					  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
 | 
				
			||||||
  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
 | 
					  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user