Files
modeling-app/src/lib/singletons.ts
Kurt Hutten 43d5a72514 Migrate Text-to-CAD (edit) to multi-file endpoint (#6066)
* start of migrate to multi file endpoint

* get some relative path stuff sorted

* blobifying files, and making selections work with imports working

* add write to disk

* warn about big projects

* update known circular

* update snapshot

* remove log

* tweak selection filters

* Update src/components/ModelingMachineProvider.tsx

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* fmt

* fix one thing

* typo

* raw dog form data like a fucking peasant

* remove fake data

* fmt

* steal Kevin's stuff

* good progress

* clean up

* fix writing to files when response returns

* comment the terriable code

* push fix of sorts

* better fix

* spot of clean up

* fix: Needed to support the bad request flow, the toast will hang forever, the return control flows don't dismiss a forever toast

* fix: handling more error flows by dismissing the toast

* chore: leaving a comment for a confusing workflow

* fix: trying to clean up some async logic

* fix: trying to fix a few things at once...

* fix: fixing toast success

* fix: how did this desync?

* fix: removing useless logic, we write to disk ahead of time, the continue is to say ya no problem

* fix: typo

* Change back to `spawnChild`, forego `actors` by reference

* fix: updating PR comments

* fix: found a bug with paths from rust! it is actually OS paths!

* fix: updated type still is failing tsc

* fix: the type of the machine was wrong, we always set it to at least ''

* fix: idk man

* Fix happy path test (button labels)

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-05-07 17:54:40 +00:00

247 lines
8.0 KiB
TypeScript

import { VITE_KC_API_BASE_URL } from '@src/env'
import EditorManager from '@src/editor/manager'
import { KclManager } from '@src/lang/KclSingleton'
import CodeManager from '@src/lang/codeManager'
import { EngineCommandManager } from '@src/lang/std/engineConnection'
import RustContext from '@src/lib/rustContext'
import { uuidv4 } from '@src/lib/utils'
import { SceneEntities } from '@src/clientSideScene/sceneEntities'
import { SceneInfra } from '@src/clientSideScene/sceneInfra'
import type { BaseUnit } from '@src/lib/settings/settingsTypes'
import { useSelector } from '@xstate/react'
import type { ActorRefFrom, SnapshotFrom } from 'xstate'
import { createActor, setup, spawnChild } from 'xstate'
import { isDesktop } from '@src/lib/isDesktop'
import { createSettings } from '@src/lib/settings/initialSettings'
import { authMachine } from '@src/machines/authMachine'
import {
BILLING_CONTEXT_DEFAULTS,
billingMachine,
} from '@src/machines/billingMachine'
import {
engineStreamContextCreate,
engineStreamMachine,
} from '@src/machines/engineStreamMachine'
import { ACTOR_IDS } from '@src/machines/machineConstants'
import { settingsMachine } from '@src/machines/settingsMachine'
import { systemIOMachineDesktop } from '@src/machines/systemIO/systemIOMachineDesktop'
import { systemIOMachineWeb } from '@src/machines/systemIO/systemIOMachineWeb'
import type { AppMachineContext } from '@src/lib/types'
import { createAuthCommands } from '@src/lib/commandBarConfigs/authCommandConfig'
import { commandBarMachine } from '@src/machines/commandBarMachine'
import { createProjectCommands } from '@src/lib/commandBarConfigs/projectsCommandConfig'
export const codeManager = new CodeManager()
export const engineCommandManager = new EngineCommandManager()
export const rustContext = new RustContext(engineCommandManager)
declare global {
interface Window {
editorManager: EditorManager
engineCommandManager: EngineCommandManager
}
}
// Accessible for tests mostly
window.engineCommandManager = engineCommandManager
export const sceneInfra = new SceneInfra(engineCommandManager)
engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange
// This needs to be after sceneInfra and engineCommandManager are is created.
export const editorManager = new EditorManager(engineCommandManager)
// This needs to be after codeManager is created.
// (lee: what??? why?)
export const kclManager = new KclManager(engineCommandManager, {
rustContext,
codeManager,
editorManager,
sceneInfra,
})
// The most obvious of cyclic dependencies.
// This is because the handleOnViewUpdate(viewUpdate: ViewUpdate): void {
// method requires it for the current ast.
// CYCLIC REF
editorManager.kclManager = kclManager
// These are all late binding because of their circular dependency.
// TODO: proper dependency injection.
engineCommandManager.kclManager = kclManager
engineCommandManager.codeManager = codeManager
engineCommandManager.rustContext = rustContext
kclManager.sceneInfraBaseUnitMultiplierSetter = (unit: BaseUnit) => {
sceneInfra.baseUnit = unit
}
export const sceneEntitiesManager = new SceneEntities(
engineCommandManager,
sceneInfra,
editorManager,
codeManager,
kclManager,
rustContext
)
if (typeof window !== 'undefined') {
;(window as any).engineCommandManager = engineCommandManager
;(window as any).kclManager = kclManager
;(window as any).sceneInfra = sceneInfra
;(window as any).sceneEntitiesManager = sceneEntitiesManager
;(window as any).editorManager = editorManager
;(window as any).codeManager = codeManager
;(window as any).rustContext = rustContext
;(window as any).enableMousePositionLogs = () =>
document.addEventListener('mousemove', (e) =>
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
)
;(window as any).enableFillet = () => {
;(window as any)._enableFillet = true
}
;(window as any).zoomToFit = () =>
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: [], // leave empty to zoom to all objects
padding: 0.2, // padding around the objects
animated: false, // don't animate the zoom for now
},
})
}
const { AUTH, SETTINGS, SYSTEM_IO, ENGINE_STREAM, COMMAND_BAR, BILLING } =
ACTOR_IDS
const appMachineActors = {
[AUTH]: authMachine,
[SETTINGS]: settingsMachine,
[SYSTEM_IO]: isDesktop() ? systemIOMachineDesktop : systemIOMachineWeb,
[ENGINE_STREAM]: engineStreamMachine,
[COMMAND_BAR]: commandBarMachine,
[BILLING]: billingMachine,
} as const
const appMachine = setup({
types: {} as {
context: AppMachineContext
},
}).createMachine({
id: 'modeling-app',
context: {
codeManager: codeManager,
kclManager: kclManager,
engineCommandManager: engineCommandManager,
sceneInfra: sceneInfra,
sceneEntitiesManager: sceneEntitiesManager,
},
entry: [
/**
* We have been battling XState's type unions exploding in size,
* so for these global actors, we have decided to forego creating them by reference
* using the `actors` property in the `setup` function, and
* inline them instead.
*/
spawnChild(appMachineActors[AUTH], { systemId: AUTH }),
spawnChild(appMachineActors[SETTINGS], {
systemId: SETTINGS,
input: createSettings(),
}),
spawnChild(appMachineActors[ENGINE_STREAM], {
systemId: ENGINE_STREAM,
input: engineStreamContextCreate(),
}),
spawnChild(appMachineActors[SYSTEM_IO], {
systemId: SYSTEM_IO,
}),
spawnChild(appMachineActors[COMMAND_BAR], {
systemId: COMMAND_BAR,
input: {
commands: [],
},
}),
spawnChild(appMachineActors[BILLING], {
systemId: BILLING,
input: {
...BILLING_CONTEXT_DEFAULTS,
urlUserService: VITE_KC_API_BASE_URL,
},
}),
],
})
export const appActor = createActor(appMachine, {
systemId: 'root',
})
/**
* GOTCHA: the type coercion of this actor works because it is spawned for
* the lifetime of {appActor}, but would not work if it were invoked
* or if it were destroyed under any conditions during {appActor}'s life
*/
export const authActor = appActor.system.get(AUTH) as ActorRefFrom<
(typeof appMachineActors)[typeof AUTH]
>
export const useAuthState = () => useSelector(authActor, (state) => state)
export const useToken = () =>
useSelector(authActor, (state) => state.context.token)
export const useUser = () =>
useSelector(authActor, (state) => state.context.user)
/**
* GOTCHA: the type coercion of this actor works because it is spawned for
* the lifetime of {appActor}, but would not work if it were invoked
* or if it were destroyed under any conditions during {appActor}'s life
*/
export const settingsActor = appActor.system.get(SETTINGS) as ActorRefFrom<
(typeof appMachineActors)[typeof SETTINGS]
>
export const getSettings = () => {
const { currentProject: _, ...settings } = settingsActor.getSnapshot().context
return settings
}
export const useSettings = () =>
useSelector(settingsActor, (state) => {
// We have to peel everything that isn't settings off
const { currentProject, ...settings } = state.context
return settings
})
export const systemIOActor = appActor.system.get(SYSTEM_IO) as ActorRefFrom<
(typeof appMachineActors)[typeof SYSTEM_IO]
>
export const engineStreamActor = appActor.system.get(
ENGINE_STREAM
) as ActorRefFrom<(typeof appMachineActors)[typeof ENGINE_STREAM]>
export const commandBarActor = appActor.system.get(COMMAND_BAR) as ActorRefFrom<
(typeof appMachineActors)[typeof COMMAND_BAR]
>
export const billingActor = appActor.system.get(BILLING) as ActorRefFrom<
(typeof appMachineActors)[typeof BILLING]
>
const cmdBarStateSelector = (state: SnapshotFrom<typeof commandBarActor>) =>
state
export const useCommandBarState = () => {
return useSelector(commandBarActor, cmdBarStateSelector)
}
// Initialize global commands
commandBarActor.send({
type: 'Add commands',
data: {
commands: [
...createAuthCommands({ authActor }),
...createProjectCommands({ systemIOActor }),
],
},
})