Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
c0f04d5f86 | |||
3dbc701f26 | |||
16e7ae38e3 | |||
24c7260327 | |||
72cfc4a471 | |||
2d128ed32e | |||
cd6749ba02 | |||
7243405e1b | |||
c8da057ec2 | |||
220fe5b2b8 | |||
4e6429de49 | |||
5391a65b18 | |||
592628917a | |||
4c6e8633f7 | |||
c5150468a2 | |||
39126dbff1 | |||
f86a69f12a | |||
de354ee5d3 | |||
dfef7338ee | |||
ee08948f54 | |||
832f6b65e2 | |||
68efd77c5d |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -377,6 +377,8 @@ jobs:
|
||||
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
|
||||
|
||||
publish-apps-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
||||
needs: [check-format, check-types, check-typos, build-test-web, prepare-json-files, build-test-apps]
|
||||
|
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.22.7",
|
||||
"version": "0.23.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.3",
|
||||
@ -35,7 +35,7 @@
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html2canvas-pro": "^1.5.0",
|
||||
"html2canvas-pro": "^1.5.1",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
"jszip": "^3.10.1",
|
||||
"re-resizable": "^6.9.11",
|
||||
@ -48,7 +48,7 @@
|
||||
"react-modal-promise": "^1.0.2",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"three": "^0.164.1",
|
||||
"three": "^0.166.1",
|
||||
"typescript": "^5.4.5",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uuid": "^9.0.1",
|
||||
@ -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",
|
||||
@ -110,7 +109,7 @@
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/preset-env": "^7.24.3",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@playwright/test": "^1.45.1",
|
||||
"@tauri-apps/cli": "==2.0.0-beta.13",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
@ -158,7 +157,7 @@
|
||||
"vitest": "^1.6.0",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"wait-on": "^7.2.0",
|
||||
"wasm-pack": "^0.12.1",
|
||||
"wasm-pack": "^0.13.0",
|
||||
"ws": "^8.17.0",
|
||||
"yarn": "^1.22.22"
|
||||
}
|
||||
|
@ -74,5 +74,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.22.7"
|
||||
"version": "0.23.0"
|
||||
}
|
||||
|
28
src/App.tsx
28
src/App.tsx
@ -1,6 +1,5 @@
|
||||
import { MouseEventHandler, useEffect, useRef } from 'react'
|
||||
import { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { useStore } from './useStore'
|
||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||
import { Stream } from './components/Stream'
|
||||
import { EngineCommand } from './lang/std/engineConnection'
|
||||
@ -26,6 +25,7 @@ import ModalContainer from 'react-modal-promise'
|
||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||
import Gizmo from 'components/Gizmo'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { useAppState } from 'AppState'
|
||||
|
||||
export function App() {
|
||||
useRefreshSettings(paths.FILE + 'SETTINGS')
|
||||
@ -44,22 +44,20 @@ export function App() {
|
||||
}, [projectName, projectPath])
|
||||
|
||||
useHotKeyListener()
|
||||
const { buttonDownInStream, didDragInStream, streamDimensions, setHtmlRef } =
|
||||
useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
didDragInStream: s.didDragInStream,
|
||||
streamDimensions: s.streamDimensions,
|
||||
setHtmlRef: s.setHtmlRef,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
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 },
|
||||
@ -81,7 +79,7 @@ export function App() {
|
||||
(p) => p === onboardingStatus.current
|
||||
)
|
||||
? 'opacity-20'
|
||||
: didDragInStream
|
||||
: context.store?.didDragInStream
|
||||
? 'opacity-40'
|
||||
: ''
|
||||
|
||||
@ -99,11 +97,11 @@ export function App() {
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
el: e.currentTarget,
|
||||
...streamDimensions,
|
||||
...context.store?.streamDimensions,
|
||||
})
|
||||
|
||||
const newCmdId = uuidv4()
|
||||
if (buttonDownInStream === undefined) {
|
||||
if (context.store?.buttonDownInStream === undefined) {
|
||||
debounceSocketSend({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
@ -125,7 +123,7 @@ export function App() {
|
||||
className={
|
||||
'transition-opacity transition-duration-75 ' +
|
||||
paneOpacity +
|
||||
(buttonDownInStream ? ' pointer-events-none' : '')
|
||||
(context.store?.buttonDownInStream ? ' pointer-events-none' : '')
|
||||
}
|
||||
project={{ project, file }}
|
||||
enableMenu={true}
|
||||
|
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>
|
||||
)
|
||||
}
|
@ -33,6 +33,14 @@ import LspProvider from 'components/LspProvider'
|
||||
import { KclContextProvider } from 'lang/KclProvider'
|
||||
import { BROWSER_PROJECT_NAME } from 'lib/constants'
|
||||
import { getState, setState } from 'lib/tauri'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||
import toast from 'react-hot-toast'
|
||||
import { coreDump } from 'lang/wasm'
|
||||
import { useMemo } from 'react'
|
||||
import { AppStateProvider, useAppState } from 'AppState'
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -45,7 +53,9 @@ const router = createBrowserRouter([
|
||||
<SettingsAuthProvider>
|
||||
<LspProvider>
|
||||
<KclContextProvider>
|
||||
<Outlet />
|
||||
<AppStateProvider>
|
||||
<Outlet />
|
||||
</AppStateProvider>
|
||||
</KclContextProvider>
|
||||
</LspProvider>
|
||||
</SettingsAuthProvider>
|
||||
@ -87,6 +97,7 @@ const router = createBrowserRouter([
|
||||
<Auth>
|
||||
<FileMachineProvider>
|
||||
<ModelingMachineProvider>
|
||||
<CoreDump />
|
||||
<Outlet />
|
||||
<App />
|
||||
<CommandBar />
|
||||
@ -165,3 +176,31 @@ export const Router = () => {
|
||||
</NetworkContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function CoreDump() {
|
||||
const { auth } = useSettingsAuthContext()
|
||||
const token = auth?.context?.token
|
||||
const { htmlRef } = useAppState()
|
||||
const coreDumpManager = useMemo(
|
||||
() => new CoreDumpManager(engineCommandManager, htmlRef, token),
|
||||
[]
|
||||
)
|
||||
useHotkeyWrapper(['meta + shift + .'], () => {
|
||||
toast.promise(
|
||||
coreDump(coreDumpManager, true),
|
||||
{
|
||||
loading: 'Starting core dump...',
|
||||
success: 'Core dump completed successfully',
|
||||
error: 'Error while exporting core dump',
|
||||
},
|
||||
{
|
||||
success: {
|
||||
// Note: this extended duration is especially important for Playwright e2e testing
|
||||
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
|
||||
duration: 6000,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { useStore } from 'useStore'
|
||||
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
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) ||
|
||||
|
@ -37,7 +37,7 @@ import { Dialog, Popover, Transition } from '@headlessui/react'
|
||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
||||
import toast from 'react-hot-toast'
|
||||
import { InstanceProps, create } from 'react-modal-promise'
|
||||
import { executeAst } from 'useStore'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import {
|
||||
deleteSegmentFromPipeExpression,
|
||||
makeRemoveSingleConstraintInput,
|
||||
|
@ -58,7 +58,7 @@ import {
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { executeAst, useStore } from 'useStore'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import {
|
||||
createArcGeometry,
|
||||
dashedStraight,
|
||||
@ -1444,11 +1444,10 @@ export class SceneEntities {
|
||||
selected.material.color = defaultPlaneColor(type)
|
||||
},
|
||||
onClick: async (args) => {
|
||||
const { streamDimensions } = useStore.getState()
|
||||
const { entity_id } = await sendSelectEventToEngine(
|
||||
args?.mouseEvent,
|
||||
document.getElementById('video-stream') as HTMLVideoElement,
|
||||
streamDimensions
|
||||
sceneInfra._streamDimensions
|
||||
)
|
||||
|
||||
let _entity_id = entity_id
|
||||
|
@ -103,6 +103,10 @@ export class SceneInfra {
|
||||
_baseUnit: BaseUnit = 'mm'
|
||||
_baseUnitMultiplier = 1
|
||||
_theme: Themes = Themes.System
|
||||
_streamDimensions: { streamWidth: number; streamHeight: number } = {
|
||||
streamWidth: 1280,
|
||||
streamHeight: 720,
|
||||
}
|
||||
extraSegmentTexture: Texture
|
||||
lastMouseState: MouseState = { type: 'idle' }
|
||||
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
||||
|
@ -10,7 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { executeAst } from 'useStore'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { trap } from 'lib/trap'
|
||||
|
||||
export const AvailableVars = ({
|
||||
|
@ -24,7 +24,7 @@ export const CommandBar = () => {
|
||||
}, [pathname])
|
||||
|
||||
// Hook up keyboard shortcuts
|
||||
useHotkeyWrapper(['mod+k', 'ctrl+c'], () => {
|
||||
useHotkeyWrapper(['mod+k'], () => {
|
||||
if (commandBarState.context.commands.length === 0) return
|
||||
if (commandBarState.matches('Closed')) {
|
||||
commandBarSend({ type: 'Open' })
|
||||
|
@ -1,11 +1,5 @@
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
import React, {
|
||||
createContext,
|
||||
useMemo,
|
||||
useEffect,
|
||||
useContext,
|
||||
useState,
|
||||
} from 'react'
|
||||
import React, { createContext, useMemo, useContext, useState } from 'react'
|
||||
import {
|
||||
LanguageServerClient,
|
||||
FromServer,
|
||||
@ -16,7 +10,6 @@ import {
|
||||
import { TEST, VITE_KC_API_BASE_URL } from 'env'
|
||||
import KclLanguageSupport from 'editor/plugins/lsp/kcl/language'
|
||||
import { copilotPlugin } from 'editor/plugins/lsp/copilot'
|
||||
import { useStore } from 'useStore'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { LanguageSupport } from '@codemirror/language'
|
||||
@ -73,19 +66,8 @@ type LspContext = {
|
||||
|
||||
export const LspStateContext = createContext({} as LspContext)
|
||||
export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const {
|
||||
isKclLspServerReady,
|
||||
isCopilotLspServerReady,
|
||||
setIsKclLspServerReady,
|
||||
setIsCopilotLspServerReady,
|
||||
} = useStore((s) => ({
|
||||
isKclLspServerReady: s.isKclLspServerReady,
|
||||
isCopilotLspServerReady: s.isCopilotLspServerReady,
|
||||
setIsKclLspServerReady: s.setIsKclLspServerReady,
|
||||
setIsCopilotLspServerReady: s.setIsCopilotLspServerReady,
|
||||
}))
|
||||
const [isLspReady, setIsLspReady] = useState(false)
|
||||
const [isCopilotReady, setIsCopilotReady] = useState(false)
|
||||
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
||||
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
||||
|
||||
const {
|
||||
auth,
|
||||
@ -132,7 +114,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
fromServer,
|
||||
intoServer,
|
||||
initializedCallback: () => {
|
||||
setIsLspReady(true)
|
||||
setIsKclLspReady(true)
|
||||
},
|
||||
})
|
||||
|
||||
@ -143,7 +125,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
])
|
||||
|
||||
useMemo(() => {
|
||||
if (!isTauri() && isKclLspServerReady && kclLspClient && codeManager.code) {
|
||||
if (!isTauri() && isKclLspReady && kclLspClient && codeManager.code) {
|
||||
kclLspClient.textDocumentDidOpen({
|
||||
textDocument: {
|
||||
uri: `file:///${PROJECT_ENTRYPOINT}`,
|
||||
@ -153,7 +135,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [kclLspClient, isKclLspServerReady])
|
||||
}, [kclLspClient, isKclLspReady])
|
||||
|
||||
// Here we initialize the plugin which will start the client.
|
||||
// Now that we have multi-file support the name of the file is a dep of
|
||||
@ -162,7 +144,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
// We do not want to restart the server, its just wasteful.
|
||||
const kclLSP = useMemo(() => {
|
||||
let plugin = null
|
||||
if (isKclLspServerReady && !TEST && kclLspClient) {
|
||||
if (isKclLspReady && !TEST && kclLspClient) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = new KclLanguageSupport({
|
||||
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
|
||||
@ -193,7 +175,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
plugin = lsp
|
||||
}
|
||||
return plugin
|
||||
}, [kclLspClient, isKclLspServerReady])
|
||||
}, [kclLspClient, isKclLspReady])
|
||||
|
||||
const { lspClient: copilotLspClient } = useMemo(() => {
|
||||
if (!token || token === '' || TEST) {
|
||||
@ -225,7 +207,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
fromServer,
|
||||
intoServer,
|
||||
initializedCallback: () => {
|
||||
setIsCopilotReady(true)
|
||||
setIsCopilotLspReady(true)
|
||||
},
|
||||
})
|
||||
return { lspClient }
|
||||
@ -238,7 +220,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
// We do not want to restart the server, its just wasteful.
|
||||
const copilotLSP = useMemo(() => {
|
||||
let plugin = null
|
||||
if (isCopilotLspServerReady && !TEST && copilotLspClient) {
|
||||
if (isCopilotLspReady && !TEST && copilotLspClient) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = copilotPlugin({
|
||||
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
|
||||
@ -250,7 +232,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
plugin = lsp
|
||||
}
|
||||
return plugin
|
||||
}, [copilotLspClient, isCopilotLspServerReady])
|
||||
}, [copilotLspClient, isCopilotLspReady])
|
||||
|
||||
let lspClients: LanguageServerClient[] = []
|
||||
if (kclLspClient) {
|
||||
@ -260,13 +242,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
lspClients.push(copilotLspClient)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setIsKclLspServerReady(isLspReady)
|
||||
}, [isLspReady])
|
||||
useEffect(() => {
|
||||
setIsCopilotLspServerReady(isCopilotReady)
|
||||
}, [isCopilotReady])
|
||||
|
||||
const onProjectClose = (
|
||||
file: FileEntry | null,
|
||||
projectPath: string | null,
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
applyConstraintAngleBetween,
|
||||
} from './Toolbar/SetAngleBetween'
|
||||
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
||||
import { useStore } from 'useStore'
|
||||
import {
|
||||
Selections,
|
||||
canExtrudeSelection,
|
||||
@ -54,13 +53,7 @@ import {
|
||||
sketchOnExtrudedFace,
|
||||
startSketchOnDefault,
|
||||
} from 'lang/modifyAst'
|
||||
import {
|
||||
Program,
|
||||
VariableDeclaration,
|
||||
coreDump,
|
||||
parse,
|
||||
recast,
|
||||
} from 'lang/wasm'
|
||||
import { Program, VariableDeclaration, parse, recast } from 'lang/wasm'
|
||||
import {
|
||||
getNodeFromPath,
|
||||
getNodePathFromSourceRange,
|
||||
@ -72,11 +65,9 @@ import { exportFromEngine } from 'lib/exportFromEngine'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import toast from 'react-hot-toast'
|
||||
import { EditorSelection, Transaction } from '@uiw/react-codemirror'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||
import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
@ -112,38 +103,6 @@ export const ModelingMachineProvider = ({
|
||||
let [searchParams] = useSearchParams()
|
||||
const pool = searchParams.get('pool')
|
||||
|
||||
useSetupEngineManager(streamRef, token, {
|
||||
pool: pool,
|
||||
theme: theme.current,
|
||||
highlightEdges: highlightEdges.current,
|
||||
enableSSAO: enableSSAO.current,
|
||||
showScaleGrid: showScaleGrid.current,
|
||||
})
|
||||
const { htmlRef } = useStore((s) => ({
|
||||
htmlRef: s.htmlRef,
|
||||
}))
|
||||
const coreDumpManager = new CoreDumpManager(
|
||||
engineCommandManager,
|
||||
htmlRef,
|
||||
token
|
||||
)
|
||||
useHotkeyWrapper(['meta + shift + .'], () => {
|
||||
toast.promise(
|
||||
coreDump(coreDumpManager, true),
|
||||
{
|
||||
loading: 'Starting core dump...',
|
||||
success: 'Core dump completed successfully',
|
||||
error: 'Error while exporting core dump',
|
||||
},
|
||||
{
|
||||
success: {
|
||||
// Note: this extended duration is especially important for Playwright e2e testing
|
||||
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
|
||||
duration: 6000,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
const { commandBarState } = useCommandsContext()
|
||||
|
||||
// Settings machine setup
|
||||
@ -163,6 +122,12 @@ export const ModelingMachineProvider = ({
|
||||
modelingMachine,
|
||||
{
|
||||
actions: {
|
||||
'disable copilot': () => {
|
||||
editorManager.setCopilotEnabled(false)
|
||||
},
|
||||
'enable copilot': () => {
|
||||
editorManager.setCopilotEnabled(true)
|
||||
},
|
||||
'sketch exit execute': () => {
|
||||
;(async () => {
|
||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||
@ -514,11 +479,15 @@ export const ModelingMachineProvider = ({
|
||||
services: {
|
||||
'AST-undo-startSketchOn': async ({ sketchDetails }) => {
|
||||
if (!sketchDetails) return
|
||||
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
||||
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
||||
// remove body item at varDecIndex
|
||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||
await kclManager.executeAstMock(newAst)
|
||||
if (kclManager.ast.body.length) {
|
||||
// this assumes no changes have been made to the sketch besides what we did when entering the sketch
|
||||
// i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
|
||||
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
||||
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
||||
// remove body item at varDecIndex
|
||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||
await kclManager.executeAstMock(newAst)
|
||||
}
|
||||
sceneInfra.setCallbacks({
|
||||
onClick: () => {},
|
||||
onDrag: () => {},
|
||||
@ -898,6 +867,16 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
)
|
||||
|
||||
useSetupEngineManager(streamRef, token, {
|
||||
pool: pool,
|
||||
theme: theme.current,
|
||||
highlightEdges: highlightEdges.current,
|
||||
enableSSAO: enableSSAO.current,
|
||||
modelingSend,
|
||||
modelingContext: modelingState.context,
|
||||
showScaleGrid: showScaleGrid.current,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
kclManager.registerExecuteCallback(() => {
|
||||
modelingSend({ type: 'Re-execute' })
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useStore } from 'useStore'
|
||||
import styles from './ModelingPane.module.css'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export interface ModelingPaneProps
|
||||
extends React.PropsWithChildren,
|
||||
@ -33,11 +33,9 @@ export const ModelingPane = ({
|
||||
}: ModelingPaneProps) => {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const onboardingStatus = settings.context.app.onboardingStatus
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const pointerEventsCssClass =
|
||||
buttonDownInStream || onboardingStatus.current === 'camera'
|
||||
context.store?.buttonDownInStream || onboardingStatus.current === 'camera'
|
||||
? 'pointer-events-none '
|
||||
: 'pointer-events-auto '
|
||||
return (
|
||||
|
@ -2,7 +2,6 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Resizable } from 're-resizable'
|
||||
import { HTMLAttributes, useCallback, useEffect, useState } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useStore } from 'useStore'
|
||||
import { Tab } from '@headlessui/react'
|
||||
import {
|
||||
SidebarPane,
|
||||
@ -15,6 +14,7 @@ import { ActionIcon } from 'components/ActionIcon'
|
||||
import styles from './ModelingSidebar.module.css'
|
||||
import { ModelingPane } from './ModelingPane'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
interface ModelingSidebarProps {
|
||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||
@ -23,14 +23,11 @@ interface ModelingSidebarProps {
|
||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const onboardingStatus = settings.context.app.onboardingStatus
|
||||
const { openPanes, buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
openPanes: s.openPanes,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const pointerEventsCssClass =
|
||||
buttonDownInStream ||
|
||||
context.store?.buttonDownInStream ||
|
||||
onboardingStatus.current === 'camera' ||
|
||||
openPanes.length === 0
|
||||
context.store?.openPanes.length === 0
|
||||
? 'pointer-events-none '
|
||||
: 'pointer-events-auto '
|
||||
|
||||
@ -45,7 +42,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
maxWidth={800}
|
||||
handleClasses={{
|
||||
right:
|
||||
(openPanes.length === 0 ? 'hidden ' : 'block ') +
|
||||
(context.store?.openPanes.length === 0 ? 'hidden ' : 'block ') +
|
||||
'translate-x-1/2 hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ',
|
||||
left: 'hidden',
|
||||
top: 'hidden',
|
||||
@ -82,11 +79,10 @@ function ModelingSidebarSection({
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const showDebugPanel = settings.context.modeling.showDebugPanel
|
||||
const paneIds = panes.map((pane) => pane.id)
|
||||
const { openPanes, setOpenPanes } = useStore((s) => ({
|
||||
openPanes: s.openPanes,
|
||||
setOpenPanes: s.setOpenPanes,
|
||||
}))
|
||||
const foundOpenPane = openPanes.find((pane) => paneIds.includes(pane))
|
||||
const { send, context } = useModelingContext()
|
||||
const foundOpenPane = context.store?.openPanes.find((pane) =>
|
||||
paneIds.includes(pane)
|
||||
)
|
||||
const [currentPane, setCurrentPane] = useState(
|
||||
foundOpenPane || ('none' as SidebarType | 'none')
|
||||
)
|
||||
@ -94,17 +90,37 @@ function ModelingSidebarSection({
|
||||
const togglePane = useCallback(
|
||||
(newPane: SidebarType | 'none') => {
|
||||
if (newPane === 'none') {
|
||||
setOpenPanes(openPanes.filter((p) => p !== currentPane))
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
openPanes: context.store?.openPanes.filter(
|
||||
(p) => p !== currentPane
|
||||
),
|
||||
},
|
||||
})
|
||||
setCurrentPane('none')
|
||||
} else if (newPane === currentPane) {
|
||||
setCurrentPane('none')
|
||||
setOpenPanes(openPanes.filter((p) => p !== newPane))
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
openPanes: context.store?.openPanes.filter((p) => p !== newPane),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
setOpenPanes([...openPanes.filter((p) => p !== currentPane), newPane])
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
openPanes: [
|
||||
...context.store?.openPanes.filter((p) => p !== currentPane),
|
||||
newPane,
|
||||
],
|
||||
},
|
||||
})
|
||||
setCurrentPane(newPane)
|
||||
}
|
||||
},
|
||||
[openPanes, setOpenPanes, currentPane, setCurrentPane]
|
||||
[context.store?.openPanes, send, currentPane, setCurrentPane]
|
||||
)
|
||||
|
||||
// Filter out the debug panel if it's not supposed to be shown
|
||||
@ -122,11 +138,11 @@ function ModelingSidebarSection({
|
||||
if (
|
||||
!showDebugPanel.current &&
|
||||
currentPane === 'debug' &&
|
||||
openPanes.includes('debug')
|
||||
context.store?.openPanes.includes('debug')
|
||||
) {
|
||||
togglePane('debug')
|
||||
}
|
||||
}, [showDebugPanel.current, togglePane, openPanes])
|
||||
}, [showDebugPanel.current, togglePane, context.store?.openPanes])
|
||||
|
||||
return (
|
||||
<div className={'group contents ' + className} {...props}>
|
||||
@ -152,7 +168,9 @@ function ModelingSidebarSection({
|
||||
: ' border-r-0') +
|
||||
' p-2 col-start-1 col-span-1 h-fit w-fit flex flex-col items-start gap-2 ' +
|
||||
'bg-chalkboard-10 border border-solid border-chalkboard-20 dark:bg-chalkboard-90 dark:border-chalkboard-80 group-focus-within:border-primary dark:group-focus-within:border-chalkboard-50 ' +
|
||||
(openPanes.length === 1 && currentPane === 'none' ? 'pr-0.5' : '')
|
||||
(context.store?.openPanes.length === 1 && currentPane === 'none'
|
||||
? 'pr-0.5'
|
||||
: '')
|
||||
}
|
||||
>
|
||||
<Tab key="none" className="sr-only">
|
||||
@ -172,7 +190,7 @@ function ModelingSidebarSection({
|
||||
as="article"
|
||||
className={
|
||||
'col-start-2 col-span-1 ' +
|
||||
(openPanes.length === 1
|
||||
(context.store?.openPanes.length === 1
|
||||
? currentPane !== 'none'
|
||||
? `row-start-1 row-end-3`
|
||||
: `hidden`
|
||||
|
@ -2,22 +2,19 @@ import { coreDump } from 'lang/wasm'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import Tooltip from './Tooltip'
|
||||
import { useStore } from 'useStore'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
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 coreDumpManager = new CoreDumpManager(
|
||||
engineCommandManager,
|
||||
htmlRef,
|
||||
token
|
||||
const { htmlRef } = useAppState()
|
||||
const coreDumpManager = useMemo(
|
||||
() => new CoreDumpManager(engineCommandManager, htmlRef, token),
|
||||
[]
|
||||
)
|
||||
|
||||
async function refresh() {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
||||
import { useStore } from '../useStore'
|
||||
import { getNormalisedCoordinates } from '../lib/utils'
|
||||
import Loading from './Loading'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
@ -10,25 +9,12 @@ import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||
import { butName } from 'lib/cameraControls'
|
||||
import { sendSelectEventToEngine } from 'lib/selections'
|
||||
|
||||
export const Stream = ({ className = '' }: { className?: string }) => {
|
||||
export const Stream = () => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const {
|
||||
mediaStream,
|
||||
setButtonDownInStream,
|
||||
didDragInStream,
|
||||
setDidDragInStream,
|
||||
streamDimensions,
|
||||
} = useStore((s) => ({
|
||||
mediaStream: s.mediaStream,
|
||||
setButtonDownInStream: s.setButtonDownInStream,
|
||||
didDragInStream: s.didDragInStream,
|
||||
setDidDragInStream: s.setDidDragInStream,
|
||||
streamDimensions: s.streamDimensions,
|
||||
}))
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const { state } = useModelingContext()
|
||||
const { state, send, context } = useModelingContext()
|
||||
const { overallState } = useNetworkContext()
|
||||
|
||||
const isNetworkOkay =
|
||||
@ -74,9 +60,9 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
||||
)
|
||||
return
|
||||
if (!videoRef.current) return
|
||||
if (!mediaStream) return
|
||||
videoRef.current.srcObject = mediaStream
|
||||
}, [mediaStream])
|
||||
if (!context.store?.mediaStream) return
|
||||
videoRef.current.srcObject = context.store.mediaStream
|
||||
}, [context.store?.mediaStream])
|
||||
|
||||
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!isNetworkOkay) return
|
||||
@ -88,25 +74,44 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
el: videoRef.current,
|
||||
...streamDimensions,
|
||||
...context.store?.streamDimensions,
|
||||
})
|
||||
|
||||
setButtonDownInStream(e.button)
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
buttonDownInStream: e.button,
|
||||
},
|
||||
})
|
||||
setClickCoords({ x, y })
|
||||
}
|
||||
|
||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!isNetworkOkay) return
|
||||
if (!videoRef.current) return
|
||||
setButtonDownInStream(undefined)
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
buttonDownInStream: undefined,
|
||||
},
|
||||
})
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches('Sketch no face')) return
|
||||
|
||||
if (!didDragInStream && butName(e).left) {
|
||||
sendSelectEventToEngine(e, videoRef.current, streamDimensions)
|
||||
if (!context.store?.didDragInStream && butName(e).left) {
|
||||
sendSelectEventToEngine(
|
||||
e,
|
||||
videoRef.current,
|
||||
context.store?.streamDimensions
|
||||
)
|
||||
}
|
||||
|
||||
setDidDragInStream(false)
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
didDragInStream: false,
|
||||
},
|
||||
})
|
||||
setClickCoords(undefined)
|
||||
}
|
||||
|
||||
@ -120,8 +125,13 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
||||
((clickCoords.x - e.clientX) ** 2 + (clickCoords.y - e.clientY) ** 2) **
|
||||
0.5
|
||||
|
||||
if (delta > 5 && !didDragInStream) {
|
||||
setDidDragInStream(true)
|
||||
if (delta > 5 && !context.store?.didDragInStream) {
|
||||
send({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
didDragInStream: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, ProgramMemory, Value } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selection, Selections } from 'lib/selections'
|
||||
import { PathToNode, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
|
@ -27,6 +27,7 @@ function diagnosticIsEqual(d1: Diagnostic, d2: Diagnostic): boolean {
|
||||
|
||||
export default class EditorManager {
|
||||
private _editorView: EditorView | null = null
|
||||
private _copilotEnabled: boolean = true
|
||||
|
||||
private _isShiftDown: boolean = false
|
||||
private _selectionRanges: Selections = {
|
||||
@ -47,6 +48,14 @@ export default class EditorManager {
|
||||
|
||||
private _highlightRange: [number, number] = [0, 0]
|
||||
|
||||
setCopilotEnabled(enabled: boolean) {
|
||||
this._copilotEnabled = enabled
|
||||
}
|
||||
|
||||
get copilotEnabled(): boolean {
|
||||
return this._copilotEnabled
|
||||
}
|
||||
|
||||
setEditorView(editorView: EditorView) {
|
||||
this._editorView = editorView
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspComp
|
||||
import { CopilotCompletionResponse } from 'wasm-lib/kcl/bindings/CopilotCompletionResponse'
|
||||
import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcceptCompletionParams'
|
||||
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
|
||||
const copilotPluginAnnotation = Annotation.define<null>()
|
||||
export const copilotPluginEvent = copilotPluginAnnotation.of(null)
|
||||
@ -269,6 +270,11 @@ export class CompletionRequester implements PluginValue {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure we are in a state where we can request completions.
|
||||
if (!editorManager.copilotEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.lastPos = this.viewUpdate.state.selection.main.head
|
||||
this._deffererCodeUpdate(true)
|
||||
}
|
||||
@ -618,6 +624,16 @@ export const copilotPlugin = (options: LanguageServerOptions): Extension => {
|
||||
},
|
||||
})
|
||||
|
||||
const rejectSuggestionCommand = (view: EditorView): boolean => {
|
||||
if (view.plugin === null) return false
|
||||
|
||||
// Get the current plugin from the map.
|
||||
const p = view.plugin(completionPlugin)
|
||||
if (p === null) return false
|
||||
|
||||
return p.rejectSuggestionCommand()
|
||||
}
|
||||
|
||||
const copilotAutocompleteKeymap: readonly KeyBinding[] = [
|
||||
{
|
||||
key: 'Tab',
|
||||
@ -633,27 +649,30 @@ export const copilotPlugin = (options: LanguageServerOptions): Extension => {
|
||||
},
|
||||
{
|
||||
key: 'Backspace',
|
||||
run: (view: EditorView): boolean => {
|
||||
if (view.plugin === null) return false
|
||||
|
||||
// Get the current plugin from the map.
|
||||
const p = view.plugin(completionPlugin)
|
||||
if (p === null) return false
|
||||
|
||||
return p.rejectSuggestionCommand()
|
||||
},
|
||||
run: rejectSuggestionCommand,
|
||||
},
|
||||
{
|
||||
key: 'Delete',
|
||||
run: (view: EditorView): boolean => {
|
||||
if (view.plugin === null) return false
|
||||
|
||||
// Get the current plugin from the map.
|
||||
const p = view.plugin(completionPlugin)
|
||||
if (p === null) return false
|
||||
|
||||
return p.rejectSuggestionCommand()
|
||||
},
|
||||
run: rejectSuggestionCommand,
|
||||
},
|
||||
{ key: 'Mod-z', run: rejectSuggestionCommand, preventDefault: true },
|
||||
{
|
||||
key: 'Mod-y',
|
||||
mac: 'Mod-Shift-z',
|
||||
run: rejectSuggestionCommand,
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
linux: 'Ctrl-Shift-z',
|
||||
run: rejectSuggestionCommand,
|
||||
preventDefault: true,
|
||||
},
|
||||
{ key: 'Mod-u', run: rejectSuggestionCommand, preventDefault: true },
|
||||
{
|
||||
key: 'Alt-u',
|
||||
mac: 'Mod-Shift-u',
|
||||
run: rejectSuggestionCommand,
|
||||
preventDefault: true,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||
import { useStore } from '../useStore'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { useAppState } from 'AppState'
|
||||
|
||||
export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
@ -13,26 +14,20 @@ export function useSetupEngineManager(
|
||||
theme: Themes.System,
|
||||
highlightEdges: true,
|
||||
enableSSAO: true,
|
||||
modelingSend: (() => {}) as any,
|
||||
modelingContext: {} as any,
|
||||
showScaleGrid: false,
|
||||
} as {
|
||||
pool: string | null
|
||||
theme: Themes
|
||||
highlightEdges: boolean
|
||||
enableSSAO: boolean
|
||||
modelingSend: ReturnType<typeof useModelingContext>['send']
|
||||
modelingContext: ReturnType<typeof useModelingContext>['context']
|
||||
showScaleGrid: boolean
|
||||
}
|
||||
) {
|
||||
const {
|
||||
setMediaStream,
|
||||
setIsStreamReady,
|
||||
setStreamDimensions,
|
||||
streamDimensions,
|
||||
} = useStore((s) => ({
|
||||
setMediaStream: s.setMediaStream,
|
||||
setIsStreamReady: s.setIsStreamReady,
|
||||
setStreamDimensions: s.setStreamDimensions,
|
||||
streamDimensions: s.streamDimensions,
|
||||
}))
|
||||
const { setAppState } = useAppState()
|
||||
|
||||
const streamWidth = streamRef?.current?.offsetWidth
|
||||
const streamHeight = streamRef?.current?.offsetHeight
|
||||
@ -52,10 +47,19 @@ export function useSetupEngineManager(
|
||||
streamWidth,
|
||||
streamHeight
|
||||
)
|
||||
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
|
||||
if (
|
||||
!hasSetNonZeroDimensions.current &&
|
||||
quadHeight &&
|
||||
quadWidth &&
|
||||
settings.modelingSend
|
||||
) {
|
||||
engineCommandManager.start({
|
||||
setMediaStream,
|
||||
setIsStreamReady,
|
||||
setMediaStream: (mediaStream) =>
|
||||
settings.modelingSend({
|
||||
type: 'Set context',
|
||||
data: { mediaStream },
|
||||
}),
|
||||
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
||||
width: quadWidth,
|
||||
height: quadHeight,
|
||||
executeCode: () => {
|
||||
@ -72,9 +76,14 @@ export function useSetupEngineManager(
|
||||
return modifyGrid(kclManager.engineCommandManager, hidden)
|
||||
},
|
||||
})
|
||||
setStreamDimensions({
|
||||
streamWidth: quadWidth,
|
||||
streamHeight: quadHeight,
|
||||
settings.modelingSend({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
streamDimensions: {
|
||||
streamWidth: quadWidth,
|
||||
streamHeight: quadHeight,
|
||||
},
|
||||
},
|
||||
})
|
||||
hasSetNonZeroDimensions.current = true
|
||||
}
|
||||
@ -83,6 +92,7 @@ export function useSetupEngineManager(
|
||||
useLayoutEffect(startEngineInstance, [
|
||||
streamRef?.current?.offsetWidth,
|
||||
streamRef?.current?.offsetHeight,
|
||||
settings.modelingSend,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
@ -92,16 +102,21 @@ export function useSetupEngineManager(
|
||||
streamRef?.current?.offsetHeight
|
||||
)
|
||||
if (
|
||||
streamDimensions.streamWidth !== width ||
|
||||
streamDimensions.streamHeight !== height
|
||||
settings.modelingContext.store.streamDimensions.streamWidth !== width ||
|
||||
settings.modelingContext.store.streamDimensions.streamHeight !== height
|
||||
) {
|
||||
engineCommandManager.handleResize({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
setStreamDimensions({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
settings.modelingSend({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
streamDimensions: {
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
|
@ -8,9 +8,9 @@ import { settingsMachine } from 'machines/settingsMachine'
|
||||
import { homeMachine } from 'machines/homeMachine'
|
||||
import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { useStore } from 'useStore'
|
||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||
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 { executeAst, lintAst } from 'useStore'
|
||||
import { executeAst, lintAst } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
@ -8,7 +8,7 @@ import toast from 'react-hot-toast'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
import { Annotation, KeyBinding, Transaction } from '@uiw/react-codemirror'
|
||||
|
||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||
const PERSIST_CODE_KEY = 'persistCode'
|
||||
|
||||
const codeManagerUpdateAnnotation = Annotation.define<null>()
|
||||
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(null)
|
||||
@ -25,7 +25,7 @@ export default class CodeManager {
|
||||
return
|
||||
}
|
||||
|
||||
const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN)
|
||||
const storedCode = safeLSGetItem(PERSIST_CODE_KEY)
|
||||
// TODO #819 remove zustand persistence logic in a few months
|
||||
// short term migration, shouldn't make a difference for tauri app users
|
||||
// anyway since that's filesystem based.
|
||||
@ -125,7 +125,7 @@ export default class CodeManager {
|
||||
})
|
||||
})
|
||||
} else {
|
||||
safeLSSetItem(PERSIST_CODE_TOKEN, this.code)
|
||||
safeLSSetItem(PERSIST_CODE_KEY, this.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
120
src/lang/langHelpers.ts
Normal file
120
src/lang/langHelpers.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import {
|
||||
Program,
|
||||
_executor,
|
||||
ProgramMemory,
|
||||
programMemoryInit,
|
||||
kclLint,
|
||||
} from 'lang/wasm'
|
||||
import { enginelessExecutor } from 'lib/testHelpers'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { KCLError } from 'lang/errors'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
|
||||
export type ToolTip =
|
||||
| 'lineTo'
|
||||
| 'line'
|
||||
| 'angledLine'
|
||||
| 'angledLineOfXLength'
|
||||
| 'angledLineOfYLength'
|
||||
| 'angledLineToX'
|
||||
| 'angledLineToY'
|
||||
| 'xLine'
|
||||
| 'yLine'
|
||||
| 'xLineTo'
|
||||
| 'yLineTo'
|
||||
| 'angledLineThatIntersects'
|
||||
| 'tangentialArcTo'
|
||||
|
||||
export const toolTips = [
|
||||
'sketch_line',
|
||||
'move',
|
||||
// original tooltips
|
||||
'line',
|
||||
'lineTo',
|
||||
'angledLine',
|
||||
'angledLineOfXLength',
|
||||
'angledLineOfYLength',
|
||||
'angledLineToX',
|
||||
'angledLineToY',
|
||||
'xLine',
|
||||
'yLine',
|
||||
'xLineTo',
|
||||
'yLineTo',
|
||||
'angledLineThatIntersects',
|
||||
'tangentialArcTo',
|
||||
] as any as ToolTip[]
|
||||
|
||||
export async function executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
useFakeExecutor = false,
|
||||
programMemoryOverride,
|
||||
}: {
|
||||
ast: Program
|
||||
engineCommandManager: EngineCommandManager
|
||||
useFakeExecutor?: boolean
|
||||
programMemoryOverride?: ProgramMemory
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
errors: KCLError[]
|
||||
programMemory: ProgramMemory
|
||||
}> {
|
||||
try {
|
||||
if (!useFakeExecutor) {
|
||||
engineCommandManager.endSession()
|
||||
engineCommandManager.startNewSession()
|
||||
}
|
||||
const programMemory = await (useFakeExecutor
|
||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
||||
: _executor(ast, programMemoryInit(), engineCommandManager, false))
|
||||
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
return {
|
||||
logs: [],
|
||||
errors: [],
|
||||
programMemory,
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e instanceof KCLError) {
|
||||
return {
|
||||
errors: [e],
|
||||
logs: [],
|
||||
programMemory: {
|
||||
root: {},
|
||||
return: null,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
console.log(e)
|
||||
return {
|
||||
logs: [e],
|
||||
errors: [],
|
||||
programMemory: {
|
||||
root: {},
|
||||
return: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function lintAst({
|
||||
ast,
|
||||
}: {
|
||||
ast: Program
|
||||
}): Promise<Array<Diagnostic>> {
|
||||
try {
|
||||
const discovered_findings = await kclLint(ast)
|
||||
return discovered_findings.map((lint) => {
|
||||
return {
|
||||
message: lint.finding.title,
|
||||
severity: 'info',
|
||||
from: lint.pos[0],
|
||||
to: lint.pos[1],
|
||||
}
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { ToolTip } from '../useStore'
|
||||
import { ToolTip } from 'lang/langHelpers'
|
||||
import { Selection, Selections } from 'lib/selections'
|
||||
import {
|
||||
ArrayExpression,
|
||||
|
@ -23,7 +23,7 @@ import {
|
||||
isLiteralArrayOrStatic,
|
||||
isNotLiteralArrayOrStatic,
|
||||
} from 'lang/std/sketchcombos'
|
||||
import { toolTips, ToolTip } from '../../useStore'
|
||||
import { toolTips, ToolTip } from 'lang/langHelpers'
|
||||
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
||||
|
||||
import {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import { ToolTip, toolTips } from '../../useStore'
|
||||
import { ToolTip, toolTips } from 'lang/langHelpers'
|
||||
import {
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
ConstraintLevel,
|
||||
getConstraintLevelFromSourceRange,
|
||||
} from './sketchcombos'
|
||||
import { ToolTip } from '../../useStore'
|
||||
import { ToolTip } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { err } from 'lib/trap'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { TransformCallback, VarValues } from './stdTypes'
|
||||
import { toolTips, ToolTip } from '../../useStore'
|
||||
import { ToolTip, toolTips } from 'lang/langHelpers'
|
||||
import { Selections, Selection } from 'lib/selections'
|
||||
import { cleanErrs, err } from 'lib/trap'
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ToolTip } from 'useStore'
|
||||
import { ToolTip } from 'lang/langHelpers'
|
||||
import {
|
||||
ProgramMemory,
|
||||
Path,
|
||||
|
@ -5,7 +5,7 @@ import { findUniqueName } from 'lang/modifyAst'
|
||||
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
|
||||
import { Value, parse } from 'lang/wasm'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { executeAst } from 'useStore'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { trap } from 'lib/trap'
|
||||
|
||||
const isValidVariableName = (name: string) =>
|
||||
|
File diff suppressed because one or more lines are too long
@ -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,6 +1,5 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import {
|
||||
CameraSystem,
|
||||
@ -8,11 +7,10 @@ import {
|
||||
cameraSystems,
|
||||
} from 'lib/cameraControls'
|
||||
import { SettingsSection } from 'components/Settings/SettingsSection'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function Units() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.STREAMING)
|
||||
const {
|
||||
@ -31,7 +29,7 @@ export default function Units() {
|
||||
<div
|
||||
className={
|
||||
'max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<SettingsSection
|
||||
|
@ -1,12 +1,10 @@
|
||||
import usePlatform from 'hooks/usePlatform'
|
||||
import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function CmdK() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.USER_MENU)
|
||||
const platformName = usePlatform()
|
||||
@ -16,7 +14,7 @@ export default function CmdK() {
|
||||
<div
|
||||
className={
|
||||
'max-w-full xl:max-w-4xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<h2 className="text-2xl font-bold">Command Bar</h2>
|
||||
|
@ -1,12 +1,10 @@
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
|
||||
export default function OnboardingCodeEditor() {
|
||||
useDemoCode()
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
@ -15,7 +13,7 @@ export default function OnboardingCodeEditor() {
|
||||
<div
|
||||
className={
|
||||
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto">
|
||||
|
@ -1,12 +1,10 @@
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function Export() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.SKETCHING)
|
||||
|
||||
@ -15,7 +13,7 @@ export default function Export() {
|
||||
<div
|
||||
className={
|
||||
'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
|
@ -6,14 +6,12 @@ import {
|
||||
useNextClick,
|
||||
} from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { bracketWidthConstantLine } from 'lib/exampleKcl'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function OnboardingInteractiveNumbers() {
|
||||
useDemoCode()
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.COMMAND_K)
|
||||
|
||||
@ -22,7 +20,7 @@ export default function OnboardingInteractiveNumbers() {
|
||||
<div
|
||||
className={
|
||||
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto mb-6">
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { bracketThicknessCalculationLine } from 'lib/exampleKcl'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function OnboardingParametricModeling() {
|
||||
useDemoCode()
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const {
|
||||
settings: {
|
||||
context: {
|
||||
@ -32,7 +30,7 @@ export default function OnboardingParametricModeling() {
|
||||
<div
|
||||
className={
|
||||
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto mb-6">
|
||||
|
@ -1,12 +1,10 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function ProjectMenu() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.EXPORT)
|
||||
const tauri = isTauri()
|
||||
@ -16,7 +14,7 @@ export default function ProjectMenu() {
|
||||
<div
|
||||
className={
|
||||
'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from 'useStore'
|
||||
import { useEffect } from 'react'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function Sketching() {
|
||||
const buttonDownInStream = useStore((s) => s.buttonDownInStream)
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.FUTURE_WORK)
|
||||
|
||||
@ -23,7 +23,7 @@ export default function Sketching() {
|
||||
<div
|
||||
className={
|
||||
'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<h1 className="text-2xl font-bold">Sketching</h1>
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
|
||||
export default function Streaming() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.EDITOR)
|
||||
|
||||
@ -14,7 +12,7 @@ export default function Streaming() {
|
||||
<div
|
||||
className={
|
||||
'max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto">
|
||||
|
@ -1,12 +1,10 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useStore } from '../../useStore'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export default function UserMenu() {
|
||||
const { buttonDownInStream } = useStore((s) => ({
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
}))
|
||||
const { context } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.PROJECT_MENU)
|
||||
const [avatarErrored, setAvatarErrored] = useState(false)
|
||||
@ -33,7 +31,7 @@ export default function UserMenu() {
|
||||
<div
|
||||
className={
|
||||
'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
|
||||
(buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
(context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
|
@ -12,8 +12,11 @@ import { SettingsSectionsList } from 'components/Settings/SettingsSectionsList'
|
||||
import { AllSettingsFields } from 'components/Settings/AllSettingsFields'
|
||||
import { AllKeybindingsFields } from 'components/Settings/AllKeybindingsFields'
|
||||
import { KeybindingsSectionsList } from 'components/Settings/KeybindingsSectionsList'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
|
||||
export const APP_VERSION = import.meta.env.PACKAGE_VERSION || 'unknown'
|
||||
export const APP_VERSION = isTauri()
|
||||
? import.meta.env.PACKAGE_VERSION || 'unknown'
|
||||
: 'main'
|
||||
|
||||
export const Settings = () => {
|
||||
const navigate = useNavigate()
|
||||
|
212
src/useStore.ts
212
src/useStore.ts
@ -1,212 +0,0 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import {
|
||||
Program,
|
||||
_executor,
|
||||
ProgramMemory,
|
||||
programMemoryInit,
|
||||
kclLint,
|
||||
} from './lang/wasm'
|
||||
import { enginelessExecutor } from './lib/testHelpers'
|
||||
import { EngineCommandManager } from './lang/std/engineConnection'
|
||||
import { KCLError } from './lang/errors'
|
||||
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
|
||||
export type ToolTip =
|
||||
| 'lineTo'
|
||||
| 'line'
|
||||
| 'angledLine'
|
||||
| 'angledLineOfXLength'
|
||||
| 'angledLineOfYLength'
|
||||
| 'angledLineToX'
|
||||
| 'angledLineToY'
|
||||
| 'xLine'
|
||||
| 'yLine'
|
||||
| 'xLineTo'
|
||||
| 'yLineTo'
|
||||
| 'angledLineThatIntersects'
|
||||
| 'tangentialArcTo'
|
||||
|
||||
export const toolTips = [
|
||||
'sketch_line',
|
||||
'move',
|
||||
// original tooltips
|
||||
'line',
|
||||
'lineTo',
|
||||
'angledLine',
|
||||
'angledLineOfXLength',
|
||||
'angledLineOfYLength',
|
||||
'angledLineToX',
|
||||
'angledLineToY',
|
||||
'xLine',
|
||||
'yLine',
|
||||
'xLineTo',
|
||||
'yLineTo',
|
||||
'angledLineThatIntersects',
|
||||
'tangentialArcTo',
|
||||
] as any as ToolTip[]
|
||||
|
||||
export interface StoreState {
|
||||
mediaStream?: MediaStream
|
||||
setMediaStream: (mediaStream: MediaStream) => void
|
||||
isStreamReady: boolean
|
||||
setIsStreamReady: (isStreamReady: boolean) => void
|
||||
isKclLspServerReady: boolean
|
||||
isCopilotLspServerReady: boolean
|
||||
setIsKclLspServerReady: (isKclLspServerReady: boolean) => void
|
||||
setIsCopilotLspServerReady: (isCopilotLspServerReady: boolean) => void
|
||||
buttonDownInStream: number | undefined
|
||||
setButtonDownInStream: (buttonDownInStream: number | undefined) => void
|
||||
didDragInStream: boolean
|
||||
setDidDragInStream: (didDragInStream: boolean) => void
|
||||
fileId: string
|
||||
setFileId: (fileId: string) => void
|
||||
streamDimensions: { streamWidth: number; streamHeight: number }
|
||||
setStreamDimensions: (dimensions: {
|
||||
streamWidth: number
|
||||
streamHeight: number
|
||||
}) => void
|
||||
setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
|
||||
htmlRef: React.RefObject<HTMLDivElement> | null
|
||||
|
||||
showHomeMenu: boolean
|
||||
setHomeShowMenu: (showMenu: boolean) => void
|
||||
openPanes: SidebarType[]
|
||||
setOpenPanes: (panes: SidebarType[]) => void
|
||||
homeMenuItems: {
|
||||
name: string
|
||||
path: string
|
||||
}[]
|
||||
setHomeMenuItems: (items: { name: string; path: string }[]) => void
|
||||
}
|
||||
|
||||
export const useStore = create<StoreState>()(
|
||||
persist(
|
||||
(set, get) => {
|
||||
return {
|
||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||
isStreamReady: false,
|
||||
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
||||
isKclLspServerReady: false,
|
||||
isCopilotLspServerReady: false,
|
||||
setIsKclLspServerReady: (isKclLspServerReady) =>
|
||||
set({ isKclLspServerReady }),
|
||||
setIsCopilotLspServerReady: (isCopilotLspServerReady) =>
|
||||
set({ isCopilotLspServerReady }),
|
||||
buttonDownInStream: undefined,
|
||||
setButtonDownInStream: (buttonDownInStream) => {
|
||||
set({ buttonDownInStream })
|
||||
},
|
||||
setHtmlRef: (htmlRef) => {
|
||||
set({ htmlRef })
|
||||
},
|
||||
htmlRef: null,
|
||||
didDragInStream: false,
|
||||
setDidDragInStream: (didDragInStream) => {
|
||||
set({ didDragInStream })
|
||||
},
|
||||
// For stream event handling
|
||||
fileId: '',
|
||||
setFileId: (fileId) => set({ fileId }),
|
||||
streamDimensions: { streamWidth: 1280, streamHeight: 720 },
|
||||
setStreamDimensions: (streamDimensions) => {
|
||||
set({ streamDimensions })
|
||||
},
|
||||
|
||||
// tauri specific app settings
|
||||
defaultDir: {
|
||||
dir: '',
|
||||
},
|
||||
openPanes: ['code'],
|
||||
setOpenPanes: (openPanes) => set({ openPanes }),
|
||||
showHomeMenu: true,
|
||||
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
||||
homeMenuItems: [],
|
||||
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'store',
|
||||
partialize: (state) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(state).filter(([key]) => ['openPanes'].includes(key))
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
export async function executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
useFakeExecutor = false,
|
||||
programMemoryOverride,
|
||||
}: {
|
||||
ast: Program
|
||||
engineCommandManager: EngineCommandManager
|
||||
useFakeExecutor?: boolean
|
||||
programMemoryOverride?: ProgramMemory
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
errors: KCLError[]
|
||||
programMemory: ProgramMemory
|
||||
}> {
|
||||
try {
|
||||
if (!useFakeExecutor) {
|
||||
engineCommandManager.endSession()
|
||||
engineCommandManager.startNewSession()
|
||||
}
|
||||
const programMemory = await (useFakeExecutor
|
||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
||||
: _executor(ast, programMemoryInit(), engineCommandManager, false))
|
||||
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
return {
|
||||
logs: [],
|
||||
errors: [],
|
||||
programMemory,
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e instanceof KCLError) {
|
||||
return {
|
||||
errors: [e],
|
||||
logs: [],
|
||||
programMemory: {
|
||||
root: {},
|
||||
return: null,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
console.log(e)
|
||||
return {
|
||||
logs: [e],
|
||||
errors: [],
|
||||
programMemory: {
|
||||
root: {},
|
||||
return: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function lintAst({
|
||||
ast,
|
||||
}: {
|
||||
ast: Program
|
||||
}): Promise<Array<Diagnostic>> {
|
||||
try {
|
||||
const discovered_findings = await kclLint(ast)
|
||||
return discovered_findings.map((lint) => {
|
||||
return {
|
||||
message: lint.finding.title,
|
||||
severity: 'info',
|
||||
from: lint.pos[0],
|
||||
to: lint.pos[1],
|
||||
}
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
4
src/wasm-lib/Cargo.lock
generated
4
src/wasm-lib/Cargo.lock
generated
@ -2606,9 +2606,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.119"
|
||||
version = "1.0.120"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
|
||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||
dependencies = [
|
||||
"indexmap 2.2.5",
|
||||
"itoa",
|
||||
|
@ -15,7 +15,7 @@ clap = "4.5.7"
|
||||
gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad.workspace = true
|
||||
serde_json = "1.0.119"
|
||||
serde_json = "1.0.120"
|
||||
tokio = { version = "1.38.0", features = ["sync"] }
|
||||
toml = "0.8.14"
|
||||
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] }
|
||||
|
@ -11,5 +11,5 @@ hyper = { version = "0.14.29", features = ["server"] }
|
||||
kcl-lib = { version = "0.1.62", path = "../kcl" }
|
||||
pico-args = "0.5.0"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.119"
|
||||
serde_json = "1.0.120"
|
||||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
|
||||
|
@ -33,7 +33,7 @@ reqwest = { version = "0.11.26", default-features = false, features = ["stream",
|
||||
ropey = "1.6.1"
|
||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.119"
|
||||
serde_json = "1.0.120"
|
||||
sha2 = "0.10.8"
|
||||
thiserror = "1.0.61"
|
||||
toml = "0.8.14"
|
||||
|
@ -69,6 +69,7 @@ impl StdLibFnArg {
|
||||
|| self.type_ == "ExtrudeGroup"
|
||||
|| self.type_ == "ExtrudeGroupSet"
|
||||
|| self.type_ == "SketchSurface"
|
||||
|| self.type_ == "SketchSurfaceOrGroup"
|
||||
{
|
||||
return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
|
||||
} else if self.type_ == "TagDeclarator" && self.required {
|
||||
@ -894,4 +895,11 @@ mod tests {
|
||||
}, ${2:%})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_autocomplete_snippet_circle() {
|
||||
let circle_fn: Box<dyn StdLibFn> = Box::new(crate::std::shapes::Circle);
|
||||
let snippet = circle_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(snippet, r#"circle([${0:3.14}, ${1:3.14}], ${2:3.14}, ${3:%})${}"#);
|
||||
}
|
||||
}
|
||||
|
64
yarn.lock
64
yarn.lock
@ -1697,12 +1697,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@playwright/test@^1.44.1":
|
||||
version "1.44.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.44.1.tgz#cc874ec31342479ad99838040e99b5f604299bcb"
|
||||
integrity sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==
|
||||
"@playwright/test@^1.45.1":
|
||||
version "1.45.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.1.tgz#819b90fa43d17000fce5ebd127043fd661938b7a"
|
||||
integrity sha512-Wo1bWTzQvGA7LyKGIZc8nFSTFf2TkthGIFBR+QVNilvwouGzFd4PYukZe3rvf5PSqjHi1+1NyKSDZKcQWETzaA==
|
||||
dependencies:
|
||||
playwright "1.44.1"
|
||||
playwright "1.45.1"
|
||||
|
||||
"@promptbook/utils@0.50.0-10":
|
||||
version "0.50.0-10"
|
||||
@ -5145,10 +5145,10 @@ html-encoding-sniffer@^3.0.0:
|
||||
dependencies:
|
||||
whatwg-encoding "^2.0.0"
|
||||
|
||||
html2canvas-pro@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/html2canvas-pro/-/html2canvas-pro-1.5.0.tgz#18925def43505bad352a394b95fffb45d6d46a8f"
|
||||
integrity sha512-izxSphcINRwfEVV6eamsPVdhsxSYqX8n/hxzK+niVWdB+onM+aYRoVO+xgS9iMmZoUleZTWg1tJwryikib2hXg==
|
||||
html2canvas-pro@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/html2canvas-pro/-/html2canvas-pro-1.5.1.tgz#e62aea27c598152308be35754b8b0f188908e171"
|
||||
integrity sha512-6LmbLb3qNg8f3jSSRdzDwG7c9VIFDC9jOP3iZ6mK1cjc9W6F2Mkh/n2WmBECxAzF2yHEznJFmgbQAut1g+NbEQ==
|
||||
dependencies:
|
||||
css-line-break "^2.1.0"
|
||||
text-segmentation "^1.0.3"
|
||||
@ -6632,17 +6632,17 @@ pkg-types@^1.0.3, pkg-types@^1.1.1:
|
||||
mlly "^1.7.0"
|
||||
pathe "^1.1.2"
|
||||
|
||||
playwright-core@1.44.1:
|
||||
version "1.44.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.44.1.tgz#53ec975503b763af6fc1a7aa995f34bc09ff447c"
|
||||
integrity sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==
|
||||
playwright-core@1.45.1:
|
||||
version "1.45.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.1.tgz#549a2701556b58245cc75263f9fc2795c1158dc1"
|
||||
integrity sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==
|
||||
|
||||
playwright@1.44.1:
|
||||
version "1.44.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.44.1.tgz#5634369d777111c1eea9180430b7a184028e7892"
|
||||
integrity sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==
|
||||
playwright@1.45.1:
|
||||
version "1.45.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.1.tgz#aaa6b0d6db14796b599d80c6679e63444e942534"
|
||||
integrity sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==
|
||||
dependencies:
|
||||
playwright-core "1.44.1"
|
||||
playwright-core "1.45.1"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
@ -7848,10 +7848,10 @@ thenify-all@^1.0.0:
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
three@^0.164.1:
|
||||
version "0.164.1"
|
||||
resolved "https://registry.yarnpkg.com/three/-/three-0.164.1.tgz#b742f76bd8dfd3736ba0d86a12dfddb73c5cdcc0"
|
||||
integrity sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==
|
||||
three@^0.166.1:
|
||||
version "0.166.1"
|
||||
resolved "https://registry.yarnpkg.com/three/-/three-0.166.1.tgz#322cfc48fff4e751cd47d61fd1558c387d098d7c"
|
||||
integrity sha512-LtuafkKHHzm61AQA1be2MAYIw1IjmhOUxhBa0prrLpEMWbV7ijvxCRHjSgHPGp2493wLBzwKV46tA9nivLEgKg==
|
||||
|
||||
through@^2.3.8:
|
||||
version "2.3.8"
|
||||
@ -8159,11 +8159,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"
|
||||
@ -8343,10 +8338,10 @@ warning@^4.0.3:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
wasm-pack@^0.12.1:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/wasm-pack/-/wasm-pack-0.12.1.tgz#974c1fbbf5b65c9e135e0d1fba3a97de1a21a489"
|
||||
integrity sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==
|
||||
wasm-pack@^0.13.0:
|
||||
version "0.13.0"
|
||||
resolved "https://registry.yarnpkg.com/wasm-pack/-/wasm-pack-0.13.0.tgz#c2637e0129e1854735f3daead45d92165d54709d"
|
||||
integrity sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==
|
||||
dependencies:
|
||||
binary-install "^1.0.1"
|
||||
|
||||
@ -8718,10 +8713,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