2023-08-06 21:29:26 -04:00
|
|
|
import {
|
|
|
|
useRef,
|
|
|
|
useEffect,
|
2023-08-09 20:49:10 +10:00
|
|
|
useLayoutEffect,
|
2023-08-06 21:29:26 -04:00
|
|
|
useMemo,
|
|
|
|
useCallback,
|
|
|
|
MouseEventHandler,
|
|
|
|
} from 'react'
|
2023-07-21 16:53:06 +10:00
|
|
|
import { DebugPanel } from './components/DebugPanel'
|
2023-08-06 21:29:26 -04:00
|
|
|
import { v4 as uuidv4 } from 'uuid'
|
2023-08-18 19:37:52 +10:00
|
|
|
import { asyncParser } from './lang/abstractSyntaxTree'
|
2023-08-08 12:39:11 -04:00
|
|
|
import { _executor } from './lang/executor'
|
2023-09-06 21:27:30 -04:00
|
|
|
import CodeMirror, { Extension } from '@uiw/react-codemirror'
|
2023-08-08 10:50:27 +10:00
|
|
|
import { linter, lintGutter } from '@codemirror/lint'
|
2023-09-05 16:02:27 -07:00
|
|
|
import { ViewUpdate, EditorView } from '@codemirror/view'
|
2022-11-26 05:13:07 +11:00
|
|
|
import {
|
|
|
|
lineHighlightField,
|
|
|
|
addLineHighlight,
|
2022-11-26 08:34:23 +11:00
|
|
|
} from './editor/highlightextension'
|
2023-08-28 20:31:49 -04:00
|
|
|
import { PaneType, Selections, useStore } from './useStore'
|
2023-09-05 16:02:27 -07:00
|
|
|
import Server from './editor/lsp/server'
|
|
|
|
import Client from './editor/lsp/client'
|
2023-07-26 14:10:30 -05:00
|
|
|
import { Logs, KCLErrors } from './components/Logs'
|
2023-08-06 21:29:26 -04:00
|
|
|
import { CollapsiblePanel } from './components/CollapsiblePanel'
|
2023-02-03 10:04:16 +11:00
|
|
|
import { MemoryPanel } from './components/MemoryPanel'
|
2023-02-21 10:28:34 +11:00
|
|
|
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
2023-03-06 20:13:34 +11:00
|
|
|
import { Stream } from './components/Stream'
|
2023-03-07 15:45:59 +11:00
|
|
|
import ModalContainer from 'react-modal-promise'
|
2023-09-05 16:02:27 -07:00
|
|
|
import { FromServer, IntoServer } from './editor/lsp/codec'
|
2023-08-06 21:29:26 -04:00
|
|
|
import {
|
|
|
|
EngineCommand,
|
|
|
|
EngineCommandManager,
|
|
|
|
} from './lang/std/engineConnection'
|
|
|
|
import { isOverlap, throttle } from './lib/utils'
|
2023-07-13 07:22:08 -04:00
|
|
|
import { AppHeader } from './components/AppHeader'
|
2023-08-03 15:56:11 -05:00
|
|
|
import { KCLError, kclErrToDiagnostic } from './lang/errors'
|
2023-08-06 21:29:26 -04:00
|
|
|
import { Resizable } from 're-resizable'
|
|
|
|
import {
|
|
|
|
faCode,
|
|
|
|
faCodeCommit,
|
|
|
|
faSquareRootVariable,
|
|
|
|
} from '@fortawesome/free-solid-svg-icons'
|
|
|
|
import { useHotkeys } from 'react-hotkeys-hook'
|
2023-08-08 10:50:27 +10:00
|
|
|
import { TEST } from './env'
|
2023-08-09 20:49:10 +10:00
|
|
|
import { getNormalisedCoordinates } from './lib/utils'
|
2023-08-28 20:31:49 -04:00
|
|
|
import { Themes, getSystemTheme } from './lib/theme'
|
2023-08-15 21:56:24 -04:00
|
|
|
import { isTauri } from './lib/isTauri'
|
|
|
|
import { useLoaderData, useParams } from 'react-router-dom'
|
|
|
|
import { writeTextFile } from '@tauri-apps/api/fs'
|
2023-08-18 10:27:01 -04:00
|
|
|
import { PROJECT_ENTRYPOINT } from './lib/tauriFS'
|
2023-08-15 21:56:24 -04:00
|
|
|
import { IndexLoaderData } from './Router'
|
|
|
|
import { toast } from 'react-hot-toast'
|
2023-08-29 10:48:55 -04:00
|
|
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
2023-08-31 16:08:15 -04:00
|
|
|
import { onboardingPaths } from 'routes/Onboarding'
|
2023-09-05 16:02:27 -07:00
|
|
|
import { LanguageServerClient } from 'editor/lsp'
|
|
|
|
import kclLanguage from 'editor/lsp/language'
|
2023-09-06 21:27:30 -04:00
|
|
|
import { CSSRuleObject } from 'tailwindcss/types/config'
|
2022-11-22 09:06:08 +11:00
|
|
|
|
2023-06-22 16:43:33 +10:00
|
|
|
export function App() {
|
2023-08-18 10:27:01 -04:00
|
|
|
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
|
2023-08-15 21:56:24 -04:00
|
|
|
const pathParams = useParams()
|
2023-08-09 20:49:10 +10:00
|
|
|
const streamRef = useRef<HTMLDivElement>(null)
|
2023-02-21 10:28:34 +11:00
|
|
|
useHotKeyListener()
|
2022-11-27 14:06:33 +11:00
|
|
|
const {
|
|
|
|
editorView,
|
|
|
|
setEditorView,
|
2023-02-21 10:28:34 +11:00
|
|
|
setSelectionRanges,
|
2023-06-22 16:43:33 +10:00
|
|
|
selectionRanges,
|
2022-11-27 14:06:33 +11:00
|
|
|
addLog,
|
2023-07-26 14:10:30 -05:00
|
|
|
addKCLError,
|
2022-11-28 09:37:46 +11:00
|
|
|
code,
|
|
|
|
setCode,
|
|
|
|
setAst,
|
2022-12-06 05:40:05 +11:00
|
|
|
setError,
|
2023-01-04 01:28:26 +11:00
|
|
|
setProgramMemory,
|
2023-02-03 11:09:09 +11:00
|
|
|
resetLogs,
|
2023-07-26 18:16:20 -05:00
|
|
|
resetKCLErrors,
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRangeTypeMap,
|
2023-06-22 16:43:33 +10:00
|
|
|
setArtifactMap,
|
2023-08-09 20:49:10 +10:00
|
|
|
engineCommandManager,
|
2023-06-22 16:43:33 +10:00
|
|
|
setEngineCommandManager,
|
2023-09-06 01:32:53 -04:00
|
|
|
highlightRange,
|
2023-06-22 16:43:33 +10:00
|
|
|
setHighlightRange,
|
|
|
|
setCursor2,
|
|
|
|
sourceRangeMap,
|
|
|
|
setMediaStream,
|
2023-06-23 14:19:15 +10:00
|
|
|
setIsStreamReady,
|
|
|
|
isStreamReady,
|
2023-09-05 16:02:27 -07:00
|
|
|
isLSPServerReady,
|
|
|
|
setIsLSPServerReady,
|
2023-08-06 21:29:26 -04:00
|
|
|
isMouseDownInStream,
|
2023-07-20 19:25:04 -04:00
|
|
|
formatCode,
|
2023-08-06 21:29:26 -04:00
|
|
|
openPanes,
|
|
|
|
setOpenPanes,
|
2023-08-18 10:27:01 -04:00
|
|
|
didDragInStream,
|
2023-08-09 20:49:10 +10:00
|
|
|
setStreamDimensions,
|
|
|
|
streamDimensions,
|
2022-11-27 14:06:33 +11:00
|
|
|
} = useStore((s) => ({
|
|
|
|
editorView: s.editorView,
|
|
|
|
setEditorView: s.setEditorView,
|
2023-02-21 10:28:34 +11:00
|
|
|
setSelectionRanges: s.setSelectionRanges,
|
|
|
|
selectionRanges: s.selectionRanges,
|
2022-11-27 14:06:33 +11:00
|
|
|
setGuiMode: s.setGuiMode,
|
|
|
|
addLog: s.addLog,
|
2022-11-28 09:37:46 +11:00
|
|
|
code: s.code,
|
|
|
|
setCode: s.setCode,
|
|
|
|
setAst: s.setAst,
|
2022-12-06 05:40:05 +11:00
|
|
|
setError: s.setError,
|
2023-01-04 01:28:26 +11:00
|
|
|
setProgramMemory: s.setProgramMemory,
|
2023-02-03 11:09:09 +11:00
|
|
|
resetLogs: s.resetLogs,
|
2023-07-26 18:16:20 -05:00
|
|
|
resetKCLErrors: s.resetKCLErrors,
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRangeTypeMap: s.selectionRangeTypeMap,
|
2023-06-22 16:43:33 +10:00
|
|
|
setArtifactMap: s.setArtifactNSourceRangeMaps,
|
|
|
|
engineCommandManager: s.engineCommandManager,
|
|
|
|
setEngineCommandManager: s.setEngineCommandManager,
|
2023-09-06 01:32:53 -04:00
|
|
|
highlightRange: s.highlightRange,
|
2023-06-22 16:43:33 +10:00
|
|
|
setHighlightRange: s.setHighlightRange,
|
|
|
|
isShiftDown: s.isShiftDown,
|
|
|
|
setCursor: s.setCursor,
|
|
|
|
setCursor2: s.setCursor2,
|
|
|
|
sourceRangeMap: s.sourceRangeMap,
|
2023-06-23 14:19:15 +10:00
|
|
|
setMediaStream: s.setMediaStream,
|
|
|
|
isStreamReady: s.isStreamReady,
|
|
|
|
setIsStreamReady: s.setIsStreamReady,
|
2023-09-05 16:02:27 -07:00
|
|
|
isLSPServerReady: s.isLSPServerReady,
|
|
|
|
setIsLSPServerReady: s.setIsLSPServerReady,
|
2023-08-06 21:29:26 -04:00
|
|
|
isMouseDownInStream: s.isMouseDownInStream,
|
2023-07-20 19:25:04 -04:00
|
|
|
formatCode: s.formatCode,
|
2023-07-26 14:10:30 -05:00
|
|
|
addKCLError: s.addKCLError,
|
2023-08-06 21:29:26 -04:00
|
|
|
openPanes: s.openPanes,
|
|
|
|
setOpenPanes: s.setOpenPanes,
|
2023-08-18 10:27:01 -04:00
|
|
|
didDragInStream: s.didDragInStream,
|
2023-08-09 20:49:10 +10:00
|
|
|
setStreamDimensions: s.setStreamDimensions,
|
|
|
|
streamDimensions: s.streamDimensions,
|
2022-11-27 14:06:33 +11:00
|
|
|
}))
|
2023-08-28 20:31:49 -04:00
|
|
|
|
2023-08-29 10:48:55 -04:00
|
|
|
const {
|
|
|
|
auth: {
|
|
|
|
context: { token },
|
|
|
|
},
|
|
|
|
settings: {
|
2023-09-06 21:27:30 -04:00
|
|
|
context: { showDebugPanel, theme, onboardingStatus, textWrapping },
|
2023-08-29 10:48:55 -04:00
|
|
|
},
|
|
|
|
} = useGlobalStateContext()
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-08-09 13:57:07 -04:00
|
|
|
const editorTheme = theme === Themes.System ? getSystemTheme() : theme
|
|
|
|
|
2023-08-06 21:29:26 -04:00
|
|
|
// Pane toggling keyboard shortcuts
|
|
|
|
const togglePane = useCallback(
|
|
|
|
(newPane: PaneType) =>
|
|
|
|
openPanes.includes(newPane)
|
|
|
|
? setOpenPanes(openPanes.filter((p) => p !== newPane))
|
|
|
|
: setOpenPanes([...openPanes, newPane]),
|
|
|
|
[openPanes, setOpenPanes]
|
|
|
|
)
|
|
|
|
useHotkeys('shift + c', () => togglePane('code'))
|
|
|
|
useHotkeys('shift + v', () => togglePane('variables'))
|
|
|
|
useHotkeys('shift + l', () => togglePane('logs'))
|
|
|
|
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
|
|
|
useHotkeys('shift + d', () => togglePane('debug'))
|
|
|
|
|
2023-08-07 17:07:28 -04:00
|
|
|
const paneOpacity =
|
2023-08-31 16:08:15 -04:00
|
|
|
onboardingStatus === onboardingPaths.CAMERA
|
2023-08-07 17:07:28 -04:00
|
|
|
? 'opacity-20'
|
2023-08-18 10:27:01 -04:00
|
|
|
: didDragInStream
|
2023-08-07 17:07:28 -04:00
|
|
|
? 'opacity-40'
|
|
|
|
: ''
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-08-15 21:56:24 -04:00
|
|
|
// Use file code loaded from disk
|
|
|
|
// on mount, and overwrite any locally-stored code
|
|
|
|
useEffect(() => {
|
|
|
|
if (isTauri() && loadedCode !== null) {
|
|
|
|
setCode(loadedCode)
|
|
|
|
}
|
|
|
|
return () => {
|
|
|
|
// Clear code on unmount if in desktop app
|
|
|
|
if (isTauri()) {
|
|
|
|
setCode('')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [loadedCode, setCode])
|
|
|
|
|
2022-11-26 05:13:07 +11:00
|
|
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
|
|
|
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
2022-11-26 08:34:23 +11:00
|
|
|
setCode(value)
|
2023-08-15 21:56:24 -04:00
|
|
|
if (isTauri() && pathParams.id) {
|
|
|
|
// Save the file to disk
|
|
|
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
|
|
|
writeTextFile(pathParams.id + '/' + PROJECT_ENTRYPOINT, value).catch(
|
|
|
|
(err) => {
|
|
|
|
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
|
|
|
console.error('error saving file', err)
|
|
|
|
toast.error('Error saving file, please check file permissions')
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2022-11-26 05:13:07 +11:00
|
|
|
if (editorView) {
|
2022-11-26 08:34:23 +11:00
|
|
|
editorView?.dispatch({ effects: addLineHighlight.of([0, 0]) })
|
2022-11-26 05:13:07 +11:00
|
|
|
}
|
2022-11-26 08:34:23 +11:00
|
|
|
} //, []);
|
2022-11-26 05:13:07 +11:00
|
|
|
const onUpdate = (viewUpdate: ViewUpdate) => {
|
|
|
|
if (!editorView) {
|
2022-11-26 08:34:23 +11:00
|
|
|
setEditorView(viewUpdate.view)
|
2022-11-26 05:13:07 +11:00
|
|
|
}
|
2023-02-21 10:28:34 +11:00
|
|
|
const ranges = viewUpdate.state.selection.ranges
|
|
|
|
|
|
|
|
const isChange =
|
2023-06-22 16:43:33 +10:00
|
|
|
ranges.length !== selectionRanges.codeBasedSelections.length ||
|
2023-02-21 10:28:34 +11:00
|
|
|
ranges.some(({ from, to }, i) => {
|
2023-04-03 16:05:25 +10:00
|
|
|
return (
|
2023-06-22 16:43:33 +10:00
|
|
|
from !== selectionRanges.codeBasedSelections[i].range[0] ||
|
|
|
|
to !== selectionRanges.codeBasedSelections[i].range[1]
|
2023-04-03 16:05:25 +10:00
|
|
|
)
|
2023-02-21 10:28:34 +11:00
|
|
|
})
|
|
|
|
|
|
|
|
if (!isChange) return
|
2023-06-22 16:43:33 +10:00
|
|
|
const codeBasedSelections: Selections['codeBasedSelections'] = ranges.map(
|
|
|
|
({ from, to }) => {
|
2023-04-03 16:05:25 +10:00
|
|
|
if (selectionRangeTypeMap[to]) {
|
|
|
|
return {
|
|
|
|
type: selectionRangeTypeMap[to],
|
|
|
|
range: [from, to],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
type: 'default',
|
|
|
|
range: [from, to],
|
|
|
|
}
|
2023-06-22 16:43:33 +10:00
|
|
|
}
|
|
|
|
)
|
|
|
|
const idBasedSelections = codeBasedSelections
|
|
|
|
.map(({ type, range }) => {
|
|
|
|
const hasOverlap = Object.entries(sourceRangeMap).filter(
|
|
|
|
([_, sourceRange]) => {
|
|
|
|
return isOverlap(sourceRange, range)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
if (hasOverlap.length) {
|
|
|
|
return {
|
|
|
|
type,
|
|
|
|
id: hasOverlap[0][0],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.filter(Boolean) as any
|
|
|
|
|
2023-08-09 20:49:10 +10:00
|
|
|
engineCommandManager?.cusorsSelected({
|
2023-06-22 16:43:33 +10:00
|
|
|
otherSelections: [],
|
|
|
|
idBasedSelections,
|
|
|
|
})
|
|
|
|
|
|
|
|
setSelectionRanges({
|
|
|
|
otherSelections: [],
|
|
|
|
codeBasedSelections,
|
2023-04-03 16:05:25 +10:00
|
|
|
})
|
2022-11-26 08:34:23 +11:00
|
|
|
}
|
2023-08-09 20:49:10 +10:00
|
|
|
const streamWidth = streamRef?.current?.offsetWidth
|
|
|
|
const streamHeight = streamRef?.current?.offsetHeight
|
|
|
|
|
2023-09-01 17:29:03 -07:00
|
|
|
const width = streamWidth ? streamWidth : 0
|
2023-08-09 20:49:10 +10:00
|
|
|
const quadWidth = Math.round(width / 4) * 4
|
2023-09-01 17:29:03 -07:00
|
|
|
const height = streamHeight ? streamHeight : 0
|
2023-08-09 20:49:10 +10:00
|
|
|
const quadHeight = Math.round(height / 4) * 4
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
setStreamDimensions({
|
|
|
|
streamWidth: quadWidth,
|
|
|
|
streamHeight: quadHeight,
|
|
|
|
})
|
|
|
|
if (!width || !height) return
|
|
|
|
const eng = new EngineCommandManager({
|
2023-07-11 20:34:09 +10:00
|
|
|
setMediaStream,
|
|
|
|
setIsStreamReady,
|
2023-08-09 20:49:10 +10:00
|
|
|
width: quadWidth,
|
|
|
|
height: quadHeight,
|
2023-07-11 20:34:09 +10:00
|
|
|
token,
|
|
|
|
})
|
2023-08-09 20:49:10 +10:00
|
|
|
setEngineCommandManager(eng)
|
2023-06-23 09:56:37 +10:00
|
|
|
return () => {
|
2023-08-09 20:49:10 +10:00
|
|
|
eng?.tearDown()
|
2023-06-23 09:56:37 +10:00
|
|
|
}
|
2023-08-09 20:49:10 +10:00
|
|
|
}, [quadWidth, quadHeight])
|
2023-06-23 09:56:37 +10:00
|
|
|
|
2022-11-23 21:28:38 +11:00
|
|
|
useEffect(() => {
|
2023-06-23 14:19:15 +10:00
|
|
|
if (!isStreamReady) return
|
2023-08-31 05:19:37 +10:00
|
|
|
if (!engineCommandManager) return
|
|
|
|
let unsubFn: any[] = []
|
2023-02-21 14:50:22 +11:00
|
|
|
const asyncWrap = async () => {
|
|
|
|
try {
|
|
|
|
if (!code) {
|
|
|
|
setAst(null)
|
|
|
|
return
|
|
|
|
}
|
2023-08-18 19:37:52 +10:00
|
|
|
const _ast = await asyncParser(code)
|
2023-02-21 14:50:22 +11:00
|
|
|
setAst(_ast)
|
|
|
|
resetLogs()
|
2023-07-26 18:16:20 -05:00
|
|
|
resetKCLErrors()
|
2023-08-31 05:19:37 +10:00
|
|
|
engineCommandManager.endSession()
|
|
|
|
engineCommandManager.startNewSession()
|
2023-08-07 20:33:38 -05:00
|
|
|
const programMemory = await _executor(
|
2023-06-22 16:43:33 +10:00
|
|
|
_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: [],
|
|
|
|
},
|
2023-04-01 21:25:00 +11:00
|
|
|
},
|
2022-11-27 14:06:33 +11:00
|
|
|
},
|
2023-08-24 15:34:51 -07:00
|
|
|
engineCommandManager
|
2023-08-07 20:33:38 -05:00
|
|
|
)
|
2023-06-22 16:43:33 +10:00
|
|
|
|
2023-08-07 20:33:38 -05:00
|
|
|
const { artifactMap, sourceRangeMap } =
|
|
|
|
await engineCommandManager.waitForAllCommands()
|
2023-01-08 16:37:31 +11:00
|
|
|
|
2023-08-07 20:33:38 -05:00
|
|
|
setArtifactMap({ artifactMap, sourceRangeMap })
|
2023-08-31 07:39:03 +10:00
|
|
|
const unSubHover = engineCommandManager.subscribeToUnreliable({
|
2023-08-31 05:19:37 +10:00
|
|
|
event: 'highlight_set_entity',
|
|
|
|
callback: ({ data }) => {
|
2023-09-06 01:32:53 -04:00
|
|
|
if (data?.entity_id) {
|
2023-08-31 05:19:37 +10:00
|
|
|
const sourceRange = sourceRangeMap[data.entity_id]
|
|
|
|
setHighlightRange(sourceRange)
|
2023-09-06 01:32:53 -04:00
|
|
|
} else if (
|
|
|
|
!highlightRange ||
|
|
|
|
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
|
|
|
) {
|
|
|
|
setHighlightRange([0, 0])
|
2023-08-31 05:19:37 +10:00
|
|
|
}
|
|
|
|
},
|
2023-06-22 16:43:33 +10:00
|
|
|
})
|
2023-08-31 05:19:37 +10:00
|
|
|
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' })
|
|
|
|
},
|
2023-08-07 20:33:38 -05:00
|
|
|
})
|
2023-08-31 05:19:37 +10:00
|
|
|
unsubFn.push(unSubHover, unSubClick)
|
2023-08-07 20:33:38 -05:00
|
|
|
if (programMemory !== undefined) {
|
|
|
|
setProgramMemory(programMemory)
|
|
|
|
}
|
|
|
|
|
|
|
|
setError()
|
2023-02-21 14:50:22 +11:00
|
|
|
} catch (e: any) {
|
2023-07-26 14:10:30 -05:00
|
|
|
if (e instanceof KCLError) {
|
|
|
|
addKCLError(e)
|
|
|
|
} else {
|
|
|
|
setError('problem')
|
|
|
|
console.log(e)
|
|
|
|
addLog(e)
|
|
|
|
}
|
2023-02-21 14:50:22 +11:00
|
|
|
}
|
2022-11-23 21:28:38 +11:00
|
|
|
}
|
2023-02-21 14:50:22 +11:00
|
|
|
asyncWrap()
|
2023-08-31 05:19:37 +10:00
|
|
|
return () => {
|
|
|
|
unsubFn.forEach((fn) => fn())
|
|
|
|
}
|
|
|
|
}, [code, isStreamReady, engineCommandManager])
|
2023-07-31 06:33:10 -04:00
|
|
|
|
2023-08-06 21:29:26 -04:00
|
|
|
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
|
|
|
engineCommandManager?.sendSceneCommand(message)
|
|
|
|
}, 16)
|
2023-08-09 20:49:10 +10:00
|
|
|
const handleMouseMove: MouseEventHandler<HTMLDivElement> = ({
|
|
|
|
clientX,
|
|
|
|
clientY,
|
|
|
|
ctrlKey,
|
2023-08-21 10:52:41 -04:00
|
|
|
shiftKey,
|
2023-08-09 20:49:10 +10:00
|
|
|
currentTarget,
|
2023-08-21 10:52:41 -04:00
|
|
|
nativeEvent,
|
2023-08-09 20:49:10 +10:00
|
|
|
}) => {
|
2023-08-21 10:52:41 -04:00
|
|
|
nativeEvent.preventDefault()
|
2023-08-09 20:49:10 +10:00
|
|
|
|
|
|
|
const { x, y } = getNormalisedCoordinates({
|
|
|
|
clientX,
|
|
|
|
clientY,
|
|
|
|
el: currentTarget,
|
|
|
|
...streamDimensions,
|
|
|
|
})
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-08-21 10:52:41 -04:00
|
|
|
const interaction = ctrlKey ? 'zoom' : shiftKey ? 'pan' : 'rotate'
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-08-09 20:49:10 +10:00
|
|
|
const newCmdId = uuidv4()
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-09-05 20:40:50 +10:00
|
|
|
if (isMouseDownInStream) {
|
2023-08-06 21:29:26 -04:00
|
|
|
debounceSocketSend({
|
|
|
|
type: 'modeling_cmd_req',
|
|
|
|
cmd: {
|
|
|
|
type: 'camera_drag_move',
|
|
|
|
interaction,
|
|
|
|
window: { x, y },
|
|
|
|
},
|
|
|
|
cmd_id: newCmdId,
|
|
|
|
})
|
2023-08-09 20:49:10 +10:00
|
|
|
} else {
|
|
|
|
debounceSocketSend({
|
|
|
|
type: 'modeling_cmd_req',
|
|
|
|
cmd: {
|
|
|
|
type: 'highlight_set_entity',
|
|
|
|
selected_at_window: { x, y },
|
|
|
|
},
|
|
|
|
cmd_id: newCmdId,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
// So this is a bit weird, we need to initialize the lsp server and client.
|
|
|
|
// But the server happens async so we break this into two parts.
|
|
|
|
// Below is the client and server promise.
|
|
|
|
const { lspClient } = useMemo(() => {
|
|
|
|
const intoServer: IntoServer = new IntoServer()
|
|
|
|
const fromServer: FromServer = FromServer.create()
|
|
|
|
const client = new Client(fromServer, intoServer)
|
|
|
|
if (!TEST) {
|
|
|
|
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
|
|
|
lspServer.start()
|
|
|
|
setIsLSPServerReady(true)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const lspClient = new LanguageServerClient({ client })
|
|
|
|
return { lspClient }
|
|
|
|
}, [setIsLSPServerReady])
|
|
|
|
|
|
|
|
// Here we initialize the plugin which will start the client.
|
|
|
|
// When we have multi-file support the name of the file will be a dep of
|
|
|
|
// this use memo, as well as the directory structure, which I think is
|
|
|
|
// a good setup becuase it will restart the client but not the server :)
|
|
|
|
// We do not want to restart the server, its just wasteful.
|
|
|
|
const kclLSP = useMemo(() => {
|
|
|
|
let plugin = null
|
|
|
|
if (isLSPServerReady && !TEST) {
|
|
|
|
// Set up the lsp plugin.
|
|
|
|
const lsp = kclLanguage({
|
|
|
|
// When we have more than one file, we'll need to change this.
|
|
|
|
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
|
|
|
workspaceFolders: null,
|
|
|
|
client: lspClient,
|
|
|
|
})
|
|
|
|
|
|
|
|
plugin = lsp
|
|
|
|
}
|
|
|
|
return plugin
|
|
|
|
}, [lspClient, isLSPServerReady])
|
|
|
|
|
2023-09-06 21:27:30 -04:00
|
|
|
const editorExtensions = useMemo(() => {
|
|
|
|
const extensions = [lineHighlightField] as Extension[]
|
|
|
|
|
|
|
|
if (kclLSP) extensions.push(kclLSP)
|
|
|
|
|
|
|
|
// These extensions have proven to mess with vitest
|
|
|
|
if (!TEST) {
|
|
|
|
extensions.push(
|
|
|
|
lintGutter(),
|
|
|
|
linter((_view) => {
|
|
|
|
return kclErrToDiagnostic(useStore.getState().kclErrors)
|
|
|
|
})
|
|
|
|
)
|
|
|
|
if (textWrapping === 'On') extensions.push(EditorView.lineWrapping)
|
|
|
|
}
|
|
|
|
|
|
|
|
return extensions
|
|
|
|
}, [kclLSP, textWrapping])
|
|
|
|
|
2022-11-12 13:11:54 +11:00
|
|
|
return (
|
2023-08-06 21:29:26 -04:00
|
|
|
<div
|
2023-08-21 10:52:41 -04:00
|
|
|
className="h-screen overflow-hidden relative flex flex-col cursor-pointer select-none"
|
2023-08-06 21:29:26 -04:00
|
|
|
onMouseMove={handleMouseMove}
|
2023-08-09 20:49:10 +10:00
|
|
|
ref={streamRef}
|
2023-08-06 21:29:26 -04:00
|
|
|
>
|
|
|
|
<AppHeader
|
|
|
|
className={
|
2023-08-07 17:07:28 -04:00
|
|
|
'transition-opacity transition-duration-75 ' +
|
|
|
|
paneOpacity +
|
|
|
|
(isMouseDownInStream ? ' pointer-events-none' : '')
|
2023-08-06 21:29:26 -04:00
|
|
|
}
|
2023-08-18 10:27:01 -04:00
|
|
|
project={project}
|
|
|
|
enableMenu={true}
|
2023-08-06 21:29:26 -04:00
|
|
|
/>
|
2023-03-07 15:45:59 +11:00
|
|
|
<ModalContainer />
|
2023-08-06 21:29:26 -04:00
|
|
|
<Resizable
|
|
|
|
className={
|
2023-08-08 12:39:11 -04:00
|
|
|
'h-full flex flex-col flex-1 z-10 my-5 ml-5 pr-1 transition-opacity transition-duration-75 ' +
|
2023-08-06 21:29:26 -04:00
|
|
|
(isMouseDownInStream || onboardingStatus === 'camera'
|
|
|
|
? ' pointer-events-none '
|
|
|
|
: ' ') +
|
|
|
|
paneOpacity
|
|
|
|
}
|
|
|
|
defaultSize={{
|
|
|
|
width: '400px',
|
|
|
|
height: 'auto',
|
|
|
|
}}
|
|
|
|
minWidth={200}
|
|
|
|
maxWidth={600}
|
|
|
|
minHeight={'auto'}
|
|
|
|
maxHeight={'auto'}
|
|
|
|
handleClasses={{
|
|
|
|
right:
|
|
|
|
'hover:bg-liquid-30/40 dark:hover:bg-liquid-10/40 bg-transparent transition-colors duration-100 transition-ease-out delay-100',
|
|
|
|
}}
|
|
|
|
>
|
2023-08-08 12:39:11 -04:00
|
|
|
<div className="h-full flex flex-col justify-between">
|
|
|
|
<CollapsiblePanel
|
|
|
|
title="Code"
|
|
|
|
icon={faCode}
|
2023-09-06 21:27:30 -04:00
|
|
|
className="open:!mb-2"
|
2023-08-08 12:39:11 -04:00
|
|
|
open={openPanes.includes('code')}
|
|
|
|
>
|
|
|
|
<div className="px-2 py-1">
|
|
|
|
<button
|
|
|
|
// disabled={!shouldFormat}
|
|
|
|
onClick={formatCode}
|
|
|
|
// className={`${!shouldFormat && 'text-gray-300'}`}
|
|
|
|
>
|
|
|
|
format
|
|
|
|
</button>
|
|
|
|
</div>
|
2023-09-05 16:02:27 -07:00
|
|
|
<div
|
|
|
|
id="code-mirror-override"
|
2023-09-06 21:27:30 -04:00
|
|
|
className="full-height-subtract"
|
|
|
|
style={{ '--height-subtract': '4.25rem' } as CSSRuleObject}
|
2023-09-05 16:02:27 -07:00
|
|
|
>
|
2023-08-08 12:39:11 -04:00
|
|
|
<CodeMirror
|
2023-09-06 21:27:30 -04:00
|
|
|
className="h-full"
|
2023-08-08 12:39:11 -04:00
|
|
|
value={code}
|
2023-09-06 21:27:30 -04:00
|
|
|
extensions={editorExtensions}
|
2023-08-08 12:39:11 -04:00
|
|
|
onChange={onChange}
|
|
|
|
onUpdate={onUpdate}
|
2023-08-09 13:57:07 -04:00
|
|
|
theme={editorTheme}
|
2023-08-08 12:39:11 -04:00
|
|
|
onCreateEditor={(_editorView) => setEditorView(_editorView)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</CollapsiblePanel>
|
|
|
|
<section className="flex flex-col">
|
|
|
|
<MemoryPanel
|
2023-08-09 13:57:07 -04:00
|
|
|
theme={editorTheme}
|
2023-08-08 12:39:11 -04:00
|
|
|
open={openPanes.includes('variables')}
|
|
|
|
title="Variables"
|
|
|
|
icon={faSquareRootVariable}
|
|
|
|
/>
|
|
|
|
<Logs
|
2023-08-09 13:57:07 -04:00
|
|
|
theme={editorTheme}
|
2023-08-08 12:39:11 -04:00
|
|
|
open={openPanes.includes('logs')}
|
|
|
|
title="Logs"
|
|
|
|
icon={faCodeCommit}
|
|
|
|
/>
|
|
|
|
<KCLErrors
|
2023-08-09 13:57:07 -04:00
|
|
|
theme={editorTheme}
|
2023-08-08 12:39:11 -04:00
|
|
|
open={openPanes.includes('kclErrors')}
|
|
|
|
title="KCL Errors"
|
|
|
|
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
|
2023-08-06 21:29:26 -04:00
|
|
|
/>
|
2023-08-08 12:39:11 -04:00
|
|
|
</section>
|
|
|
|
</div>
|
2023-08-06 21:29:26 -04:00
|
|
|
</Resizable>
|
|
|
|
<Stream className="absolute inset-0 z-0" />
|
2023-08-28 20:31:49 -04:00
|
|
|
{showDebugPanel && (
|
2023-08-06 21:29:26 -04:00
|
|
|
<DebugPanel
|
|
|
|
title="Debug"
|
|
|
|
className={
|
2023-08-07 17:07:28 -04:00
|
|
|
'transition-opacity transition-duration-75 ' +
|
|
|
|
paneOpacity +
|
|
|
|
(isMouseDownInStream ? ' pointer-events-none' : '')
|
2023-08-06 21:29:26 -04:00
|
|
|
}
|
|
|
|
open={openPanes.includes('debug')}
|
|
|
|
/>
|
|
|
|
)}
|
2022-11-12 13:11:54 +11:00
|
|
|
</div>
|
2022-11-26 08:34:23 +11:00
|
|
|
)
|
2022-11-12 13:11:54 +11:00
|
|
|
}
|