- {title}
+
+ {title}
)
}
diff --git a/src/components/RenderViewerArtifacts.tsx b/src/components/RenderViewerArtifacts.tsx
index d1dcb7f06..07fe8ed76 100644
--- a/src/components/RenderViewerArtifacts.tsx
+++ b/src/components/RenderViewerArtifacts.tsx
@@ -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])
}
diff --git a/src/components/SketchPlane.tsx b/src/components/SketchPlane.tsx
index b434b674c..a521a05ff 100644
--- a/src/components/SketchPlane.tsx
+++ b/src/components/SketchPlane.tsx
@@ -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
}
diff --git a/src/hooks/useHotKeyListener.ts b/src/hooks/useHotKeyListener.ts
new file mode 100644
index 000000000..316ce1f5e
--- /dev/null
+++ b/src/hooks/useHotKeyListener.ts
@@ -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])
+}
diff --git a/src/hooks/useSetCursor.ts b/src/hooks/useSetCursor.ts
index 2a7008d26..002eabd5b 100644
--- a/src/hooks/useSetCursor.ts
+++ b/src/hooks/useSetCursor.ts
@@ -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()
diff --git a/src/lang/artifact.test.ts b/src/lang/artifact.test.ts
index d733c40da..199e67e01 100644
--- a/src/lang/artifact.test.ts
+++ b/src/lang/artifact.test.ts
@@ -190,7 +190,7 @@ show(theExtrude, sk2)`
0.9230002039112792,
],
__meta: [
- { sourceRange: [190, 218], pathToNode: [] },
+ { sourceRange: [203, 218], pathToNode: [] },
{ sourceRange: [13, 34], pathToNode: [] },
],
},
diff --git a/src/lang/executor.ts b/src/lang/executor.ts
index aae0cd89a..25029ac7f 100644
--- a/src/lang/executor.ts
+++ b/src/lang/executor.ts
@@ -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
diff --git a/src/lang/std/sketch.test.ts b/src/lang/std/sketch.test.ts
index 03ab90fe8..c5a6d0de5 100644
--- a/src/lang/std/sketch.test.ts
+++ b/src/lang/std/sketch.test.ts
@@ -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'],
diff --git a/src/useStore.ts b/src/useStore.ts
index 2d64b8f0d..08fc5ec24 100644
--- a/src/useStore.ts
+++ b/src/useStore.ts
@@ -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
()((set, get) => ({
@@ -118,27 +120,25 @@ export const useStore = create()((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()((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()((set, get) => ({
},
programMemory: { root: {}, _sketch: [] },
setProgramMemory: (programMemory) => set({ programMemory }),
+ isShiftDown: false,
+ setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
}))