clear old engine ids (#415)
* clear old engine ids * animate re-execute and deffer execution for user typing
This commit is contained in:
17
src/App.tsx
17
src/App.tsx
@ -95,6 +95,9 @@ export function App() {
|
|||||||
didDragInStream,
|
didDragInStream,
|
||||||
setStreamDimensions,
|
setStreamDimensions,
|
||||||
streamDimensions,
|
streamDimensions,
|
||||||
|
setIsExecuting,
|
||||||
|
defferedCode,
|
||||||
|
defferedSetCode,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
setEditorView: s.setEditorView,
|
setEditorView: s.setEditorView,
|
||||||
@ -103,7 +106,9 @@ export function App() {
|
|||||||
setGuiMode: s.setGuiMode,
|
setGuiMode: s.setGuiMode,
|
||||||
addLog: s.addLog,
|
addLog: s.addLog,
|
||||||
code: s.code,
|
code: s.code,
|
||||||
|
defferedCode: s.defferedCode,
|
||||||
setCode: s.setCode,
|
setCode: s.setCode,
|
||||||
|
defferedSetCode: s.defferedSetCode,
|
||||||
setAst: s.setAst,
|
setAst: s.setAst,
|
||||||
setError: s.setError,
|
setError: s.setError,
|
||||||
setProgramMemory: s.setProgramMemory,
|
setProgramMemory: s.setProgramMemory,
|
||||||
@ -132,6 +137,7 @@ export function App() {
|
|||||||
didDragInStream: s.didDragInStream,
|
didDragInStream: s.didDragInStream,
|
||||||
setStreamDimensions: s.setStreamDimensions,
|
setStreamDimensions: s.setStreamDimensions,
|
||||||
streamDimensions: s.streamDimensions,
|
streamDimensions: s.streamDimensions,
|
||||||
|
setIsExecuting: s.setIsExecuting,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -182,7 +188,7 @@ export function App() {
|
|||||||
|
|
||||||
// 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) => {
|
||||||
setCode(value)
|
defferedSetCode(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
|
||||||
@ -287,16 +293,17 @@ export function App() {
|
|||||||
let unsubFn: any[] = []
|
let unsubFn: any[] = []
|
||||||
const asyncWrap = async () => {
|
const asyncWrap = async () => {
|
||||||
try {
|
try {
|
||||||
if (!code) {
|
if (!defferedCode) {
|
||||||
setAst(null)
|
setAst(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const _ast = await asyncParser(code)
|
const _ast = await asyncParser(defferedCode)
|
||||||
setAst(_ast)
|
setAst(_ast)
|
||||||
resetLogs()
|
resetLogs()
|
||||||
resetKCLErrors()
|
resetKCLErrors()
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.endSession()
|
||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
|
setIsExecuting(true)
|
||||||
const programMemory = await _executor(
|
const programMemory = await _executor(
|
||||||
_ast,
|
_ast,
|
||||||
{
|
{
|
||||||
@ -328,6 +335,7 @@ export function App() {
|
|||||||
|
|
||||||
const { artifactMap, sourceRangeMap } =
|
const { artifactMap, sourceRangeMap } =
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
setIsExecuting(false)
|
||||||
|
|
||||||
setArtifactMap({ artifactMap, sourceRangeMap })
|
setArtifactMap({ artifactMap, sourceRangeMap })
|
||||||
const unSubHover = engineCommandManager.subscribeToUnreliable({
|
const unSubHover = engineCommandManager.subscribeToUnreliable({
|
||||||
@ -362,6 +370,7 @@ export function App() {
|
|||||||
|
|
||||||
setError()
|
setError()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
setIsExecuting(false)
|
||||||
if (e instanceof KCLError) {
|
if (e instanceof KCLError) {
|
||||||
addKCLError(e)
|
addKCLError(e)
|
||||||
} else {
|
} else {
|
||||||
@ -375,7 +384,7 @@ export function App() {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubFn.forEach((fn) => fn())
|
unsubFn.forEach((fn) => fn())
|
||||||
}
|
}
|
||||||
}, [code, isStreamReady, engineCommandManager])
|
}, [defferedCode, isStreamReady, engineCommandManager])
|
||||||
|
|
||||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||||
engineCommandManager?.sendSceneCommand(message)
|
engineCommandManager?.sendSceneCommand(message)
|
||||||
|
@ -21,6 +21,7 @@ export const Stream = ({ className = '' }) => {
|
|||||||
didDragInStream,
|
didDragInStream,
|
||||||
setDidDragInStream,
|
setDidDragInStream,
|
||||||
streamDimensions,
|
streamDimensions,
|
||||||
|
isExecuting,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
mediaStream: s.mediaStream,
|
mediaStream: s.mediaStream,
|
||||||
engineCommandManager: s.engineCommandManager,
|
engineCommandManager: s.engineCommandManager,
|
||||||
@ -30,6 +31,7 @@ export const Stream = ({ className = '' }) => {
|
|||||||
didDragInStream: s.didDragInStream,
|
didDragInStream: s.didDragInStream,
|
||||||
setDidDragInStream: s.setDidDragInStream,
|
setDidDragInStream: s.setDidDragInStream,
|
||||||
streamDimensions: s.streamDimensions,
|
streamDimensions: s.streamDimensions,
|
||||||
|
isExecuting: s.isExecuting,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -155,7 +157,8 @@ export const Stream = ({ className = '' }) => {
|
|||||||
onWheel={handleScroll}
|
onWheel={handleScroll}
|
||||||
onPlay={() => setIsLoading(false)}
|
onPlay={() => setIsLoading(false)}
|
||||||
onMouseMoveCapture={handleMouseMove}
|
onMouseMoveCapture={handleMouseMove}
|
||||||
className="w-full h-full"
|
className={`w-full h-full ${isExecuting && 'blur-md'}`}
|
||||||
|
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
||||||
/>
|
/>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||||
|
@ -10,11 +10,16 @@ import { exportSave } from 'lib/exportSave'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import * as Sentry from '@sentry/react'
|
import * as Sentry from '@sentry/react'
|
||||||
|
|
||||||
interface ResultCommand {
|
interface CommandInfo {
|
||||||
|
commandType: CommandTypes
|
||||||
|
range: SourceRange
|
||||||
|
parentId?: string
|
||||||
|
}
|
||||||
|
interface ResultCommand extends CommandInfo {
|
||||||
type: 'result'
|
type: 'result'
|
||||||
data: any
|
data: any
|
||||||
}
|
}
|
||||||
interface PendingCommand {
|
interface PendingCommand extends CommandInfo {
|
||||||
type: 'pending'
|
type: 'pending'
|
||||||
promise: Promise<any>
|
promise: Promise<any>
|
||||||
resolve: (val: any) => void
|
resolve: (val: any) => void
|
||||||
@ -546,6 +551,8 @@ export class EngineConnection {
|
|||||||
export type EngineCommand = Models['WebSocketRequest_type']
|
export type EngineCommand = Models['WebSocketRequest_type']
|
||||||
type ModelTypes = Models['OkModelingCmdResponse_type']['type']
|
type ModelTypes = Models['OkModelingCmdResponse_type']['type']
|
||||||
|
|
||||||
|
type CommandTypes = Models['ModelingCmd_type']['type']
|
||||||
|
|
||||||
type UnreliableResponses = Extract<
|
type UnreliableResponses = Extract<
|
||||||
Models['OkModelingCmdResponse_type'],
|
Models['OkModelingCmdResponse_type'],
|
||||||
{ type: 'highlight_set_entity' }
|
{ type: 'highlight_set_entity' }
|
||||||
@ -687,15 +694,22 @@ export class EngineCommandManager {
|
|||||||
const resolve = command.resolve
|
const resolve = command.resolve
|
||||||
this.artifactMap[id] = {
|
this.artifactMap[id] = {
|
||||||
type: 'result',
|
type: 'result',
|
||||||
|
range: command.range,
|
||||||
|
commandType: command.commandType,
|
||||||
|
parentId: command.parentId ? command.parentId : undefined,
|
||||||
data: modelingResponse,
|
data: modelingResponse,
|
||||||
}
|
}
|
||||||
resolve({
|
resolve({
|
||||||
id,
|
id,
|
||||||
|
commandType: command.commandType,
|
||||||
|
range: command.range,
|
||||||
data: modelingResponse,
|
data: modelingResponse,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.artifactMap[id] = {
|
this.artifactMap[id] = {
|
||||||
type: 'result',
|
type: 'result',
|
||||||
|
commandType: command?.commandType,
|
||||||
|
range: command?.range,
|
||||||
data: modelingResponse,
|
data: modelingResponse,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -747,8 +761,29 @@ export class EngineCommandManager {
|
|||||||
delete this.unreliableSubscriptions[event][id]
|
delete this.unreliableSubscriptions[event][id]
|
||||||
}
|
}
|
||||||
endSession() {
|
endSession() {
|
||||||
// this.websocket?.close()
|
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
|
||||||
// socket.off('command')
|
// we need to loop over them each individualy because if the engine doesn't recognise a single
|
||||||
|
// id the whole command fails.
|
||||||
|
Object.entries(this.artifactMap).forEach(([id, artifact]) => {
|
||||||
|
const artifactTypesToDelete: ArtifactMap[string]['commandType'][] = [
|
||||||
|
// 'start_path' creates a new scene object for the path, which is why it needs to be deleted,
|
||||||
|
// however all of the segments in the path are its children so there don't need to be deleted.
|
||||||
|
// this fact is very opaque in the api and docs (as to what should can be deleted).
|
||||||
|
// Using an array is the list is likely to grow.
|
||||||
|
'start_path',
|
||||||
|
]
|
||||||
|
if (!artifactTypesToDelete.includes(artifact.commandType)) return
|
||||||
|
|
||||||
|
const deletCmd: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'remove_scene_objects',
|
||||||
|
object_ids: [id],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.engineConnection?.send(deletCmd)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
cusorsSelected(selections: {
|
cusorsSelected(selections: {
|
||||||
otherSelections: Selections['otherSelections']
|
otherSelections: Selections['otherSelections']
|
||||||
@ -801,11 +836,20 @@ export class EngineCommandManager {
|
|||||||
JSON.stringify(command)
|
JSON.stringify(command)
|
||||||
)
|
)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
} else if (
|
||||||
|
cmd.type === 'mouse_move' &&
|
||||||
|
this.engineConnection.unreliableDataChannel
|
||||||
|
) {
|
||||||
|
cmd.sequence = this.outSequence
|
||||||
|
this.outSequence++
|
||||||
|
this.engineConnection?.unreliableDataChannel?.send(
|
||||||
|
JSON.stringify(command)
|
||||||
|
)
|
||||||
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
console.log('sending command', command)
|
|
||||||
// since it's not mouse drag or highlighting send over TCP and keep track of the command
|
// since it's not mouse drag or highlighting send over TCP and keep track of the command
|
||||||
this.engineConnection?.send(command)
|
this.engineConnection?.send(command)
|
||||||
return this.handlePendingCommand(command.cmd_id)
|
return this.handlePendingCommand(command.cmd_id, command.cmd)
|
||||||
}
|
}
|
||||||
sendModelingCommand({
|
sendModelingCommand({
|
||||||
id,
|
id,
|
||||||
@ -823,15 +867,35 @@ export class EngineCommandManager {
|
|||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
this.engineConnection?.send(command)
|
this.engineConnection?.send(command)
|
||||||
return this.handlePendingCommand(id)
|
if (typeof command !== 'string' && command.type === 'modeling_cmd_req') {
|
||||||
|
return this.handlePendingCommand(id, command?.cmd, range)
|
||||||
|
} else if (typeof command === 'string') {
|
||||||
|
const parseCommand: EngineCommand = JSON.parse(command)
|
||||||
|
if (parseCommand.type === 'modeling_cmd_req')
|
||||||
|
return this.handlePendingCommand(id, parseCommand?.cmd, range)
|
||||||
|
}
|
||||||
|
throw 'shouldnt reach here'
|
||||||
}
|
}
|
||||||
handlePendingCommand(id: string) {
|
handlePendingCommand(
|
||||||
|
id: string,
|
||||||
|
command: Models['ModelingCmd_type'],
|
||||||
|
range?: SourceRange
|
||||||
|
) {
|
||||||
let resolve: (val: any) => void = () => {}
|
let resolve: (val: any) => void = () => {}
|
||||||
const promise = new Promise((_resolve, reject) => {
|
const promise = new Promise((_resolve, reject) => {
|
||||||
resolve = _resolve
|
resolve = _resolve
|
||||||
})
|
})
|
||||||
|
const getParentId = (): string | undefined => {
|
||||||
|
if (command.type === 'extend_path') {
|
||||||
|
return command.path
|
||||||
|
}
|
||||||
|
// TODO handle other commands that have a parent
|
||||||
|
}
|
||||||
this.artifactMap[id] = {
|
this.artifactMap[id] = {
|
||||||
|
range: range || [0, 0],
|
||||||
type: 'pending',
|
type: 'pending',
|
||||||
|
commandType: command.type,
|
||||||
|
parentId: getParentId(),
|
||||||
promise,
|
promise,
|
||||||
resolve,
|
resolve,
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,27 @@ export function throttle<T>(
|
|||||||
return throttled
|
return throttled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
let timeout: ReturnType<typeof setTimeout> | null
|
||||||
|
let latestArgs: T
|
||||||
|
|
||||||
|
function later() {
|
||||||
|
timeout = null
|
||||||
|
func(latestArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deffered(args: T) {
|
||||||
|
latestArgs = args
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
timeout = setTimeout(later, wait)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deffered
|
||||||
|
}
|
||||||
|
|
||||||
export function getNormalisedCoordinates({
|
export function getNormalisedCoordinates({
|
||||||
clientX,
|
clientX,
|
||||||
clientY,
|
clientY,
|
||||||
|
369
src/useStore.ts
369
src/useStore.ts
@ -19,6 +19,7 @@ import {
|
|||||||
EngineCommandManager,
|
EngineCommandManager,
|
||||||
} from './lang/std/engineConnection'
|
} from './lang/std/engineConnection'
|
||||||
import { KCLError } from './lang/errors'
|
import { KCLError } from './lang/errors'
|
||||||
|
import { defferExecution } from 'lib/utils'
|
||||||
|
|
||||||
export type Selection = {
|
export type Selection = {
|
||||||
type: 'default' | 'line-end' | 'line-mid'
|
type: 'default' | 'line-end' | 'line-mid'
|
||||||
@ -132,7 +133,9 @@ export interface StoreState {
|
|||||||
) => void
|
) => void
|
||||||
updateAstAsync: (ast: Program, focusPath?: PathToNode) => void
|
updateAstAsync: (ast: Program, focusPath?: PathToNode) => void
|
||||||
code: string
|
code: string
|
||||||
|
defferedCode: string
|
||||||
setCode: (code: string) => void
|
setCode: (code: string) => void
|
||||||
|
defferedSetCode: (code: string) => void
|
||||||
formatCode: () => void
|
formatCode: () => void
|
||||||
errorState: {
|
errorState: {
|
||||||
isError: boolean
|
isError: boolean
|
||||||
@ -168,6 +171,8 @@ export interface StoreState {
|
|||||||
streamWidth: number
|
streamWidth: number
|
||||||
streamHeight: number
|
streamHeight: number
|
||||||
}) => void
|
}) => void
|
||||||
|
isExecuting: boolean
|
||||||
|
setIsExecuting: (isExecuting: boolean) => void
|
||||||
|
|
||||||
showHomeMenu: boolean
|
showHomeMenu: boolean
|
||||||
setHomeShowMenu: (showMenu: boolean) => void
|
setHomeShowMenu: (showMenu: boolean) => void
|
||||||
@ -186,187 +191,201 @@ let pendingAstUpdates: number[] = []
|
|||||||
|
|
||||||
export const useStore = create<StoreState>()(
|
export const useStore = create<StoreState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => {
|
||||||
editorView: null,
|
const setDefferedCode = defferExecution(
|
||||||
setEditorView: (editorView) => {
|
(code: string) => set({ defferedCode: code }),
|
||||||
set({ editorView })
|
600
|
||||||
},
|
)
|
||||||
highlightRange: [0, 0],
|
return {
|
||||||
setHighlightRange: (selection) => {
|
editorView: null,
|
||||||
set({ highlightRange: selection })
|
setEditorView: (editorView) => {
|
||||||
const editorView = get().editorView
|
set({ editorView })
|
||||||
if (editorView) {
|
},
|
||||||
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
highlightRange: [0, 0],
|
||||||
}
|
setHighlightRange: (selection) => {
|
||||||
},
|
set({ highlightRange: selection })
|
||||||
setCursor: (selections) => {
|
const editorView = get().editorView
|
||||||
const { editorView } = get()
|
if (editorView) {
|
||||||
if (!editorView) return
|
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
||||||
const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
|
|
||||||
const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {}
|
|
||||||
set({ selectionRangeTypeMap })
|
|
||||||
selections.codeBasedSelections.forEach(({ range, type }) => {
|
|
||||||
if (range?.[1]) {
|
|
||||||
ranges.push(EditorSelection.cursor(range[1]))
|
|
||||||
selectionRangeTypeMap[range[1]] = type
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
setTimeout(() => {
|
setCursor: (selections) => {
|
||||||
editorView.dispatch({
|
const { editorView } = get()
|
||||||
selection: EditorSelection.create(
|
if (!editorView) return
|
||||||
ranges,
|
const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
|
||||||
selections.codeBasedSelections.length - 1
|
const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {}
|
||||||
),
|
set({ selectionRangeTypeMap })
|
||||||
|
selections.codeBasedSelections.forEach(({ range, type }) => {
|
||||||
|
if (range?.[1]) {
|
||||||
|
ranges.push(EditorSelection.cursor(range[1]))
|
||||||
|
selectionRangeTypeMap[range[1]] = type
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
|
||||||
setCursor2: (codeSelections) => {
|
|
||||||
const currestSelections = get().selectionRanges
|
|
||||||
const code = get().code
|
|
||||||
if (!codeSelections) {
|
|
||||||
get().setCursor({
|
|
||||||
otherSelections: currestSelections.otherSelections,
|
|
||||||
codeBasedSelections: [
|
|
||||||
{ range: [0, code.length - 1], type: 'default' },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const selections: Selections = {
|
|
||||||
...currestSelections,
|
|
||||||
codeBasedSelections: get().isShiftDown
|
|
||||||
? [...currestSelections.codeBasedSelections, codeSelections]
|
|
||||||
: [codeSelections],
|
|
||||||
}
|
|
||||||
get().setCursor(selections)
|
|
||||||
},
|
|
||||||
selectionRangeTypeMap: {},
|
|
||||||
selectionRanges: {
|
|
||||||
otherSelections: [],
|
|
||||||
codeBasedSelections: [],
|
|
||||||
},
|
|
||||||
setSelectionRanges: (selectionRanges) =>
|
|
||||||
set({ selectionRanges, selectionRangeTypeMap: {} }),
|
|
||||||
guiMode: { mode: 'default' },
|
|
||||||
lastGuiMode: { mode: 'default' },
|
|
||||||
setGuiMode: (guiMode) => {
|
|
||||||
set({ guiMode })
|
|
||||||
},
|
|
||||||
logs: [],
|
|
||||||
addLog: (log) => {
|
|
||||||
if (Array.isArray(log)) {
|
|
||||||
const cleanLog: any = log.map(({ __geoMeta, ...rest }) => rest)
|
|
||||||
set((state) => ({ logs: [...state.logs, cleanLog] }))
|
|
||||||
} else {
|
|
||||||
set((state) => ({ logs: [...state.logs, log] }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetLogs: () => {
|
|
||||||
set({ logs: [] })
|
|
||||||
},
|
|
||||||
kclErrors: [],
|
|
||||||
addKCLError: (e) => {
|
|
||||||
set((state) => ({ kclErrors: [...state.kclErrors, e] }))
|
|
||||||
},
|
|
||||||
resetKCLErrors: () => {
|
|
||||||
set({ kclErrors: [] })
|
|
||||||
},
|
|
||||||
ast: null,
|
|
||||||
setAst: (ast) => {
|
|
||||||
set({ ast })
|
|
||||||
},
|
|
||||||
updateAst: async (ast, { focusPath, callBack = () => {} } = {}) => {
|
|
||||||
const newCode = recast(ast)
|
|
||||||
const astWithUpdatedSource = parser_wasm(newCode)
|
|
||||||
callBack(astWithUpdatedSource)
|
|
||||||
|
|
||||||
set({ ast: astWithUpdatedSource, code: newCode })
|
|
||||||
if (focusPath) {
|
|
||||||
const { node } = getNodeFromPath<any>(astWithUpdatedSource, focusPath)
|
|
||||||
const { start, end } = node
|
|
||||||
if (!start || !end) return
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
get().setCursor({
|
editorView.dispatch({
|
||||||
codeBasedSelections: [
|
selection: EditorSelection.create(
|
||||||
{
|
ranges,
|
||||||
type: 'default',
|
selections.codeBasedSelections.length - 1
|
||||||
range: [start, end],
|
),
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
setCursor2: (codeSelections) => {
|
||||||
updateAstAsync: async (ast, focusPath) => {
|
const currestSelections = get().selectionRanges
|
||||||
// clear any pending updates
|
const code = get().code
|
||||||
pendingAstUpdates.forEach((id) => clearTimeout(id))
|
if (!codeSelections) {
|
||||||
pendingAstUpdates = []
|
get().setCursor({
|
||||||
// setup a new update
|
otherSelections: currestSelections.otherSelections,
|
||||||
pendingAstUpdates.push(
|
codeBasedSelections: [
|
||||||
setTimeout(() => {
|
{ range: [0, code.length - 1], type: 'default' },
|
||||||
get().updateAst(ast, { focusPath })
|
],
|
||||||
}, 100) as unknown as number
|
})
|
||||||
)
|
return
|
||||||
},
|
}
|
||||||
code: '',
|
const selections: Selections = {
|
||||||
setCode: (code) => {
|
...currestSelections,
|
||||||
set({ code })
|
codeBasedSelections: get().isShiftDown
|
||||||
},
|
? [...currestSelections.codeBasedSelections, codeSelections]
|
||||||
formatCode: async () => {
|
: [codeSelections],
|
||||||
const code = get().code
|
}
|
||||||
const ast = parser_wasm(code)
|
get().setCursor(selections)
|
||||||
const newCode = recast(ast)
|
},
|
||||||
set({ code: newCode, ast })
|
selectionRangeTypeMap: {},
|
||||||
},
|
selectionRanges: {
|
||||||
errorState: {
|
otherSelections: [],
|
||||||
isError: false,
|
codeBasedSelections: [],
|
||||||
error: '',
|
},
|
||||||
},
|
setSelectionRanges: (selectionRanges) =>
|
||||||
setError: (error = '') => {
|
set({ selectionRanges, selectionRangeTypeMap: {} }),
|
||||||
set({ errorState: { isError: !!error, error } })
|
guiMode: { mode: 'default' },
|
||||||
},
|
lastGuiMode: { mode: 'default' },
|
||||||
programMemory: { root: {}, pendingMemory: {} },
|
setGuiMode: (guiMode) => {
|
||||||
setProgramMemory: (programMemory) => set({ programMemory }),
|
set({ guiMode })
|
||||||
isShiftDown: false,
|
},
|
||||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
logs: [],
|
||||||
artifactMap: {},
|
addLog: (log) => {
|
||||||
sourceRangeMap: {},
|
if (Array.isArray(log)) {
|
||||||
setArtifactNSourceRangeMaps: (maps) => set({ ...maps }),
|
const cleanLog: any = log.map(({ __geoMeta, ...rest }) => rest)
|
||||||
setEngineCommandManager: (engineCommandManager) =>
|
set((state) => ({ logs: [...state.logs, cleanLog] }))
|
||||||
set({ engineCommandManager }),
|
} else {
|
||||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
set((state) => ({ logs: [...state.logs, log] }))
|
||||||
isStreamReady: false,
|
}
|
||||||
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
},
|
||||||
isLSPServerReady: false,
|
resetLogs: () => {
|
||||||
setIsLSPServerReady: (isLSPServerReady) => set({ isLSPServerReady }),
|
set({ logs: [] })
|
||||||
isMouseDownInStream: false,
|
},
|
||||||
setIsMouseDownInStream: (isMouseDownInStream) => {
|
kclErrors: [],
|
||||||
set({ isMouseDownInStream })
|
addKCLError: (e) => {
|
||||||
},
|
set((state) => ({ kclErrors: [...state.kclErrors, e] }))
|
||||||
didDragInStream: false,
|
},
|
||||||
setDidDragInStream: (didDragInStream) => {
|
resetKCLErrors: () => {
|
||||||
set({ didDragInStream })
|
set({ kclErrors: [] })
|
||||||
},
|
},
|
||||||
// For stream event handling
|
ast: null,
|
||||||
fileId: '',
|
setAst: (ast) => {
|
||||||
setFileId: (fileId) => set({ fileId }),
|
set({ ast })
|
||||||
streamDimensions: { streamWidth: 1280, streamHeight: 720 },
|
},
|
||||||
setStreamDimensions: (streamDimensions) => set({ streamDimensions }),
|
updateAst: async (ast, { focusPath, callBack = () => {} } = {}) => {
|
||||||
|
const newCode = recast(ast)
|
||||||
|
const astWithUpdatedSource = parser_wasm(newCode)
|
||||||
|
callBack(astWithUpdatedSource)
|
||||||
|
|
||||||
// tauri specific app settings
|
set({ ast: astWithUpdatedSource, code: newCode })
|
||||||
defaultDir: {
|
if (focusPath) {
|
||||||
dir: '',
|
const { node } = getNodeFromPath<any>(
|
||||||
},
|
astWithUpdatedSource,
|
||||||
isBannerDismissed: false,
|
focusPath
|
||||||
setBannerDismissed: (isBannerDismissed) => set({ isBannerDismissed }),
|
)
|
||||||
openPanes: ['code'],
|
const { start, end } = node
|
||||||
setOpenPanes: (openPanes) => set({ openPanes }),
|
if (!start || !end) return
|
||||||
showHomeMenu: true,
|
setTimeout(() => {
|
||||||
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
get().setCursor({
|
||||||
homeMenuItems: [],
|
codeBasedSelections: [
|
||||||
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
{
|
||||||
}),
|
type: 'default',
|
||||||
|
range: [start, end],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateAstAsync: async (ast, focusPath) => {
|
||||||
|
// clear any pending updates
|
||||||
|
pendingAstUpdates.forEach((id) => clearTimeout(id))
|
||||||
|
pendingAstUpdates = []
|
||||||
|
// setup a new update
|
||||||
|
pendingAstUpdates.push(
|
||||||
|
setTimeout(() => {
|
||||||
|
get().updateAst(ast, { focusPath })
|
||||||
|
}, 100) as unknown as number
|
||||||
|
)
|
||||||
|
},
|
||||||
|
code: '',
|
||||||
|
defferedCode: '',
|
||||||
|
setCode: (code) => set({ code, defferedCode: code }),
|
||||||
|
defferedSetCode: (code) => {
|
||||||
|
set({ code })
|
||||||
|
setDefferedCode(code)
|
||||||
|
},
|
||||||
|
formatCode: async () => {
|
||||||
|
const code = get().code
|
||||||
|
const ast = parser_wasm(code)
|
||||||
|
const newCode = recast(ast)
|
||||||
|
set({ code: newCode, ast })
|
||||||
|
},
|
||||||
|
errorState: {
|
||||||
|
isError: false,
|
||||||
|
error: '',
|
||||||
|
},
|
||||||
|
setError: (error = '') => {
|
||||||
|
set({ errorState: { isError: !!error, error } })
|
||||||
|
},
|
||||||
|
programMemory: { root: {}, pendingMemory: {} },
|
||||||
|
setProgramMemory: (programMemory) => set({ programMemory }),
|
||||||
|
isShiftDown: false,
|
||||||
|
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
||||||
|
artifactMap: {},
|
||||||
|
sourceRangeMap: {},
|
||||||
|
setArtifactNSourceRangeMaps: (maps) => set({ ...maps }),
|
||||||
|
setEngineCommandManager: (engineCommandManager) =>
|
||||||
|
set({ engineCommandManager }),
|
||||||
|
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||||
|
isStreamReady: false,
|
||||||
|
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
||||||
|
isLSPServerReady: false,
|
||||||
|
setIsLSPServerReady: (isLSPServerReady) => set({ isLSPServerReady }),
|
||||||
|
isMouseDownInStream: false,
|
||||||
|
setIsMouseDownInStream: (isMouseDownInStream) => {
|
||||||
|
set({ isMouseDownInStream })
|
||||||
|
},
|
||||||
|
didDragInStream: false,
|
||||||
|
setDidDragInStream: (didDragInStream) => {
|
||||||
|
set({ didDragInStream })
|
||||||
|
},
|
||||||
|
// For stream event handling
|
||||||
|
fileId: '',
|
||||||
|
setFileId: (fileId) => set({ fileId }),
|
||||||
|
streamDimensions: { streamWidth: 1280, streamHeight: 720 },
|
||||||
|
setStreamDimensions: (streamDimensions) => set({ streamDimensions }),
|
||||||
|
isExecuting: false,
|
||||||
|
setIsExecuting: (isExecuting) => set({ isExecuting }),
|
||||||
|
|
||||||
|
// tauri specific app settings
|
||||||
|
defaultDir: {
|
||||||
|
dir: '',
|
||||||
|
},
|
||||||
|
isBannerDismissed: false,
|
||||||
|
setBannerDismissed: (isBannerDismissed) => set({ isBannerDismissed }),
|
||||||
|
openPanes: ['code'],
|
||||||
|
setOpenPanes: (openPanes) => set({ openPanes }),
|
||||||
|
showHomeMenu: true,
|
||||||
|
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
||||||
|
homeMenuItems: [],
|
||||||
|
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'store',
|
name: 'store',
|
||||||
partialize: (state) =>
|
partialize: (state) =>
|
||||||
|
Reference in New Issue
Block a user