circular dependencies refactor (#1863)

* circular dependencies refactor

* clean up
This commit is contained in:
Kurt Hutten
2024-03-22 16:55:30 +11:00
committed by GitHub
parent ccd0c619a6
commit 465d933d53
49 changed files with 283 additions and 258 deletions

View File

@ -28,7 +28,7 @@ import { CodeMenu } from 'components/CodeMenu'
import { TextEditor } from 'components/TextEditor' import { TextEditor } from 'components/TextEditor'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions' import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { engineCommandManager } from './lang/std/engineConnection' import { engineCommandManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'

View File

@ -28,7 +28,7 @@ import {
import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider' import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider'
import SettingsAuthProvider from 'components/SettingsAuthProvider' import SettingsAuthProvider from 'components/SettingsAuthProvider'
import LspProvider from 'components/LspProvider' import LspProvider from 'components/LspProvider'
import { KclContextProvider } from 'lang/KclSingleton' import { KclContextProvider } from 'lang/KclProvider'
export const BROWSER_FILE_NAME = 'new' export const BROWSER_FILE_NAME = 'new'

View File

@ -1,12 +1,12 @@
import { WheelEvent, useRef, useMemo } from 'react' import { WheelEvent, useRef, useMemo } from 'react'
import { isCursorInSketchCommandRange } from 'lang/util' import { isCursorInSketchCommandRange } from 'lang/util'
import { engineCommandManager } from './lang/std/engineConnection' import { engineCommandManager, kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import usePlatform from 'hooks/usePlatform' import usePlatform from 'hooks/usePlatform'
import { isSingleCursorInPipe } from 'lang/queryAst' import { isSingleCursorInPipe } from 'lang/queryAst'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { import {
NetworkHealthState, NetworkHealthState,
useNetworkStatus, useNetworkStatus,

View File

@ -19,7 +19,7 @@ import {
import { import {
EngineCommand, EngineCommand,
Subscription, Subscription,
engineCommandManager, EngineCommandManager,
} from 'lang/std/engineConnection' } from 'lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { deg2Rad } from 'lib/utils2d' import { deg2Rad } from 'lib/utils2d'
@ -34,10 +34,6 @@ const tempQuaternion = new Quaternion() // just used for maths
type interactionType = 'pan' | 'rotate' | 'zoom' type interactionType = 'pan' | 'rotate' | 'zoom'
const throttledEngCmd = throttle((cmd: EngineCommand) => {
engineCommandManager.sendSceneCommand(cmd)
}, 1000 / 30)
interface ThreeCamValues { interface ThreeCamValues {
position: Vector3 position: Vector3
quaternion: Quaternion quaternion: Quaternion
@ -62,68 +58,8 @@ export type ReactCameraProperties =
const lastCmdDelay = 50 const lastCmdDelay = 50
const throttledUpdateEngineCamera = throttle((threeValues: ThreeCamValues) => {
const cmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
...convertThreeCamValuesToEngineCam(threeValues),
},
}
engineCommandManager.sendSceneCommand(cmd)
}, 1000 / 15)
let lastPerspectiveCmd: EngineCommand | null = null
let lastPerspectiveCmdTime: number = Date.now()
let lastPerspectiveCmdTimeoutId: number | null = null
const sendLastPerspectiveReliableChannel = () => {
if (
lastPerspectiveCmd &&
Date.now() - lastPerspectiveCmdTime >= lastCmdDelay
) {
engineCommandManager.sendSceneCommand(lastPerspectiveCmd, true)
lastPerspectiveCmdTime = Date.now()
}
}
const throttledUpdateEngineFov = throttle(
(vals: {
position: Vector3
quaternion: Quaternion
zoom: number
fov: number
target: Vector3
}) => {
const cmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_perspective_settings',
...convertThreeCamValuesToEngineCam({
...vals,
isPerspective: true,
}),
fov_y: vals.fov,
...calculateNearFarFromFOV(vals.fov),
},
}
engineCommandManager.sendSceneCommand(cmd)
lastPerspectiveCmd = cmd
lastPerspectiveCmdTime = Date.now()
if (lastPerspectiveCmdTimeoutId !== null) {
clearTimeout(lastPerspectiveCmdTimeoutId)
}
lastPerspectiveCmdTimeoutId = setTimeout(
sendLastPerspectiveReliableChannel,
lastCmdDelay
) as any as number
},
1000 / 30
)
export class CameraControls { export class CameraControls {
engineCommandManager: EngineCommandManager
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient' syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
camera: PerspectiveCamera | OrthographicCamera camera: PerspectiveCamera | OrthographicCamera
target: Vector3 target: Vector3
@ -213,7 +149,77 @@ export class CameraControls {
this.update(true) this.update(true)
} }
constructor(isOrtho = false, domElement: HTMLCanvasElement) { throttledEngCmd = throttle((cmd: EngineCommand) => {
this.engineCommandManager.sendSceneCommand(cmd)
}, 1000 / 30)
throttledUpdateEngineCamera = throttle((threeValues: ThreeCamValues) => {
const cmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
...convertThreeCamValuesToEngineCam(threeValues),
},
}
this.engineCommandManager.sendSceneCommand(cmd)
}, 1000 / 15)
lastPerspectiveCmd: EngineCommand | null = null
lastPerspectiveCmdTime: number = Date.now()
lastPerspectiveCmdTimeoutId: number | null = null
sendLastPerspectiveReliableChannel = () => {
if (
this.lastPerspectiveCmd &&
Date.now() - this.lastPerspectiveCmdTime >= lastCmdDelay
) {
this.engineCommandManager.sendSceneCommand(this.lastPerspectiveCmd, true)
this.lastPerspectiveCmdTime = Date.now()
}
}
throttledUpdateEngineFov = throttle(
(vals: {
position: Vector3
quaternion: Quaternion
zoom: number
fov: number
target: Vector3
}) => {
const cmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_perspective_settings',
...convertThreeCamValuesToEngineCam({
...vals,
isPerspective: true,
}),
fov_y: vals.fov,
...calculateNearFarFromFOV(vals.fov),
},
}
this.engineCommandManager.sendSceneCommand(cmd)
this.lastPerspectiveCmd = cmd
this.lastPerspectiveCmdTime = Date.now()
if (this.lastPerspectiveCmdTimeoutId !== null) {
clearTimeout(this.lastPerspectiveCmdTimeoutId)
}
this.lastPerspectiveCmdTimeoutId = setTimeout(
this.sendLastPerspectiveReliableChannel,
lastCmdDelay
) as any as number
},
1000 / 30
)
constructor(
isOrtho = false,
domElement: HTMLCanvasElement,
engineCommandManager: EngineCommandManager
) {
this.engineCommandManager = engineCommandManager
this.camera = isOrtho ? new OrthographicCamera() : new PerspectiveCamera() this.camera = isOrtho ? new OrthographicCamera() : new PerspectiveCamera()
this.camera.up.set(0, 0, 1) this.camera.up.set(0, 0, 1)
this.camera.far = 20000 this.camera.far = 20000
@ -310,7 +316,7 @@ export class CameraControls {
this.handleStart() this.handleStart()
if (this.syncDirection === 'engineToClient') { if (this.syncDirection === 'engineToClient') {
void engineCommandManager.sendSceneCommand({ void this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'camera_drag_start', type: 'camera_drag_start',
@ -334,7 +340,7 @@ export class CameraControls {
if (interaction === 'none') return if (interaction === 'none') return
if (this.syncDirection === 'engineToClient') { if (this.syncDirection === 'engineToClient') {
throttledEngCmd({ this.throttledEngCmd({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'camera_drag_move', type: 'camera_drag_move',
@ -377,7 +383,7 @@ export class CameraControls {
if (this.syncDirection === 'engineToClient') { if (this.syncDirection === 'engineToClient') {
const interaction = this.getInteractionType(event) const interaction = this.getInteractionType(event)
if (interaction === 'none') return if (interaction === 'none') return
void engineCommandManager.sendSceneCommand({ void this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'camera_drag_end', type: 'camera_drag_end',
@ -401,7 +407,7 @@ export class CameraControls {
this.handleEnd() this.handleEnd()
return return
} }
throttledEngCmd({ this.throttledEngCmd({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'default_camera_zoom', type: 'default_camera_zoom',
@ -449,7 +455,7 @@ export class CameraControls {
this.camera.quaternion.set(qx, qy, qz, qw) this.camera.quaternion.set(qx, qy, qz, qw)
this.camera.updateProjectionMatrix() this.camera.updateProjectionMatrix()
engineCommandManager.sendSceneCommand({ this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -493,7 +499,7 @@ export class CameraControls {
} }
usePerspectiveCamera = () => { usePerspectiveCamera = () => {
this._usePerspectiveCamera() this._usePerspectiveCamera()
engineCommandManager.sendSceneCommand({ this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -561,7 +567,7 @@ export class CameraControls {
this.camera.near = z_near this.camera.near = z_near
this.camera.far = z_far this.camera.far = z_far
throttledUpdateEngineFov({ this.throttledUpdateEngineFov({
fov: newFov, fov: newFov,
position: newPosition, position: newPosition,
quaternion: this.camera.quaternion, quaternion: this.camera.quaternion,
@ -924,7 +930,7 @@ export class CameraControls {
} }
if (this.syncDirection === 'clientToEngine' || forceUpdate) if (this.syncDirection === 'clientToEngine' || forceUpdate)
throttledUpdateEngineCamera({ this.throttledUpdateEngineCamera({
quaternion: this.camera.quaternion, quaternion: this.camera.quaternion,
position: this.camera.position, position: this.camera.position,
zoom: this.camera.zoom, zoom: this.camera.zoom,

View File

@ -4,9 +4,10 @@ import { useModelingContext } from 'hooks/useModelingContext'
import { cameraMouseDragGuards } from 'lib/cameraControls' import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useStore } from 'useStore' import { useStore } from 'useStore'
import { DEBUG_SHOW_BOTH_SCENES, sceneInfra } from './sceneInfra' import { DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
import { ReactCameraProperties } from './CameraControls' import { ReactCameraProperties } from './CameraControls'
import { throttle } from 'lib/utils' import { throttle } from 'lib/utils'
import { sceneInfra } from 'lib/singletons'
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } { function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
const [isCamMoving, setIsCamMoving] = useState(false) const [isCamMoving, setIsCamMoving] = useState(false)

View File

@ -28,7 +28,6 @@ import {
INTERSECTION_PLANE_LAYER, INTERSECTION_PLANE_LAYER,
OnMouseEnterLeaveArgs, OnMouseEnterLeaveArgs,
RAYCASTABLE_PLANE, RAYCASTABLE_PLANE,
sceneInfra,
SKETCH_GROUP_SEGMENTS, SKETCH_GROUP_SEGMENTS,
SKETCH_LAYER, SKETCH_LAYER,
X_AXIS, X_AXIS,
@ -52,10 +51,9 @@ import {
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
} from 'lang/wasm' } from 'lang/wasm'
import { kclManager } from 'lang/KclSingleton' import { engineCommandManager, kclManager, sceneInfra } from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst, useStore } from 'useStore' import { executeAst, useStore } from 'useStore'
import { engineCommandManager } from 'lang/std/engineConnection'
import { import {
createArcGeometry, createArcGeometry,
dashedStraight, dashedStraight,
@ -85,6 +83,7 @@ import { createGridHelper, orthoScale, perspScale } from './helpers'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { SketchDetails } from 'machines/modelingMachine' import { SketchDetails } from 'machines/modelingMachine'
import { EngineCommandManager } from 'lang/std/engineConnection'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -100,14 +99,16 @@ export const PROFILE_START = 'profile-start'
// This singleton Class is responsible for all of the things the user sees and interacts with. // This singleton Class is responsible for all of the things the user sees and interacts with.
// That mostly mean sketch elements. // That mostly mean sketch elements.
// Cameras, controls, raycasters, etc are handled by sceneInfra // Cameras, controls, raycasters, etc are handled by sceneInfra
class SceneEntities { export class SceneEntities {
engineCommandManager: EngineCommandManager
scene: Scene scene: Scene
sceneProgramMemory: ProgramMemory = { root: {}, return: null } sceneProgramMemory: ProgramMemory = { root: {}, return: null }
activeSegments: { [key: string]: Group } = {} activeSegments: { [key: string]: Group } = {}
intersectionPlane: Mesh | null = null intersectionPlane: Mesh | null = null
axisGroup: Group | null = null axisGroup: Group | null = null
currentSketchQuaternion: Quaternion | null = null currentSketchQuaternion: Quaternion | null = null
constructor() { constructor(engineCommandManager: EngineCommandManager) {
this.engineCommandManager = engineCommandManager
this.scene = sceneInfra?.scene this.scene = sceneInfra?.scene
sceneInfra?.camControls.subscribeToCamChange(this.onCamChange) sceneInfra?.camControls.subscribeToCamChange(this.onCamChange)
} }
@ -289,7 +290,7 @@ class SceneEntities {
const { programMemory } = await executeAst({ const { programMemory } = await executeAst({
ast: truncatedAst, ast: truncatedAst,
useFakeExecutor: true, useFakeExecutor: true,
engineCommandManager, engineCommandManager: this.engineCommandManager,
programMemoryOverride, programMemoryOverride,
}) })
const sketchGroup = sketchGroupFromPathToNode({ const sketchGroup = sketchGroupFromPathToNode({
@ -637,7 +638,7 @@ class SceneEntities {
const { programMemory } = await executeAst({ const { programMemory } = await executeAst({
ast: truncatedAst, ast: truncatedAst,
useFakeExecutor: true, useFakeExecutor: true,
engineCommandManager: engineCommandManager, engineCommandManager: this.engineCommandManager,
programMemoryOverride, programMemoryOverride,
}) })
this.sceneProgramMemory = programMemory this.sceneProgramMemory = programMemory
@ -904,11 +905,11 @@ class SceneEntities {
streamDimensions streamDimensions
) )
if (!entity_id) return false if (!entity_id) return false
const artifact = engineCommandManager.artifactMap[entity_id] const artifact = this.engineCommandManager.artifactMap[entity_id]
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info') if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
return false return false
const faceInfo: Models['FaceIsPlanar_type'] = ( const faceInfo: Models['FaceIsPlanar_type'] = (
await engineCommandManager.sendSceneCommand({ await this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -979,8 +980,6 @@ class SceneEntities {
export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ' export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
export const sceneEntitiesManager = new SceneEntities()
// calculations/pure-functions/easy to test so no excuse not to // calculations/pure-functions/easy to test so no excuse not to
function prepareTruncatedMemoryAndAst( function prepareTruncatedMemoryAndAst(

View File

@ -27,6 +27,7 @@ import { Axis } from 'lib/selections'
import { type BaseUnit } from 'lib/settings/settingsTypes' import { type BaseUnit } from 'lib/settings/settingsTypes'
import { SETTINGS_PERSIST_KEY } from 'lib/constants' import { SETTINGS_PERSIST_KEY } from 'lib/constants'
import { CameraControls } from './CameraControls' import { CameraControls } from './CameraControls'
import { EngineCommandManager } from 'lang/std/engineConnection'
type SendType = ReturnType<typeof useModelingContext>['send'] type SendType = ReturnType<typeof useModelingContext>['send']
@ -86,7 +87,7 @@ interface OnMoveCallbackArgs {
// This singleton class is responsible for all of the under the hood setup for the client side scene. // This singleton class is responsible for all of the under the hood setup for the client side scene.
// That is the cameras and switching between them, raycasters for click mouse events and their abstractions (onClick etc), setting up controls. // That is the cameras and switching between them, raycasters for click mouse events and their abstractions (onClick etc), setting up controls.
// Anything that added the the scene for the user to interact with is probably in SceneEntities.ts // Anything that added the the scene for the user to interact with is probably in SceneEntities.ts
class SceneInfra { export class SceneInfra {
static instance: SceneInfra static instance: SceneInfra
scene: Scene scene: Scene
renderer: WebGLRenderer renderer: WebGLRenderer
@ -126,7 +127,7 @@ class SceneInfra {
) )
} }
resetMouseListeners = () => { resetMouseListeners = () => {
sceneInfra.setCallbacks({ this.setCallbacks({
onDrag: () => {}, onDrag: () => {},
onMove: () => {}, onMove: () => {},
onClick: () => {}, onClick: () => {},
@ -155,7 +156,7 @@ class SceneInfra {
} | null = null } | null = null
mouseDownVector: null | Vector2 = null mouseDownVector: null | Vector2 = null
constructor() { constructor(engineCommandManager: EngineCommandManager) {
// SCENE // SCENE
this.scene = new Scene() this.scene = new Scene()
this.scene.background = new Color(0x000000) this.scene.background = new Color(0x000000)
@ -178,7 +179,11 @@ class SceneInfra {
const x = Math.cos(ang) * length const x = Math.cos(ang) * length
const y = Math.sin(ang) * length const y = Math.sin(ang) * length
this.camControls = new CameraControls(false, this.renderer.domElement) this.camControls = new CameraControls(
false,
this.renderer.domElement,
engineCommandManager
)
this.camControls.subscribeToCamChange(() => this.onCameraChange()) this.camControls.subscribeToCamChange(() => this.onCameraChange())
this.camControls.camera.layers.enable(SKETCH_LAYER) this.camControls.camera.layers.enable(SKETCH_LAYER)
this.camControls.camera.position.set(0, -x, y) this.camControls.camera.position.set(0, -x, y)
@ -221,9 +226,9 @@ class SceneInfra {
?.getObjectByName('gridHelper') ?.getObjectByName('gridHelper')
planesGroup && planesGroup &&
planesGroup.scale.set( planesGroup.scale.set(
scale / sceneInfra._baseUnitMultiplier, scale / this._baseUnitMultiplier,
scale / sceneInfra._baseUnitMultiplier, scale / this._baseUnitMultiplier,
scale / sceneInfra._baseUnitMultiplier scale / this._baseUnitMultiplier
) )
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale) axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
} }
@ -255,7 +260,7 @@ class SceneInfra {
} | null => { } | null => {
this.planeRaycaster.setFromCamera( this.planeRaycaster.setFromCamera(
this.currentMouseVector, this.currentMouseVector,
sceneInfra.camControls.camera this.camControls.camera
) )
const planeIntersects = this.planeRaycaster.intersectObjects( const planeIntersects = this.planeRaycaster.intersectObjects(
this.scene.children, this.scene.children,
@ -527,9 +532,9 @@ class SceneInfra {
this.camControls.target this.camControls.target
) )
planesGroup.scale.set( planesGroup.scale.set(
sceneScale / sceneInfra._baseUnitMultiplier, sceneScale / this._baseUnitMultiplier,
sceneScale / sceneInfra._baseUnitMultiplier, sceneScale / this._baseUnitMultiplier,
sceneScale / sceneInfra._baseUnitMultiplier sceneScale / this._baseUnitMultiplier
) )
this.scene.add(planesGroup) this.scene.add(planesGroup)
} }
@ -540,7 +545,7 @@ class SceneInfra {
if (planesGroup) this.scene.remove(planesGroup) if (planesGroup) this.scene.remove(planesGroup)
} }
updateOtherSelectionColors = (otherSelections: Axis[]) => { updateOtherSelectionColors = (otherSelections: Axis[]) => {
const axisGroup = sceneInfra.scene.children.find( const axisGroup = this.scene.children.find(
({ userData }) => userData?.type === AXIS_GROUP ({ userData }) => userData?.type === AXIS_GROUP
) )
const axisMap: { [key: string]: Axis } = { const axisMap: { [key: string]: Axis } = {
@ -562,8 +567,6 @@ class SceneInfra {
} }
} }
export const sceneInfra = new SceneInfra()
export function getSceneScale( export function getSceneScale(
camera: PerspectiveCamera | OrthographicCamera, camera: PerspectiveCamera | OrthographicCamera,
target: Vector3 target: Vector3

View File

@ -1,5 +1,5 @@
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useStore } from 'useStore' import { useStore } from 'useStore'

View File

@ -7,8 +7,8 @@ import {
findUniqueName, findUniqueName,
} from '../lang/modifyAst' } from '../lang/modifyAst'
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst' import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager, kclManager } from 'lib/singletons'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { executeAst } from 'useStore' import { executeAst } from 'useStore'

View File

@ -1,6 +1,5 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { sceneInfra } from '../clientSideScene/sceneInfra' import { engineCommandManager, sceneInfra } from 'lib/singletons'
import { engineCommandManager } from 'lang/std/engineConnection'
import { throttle, isReducedMotion } from 'lib/utils' import { throttle, isReducedMotion } from 'lib/utils'
const updateDollyZoom = throttle( const updateDollyZoom = throttle(

View File

@ -6,7 +6,7 @@ import styles from './CodeMenu.module.css'
import { useConvertToVariable } from 'hooks/useToolbarGuards' import { useConvertToVariable } from 'hooks/useToolbarGuards'
import { editorShortcutMeta } from './TextEditor' import { editorShortcutMeta } from './TextEditor'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export const CodeMenu = ({ children }: PropsWithChildren) => { export const CodeMenu = ({ children }: PropsWithChildren) => {
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } = const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =

View File

@ -1,6 +1,6 @@
import { useSelector } from '@xstate/react' import { useSelector } from '@xstate/react'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { CommandArgument } from 'lib/commandTypes' import { CommandArgument } from 'lib/commandTypes'
import { import {
ResolvedSelectionType, ResolvedSelectionType,

View File

@ -1,4 +1,5 @@
import { CommandLog, engineCommandManager } from 'lang/std/engineConnection' import { CommandLog } from 'lang/std/engineConnection'
import { engineCommandManager } from 'lib/singletons'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
function useEngineCommands(): [CommandLog[], () => void] { function useEngineCommands(): [CommandLog[], () => void] {

View File

@ -13,7 +13,7 @@ import { useHotkeys } from 'react-hotkeys-hook'
import styles from './FileTree.module.css' import styles from './FileTree.module.css'
import { FILE_EXT, sortProject } from 'lib/tauriFS' import { FILE_EXT, sortProject } from 'lib/tauriFS'
import { CustomIcon } from './CustomIcon' import { CustomIcon } from './CustomIcon'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus' import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus'
import { useLspContext } from './LspProvider' import { useLspContext } from './LspProvider'

View File

@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
import { useEffect } from 'react' import { useEffect } from 'react'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel' import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { Themes } from '../lib/theme' import { Themes } from '../lib/theme'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
const ReactJsonTypeHack = ReactJson as any const ReactJsonTypeHack = ReactJson as any

View File

@ -3,7 +3,7 @@ import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useMemo } from 'react' import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm' import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
import { Themes } from '../lib/theme' import { Themes } from '../lib/theme'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
interface MemoryPanelProps extends CollapsiblePanelProps { interface MemoryPanelProps extends CollapsiblePanelProps {
theme?: Exclude<Themes, Themes.System> theme?: Exclude<Themes, Themes.System>

View File

@ -12,8 +12,8 @@ import { SetSelections, modelingMachine } from 'machines/modelingMachine'
import { useSetupEngineManager } from 'hooks/useSetupEngineManager' import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { isCursorInSketchCommandRange } from 'lang/util' import { isCursorInSketchCommandRange } from 'lang/util'
import { engineCommandManager } from 'lang/std/engineConnection' import { kclManager, sceneInfra, engineCommandManager } from 'lib/singletons'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance' import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
import { import {
angleBetweenInfo, angleBetweenInfo,
@ -33,10 +33,9 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance' import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
import useStateMachineCommands from 'hooks/useStateMachineCommands' import useStateMachineCommands from 'hooks/useStateMachineCommands'
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig' import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
import { sceneInfra } from 'clientSideScene/sceneInfra'
import { import {
getSketchQuaternion,
getSketchOrientationDetails, getSketchOrientationDetails,
getSketchQuaternion,
} from 'clientSideScene/sceneEntities' } from 'clientSideScene/sceneEntities'
import { sketchOnExtrudedFace, startSketchOnDefault } from 'lang/modifyAst' import { sketchOnExtrudedFace, startSketchOnDefault } from 'lang/modifyAst'
import { Program, parse } from 'lang/wasm' import { Program, parse } from 'lang/wasm'

View File

@ -5,12 +5,12 @@ import {
ConnectingType, ConnectingType,
ConnectingTypeGroup, ConnectingTypeGroup,
DisconnectingType, DisconnectingType,
engineCommandManager,
EngineConnectionState, EngineConnectionState,
EngineConnectionStateType, EngineConnectionStateType,
ErrorType, ErrorType,
initialConnectingTypeGroupState, initialConnectingTypeGroupState,
} from '../lang/std/engineConnection' } from '../lang/std/engineConnection'
import { engineCommandManager } from '../lib/singletons'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
export enum NetworkHealthState { export enum NetworkHealthState {

View File

@ -22,8 +22,7 @@ import {
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig' import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig' import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
import { sceneInfra } from 'clientSideScene/sceneInfra' import { kclManager, sceneInfra } from 'lib/singletons'
import { kclManager } from 'lang/KclSingleton'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -147,7 +146,6 @@ export const SettingsAuthProviderBase = ({
actions: { actions: {
goToSignInPage: () => { goToSignInPage: () => {
navigate(paths.SIGN_IN) navigate(paths.SIGN_IN)
logout() logout()
}, },
goToIndexPage: () => { goToIndexPage: () => {

View File

@ -4,7 +4,7 @@ import { getNormalisedCoordinates } from '../lib/utils'
import Loading from './Loading' import Loading from './Loading'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp' import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator' import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
import { butName } from 'lib/cameraControls' import { butName } from 'lib/cameraControls'

View File

@ -20,10 +20,9 @@ import { kclErrToDiagnostic } from 'lang/errors'
import { CSSRuleObject } from 'tailwindcss/types/config' import { CSSRuleObject } from 'tailwindcss/types/config'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import interact from '@replit/codemirror-interact' import interact from '@replit/codemirror-interact'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager, sceneInfra, kclManager } from 'lib/singletons'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { ModelingMachineEvent } from 'machines/modelingMachine' import { ModelingMachineEvent } from 'machines/modelingMachine'
import { sceneInfra } from 'clientSideScene/sceneInfra'
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator' import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { useLspContext } from './LspProvider' import { useLspContext } from './LspProvider'

View File

@ -11,7 +11,7 @@ import {
getTransformInfos, getTransformInfos,
PathToNodeMap, PathToNodeMap,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export function equalAngleInfo({ export function equalAngleInfo({
selectionRanges, selectionRanges,

View File

@ -11,7 +11,7 @@ import {
getTransformInfos, getTransformInfos,
PathToNodeMap, PathToNodeMap,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export function setEqualLengthInfo({ export function setEqualLengthInfo({
selectionRanges, selectionRanges,

View File

@ -10,7 +10,7 @@ import {
getTransformInfos, getTransformInfos,
transformAstSketchLines, transformAstSketchLines,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export function horzVertInfo( export function horzVertInfo(
selectionRanges: Selections, selectionRanges: Selections,

View File

@ -15,7 +15,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst' import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -10,7 +10,7 @@ import {
getRemoveConstraintsTransforms, getRemoveConstraintsTransforms,
transformAstSketchLines, transformAstSketchLines,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export function removeConstrainingValuesInfo({ export function removeConstrainingValuesInfo({
selectionRanges, selectionRanges,

View File

@ -19,7 +19,7 @@ import {
createVariableDeclaration, createVariableDeclaration,
} from '../../lang/modifyAst' } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)

View File

@ -14,7 +14,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst' import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -13,7 +13,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst' import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -21,7 +21,7 @@ import {
} from '../../lang/modifyAst' } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { normaliseAngle } from '../../lib/utils' import { normaliseAngle } from '../../lib/utils'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)

View File

@ -1,7 +1,7 @@
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { useState } from 'react' import { useState } from 'react'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
export function WasmErrBanner() { export function WasmErrBanner() {
const [isBannerDismissed, setBannerDismissed] = useState(false) const [isBannerDismissed, setBannerDismissed] = useState(false)

View File

@ -1,6 +1,6 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useStore } from 'useStore' import { useStore } from 'useStore'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager } from 'lib/singletons'
import { useModelingContext } from './useModelingContext' import { useModelingContext } from './useModelingContext'
import { getEventForSelectWithPoint } from 'lib/selections' import { getEventForSelectWithPoint } from 'lib/selections'

View File

@ -1,9 +1,8 @@
import { useLayoutEffect, useEffect, useRef } from 'react' import { useLayoutEffect, useEffect, useRef } from 'react'
import { parse } from '../lang/wasm' import { parse } from '../lang/wasm'
import { useStore } from '../useStore' import { useStore } from '../useStore'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager, kclManager } from 'lib/singletons'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { kclManager } from 'lang/KclSingleton'
export function useSetupEngineManager( export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>, streamRef: React.RefObject<HTMLDivElement>,

View File

@ -11,7 +11,7 @@ import {
NetworkHealthState, NetworkHealthState,
useNetworkStatus, useNetworkStatus,
} from 'components/NetworkHealthIndicator' } from 'components/NetworkHealthIndicator'
import { useKclContext } from 'lang/KclSingleton' import { useKclContext } from 'lang/KclProvider'
import { useStore } from 'useStore' import { useStore } from 'useStore'
// This might not be necessary, AnyStateMachine from xstate is working // This might not be necessary, AnyStateMachine from xstate is working

View File

@ -2,7 +2,7 @@ import {
SetVarNameModal, SetVarNameModal,
createSetVarNameModal, createSetVarNameModal,
} from 'components/SetVarNameModal' } from 'components/SetVarNameModal'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { moveValueIntoNewVariable } from 'lang/modifyAst' import { moveValueIntoNewVariable } from 'lang/modifyAst'
import { isNodeSafeToReplace } from 'lang/queryAst' import { isNodeSafeToReplace } from 'lang/queryAst'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'

69
src/lang/KclProvider.tsx Normal file
View File

@ -0,0 +1,69 @@
import { KCLError } from './errors'
import { createContext, useContext, useEffect, useState } from 'react'
import { type IndexLoaderData } from 'lib/types'
import { useLoaderData } from 'react-router-dom'
import { useParams } from 'react-router-dom'
import { kclManager } from 'lib/singletons'
const KclContext = createContext({
code: kclManager?.code || '',
programMemory: kclManager?.programMemory,
ast: kclManager?.ast,
isExecuting: kclManager?.isExecuting,
errors: kclManager?.kclErrors,
logs: kclManager?.logs,
wasmInitFailed: kclManager?.wasmInitFailed,
})
export function useKclContext() {
return useContext(KclContext)
}
export function KclContextProvider({
children,
}: {
children: React.ReactNode
}) {
// If we try to use this component anywhere but under the paths.FILE route it will fail
// Because useLoaderData assumes we are on within it's context.
const { code: loadedCode } = useLoaderData() as IndexLoaderData
const [code, setCode] = useState(loadedCode || kclManager.code)
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
const [ast, setAst] = useState(kclManager.ast)
const [isExecuting, setIsExecuting] = useState(false)
const [errors, setErrors] = useState<KCLError[]>([])
const [logs, setLogs] = useState<string[]>([])
const [wasmInitFailed, setWasmInitFailed] = useState(false)
useEffect(() => {
kclManager.registerCallBacks({
setCode,
setProgramMemory,
setAst,
setLogs,
setKclErrors: setErrors,
setIsExecuting,
setWasmInitFailed,
})
}, [])
const params = useParams()
useEffect(() => {
kclManager.setParams(params)
}, [params])
return (
<KclContext.Provider
value={{
code,
programMemory,
ast,
isExecuting,
errors,
logs,
wasmInitFailed,
}}
>
{children}
</KclContext.Provider>
)
}

View File

@ -2,10 +2,8 @@ import { executeAst, executeCode } from 'useStore'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { KCLError } from './errors' import { KCLError } from './errors'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { import { EngineCommandManager } from './std/engineConnection'
EngineCommandManager,
engineCommandManager,
} from './std/engineConnection'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { import {
CallExpression, CallExpression,
@ -19,18 +17,15 @@ import {
ExtrudeGroup, ExtrudeGroup,
} from 'lang/wasm' } from 'lang/wasm'
import { bracket } from 'lib/exampleKcl' import { bracket } from 'lib/exampleKcl'
import { createContext, useContext, useEffect, useState } from 'react'
import { getNodeFromPath } from './queryAst' import { getNodeFromPath } from './queryAst'
import { type IndexLoaderData } from 'lib/types' import { Params } from 'react-router-dom'
import { Params, useLoaderData } from 'react-router-dom'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { writeTextFile } from '@tauri-apps/api/fs' import { writeTextFile } from '@tauri-apps/api/fs'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { useParams } from 'react-router-dom'
const PERSIST_CODE_TOKEN = 'persistCode' const PERSIST_CODE_TOKEN = 'persistCode'
class KclManager { export class KclManager {
private _code = bracket private _code = bracket
private _ast: Program = { private _ast: Program = {
body: [], body: [],
@ -214,7 +209,7 @@ class KclManager {
console.error('error parsing code', e) console.error('error parsing code', e)
if (e instanceof KCLError) { if (e instanceof KCLError) {
this.kclErrors = [e] this.kclErrors = [e]
if (e.msg === 'file is empty') engineCommandManager?.endSession() if (e.msg === 'file is empty') this.engineCommandManager?.endSession()
} }
return null return null
} }
@ -247,7 +242,7 @@ class KclManager {
ast, ast,
engineCommandManager: this.engineCommandManager, engineCommandManager: this.engineCommandManager,
}) })
enterEditMode(programMemory) enterEditMode(programMemory, this.engineCommandManager)
this.isExecuting = false this.isExecuting = false
// Check the cancellation token for this execution before applying side effects // Check the cancellation token for this execution before applying side effects
if (this._cancelTokens.get(currentExecutionId)) { if (this._cancelTokens.get(currentExecutionId)) {
@ -262,7 +257,7 @@ class KclManager {
this.code = recast(ast) this.code = recast(ast)
} }
this._executeCallback() this._executeCallback()
engineCommandManager.addCommandLog({ this.engineCommandManager.addCommandLog({
type: 'execution-done', type: 'execution-done',
data: null, data: null,
}) })
@ -295,11 +290,11 @@ class KclManager {
this._kclErrors = errors this._kclErrors = errors
this._programMemory = programMemory this._programMemory = programMemory
if (updates !== 'codeAndArtifactRanges') return if (updates !== 'codeAndArtifactRanges') return
Object.entries(engineCommandManager.artifactMap).forEach( Object.entries(this.engineCommandManager.artifactMap).forEach(
([commandId, artifact]) => { ([commandId, artifact]) => {
if (!artifact.pathToNode) return if (!artifact.pathToNode) return
const node = getNodeFromPath<CallExpression>( const node = getNodeFromPath<CallExpression>(
kclManager.ast, this.ast,
artifact.pathToNode, artifact.pathToNode,
'CallExpression' 'CallExpression'
).node ).node
@ -307,14 +302,14 @@ class KclManager {
const [oldStart, oldEnd] = artifact.range const [oldStart, oldEnd] = artifact.range
if (oldStart === 0 && oldEnd === 0) return if (oldStart === 0 && oldEnd === 0) return
if (oldStart === node.start && oldEnd === node.end) return if (oldStart === node.start && oldEnd === node.end) return
engineCommandManager.artifactMap[commandId].range = [ this.engineCommandManager.artifactMap[commandId].range = [
node.start, node.start,
node.end, node.end,
] ]
} }
) )
} }
async executeCode(code?: string, executionId?: number) { executeCode = async (code?: string, executionId?: number) => {
const currentExecutionId = executionId || Date.now() const currentExecutionId = executionId || Date.now()
this._cancelTokens.set(currentExecutionId, false) this._cancelTokens.set(currentExecutionId, false)
if (this._cancelTokens.get(currentExecutionId)) { if (this._cancelTokens.get(currentExecutionId)) {
@ -324,7 +319,7 @@ class KclManager {
await this.ensureWasmInit() await this.ensureWasmInit()
await this?.engineCommandManager?.waitForReady await this?.engineCommandManager?.waitForReady
const result = await executeCode({ const result = await executeCode({
engineCommandManager, engineCommandManager: this.engineCommandManager,
code: code || this._code, code: code || this._code,
lastAst: this._ast, lastAst: this._ast,
force: false, force: false,
@ -336,7 +331,7 @@ class KclManager {
} }
if (!result.isChange) return if (!result.isChange) return
const { logs, errors, programMemory, ast } = result const { logs, errors, programMemory, ast } = result
enterEditMode(programMemory) enterEditMode(programMemory, this.engineCommandManager)
this.logs = logs this.logs = logs
this.kclErrors = errors this.kclErrors = errors
this.programMemory = programMemory this.programMemory = programMemory
@ -450,71 +445,6 @@ class KclManager {
} }
} }
export const kclManager = new KclManager(engineCommandManager)
const KclContext = createContext({
code: kclManager.code,
programMemory: kclManager.programMemory,
ast: kclManager.ast,
isExecuting: kclManager.isExecuting,
errors: kclManager.kclErrors,
logs: kclManager.logs,
wasmInitFailed: kclManager.wasmInitFailed,
})
export function useKclContext() {
return useContext(KclContext)
}
export function KclContextProvider({
children,
}: {
children: React.ReactNode
}) {
// If we try to use this component anywhere but under the paths.FILE route it will fail
// Because useLoaderData assumes we are on within it's context.
const { code: loadedCode } = useLoaderData() as IndexLoaderData
const [code, setCode] = useState(loadedCode || kclManager.code)
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
const [ast, setAst] = useState(kclManager.ast)
const [isExecuting, setIsExecuting] = useState(false)
const [errors, setErrors] = useState<KCLError[]>([])
const [logs, setLogs] = useState<string[]>([])
const [wasmInitFailed, setWasmInitFailed] = useState(false)
useEffect(() => {
kclManager.registerCallBacks({
setCode,
setProgramMemory,
setAst,
setLogs,
setKclErrors: setErrors,
setIsExecuting,
setWasmInitFailed,
})
}, [])
const params = useParams()
useEffect(() => {
kclManager.setParams(params)
}, [params])
return (
<KclContext.Provider
value={{
code,
programMemory,
ast,
isExecuting,
errors,
logs,
wasmInitFailed,
}}
>
{children}
</KclContext.Provider>
)
}
function safeLSGetItem(key: string) { function safeLSGetItem(key: string) {
if (typeof window === 'undefined') return null if (typeof window === 'undefined') return null
return localStorage?.getItem(key) return localStorage?.getItem(key)
@ -525,7 +455,10 @@ function safteLSSetItem(key: string, value: string) {
localStorage?.setItem(key, value) localStorage?.setItem(key, value)
} }
function enterEditMode(programMemory: ProgramMemory) { function enterEditMode(
programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager
) {
const firstSketchOrExtrudeGroup = Object.values(programMemory.root).find( const firstSketchOrExtrudeGroup = Object.values(programMemory.root).find(
(node) => node.type === 'ExtrudeGroup' || node.type === 'SketchGroup' (node) => node.type === 'ExtrudeGroup' || node.type === 'SketchGroup'
) as SketchGroup | ExtrudeGroup ) as SketchGroup | ExtrudeGroup

View File

@ -4,7 +4,6 @@ import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave' import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { getNodePathFromSourceRange } from 'lang/queryAst' import { getNodePathFromSourceRange } from 'lang/queryAst'
import { sceneInfra } from 'clientSideScene/sceneInfra'
let lastMessage = '' let lastMessage = ''
@ -183,7 +182,6 @@ class EngineConnection {
console.log(`${JSON.stringify(this.state)}${JSON.stringify(next)}`) console.log(`${JSON.stringify(this.state)}${JSON.stringify(next)}`)
if (next.type === EngineConnectionStateType.Disconnecting) { if (next.type === EngineConnectionStateType.Disconnecting) {
console.trace()
const sub = next.value const sub = next.value
if (sub.type === DisconnectingType.Error) { if (sub.type === DisconnectingType.Error) {
// Record the last step we failed at. // Record the last step we failed at.
@ -220,8 +218,10 @@ class EngineConnection {
// TODO: actual type is ClientMetrics // TODO: actual type is ClientMetrics
private webrtcStatsCollector?: () => Promise<ClientMetrics> private webrtcStatsCollector?: () => Promise<ClientMetrics>
private engineCommandManager: EngineCommandManager
constructor({ constructor({
engineCommandManager,
url, url,
token, token,
onConnectionStateChange = () => {}, onConnectionStateChange = () => {},
@ -230,6 +230,7 @@ class EngineConnection {
onConnectionStarted = () => {}, onConnectionStarted = () => {},
onClose = () => {}, onClose = () => {},
}: { }: {
engineCommandManager: EngineCommandManager
url: string url: string
token?: string token?: string
onConnectionStateChange?: (state: EngineConnectionState) => void onConnectionStateChange?: (state: EngineConnectionState) => void
@ -238,6 +239,7 @@ class EngineConnection {
onClose?: (engineConnection: EngineConnection) => void onClose?: (engineConnection: EngineConnection) => void
onNewTrack?: (track: NewTrackArgs) => void onNewTrack?: (track: NewTrackArgs) => void
}) { }) {
this.engineCommandManager = engineCommandManager
this.url = url this.url = url
this.token = token this.token = token
this.failedConnTimeout = null this.failedConnTimeout = null
@ -563,8 +565,8 @@ class EngineConnection {
.join('\n') .join('\n')
if (message.request_id) { if (message.request_id) {
const artifactThatFailed = const artifactThatFailed =
engineCommandManager.artifactMap[message.request_id] || this.engineCommandManager.artifactMap[message.request_id] ||
engineCommandManager.lastArtifactMap[message.request_id] this.engineCommandManager.lastArtifactMap[message.request_id]
console.error( console.error(
`Error in response to request ${message.request_id}:\n${errorsString} `Error in response to request ${message.request_id}:\n${errorsString}
failed cmd type was ${artifactThatFailed?.commandType}` failed cmd type was ${artifactThatFailed?.commandType}`
@ -831,7 +833,6 @@ export class EngineCommandManager {
artifactMap: ArtifactMap = {} artifactMap: ArtifactMap = {}
lastArtifactMap: ArtifactMap = {} lastArtifactMap: ArtifactMap = {}
sceneCommandArtifacts: ArtifactMap = {} sceneCommandArtifacts: ArtifactMap = {}
private getAst: () => Program = () => ({ start: 0, end: 0, body: [] } as any)
outSequence = 1 outSequence = 1
inSequence = 1 inSequence = 1
engineConnection?: EngineConnection engineConnection?: EngineConnection
@ -866,11 +867,17 @@ export class EngineCommandManager {
constructor() { constructor() {
this.engineConnection = undefined this.engineConnection = undefined
;(async () => { }
// circular dependency needs one to be lazy loaded
const { kclManager } = await import('lang/KclSingleton') private _camControlsCameraChange = () => {}
this.getAst = () => kclManager.ast set camControlsCameraChange(cb: () => void) {
})() this._camControlsCameraChange = cb
}
private getAst: () => Program = () =>
({ start: 0, end: 0, body: [], nonCodeMeta: {} } as any)
set getAstCb(cb: () => Program) {
this.getAst = cb
} }
start({ start({
@ -903,6 +910,7 @@ export class EngineCommandManager {
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}` const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}`
this.engineConnection = new EngineConnection({ this.engineConnection = new EngineConnection({
engineCommandManager: this,
url, url,
token, token,
onConnectionStateChange: (state: EngineConnectionState) => { onConnectionStateChange: (state: EngineConnectionState) => {
@ -929,7 +937,7 @@ export class EngineCommandManager {
gizmo_mode: true, gizmo_mode: true,
}, },
}) })
sceneInfra.camControls.onCameraChange() this._camControlsCameraChange()
this.sendSceneCommand({ this.sendSceneCommand({
// CameraControls subscribes to default_camera_get_settings response events // CameraControls subscribes to default_camera_get_settings response events
// firing this at connection ensure the camera's are synced initially // firing this at connection ensure the camera's are synced initially
@ -1619,5 +1627,3 @@ export class EngineCommandManager {
return planeId return planeId
} }
} }
export const engineCommandManager = new EngineCommandManager()

View File

@ -1,4 +1,4 @@
import { engineCommandManager } from 'lang/std/engineConnection' import { engineCommandManager } from 'lib/singletons'
import { type Models } from '@kittycad/lib' import { type Models } from '@kittycad/lib'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'

View File

@ -15,7 +15,7 @@ import makeUrlPathRelative from './makeUrlPathRelative'
import { sep } from '@tauri-apps/api/path' import { sep } from '@tauri-apps/api/path'
import { readDir, readTextFile } from '@tauri-apps/api/fs' import { readDir, readTextFile } from '@tauri-apps/api/fs'
import { metadata } from 'tauri-plugin-fs-extra-api' import { metadata } from 'tauri-plugin-fs-extra-api'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { fileSystemManager } from 'lang/std/fileSystemManager' import { fileSystemManager } from 'lang/std/fileSystemManager'
// The root loader simply resolves the settings and any errors that // The root loader simply resolves the settings and any errors that

View File

@ -1,10 +1,13 @@
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { engineCommandManager } from 'lang/std/engineConnection' import {
engineCommandManager,
kclManager,
sceneEntitiesManager,
} from 'lib/singletons'
import { CallExpression, SourceRange, parse, recast } from 'lang/wasm' import { CallExpression, SourceRange, parse, recast } from 'lang/wasm'
import { ModelingMachineEvent } from 'machines/modelingMachine' import { ModelingMachineEvent } from 'machines/modelingMachine'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { EditorSelection } from '@codemirror/state' import { EditorSelection } from '@codemirror/state'
import { kclManager } from 'lang/KclSingleton'
import { SelectionRange } from '@uiw/react-codemirror' import { SelectionRange } from '@uiw/react-codemirror'
import { getNormalisedCoordinates, isOverlap } from 'lib/utils' import { getNormalisedCoordinates, isOverlap } from 'lib/utils'
import { isCursorInSketchCommandRange } from 'lang/util' import { isCursorInSketchCommandRange } from 'lang/util'
@ -18,7 +21,6 @@ import { CommandArgument } from './commandTypes'
import { import {
STRAIGHT_SEGMENT, STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT,
sceneEntitiesManager,
getParentGroup, getParentGroup,
PROFILE_START, PROFILE_START,
} from 'clientSideScene/sceneEntities' } from 'clientSideScene/sceneEntities'

14
src/lib/singletons.ts Normal file
View File

@ -0,0 +1,14 @@
import { SceneEntities } from 'clientSideScene/sceneEntities'
import { SceneInfra } from 'clientSideScene/sceneInfra'
import { KclManager } from 'lang/KclSingleton'
import { EngineCommandManager } from 'lang/std/engineConnection'
export const engineCommandManager = new EngineCommandManager()
export const kclManager = new KclManager(engineCommandManager)
engineCommandManager.getAstCb = () => kclManager.ast
export const sceneInfra = new SceneInfra(engineCommandManager)
engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange
export const sceneEntitiesManager = new SceneEntities(engineCommandManager)

View File

@ -1,8 +1,8 @@
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { kclManager, engineCommandManager } from 'lib/singletons'
import { useKclContext } from 'lang/KclProvider'
import { findUniqueName } from 'lang/modifyAst' import { findUniqueName } from 'lang/modifyAst'
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst' import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
import { engineCommandManager } from '../lang/std/engineConnection'
import { Value, parse } from 'lang/wasm' import { Value, parse } from 'lang/wasm'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { executeAst } from 'useStore' import { executeAst } from 'useStore'

View File

@ -1,5 +1,6 @@
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager, useKclContext } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { useKclContext } from 'lang/KclProvider'
import { findAllPreviousVariables } from 'lang/queryAst' import { findAllPreviousVariables } from 'lang/queryAst'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'

View File

@ -2,7 +2,7 @@ import { PathToNode, VariableDeclarator } from 'lang/wasm'
import { Axis, Selection, Selections } from 'lib/selections' import { Axis, Selection, Selections } from 'lib/selections'
import { assign, createMachine } from 'xstate' import { assign, createMachine } from 'xstate'
import { getNodePathFromSourceRange } from 'lang/queryAst' import { getNodePathFromSourceRange } from 'lang/queryAst'
import { kclManager } from 'lang/KclSingleton' import { kclManager, sceneInfra, sceneEntitiesManager } from 'lib/singletons'
import { import {
horzVertInfo, horzVertInfo,
applyConstraintHorzVert, applyConstraintHorzVert,
@ -34,11 +34,7 @@ import {
} from 'components/Toolbar/SetAbsDistance' } from 'components/Toolbar/SetAbsDistance'
import { Models } from '@kittycad/lib/dist/types/src' import { Models } from '@kittycad/lib/dist/types/src'
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig' import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
import { import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
DefaultPlaneStr,
sceneEntitiesManager,
} from 'clientSideScene/sceneEntities'
import { sceneInfra } from 'clientSideScene/sceneInfra'
import { Vector3 } from 'three' import { Vector3 } from 'three'
import { quaternionFromUpNForward } from 'clientSideScene/helpers' import { quaternionFromUpNForward } from 'clientSideScene/helpers'

View File

@ -36,7 +36,7 @@ import { sep } from '@tauri-apps/api/path'
import { homeCommandBarConfig } from 'lib/commandBarConfigs/homeCommandConfig' import { homeCommandBarConfig } from 'lib/commandBarConfigs/homeCommandConfig'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { useLspContext } from 'components/LspProvider' import { useLspContext } from 'components/LspProvider'
import { useValidateSettings } from 'hooks/useValidateSettings' import { useValidateSettings } from 'hooks/useValidateSettings'

View File

@ -1,7 +1,7 @@
import { OnboardingButtons, useDismiss } from '.' import { OnboardingButtons, useDismiss } from '.'
import { useEffect } from 'react' import { useEffect } from 'react'
import { bracket } from 'lib/exampleKcl' import { bracket } from 'lib/exampleKcl'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { APP_NAME } from 'lib/constants' import { APP_NAME } from 'lib/constants'
import { onboardingPaths } from './paths' import { onboardingPaths } from './paths'

View File

@ -19,7 +19,7 @@ import { isTauri } from 'lib/isTauri'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { paths } from 'lib/paths' import { paths } from 'lib/paths'
import { useEffect } from 'react' import { useEffect } from 'react'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
import { sep } from '@tauri-apps/api/path' import { sep } from '@tauri-apps/api/path'
import { APP_NAME } from 'lib/constants' import { APP_NAME } from 'lib/constants'

View File

@ -2,7 +2,7 @@ import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from 'useStore' import { useStore } from 'useStore'
import { useEffect } from 'react' import { useEffect } from 'react'
import { kclManager } from 'lang/KclSingleton' import { kclManager } from 'lib/singletons'
export default function Sketching() { export default function Sketching() {
const buttonDownInStream = useStore((s) => s.buttonDownInStream) const buttonDownInStream = useStore((s) => s.buttonDownInStream)