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",
 | 
			
		||||
    "@quri/squiggle-lang": "^0.2.8",
 | 
			
		||||
    "@react-hook/size": "^2.1.2",
 | 
			
		||||
    "clsx": "^1.2.0",
 | 
			
		||||
    "clsx": "^1.1.1",
 | 
			
		||||
    "framer-motion": "^6.4.1",
 | 
			
		||||
    "lodash": "^4.17.21",
 | 
			
		||||
    "react": "^18.1.0",
 | 
			
		||||
    "react-ace": "^10.1.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import _ from "lodash";
 | 
			
		||||
import React, { FC, useMemo } from "react";
 | 
			
		||||
import React, { FC, useMemo, useRef } from "react";
 | 
			
		||||
import AceEditor from "react-ace";
 | 
			
		||||
 | 
			
		||||
import "ace-builds/src-noconflict/mode-golang";
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import "ace-builds/src-noconflict/theme-github";
 | 
			
		|||
interface CodeEditorProps {
 | 
			
		||||
  value: string;
 | 
			
		||||
  onChange: (value: string) => void;
 | 
			
		||||
  onSubmit?: () => void;
 | 
			
		||||
  oneLine?: boolean;
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height: number;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +18,7 @@ interface CodeEditorProps {
 | 
			
		|||
export const CodeEditor: FC<CodeEditorProps> = ({
 | 
			
		||||
  value,
 | 
			
		||||
  onChange,
 | 
			
		||||
  onSubmit,
 | 
			
		||||
  oneLine = false,
 | 
			
		||||
  showGutter = false,
 | 
			
		||||
  height,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,10 @@ export const CodeEditor: FC<CodeEditorProps> = ({
 | 
			
		|||
  const lineCount = value.split("\n").length;
 | 
			
		||||
  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 (
 | 
			
		||||
    <AceEditor
 | 
			
		||||
      value={value}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +52,13 @@ export const CodeEditor: FC<CodeEditorProps> = ({
 | 
			
		|||
        enableBasicAutocompletion: false,
 | 
			
		||||
        enableLiveAutocompletion: false,
 | 
			
		||||
      }}
 | 
			
		||||
      commands={[
 | 
			
		||||
        {
 | 
			
		||||
          name: "submit",
 | 
			
		||||
          bindKey: { mac: "Cmd-Enter", win: "Ctrl-Enter" },
 | 
			
		||||
          exec: () => onSubmitRef.current?.(),
 | 
			
		||||
        },
 | 
			
		||||
      ]}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,8 @@ export interface SquiggleChartProps {
 | 
			
		|||
 | 
			
		||||
const defaultOnChange = () => {};
 | 
			
		||||
 | 
			
		||||
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
 | 
			
		||||
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
			
		||||
  ({
 | 
			
		||||
    code = "",
 | 
			
		||||
    environment,
 | 
			
		||||
    onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
 | 
			
		||||
| 
						 | 
				
			
			@ -101,4 +102,5 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
 | 
			
		|||
        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 * as yup from "yup";
 | 
			
		||||
import { useMaybeControlledValue } from "../lib/hooks";
 | 
			
		||||
import { yupResolver } from "@hookform/resolvers/yup";
 | 
			
		||||
import { Tab } from "@headlessui/react";
 | 
			
		||||
import {
 | 
			
		||||
  ChartSquareBarIcon,
 | 
			
		||||
  CheckCircleIcon,
 | 
			
		||||
  CodeIcon,
 | 
			
		||||
  CogIcon,
 | 
			
		||||
  CurrencyDollarIcon,
 | 
			
		||||
  EyeIcon,
 | 
			
		||||
  PauseIcon,
 | 
			
		||||
  PlayIcon,
 | 
			
		||||
  RefreshIcon,
 | 
			
		||||
} from "@heroicons/react/solid";
 | 
			
		||||
import clsx from "clsx";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +23,9 @@ import { CodeEditor } from "./CodeEditor";
 | 
			
		|||
import { JsonEditor } from "./JsonEditor";
 | 
			
		||||
import { ErrorAlert, SuccessAlert } from "./Alert";
 | 
			
		||||
import { SquiggleContainer } from "./SquiggleContainer";
 | 
			
		||||
import { Toggle } from "./ui/Toggle";
 | 
			
		||||
import { Checkbox } from "./ui/Checkbox";
 | 
			
		||||
import { StyledTab } from "./ui/StyledTab";
 | 
			
		||||
 | 
			
		||||
interface PlaygroundProps {
 | 
			
		||||
  /** The initial squiggle string to put in the playground */
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +50,7 @@ interface PlaygroundProps {
 | 
			
		|||
  showEditor?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const schema = yup
 | 
			
		||||
  .object()
 | 
			
		||||
  .shape({
 | 
			
		||||
const schema = yup.object({}).shape({
 | 
			
		||||
  sampleCount: yup
 | 
			
		||||
    .number()
 | 
			
		||||
    .required()
 | 
			
		||||
| 
						 | 
				
			
			@ -72,76 +76,19 @@ const schema = yup
 | 
			
		|||
    .min(10)
 | 
			
		||||
    .max(100)
 | 
			
		||||
    .default(50),
 | 
			
		||||
    showTypes: yup.boolean(),
 | 
			
		||||
    showControls: yup.boolean(),
 | 
			
		||||
    showSummary: yup.boolean(),
 | 
			
		||||
    showEditor: yup.boolean(),
 | 
			
		||||
    logX: yup.boolean(),
 | 
			
		||||
    expY: yup.boolean(),
 | 
			
		||||
  showTypes: yup.boolean().required(),
 | 
			
		||||
  showControls: yup.boolean().required(),
 | 
			
		||||
  showSummary: yup.boolean().required(),
 | 
			
		||||
  showEditor: yup.boolean().required(),
 | 
			
		||||
  logX: yup.boolean().required(),
 | 
			
		||||
  expY: yup.boolean().required(),
 | 
			
		||||
  showSettingsPage: yup.boolean().default(false),
 | 
			
		||||
    diagramStart: yup
 | 
			
		||||
      .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();
 | 
			
		||||
  diagramStart: yup.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),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
type StyledTabProps = {
 | 
			
		||||
  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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
type FormFields = yup.InferType<typeof schema>;
 | 
			
		||||
 | 
			
		||||
const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({
 | 
			
		||||
  title,
 | 
			
		||||
| 
						 | 
				
			
			@ -182,91 +129,9 @@ function InputItem<T>({
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Checkbox<T>({
 | 
			
		||||
  name,
 | 
			
		||||
  label,
 | 
			
		||||
const SamplingSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
 | 
			
		||||
  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>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		||||
  defaultCode = "",
 | 
			
		||||
  height = 500,
 | 
			
		||||
  showTypes = false,
 | 
			
		||||
  showControls = false,
 | 
			
		||||
  showSummary = false,
 | 
			
		||||
  logX = false,
 | 
			
		||||
  expY = false,
 | 
			
		||||
  code: controlledCode,
 | 
			
		||||
  onCodeChange,
 | 
			
		||||
  onSettingsChange,
 | 
			
		||||
  showEditor = true,
 | 
			
		||||
}) => {
 | 
			
		||||
  const [code, setCode] = useMaybeControlledValue({
 | 
			
		||||
    value: controlledCode,
 | 
			
		||||
    defaultValue: defaultCode,
 | 
			
		||||
    onChange: onCodeChange,
 | 
			
		||||
  });
 | 
			
		||||
  const [importString, setImportString] = useState("{}");
 | 
			
		||||
  const [imports, setImports] = useState({});
 | 
			
		||||
  const [importsAreValid, setImportsAreValid] = useState(true);
 | 
			
		||||
  const { register, control } = useForm({
 | 
			
		||||
    resolver: yupResolver(schema),
 | 
			
		||||
    defaultValues: {
 | 
			
		||||
      sampleCount: 1000,
 | 
			
		||||
      xyPointLength: 1000,
 | 
			
		||||
      chartHeight: 150,
 | 
			
		||||
      showTypes,
 | 
			
		||||
      showControls,
 | 
			
		||||
      logX,
 | 
			
		||||
      expY,
 | 
			
		||||
      showSummary,
 | 
			
		||||
      showEditor,
 | 
			
		||||
      leftSizePercent: 50,
 | 
			
		||||
      showSettingsPage: false,
 | 
			
		||||
      diagramStart: 0,
 | 
			
		||||
      diagramStop: 10,
 | 
			
		||||
      diagramCount: 20,
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
  const vars = useWatch({
 | 
			
		||||
    control,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    onSettingsChange?.(vars);
 | 
			
		||||
  }, [vars, onSettingsChange]);
 | 
			
		||||
 | 
			
		||||
  const env: environment = {
 | 
			
		||||
    sampleCount: Number(vars.sampleCount),
 | 
			
		||||
    xyPointLength: Number(vars.xyPointLength),
 | 
			
		||||
  };
 | 
			
		||||
  const getChangeJson = (r: string) => {
 | 
			
		||||
    setImportString(r);
 | 
			
		||||
    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
 | 
			
		||||
| 
						 | 
				
			
			@ -291,15 +156,17 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
      />
 | 
			
		||||
      <div className="mt-2">
 | 
			
		||||
        <Text>
 | 
			
		||||
            When distributions are converted into PointSet shapes, we need to
 | 
			
		||||
            know how many coordinates to use.
 | 
			
		||||
          When distributions are converted into PointSet shapes, we need to know
 | 
			
		||||
          how many coordinates to use.
 | 
			
		||||
        </Text>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
  const viewSettings = (
 | 
			
		||||
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">
 | 
			
		||||
| 
						 | 
				
			
			@ -353,10 +220,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
      <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.
 | 
			
		||||
            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
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +251,28 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
  </div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
  const inputVariableSettings = (
 | 
			
		||||
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">
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +284,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
          <div className="border border-slate-200 mt-6 mb-2">
 | 
			
		||||
            <JsonEditor
 | 
			
		||||
              value={importString}
 | 
			
		||||
              onChange={getChangeJson}
 | 
			
		||||
              onChange={onChange}
 | 
			
		||||
              oneLine={false}
 | 
			
		||||
              showGutter={true}
 | 
			
		||||
              height={150}
 | 
			
		||||
| 
						 | 
				
			
			@ -415,10 +303,142 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
      </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> = ({
 | 
			
		||||
  defaultCode = "",
 | 
			
		||||
  height = 500,
 | 
			
		||||
  showTypes = false,
 | 
			
		||||
  showControls = false,
 | 
			
		||||
  showSummary = false,
 | 
			
		||||
  logX = false,
 | 
			
		||||
  expY = false,
 | 
			
		||||
  code: controlledCode,
 | 
			
		||||
  onCodeChange,
 | 
			
		||||
  onSettingsChange,
 | 
			
		||||
  showEditor = true,
 | 
			
		||||
}) => {
 | 
			
		||||
  const [code, setCode] = useMaybeControlledValue({
 | 
			
		||||
    value: controlledCode,
 | 
			
		||||
    defaultValue: defaultCode,
 | 
			
		||||
    onChange: onCodeChange,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const [imports, setImports] = useState({});
 | 
			
		||||
 | 
			
		||||
  const { register, control } = useForm({
 | 
			
		||||
    resolver: yupResolver(schema),
 | 
			
		||||
    defaultValues: {
 | 
			
		||||
      sampleCount: 1000,
 | 
			
		||||
      xyPointLength: 1000,
 | 
			
		||||
      chartHeight: 150,
 | 
			
		||||
      showTypes,
 | 
			
		||||
      showControls,
 | 
			
		||||
      logX,
 | 
			
		||||
      expY,
 | 
			
		||||
      showSummary,
 | 
			
		||||
      showEditor,
 | 
			
		||||
      leftSizePercent: 50,
 | 
			
		||||
      showSettingsPage: false,
 | 
			
		||||
      diagramStart: 0,
 | 
			
		||||
      diagramStop: 10,
 | 
			
		||||
      diagramCount: 20,
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
  const vars = useWatch({
 | 
			
		||||
    control,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    onSettingsChange?.(vars);
 | 
			
		||||
  }, [vars, onSettingsChange]);
 | 
			
		||||
 | 
			
		||||
  const env: environment = useMemo(
 | 
			
		||||
    () => ({
 | 
			
		||||
      sampleCount: Number(vars.sampleCount),
 | 
			
		||||
      xyPointLength: Number(vars.xyPointLength),
 | 
			
		||||
    }),
 | 
			
		||||
    [vars.sampleCount, vars.xyPointLength]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const { run, autorunMode, setAutorunMode, isRunning, renderedCode } =
 | 
			
		||||
    useRunnerState(code);
 | 
			
		||||
 | 
			
		||||
  const squiggleChart = (
 | 
			
		||||
    <SquiggleChart
 | 
			
		||||
      code={code}
 | 
			
		||||
      code={renderedCode}
 | 
			
		||||
      environment={env}
 | 
			
		||||
      diagramStart={Number(vars.diagramStart)}
 | 
			
		||||
      diagramStop={Number(vars.diagramStop)}
 | 
			
		||||
| 
						 | 
				
			
			@ -437,8 +457,9 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
  const firstTab = vars.showEditor ? (
 | 
			
		||||
    <div className="border border-slate-200">
 | 
			
		||||
      <CodeEditor
 | 
			
		||||
        value={code ?? ""}
 | 
			
		||||
        value={code}
 | 
			
		||||
        onChange={setCode}
 | 
			
		||||
        onSubmit={run}
 | 
			
		||||
        oneLine={false}
 | 
			
		||||
        showGutter={true}
 | 
			
		||||
        height={height - 1}
 | 
			
		||||
| 
						 | 
				
			
			@ -449,12 +470,21 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
  );
 | 
			
		||||
 | 
			
		||||
  const tabs = (
 | 
			
		||||
    <Tab.Panels>
 | 
			
		||||
      <Tab.Panel>{firstTab}</Tab.Panel>
 | 
			
		||||
      <Tab.Panel>{samplingSettings}</Tab.Panel>
 | 
			
		||||
      <Tab.Panel>{viewSettings}</Tab.Panel>
 | 
			
		||||
      <Tab.Panel>{inputVariableSettings}</Tab.Panel>
 | 
			
		||||
    </Tab.Panels>
 | 
			
		||||
    <StyledTab.Panels>
 | 
			
		||||
      <StyledTab.Panel>{firstTab}</StyledTab.Panel>
 | 
			
		||||
      <StyledTab.Panel>
 | 
			
		||||
        <SamplingSettings register={register} />
 | 
			
		||||
      </StyledTab.Panel>
 | 
			
		||||
      <StyledTab.Panel>
 | 
			
		||||
        <ViewSettings register={register} />
 | 
			
		||||
      </StyledTab.Panel>
 | 
			
		||||
      <StyledTab.Panel>
 | 
			
		||||
        <InputVariablesSettings
 | 
			
		||||
          initialImports={imports}
 | 
			
		||||
          setImports={setImports}
 | 
			
		||||
        />
 | 
			
		||||
      </StyledTab.Panel>
 | 
			
		||||
    </StyledTab.Panels>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const withEditor = (
 | 
			
		||||
| 
						 | 
				
			
			@ -468,9 +498,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
 | 
			
		||||
  return (
 | 
			
		||||
    <SquiggleContainer>
 | 
			
		||||
      <Tab.Group>
 | 
			
		||||
      <StyledTab.Group>
 | 
			
		||||
        <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.List>
 | 
			
		||||
              <StyledTab
 | 
			
		||||
                name={vars.showEditor ? "Code" : "Display"}
 | 
			
		||||
                icon={vars.showEditor ? CodeIcon : EyeIcon}
 | 
			
		||||
| 
						 | 
				
			
			@ -478,10 +509,18 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
              <StyledTab name="Sampling Settings" icon={CogIcon} />
 | 
			
		||||
              <StyledTab name="View Settings" icon={ChartSquareBarIcon} />
 | 
			
		||||
              <StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
 | 
			
		||||
          </Tab.List>
 | 
			
		||||
            </StyledTab.List>
 | 
			
		||||
            <RunControls
 | 
			
		||||
              autorunMode={autorunMode}
 | 
			
		||||
              isStale={renderedCode !== code}
 | 
			
		||||
              run={run}
 | 
			
		||||
              isRunning={isRunning}
 | 
			
		||||
              onAutorunModeChange={setAutorunMode}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          {vars.showEditor ? withEditor : withoutEditor}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Tab.Group>
 | 
			
		||||
      </StyledTab.Group>
 | 
			
		||||
    </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"
 | 
			
		||||
    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":
 | 
			
		||||
  version "1.3.0"
 | 
			
		||||
  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"
 | 
			
		||||
  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":
 | 
			
		||||
  version "2.2.1"
 | 
			
		||||
  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:
 | 
			
		||||
    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:
 | 
			
		||||
  version "0.5.2"
 | 
			
		||||
  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"
 | 
			
		||||
  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:
 | 
			
		||||
  version "10.7.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
 | 
			
		||||
| 
						 | 
				
			
			@ -13390,6 +13481,16 @@ polished@^4.2.2:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    "@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:
 | 
			
		||||
  version "0.1.1"
 | 
			
		||||
  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:
 | 
			
		||||
    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:
 | 
			
		||||
  version "5.1.0"
 | 
			
		||||
  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"
 | 
			
		||||
  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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
 | 
			
		||||
  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user