inital rework of execution (#528)
* inital rework Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * update the program memory as well Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanups Signed-off-by: Jess Frazelle <github@jessfraz.com> * code Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates for typing code Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixing Signed-off-by: Jess Frazelle <github@jessfraz.com> * some fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * more fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * Only unselect line or move tool on escape, don't exit sketch * Make scrollbar on toolbar smaller * Add escape to exit sketch mode * tidy up usestore * clear scene on empty file * disable sketch mode and re-execute on sketch loop close * disable all but xy plane * fix entering back into edit mode --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: Frank Noirot <frank@kittycad.io> Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
38
src/App.tsx
38
src/App.tsx
@ -31,7 +31,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 { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||||
import { useCodeEval } from 'hooks/useCodeEval'
|
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
|
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
|
||||||
@ -47,8 +47,10 @@ export function App() {
|
|||||||
didDragInStream,
|
didDragInStream,
|
||||||
streamDimensions,
|
streamDimensions,
|
||||||
guiMode,
|
guiMode,
|
||||||
|
setGuiMode,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
guiMode: s.guiMode,
|
guiMode: s.guiMode,
|
||||||
|
setGuiMode: s.setGuiMode,
|
||||||
setCode: s.setCode,
|
setCode: s.setCode,
|
||||||
engineCommandManager: s.engineCommandManager,
|
engineCommandManager: s.engineCommandManager,
|
||||||
buttonDownInStream: s.buttonDownInStream,
|
buttonDownInStream: s.buttonDownInStream,
|
||||||
@ -82,6 +84,38 @@ export function App() {
|
|||||||
useHotkeys('shift + l', () => togglePane('logs'))
|
useHotkeys('shift + l', () => togglePane('logs'))
|
||||||
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
||||||
useHotkeys('shift + d', () => togglePane('debug'))
|
useHotkeys('shift + d', () => togglePane('debug'))
|
||||||
|
useHotkeys('esc', () => {
|
||||||
|
if (guiMode.mode === 'sketch') {
|
||||||
|
if (guiMode.sketchMode === 'selectFace') return
|
||||||
|
if (guiMode.sketchMode === 'sketchEdit') {
|
||||||
|
engineCommandManager?.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: { type: 'edit_mode_exit' },
|
||||||
|
})
|
||||||
|
setGuiMode({ mode: 'default' })
|
||||||
|
} else {
|
||||||
|
engineCommandManager?.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'set_tool',
|
||||||
|
tool: 'select',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
setGuiMode({
|
||||||
|
mode: 'sketch',
|
||||||
|
sketchMode: 'sketchEdit',
|
||||||
|
rotation: guiMode.rotation,
|
||||||
|
position: guiMode.position,
|
||||||
|
pathToNode: guiMode.pathToNode,
|
||||||
|
// todo: ...guiMod is adding isTooltip: true, will probably just fix with xstate migtaion
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setGuiMode({ mode: 'default' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const paneOpacity =
|
const paneOpacity =
|
||||||
onboardingStatus === onboardingPaths.CAMERA
|
onboardingStatus === onboardingPaths.CAMERA
|
||||||
@ -105,7 +139,7 @@ export function App() {
|
|||||||
}, [loadedCode, setCode])
|
}, [loadedCode, setCode])
|
||||||
|
|
||||||
useSetupEngineManager(streamRef, token)
|
useSetupEngineManager(streamRef, token)
|
||||||
useCodeEval()
|
useEngineConnectionSubscriptions()
|
||||||
|
|
||||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||||
engineCommandManager?.sendSceneCommand(message)
|
engineCommandManager?.sendSceneCommand(message)
|
||||||
|
@ -47,6 +47,15 @@
|
|||||||
@apply hover:bg-cool-20;
|
@apply hover:bg-cool-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.smallScrollbar::-webkit-scrollbar {
|
||||||
|
@apply h-0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallScrollbar {
|
||||||
|
@apply overflow-x-auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.dark) .popoverToggle {
|
:global(.dark) .popoverToggle {
|
||||||
@apply hover:bg-cool-90;
|
@apply hover:bg-cool-90;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ export const Toolbar = () => {
|
|||||||
updateAst,
|
updateAst,
|
||||||
programMemory,
|
programMemory,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
|
executeAst,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
guiMode: s.guiMode,
|
guiMode: s.guiMode,
|
||||||
setGuiMode: s.setGuiMode,
|
setGuiMode: s.setGuiMode,
|
||||||
@ -35,6 +36,7 @@ export const Toolbar = () => {
|
|||||||
updateAst: s.updateAst,
|
updateAst: s.updateAst,
|
||||||
programMemory: s.programMemory,
|
programMemory: s.programMemory,
|
||||||
engineCommandManager: s.engineCommandManager,
|
engineCommandManager: s.engineCommandManager,
|
||||||
|
executeAst: s.executeAst,
|
||||||
}))
|
}))
|
||||||
useAppMode()
|
useAppMode()
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ export const Toolbar = () => {
|
|||||||
|
|
||||||
function ToolbarButtons() {
|
function ToolbarButtons() {
|
||||||
return (
|
return (
|
||||||
<span className="overflow-x-auto">
|
<span className={styles.smallScrollbar}>
|
||||||
{guiMode.mode === 'default' && (
|
{guiMode.mode === 'default' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -70,7 +72,7 @@ export const Toolbar = () => {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst, true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
SketchOnFace
|
SketchOnFace
|
||||||
@ -113,7 +115,7 @@ export const Toolbar = () => {
|
|||||||
ast,
|
ast,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
updateAst(modifiedAst, { focusPath: pathToExtrudeArg })
|
updateAst(modifiedAst, true, { focusPath: pathToExtrudeArg })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
ExtrudeSketch
|
ExtrudeSketch
|
||||||
@ -130,7 +132,7 @@ export const Toolbar = () => {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
updateAst(modifiedAst, { focusPath: pathToExtrudeArg })
|
updateAst(modifiedAst, true, { focusPath: pathToExtrudeArg })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
ExtrudeSketch (w/o pipe)
|
ExtrudeSketch (w/o pipe)
|
||||||
@ -146,7 +148,14 @@ export const Toolbar = () => {
|
|||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: { type: 'edit_mode_exit' },
|
cmd: { type: 'edit_mode_exit' },
|
||||||
})
|
})
|
||||||
|
engineCommandManager?.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||||||
|
})
|
||||||
|
|
||||||
setGuiMode({ mode: 'default' })
|
setGuiMode({ mode: 'default' })
|
||||||
|
executeAst()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Exit sketch
|
Exit sketch
|
||||||
|
@ -220,9 +220,21 @@ export const Stream = ({ className = '' }) => {
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// Check if the sketch group already exists.
|
||||||
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
guiMode.pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
).node
|
||||||
|
const variableName = varDec?.id?.name
|
||||||
|
const sketchGroup = programMemory.root[variableName]
|
||||||
|
const isEditingExistingSketch =
|
||||||
|
sketchGroup?.type === 'SketchGroup' && sketchGroup.value.length
|
||||||
|
|
||||||
if (
|
if (
|
||||||
resp?.data?.data?.entities_modified?.length &&
|
resp?.data?.data?.entities_modified?.length &&
|
||||||
guiMode.waitingFirstClick
|
guiMode.waitingFirstClick &&
|
||||||
|
!isEditingExistingSketch
|
||||||
) {
|
) {
|
||||||
const curve = await engineCommandManager?.sendSceneCommand({
|
const curve = await engineCommandManager?.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
@ -250,10 +262,10 @@ export const Stream = ({ className = '' }) => {
|
|||||||
pathToNode: _pathToNode,
|
pathToNode: _pathToNode,
|
||||||
waitingFirstClick: false,
|
waitingFirstClick: false,
|
||||||
})
|
})
|
||||||
updateAst(_modifiedAst)
|
updateAst(_modifiedAst, false)
|
||||||
} else if (
|
} else if (
|
||||||
resp?.data?.data?.entities_modified?.length &&
|
resp?.data?.data?.entities_modified?.length &&
|
||||||
!guiMode.waitingFirstClick
|
(!guiMode.waitingFirstClick || isEditingExistingSketch)
|
||||||
) {
|
) {
|
||||||
const curve = await engineCommandManager?.sendSceneCommand({
|
const curve = await engineCommandManager?.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
@ -290,6 +302,7 @@ export const Stream = ({ className = '' }) => {
|
|||||||
fnName: 'line',
|
fnName: 'line',
|
||||||
pathToNode: guiMode.pathToNode,
|
pathToNode: guiMode.pathToNode,
|
||||||
}).modifiedAst
|
}).modifiedAst
|
||||||
|
updateAst(_modifiedAst, false)
|
||||||
} else {
|
} else {
|
||||||
_modifiedAst = addCloseToPipe({
|
_modifiedAst = addCloseToPipe({
|
||||||
node: ast,
|
node: ast,
|
||||||
@ -299,8 +312,15 @@ export const Stream = ({ className = '' }) => {
|
|||||||
setGuiMode({
|
setGuiMode({
|
||||||
mode: 'default',
|
mode: 'default',
|
||||||
})
|
})
|
||||||
|
engineCommandManager?.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'sketch_mode_disable',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
updateAst(_modifiedAst, true)
|
||||||
}
|
}
|
||||||
updateAst(_modifiedAst)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setDidDragInStream(false)
|
setDidDragInStream(false)
|
||||||
|
@ -50,7 +50,7 @@ export const TextEditor = ({
|
|||||||
const pathParams = useParams()
|
const pathParams = useParams()
|
||||||
const {
|
const {
|
||||||
code,
|
code,
|
||||||
defferedSetCode,
|
deferredSetCode,
|
||||||
editorView,
|
editorView,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
formatCode,
|
formatCode,
|
||||||
@ -60,10 +60,9 @@ export const TextEditor = ({
|
|||||||
setEditorView,
|
setEditorView,
|
||||||
setIsLSPServerReady,
|
setIsLSPServerReady,
|
||||||
setSelectionRanges,
|
setSelectionRanges,
|
||||||
sourceRangeMap,
|
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
code: s.code,
|
code: s.code,
|
||||||
defferedSetCode: s.defferedSetCode,
|
deferredSetCode: s.deferredSetCode,
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
engineCommandManager: s.engineCommandManager,
|
engineCommandManager: s.engineCommandManager,
|
||||||
formatCode: s.formatCode,
|
formatCode: s.formatCode,
|
||||||
@ -73,7 +72,6 @@ export const TextEditor = ({
|
|||||||
setEditorView: s.setEditorView,
|
setEditorView: s.setEditorView,
|
||||||
setIsLSPServerReady: s.setIsLSPServerReady,
|
setIsLSPServerReady: s.setIsLSPServerReady,
|
||||||
setSelectionRanges: s.setSelectionRanges,
|
setSelectionRanges: s.setSelectionRanges,
|
||||||
sourceRangeMap: s.sourceRangeMap,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -126,7 +124,7 @@ export const TextEditor = ({
|
|||||||
|
|
||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
||||||
defferedSetCode(value)
|
deferredSetCode(value)
|
||||||
if (isTauri() && pathParams.id) {
|
if (isTauri() && pathParams.id) {
|
||||||
// Save the file to disk
|
// Save the file to disk
|
||||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||||
@ -174,11 +172,11 @@ export const TextEditor = ({
|
|||||||
)
|
)
|
||||||
const idBasedSelections = codeBasedSelections
|
const idBasedSelections = codeBasedSelections
|
||||||
.map(({ type, range }) => {
|
.map(({ type, range }) => {
|
||||||
const hasOverlap = Object.entries(sourceRangeMap).filter(
|
const hasOverlap = Object.entries(
|
||||||
([_, sourceRange]) => {
|
engineCommandManager?.sourceRangeMap || {}
|
||||||
return isOverlap(sourceRange, range)
|
).filter(([_, sourceRange]) => {
|
||||||
}
|
return isOverlap(sourceRange, range)
|
||||||
)
|
})
|
||||||
if (hasOverlap.length) {
|
if (hasOverlap.length) {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
|
@ -82,7 +82,7 @@ export const EqualAngle = () => {
|
|||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
})
|
})
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -82,7 +82,7 @@ export const EqualLength = () => {
|
|||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
})
|
})
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -61,7 +61,7 @@ export const HorzVert = ({
|
|||||||
programMemory,
|
programMemory,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -154,7 +154,7 @@ export const Intersect = () => {
|
|||||||
initialVariableName: 'offset',
|
initialVariableName: 'offset',
|
||||||
} as any)
|
} as any)
|
||||||
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -182,7 +182,7 @@ export const Intersect = () => {
|
|||||||
)
|
)
|
||||||
_modifiedAst.body = newBody
|
_modifiedAst.body = newBody
|
||||||
}
|
}
|
||||||
updateAst(_modifiedAst, {
|
updateAst(_modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ export const RemoveConstrainingValues = () => {
|
|||||||
programMemory,
|
programMemory,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -124,7 +124,7 @@ export const SetAbsDistance = ({
|
|||||||
_modifiedAst.body = newBody
|
_modifiedAst.body = newBody
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAst(_modifiedAst, {
|
updateAst(_modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -113,7 +113,7 @@ export const SetAngleBetween = () => {
|
|||||||
initialVariableName: 'angle',
|
initialVariableName: 'angle',
|
||||||
} as any)
|
} as any)
|
||||||
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -141,7 +141,7 @@ export const SetAngleBetween = () => {
|
|||||||
)
|
)
|
||||||
_modifiedAst.body = newBody
|
_modifiedAst.body = newBody
|
||||||
}
|
}
|
||||||
updateAst(_modifiedAst, {
|
updateAst(_modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ export const SetHorzVertDistance = ({
|
|||||||
constraint === 'setHorzDistance' ? 'xDis' : 'yDis',
|
constraint === 'setHorzDistance' ? 'xDis' : 'yDis',
|
||||||
} as any))
|
} as any))
|
||||||
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
||||||
updateAst(modifiedAst, {
|
updateAst(modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +163,7 @@ export const SetHorzVertDistance = ({
|
|||||||
)
|
)
|
||||||
_modifiedAst.body = newBody
|
_modifiedAst.body = newBody
|
||||||
}
|
}
|
||||||
updateAst(_modifiedAst, {
|
updateAst(_modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ export const SetAngleLength = ({
|
|||||||
_modifiedAst.body = newBody
|
_modifiedAst.body = newBody
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAst(_modifiedAst, {
|
updateAst(_modifiedAst, true, {
|
||||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -11,8 +11,9 @@ import { isOverlap } from 'lib/utils'
|
|||||||
|
|
||||||
interface DefaultPlanes {
|
interface DefaultPlanes {
|
||||||
xy: string
|
xy: string
|
||||||
yz: string
|
// TODO re-enable
|
||||||
xz: string
|
// yz: string
|
||||||
|
// xz: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAppMode() {
|
export function useAppMode() {
|
||||||
@ -42,24 +43,26 @@ export function useAppMode() {
|
|||||||
y_axis: { x: 0, y: 1, z: 0 },
|
y_axis: { x: 0, y: 1, z: 0 },
|
||||||
color: { r: 0.7, g: 0.28, b: 0.28, a: 0.4 },
|
color: { r: 0.7, g: 0.28, b: 0.28, a: 0.4 },
|
||||||
})
|
})
|
||||||
const yz = createPlane(engineCommandManager, {
|
// TODO re-enable
|
||||||
x_axis: { x: 0, y: 1, z: 0 },
|
// const yz = createPlane(engineCommandManager, {
|
||||||
y_axis: { x: 0, y: 0, z: 1 },
|
// x_axis: { x: 0, y: 1, z: 0 },
|
||||||
color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
// y_axis: { x: 0, y: 0, z: 1 },
|
||||||
})
|
// color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
||||||
const xz = createPlane(engineCommandManager, {
|
// })
|
||||||
x_axis: { x: 1, y: 0, z: 0 },
|
// const xz = createPlane(engineCommandManager, {
|
||||||
y_axis: { x: 0, y: 0, z: 1 },
|
// x_axis: { x: 1, y: 0, z: 0 },
|
||||||
color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
// y_axis: { x: 0, y: 0, z: 1 },
|
||||||
})
|
// color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
||||||
setDefaultPlanes({ xy, yz, xz })
|
// })
|
||||||
|
setDefaultPlanes({ xy })
|
||||||
} else {
|
} else {
|
||||||
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, false)
|
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (guiMode.mode !== 'sketch' && defaultPlanes) {
|
if (guiMode.mode !== 'sketch' && defaultPlanes) {
|
||||||
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true)
|
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true)
|
||||||
} else if (guiMode.mode === 'default') {
|
}
|
||||||
|
if (guiMode.mode === 'default') {
|
||||||
const pathId =
|
const pathId =
|
||||||
engineCommandManager &&
|
engineCommandManager &&
|
||||||
isCursorInSketchCommandRange(
|
isCursorInSketchCommandRange(
|
||||||
@ -220,15 +223,17 @@ function isCursorInSketchCommandRange(
|
|||||||
([id, artifact]) =>
|
([id, artifact]) =>
|
||||||
selectionRanges.codeBasedSelections.some(
|
selectionRanges.codeBasedSelections.some(
|
||||||
(selection) =>
|
(selection) =>
|
||||||
Array.isArray(selection.range) &&
|
Array.isArray(selection?.range) &&
|
||||||
Array.isArray(artifact.range) &&
|
Array.isArray(artifact?.range) &&
|
||||||
isOverlap(selection.range, artifact.range) &&
|
isOverlap(selection.range, artifact.range) &&
|
||||||
(artifact.commandType === 'start_path' ||
|
(artifact.commandType === 'start_path' ||
|
||||||
artifact.commandType === 'extend_path' ||
|
artifact.commandType === 'extend_path' ||
|
||||||
'close_path')
|
artifact.commandType === 'close_path')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return overlapingEntries.length === 1 && overlapingEntries[0][1].parentId
|
return overlapingEntries.length && overlapingEntries[0][1].parentId
|
||||||
? overlapingEntries[0][1].parentId
|
? overlapingEntries[0][1].parentId
|
||||||
: false
|
: overlapingEntries.find(
|
||||||
|
([, artifact]) => artifact.commandType === 'start_path'
|
||||||
|
)?.[0] || false
|
||||||
}
|
}
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
import { useEffect } from 'react'
|
|
||||||
import { asyncParser } from '../lang/abstractSyntaxTree'
|
|
||||||
import { _executor } from '../lang/executor'
|
|
||||||
import { useStore } from '../useStore'
|
|
||||||
import { KCLError } from '../lang/errors'
|
|
||||||
|
|
||||||
// This recently moved out of app.tsx
|
|
||||||
// and is our old way of thinking that whenever the code changes we need to re-execute, instead of
|
|
||||||
// being more decisive about when and where we execute, its likey this custom hook will be
|
|
||||||
// refactored away entirely at some point
|
|
||||||
|
|
||||||
export function useCodeEval() {
|
|
||||||
const {
|
|
||||||
addLog,
|
|
||||||
addKCLError,
|
|
||||||
setAst,
|
|
||||||
setError,
|
|
||||||
setProgramMemory,
|
|
||||||
resetLogs,
|
|
||||||
resetKCLErrors,
|
|
||||||
setArtifactMap,
|
|
||||||
engineCommandManager,
|
|
||||||
highlightRange,
|
|
||||||
setHighlightRange,
|
|
||||||
setCursor2,
|
|
||||||
isStreamReady,
|
|
||||||
setIsExecuting,
|
|
||||||
defferedCode,
|
|
||||||
} = useStore((s) => ({
|
|
||||||
addLog: s.addLog,
|
|
||||||
defferedCode: s.defferedCode,
|
|
||||||
setAst: s.setAst,
|
|
||||||
setError: s.setError,
|
|
||||||
setProgramMemory: s.setProgramMemory,
|
|
||||||
resetLogs: s.resetLogs,
|
|
||||||
resetKCLErrors: s.resetKCLErrors,
|
|
||||||
setArtifactMap: s.setArtifactNSourceRangeMaps,
|
|
||||||
engineCommandManager: s.engineCommandManager,
|
|
||||||
highlightRange: s.highlightRange,
|
|
||||||
setHighlightRange: s.setHighlightRange,
|
|
||||||
setCursor2: s.setCursor2,
|
|
||||||
isStreamReady: s.isStreamReady,
|
|
||||||
addKCLError: s.addKCLError,
|
|
||||||
setIsExecuting: s.setIsExecuting,
|
|
||||||
}))
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isStreamReady) return
|
|
||||||
if (!engineCommandManager) return
|
|
||||||
let unsubFn: any[] = []
|
|
||||||
const asyncWrap = async () => {
|
|
||||||
try {
|
|
||||||
if (!defferedCode) {
|
|
||||||
setAst({
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
body: [],
|
|
||||||
nonCodeMeta: {
|
|
||||||
noneCodeNodes: {},
|
|
||||||
start: null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
setProgramMemory({ root: {}, return: null })
|
|
||||||
engineCommandManager.endSession()
|
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const _ast = await asyncParser(defferedCode)
|
|
||||||
setAst(_ast)
|
|
||||||
resetLogs()
|
|
||||||
resetKCLErrors()
|
|
||||||
engineCommandManager.endSession()
|
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
setIsExecuting(true)
|
|
||||||
const programMemory = await _executor(
|
|
||||||
_ast,
|
|
||||||
{
|
|
||||||
root: {
|
|
||||||
_0: {
|
|
||||||
type: 'UserVal',
|
|
||||||
value: 0,
|
|
||||||
__meta: [],
|
|
||||||
},
|
|
||||||
_90: {
|
|
||||||
type: 'UserVal',
|
|
||||||
value: 90,
|
|
||||||
__meta: [],
|
|
||||||
},
|
|
||||||
_180: {
|
|
||||||
type: 'UserVal',
|
|
||||||
value: 180,
|
|
||||||
__meta: [],
|
|
||||||
},
|
|
||||||
_270: {
|
|
||||||
type: 'UserVal',
|
|
||||||
value: 270,
|
|
||||||
__meta: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
return: null,
|
|
||||||
},
|
|
||||||
engineCommandManager
|
|
||||||
)
|
|
||||||
|
|
||||||
const { artifactMap, sourceRangeMap } =
|
|
||||||
await engineCommandManager.waitForAllCommands(_ast, programMemory)
|
|
||||||
setIsExecuting(false)
|
|
||||||
if (programMemory !== undefined) {
|
|
||||||
setProgramMemory(programMemory)
|
|
||||||
}
|
|
||||||
|
|
||||||
setArtifactMap({ artifactMap, sourceRangeMap })
|
|
||||||
const unSubHover = engineCommandManager.subscribeToUnreliable({
|
|
||||||
event: 'highlight_set_entity',
|
|
||||||
callback: ({ data }) => {
|
|
||||||
if (data?.entity_id) {
|
|
||||||
const sourceRange = sourceRangeMap[data.entity_id]
|
|
||||||
setHighlightRange(sourceRange)
|
|
||||||
} else if (
|
|
||||||
!highlightRange ||
|
|
||||||
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
|
||||||
) {
|
|
||||||
setHighlightRange([0, 0])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const unSubClick = engineCommandManager.subscribeTo({
|
|
||||||
event: 'select_with_point',
|
|
||||||
callback: ({ data }) => {
|
|
||||||
if (!data?.entity_id) {
|
|
||||||
setCursor2()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const sourceRange = sourceRangeMap[data.entity_id]
|
|
||||||
setCursor2({ range: sourceRange, type: 'default' })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
unsubFn.push(unSubHover, unSubClick)
|
|
||||||
|
|
||||||
setError()
|
|
||||||
} catch (e: any) {
|
|
||||||
setIsExecuting(false)
|
|
||||||
if (e instanceof KCLError) {
|
|
||||||
addKCLError(e)
|
|
||||||
} else {
|
|
||||||
setError('problem')
|
|
||||||
console.log(e)
|
|
||||||
addLog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asyncWrap()
|
|
||||||
return () => {
|
|
||||||
unsubFn.forEach((fn) => fn())
|
|
||||||
}
|
|
||||||
}, [defferedCode, isStreamReady, engineCommandManager])
|
|
||||||
}
|
|
50
src/hooks/useEngineConnectionSubscriptions.ts
Normal file
50
src/hooks/useEngineConnectionSubscriptions.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useEffect } from 'react'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
|
|
||||||
|
export function useEngineConnectionSubscriptions() {
|
||||||
|
const {
|
||||||
|
engineCommandManager,
|
||||||
|
setCursor2,
|
||||||
|
setHighlightRange,
|
||||||
|
highlightRange,
|
||||||
|
} = useStore((s) => ({
|
||||||
|
engineCommandManager: s.engineCommandManager,
|
||||||
|
setCursor2: s.setCursor2,
|
||||||
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
highlightRange: s.highlightRange,
|
||||||
|
}))
|
||||||
|
useEffect(() => {
|
||||||
|
if (!engineCommandManager) return
|
||||||
|
|
||||||
|
const unSubHover = engineCommandManager.subscribeToUnreliable({
|
||||||
|
event: 'highlight_set_entity',
|
||||||
|
callback: ({ data }) => {
|
||||||
|
if (data?.entity_id) {
|
||||||
|
const sourceRange =
|
||||||
|
engineCommandManager.sourceRangeMap[data.entity_id]
|
||||||
|
setHighlightRange(sourceRange)
|
||||||
|
} else if (
|
||||||
|
!highlightRange ||
|
||||||
|
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
||||||
|
) {
|
||||||
|
setHighlightRange([0, 0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const unSubClick = engineCommandManager.subscribeTo({
|
||||||
|
event: 'select_with_point',
|
||||||
|
callback: ({ data }) => {
|
||||||
|
if (!data?.entity_id) {
|
||||||
|
setCursor2()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const sourceRange = engineCommandManager.sourceRangeMap[data.entity_id]
|
||||||
|
setCursor2({ range: sourceRange, type: 'default' })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return () => {
|
||||||
|
unSubHover()
|
||||||
|
unSubClick()
|
||||||
|
}
|
||||||
|
}, [engineCommandManager, setCursor2, setHighlightRange, highlightRange])
|
||||||
|
}
|
@ -12,11 +12,13 @@ export function useSetupEngineManager(
|
|||||||
setMediaStream,
|
setMediaStream,
|
||||||
setIsStreamReady,
|
setIsStreamReady,
|
||||||
setStreamDimensions,
|
setStreamDimensions,
|
||||||
|
executeCode,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
setEngineCommandManager: s.setEngineCommandManager,
|
setEngineCommandManager: s.setEngineCommandManager,
|
||||||
setMediaStream: s.setMediaStream,
|
setMediaStream: s.setMediaStream,
|
||||||
setIsStreamReady: s.setIsStreamReady,
|
setIsStreamReady: s.setIsStreamReady,
|
||||||
setStreamDimensions: s.setStreamDimensions,
|
setStreamDimensions: s.setStreamDimensions,
|
||||||
|
executeCode: s.executeCode,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const streamWidth = streamRef?.current?.offsetWidth
|
const streamWidth = streamRef?.current?.offsetWidth
|
||||||
@ -41,6 +43,9 @@ export function useSetupEngineManager(
|
|||||||
token,
|
token,
|
||||||
})
|
})
|
||||||
setEngineCommandManager(eng)
|
setEngineCommandManager(eng)
|
||||||
|
eng.waitForReady.then(() => {
|
||||||
|
executeCode()
|
||||||
|
})
|
||||||
return () => {
|
return () => {
|
||||||
eng?.tearDown()
|
eng?.tearDown()
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ export function useConvertToVariable() {
|
|||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
|
|
||||||
updateAst(_modifiedAst)
|
updateAst(_modifiedAst, true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('e', e)
|
console.log('e', e)
|
||||||
}
|
}
|
||||||
|
@ -908,14 +908,14 @@ export class EngineCommandManager {
|
|||||||
}
|
}
|
||||||
private async fixIdMappings(ast: Program, programMemory: ProgramMemory) {
|
private async fixIdMappings(ast: Program, programMemory: ProgramMemory) {
|
||||||
/* This is a temporary solution since the cmd_ids that are sent through when
|
/* This is a temporary solution since the cmd_ids that are sent through when
|
||||||
sending 'extend_path' ids are not used as the segment ids.
|
sending 'extend_path' ids are not used as the segment ids.
|
||||||
|
|
||||||
We have a way to back fill them with 'path_get_info', however this relies on one
|
We have a way to back fill them with 'path_get_info', however this relies on one
|
||||||
the sketchGroup array and the segements array returned from the server to be in
|
the sketchGroup array and the segements array returned from the server to be in
|
||||||
the same length and order. plus it's super hacky, we first use the path_id to get
|
the same length and order. plus it's super hacky, we first use the path_id to get
|
||||||
the source range of the pipe expression then use the name of the variable to get
|
the source range of the pipe expression then use the name of the variable to get
|
||||||
the sketchGroup from programMemory.
|
the sketchGroup from programMemory.
|
||||||
|
|
||||||
I feel queezy about relying on all these steps to always line up.
|
I feel queezy about relying on all these steps to always line up.
|
||||||
We have also had to pollute this EngineCommandManager class with knowledge of both the ast and programMemory
|
We have also had to pollute this EngineCommandManager class with knowledge of both the ast and programMemory
|
||||||
We should get the cmd_ids to match with the segment ids and delete this method.
|
We should get the cmd_ids to match with the segment ids and delete this method.
|
||||||
|
@ -57,7 +57,7 @@ export function throttle<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// takes a function and executes it after the wait time, if the function is called again before the wait time is up, the timer is reset
|
// takes a function and executes it after the wait time, if the function is called again before the wait time is up, the timer is reset
|
||||||
export function defferExecution<T>(func: (args: T) => any, wait: number) {
|
export function deferExecution<T>(func: (args: T) => any, wait: number) {
|
||||||
let timeout: ReturnType<typeof setTimeout> | null
|
let timeout: ReturnType<typeof setTimeout> | null
|
||||||
let latestArgs: T
|
let latestArgs: T
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ export function defferExecution<T>(func: (args: T) => any, wait: number) {
|
|||||||
func(latestArgs)
|
func(latestArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
function deffered(args: T) {
|
function deferred(args: T) {
|
||||||
latestArgs = args
|
latestArgs = args
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
@ -74,7 +74,7 @@ export function defferExecution<T>(func: (args: T) => any, wait: number) {
|
|||||||
timeout = setTimeout(later, wait)
|
timeout = setTimeout(later, wait)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deffered
|
return deferred
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNormalisedCoordinates({
|
export function getNormalisedCoordinates({
|
||||||
|
308
src/useStore.ts
308
src/useStore.ts
@ -4,6 +4,7 @@ import { addLineHighlight, EditorView } from './editor/highlightextension'
|
|||||||
import { parser_wasm } from './lang/abstractSyntaxTree'
|
import { parser_wasm } from './lang/abstractSyntaxTree'
|
||||||
import { Program } from './lang/abstractSyntaxTreeTypes'
|
import { Program } from './lang/abstractSyntaxTreeTypes'
|
||||||
import { getNodeFromPath } from './lang/queryAst'
|
import { getNodeFromPath } from './lang/queryAst'
|
||||||
|
import { enginelessExecutor } from './lib/testHelpers'
|
||||||
import {
|
import {
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
Position,
|
Position,
|
||||||
@ -13,13 +14,10 @@ import {
|
|||||||
} from './lang/executor'
|
} from './lang/executor'
|
||||||
import { recast } from './lang/recast'
|
import { recast } from './lang/recast'
|
||||||
import { EditorSelection } from '@codemirror/state'
|
import { EditorSelection } from '@codemirror/state'
|
||||||
import {
|
import { EngineCommandManager } from './lang/std/engineConnection'
|
||||||
ArtifactMap,
|
|
||||||
SourceRangeMap,
|
|
||||||
EngineCommandManager,
|
|
||||||
} from './lang/std/engineConnection'
|
|
||||||
import { KCLError } from './lang/errors'
|
import { KCLError } from './lang/errors'
|
||||||
import { defferExecution } from 'lib/utils'
|
import { deferExecution } from 'lib/utils'
|
||||||
|
import { _executor } from './lang/executor'
|
||||||
|
|
||||||
export type Selection = {
|
export type Selection = {
|
||||||
type: 'default' | 'line-end' | 'line-mid'
|
type: 'default' | 'line-end' | 'line-mid'
|
||||||
@ -123,40 +121,37 @@ export interface StoreState {
|
|||||||
setGuiMode: (guiMode: GuiModes) => void
|
setGuiMode: (guiMode: GuiModes) => void
|
||||||
logs: string[]
|
logs: string[]
|
||||||
addLog: (log: string) => void
|
addLog: (log: string) => void
|
||||||
resetLogs: () => void
|
setLogs: (logs: string[]) => void
|
||||||
kclErrors: KCLError[]
|
kclErrors: KCLError[]
|
||||||
addKCLError: (err: KCLError) => void
|
addKCLError: (err: KCLError) => void
|
||||||
|
setErrors: (errors: KCLError[]) => void
|
||||||
resetKCLErrors: () => void
|
resetKCLErrors: () => void
|
||||||
ast: Program
|
ast: Program
|
||||||
setAst: (ast: Program) => void
|
setAst: (ast: Program) => void
|
||||||
|
executeAst: (ast?: Program) => void
|
||||||
|
executeAstMock: (ast?: Program) => void
|
||||||
updateAst: (
|
updateAst: (
|
||||||
ast: Program,
|
ast: Program,
|
||||||
|
execute: boolean,
|
||||||
optionalParams?: {
|
optionalParams?: {
|
||||||
focusPath?: PathToNode
|
focusPath?: PathToNode
|
||||||
callBack?: (ast: Program) => void
|
callBack?: (ast: Program) => void
|
||||||
}
|
}
|
||||||
) => void
|
) => void
|
||||||
updateAstAsync: (ast: Program, focusPath?: PathToNode) => void
|
updateAstAsync: (
|
||||||
|
ast: Program,
|
||||||
|
reexecute: boolean,
|
||||||
|
focusPath?: PathToNode
|
||||||
|
) => void
|
||||||
code: string
|
code: string
|
||||||
defferedCode: string
|
|
||||||
setCode: (code: string) => void
|
setCode: (code: string) => void
|
||||||
defferedSetCode: (code: string) => void
|
deferredSetCode: (code: string) => void
|
||||||
|
executeCode: (code?: string) => void
|
||||||
formatCode: () => void
|
formatCode: () => void
|
||||||
errorState: {
|
|
||||||
isError: boolean
|
|
||||||
error: string
|
|
||||||
}
|
|
||||||
setError: (error?: string) => void
|
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
setProgramMemory: (programMemory: ProgramMemory) => void
|
setProgramMemory: (programMemory: ProgramMemory) => void
|
||||||
isShiftDown: boolean
|
isShiftDown: boolean
|
||||||
setIsShiftDown: (isShiftDown: boolean) => void
|
setIsShiftDown: (isShiftDown: boolean) => void
|
||||||
artifactMap: ArtifactMap
|
|
||||||
sourceRangeMap: SourceRangeMap
|
|
||||||
setArtifactNSourceRangeMaps: (a: {
|
|
||||||
artifactMap: ArtifactMap
|
|
||||||
sourceRangeMap: SourceRangeMap
|
|
||||||
}) => void
|
|
||||||
engineCommandManager?: EngineCommandManager
|
engineCommandManager?: EngineCommandManager
|
||||||
setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void
|
setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void
|
||||||
mediaStream?: MediaStream
|
mediaStream?: MediaStream
|
||||||
@ -197,10 +192,13 @@ let pendingAstUpdates: number[] = []
|
|||||||
export const useStore = create<StoreState>()(
|
export const useStore = create<StoreState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => {
|
(set, get) => {
|
||||||
const setDefferedCode = defferExecution(
|
// We defer this so that likely our ast has caught up to the code.
|
||||||
(code: string) => set({ defferedCode: code }),
|
// If we are making changes that are not reflected in the ast, we
|
||||||
600
|
// should not be updating the ast.
|
||||||
)
|
const setDeferredCode = deferExecution((code: string) => {
|
||||||
|
set({ code })
|
||||||
|
get().executeCode(code)
|
||||||
|
}, 600)
|
||||||
return {
|
return {
|
||||||
editorView: null,
|
editorView: null,
|
||||||
setEditorView: (editorView) => {
|
setEditorView: (editorView) => {
|
||||||
@ -214,6 +212,22 @@ export const useStore = create<StoreState>()(
|
|||||||
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
executeCode: async (code) => {
|
||||||
|
const result = await executeCode({
|
||||||
|
code: code || get().code,
|
||||||
|
lastAst: get().ast,
|
||||||
|
engineCommandManager: get().engineCommandManager,
|
||||||
|
})
|
||||||
|
if (!result.isChange) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set({
|
||||||
|
ast: result.ast,
|
||||||
|
logs: result.logs,
|
||||||
|
kclErrors: result.errors,
|
||||||
|
programMemory: result.programMemory,
|
||||||
|
})
|
||||||
|
},
|
||||||
setCursor: (selections) => {
|
setCursor: (selections) => {
|
||||||
const { editorView } = get()
|
const { editorView } = get()
|
||||||
if (!editorView) return
|
if (!editorView) return
|
||||||
@ -243,7 +257,10 @@ export const useStore = create<StoreState>()(
|
|||||||
get().setCursor({
|
get().setCursor({
|
||||||
otherSelections: currestSelections.otherSelections,
|
otherSelections: currestSelections.otherSelections,
|
||||||
codeBasedSelections: [
|
codeBasedSelections: [
|
||||||
{ range: [0, code.length - 1], type: 'default' },
|
{
|
||||||
|
range: [0, code.length ? code.length - 1 : 0],
|
||||||
|
type: 'default',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -277,8 +294,8 @@ export const useStore = create<StoreState>()(
|
|||||||
set((state) => ({ logs: [...state.logs, log] }))
|
set((state) => ({ logs: [...state.logs, log] }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetLogs: () => {
|
setLogs: (logs) => {
|
||||||
set({ logs: [] })
|
set({ logs })
|
||||||
},
|
},
|
||||||
kclErrors: [],
|
kclErrors: [],
|
||||||
addKCLError: (e) => {
|
addKCLError: (e) => {
|
||||||
@ -287,6 +304,9 @@ export const useStore = create<StoreState>()(
|
|||||||
resetKCLErrors: () => {
|
resetKCLErrors: () => {
|
||||||
set({ kclErrors: [] })
|
set({ kclErrors: [] })
|
||||||
},
|
},
|
||||||
|
setErrors: (errors) => {
|
||||||
|
set({ kclErrors: errors })
|
||||||
|
},
|
||||||
ast: {
|
ast: {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
@ -299,7 +319,47 @@ export const useStore = create<StoreState>()(
|
|||||||
setAst: (ast) => {
|
setAst: (ast) => {
|
||||||
set({ ast })
|
set({ ast })
|
||||||
},
|
},
|
||||||
updateAst: async (ast, { focusPath, callBack = () => {} } = {}) => {
|
executeAst: async (ast) => {
|
||||||
|
const _ast = ast || get().ast
|
||||||
|
if (!get().isStreamReady) return
|
||||||
|
const engineCommandManager = get().engineCommandManager!
|
||||||
|
if (!engineCommandManager) return
|
||||||
|
|
||||||
|
set({ isExecuting: true })
|
||||||
|
const { logs, errors, programMemory } = await executeAst({
|
||||||
|
ast: _ast,
|
||||||
|
engineCommandManager,
|
||||||
|
})
|
||||||
|
set({
|
||||||
|
programMemory,
|
||||||
|
logs,
|
||||||
|
kclErrors: errors,
|
||||||
|
isExecuting: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
executeAstMock: async (ast) => {
|
||||||
|
const _ast = ast || get().ast
|
||||||
|
if (!get().isStreamReady) return
|
||||||
|
const engineCommandManager = get().engineCommandManager!
|
||||||
|
if (!engineCommandManager) return
|
||||||
|
|
||||||
|
const { logs, errors, programMemory } = await executeAst({
|
||||||
|
ast: _ast,
|
||||||
|
engineCommandManager,
|
||||||
|
useFakeExecutor: true,
|
||||||
|
})
|
||||||
|
set({
|
||||||
|
programMemory,
|
||||||
|
logs,
|
||||||
|
kclErrors: errors,
|
||||||
|
isExecuting: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateAst: async (
|
||||||
|
ast,
|
||||||
|
reexecute,
|
||||||
|
{ focusPath, callBack = () => {} } = {}
|
||||||
|
) => {
|
||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
const astWithUpdatedSource = parser_wasm(newCode)
|
const astWithUpdatedSource = parser_wasm(newCode)
|
||||||
callBack(astWithUpdatedSource)
|
callBack(astWithUpdatedSource)
|
||||||
@ -307,7 +367,6 @@ export const useStore = create<StoreState>()(
|
|||||||
set({
|
set({
|
||||||
ast: astWithUpdatedSource,
|
ast: astWithUpdatedSource,
|
||||||
code: newCode,
|
code: newCode,
|
||||||
defferedCode: newCode,
|
|
||||||
})
|
})
|
||||||
if (focusPath) {
|
if (focusPath) {
|
||||||
const { node } = getNodeFromPath<any>(
|
const { node } = getNodeFromPath<any>(
|
||||||
@ -328,24 +387,33 @@ export const useStore = create<StoreState>()(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reexecute) {
|
||||||
|
// Call execute on the set ast.
|
||||||
|
get().executeAst(astWithUpdatedSource)
|
||||||
|
} else {
|
||||||
|
// 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
|
||||||
|
// instead.
|
||||||
|
get().executeAstMock(astWithUpdatedSource)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateAstAsync: async (ast, focusPath) => {
|
updateAstAsync: async (ast, reexecute, focusPath) => {
|
||||||
// clear any pending updates
|
// clear any pending updates
|
||||||
pendingAstUpdates.forEach((id) => clearTimeout(id))
|
pendingAstUpdates.forEach((id) => clearTimeout(id))
|
||||||
pendingAstUpdates = []
|
pendingAstUpdates = []
|
||||||
// setup a new update
|
// setup a new update
|
||||||
pendingAstUpdates.push(
|
pendingAstUpdates.push(
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
get().updateAst(ast, { focusPath })
|
get().updateAst(ast, reexecute, { focusPath })
|
||||||
}, 100) as unknown as number
|
}, 100) as unknown as number
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
code: '',
|
code: '',
|
||||||
defferedCode: '',
|
setCode: (code) => set({ code }),
|
||||||
setCode: (code) => set({ code, defferedCode: code }),
|
deferredSetCode: (code) => {
|
||||||
defferedSetCode: (code) => {
|
|
||||||
set({ code })
|
set({ code })
|
||||||
setDefferedCode(code)
|
setDeferredCode(code)
|
||||||
},
|
},
|
||||||
formatCode: async () => {
|
formatCode: async () => {
|
||||||
const code = get().code
|
const code = get().code
|
||||||
@ -353,20 +421,10 @@ export const useStore = create<StoreState>()(
|
|||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
set({ code: newCode, ast })
|
set({ code: newCode, ast })
|
||||||
},
|
},
|
||||||
errorState: {
|
|
||||||
isError: false,
|
|
||||||
error: '',
|
|
||||||
},
|
|
||||||
setError: (error = '') => {
|
|
||||||
set({ errorState: { isError: !!error, error } })
|
|
||||||
},
|
|
||||||
programMemory: { root: {}, return: null },
|
programMemory: { root: {}, return: null },
|
||||||
setProgramMemory: (programMemory) => set({ programMemory }),
|
setProgramMemory: (programMemory) => set({ programMemory }),
|
||||||
isShiftDown: false,
|
isShiftDown: false,
|
||||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
||||||
artifactMap: {},
|
|
||||||
sourceRangeMap: {},
|
|
||||||
setArtifactNSourceRangeMaps: (maps) => set({ ...maps }),
|
|
||||||
setEngineCommandManager: (engineCommandManager) =>
|
setEngineCommandManager: (engineCommandManager) =>
|
||||||
set({ engineCommandManager }),
|
set({ engineCommandManager }),
|
||||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||||
@ -409,9 +467,165 @@ export const useStore = create<StoreState>()(
|
|||||||
partialize: (state) =>
|
partialize: (state) =>
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
Object.entries(state).filter(([key]) =>
|
Object.entries(state).filter(([key]) =>
|
||||||
['code', 'defferedCode', 'openPanes'].includes(key)
|
['code', 'openPanes'].includes(key)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultProgramMemory: ProgramMemory['root'] = {
|
||||||
|
_0: {
|
||||||
|
type: 'UserVal',
|
||||||
|
value: 0,
|
||||||
|
__meta: [],
|
||||||
|
},
|
||||||
|
_90: {
|
||||||
|
type: 'UserVal',
|
||||||
|
value: 90,
|
||||||
|
__meta: [],
|
||||||
|
},
|
||||||
|
_180: {
|
||||||
|
type: 'UserVal',
|
||||||
|
value: 180,
|
||||||
|
__meta: [],
|
||||||
|
},
|
||||||
|
_270: {
|
||||||
|
type: 'UserVal',
|
||||||
|
value: 270,
|
||||||
|
__meta: [],
|
||||||
|
},
|
||||||
|
PI: {
|
||||||
|
type: 'UserVal',
|
||||||
|
value: Math.PI,
|
||||||
|
__meta: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeCode({
|
||||||
|
engineCommandManager,
|
||||||
|
code,
|
||||||
|
lastAst,
|
||||||
|
}: {
|
||||||
|
code: string
|
||||||
|
lastAst: Program
|
||||||
|
engineCommandManager?: EngineCommandManager
|
||||||
|
}): Promise<
|
||||||
|
| {
|
||||||
|
logs: string[]
|
||||||
|
errors: KCLError[]
|
||||||
|
programMemory: ProgramMemory
|
||||||
|
ast: Program
|
||||||
|
isChange: true
|
||||||
|
}
|
||||||
|
| { isChange: false }
|
||||||
|
> {
|
||||||
|
let ast: Program
|
||||||
|
try {
|
||||||
|
ast = parser_wasm(code)
|
||||||
|
} catch (e) {
|
||||||
|
let errors: KCLError[] = []
|
||||||
|
let logs: string[] = [JSON.stringify(e)]
|
||||||
|
if (e instanceof KCLError) {
|
||||||
|
errors = [e]
|
||||||
|
logs = []
|
||||||
|
if (e.msg === 'file is empty') engineCommandManager?.endSession()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isChange: true,
|
||||||
|
logs,
|
||||||
|
errors,
|
||||||
|
programMemory: {
|
||||||
|
root: {},
|
||||||
|
return: null,
|
||||||
|
},
|
||||||
|
ast: {
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
body: [],
|
||||||
|
nonCodeMeta: {
|
||||||
|
noneCodeNodes: {},
|
||||||
|
start: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the ast we have is equal to the ast in the storage.
|
||||||
|
// If it is, we don't need to update the ast.
|
||||||
|
if (!engineCommandManager || JSON.stringify(ast) === JSON.stringify(lastAst))
|
||||||
|
return { isChange: false }
|
||||||
|
|
||||||
|
const { logs, errors, programMemory } = await executeAst({
|
||||||
|
ast,
|
||||||
|
engineCommandManager,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
ast,
|
||||||
|
logs,
|
||||||
|
errors,
|
||||||
|
programMemory,
|
||||||
|
isChange: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeAst({
|
||||||
|
ast,
|
||||||
|
engineCommandManager,
|
||||||
|
useFakeExecutor = false,
|
||||||
|
}: {
|
||||||
|
ast: Program
|
||||||
|
engineCommandManager: EngineCommandManager
|
||||||
|
useFakeExecutor?: boolean
|
||||||
|
}): Promise<{
|
||||||
|
logs: string[]
|
||||||
|
errors: KCLError[]
|
||||||
|
programMemory: ProgramMemory
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
if (!useFakeExecutor) {
|
||||||
|
engineCommandManager.endSession()
|
||||||
|
engineCommandManager.startNewSession()
|
||||||
|
}
|
||||||
|
const programMemory = await (useFakeExecutor
|
||||||
|
? enginelessExecutor(ast, {
|
||||||
|
root: defaultProgramMemory,
|
||||||
|
return: null,
|
||||||
|
})
|
||||||
|
: _executor(
|
||||||
|
ast,
|
||||||
|
{
|
||||||
|
root: defaultProgramMemory,
|
||||||
|
return: null,
|
||||||
|
},
|
||||||
|
engineCommandManager
|
||||||
|
))
|
||||||
|
|
||||||
|
await engineCommandManager.waitForAllCommands(ast, programMemory)
|
||||||
|
return {
|
||||||
|
logs: [],
|
||||||
|
errors: [],
|
||||||
|
programMemory,
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e instanceof KCLError) {
|
||||||
|
return {
|
||||||
|
errors: [e],
|
||||||
|
logs: [],
|
||||||
|
programMemory: {
|
||||||
|
root: {},
|
||||||
|
return: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(e)
|
||||||
|
return {
|
||||||
|
logs: [e],
|
||||||
|
errors: [],
|
||||||
|
programMemory: {
|
||||||
|
root: {},
|
||||||
|
return: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user