import { useEffect , useRef , useState } from 'react' ; import { useIdeContext } from 'src/helpers/hooks/useIdeContext' ; import { makeCodeStoreKey , requestRender } from 'src/helpers/hooks/useIdeState' ; import Editor , { useMonaco } from '@monaco-editor/react' ; import { theme } from 'src/../config/tailwind.config' ; import { useSaveCode } from 'src/components/IdeWrapper/useSaveCode' ; import type { CadPackageType } from 'src/components/CadPackage/CadPackage' ; import EditorGuide from 'src/components/EditorGuide/EditorGuide' ; const colors = theme . extend . colors ; const IdeEditor = ( { Loading } ) => { const { state , thunkDispatch } = useIdeContext ( ) ; const [ theme , setTheme ] = useState ( 'vs-dark' ) ; const saveCode = useSaveCode ( ) ; const ideTypeToLanguageMap = { cadquery : 'python' , openscad : 'cpp' , jscad : 'javascript' , curv : 'python' , INIT : '' , } ; const monaco = useMonaco ( ) ; useEffect ( ( ) => { if ( monaco ) { const themeName = 'cadhub' ; monaco . editor . defineTheme ( themeName , { base : 'vs-dark' , inherit : true , rules : [ { background : colors [ 'ch-gray' ] [ 750 ] } , { token : 'comment' , foreground : colors [ 'ch-blue' ] [ 600 ] , fontStyle : 'italic' , } , { token : 'keyword' , foreground : colors [ 'ch-purple' ] [ 600 ] } , { token : 'string' , foreground : colors [ 'ch-pink' ] [ 300 ] } , ] , colors : { 'editor.background' : colors [ 'ch-gray' ] [ 800 ] , } , } ) ; setTheme ( themeName ) ; } } , [ monaco ] ) ; function handleCodeChange ( value , _event ) { thunkDispatch ( { type : 'updateCode' , payload : value } ) ; } function handleSaveHotkey ( event ) { const { key , ctrlKey , metaKey } = event ; if ( key === 's' && ( ctrlKey || metaKey ) ) { event . preventDefault ( ) ; thunkDispatch ( ( dispatch , getState ) => { const state = getState ( ) ; dispatch ( { type : 'setLoading' } ) ; saveCode ( { code : state . code } ) ; requestRender ( { state , dispatch , } ) ; } ) ; localStorage . setItem ( makeCodeStoreKey ( state . ideType ) , state . code ) ; } } const currentTab = state . editorTabs [ state . currentModel ] ; return ( < div className = "h-full" onKeyDown = { handleSaveHotkey } > { state . editorTabs . length > 1 && ( < fieldset className = "bg-ch-gray-700 text-ch-gray-300 flex m-0 p-0" > { state . editorTabs . map ( ( model , i ) => ( < label key = { model . type + '-' + i } className = { 'flex items-center gap-2 px-4 py-1 block m-0 select-none relative bg-ch-gray-600 ' + ( state . currentModel === i && 'bg-ch-gray-800' ) } > { model . label } < input type = "radio" name = "models" className = "sr-only absolute inset-0" value = { i } checked = { state . currentModel === i } onChange = { ( ) => thunkDispatch ( { type : 'switchEditorModel' , payload : i } ) } /> { model . type !== 'code' && ( < button onClick = { ( ) => thunkDispatch ( { type : 'removeEditorModel' , payload : i } ) } className = "block p-1 m-.5 hover:bg-ch-gray-550" > < svg viewBox = "0 0 5 5" className = "w-4 text-ch-gray-300" > < path stroke = "currentColor" d = "M 1 1 l 3 3 M 1 4 l 3 -3" strokeLinecap = "round" strokeWidth = ".5" / > < /svg> < /button>)} < /label>))} < /fieldset>)} { currentTab . type === 'code' && ( < Editor defaultValue = { state . code } value = { state . code } theme = { theme } loading = { Loading } defaultLanguage = { ideTypeToLanguageMap [ state . ideType ] || 'cpp' } language = { ideTypeToLanguageMap [ state . ideType ] || 'cpp' } onChange = { handleCodeChange } / > ) } { currentTab . type === 'guide' && ( < div className = "bg-ch-gray-800 h-full" > < EditorGuide content = { currentTab . content } /> < /div>)} { currentTab . type === 'component' && < currentTab . Component />} < /div>); } ; export default IdeEditor ;