undo button

pull/43/head
Alexander Bentkamp 3 years ago
parent fbdc1afe34
commit 45f88cf5a8

@ -10,18 +10,10 @@ import '@fontsource/roboto/700.css';
import './reset.css';
import './app.css';
type SetTitleType = {setTitle: (string) => void, setSubtitle: (string) => void}
export const SetTitleContext = React.createContext<SetTitleType>({setTitle: () => {}, setSubtitle: () => {}})
function App() {
const [title, setTitle] = useState("")
const [subtitle, setSubtitle] = useState("")
return (
<div className="app">
<SetTitleContext.Provider value={{setTitle, setSubtitle}}>
<Outlet />
</SetTitleContext.Provider>
<Outlet />
</div>
)
}

@ -35,15 +35,22 @@ code {
}
.btn {
line-height: inherit;
text-decoration: none;
color: var(--clr-primary);
background: white;
background: var(--clr-primary);
color: white;
display: inline-block;
border-radius: 0.2em;
padding: 0.2em .6em;
padding: 0.2rem .6em;
font-size: 1rem;
margin: 0 .2em;
border: 0;
cursor: pointer;
}
.btn-inverted {
color: var(--clr-primary);
background: white;
}
.btn-disabled, .btn[disabled] {
@ -63,7 +70,6 @@ code {
}
.app-bar {
z-index: 1500;
flex: 0;
background: var(--clr-primary);
display: flex;

@ -3,12 +3,13 @@ import { Link, LinkProps } from "react-router-dom";
export interface ButtonProps extends LinkProps {
disabled?: boolean
inverted?: boolean
}
export function Button(props: ButtonProps) {
if (props.disabled) {
return <span className="btn btn-disabled" {...props}>{props.children}</span>
return <span className={`btn btn-disabled ${props.inverted ? 'btn-inverted' : ''}`} {...props}>{props.children}</span>
} else {
return <Link className="btn" {...props}>{props.children}</Link>
return <Link className={`btn ${props.inverted ? 'btn-inverted' : ''}`} {...props}>{props.children}</Link>
}
}

@ -33,13 +33,10 @@ import { Main } from './infoview/main'
import type { Location } from 'vscode-languageserver-protocol';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faHome, faArrowRight, faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { faHome, faArrowRight, faArrowLeft, faRotateLeft } from '@fortawesome/free-solid-svg-icons'
import { styled, useTheme, Theme, CSSObject } from '@mui/material/styles';
import { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
import Divider from '@mui/material/Divider';
import Markdown from './Markdown';
import { SetTitleContext } from '../App';
import Split from 'react-split'
@ -73,6 +70,40 @@ function Level() {
introductionPanelRef.current!.scrollTo(0,0)
}, [levelId])
React.useEffect(() => {
if (!commandLineMode) {
// Delete last input attempt from command line
editor.executeEdits("command-line", [{
range: editor.getSelection(),
text: "",
forceMoveMarkers: false
}]);
editor.focus()
}
}, [commandLineMode])
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
}]);
}
const connection = React.useContext(ConnectionContext)
const gameInfo = useGetGameInfoQuery()
@ -103,10 +134,10 @@ function Level() {
<span className="app-bar-title">
{levelId && `Level ${levelId}`}{level?.data?.title && `: ${level?.data?.title}`}
</span>
<Button disabled={levelId <= 1}
<Button disabled={levelId <= 1} inverted={true}
to={`/world/${worldId}/level/${levelId - 1}`}
><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]}
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true}
to={`/world/${worldId}/level/${levelId + 1}`}
>Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>
</div>
@ -125,9 +156,13 @@ function Level() {
</div>
</div>
<div className="info-panel">
<FormGroup className="input-mode-switch">
<FormControlLabel control={<Switch onChange={(ev) => { setCommandLineMode(!commandLineMode) }} />} label="Editor mode" />
</FormGroup>
<div className="input-mode-switch">
{commandLineMode && <button className="btn" onClick={handleUndo}><FontAwesomeIcon icon={faRotateLeft} /> Undo</button>}
<FormGroup>
<FormControlLabel control={<Switch onChange={(ev) => { setCommandLineMode(!commandLineMode) }} />} label="Editor mode" />
</FormGroup>
</div>
<EditorContext.Provider value={editorConnection}>
<MonacoEditorContext.Provider value={editor}>
<InputModeContext.Provider value={{commandLineMode, setCommandLineMode}}>

@ -14,7 +14,6 @@ import { useGetGameInfoQuery } from '../state/api';
import { Link } from 'react-router-dom';
import Markdown from './Markdown';
import { selectCompleted } from '../state/progress';
import { SetTitleContext } from '../App';
function LevelIcon({ worldId, levelId, position }) {
@ -34,12 +33,9 @@ function Welcome() {
const { nodes, bounds }: any = gameInfo.data ? computeWorldLayout(gameInfo.data?.worlds) : {nodes: []}
const {setTitle, setSubtitle} = React.useContext(SetTitleContext);
useEffect(() => {
if (gameInfo.data?.title) {
window.document.title = gameInfo.data.title
setTitle(gameInfo.data.title)
}
}, [gameInfo.data?.title])

@ -4,43 +4,67 @@ import { LspDiagnosticsContext } from '../../../../node_modules/lean4-infoview/s
import { useServerNotificationEffect } from '../../../../node_modules/lean4-infoview/src/infoview/util';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
import { DiagnosticSeverity, PublishDiagnosticsParams } from 'vscode-languageserver-protocol';
import { MonacoEditorContext } from '../Level'
import { InputModeContext, MonacoEditorContext } from '../Level'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faWandMagicSparkles } from '@fortawesome/free-solid-svg-icons'
function hasErrorsOrWarnings(diags) {
return diags.some(
(d) =>
!d.message.startsWith("unsolved goals") &&
(d.severity == DiagnosticSeverity.Error || d.severity == DiagnosticSeverity.Warning)
)
}
export function CommandLine() {
const editor = React.useContext(MonacoEditorContext)
const commandInput = useRef<HTMLInputElement>()
const [processing, setProcessing] = useState(false)
// const allDiags = React.useContext(LspDiagnosticsContext)
// const fileDiags = allDiags.get(editor.getModel().uri.toString())
const { commandLineMode } = React.useContext(InputModeContext)
const allDiags = React.useContext(LspDiagnosticsContext)
const diags = allDiags.get(editor.getModel().uri.toString())
React.useEffect(() => {
if (hasErrorsOrWarnings(diags)) {
// alert("err")
}
}, [])
React.useEffect(() => {
if (commandLineMode) {
const endPos = editor.getModel().getFullModelRange().getEndPosition()
if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") {
editor.executeEdits("command-line", [{
range: monaco.Selection.fromPositions(endPos, endPos),
text: "\n",
forceMoveMarkers: false
}]);
}
editor.setPosition(editor.getModel().getFullModelRange().getEndPosition())
}
}, [commandLineMode])
const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => {
ev.preventDefault()
var selection = monaco.Selection.fromPositions(
editor.getPosition(),
editor.getModel().getFullModelRange().getEndPosition()
);
var text = commandInput.current!.value + "\n";
var op = {range: selection, text: text, forceMoveMarkers: false};
editor.executeEdits("my-source", [op], editor.getSelections());
editor.executeEdits("command-line", [{
range: monaco.Selection.fromPositions(
editor.getPosition(),
editor.getModel().getFullModelRange().getEndPosition()
),
text: commandInput.current!.value + "\n",
forceMoveMarkers: false
}]);
setProcessing(true)
}
useServerNotificationEffect('textDocument/publishDiagnostics', (params: PublishDiagnosticsParams) => {
if (params.uri == editor.getModel().uri.toString()) {
setProcessing(false)
const hasErrorsOrWarnings = params.diagnostics.some(
(d) =>
!d.message.startsWith("unsolved goals") &&
(d.severity == DiagnosticSeverity.Error || d.severity == DiagnosticSeverity.Warning)
)
if (!hasErrorsOrWarnings) {
if (!hasErrorsOrWarnings(params.diagnostics)) {
commandInput.current!.value = "";
editor.setPosition(editor.getModel().getFullModelRange().getEndPosition())
}
@ -50,7 +74,7 @@ export function CommandLine() {
return <div className="command-line">
<form onSubmit={handleSubmit}>
<input type="text" ref={commandInput} disabled={processing} />
<button type="submit" disabled={processing} className="btn"><FontAwesomeIcon icon={faWandMagicSparkles} /> Run</button>
<button type="submit" disabled={processing} className="btn btn-inverted"><FontAwesomeIcon icon={faWandMagicSparkles} /> Run</button>
</form>
</div>
}

@ -112,7 +112,6 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
}), [selectedLocs])
const goalFilter = { reverse: false, showType: true, showInstance: true, showHiddenAssumption: true, showLetValue: true }
/* Adding {' '} to manage string literals properly: https://reactjs.org/docs/jsx-in-depth.html#string-literals-1 */
return <>
{hasError &&
@ -121,13 +120,13 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
<a className='link pointer dim' onClick={e => { e.preventDefault(); void triggerUpdate(); }}>{' '}Try again.</a>
</div>}
<LocationsContext.Provider value={locs}>
{goals && goals.goals.length > 0 && <div className="goals-section">
<div className="goals-section-title">Main Goal</div>
<Goal filter={goalFilter} key='mainGoal' goal={goals.goals[0]} showHints={true} />
</div>}
<div className="goals-section">
{ goals && goals.goals.length > 0
? <><div className="goals-section-title">Main Goal</div>
<Goal filter={goalFilter} key='mainGoal' goal={goals.goals[0]} showHints={true} /></>
: <div className="goals-section-title">No Goals</div> }
</div>
</LocationsContext.Provider>
{/* <FilteredGoals headerChildren='Expected type' key='term-goal'
goals={termGoal !== undefined ? {goals: [{...termGoal, hints: []}]} : undefined} /> */}
{userWidgets.map(widget =>
<details key={`widget::${widget.id}::${widget.range?.toString()}`} open>
<summary className='mv2 pointer'>{widget.name}</summary>
@ -135,14 +134,6 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
selectedLocations={selectedLocs} widget={widget}/>
</details>
)}
{/* <div style={{display: hasMessages ? 'block' : 'none'}} key='messages'>
<details key='messages' open>
<summary className='mv2 pointer'>Messages ({messages.length})</summary>
<div className='ml1'>
<MessagesList uri={pos.uri} messages={messages} />
</div>
</details>
</div> */}
{nothingToShow && (
isPaused ?
/* Adding {' '} to manage string literals properly: https://reactjs.org/docs/jsx-in-depth.html#string-literals-1 */

@ -24,7 +24,7 @@
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
}
.main-panel, .info-panel {
.main-panel, .info-panel, .doc-panel {
height: 100%;
overflow: auto;
}
@ -66,6 +66,9 @@
.input-mode-switch {
margin: .5em 0;
float: right;
display: flex;
flex-direction: row;
gap: 1em;
}
.doc-panel li {

Loading…
Cancel
Save