diff --git a/client/src/components/editor/Editor.tsx b/client/src/components/editor/Editor.tsx index 23a3087..d587d67 100644 --- a/client/src/components/editor/Editor.tsx +++ b/client/src/components/editor/Editor.tsx @@ -11,7 +11,7 @@ import { LeanMonaco, LeanMonacoEditor, LeanMonacoOptions } from 'lean4monaco' import '../../css/editor.css' import { useSelector } from 'react-redux'; import { selectTypewriterMode } from '../../state/progress'; -import { Typewriter } from './Typewriter'; +import { TypewriterInterFace } from './Typewriter'; export function Editor() { let { t } = useTranslation() @@ -98,7 +98,7 @@ export function Editor() {
- {typewriterMode && } + {typewriterMode && }
} diff --git a/client/src/components/editor/Typewriter.tsx b/client/src/components/editor/Typewriter.tsx index 29fa65c..0db1186 100644 --- a/client/src/components/editor/Typewriter.tsx +++ b/client/src/components/editor/Typewriter.tsx @@ -378,7 +378,7 @@ export function TypewriterInterFace() { } - {mobile && i == 0 && props.data?.introduction && + {/* {mobile && i == 0 && props.data?.introduction &&
{props.data?.introduction}
@@ -386,7 +386,7 @@ export function TypewriterInterFace() { {mobile && ({hint: hint, step: i}))} /> - } + } */} {/* setDisableInput(n > 0) : (n) => {}}/> */} {!(isLastStepWithErrors(proof, i)) && setDisableInput(n > 0) : (n) => {}}/> @@ -418,7 +418,7 @@ export function TypewriterInterFace() { } {mobile && proof?.completed &&
- {props.level >= props.worldSize ? + {/* {props.level >= props.worldSize ? @@ -426,7 +426,7 @@ export function TypewriterInterFace() { - } + } */}
} : diff --git a/client/src/components/game.tsx b/client/src/components/game.tsx index 1ae1d89..7333b20 100644 --- a/client/src/components/game.tsx +++ b/client/src/components/game.tsx @@ -12,7 +12,7 @@ import { WorldTreePanel } from './world_tree' import i18next from 'i18next' import { ChatPanel } from './chat' import { NewLevel } from './level' -import { GameHint, ProofState } from './infoview/rpc_api' +import { GameHint, ProofState } from './editor/Defs' import { useSelector } from 'react-redux' import { Diagnostic } from 'vscode-languageserver-types' diff --git a/client/src/components/level.tsx b/client/src/components/level.tsx index 5c0c017..0b44ade 100644 --- a/client/src/components/level.tsx +++ b/client/src/components/level.tsx @@ -124,596 +124,482 @@ export function NewLevel({visible = true}) { // monacoSetup() -export function Level({visible = true}) { - let { t } = useTranslation() - - - const {gameId, worldId, levelId} = React.useContext(GameIdContext) - - // Load the namespace of the game - i18next.loadNamespaces(gameId).catch(err => { - console.warn(`translations for ${gameId} do not exist.`) - }) - - const gameInfo = useGetGameInfoQuery({game: gameId}) - const levelInfo = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) - - const codeviewRef = useRef(null) - - const dispatch = useAppDispatch() - - const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) - const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) - - const typewriterMode = useSelector(selectTypewriterMode(gameId)) - const setTypewriterMode = (newTypewriterMode: boolean) => dispatch(changeTypewriterMode({game: gameId, typewriterMode: newTypewriterMode})) - - const {deletedChat, setDeletedChat,showHelp, setShowHelp} = useContext(ChatContext) - const {proof, setProof} = useContext(ProofContext) - - // Only for mobile layout - const {page, setPage} = useContext(PageContext) - - // set to true to prevent switching between typewriter and editor - const [lockEditorMode, setLockEditorMode] = useState(false) - const [typewriterInput, setTypewriterInput] = useState("") - const lastLevel = levelId >= gameInfo.data?.worldSize[worldId] - - // // impressum pop-up - // function toggleImpressum() {setImpressum(!impressum)} - // function togglePrivacy() {setPrivacy(!privacy)} - - // When clicking on an inventory item, the inventory is overlayed by the item's doc. - // If this state is set to a pair `(name, type)` then the according doc will be open. - // Set `inventoryDoc` to `null` to close the doc - const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null) - function closeInventoryDoc () {setInventoryDoc(null)} - - const onDidChangeContent = (code) => { - dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code})) - } - - const onDidChangeSelection = (monacoSelections) => { - const selections = monacoSelections.map( - ({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) => - {return {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}}) - dispatch(changedSelection({game: gameId, world: worldId, level: levelId, selections})) - } - - - // const {editor, infoProvider, editorConnection} = - // useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) - - // /** Unused. Was implementing an undo button, which has been replaced by `deleteProof` inside - // * `TypewriterInterface`. - // */ - // const handleUndo = () => { - // const endPos = editor.getModel().getFullModelRange().getEndPosition() - // let range - // console.log(endPos.column) - // if (endPos.column === 1) { - // range = monaco.Selection.fromPositions( - // new monaco.Position(endPos.lineNumber - 1, 1), - // endPos - // ) - // } else { - // range = monaco.Selection.fromPositions( - // new monaco.Position(endPos.lineNumber, 1), - // endPos - // ) - // } - // editor.executeEdits("undo-button", [{ - // range, - // text: "", - // forceMoveMarkers: false - // }]); - // } - - // Select and highlight proof steps and corresponding hints - // TODO: with the new design, there is no difference between the introduction and - // a hint at the beginning of the proof... - const [selectedStep, setSelectedStep] = useState() - - - // useEffect (() => { - // // Lock editor mode - // if (levelInfo.data?.template) { - // setLockEditorMode(true) - - // if (editor) { - // let code = editor.getModel().getLinesContent() - - // // console.log(`insert. code: ${code}`) - // // console.log(`insert. join: ${code.join('')}`) - // // console.log(`insert. trim: ${code.join('').trim()}`) - // // console.log(`insert. length: ${code.join('').trim().length}`) - // // console.log(`insert. range: ${editor.getModel().getFullModelRange()}`) - - - // // TODO: It does seem that the template is always indented by spaces. - // // This is a hack, assuming there are exactly two. - // if (!code.join('').trim().length) { - // console.debug(`inserting template:\n${levelInfo.data.template}`) - // // TODO: This does not work! HERE - // // Probably overwritten by a query to the server - // editor.executeEdits("template-writer", [{ - // range: editor.getModel().getFullModelRange(), - // text: levelInfo.data.template + `\n`, - // forceMoveMarkers: true - // }]) - // } else { - // console.debug(`not inserting template.`) - // } - // } - // } else { - // setLockEditorMode(false) - // } - // }, [levelInfo, levelId, worldId, gameId, editor]) - - - // useEffect(() => { - // // TODO: That's a problem if the saved proof contains an error - // // Reset command line input when loading a new level - // setTypewriterInput("") - - // // Load the selected help steps from the store - // setShowHelp(new Set(selectHelp(gameId, worldId, levelId)(store.getState()))) - // }, [gameId, worldId, levelId]) - - // useEffect(() => { - // if (!(typewriterMode && !lockEditorMode) && editor) { - // // Delete last input attempt from command line - // editor.executeEdits("typewriter", [{ - // range: editor.getSelection(), - // text: "", - // forceMoveMarkers: false - // }]); - // editor.focus() - // } - // }, [typewriterMode, lockEditorMode]) - - useEffect(() => { - // Forget whether hidden hints are displayed for steps that don't exist yet - if (proof?.steps.length) { - console.debug(Array.from(showHelp)) - setShowHelp(new Set(Array.from(showHelp).filter(i => (i < proof?.steps.length)))) - } - }, [proof]) - - // save showed help in store - useEffect(() => { - if (proof?.steps.length) { - console.debug(`showHelp:\n ${showHelp}`) - dispatch(helpEdited({game: gameId, world: worldId, level: levelId, help: Array.from(showHelp)})) - } - }, [showHelp]) - - // Effect when command line mode gets enabled - // useEffect(() => { - // if (//onigasmH && - // editor && (typewriterMode && !lockEditorMode)) { - // let code = editor.getModel().getLinesContent().filter(line => line.trim()) - // editor.executeEdits("typewriter", [{ - // range: editor.getModel().getFullModelRange(), - // text: code.length ? code.join('\n') + '\n' : '', - // forceMoveMarkers: true - // }]); - - // let endPos = editor.getModel().getFullModelRange().getEndPosition() - // if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") { - // editor.executeEdits("typewriter", [{ - // range: monaco.Selection.fromPositions(endPos, endPos), - // text: "\n", - // forceMoveMarkers: true - // }]); - // } - // let endPos = editor.getModel().getFullModelRange().getEndPosition() - // let currPos = editor.getPosition() - // if (currPos.column != 1 || (currPos.lineNumber != endPos.lineNumber && currPos.lineNumber != endPos.lineNumber - 1)) { - // // This is not a position that would naturally occur from Typewriter, reset: - // editor.setSelection(monaco.Selection.fromPositions(endPos, endPos)) - // } - // } - // }, [editor, typewriterMode, lockEditorMode, ])//onigasmH == null]) - - - return
- {/*
*/} - {/* - */} - {levelId > 0 && - } - {/* - */} -
-} - -export function LevelWrapper({visible = true}) { - let { t } = useTranslation() - const {gameId, worldId, levelId} = React.useContext(GameIdContext) - - // Load the namespace of the game - i18next.loadNamespaces(gameId).catch(err => { - console.warn(`translations for ${gameId} do not exist.`) - }) - - - const { mobile } = useContext(PreferencesContext) - - const gameInfo = useGetGameInfoQuery({game: gameId}) - const levelInfo = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) - - let image: string = gameInfo.data?.worlds.nodes[worldId].image - - const codeviewRef = useRef(null) - const proofPanelRef = React.useRef(null) - - // set to true to prevent switching between typewriter and editor - const [lockEditorMode, setLockEditorMode] = useState(false) - const [typewriterInput, setTypewriterInput] = useState("") - const lastLevel = levelId >= gameInfo.data?.worldSize[worldId] - const { proof } = useContext(ProofContext) - - const dispatch = useAppDispatch() - - const typewriterMode = useSelector(selectTypewriterMode(gameId)) - const setTypewriterMode = (newTypewriterMode: boolean) => dispatch(changeTypewriterMode({game: gameId, typewriterMode: newTypewriterMode})) - - const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) - const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) - - - const onDidChangeContent = (code) => { - dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code})) - } - - const onDidChangeSelection = (monacoSelections) => { - const selections = monacoSelections.map( - ({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) => - {return {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}}) - dispatch(changedSelection({game: gameId, world: worldId, level: levelId, selections})) - } - - - useEffect(() => { - console.info(`Loading Level: ${mobile}`) - }, [mobile, gameId, worldId, levelId]) - - // const {editor, infoProvider, editorConnection} = - // useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) - - const navigate = useNavigate() - - // Keyboard listener - useEffect(() => { - function keyDownHandler(e: globalThis.KeyboardEvent) { - if (e.key === "Enter") { - // use enter on a completed proof to go to the next level or home. - if (proof?.completed) { - if (levelId == gameInfo.data?.worldSize[worldId]){ - navigate(`/${gameId}`) - e.preventDefault() - } else { - navigate(`/${gameId}/world/${worldId}/level/${levelId+1}`) - e.preventDefault() - } - } - } - } - document.addEventListener("keydown", keyDownHandler) - return () => {document.removeEventListener("keydown", keyDownHandler)} - }, [proof]) - - /** toggle input mode if allowed */ - function toggleInputMode(ev: React.MouseEvent) { - if (!lockEditorMode) { - setTypewriterMode(!typewriterMode) - console.log('test') - } - } - - // { typewriterMode ? : } - - // - // - return
- { image && -
- -
- } - { levelId > 0 && -
- toggleInputMode(ev)} - title={lockEditorMode ? t("Editor mode is enforced!") : typewriterMode ? t("Editor mode") : t("Typewriter mode")} /> -
-
- - - {/* { typewriterMode ? : } */} -
- -
- } - -
-} +// export function Level({visible = true}) { +// let { t } = useTranslation() + + +// const {gameId, worldId, levelId} = React.useContext(GameIdContext) + +// // Load the namespace of the game +// i18next.loadNamespaces(gameId).catch(err => { +// console.warn(`translations for ${gameId} do not exist.`) +// }) + +// const gameInfo = useGetGameInfoQuery({game: gameId}) +// const levelInfo = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) + +// const codeviewRef = useRef(null) + +// const dispatch = useAppDispatch() + +// const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) +// const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) + +// const typewriterMode = useSelector(selectTypewriterMode(gameId)) +// const setTypewriterMode = (newTypewriterMode: boolean) => dispatch(changeTypewriterMode({game: gameId, typewriterMode: newTypewriterMode})) + +// const {deletedChat, setDeletedChat,showHelp, setShowHelp} = useContext(ChatContext) +// const {proof, setProof} = useContext(ProofContext) + +// // Only for mobile layout +// const {page, setPage} = useContext(PageContext) + +// // set to true to prevent switching between typewriter and editor +// const [lockEditorMode, setLockEditorMode] = useState(false) +// const [typewriterInput, setTypewriterInput] = useState("") +// const lastLevel = levelId >= gameInfo.data?.worldSize[worldId] + +// // // impressum pop-up +// // function toggleImpressum() {setImpressum(!impressum)} +// // function togglePrivacy() {setPrivacy(!privacy)} + +// // When clicking on an inventory item, the inventory is overlayed by the item's doc. +// // If this state is set to a pair `(name, type)` then the according doc will be open. +// // Set `inventoryDoc` to `null` to close the doc +// const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null) +// function closeInventoryDoc () {setInventoryDoc(null)} + +// const onDidChangeContent = (code) => { +// dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code})) +// } + +// const onDidChangeSelection = (monacoSelections) => { +// const selections = monacoSelections.map( +// ({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) => +// {return {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}}) +// dispatch(changedSelection({game: gameId, world: worldId, level: levelId, selections})) +// } + + +// // const {editor, infoProvider, editorConnection} = +// // useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) + +// // /** Unused. Was implementing an undo button, which has been replaced by `deleteProof` inside +// // * `TypewriterInterface`. +// // */ +// // const handleUndo = () => { +// // const endPos = editor.getModel().getFullModelRange().getEndPosition() +// // let range +// // console.log(endPos.column) +// // if (endPos.column === 1) { +// // range = monaco.Selection.fromPositions( +// // new monaco.Position(endPos.lineNumber - 1, 1), +// // endPos +// // ) +// // } else { +// // range = monaco.Selection.fromPositions( +// // new monaco.Position(endPos.lineNumber, 1), +// // endPos +// // ) +// // } +// // editor.executeEdits("undo-button", [{ +// // range, +// // text: "", +// // forceMoveMarkers: false +// // }]); +// // } + +// // Select and highlight proof steps and corresponding hints +// // TODO: with the new design, there is no difference between the introduction and +// // a hint at the beginning of the proof... +// const [selectedStep, setSelectedStep] = useState() + + +// // useEffect (() => { +// // // Lock editor mode +// // if (levelInfo.data?.template) { +// // setLockEditorMode(true) + +// // if (editor) { +// // let code = editor.getModel().getLinesContent() + +// // // console.log(`insert. code: ${code}`) +// // // console.log(`insert. join: ${code.join('')}`) +// // // console.log(`insert. trim: ${code.join('').trim()}`) +// // // console.log(`insert. length: ${code.join('').trim().length}`) +// // // console.log(`insert. range: ${editor.getModel().getFullModelRange()}`) + + +// // // TODO: It does seem that the template is always indented by spaces. +// // // This is a hack, assuming there are exactly two. +// // if (!code.join('').trim().length) { +// // console.debug(`inserting template:\n${levelInfo.data.template}`) +// // // TODO: This does not work! HERE +// // // Probably overwritten by a query to the server +// // editor.executeEdits("template-writer", [{ +// // range: editor.getModel().getFullModelRange(), +// // text: levelInfo.data.template + `\n`, +// // forceMoveMarkers: true +// // }]) +// // } else { +// // console.debug(`not inserting template.`) +// // } +// // } +// // } else { +// // setLockEditorMode(false) +// // } +// // }, [levelInfo, levelId, worldId, gameId, editor]) + + +// // useEffect(() => { +// // // TODO: That's a problem if the saved proof contains an error +// // // Reset command line input when loading a new level +// // setTypewriterInput("") + +// // // Load the selected help steps from the store +// // setShowHelp(new Set(selectHelp(gameId, worldId, levelId)(store.getState()))) +// // }, [gameId, worldId, levelId]) + +// // useEffect(() => { +// // if (!(typewriterMode && !lockEditorMode) && editor) { +// // // Delete last input attempt from command line +// // editor.executeEdits("typewriter", [{ +// // range: editor.getSelection(), +// // text: "", +// // forceMoveMarkers: false +// // }]); +// // editor.focus() +// // } +// // }, [typewriterMode, lockEditorMode]) + +// useEffect(() => { +// // Forget whether hidden hints are displayed for steps that don't exist yet +// if (proof?.steps.length) { +// console.debug(Array.from(showHelp)) +// setShowHelp(new Set(Array.from(showHelp).filter(i => (i < proof?.steps.length)))) +// } +// }, [proof]) + +// // save showed help in store +// useEffect(() => { +// if (proof?.steps.length) { +// console.debug(`showHelp:\n ${showHelp}`) +// dispatch(helpEdited({game: gameId, world: worldId, level: levelId, help: Array.from(showHelp)})) +// } +// }, [showHelp]) + +// // Effect when command line mode gets enabled +// // useEffect(() => { +// // if (//onigasmH && +// // editor && (typewriterMode && !lockEditorMode)) { +// // let code = editor.getModel().getLinesContent().filter(line => line.trim()) +// // editor.executeEdits("typewriter", [{ +// // range: editor.getModel().getFullModelRange(), +// // text: code.length ? code.join('\n') + '\n' : '', +// // forceMoveMarkers: true +// // }]); + +// // let endPos = editor.getModel().getFullModelRange().getEndPosition() +// // if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") { +// // editor.executeEdits("typewriter", [{ +// // range: monaco.Selection.fromPositions(endPos, endPos), +// // text: "\n", +// // forceMoveMarkers: true +// // }]); +// // } +// // let endPos = editor.getModel().getFullModelRange().getEndPosition() +// // let currPos = editor.getPosition() +// // if (currPos.column != 1 || (currPos.lineNumber != endPos.lineNumber && currPos.lineNumber != endPos.lineNumber - 1)) { +// // // This is not a position that would naturally occur from Typewriter, reset: +// // editor.setSelection(monaco.Selection.fromPositions(endPos, endPos)) +// // } +// // } +// // }, [editor, typewriterMode, lockEditorMode, ])//onigasmH == null]) + + +// return
+// {/*
*/} +// {/* +// */} +// {levelId > 0 && +// } +// {/* +// */} +//
+// } + +// // // -export function ExercisePanel({codeviewRef, visible=true}: {codeviewRef: React.MutableRefObject, visible?: boolean}) { - const {gameId, worldId, levelId} = React.useContext(GameIdContext) - const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) - const gameInfo = useGetGameInfoQuery({game: gameId}) - return
-
- {/* */} -
-
-} - - - - - - - -function IntroductionPanel({gameInfo}) { - let { t } = useTranslation() - const {gameId, worldId} = React.useContext(GameIdContext) - const {mobile} = React.useContext(PreferencesContext) - - let text: Array = t(gameInfo.data?.worlds.nodes[worldId].introduction, {ns: gameId}).split(/\n(\s*\n)+/) - - return
-
- {text?.filter(t => t.trim()).map(((t, i) => - - ))} -
- -
-} - -/** The site with the introduction text of a world */ -function Introduction() { - let { t } = useTranslation() - - const {gameId, worldId} = React.useContext(GameIdContext) - const {mobile} = useContext(PreferencesContext) - - const inventory = useLoadInventoryOverviewQuery({game: gameId}) - - const gameInfo = useGetGameInfoQuery({game: gameId}) - - - let image: string = gameInfo.data?.worlds.nodes[worldId].image - - - // const toggleImpressum = () => { - // setImpressum(!impressum) - // } - // const togglePrivacy = () => { - // setPrivacy(!privacy) - // } - return <> - {/* */} - {gameInfo.isLoading ? -
- : mobile ? - - : - // - // -
- {image && - - } - -
- // {/* */} - //
- } - - -} - -{mobile? - // TODO: This is copied from the `Split` component below... - <> -
- - -
- -: - - - - - -} - -function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) { - - const {gameId, worldId, levelId} = React.useContext(GameIdContext) - - const [editor, setEditor] = useState(null) - const [infoProvider, setInfoProvider] = useState(null) - const [editorConnection, setEditorConnection] = useState(null) - - const uriStr = `file:///${worldId}/${levelId}` - const uri = monaco.Uri.parse(uriStr) - - const inventory: Array = useSelector(selectInventory(gameId)) - const difficulty: number = useSelector(selectDifficulty(gameId)) - - useEffect(() => { - // monaco.editor.getModels().forEach(model => model.dispose()); - - console.info(`trying to create model: ${gameId} ${worldId} ${levelId} ${uri}`) - const model = monaco.editor.createModel(initialCode ?? '', 'lean4', uri) - if (onDidChangeContent) { - model.onDidChangeContent(() => onDidChangeContent(model.getValue())) - } - - const editor = monaco.editor.create(codeviewRef.current!, { - model, - glyphMargin: true, - quickSuggestions: false, - lineDecorationsWidth: 5, - folding: false, - lineNumbers: 'on', - lightbulb: { - enabled: true - }, - unicodeHighlight: { - ambiguousCharacters: false, - }, - automaticLayout: true, - minimap: { - enabled: false - }, - lineNumbersMinChars: 3, - tabSize: 2, - 'semanticHighlighting.enabled': true, - fontFamily: "JuliaMono", - theme: 'vs-code-theme-converted' - }) - if (onDidChangeSelection) { - editor.onDidChangeCursorSelection(() => onDidChangeSelection(editor.getSelections())) - } - if (initialSelections) { - console.debug("Initial Selection: ", initialSelections) - // BUG: Somehow I get an `invalid arguments` bug here - // editor.setSelections(initialSelections) - } - setEditor(editor) - const abbrevRewriter = new AbbreviationRewriter(new AbbreviationProvider(), model, editor) - - const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/' + gameId - - const connectionProvider : IConnectionProvider = { - get: async () => { - return await new Promise((resolve, reject) => { - console.log(`connecting ${socketUrl}`) - const websocket = new WebSocket(socketUrl) - websocket.addEventListener('error', (ev) => { - reject(ev) - }) - websocket.addEventListener('message', (msg) => { - // console.log(msg.data) - }) - websocket.addEventListener('open', () => { - const socket = toSocket(websocket) - const reader = new DisposingWebSocketMessageReader(socket) - const writer = new WebSocketMessageWriter(socket) - resolve({ - reader, - writer - }) - }) - }) - } - } - - // Following `vscode-lean4/webview/index.ts` - const client = new LeanClient(connectionProvider, showRestartMessage, {inventory, difficulty}) - const infoProvider = new InfoProvider(client) - // const div: HTMLElement = infoviewRef.current! - const imports = { - '@leanprover/infoview': `${window.location.origin}/index.production.min.js`, - 'react': `${window.location.origin}/react.production.min.js`, - 'react/jsx-runtime': `${window.location.origin}/react-jsx-runtime.production.min.js`, - 'react-dom': `${window.location.origin}/react-dom.production.min.js`, - 'react-popper': `${window.location.origin}/react-popper.production.min.js` - } - // loadRenderInfoview(imports, [infoProvider.getApi(), div], setInfoviewApi) - setInfoProvider(infoProvider) - - // TODO: it looks like we get errors "File Changed" here. - client.restart("Lean4Game") - - const editorApi = infoProvider.getApi() - - const editorEvents: EditorEvents = { - initialize: new EventEmitter(), - gotServerNotification: new EventEmitter(), - sentClientNotification: new EventEmitter(), - serverRestarted: new EventEmitter(), - serverStopped: new EventEmitter(), - changedCursorLocation: new EventEmitter(), - changedInfoviewConfig: new EventEmitter(), - runTestScript: new EventEmitter(), - requestedAction: new EventEmitter(), - }; - - // Challenge: write a type-correct fn from `Eventify` to `T` without using `any` - const infoviewApi: InfoviewApi = { - initialize: async l => editorEvents.initialize.fire(l), - gotServerNotification: async (method, params) => { - editorEvents.gotServerNotification.fire([method, params]); - }, - sentClientNotification: async (method, params) => { - editorEvents.sentClientNotification.fire([method, params]); - }, - serverRestarted: async r => editorEvents.serverRestarted.fire(r), - serverStopped: async serverStoppedReason => { - editorEvents.serverStopped.fire(serverStoppedReason) - }, - changedCursorLocation: async loc => editorEvents.changedCursorLocation.fire(loc), - changedInfoviewConfig: async conf => editorEvents.changedInfoviewConfig.fire(conf), - requestedAction: async action => editorEvents.requestedAction.fire(action), - // See https://rollupjs.org/guide/en/#avoiding-eval - // eslint-disable-next-line @typescript-eslint/no-implied-eval - runTestScript: async script => new Function(script)(), - getInfoviewHtml: async () => document.body.innerHTML, - }; - - const ec = new EditorConnection(editorApi, editorEvents); - setEditorConnection(ec) - - editorEvents.initialize.on((loc: Location) => ec.events.changedCursorLocation.fire(loc)) - - setEditor(editor) - setInfoProvider(infoProvider) - - infoProvider.openPreview(editor, infoviewApi) - const taskgutter = new LeanTaskGutter(infoProvider.client, editor) - - // TODO: - // setRestart(() => restart) - - return () => { - editor.dispose(); - model.dispose(); - abbrevRewriter.dispose(); - taskgutter.dispose(); - infoProvider.dispose(); - client.dispose(); - } - }, [gameId, worldId, levelId]) - - const showRestartMessage = () => { - // setRestartMessage(true) - console.log("TODO: SHOW RESTART MESSAGE") - } - - return {editor, infoProvider, editorConnection} -} +// export function ExercisePanel({codeviewRef, visible=true}: {codeviewRef: React.MutableRefObject, visible?: boolean}) { +// const {gameId, worldId, levelId} = React.useContext(GameIdContext) +// const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) +// const gameInfo = useGetGameInfoQuery({game: gameId}) +// return
+//
+// {/* */} +//
+//
+// } + + +// +// +// +// + +// function IntroductionPanel({gameInfo}) { +// let { t } = useTranslation() +// const {gameId, worldId} = React.useContext(GameIdContext) +// const {mobile} = React.useContext(PreferencesContext) + +// let text: Array = t(gameInfo.data?.worlds.nodes[worldId].introduction, {ns: gameId}).split(/\n(\s*\n)+/) + +// return
+//
+// {text?.filter(t => t.trim()).map(((t, i) => +// +// ))} +//
+// +//
+// } + +// /** The site with the introduction text of a world */ +// function Introduction() { +// let { t } = useTranslation() + +// const {gameId, worldId} = React.useContext(GameIdContext) +// const {mobile} = useContext(PreferencesContext) + +// const inventory = useLoadInventoryOverviewQuery({game: gameId}) + +// const gameInfo = useGetGameInfoQuery({game: gameId}) + + +// let image: string = gameInfo.data?.worlds.nodes[worldId].image + + +// // const toggleImpressum = () => { +// // setImpressum(!impressum) +// // } +// // const togglePrivacy = () => { +// // setPrivacy(!privacy) +// // } +// return <> +// {/* */} +// {gameInfo.isLoading ? +//
+// : mobile ? +// +// : +// // +// // +//
+// {image && +// +// } + +//
+// // {/* */} +// //
+// } + +// +// } + +// {mobile? +// // TODO: This is copied from the `Split` component below... +// <> +//
+// +// +//
+// +// : +// +// +// +// +// +// } + +// function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) { + +// const {gameId, worldId, levelId} = React.useContext(GameIdContext) + +// const [editor, setEditor] = useState(null) +// const [infoProvider, setInfoProvider] = useState(null) +// const [editorConnection, setEditorConnection] = useState(null) + +// const uriStr = `file:///${worldId}/${levelId}` +// const uri = monaco.Uri.parse(uriStr) + +// const inventory: Array = useSelector(selectInventory(gameId)) +// const difficulty: number = useSelector(selectDifficulty(gameId)) + +// useEffect(() => { +// // monaco.editor.getModels().forEach(model => model.dispose()); + +// console.info(`trying to create model: ${gameId} ${worldId} ${levelId} ${uri}`) +// const model = monaco.editor.createModel(initialCode ?? '', 'lean4', uri) +// if (onDidChangeContent) { +// model.onDidChangeContent(() => onDidChangeContent(model.getValue())) +// } + +// const editor = monaco.editor.create(codeviewRef.current!, { +// model, +// glyphMargin: true, +// quickSuggestions: false, +// lineDecorationsWidth: 5, +// folding: false, +// lineNumbers: 'on', +// lightbulb: { +// enabled: true +// }, +// unicodeHighlight: { +// ambiguousCharacters: false, +// }, +// automaticLayout: true, +// minimap: { +// enabled: false +// }, +// lineNumbersMinChars: 3, +// tabSize: 2, +// 'semanticHighlighting.enabled': true, +// fontFamily: "JuliaMono", +// theme: 'vs-code-theme-converted' +// }) +// if (onDidChangeSelection) { +// editor.onDidChangeCursorSelection(() => onDidChangeSelection(editor.getSelections())) +// } +// if (initialSelections) { +// console.debug("Initial Selection: ", initialSelections) +// // BUG: Somehow I get an `invalid arguments` bug here +// // editor.setSelections(initialSelections) +// } +// setEditor(editor) +// const abbrevRewriter = new AbbreviationRewriter(new AbbreviationProvider(), model, editor) + +// const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/' + gameId + +// const connectionProvider : IConnectionProvider = { +// get: async () => { +// return await new Promise((resolve, reject) => { +// console.log(`connecting ${socketUrl}`) +// const websocket = new WebSocket(socketUrl) +// websocket.addEventListener('error', (ev) => { +// reject(ev) +// }) +// websocket.addEventListener('message', (msg) => { +// // console.log(msg.data) +// }) +// websocket.addEventListener('open', () => { +// const socket = toSocket(websocket) +// const reader = new DisposingWebSocketMessageReader(socket) +// const writer = new WebSocketMessageWriter(socket) +// resolve({ +// reader, +// writer +// }) +// }) +// }) +// } +// } + +// // Following `vscode-lean4/webview/index.ts` +// const client = new LeanClient(connectionProvider, showRestartMessage, {inventory, difficulty}) +// const infoProvider = new InfoProvider(client) +// // const div: HTMLElement = infoviewRef.current! +// const imports = { +// '@leanprover/infoview': `${window.location.origin}/index.production.min.js`, +// 'react': `${window.location.origin}/react.production.min.js`, +// 'react/jsx-runtime': `${window.location.origin}/react-jsx-runtime.production.min.js`, +// 'react-dom': `${window.location.origin}/react-dom.production.min.js`, +// 'react-popper': `${window.location.origin}/react-popper.production.min.js` +// } +// // loadRenderInfoview(imports, [infoProvider.getApi(), div], setInfoviewApi) +// setInfoProvider(infoProvider) + +// // TODO: it looks like we get errors "File Changed" here. +// client.restart("Lean4Game") + +// const editorApi = infoProvider.getApi() + +// const editorEvents: EditorEvents = { +// initialize: new EventEmitter(), +// gotServerNotification: new EventEmitter(), +// sentClientNotification: new EventEmitter(), +// serverRestarted: new EventEmitter(), +// serverStopped: new EventEmitter(), +// changedCursorLocation: new EventEmitter(), +// changedInfoviewConfig: new EventEmitter(), +// runTestScript: new EventEmitter(), +// requestedAction: new EventEmitter(), +// }; + +// // Challenge: write a type-correct fn from `Eventify` to `T` without using `any` +// const infoviewApi: InfoviewApi = { +// initialize: async l => editorEvents.initialize.fire(l), +// gotServerNotification: async (method, params) => { +// editorEvents.gotServerNotification.fire([method, params]); +// }, +// sentClientNotification: async (method, params) => { +// editorEvents.sentClientNotification.fire([method, params]); +// }, +// serverRestarted: async r => editorEvents.serverRestarted.fire(r), +// serverStopped: async serverStoppedReason => { +// editorEvents.serverStopped.fire(serverStoppedReason) +// }, +// changedCursorLocation: async loc => editorEvents.changedCursorLocation.fire(loc), +// changedInfoviewConfig: async conf => editorEvents.changedInfoviewConfig.fire(conf), +// requestedAction: async action => editorEvents.requestedAction.fire(action), +// // See https://rollupjs.org/guide/en/#avoiding-eval +// // eslint-disable-next-line @typescript-eslint/no-implied-eval +// runTestScript: async script => new Function(script)(), +// getInfoviewHtml: async () => document.body.innerHTML, +// }; + +// const ec = new EditorConnection(editorApi, editorEvents); +// setEditorConnection(ec) + +// editorEvents.initialize.on((loc: Location) => ec.events.changedCursorLocation.fire(loc)) + +// setEditor(editor) +// setInfoProvider(infoProvider) + +// infoProvider.openPreview(editor, infoviewApi) +// const taskgutter = new LeanTaskGutter(infoProvider.client, editor) + +// // TODO: +// // setRestart(() => restart) + +// return () => { +// editor.dispose(); +// model.dispose(); +// abbrevRewriter.dispose(); +// taskgutter.dispose(); +// infoProvider.dispose(); +// client.dispose(); +// } +// }, [gameId, worldId, levelId]) + +// const showRestartMessage = () => { +// // setRestartMessage(true) +// console.log("TODO: SHOW RESTART MESSAGE") +// } + +// return {editor, infoProvider, editorConnection} +// }