diff --git a/client/src/components/hints.tsx b/client/src/components/hints.tsx index 10aa46f..f9e1371 100644 --- a/client/src/components/hints.tsx +++ b/client/src/components/hints.tsx @@ -25,3 +25,21 @@ export function Hints({hints, showHidden, step, selected, toggleSelection} : {hi {showHidden && hiddenHints.map((hint, j) => )} } + +export function DeletedHint({hint} : {hint: GameHint}) { + return
+ {hint.text} +
+} + +export function DeletedHints({hints, showHidden} : {hints: GameHint[], showHidden: boolean}) { + + const openHints = hints.filter(hint => !hint.hidden) + const hiddenHints = hints.filter(hint => hint.hidden) + + // TODO: Should not use index as key. + return <> + {openHints.map((hint, i) => )} + {showHidden && hiddenHints.map((hint, i) => )} + +} diff --git a/client/src/components/infoview/command_line.tsx b/client/src/components/infoview/command_line.tsx index e892f66..3991e40 100644 --- a/client/src/components/infoview/command_line.tsx +++ b/client/src/components/infoview/command_line.tsx @@ -17,7 +17,7 @@ import { InteractiveDiagnostic, getInteractiveDiagnostics } from '@leanprover/in import { Diagnostic } from 'vscode-languageserver-types'; import { DocumentPosition } from '../../../../node_modules/lean4-infoview/src/infoview/util'; import { useRpcSessionAtPos } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions'; -import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './context' +import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './context' import { goalsToString } from './goals' import { InteractiveGoals } from './rpc_api' @@ -81,6 +81,9 @@ export function CommandLine({proofPanelRef}: {proofPanelRef: React.MutableRefObj // The context storing all information about the current proof const {proof, setProof} = React.useContext(ProofContext) + // state to store the last batch of deleted messages + const {setDeletedChat} = React.useContext(DeletedChatContext) + // TODO: does the position matter at all? const rpcSess = useRpcSessionAtPos({uri: uri, line: 1, character: 1}) @@ -184,6 +187,10 @@ export function CommandLine({proofPanelRef}: {proofPanelRef: React.MutableRefObj // Run the command const runCommand = React.useCallback(() => { if (processing) {return} + + // TODO: Desired logic is to only reset this after a new *error-free* command has been entered + setDeletedChat([]) + const pos = editor.getPosition() if (commandLineInput) { setProcessing(true) diff --git a/client/src/components/infoview/context.ts b/client/src/components/infoview/context.ts index cff5400..dfc570a 100644 --- a/client/src/components/infoview/context.ts +++ b/client/src/components/infoview/context.ts @@ -4,7 +4,7 @@ import * as React from 'react'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js' import { InteractiveDiagnostic, InteractiveTermGoal } from '@leanprover/infoview-api'; -import { InteractiveGoal, InteractiveGoals } from './rpc_api'; +import { GameHint, InteractiveGoal, InteractiveGoals } from './rpc_api'; export const MonacoEditorContext = React.createContext( null as any) @@ -18,7 +18,7 @@ export type ProofStep = { /** List of goals *after* this command */ goals: InteractiveGoal[] // TODO: Add correct type /** Story relevant messages */ - hints: any // TODO: Add correct type + hints: GameHint[] // TODO: Add correct type /** Errors and warnings */ errors: InteractiveDiagnostic[] // TODO: Add correct type } @@ -71,6 +71,14 @@ export const SelectionContext = React.createContext<{ setSelectedStep: () => {} }) +/** Context for deleted Hints that are visible just a bit after they've been deleted */ +export const DeletedChatContext = React.createContext<{ + deletedChat : GameHint[], + setDeletedChat: React.Dispatch>> +}>({ + deletedChat: undefined, + setDeletedChat: () => {} +}) export const InputModeContext = React.createContext<{ commandLineMode: boolean, diff --git a/client/src/components/infoview/infoview.css b/client/src/components/infoview/infoview.css index 9c1757e..1d533ad 100644 --- a/client/src/components/infoview/infoview.css +++ b/client/src/components/infoview/infoview.css @@ -18,6 +18,12 @@ background-color: #FFBABA; } +.message.deleted-hint { + background-color: #eee; + color: #777; + box-shadow: .0em .0em .5em .2em #eee; + } + .hyp-group { margin-bottom: 0.3em; } diff --git a/client/src/components/infoview/main.tsx b/client/src/components/infoview/main.tsx index 7918617..f83fa9c 100644 --- a/client/src/components/infoview/main.tsx +++ b/client/src/components/infoview/main.tsx @@ -27,11 +27,12 @@ import Markdown from '../markdown'; import { Infos } from './infos'; import { AllMessages, Errors, WithLspDiagnosticsContext } from './messages'; import { Goal } from './goals'; -import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './context'; +import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './context'; import { CommandLine, hasErrors, hasInteractiveErrors } from './command_line'; import { InteractiveDiagnostic } from '@leanprover/infoview/*'; import { Button } from '../button'; import { CircularProgress } from '@mui/material'; +import { GameHint } from './rpc_api'; /** Wrapper for the two editors. It is important that the `div` with `codeViewRef` is * always present, or the monaco editor cannot start. @@ -275,6 +276,7 @@ export function CommandLineInterface(props: {world: string, level: number, data: const gameId = React.useContext(GameIdContext) const {proof} = React.useContext(ProofContext) const {selectedStep, setSelectedStep} = React.useContext(SelectionContext) + const {setDeletedChat} = React.useContext(DeletedChatContext) const proofPanelRef = React.useRef(null) @@ -302,6 +304,12 @@ export function CommandLineInterface(props: {world: string, level: number, data: */ function deleteProof(line: number) { return (ev) => { + let deletedChat: Array = [] + proof.slice(line).map(step => { + deletedChat = [...deletedChat, ...step.hints] + }) + setDeletedChat(deletedChat) + editor.executeEdits("command-line", [{ range: monaco.Selection.fromPositions( {lineNumber: line, column: 1}, diff --git a/client/src/components/level.tsx b/client/src/components/level.tsx index d0efaad..2a043ab 100644 --- a/client/src/components/level.tsx +++ b/client/src/components/level.tsx @@ -38,9 +38,10 @@ import {Inventory, Documentation} from './inventory'; import { useGetGameInfoQuery, useLoadLevelQuery } from '../state/api'; import { changedSelection, codeEdited, selectCode, selectSelections, progressSlice, selectCompleted } from '../state/progress'; import { DualEditor } from './infoview/main' -import { Hints } from './hints'; -import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './infoview/context'; +import { DeletedHint, DeletedHints, Hints } from './hints'; +import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './infoview/context'; import { hasInteractiveErrors } from './infoview/command_line'; +import { GameHint } from './infoview/rpc_api'; function Level() { @@ -67,6 +68,10 @@ function PlayableLevel({worldId, levelId}) { // The state variables for the `ProofContext` const [proof, setProof] = useState>([]) + // When deleting the proof, we want to keep to old messages around until + // a new proof has been entered. e.g. to consult messages coming from dead ends + const [deletedChat, setDeletedChat] = useState>([]) + const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) @@ -216,6 +221,7 @@ function PlayableLevel({worldId, levelId}) { return <>
+ @@ -238,6 +244,7 @@ function PlayableLevel({worldId, levelId}) { selected={selectedStep} toggleSelection={toggleSelection(i)}/> } })} + {completed && <>
@@ -284,6 +291,7 @@ function PlayableLevel({worldId, levelId}) { + }