Add multi-cursor support (#30)
* update execution of callExpressions * tweak types to store multiple cursor ranges and hook up with artifact highlighting * multi-cursor from 3d scene Working but has to be capslock for the time being * tweak pannel headers * add issue to todo comment
This commit is contained in:
43
src/App.tsx
43
src/App.tsx
@ -22,19 +22,20 @@ import { AxisIndicator } from './components/AxisIndicator'
|
||||
import { RenderViewerArtifacts } from './components/RenderViewerArtifacts'
|
||||
import { PanelHeader } from './components/PanelHeader'
|
||||
import { MemoryPanel } from './components/MemoryPanel'
|
||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||
|
||||
const OrrthographicCamera = OrthographicCamera as any
|
||||
|
||||
function App() {
|
||||
const cam = useRef()
|
||||
useHotKeyListener()
|
||||
const {
|
||||
editorView,
|
||||
setEditorView,
|
||||
setSelectionRange,
|
||||
selectionRange,
|
||||
setSelectionRanges,
|
||||
selectionRanges: selectionRange,
|
||||
guiMode,
|
||||
lastGuiMode,
|
||||
removeError,
|
||||
addLog,
|
||||
code,
|
||||
setCode,
|
||||
@ -48,11 +49,10 @@ function App() {
|
||||
} = useStore((s) => ({
|
||||
editorView: s.editorView,
|
||||
setEditorView: s.setEditorView,
|
||||
setSelectionRange: s.setSelectionRange,
|
||||
selectionRange: s.selectionRange,
|
||||
setSelectionRanges: s.setSelectionRanges,
|
||||
selectionRanges: s.selectionRanges,
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
removeError: s.removeError,
|
||||
addLog: s.addLog,
|
||||
code: s.code,
|
||||
setCode: s.setCode,
|
||||
@ -76,13 +76,16 @@ function App() {
|
||||
if (!editorView) {
|
||||
setEditorView(viewUpdate.view)
|
||||
}
|
||||
const range = viewUpdate.state.selection.ranges[0]
|
||||
// console.log(viewUpdate.state.selection.ranges)
|
||||
// TODO allow multiple cursors so that we can do constrain style features
|
||||
const isNoChange =
|
||||
range.from === selectionRange[0] && range.to === selectionRange[1]
|
||||
if (isNoChange) return
|
||||
setSelectionRange([range.from, range.to])
|
||||
const ranges = viewUpdate.state.selection.ranges
|
||||
|
||||
const isChange =
|
||||
ranges.length !== selectionRange.length ||
|
||||
ranges.some(({ from, to }, i) => {
|
||||
return from !== selectionRange[i][0] || to !== selectionRange[i][1]
|
||||
})
|
||||
|
||||
if (!isChange) return
|
||||
setSelectionRanges(ranges.map(({ from, to }) => [from, to]))
|
||||
}
|
||||
const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([])
|
||||
useEffect(() => {
|
||||
@ -90,7 +93,6 @@ function App() {
|
||||
if (!code) {
|
||||
setGeoArray([])
|
||||
setAst(null)
|
||||
removeError()
|
||||
return
|
||||
}
|
||||
const tokens = lexer(code)
|
||||
@ -129,7 +131,6 @@ function App() {
|
||||
.filter((a) => a) as (ExtrudeGroup | SketchGroup)[]
|
||||
|
||||
setGeoArray(geos)
|
||||
removeError()
|
||||
console.log(programMemory)
|
||||
setError()
|
||||
} catch (e: any) {
|
||||
@ -138,15 +139,15 @@ function App() {
|
||||
addLog(e)
|
||||
}
|
||||
}, [code])
|
||||
const shouldFormat = useMemo(() => {
|
||||
if (!ast) return false
|
||||
const recastedCode = recast(ast)
|
||||
return recastedCode !== code
|
||||
}, [code, ast])
|
||||
// const shouldFormat = useMemo(() => {
|
||||
// if (!ast) return false
|
||||
// const recastedCode = recast(ast)
|
||||
// return recastedCode !== code
|
||||
// }, [code, ast])
|
||||
return (
|
||||
<div className="h-screen">
|
||||
<Allotment snap={true}>
|
||||
<Allotment vertical defaultSizes={[4, 1, 1]}>
|
||||
<Allotment vertical defaultSizes={[4, 1, 1]} minSize={20}>
|
||||
<div className="h-full flex flex-col items-start">
|
||||
<PanelHeader title="Editor" />
|
||||
{/* <button
|
||||
|
@ -3,15 +3,21 @@ import { extrudeSketch, sketchOnExtrudedFace } from './lang/modifyAst'
|
||||
import { getNodePathFromSourceRange } from './lang/abstractSyntaxTree'
|
||||
|
||||
export const Toolbar = () => {
|
||||
const { setGuiMode, guiMode, selectionRange, ast, updateAst, programMemory } =
|
||||
useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
selectionRange: s.selectionRange,
|
||||
ast: s.ast,
|
||||
updateAst: s.updateAst,
|
||||
programMemory: s.programMemory,
|
||||
}))
|
||||
const {
|
||||
setGuiMode,
|
||||
guiMode,
|
||||
selectionRanges,
|
||||
ast,
|
||||
updateAst,
|
||||
programMemory,
|
||||
} = useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
selectionRanges: s.selectionRanges,
|
||||
ast: s.ast,
|
||||
updateAst: s.updateAst,
|
||||
programMemory: s.programMemory,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -32,7 +38,10 @@ export const Toolbar = () => {
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!ast) return
|
||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
ast,
|
||||
selectionRanges[0]
|
||||
)
|
||||
const { modifiedAst } = sketchOnExtrudedFace(
|
||||
ast,
|
||||
pathToNode,
|
||||
@ -54,7 +63,6 @@ export const Toolbar = () => {
|
||||
pathToNode: guiMode.pathToNode,
|
||||
rotation: guiMode.rotation,
|
||||
position: guiMode.position,
|
||||
isTooltip: true,
|
||||
})
|
||||
}}
|
||||
className="border m-1 px-1 rounded"
|
||||
@ -67,7 +75,10 @@ export const Toolbar = () => {
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!ast) return
|
||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
ast,
|
||||
selectionRanges[0]
|
||||
)
|
||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||
ast,
|
||||
pathToNode
|
||||
@ -81,7 +92,10 @@ export const Toolbar = () => {
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!ast) return
|
||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
ast,
|
||||
selectionRanges[0]
|
||||
)
|
||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||
ast,
|
||||
pathToNode,
|
||||
@ -105,7 +119,11 @@ export const Toolbar = () => {
|
||||
</button>
|
||||
)}
|
||||
{toolTips.map((sketchFnName) => {
|
||||
if (guiMode.mode !== 'sketch' || !('isTooltip' in guiMode)) return null
|
||||
if (
|
||||
guiMode.mode !== 'sketch' ||
|
||||
!('isTooltip' in guiMode || guiMode.sketchMode === 'sketchEdit')
|
||||
)
|
||||
return null
|
||||
return (
|
||||
<button
|
||||
key={sketchFnName}
|
||||
@ -115,10 +133,14 @@ export const Toolbar = () => {
|
||||
onClick={() =>
|
||||
setGuiMode({
|
||||
...guiMode,
|
||||
sketchMode:
|
||||
guiMode.sketchMode === sketchFnName
|
||||
? 'sketchEdit'
|
||||
: sketchFnName,
|
||||
...(guiMode.sketchMode === sketchFnName
|
||||
? {
|
||||
sketchMode: 'sketchEdit',
|
||||
}
|
||||
: {
|
||||
sketchMode: sketchFnName,
|
||||
isTooltip: true,
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -69,7 +69,6 @@ export const BasePlanes = () => {
|
||||
rotation: quaternion.toArray() as [number, number, number, number],
|
||||
position: [0, 0, 0],
|
||||
pathToNode,
|
||||
isTooltip: true,
|
||||
})
|
||||
|
||||
updateAst(modifiedAst)
|
||||
|
@ -1,7 +1,7 @@
|
||||
export const PanelHeader = ({ title }: { title: string }) => {
|
||||
return (
|
||||
<div className="font-mono text-xs bg-stone-100 w-full pl-4 h-[30px] text-stone-700 flex items-center">
|
||||
{title}
|
||||
<div className="font-mono text-[11px] bg-stone-100 w-full pl-4 h-[20px] text-stone-700 flex items-center">
|
||||
<span className="pt-1">{title}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -88,7 +88,9 @@ function MovingSphere({
|
||||
inverseQuaternion.set(...guiMode.rotation)
|
||||
inverseQuaternion.invert()
|
||||
}
|
||||
current2d.sub(new Vector3(...position).applyQuaternion(inverseQuaternion))
|
||||
current2d.sub(
|
||||
new Vector3(...position).applyQuaternion(inverseQuaternion)
|
||||
)
|
||||
let [x, y] = [roundOff(current2d.x, 2), roundOff(current2d.y, 2)]
|
||||
let theNewPoints: [number, number] = [x, y]
|
||||
const { modifiedAst } = changeSketchArguments(
|
||||
@ -284,10 +286,10 @@ function WallRender({
|
||||
rotation: Rotation
|
||||
position: Position
|
||||
}) {
|
||||
const { setHighlightRange, selectionRange } = useStore(
|
||||
({ setHighlightRange, selectionRange }) => ({
|
||||
const { setHighlightRange, selectionRanges } = useStore(
|
||||
({ setHighlightRange, selectionRanges }) => ({
|
||||
setHighlightRange,
|
||||
selectionRange,
|
||||
selectionRanges,
|
||||
})
|
||||
)
|
||||
const onClick = useSetCursor(geoInfo.__geoMeta.sourceRange)
|
||||
@ -297,12 +299,11 @@ function WallRender({
|
||||
|
||||
const [editorCursor, setEditorCursor] = useState(false)
|
||||
useEffect(() => {
|
||||
const shouldHighlight = isOverlap(
|
||||
geoInfo.__geoMeta.sourceRange,
|
||||
selectionRange
|
||||
const shouldHighlight = selectionRanges.some((range) =>
|
||||
isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
||||
)
|
||||
setEditorCursor(shouldHighlight)
|
||||
}, [selectionRange, geoInfo])
|
||||
}, [selectionRanges, geoInfo])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -347,17 +348,16 @@ function PathRender({
|
||||
rotation: Rotation
|
||||
position: Position
|
||||
}) {
|
||||
const { selectionRange } = useStore(({ selectionRange }) => ({
|
||||
selectionRange,
|
||||
const { selectionRanges } = useStore(({ selectionRanges }) => ({
|
||||
selectionRanges,
|
||||
}))
|
||||
const [editorCursor, setEditorCursor] = useState(false)
|
||||
useEffect(() => {
|
||||
const shouldHighlight = isOverlap(
|
||||
geoInfo.__geoMeta.sourceRange,
|
||||
selectionRange
|
||||
const shouldHighlight = selectionRanges.some((range) =>
|
||||
isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
||||
)
|
||||
setEditorCursor(shouldHighlight)
|
||||
}, [selectionRange, geoInfo])
|
||||
}, [selectionRanges, geoInfo])
|
||||
return (
|
||||
<>
|
||||
{geoInfo.__geoMeta.geos.map((meta, i) => {
|
||||
@ -404,8 +404,8 @@ function LineRender({
|
||||
rotation: Rotation
|
||||
position: Position
|
||||
}) {
|
||||
const { setHighlightRange } = useStore(({ setHighlightRange }) => ({
|
||||
setHighlightRange,
|
||||
const { setHighlightRange } = useStore((s) => ({
|
||||
setHighlightRange: s.setHighlightRange,
|
||||
}))
|
||||
const onClick = useSetCursor(sourceRange)
|
||||
// This reference will give us direct access to the mesh
|
||||
@ -440,9 +440,9 @@ function LineRender({
|
||||
type Artifact = ExtrudeGroup | SketchGroup
|
||||
|
||||
function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
||||
const { selectionRange, guiMode, setGuiMode, ast } = useStore(
|
||||
({ selectionRange, guiMode, setGuiMode, ast }) => ({
|
||||
selectionRange,
|
||||
const { selectionRanges, guiMode, setGuiMode, ast } = useStore(
|
||||
({ selectionRanges, guiMode, setGuiMode, ast }) => ({
|
||||
selectionRanges,
|
||||
guiMode,
|
||||
setGuiMode,
|
||||
ast,
|
||||
@ -469,7 +469,7 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
||||
)[] = []
|
||||
artifacts?.forEach((artifact) => {
|
||||
artifact.value.forEach((geo) => {
|
||||
if (isOverlap(geo.__geoMeta.sourceRange, selectionRange)) {
|
||||
if (isOverlap(geo.__geoMeta.sourceRange, selectionRanges[0])) {
|
||||
artifactsWithinCursorRange.push({
|
||||
parentType: artifact.type,
|
||||
isParent: false,
|
||||
@ -481,7 +481,7 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
||||
}
|
||||
})
|
||||
artifact.__meta.forEach((meta) => {
|
||||
if (isOverlap(meta.sourceRange, selectionRange)) {
|
||||
if (isOverlap(meta.sourceRange, selectionRanges[0])) {
|
||||
artifactsWithinCursorRange.push({
|
||||
parentType: artifact.type,
|
||||
isParent: true,
|
||||
@ -530,5 +530,5 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
||||
) {
|
||||
setGuiMode({ mode: 'default' })
|
||||
}
|
||||
}, [artifacts, selectionRange])
|
||||
}, [artifacts, selectionRanges])
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export const SketchPlane = () => {
|
||||
if (guiMode.mode !== 'sketch') {
|
||||
return null
|
||||
}
|
||||
if (!(guiMode.sketchMode === 'lineTo') && !('isTooltip' in guiMode)) {
|
||||
if (!(guiMode.sketchMode === 'sketchEdit') && !('isTooltip' in guiMode)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
21
src/hooks/useHotKeyListener.ts
Normal file
21
src/hooks/useHotKeyListener.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { useStore } from '../useStore'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export function useHotKeyListener() {
|
||||
const { setIsShiftDown } = useStore((s) => ({
|
||||
setIsShiftDown: s.setIsShiftDown,
|
||||
}))
|
||||
const keyName = 'CapsLock' // TODO #32 should be shift, but shift conflicts with the editor's use of the shift key atm.
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) =>
|
||||
event.key === keyName && setIsShiftDown(true)
|
||||
const handleKeyUp = (event: KeyboardEvent) =>
|
||||
event.key === keyName && setIsShiftDown(false)
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
}
|
||||
}, [setIsShiftDown])
|
||||
}
|
@ -1,9 +1,17 @@
|
||||
import { useStore } from '../useStore'
|
||||
import { useStore, Range } from '../useStore'
|
||||
|
||||
export function useSetCursor(sourceRange: [number, number]) {
|
||||
const setCursor = useStore((state) => state.setCursor)
|
||||
export function useSetCursor(sourceRange: Range) {
|
||||
const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({
|
||||
setCursor: s.setCursor,
|
||||
selectionRanges: s.selectionRanges,
|
||||
isShiftDown: s.isShiftDown,
|
||||
}))
|
||||
return () => {
|
||||
setCursor(sourceRange[1])
|
||||
console.log('isShiftDown', isShiftDown, selectionRanges, sourceRange)
|
||||
const ranges = isShiftDown
|
||||
? [...selectionRanges, sourceRange]
|
||||
: [sourceRange]
|
||||
setCursor(ranges)
|
||||
const element: HTMLDivElement | null = document.querySelector('.cm-content')
|
||||
if (element) {
|
||||
element.focus()
|
||||
|
@ -190,7 +190,7 @@ show(theExtrude, sk2)`
|
||||
0.9230002039112792,
|
||||
],
|
||||
__meta: [
|
||||
{ sourceRange: [190, 218], pathToNode: [] },
|
||||
{ sourceRange: [203, 218], pathToNode: [] },
|
||||
{ sourceRange: [13, 34], pathToNode: [] },
|
||||
],
|
||||
},
|
||||
|
@ -258,53 +258,19 @@ export const executor = (
|
||||
__meta,
|
||||
}
|
||||
} else if (declaration.init.type === 'CallExpression') {
|
||||
const functionName = declaration.init.callee.name
|
||||
const fnArgs = declaration.init.arguments.map((arg) => {
|
||||
if (arg.type === 'Literal') {
|
||||
return arg.value
|
||||
} else if (arg.type === 'Identifier') {
|
||||
return _programMemory.root[arg.name].value
|
||||
} else if (arg.type === 'ObjectExpression') {
|
||||
return executeObjectExpression(_programMemory, arg)
|
||||
} else if (arg.type === 'ArrayExpression') {
|
||||
return executeArrayExpression(_programMemory, arg)
|
||||
}
|
||||
throw new Error(
|
||||
`Unexpected argument type ${arg.type} in function call`
|
||||
)
|
||||
})
|
||||
if (functionName in internalFns) {
|
||||
const result = executeCallExpression(
|
||||
_programMemory,
|
||||
declaration.init,
|
||||
previousPathToNode,
|
||||
{
|
||||
sourceRangeOverride: [declaration.start, declaration.end],
|
||||
isInPipe: false,
|
||||
previousResults: [],
|
||||
expressionIndex: 0,
|
||||
body: [],
|
||||
}
|
||||
)
|
||||
if (
|
||||
result.type === 'extrudeGroup' ||
|
||||
result.type === 'sketchGroup'
|
||||
) {
|
||||
_programMemory.root[variableName] = result
|
||||
} else {
|
||||
_programMemory.root[variableName] = {
|
||||
type: 'userVal',
|
||||
value: result,
|
||||
__meta,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_programMemory.root[variableName] = {
|
||||
type: 'userVal',
|
||||
value: _programMemory.root[functionName].value(...fnArgs),
|
||||
__meta,
|
||||
}
|
||||
}
|
||||
const result = executeCallExpression(
|
||||
_programMemory,
|
||||
declaration.init,
|
||||
previousPathToNode
|
||||
)
|
||||
_programMemory.root[variableName] =
|
||||
result?.type === 'sketchGroup' || result?.type === 'extrudeGroup'
|
||||
? result
|
||||
: {
|
||||
type: 'userVal',
|
||||
value: result,
|
||||
__meta,
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unsupported declaration type: ' + declaration.init.type
|
||||
|
@ -118,7 +118,6 @@ show(mySketch001)`
|
||||
{
|
||||
mode: 'sketch',
|
||||
sketchMode: 'sketchEdit',
|
||||
isTooltip: true,
|
||||
rotation: [0, 0, 0, 1],
|
||||
position: [0, 0, 0],
|
||||
pathToNode: ['body', 0, 'declarations', '0', 'init'],
|
||||
|
@ -8,8 +8,10 @@ import {
|
||||
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor'
|
||||
import { recast } from './lang/recast'
|
||||
import { asyncLexer } from './lang/tokeniser'
|
||||
import { EditorSelection } from '@codemirror/state'
|
||||
|
||||
export type Range = [number, number]
|
||||
export type Ranges = Range[]
|
||||
export type TooTip =
|
||||
| 'lineTo'
|
||||
| 'line'
|
||||
@ -53,7 +55,6 @@ export type GuiModes =
|
||||
| {
|
||||
mode: 'sketch'
|
||||
sketchMode: 'sketchEdit'
|
||||
isTooltip: true
|
||||
rotation: Rotation
|
||||
position: Position
|
||||
pathToNode: PathToNode
|
||||
@ -80,13 +81,12 @@ interface StoreState {
|
||||
setEditorView: (editorView: EditorView) => void
|
||||
highlightRange: [number, number]
|
||||
setHighlightRange: (range: Range) => void
|
||||
setCursor: (start: number, end?: number) => void
|
||||
selectionRange: [number, number]
|
||||
setSelectionRange: (range: Range) => void
|
||||
setCursor: (selections: Ranges) => void
|
||||
selectionRanges: Ranges
|
||||
setSelectionRanges: (range: Ranges) => void
|
||||
guiMode: GuiModes
|
||||
lastGuiMode: GuiModes
|
||||
setGuiMode: (guiMode: GuiModes) => void
|
||||
removeError: () => void
|
||||
logs: string[]
|
||||
addLog: (log: string) => void
|
||||
resetLogs: () => void
|
||||
@ -103,6 +103,8 @@ interface StoreState {
|
||||
setError: (error?: string) => void
|
||||
programMemory: ProgramMemory
|
||||
setProgramMemory: (programMemory: ProgramMemory) => void
|
||||
isShiftDown: boolean
|
||||
setIsShiftDown: (isShiftDown: boolean) => void
|
||||
}
|
||||
|
||||
export const useStore = create<StoreState>()((set, get) => ({
|
||||
@ -118,27 +120,25 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) })
|
||||
}
|
||||
},
|
||||
setCursor: (start: number, end: number = start) => {
|
||||
const editorView = get().editorView
|
||||
setCursor: (ranges: Ranges) => {
|
||||
const { editorView } = get()
|
||||
if (!editorView) return
|
||||
editorView.dispatch({
|
||||
selection: { anchor: start, head: end },
|
||||
selection: EditorSelection.create(
|
||||
[...ranges.map(([start, end]) => EditorSelection.cursor(end))],
|
||||
ranges.length - 1
|
||||
),
|
||||
})
|
||||
},
|
||||
selectionRange: [0, 0],
|
||||
setSelectionRange: (selectionRange) => {
|
||||
set({ selectionRange })
|
||||
selectionRanges: [[0, 0]],
|
||||
setSelectionRanges: (selectionRanges) => {
|
||||
set({ selectionRanges })
|
||||
},
|
||||
guiMode: { mode: 'default' },
|
||||
lastGuiMode: { mode: 'default' },
|
||||
setGuiMode: (guiMode) => {
|
||||
const lastGuiMode = get().guiMode
|
||||
set({ guiMode })
|
||||
},
|
||||
removeError: () => {
|
||||
const lastGuiMode = get().lastGuiMode
|
||||
const currentGuiMode = get().guiMode
|
||||
},
|
||||
logs: [],
|
||||
addLog: (log) => {
|
||||
if (Array.isArray(log)) {
|
||||
@ -165,7 +165,7 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
const { start, end } = node
|
||||
if (!start || !end) return
|
||||
setTimeout(() => {
|
||||
get().setCursor(start, end)
|
||||
get().setCursor([[start, end]])
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -188,4 +188,6 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
},
|
||||
programMemory: { root: {}, _sketch: [] },
|
||||
setProgramMemory: (programMemory) => set({ programMemory }),
|
||||
isShiftDown: false,
|
||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
||||
}))
|
||||
|
Reference in New Issue
Block a user