@ -56,8 +56,7 @@
 | 
			
		||||
    "vscode-languageserver-protocol": "^3.17.5",
 | 
			
		||||
    "vscode-uri": "^3.0.8",
 | 
			
		||||
    "web-vitals": "^3.5.2",
 | 
			
		||||
    "xstate": "^4.38.2",
 | 
			
		||||
    "zustand": "^4.5.2"
 | 
			
		||||
    "xstate": "^4.38.2"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "vite",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/App.tsx
									
									
									
									
									
								
							@ -1,4 +1,4 @@
 | 
			
		||||
import { MouseEventHandler, useEffect, useRef } from 'react'
 | 
			
		||||
import { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
 | 
			
		||||
import { uuidv4 } from 'lib/utils'
 | 
			
		||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
 | 
			
		||||
import { Stream } from './components/Stream'
 | 
			
		||||
@ -25,7 +25,7 @@ import ModalContainer from 'react-modal-promise'
 | 
			
		||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
 | 
			
		||||
import Gizmo from 'components/Gizmo'
 | 
			
		||||
import { CoreDumpManager } from 'lib/coredump'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
export function App() {
 | 
			
		||||
  useRefreshSettings(paths.FILE + 'SETTINGS')
 | 
			
		||||
@ -45,18 +45,19 @@ export function App() {
 | 
			
		||||
 | 
			
		||||
  useHotKeyListener()
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const { setHtmlRef } = useStore((s) => ({
 | 
			
		||||
    setHtmlRef: s.setHtmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const { setAppState } = useAppState()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setHtmlRef(ref)
 | 
			
		||||
    setAppState({ htmlRef: ref })
 | 
			
		||||
  }, [ref])
 | 
			
		||||
 | 
			
		||||
  const { auth, settings } = useSettingsAuthContext()
 | 
			
		||||
  const token = auth?.context?.token
 | 
			
		||||
 | 
			
		||||
  const coreDumpManager = new CoreDumpManager(engineCommandManager, ref, token)
 | 
			
		||||
  const coreDumpManager = useMemo(
 | 
			
		||||
    () => new CoreDumpManager(engineCommandManager, ref, token),
 | 
			
		||||
    []
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  const {
 | 
			
		||||
    app: { onboardingStatus },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										45
									
								
								src/AppState.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/AppState.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
import { createContext, useContext, useState, ReactNode } from 'react'
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
This is for a very small handful of global state we need that doesn't fit into
 | 
			
		||||
any of the Xstate machines.
 | 
			
		||||
Please do not fill this up with junk.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
interface AppState {
 | 
			
		||||
  isStreamReady: boolean
 | 
			
		||||
  htmlRef: React.RefObject<HTMLDivElement> | null
 | 
			
		||||
  setAppState: (newAppState: Partial<AppState>) => void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AppStateContext = createContext<AppState>({
 | 
			
		||||
  htmlRef: null,
 | 
			
		||||
  isStreamReady: false,
 | 
			
		||||
  setAppState: () => {},
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const useAppState = () => useContext(AppStateContext)
 | 
			
		||||
 | 
			
		||||
export const AppStateProvider = ({ children }: { children: ReactNode }) => {
 | 
			
		||||
  const [appState, _setAppState] = useState<AppState>({
 | 
			
		||||
    htmlRef: null,
 | 
			
		||||
    isStreamReady: false,
 | 
			
		||||
    setAppState: () => {},
 | 
			
		||||
  })
 | 
			
		||||
  const setAppState = (newAppState: Partial<AppState>) =>
 | 
			
		||||
    _setAppState({ ...appState, ...newAppState })
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <AppStateContext.Provider
 | 
			
		||||
      value={{
 | 
			
		||||
        htmlRef: appState.htmlRef,
 | 
			
		||||
        isStreamReady: appState.isStreamReady,
 | 
			
		||||
        setAppState,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </AppStateContext.Provider>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
@ -40,7 +40,7 @@ import useHotkeyWrapper from 'lib/hotkeyWrapper'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import { coreDump } from 'lang/wasm'
 | 
			
		||||
import { useMemo } from 'react'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { AppStateProvider, useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
const router = createBrowserRouter([
 | 
			
		||||
  {
 | 
			
		||||
@ -53,7 +53,9 @@ const router = createBrowserRouter([
 | 
			
		||||
        <SettingsAuthProvider>
 | 
			
		||||
          <LspProvider>
 | 
			
		||||
            <KclContextProvider>
 | 
			
		||||
              <Outlet />
 | 
			
		||||
              <AppStateProvider>
 | 
			
		||||
                <Outlet />
 | 
			
		||||
              </AppStateProvider>
 | 
			
		||||
            </KclContextProvider>
 | 
			
		||||
          </LspProvider>
 | 
			
		||||
        </SettingsAuthProvider>
 | 
			
		||||
@ -178,9 +180,7 @@ export const Router = () => {
 | 
			
		||||
function CoreDump() {
 | 
			
		||||
  const { auth } = useSettingsAuthContext()
 | 
			
		||||
  const token = auth?.context?.token
 | 
			
		||||
  const { htmlRef } = useStore((s) => ({
 | 
			
		||||
    htmlRef: s.htmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const { htmlRef } = useAppState()
 | 
			
		||||
  const coreDumpManager = useMemo(
 | 
			
		||||
    () => new CoreDumpManager(engineCommandManager, htmlRef, token),
 | 
			
		||||
    []
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ import { useKclContext } from 'lang/KclProvider'
 | 
			
		||||
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
 | 
			
		||||
import { useHotkeys } from 'react-hotkeys-hook'
 | 
			
		||||
import Tooltip from 'components/Tooltip'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
export function Toolbar({
 | 
			
		||||
  className = '',
 | 
			
		||||
@ -38,9 +38,8 @@ export function Toolbar({
 | 
			
		||||
  const toolbarButtonsRef = useRef<HTMLUListElement>(null)
 | 
			
		||||
  const { overallState } = useNetworkContext()
 | 
			
		||||
  const { isExecuting } = useKclContext()
 | 
			
		||||
  const { isStreamReady } = useStore((s) => ({
 | 
			
		||||
    isStreamReady: s.isStreamReady,
 | 
			
		||||
  }))
 | 
			
		||||
  const { isStreamReady } = useAppState()
 | 
			
		||||
 | 
			
		||||
  const disableAllButtons =
 | 
			
		||||
    (overallState !== NetworkHealthState.Ok &&
 | 
			
		||||
      overallState !== NetworkHealthState.Weak) ||
 | 
			
		||||
 | 
			
		||||
@ -6,14 +6,12 @@ import React, { useMemo } from 'react'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import Tooltip from './Tooltip'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
 | 
			
		||||
  const { auth } = useSettingsAuthContext()
 | 
			
		||||
  const token = auth?.context?.token
 | 
			
		||||
  const { htmlRef } = useStore((s) => ({
 | 
			
		||||
    htmlRef: s.htmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const { htmlRef } = useAppState()
 | 
			
		||||
  const coreDumpManager = useMemo(
 | 
			
		||||
    () => new CoreDumpManager(engineCommandManager, htmlRef, token),
 | 
			
		||||
    []
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import { deferExecution } from 'lib/utils'
 | 
			
		||||
import { Themes } from 'lib/theme'
 | 
			
		||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
 | 
			
		||||
import { useModelingContext } from './useModelingContext'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
export function useSetupEngineManager(
 | 
			
		||||
  streamRef: React.RefObject<HTMLDivElement>,
 | 
			
		||||
@ -27,9 +27,7 @@ export function useSetupEngineManager(
 | 
			
		||||
    showScaleGrid: boolean
 | 
			
		||||
  }
 | 
			
		||||
) {
 | 
			
		||||
  const { setIsStreamReady } = useStore((s) => ({
 | 
			
		||||
    setIsStreamReady: s.setIsStreamReady,
 | 
			
		||||
  }))
 | 
			
		||||
  const { setAppState } = useAppState()
 | 
			
		||||
 | 
			
		||||
  const streamWidth = streamRef?.current?.offsetWidth
 | 
			
		||||
  const streamHeight = streamRef?.current?.offsetHeight
 | 
			
		||||
@ -61,7 +59,7 @@ export function useSetupEngineManager(
 | 
			
		||||
            type: 'Set context',
 | 
			
		||||
            data: { mediaStream },
 | 
			
		||||
          }),
 | 
			
		||||
        setIsStreamReady,
 | 
			
		||||
        setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
 | 
			
		||||
        width: quadWidth,
 | 
			
		||||
        height: quadHeight,
 | 
			
		||||
        executeCode: () => {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
 | 
			
		||||
import { useKclContext } from 'lang/KclProvider'
 | 
			
		||||
import { useNetworkContext } from 'hooks/useNetworkContext'
 | 
			
		||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
// This might not be necessary, AnyStateMachine from xstate is working
 | 
			
		||||
export type AllMachines =
 | 
			
		||||
@ -47,9 +47,7 @@ export default function useStateMachineCommands<
 | 
			
		||||
  const { commandBarSend } = useCommandsContext()
 | 
			
		||||
  const { overallState } = useNetworkContext()
 | 
			
		||||
  const { isExecuting } = useKclContext()
 | 
			
		||||
  const { isStreamReady } = useStore((s) => ({
 | 
			
		||||
    isStreamReady: s.isStreamReady,
 | 
			
		||||
  }))
 | 
			
		||||
  const { isStreamReady } = useAppState()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const disableAllButtons =
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { FormEvent, useEffect } from 'react'
 | 
			
		||||
import { FormEvent, useEffect, useRef } from 'react'
 | 
			
		||||
import { remove } from '@tauri-apps/plugin-fs'
 | 
			
		||||
import {
 | 
			
		||||
  getNextProjectIndex,
 | 
			
		||||
@ -39,6 +39,7 @@ import {
 | 
			
		||||
  listProjects,
 | 
			
		||||
  renameProjectDirectory,
 | 
			
		||||
} from 'lib/tauri'
 | 
			
		||||
import { useAppState } from 'AppState'
 | 
			
		||||
 | 
			
		||||
// This route only opens in the Tauri desktop context for now,
 | 
			
		||||
// as defined in Router.tsx, so we can use the Tauri APIs and types.
 | 
			
		||||
@ -64,6 +65,12 @@ const Home = () => {
 | 
			
		||||
      splitKey: '|',
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
  const ref = useRef<HTMLDivElement>(null)
 | 
			
		||||
  const { setAppState } = useAppState()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setAppState({ htmlRef: ref })
 | 
			
		||||
  }, [ref])
 | 
			
		||||
 | 
			
		||||
  const [state, send, actor] = useMachine(homeMachine, {
 | 
			
		||||
    context: {
 | 
			
		||||
@ -198,7 +205,7 @@ const Home = () => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="relative flex flex-col h-screen overflow-hidden">
 | 
			
		||||
    <div className="relative flex flex-col h-screen overflow-hidden" ref={ref}>
 | 
			
		||||
      <AppHeader showToolbar={false} />
 | 
			
		||||
      <div className="w-full flex flex-col overflow-hidden max-w-5xl px-4 mx-auto mt-24 lg:px-2">
 | 
			
		||||
        <section>
 | 
			
		||||
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
import { create } from 'zustand'
 | 
			
		||||
 | 
			
		||||
export interface StoreState {
 | 
			
		||||
  isStreamReady: boolean
 | 
			
		||||
  setIsStreamReady: (isStreamReady: boolean) => void
 | 
			
		||||
  setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
 | 
			
		||||
  htmlRef: React.RefObject<HTMLDivElement> | null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useStore = create<StoreState>()((set, get) => {
 | 
			
		||||
  return {
 | 
			
		||||
    setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
 | 
			
		||||
    setHtmlRef: (htmlRef) => {
 | 
			
		||||
      set({ htmlRef })
 | 
			
		||||
    },
 | 
			
		||||
    htmlRef: null,
 | 
			
		||||
    isStreamReady: false,
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										43
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								yarn.lock
									
									
									
									
									
								
							@ -7565,16 +7565,7 @@ string-natural-compare@^3.0.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
 | 
			
		||||
  integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
 | 
			
		||||
 | 
			
		||||
"string-width-cjs@npm:string-width@^4.2.0":
 | 
			
		||||
  version "4.2.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
 | 
			
		||||
  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    emoji-regex "^8.0.0"
 | 
			
		||||
    is-fullwidth-code-point "^3.0.0"
 | 
			
		||||
    strip-ansi "^6.0.1"
 | 
			
		||||
 | 
			
		||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
 | 
			
		||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
 | 
			
		||||
  version "4.2.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
 | 
			
		||||
  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
 | 
			
		||||
@ -7652,14 +7643,7 @@ string_decoder@~1.1.1:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    safe-buffer "~5.1.0"
 | 
			
		||||
 | 
			
		||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
 | 
			
		||||
  version "6.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
 | 
			
		||||
  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    ansi-regex "^5.0.1"
 | 
			
		||||
 | 
			
		||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
 | 
			
		||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
 | 
			
		||||
  version "6.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
 | 
			
		||||
  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
 | 
			
		||||
@ -8159,11 +8143,6 @@ use-latest@^1.2.1:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    use-isomorphic-layout-effect "^1.1.1"
 | 
			
		||||
 | 
			
		||||
use-sync-external-store@1.2.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
 | 
			
		||||
  integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
 | 
			
		||||
 | 
			
		||||
use-sync-external-store@^1.0.0:
 | 
			
		||||
  version "1.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
 | 
			
		||||
@ -8532,7 +8511,7 @@ workerpool@6.2.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
 | 
			
		||||
  integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
 | 
			
		||||
 | 
			
		||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
 | 
			
		||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
 | 
			
		||||
  version "7.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
 | 
			
		||||
  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
 | 
			
		||||
@ -8550,15 +8529,6 @@ wrap-ansi@^6.2.0:
 | 
			
		||||
    string-width "^4.1.0"
 | 
			
		||||
    strip-ansi "^6.0.0"
 | 
			
		||||
 | 
			
		||||
wrap-ansi@^7.0.0:
 | 
			
		||||
  version "7.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
 | 
			
		||||
  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    ansi-styles "^4.0.0"
 | 
			
		||||
    string-width "^4.1.0"
 | 
			
		||||
    strip-ansi "^6.0.0"
 | 
			
		||||
 | 
			
		||||
wrap-ansi@^8.1.0:
 | 
			
		||||
  version "8.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
 | 
			
		||||
@ -8718,10 +8688,3 @@ zip-stream@^6.0.1:
 | 
			
		||||
    archiver-utils "^5.0.0"
 | 
			
		||||
    compress-commons "^6.0.2"
 | 
			
		||||
    readable-stream "^4.0.0"
 | 
			
		||||
 | 
			
		||||
zustand@^4.5.2:
 | 
			
		||||
  version "4.5.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
 | 
			
		||||
  integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    use-sync-external-store "1.2.0"
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user