Merge pull request #777 from quantified-uncertainty/pause-autoplay

Manual play mode
This commit is contained in:
Ozzie Gooen 2022-07-05 19:52:29 -04:00 committed by GitHub
commit f2ccf574ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 641 additions and 352 deletions

View File

@ -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",

View File

@ -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?.(),
},
]}
/> />
); );
}; };

View File

@ -48,7 +48,8 @@ export interface SquiggleChartProps {
const defaultOnChange = () => {}; const defaultOnChange = () => {};
export const SquiggleChart: React.FC<SquiggleChartProps> = ({ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
({
code = "", code = "",
environment, environment,
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
@ -64,7 +65,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
diagramStart = 0, diagramStart = 0,
diagramStop = 10, diagramStop = 10,
diagramCount = 100, diagramCount = 100,
}) => { }) => {
const result = useSquiggle({ const result = useSquiggle({
code, code,
bindings, bindings,
@ -101,4 +102,5 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
environment={environment ?? defaultEnvironment} environment={environment ?? defaultEnvironment}
/> />
); );
}; }
);

View File

@ -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,9 +50,7 @@ interface PlaygroundProps {
showEditor?: boolean; showEditor?: boolean;
} }
const schema = yup const schema = yup.object({}).shape({
.object()
.shape({
sampleCount: yup sampleCount: yup
.number() .number()
.required() .required()
@ -72,76 +76,19 @@ const schema = yup
.min(10) .min(10)
.max(100) .max(100)
.default(50), .default(50),
showTypes: yup.boolean(), showTypes: yup.boolean().required(),
showControls: yup.boolean(), showControls: yup.boolean().required(),
showSummary: yup.boolean(), showSummary: yup.boolean().required(),
showEditor: yup.boolean(), showEditor: yup.boolean().required(),
logX: yup.boolean(), logX: yup.boolean().required(),
expY: yup.boolean(), expY: yup.boolean().required(),
showSettingsPage: yup.boolean().default(false), showSettingsPage: yup.boolean().default(false),
diagramStart: yup diagramStart: yup.number().required().positive().integer().default(0).min(0),
.number() diagramStop: yup.number().required().positive().integer().default(10).min(0),
.required() diagramCount: yup.number().required().positive().integer().default(20).min(2),
.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,91 +129,9 @@ function InputItem<T>({
); );
} }
function Checkbox<T>({ const SamplingSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
name,
label,
register, 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 className="space-y-6 p-3 max-w-xl">
<div> <div>
<InputItem <InputItem
@ -291,15 +156,17 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
/> />
<div className="mt-2"> <div className="mt-2">
<Text> <Text>
When distributions are converted into PointSet shapes, we need to When distributions are converted into PointSet shapes, we need to know
know how many coordinates to use. how many coordinates to use.
</Text> </Text>
</div> </div>
</div> </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"> <div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
<HeadedSection title="General Display Settings"> <HeadedSection title="General Display Settings">
<div className="space-y-4"> <div className="space-y-4">
@ -353,10 +220,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
<HeadedSection title="Function Display Settings"> <HeadedSection title="Function Display Settings">
<div className="space-y-6"> <div className="space-y-6">
<Text> <Text>
When displaying functions of single variables that return numbers When displaying functions of single variables that return numbers or
or distributions, we need to use defaults for the x-axis. We need distributions, we need to use defaults for the x-axis. We need to
to select a minimum and maximum value of x to sample, and a number select a minimum and maximum value of x to sample, and a number n of
n of the number of points to sample. the number of points to sample.
</Text> </Text>
<div className="space-y-4"> <div className="space-y-4">
<InputItem <InputItem
@ -382,9 +249,30 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
</HeadedSection> </HeadedSection>
</div> </div>
</div> </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"> <div className="p-3 max-w-3xl">
<HeadedSection title="Import Variables from JSON"> <HeadedSection title="Import Variables from JSON">
<div className="space-y-6"> <div className="space-y-6">
@ -396,7 +284,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
<div className="border border-slate-200 mt-6 mb-2"> <div className="border border-slate-200 mt-6 mb-2">
<JsonEditor <JsonEditor
value={importString} value={importString}
onChange={getChangeJson} onChange={onChange}
oneLine={false} oneLine={false}
showGutter={true} showGutter={true}
height={150} height={150}
@ -415,10 +303,142 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
</HeadedSection> </HeadedSection>
</div> </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 = ( 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,9 +498,10 @@ 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.List>
<StyledTab <StyledTab
name={vars.showEditor ? "Code" : "Display"} name={vars.showEditor ? "Code" : "Display"}
icon={vars.showEditor ? CodeIcon : EyeIcon} icon={vars.showEditor ? CodeIcon : EyeIcon}
@ -478,10 +509,18 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
<StyledTab name="Sampling Settings" icon={CogIcon} /> <StyledTab name="Sampling Settings" icon={CogIcon} />
<StyledTab name="View Settings" icon={ChartSquareBarIcon} /> <StyledTab name="View Settings" icon={ChartSquareBarIcon} />
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} /> <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} {vars.showEditor ? withEditor : withoutEditor}
</div> </div>
</Tab.Group> </StyledTab.Group>
</SquiggleContainer> </SquiggleContainer>
); );
}; };

View 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>
);
}

View 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;

View 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
View File

@ -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==