start of fixing changing files and cleaning up after execute (#897)
* start of fixing changing files and cleaning up after execute * stop constraints from leaving artifacts * don't write file on initial load
This commit is contained in:
21
src/App.tsx
21
src/App.tsx
@ -31,7 +31,6 @@ 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 './lang/std/engineConnection'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
@ -82,26 +81,6 @@ export function App() {
|
|||||||
? 'opacity-40'
|
? 'opacity-40'
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
// Use file code loaded from disk
|
|
||||||
// on mount, and overwrite any locally-stored code
|
|
||||||
useEffect(() => {
|
|
||||||
if (isTauri() && loadedCode !== null) {
|
|
||||||
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
|
|
||||||
// If the engine is ready, promptly execute the loaded code
|
|
||||||
kclManager.setCodeAndExecute(loadedCode)
|
|
||||||
} else {
|
|
||||||
// Otherwise, just set the code and wait for the connection to complete
|
|
||||||
kclManager.setCode(loadedCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
// Clear code on unmount if in desktop app
|
|
||||||
if (isTauri()) {
|
|
||||||
kclManager.setCode('')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [loadedCode])
|
|
||||||
|
|
||||||
useEngineConnectionSubscriptions()
|
useEngineConnectionSubscriptions()
|
||||||
|
|
||||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||||
|
@ -42,7 +42,7 @@ import CommandBarProvider from 'components/CommandBar'
|
|||||||
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
||||||
import * as Sentry from '@sentry/react'
|
import * as Sentry from '@sentry/react'
|
||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||||
import { KclContextProvider } from 'lang/KclSinglton'
|
import { KclContextProvider, kclManager } from 'lang/KclSinglton'
|
||||||
import FileMachineProvider from 'components/FileMachineProvider'
|
import FileMachineProvider from 'components/FileMachineProvider'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
@ -207,6 +207,7 @@ const router = createBrowserRouter(
|
|||||||
projectPath + sep + PROJECT_ENTRYPOINT
|
projectPath + sep + PROJECT_ENTRYPOINT
|
||||||
)
|
)
|
||||||
const children = await readDir(projectPath, { recursive: true })
|
const children = await readDir(projectPath, { recursive: true })
|
||||||
|
kclManager.setCodeAndExecute(code, false)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { parse, BinaryPart, Value, executor } from '../lang/wasm'
|
import { parse, BinaryPart, Value } from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -10,6 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
|||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
import { executeAst } from 'useStore'
|
||||||
|
|
||||||
export const AvailableVars = ({
|
export const AvailableVars = ({
|
||||||
onVarClick,
|
onVarClick,
|
||||||
@ -130,27 +131,29 @@ export function useCalc({
|
|||||||
if (!programMemory || !selectionRange) return
|
if (!programMemory || !selectionRange) return
|
||||||
const varInfo = findAllPreviousVariables(
|
const varInfo = findAllPreviousVariables(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
programMemory,
|
kclManager.programMemory,
|
||||||
selectionRange
|
selectionRange
|
||||||
)
|
)
|
||||||
setAvailableVarInfo(varInfo)
|
setAvailableVarInfo(varInfo)
|
||||||
}, [kclManager.ast, programMemory, selectionRange])
|
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}\nshow(__result__)`
|
const code = `const __result__ = ${value}`
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const _programMem: any = { root: {}, return: null }
|
const _programMem: any = { root: {}, return: null }
|
||||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||||
})
|
})
|
||||||
|
executeAst({
|
||||||
executor(
|
|
||||||
ast,
|
ast,
|
||||||
_programMem,
|
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
kclManager.defaultPlanes
|
defaultPlanes: kclManager.defaultPlanes,
|
||||||
).then((programMemory) => {
|
useFakeExecutor: true,
|
||||||
|
programMemoryOverride: JSON.parse(
|
||||||
|
JSON.stringify(kclManager.programMemory)
|
||||||
|
),
|
||||||
|
}).then(({ programMemory }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
a.type === 'VariableDeclaration' &&
|
a.type === 'VariableDeclaration' &&
|
||||||
@ -167,7 +170,7 @@ export function useCalc({
|
|||||||
setCalcResult('NAN')
|
setCalcResult('NAN')
|
||||||
setValueNode(null)
|
setValueNode(null)
|
||||||
}
|
}
|
||||||
}, [value])
|
}, [value, availableVarInfo])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valueNode,
|
valueNode,
|
||||||
@ -212,7 +215,10 @@ export const CreateNewVariable = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label htmlFor="create-new-variable" className="block mt-3 font-mono">
|
<label
|
||||||
|
htmlFor="create-new-variable"
|
||||||
|
className="block mt-3 font-mono text-gray-900"
|
||||||
|
>
|
||||||
Create new variable
|
Create new variable
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 flex gap-2 items-center">
|
<div className="mt-1 flex gap-2 items-center">
|
||||||
@ -223,6 +229,7 @@ export const CreateNewVariable = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setShouldCreateVariable(e.target.checked)
|
setShouldCreateVariable(e.target.checked)
|
||||||
}}
|
}}
|
||||||
|
className="bg-white text-gray-900"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
|
@ -163,7 +163,6 @@ const FileTreeItem = ({
|
|||||||
|
|
||||||
function openFile() {
|
function openFile() {
|
||||||
if (fileOrDir.children !== undefined) return // Don't open directories
|
if (fileOrDir.children !== undefined) return // Don't open directories
|
||||||
kclManager.setCode('')
|
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
navigate(`${paths.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||||
closePanel()
|
closePanel()
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ export const ModelingMachineProvider = ({
|
|||||||
engineCommandManager.artifactMap[sketchEnginePathId] = {
|
engineCommandManager.artifactMap[sketchEnginePathId] = {
|
||||||
type: 'result',
|
type: 'result',
|
||||||
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
|
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
|
||||||
commandType: 'extend_path',
|
commandType: 'start_path',
|
||||||
data: null,
|
data: null,
|
||||||
raw: {} as any,
|
raw: {} as any,
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ export const SetAngleLengthModal = ({
|
|||||||
</label>
|
</label>
|
||||||
<div className="mt-1 flex">
|
<div className="mt-1 flex">
|
||||||
<button
|
<button
|
||||||
className="border border-gray-300 px-2"
|
className="border border-gray-300 px-2 text-gray-900"
|
||||||
onClick={() => setSign(-sign)}
|
onClick={() => setSign(-sign)}
|
||||||
>
|
>
|
||||||
{sign > 0 ? '+' : '-'}
|
{sign > 0 ? '+' : '-'}
|
||||||
@ -118,7 +118,7 @@ export const SetAngleLengthModal = ({
|
|||||||
type="text"
|
type="text"
|
||||||
name="val"
|
name="val"
|
||||||
id="val"
|
id="val"
|
||||||
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1"
|
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1 text-gray-900"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue(e.target.value)
|
setValue(e.target.value)
|
||||||
|
@ -87,7 +87,7 @@ export const GetInfoModal = ({
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white/90 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
className="text-lg font-medium leading-6 text-gray-900"
|
className="text-lg font-medium leading-6 text-gray-900"
|
||||||
@ -109,7 +109,7 @@ export const GetInfoModal = ({
|
|||||||
</label>
|
</label>
|
||||||
<div className="mt-1 flex">
|
<div className="mt-1 flex">
|
||||||
<button
|
<button
|
||||||
className="border border-gray-300 px-2 mr-1"
|
className="border border-gray-400 px-2 mr-1 text-gray-900"
|
||||||
onClick={() => setSign(-sign)}
|
onClick={() => setSign(-sign)}
|
||||||
>
|
>
|
||||||
{sign > 0 ? '+' : '-'}
|
{sign > 0 ? '+' : '-'}
|
||||||
@ -119,7 +119,7 @@ export const GetInfoModal = ({
|
|||||||
name="val"
|
name="val"
|
||||||
id="val"
|
id="val"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono"
|
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm text-gray-900 border-gray-300 rounded-md font-mono"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue(e.target.value)
|
setValue(e.target.value)
|
||||||
@ -139,7 +139,7 @@ export const GetInfoModal = ({
|
|||||||
name="segName"
|
name="segName"
|
||||||
id="segName"
|
id="segName"
|
||||||
disabled={!isSegNameEditable}
|
disabled={!isSegNameEditable}
|
||||||
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono"
|
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm text-gray-900 border-gray-300 rounded-md font-mono"
|
||||||
value={segName}
|
value={segName}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSegName(e.target.value)
|
setSegName(e.target.value)
|
||||||
|
@ -17,15 +17,7 @@ import { useStore } from 'useStore'
|
|||||||
import { processCodeMirrorRanges } from 'lib/selections'
|
import { processCodeMirrorRanges } from 'lib/selections'
|
||||||
import { LanguageServerClient } from 'editor/lsp'
|
import { LanguageServerClient } from 'editor/lsp'
|
||||||
import kclLanguage from 'editor/lsp/language'
|
import kclLanguage from 'editor/lsp/language'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { EditorView, lineHighlightField } from 'editor/highlightextension'
|
||||||
import { useParams } from 'react-router-dom'
|
|
||||||
import { writeTextFile } from '@tauri-apps/api/fs'
|
|
||||||
import { toast } from 'react-hot-toast'
|
|
||||||
import {
|
|
||||||
EditorView,
|
|
||||||
addLineHighlight,
|
|
||||||
lineHighlightField,
|
|
||||||
} from 'editor/highlightextension'
|
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
import { kclErrToDiagnostic } from 'lang/errors'
|
import { kclErrToDiagnostic } from 'lang/errors'
|
||||||
import { CSSRuleObject } from 'tailwindcss/types/config'
|
import { CSSRuleObject } from 'tailwindcss/types/config'
|
||||||
@ -50,7 +42,6 @@ export const TextEditor = ({
|
|||||||
}: {
|
}: {
|
||||||
theme: Themes.Light | Themes.Dark
|
theme: Themes.Light | Themes.Dark
|
||||||
}) => {
|
}) => {
|
||||||
const pathParams = useParams()
|
|
||||||
const { editorView, isLSPServerReady, setEditorView, setIsLSPServerReady } =
|
const { editorView, isLSPServerReady, setEditorView, setIsLSPServerReady } =
|
||||||
useStore((s) => ({
|
useStore((s) => ({
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
@ -113,18 +104,6 @@ export const TextEditor = ({
|
|||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (newCode: string) => {
|
const onChange = (newCode: string) => {
|
||||||
kclManager.setCodeAndExecute(newCode)
|
kclManager.setCodeAndExecute(newCode)
|
||||||
if (isTauri() && pathParams.id) {
|
|
||||||
// Save the file to disk
|
|
||||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
|
||||||
writeTextFile(pathParams.id, newCode).catch((err) => {
|
|
||||||
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
|
||||||
console.error('error saving file', err)
|
|
||||||
toast.error('Error saving file, please check file permissions')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (editorView) {
|
|
||||||
editorView?.dispatch({ effects: addLineHighlight.of([0, 0]) })
|
|
||||||
}
|
|
||||||
} //, []);
|
} //, []);
|
||||||
const onUpdate = (viewUpdate: ViewUpdate) => {
|
const onUpdate = (viewUpdate: ViewUpdate) => {
|
||||||
if (!editorView) {
|
if (!editorView) {
|
||||||
|
@ -18,7 +18,11 @@ import { bracket } from 'lib/exampleKcl'
|
|||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { getNodeFromPath } from './queryAst'
|
import { getNodeFromPath } from './queryAst'
|
||||||
import { IndexLoaderData } from 'Router'
|
import { IndexLoaderData } from 'Router'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { Params, useLoaderData } from 'react-router-dom'
|
||||||
|
import { isTauri } from 'lib/isTauri'
|
||||||
|
import { writeTextFile } from '@tauri-apps/api/fs'
|
||||||
|
import { toast } from 'react-hot-toast'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||||
|
|
||||||
@ -41,6 +45,7 @@ class KclManager {
|
|||||||
private _kclErrors: KCLError[] = []
|
private _kclErrors: KCLError[] = []
|
||||||
private _isExecuting = false
|
private _isExecuting = false
|
||||||
private _wasmInitFailed = true
|
private _wasmInitFailed = true
|
||||||
|
private _params: Params<string> = {}
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
private _defferer = deferExecution((code: string) => {
|
private _defferer = deferExecution((code: string) => {
|
||||||
@ -71,6 +76,21 @@ class KclManager {
|
|||||||
set code(code) {
|
set code(code) {
|
||||||
this._code = code
|
this._code = code
|
||||||
this._codeCallBack(code)
|
this._codeCallBack(code)
|
||||||
|
if (isTauri()) {
|
||||||
|
setTimeout(() => {
|
||||||
|
// Wait one event loop to give a chance for params to be set
|
||||||
|
// Save the file to disk
|
||||||
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||||
|
this._params.id &&
|
||||||
|
writeTextFile(this._params.id, code).catch((err) => {
|
||||||
|
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||||
|
console.error('error saving file', err)
|
||||||
|
toast.error('Error saving file, please check file permissions')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(PERSIST_CODE_TOKEN, code)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get programMemory() {
|
get programMemory() {
|
||||||
@ -117,8 +137,17 @@ class KclManager {
|
|||||||
this._wasmInitFailedCallback(wasmInitFailed)
|
this._wasmInitFailedCallback(wasmInitFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setParams(params: Params<string>) {
|
||||||
|
this._params = params
|
||||||
|
}
|
||||||
|
|
||||||
constructor(engineCommandManager: EngineCommandManager) {
|
constructor(engineCommandManager: EngineCommandManager) {
|
||||||
this.engineCommandManager = engineCommandManager
|
this.engineCommandManager = engineCommandManager
|
||||||
|
|
||||||
|
if (isTauri()) {
|
||||||
|
this.code = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
const storedCode = localStorage.getItem(PERSIST_CODE_TOKEN)
|
const storedCode = localStorage.getItem(PERSIST_CODE_TOKEN)
|
||||||
// TODO #819 remove zustand persistence logic in a few months
|
// TODO #819 remove zustand persistence logic in a few months
|
||||||
// short term migration, shouldn't make a difference for tauri app users
|
// short term migration, shouldn't make a difference for tauri app users
|
||||||
@ -130,6 +159,7 @@ class KclManager {
|
|||||||
zustandStore.state.code = ''
|
zustandStore.state.code = ''
|
||||||
localStorage.setItem('store', JSON.stringify(zustandStore))
|
localStorage.setItem('store', JSON.stringify(zustandStore))
|
||||||
} else if (storedCode === null) {
|
} else if (storedCode === null) {
|
||||||
|
console.log('stored brack thing')
|
||||||
this.code = bracket
|
this.code = bracket
|
||||||
} else {
|
} else {
|
||||||
this.code = storedCode
|
this.code = storedCode
|
||||||
@ -189,8 +219,7 @@ class KclManager {
|
|||||||
this._programMemory = programMemory
|
this._programMemory = programMemory
|
||||||
this._ast = { ...ast }
|
this._ast = { ...ast }
|
||||||
if (updateCode) {
|
if (updateCode) {
|
||||||
this._code = recast(ast)
|
this.code = recast(ast)
|
||||||
this._codeCallBack(this._code)
|
|
||||||
}
|
}
|
||||||
this._executeCallback()
|
this._executeCallback()
|
||||||
}
|
}
|
||||||
@ -233,13 +262,17 @@ class KclManager {
|
|||||||
this.ast = ast
|
this.ast = ast
|
||||||
if (code) this.code = code
|
if (code) this.code = code
|
||||||
}
|
}
|
||||||
setCode(code: string) {
|
setCode(code: string, shouldWriteFile = true) {
|
||||||
|
if (shouldWriteFile) {
|
||||||
|
// use the normal code setter
|
||||||
|
this.code = code
|
||||||
|
return
|
||||||
|
}
|
||||||
this._code = code
|
this._code = code
|
||||||
this._codeCallBack(code)
|
this._codeCallBack(code)
|
||||||
localStorage.setItem(PERSIST_CODE_TOKEN, code)
|
|
||||||
}
|
}
|
||||||
setCodeAndExecute(code: string) {
|
setCodeAndExecute(code: string, shouldWriteFile = true) {
|
||||||
this.setCode(code)
|
this.setCode(code, shouldWriteFile)
|
||||||
if (code.trim()) {
|
if (code.trim()) {
|
||||||
this._defferer(code)
|
this._defferer(code)
|
||||||
return
|
return
|
||||||
@ -270,15 +303,12 @@ class KclManager {
|
|||||||
execute: boolean,
|
execute: boolean,
|
||||||
optionalParams?: {
|
optionalParams?: {
|
||||||
focusPath?: PathToNode
|
focusPath?: PathToNode
|
||||||
callBack?: (ast: Program) => void
|
|
||||||
}
|
}
|
||||||
): Promise<Selections | null> {
|
): Promise<Selections | null> {
|
||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
const astWithUpdatedSource = parse(newCode)
|
const astWithUpdatedSource = parse(newCode)
|
||||||
optionalParams?.callBack?.(astWithUpdatedSource)
|
|
||||||
let returnVal: Selections | null = null
|
let returnVal: Selections | null = null
|
||||||
|
|
||||||
this.code = newCode
|
|
||||||
if (optionalParams?.focusPath) {
|
if (optionalParams?.focusPath) {
|
||||||
const { node } = getNodeFromPath<any>(
|
const { node } = getNodeFromPath<any>(
|
||||||
astWithUpdatedSource,
|
astWithUpdatedSource,
|
||||||
@ -299,12 +329,12 @@ class KclManager {
|
|||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
// Call execute on the set ast.
|
// Call execute on the set ast.
|
||||||
await this.executeAst(astWithUpdatedSource)
|
await this.executeAst(astWithUpdatedSource, true)
|
||||||
} else {
|
} else {
|
||||||
// When we don't re-execute, we still want to update the program
|
// When we don't re-execute, we still want to update the program
|
||||||
// memory with the new ast. So we will hit the mock executor
|
// memory with the new ast. So we will hit the mock executor
|
||||||
// instead.
|
// instead.
|
||||||
await this.executeAstMock(astWithUpdatedSource)
|
await this.executeAstMock(astWithUpdatedSource, true)
|
||||||
}
|
}
|
||||||
return returnVal
|
return returnVal
|
||||||
}
|
}
|
||||||
@ -369,6 +399,11 @@ export function KclContextProvider({
|
|||||||
setWasmInitFailed,
|
setWasmInitFailed,
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const params = useParams()
|
||||||
|
useEffect(() => {
|
||||||
|
kclManager.setParams(params)
|
||||||
|
}, [params])
|
||||||
return (
|
return (
|
||||||
<KclContext.Provider
|
<KclContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -889,6 +889,7 @@ export class EngineCommandManager {
|
|||||||
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
|
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
|
||||||
// we need to loop over them each individually because if the engine doesn't recognise a single
|
// we need to loop over them each individually because if the engine doesn't recognise a single
|
||||||
// id the whole command fails.
|
// id the whole command fails.
|
||||||
|
const artifactsToDelete: any = {}
|
||||||
Object.entries(this.artifactMap).forEach(([id, artifact]) => {
|
Object.entries(this.artifactMap).forEach(([id, artifact]) => {
|
||||||
const artifactTypesToDelete: ArtifactMap[string]['commandType'][] = [
|
const artifactTypesToDelete: ArtifactMap[string]['commandType'][] = [
|
||||||
// 'start_path' creates a new scene object for the path, which is why it needs to be deleted,
|
// 'start_path' creates a new scene object for the path, which is why it needs to be deleted,
|
||||||
@ -898,7 +899,9 @@ export class EngineCommandManager {
|
|||||||
'start_path',
|
'start_path',
|
||||||
]
|
]
|
||||||
if (!artifactTypesToDelete.includes(artifact.commandType)) return
|
if (!artifactTypesToDelete.includes(artifact.commandType)) return
|
||||||
|
artifactsToDelete[id] = artifact
|
||||||
|
})
|
||||||
|
Object.keys(artifactsToDelete).forEach((id) => {
|
||||||
const deletCmd: EngineCommand = {
|
const deletCmd: EngineCommand = {
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
|
@ -26,5 +26,4 @@ const bracket = startSketchOn('XY')
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(width, %)
|
|> extrude(width, %)
|
||||||
|
|
||||||
show(bracket)
|
|
||||||
`
|
`
|
||||||
|
@ -9,7 +9,6 @@ import { documentDir, homeDir, sep } from '@tauri-apps/api/path'
|
|||||||
import { isTauri } from './isTauri'
|
import { isTauri } from './isTauri'
|
||||||
import { ProjectWithEntryPointMetadata } from '../Router'
|
import { ProjectWithEntryPointMetadata } from '../Router'
|
||||||
import { metadata } from 'tauri-plugin-fs-extra-api'
|
import { metadata } from 'tauri-plugin-fs-extra-api'
|
||||||
import { bracket } from './exampleKcl'
|
|
||||||
|
|
||||||
const PROJECT_FOLDER = 'kittycad-modeling-projects'
|
const PROJECT_FOLDER = 'kittycad-modeling-projects'
|
||||||
export const FILE_EXT = '.kcl'
|
export const FILE_EXT = '.kcl'
|
||||||
@ -211,7 +210,8 @@ export function sortProject(project: FileEntry[]): FileEntry[] {
|
|||||||
// Creates a new file in the default directory with the default project name
|
// Creates a new file in the default directory with the default project name
|
||||||
// Returns the path to the new file
|
// Returns the path to the new file
|
||||||
export async function createNewProject(
|
export async function createNewProject(
|
||||||
path: string
|
path: string,
|
||||||
|
initCode = ''
|
||||||
): Promise<ProjectWithEntryPointMetadata> {
|
): Promise<ProjectWithEntryPointMetadata> {
|
||||||
if (!isTauri) {
|
if (!isTauri) {
|
||||||
throw new Error('createNewProject() can only be called from a Tauri app')
|
throw new Error('createNewProject() can only be called from a Tauri app')
|
||||||
@ -225,10 +225,12 @@ export async function createNewProject(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, bracket).catch((err) => {
|
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, initCode).catch(
|
||||||
console.error('Error creating new file:', err)
|
(err) => {
|
||||||
throw err
|
console.error('Error creating new file:', err)
|
||||||
})
|
throw err
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const m = await metadata(path)
|
const m = await metadata(path)
|
||||||
|
|
||||||
|
@ -43,7 +43,10 @@ function OnboardingWithNewFile() {
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
const newFile = await createNewProject(defaultDirectory + sep + name)
|
const newFile = await createNewProject(
|
||||||
|
defaultDirectory + sep + name,
|
||||||
|
bracket
|
||||||
|
)
|
||||||
navigate(
|
navigate(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
newFile.path + sep + PROJECT_ENTRYPOINT
|
newFile.path + sep + PROJECT_ENTRYPOINT
|
||||||
@ -79,7 +82,7 @@ function OnboardingWithNewFile() {
|
|||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
kclManager.setCode(bracket)
|
kclManager.setCodeAndExecute(bracket)
|
||||||
next()
|
next()
|
||||||
}}
|
}}
|
||||||
icon={{ icon: faArrowRight }}
|
icon={{ icon: faArrowRight }}
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
} from 'lib/tauriFS'
|
} from 'lib/tauriFS'
|
||||||
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
import { bracket } from 'lib/exampleKcl'
|
||||||
|
|
||||||
export const Settings = () => {
|
export const Settings = () => {
|
||||||
const loaderData =
|
const loaderData =
|
||||||
@ -96,7 +97,10 @@ export const Settings = () => {
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
const newFile = await createNewProject(defaultDirectory + sep + name)
|
const newFile = await createNewProject(
|
||||||
|
defaultDirectory + sep + name,
|
||||||
|
bracket
|
||||||
|
)
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,11 +253,13 @@ export async function executeAst({
|
|||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
defaultPlanes,
|
defaultPlanes,
|
||||||
useFakeExecutor = false,
|
useFakeExecutor = false,
|
||||||
|
programMemoryOverride,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Program
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
defaultPlanes: DefaultPlanes
|
defaultPlanes: DefaultPlanes
|
||||||
useFakeExecutor?: boolean
|
useFakeExecutor?: boolean
|
||||||
|
programMemoryOverride?: ProgramMemory
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
logs: string[]
|
logs: string[]
|
||||||
errors: KCLError[]
|
errors: KCLError[]
|
||||||
@ -269,10 +271,13 @@ export async function executeAst({
|
|||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
}
|
}
|
||||||
const programMemory = await (useFakeExecutor
|
const programMemory = await (useFakeExecutor
|
||||||
? enginelessExecutor(ast, {
|
? enginelessExecutor(
|
||||||
root: defaultProgramMemory,
|
ast,
|
||||||
return: null,
|
programMemoryOverride || {
|
||||||
})
|
root: defaultProgramMemory,
|
||||||
|
return: null,
|
||||||
|
}
|
||||||
|
)
|
||||||
: _executor(
|
: _executor(
|
||||||
ast,
|
ast,
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user