Compare commits

..
Author SHA1 Message Date
Jon Eugster 391285e5cc Merge branch 'dev' into mobile-option 2023-12-13 14:59:45 +01:00
19 changed files with 534 additions and 239 deletions
+1
View File
@@ -11,6 +11,7 @@ import './css/app.css';
import { MobileContext } from './components/infoview/context'; import { MobileContext } from './components/infoview/context';
import { useMobile } from './hooks'; import { useMobile } from './hooks';
import { AUTO_SWITCH_THRESHOLD, getWindowDimensions} from './state/preferences'; import { AUTO_SWITCH_THRESHOLD, getWindowDimensions} from './state/preferences';
import { connection } from './connection'; import { connection } from './connection';
export const GameIdContext = React.createContext<string>(undefined); export const GameIdContext = React.createContext<string>(undefined);
+8 -10
View File
@@ -493,20 +493,18 @@ export function TypewriterInterface({props}) {
<Markdown>{props.data?.introduction}</Markdown> <Markdown>{props.data?.introduction}</Markdown>
</div> </div>
} }
{mobile && {mobile && <>
<Hints key={`hints-${i}`} <Hints key={`hints-${i}`}
hints={step.hints} showHidden={showHelp.has(i)} step={i} hints={step.hints} showHidden={showHelp.has(i)} step={i}
selected={selectedStep} toggleSelection={toggleSelectStep(i)}/> selected={selectedStep} toggleSelection={toggleSelectStep(i)}/>
{i == proof.length - 1 && hasHiddenHints(proof.length - 1) && !showHelp.has(k - withErr) &&
<Button className="btn btn-help" to="" onClick={activateHiddenHints}>
Show more help!
</Button>
}
</>
} }
<GoalsTabs proofStep={step} last={i == proof.length - (lastStepErrors ? 2 : 1)} onClick={toggleSelectStep(i)} onGoalChange={i == proof.length - 1 - withErr ? (n) => setDisableInput(n > 0) : (n) => {}}/> <GoalsTabs proofStep={step} last={i == proof.length - (lastStepErrors ? 2 : 1)} onClick={toggleSelectStep(i)} onGoalChange={i == proof.length - 1 - withErr ? (n) => setDisableInput(n > 0) : (n) => {}}/>
{mobile && i == proof.length - 1 &&
hasHiddenHints(proof.length - 1) && !showHelp.has(k - withErr) &&
<Button className="btn btn-help" to="" onClick={activateHiddenHints}>
Show more help!
</Button>
}
{/* Show a message that there are no goals left */} {/* Show a message that there are no goals left */}
{!step.goals.length && ( {!step.goals.length && (
<div className="message information"> <div className="message information">
@@ -523,7 +521,7 @@ export function TypewriterInterface({props}) {
} }
})} })}
{mobile && completed && {mobile && completed &&
<div className="button-row mobile"> <div className="button-row">
{props.level >= props.worldSize ? {props.level >= props.worldSize ?
<Button to={`/${gameId}`}> <Button to={`/${gameId}`}>
<FontAwesomeIcon icon={faHome} />&nbsp;Leave World <FontAwesomeIcon icon={faHome} />&nbsp;Leave World
+21 -44
View File
@@ -2,8 +2,7 @@ import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import '../css/inventory.css' import '../css/inventory.css'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLock, faBan, faCheck } from '@fortawesome/free-solid-svg-icons' import { faLock, faBan } from '@fortawesome/free-solid-svg-icons'
import { faClipboard } from '@fortawesome/free-regular-svg-icons'
import { GameIdContext } from '../app'; import { GameIdContext } from '../app';
import Markdown from './markdown'; import Markdown from './markdown';
import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadInventoryOverviewQuery } from '../state/api'; import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadInventoryOverviewQuery } from '../state/api';
@@ -11,12 +10,10 @@ import { selectDifficulty, selectInventory } from '../state/progress';
import { store } from '../state/store'; import { store } from '../state/store';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
export function Inventory({levelInfo, openDoc, lemmaTab, setLemmaTab, enableAll=false} : export function Inventory({levelInfo, openDoc, enableAll=false} :
{ {
levelInfo: LevelInfo|InventoryOverview, levelInfo: LevelInfo|InventoryOverview,
openDoc: (props: {name: string, type: string}) => void, openDoc: (props: {name: string, type: string}) => void,
lemmaTab: any,
setLemmaTab: any,
enableAll?: boolean, enableAll?: boolean,
}) { }) {
@@ -34,20 +31,19 @@ export function Inventory({levelInfo, openDoc, lemmaTab, setLemmaTab, enableAll=
} }
<h2>Theorems</h2> <h2>Theorems</h2>
{levelInfo?.lemmas && {levelInfo?.lemmas &&
<InventoryList items={levelInfo?.lemmas} docType="Lemma" openDoc={openDoc} level={levelInfo} enableAll={enableAll} tab={lemmaTab} setTab={setLemmaTab}/> <InventoryList items={levelInfo?.lemmas} docType="Lemma" openDoc={openDoc} defaultTab={levelInfo?.lemmaTab} level={levelInfo} enableAll={enableAll}/>
} }
</div> </div>
) )
} }
function InventoryList({items, docType, openDoc, tab=null, setTab=undefined, level=undefined, enableAll=false} : function InventoryList({items, docType, openDoc, defaultTab=null, level=undefined, enableAll=false} :
{ {
items: InventoryTile[], items: InventoryTile[],
docType: string, docType: string,
openDoc(props: {name: string, type: string}): void, openDoc(props: {name: string, type: string}): void,
tab?: any, defaultTab? : string,
setTab?: any, level? : LevelInfo|InventoryOverview,
level?: LevelInfo|InventoryOverview,
enableAll?: boolean, enableAll?: boolean,
}) { }) {
// TODO: `level` is only used in the `useEffect` below to check if a new level has // TODO: `level` is only used in the `useEffect` below to check if a new level has
@@ -63,6 +59,8 @@ function InventoryList({items, docType, openDoc, tab=null, setTab=undefined, lev
} }
const categories = Array.from(categorySet).sort() const categories = Array.from(categorySet).sort()
const [tab, setTab] = useState(defaultTab)
// Add inventory items from local store as unlocked. // Add inventory items from local store as unlocked.
// Items are unlocked if they are in the local store, or if the server says they should be // Items are unlocked if they are in the local store, or if the server says they should be
// given the dependency graph. (OR-connection) (TODO: maybe add different logic for different // given the dependency graph. (OR-connection) (TODO: maybe add different logic for different
@@ -70,6 +68,13 @@ function InventoryList({items, docType, openDoc, tab=null, setTab=undefined, lev
let inv: string[] = selectInventory(gameId)(store.getState()) let inv: string[] = selectInventory(gameId)(store.getState())
let modifiedItems : InventoryTile[] = items.map(tile => inv.includes(tile.name) ? {...tile, locked: false} : tile) let modifiedItems : InventoryTile[] = items.map(tile => inv.includes(tile.name) ? {...tile, locked: false} : tile)
useEffect(() => {
// If the level specifies `LemmaTab "Nat"`, we switch to this tab on loading.
// `defaultTab` is `null` or `undefined` otherwise, in which case we don't want to switch.
if (defaultTab) {
setTab(defaultTab)
}}, [level])
return <> return <>
{categories.length > 1 && {categories.length > 1 &&
<div className="tab-bar"> <div className="tab-bar">
@@ -84,26 +89,21 @@ function InventoryList({items, docType, openDoc, tab=null, setTab=undefined, lev
(x, y) => +(docType == "Lemma") * (+x.locked - +y.locked || +x.disabled - +y.disabled) || x.displayName.localeCompare(y.displayName) (x, y) => +(docType == "Lemma") * (+x.locked - +y.locked || +x.disabled - +y.disabled) || x.displayName.localeCompare(y.displayName)
).filter(item => !item.hidden && ((tab ?? categories[0]) == item.category)).map((item, i) => { ).filter(item => !item.hidden && ((tab ?? categories[0]) == item.category)).map((item, i) => {
return <InventoryItem key={`${item.category}-${item.name}`} return <InventoryItem key={`${item.category}-${item.name}`}
item={item}
showDoc={() => {openDoc({name: item.name, type: docType})}} showDoc={() => {openDoc({name: item.name, type: docType})}}
name={item.name} displayName={item.displayName} locked={difficulty > 0 ? item.locked : false} name={item.name} displayName={item.displayName} locked={difficulty > 0 ? item.locked : false}
disabled={item.disabled} newly={item.new} enableAll={enableAll} /> disabled={item.disabled} newly={item.new} enableAll={enableAll}/>
}) })
} }
</div> </div>
</> </>
} }
function InventoryItem({item, name, displayName, locked, disabled, newly, showDoc, enableAll=false}) { function InventoryItem({name, displayName, locked, disabled, newly, showDoc, enableAll=false}) {
const icon = locked ? <FontAwesomeIcon icon={faLock} /> : const icon = locked ? <FontAwesomeIcon icon={faLock} /> :
disabled ? <FontAwesomeIcon icon={faBan} /> : item.st disabled ? <FontAwesomeIcon icon={faBan} /> : ""
const className = locked ? "locked" : disabled ? "disabled" : newly ? "new" : "" const className = locked ? "locked" : disabled ? "disabled" : newly ? "new" : ""
// Note: This is somewhat a hack as the statement of lemmas comes currently in the form
// `Namespace.statement_name (x y : Nat) : some type`
const title = locked ? "Not unlocked yet" : const title = locked ? "Not unlocked yet" :
disabled ? "Not available in this level" : (item.altTitle ? item.altTitle.substring(item.altTitle.indexOf(' ') + 1) : '') disabled ? "Not available in this level" : ""
const [copied, setCopied] = useState(false)
const handleClick = () => { const handleClick = () => {
if (enableAll || !locked) { if (enableAll || !locked) {
@@ -111,21 +111,7 @@ function InventoryItem({item, name, displayName, locked, disabled, newly, showDo
} }
} }
const copyItemName = (ev) => { return <div className={`item ${className}${enableAll ? ' enabled' : ''}`} onClick={handleClick} title={title}>{icon} {displayName}</div>
navigator.clipboard.writeText(displayName)
setCopied(true)
setInterval(() => {
setCopied(false)
}, 3000);
ev.stopPropagation()
}
return <div className={`item ${className}${enableAll ? ' enabled' : ''}`} onClick={handleClick} title={title}>
{icon} {displayName}
<div className="copy-button" onClick={copyItemName}>
{copied ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faClipboard} />}
</div>
</div>
} }
export function Documentation({name, type, handleClose}) { export function Documentation({name, type, handleClose}) {
@@ -145,25 +131,16 @@ export function Documentation({name, type, handleClose}) {
export function InventoryPanel({levelInfo, visible = true}) { export function InventoryPanel({levelInfo, visible = true}) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const [lemmaTab, setLemmaTab] = useState(levelInfo?.lemmaTab)
// The inventory is overlayed by the doc entry of a clicked item // The inventory is overlayed by the doc entry of a clicked item
const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null) const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
// Set `inventoryDoc` to `null` to close the doc // Set `inventoryDoc` to `null` to close the doc
function closeInventoryDoc() {setInventoryDoc(null)} function closeInventoryDoc() {setInventoryDoc(null)}
useEffect(() => {
// If the level specifies `LemmaTab "Nat"`, we switch to this tab on loading.
// `defaultTab` is `null` or `undefined` otherwise, in which case we don't want to switch.
if (levelInfo?.lemmaTab) {
setLemmaTab(levelInfo?.lemmaTab)
}}, [levelInfo])
return <div className={`column inventory-panel ${visible ? '' : 'hidden'}`}> return <div className={`column inventory-panel ${visible ? '' : 'hidden'}`}>
{inventoryDoc ? {inventoryDoc ?
<Documentation name={inventoryDoc.name} type={inventoryDoc.type} handleClose={closeInventoryDoc}/> <Documentation name={inventoryDoc.name} type={inventoryDoc.type} handleClose={closeInventoryDoc}/>
: :
<Inventory levelInfo={levelInfo} openDoc={setInventoryDoc} enableAll={true} lemmaTab={lemmaTab} setLemmaTab={setLemmaTab}/> <Inventory levelInfo={levelInfo} openDoc={setInventoryDoc} enableAll={true}/>
} }
</div> </div>
} }
+1 -2
View File
@@ -441,7 +441,6 @@ function PlayableLevel({impressum, setImpressum}) {
function IntroductionPanel({gameInfo}) { function IntroductionPanel({gameInfo}) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const {worldId} = useContext(WorldLevelIdContext) const {worldId} = useContext(WorldLevelIdContext)
const {mobile} = React.useContext(MobileContext)
let text: Array<string> = gameInfo.data?.worlds.nodes[worldId].introduction.split(/\n(\s*\n)+/) let text: Array<string> = gameInfo.data?.worlds.nodes[worldId].introduction.split(/\n(\s*\n)+/)
@@ -452,7 +451,7 @@ function IntroductionPanel({gameInfo}) {
hint={{text: t, hidden: false}} step={0} selected={null} toggleSelection={undefined} /> hint={{text: t, hidden: false}} step={0} selected={null} toggleSelection={undefined} />
))} ))}
</div> </div>
<div className={`button-row${mobile ? ' mobile' : ''}`}> <div className="button-row">
{gameInfo.data?.worldSize[worldId] == 0 ? {gameInfo.data?.worldSize[worldId] == 0 ?
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> : <Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/${gameId}/world/${worldId}/level/1`}> <Button to={`/${gameId}/world/${worldId}/level/1`}>
+3 -5
View File
@@ -11,7 +11,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faXmark, faCircleQuestion } from '@fortawesome/free-solid-svg-icons' import { faXmark, faCircleQuestion } from '@fortawesome/free-solid-svg-icons'
import { GameIdContext } from '../app' import { GameIdContext } from '../app'
import { useAppDispatch, useMobile } from '../hooks' import { useAppDispatch } from '../hooks'
import { selectDifficulty, changedDifficulty, selectCompleted } from '../state/progress' import { selectDifficulty, changedDifficulty, selectCompleted } from '../state/progress'
import { store } from '../state/store' import { store } from '../state/store'
@@ -197,15 +197,13 @@ export function WorldSelectionMenu({rulesHelp, setRulesHelp}) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const difficulty = useSelector(selectDifficulty(gameId)) const difficulty = useSelector(selectDifficulty(gameId))
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { mobile } = useMobile()
function label(x : number) { function label(x : number) {
return x == 0 ? 'none' : x == 1 ? 'relaxed' : 'regular' return x == 0 ? 'none' : x == 1 ? 'relaxed' : 'regular'
} }
return <nav className={`world-selection-menu${mobile ? '' : ' desktop'}`}> return <nav className="world-selection-menu">
<div className="slider-wrap"> <div className="slider-wrap">
<span className="difficulty-label">Rules <span className="difficulty-label">Rules
<FontAwesomeIcon icon={rulesHelp ? faXmark : faCircleQuestion} className='helpButton' onClick={() => (setRulesHelp(!rulesHelp))}/> <FontAwesomeIcon icon={rulesHelp ? faXmark : faCircleQuestion} className='helpButton' onClick={() => (setRulesHelp(!rulesHelp))}/>
@@ -215,7 +213,7 @@ export function WorldSelectionMenu({rulesHelp, setRulesHelp}) {
title="Game Rules" title="Game Rules"
min={0} max={2} min={0} max={2}
aria-label="Game Rules" aria-label="Game Rules"
value={difficulty} defaultValue={difficulty}
marks={[ marks={[
{value: 0, label: label(0)}, {value: 0, label: label(0)},
{value: 1, label: label(1)}, {value: 1, label: label(1)},
+1 -23
View File
@@ -26,11 +26,7 @@
.inventory .item { .inventory .item {
background: #fff; background: #fff;
border: solid 1px #777; border: solid 1px #777;
padding-left: .5rem; padding: .1em .5em;
padding-right: 1.0rem;
padding-top: .1rem;
padding-bottom: .1rem;
position: relative;
} }
.inventory .item.locked { .inventory .item.locked {
@@ -76,21 +72,3 @@
color: black; color: black;
border-bottom: 0.3em solid #999; border-bottom: 0.3em solid #999;
} }
.inventory .item .copy-button {
min-width: 3px;
min-height: 3px;
display: inline-block;
color: #ccc;
font-size: 0.6em;
padding-right: .2rem;
vertical-align: top;
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 1rem;
align-items: end;
text-align: end;
}
-14
View File
@@ -232,20 +232,6 @@ td code {
height: 100%; height: 100%;
} */ } */
.button-row.mobile {
margin: .5rem;
padding-top: .2rem;
}
.button-row.mobile .btn {
padding: .5em;
border-radius: .2em;
width: 100%;
margin: 0;
text-align: center;
}
.typewriter-interface { .typewriter-interface {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
+1 -4
View File
@@ -49,6 +49,7 @@ svg .disabled {
} }
.world-selection-menu { .world-selection-menu {
position: absolute;
right: 1em; right: 1em;
top: 1em; top: 1em;
/* margin: 1em; */ /* margin: 1em; */
@@ -59,10 +60,6 @@ svg .disabled {
filter: drop-shadow(4px 4px 5px rgba(0,0,0,0.5)); filter: drop-shadow(4px 4px 5px rgba(0,0,0,0.5));
} }
.world-selection-menu.desktop {
position: absolute;
}
.world-selection-menu .btn, .welcome .btn { .world-selection-menu .btn, .welcome .btn {
min-width: 5em; min-width: 5em;
text-align: center; text-align: center;
-1
View File
@@ -35,7 +35,6 @@ export interface InventoryTile {
locked: boolean, locked: boolean,
new: boolean, new: boolean,
hidden: boolean hidden: boolean
altTitle: string,
} }
export interface LevelInfo { export interface LevelInfo {
+459 -27
View File
@@ -13,10 +13,6 @@
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
"@fontsource/roboto": "^4.5.8", "@fontsource/roboto": "^4.5.8",
"@fontsource/roboto-mono": "^4.5.8", "@fontsource/roboto-mono": "^4.5.8",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@leanprover/infoview": "^0.4.3", "@leanprover/infoview": "^0.4.3",
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.1", "@mui/material": "^5.11.1",
@@ -2225,6 +2221,231 @@
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
}, },
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
"cpu": [
"loong64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
"cpu": [
"mips64el"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
"cpu": [
"ppc64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
"cpu": [
"riscv64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
"cpu": [
"s390x"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.18.20", "version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
@@ -2240,6 +2461,96 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@esbuild/netbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@floating-ui/core": { "node_modules/@floating-ui/core": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz",
@@ -2285,45 +2596,33 @@
"integrity": "sha512-KrJdmkqz6DszT2wV/bbhXef4r0hV3B0vw2mAqei8A2kRnvq+gcJLmmIeQ94vu9VEXrUQzos5M9lH1TAAXpRphw==" "integrity": "sha512-KrJdmkqz6DszT2wV/bbhXef4r0hV3B0vw2mAqei8A2kRnvq+gcJLmmIeQ94vu9VEXrUQzos5M9lH1TAAXpRphw=="
}, },
"node_modules/@fortawesome/fontawesome-common-types": { "node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.5.1", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz",
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==",
"hasInstallScript": true, "hasInstallScript": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@fortawesome/fontawesome-svg-core": { "node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.5.1", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz",
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1" "@fortawesome/fontawesome-common-types": "6.4.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
}, },
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@fortawesome/free-solid-svg-icons": { "node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.5.1", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz",
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1" "@fortawesome/fontawesome-common-types": "6.4.2"
}, },
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@@ -4778,6 +5077,81 @@
} }
} }
}, },
"node_modules/@swc/core-darwin-arm64": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.95.tgz",
"integrity": "sha512-VAuBAP3MNetO/yBIBzvorUXq7lUBwhfpJxYViSxyluMwtoQDhE/XWN598TWMwMl1ZuImb56d7eUsuFdjgY7pJw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.95.tgz",
"integrity": "sha512-20vF2rvUsN98zGLZc+dsEdHvLoCuiYq/1B+TDeE4oolgTFDmI1jKO+m44PzWjYtKGU9QR95sZ6r/uec0QC5O4Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.95.tgz",
"integrity": "sha512-oEudEM8PST1MRNGs+zu0cx5i9uP8TsLE4/L9HHrS07Ck0RJ3DCj3O2fU832nmLe2QxnAGPwBpSO9FntLfOiWEQ==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.95.tgz",
"integrity": "sha512-pIhFI+cuC1aYg+0NAPxwT/VRb32f2ia8oGxUjQR6aJg65gLkUYQzdwuUmpMtFR2WVf7WVFYxUnjo4UyMuyh3ng==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.95.tgz",
"integrity": "sha512-ZpbTr+QZDT4OPJfjPAmScqdKKaT+wGurvMU5AhxLaf85DuL8HwUwwlL0n1oLieLc47DwIJEMuKQkYhXMqmJHlg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": { "node_modules/@swc/core-linux-x64-gnu": {
"version": "1.3.95", "version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.95.tgz", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.95.tgz",
@@ -4808,6 +5182,51 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.95.tgz",
"integrity": "sha512-YaP4x/aZbUyNdqCBpC2zL8b8n58MEpOUpmOIZK6G1SxGi+2ENht7gs7+iXpWPc0sy7X3YPKmSWMAuui0h8lgAA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.95.tgz",
"integrity": "sha512-w0u3HI916zT4BC/57gOd+AwAEjXeUlQbGJ9H4p/gzs1zkSHtoDQghVUNy3n/ZKp9KFod/95cA8mbVF9t1+6epQ==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.3.95",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.95.tgz",
"integrity": "sha512-5RGnMt0S6gg4Gc6QtPUJ3Qs9Un4sKqccEzgH/tj7V/DVTJwKdnBKxFZfgQ34OR2Zpz7zGOn889xwsFVXspVWNA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": { "node_modules/@swc/counter": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz",
@@ -8116,6 +8535,19 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-4
View File
@@ -10,10 +10,6 @@
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
"@fontsource/roboto": "^4.5.8", "@fontsource/roboto": "^4.5.8",
"@fontsource/roboto-mono": "^4.5.8", "@fontsource/roboto-mono": "^4.5.8",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@leanprover/infoview": "^0.4.3", "@leanprover/infoview": "^0.4.3",
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.1", "@mui/material": "^5.11.1",
+21 -93
View File
@@ -124,7 +124,7 @@ elab "CoverImage" t:str : command => do
/-! # Inventory /-! # Inventory
The inventory contains docs for tactics, theorems, and definitions. These are all locked The inventory contains docs for tactics, lemmas, and definitions. These are all locked
in the first level and get enabled during the game. in the first level and get enabled during the game.
-/ -/
@@ -147,22 +147,22 @@ elab doc:docComment ? "TacticDoc" name:ident content:str ? : command => do
displayName := name.getId.toString displayName := name.getId.toString
content := doc }) content := doc })
/-- Documentation entry of a theorem. Example: /-- Documentation entry of a lemma. Example:
``` ```
TheoremDoc Nat.succ_pos as "succ_pos" in "Nat" "says `0 < n.succ`, etc." LemmaDoc Nat.succ_pos as "succ_pos" in "Nat" "says `0 < n.succ`, etc."
``` ```
* The first identifier is used in the commands `[New/Only/Disabled]Theorem`. * The first identifier is used in the commands `[New/Only/Disabled]Lemma`.
It is preferably the true name of the theorem. However, this is not required. It is preferably the true name of the lemma. However, this is not required.
* The string following `as` is the displayed name (in the Inventory). * The string following `as` is the displayed name (in the Inventory).
* The identifier after `in` is the category to group theorems by (in the Inventory). * The identifier after `in` is the category to group lemmas by (in the Inventory).
* The description is a string supporting Markdown. * The description is a string supporting Markdown.
Use `[[mathlib_doc]]` in the string to insert a link to the mathlib doc page. This requires Use `[[mathlib_doc]]` in the string to insert a link to the mathlib doc page. This requires
The theorem/definition to have the same fully qualified name as in mathlib. The lemma/definition to have the same fully qualified name as in mathlib.
-/ -/
elab doc:docComment ? "TheoremDoc" name:ident "as" displayName:str "in" category:str content:str ? : elab doc:docComment ? "LemmaDoc" name:ident "as" displayName:str "in" category:str content:str ? :
command => do command => do
let doc ← parseDocCommentLegacy doc content let doc ← parseDocCommentLegacy doc content
modifyEnv (inventoryTemplateExt.addEntry · { modifyEnv (inventoryTemplateExt.addEntry · {
@@ -172,7 +172,7 @@ elab doc:docComment ? "TheoremDoc" name:ident "as" displayName:str "in" category
displayName := displayName.getString displayName := displayName.getString
content := doc }) content := doc })
-- TODO: Catch the following behaviour. -- TODO: Catch the following behaviour.
-- 1. if `TheoremDoc` appears in the same file as `Statement`, it will silently use -- 1. if `LemmaDoc` appears in the same file as `Statement`, it will silently use
-- it but display the info that it wasn't found in `Statement` -- it but display the info that it wasn't found in `Statement`
-- 2. if it appears in a later file, however, it will silently not do anything and keep -- 2. if it appears in a later file, however, it will silently not do anything and keep
-- the first one. -- the first one.
@@ -190,7 +190,7 @@ DefinitionDoc Function.Bijective as "Bijective" "defined as `Injective f ∧ Sur
* The description is a string supporting Markdown. * The description is a string supporting Markdown.
Use `[[mathlib_doc]]` in the string to insert a link to the mathlib doc page. This requires Use `[[mathlib_doc]]` in the string to insert a link to the mathlib doc page. This requires
The theorem/definition to have the same fully qualified name as in mathlib. The lemma/definition to have the same fully qualified name as in mathlib.
-/ -/
elab doc:docComment ? "DefinitionDoc" name:ident "as" displayName:str template:str ? : command => do elab doc:docComment ? "DefinitionDoc" name:ident "as" displayName:str template:str ? : command => do
let doc ← parseDocCommentLegacy doc template let doc ← parseDocCommentLegacy doc template
@@ -202,13 +202,8 @@ elab doc:docComment ? "DefinitionDoc" name:ident "as" displayName:str template:s
/-! ## Add inventory items -/ /-! ## Add inventory items -/
def checkCommandNotDuplicated (items : Array Name) (cmd := "Command") : CommandElabM Unit := do
if ¬ items.isEmpty then
logWarning s!"You should only use one `{cmd}` per level, but it takes multiple arguments: `{cmd} obj₁ obj₂ obj₃`!"
/-- Declare tactics that are introduced by this level. -/ /-- Declare tactics that are introduced by this level. -/
elab "NewTactic" args:ident* : command => do elab "NewTactic" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).tactics.new) "NewTactic"
for name in ↑args do for name in ↑args do
checkInventoryDoc .Tactic name -- TODO: Add (template := "[docstring]") checkInventoryDoc .Tactic name -- TODO: Add (template := "[docstring]")
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
@@ -216,16 +211,14 @@ elab "NewTactic" args:ident* : command => do
/-- Declare tactics that are introduced by this level but do not show up in inventory. -/ /-- Declare tactics that are introduced by this level but do not show up in inventory. -/
elab "NewHiddenTactic" args:ident* : command => do elab "NewHiddenTactic" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).tactics.hidden) "NewHiddenTactic"
for name in ↑args do for name in ↑args do
checkInventoryDoc .Tactic name (template := "") checkInventoryDoc .Tactic name (template := "")
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
tactics := {level.tactics with new := level.tactics.new ++ args.map (·.getId), tactics := {level.tactics with new := level.tactics.new ++ args.map (·.getId),
hidden := level.tactics.hidden ++ args.map (·.getId)}} hidden := level.tactics.hidden ++ args.map (·.getId)}}
/-- Declare theorems that are introduced by this level. -/ /-- Declare lemmas that are introduced by this level. -/
elab "NewTheorem" args:ident* : command => do elab "NewLemma" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.new) "NewTheorem"
for name in ↑args do for name in ↑args do
try let _decl ← getConstInfo name.getId catch try let _decl ← getConstInfo name.getId catch
| _ => logErrorAt name m!"unknown identifier '{name}'." | _ => logErrorAt name m!"unknown identifier '{name}'."
@@ -235,7 +228,6 @@ elab "NewTheorem" args:ident* : command => do
/-- Declare definitions that are introduced by this level. -/ /-- Declare definitions that are introduced by this level. -/
elab "NewDefinition" args:ident* : command => do elab "NewDefinition" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).definitions.new) "NewDefinition"
for name in ↑args do checkInventoryDoc .Definition name -- TODO: Add (template := "[mathlib]") for name in ↑args do checkInventoryDoc .Definition name -- TODO: Add (template := "[mathlib]")
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
definitions := {level.definitions with new := args.map (·.getId)}} definitions := {level.definitions with new := args.map (·.getId)}}
@@ -243,36 +235,31 @@ elab "NewDefinition" args:ident* : command => do
/-- Declare tactics that are temporarily disabled in this level. /-- Declare tactics that are temporarily disabled in this level.
This is ignored if `OnlyTactic` is set. -/ This is ignored if `OnlyTactic` is set. -/
elab "DisabledTactic" args:ident* : command => do elab "DisabledTactic" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).tactics.disabled) "DisabledTactic"
for name in ↑args do checkInventoryDoc .Tactic name for name in ↑args do checkInventoryDoc .Tactic name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
tactics := {level.tactics with disabled := args.map (·.getId)}} tactics := {level.tactics with disabled := args.map (·.getId)}}
/-- Declare theorems that are temporarily disabled in this level. /-- Declare lemmas that are temporarily disabled in this level.
This is ignored if `OnlyTheorem` is set. -/ This is ignored if `OnlyLemma` is set. -/
elab "DisabledTheorem" args:ident* : command => do elab "DisabledLemma" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.disabled) "DisabledTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with disabled := args.map (·.getId)}} lemmas := {level.lemmas with disabled := args.map (·.getId)}}
/-- Declare definitions that are temporarily disabled in this level -/ /-- Declare definitions that are temporarily disabled in this level -/
elab "DisabledDefinition" args:ident* : command => do elab "DisabledDefinition" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).definitions.disabled) "DisabledDefinition"
for name in ↑args do checkInventoryDoc .Definition name for name in ↑args do checkInventoryDoc .Definition name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
definitions := {level.definitions with disabled := args.map (·.getId)}} definitions := {level.definitions with disabled := args.map (·.getId)}}
/-- Temporarily disable all tactics except the ones declared here -/ /-- Temporarily disable all tactics except the ones declared here -/
elab "OnlyTactic" args:ident* : command => do elab "OnlyTactic" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).tactics.only) "OnlyTactic"
for name in ↑args do checkInventoryDoc .Tactic name for name in ↑args do checkInventoryDoc .Tactic name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
tactics := {level.tactics with only := args.map (·.getId)}} tactics := {level.tactics with only := args.map (·.getId)}}
/-- Temporarily disable all theorems except the ones declared here -/ /-- Temporarily disable all lemmas except the ones declared here -/
elab "OnlyTheorem" args:ident* : command => do elab "OnlyLemma" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.only) "OnlyTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with only := args.map (·.getId)}} lemmas := {level.lemmas with only := args.map (·.getId)}}
@@ -280,56 +267,13 @@ elab "OnlyTheorem" args:ident* : command => do
/-- Temporarily disable all definitions except the ones declared here. /-- Temporarily disable all definitions except the ones declared here.
This is ignored if `OnlyDefinition` is set. -/ This is ignored if `OnlyDefinition` is set. -/
elab "OnlyDefinition" args:ident* : command => do elab "OnlyDefinition" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).definitions.only) "OnlyDefinition"
for name in ↑args do checkInventoryDoc .Definition name for name in ↑args do checkInventoryDoc .Definition name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
definitions := {level.definitions with only := args.map (·.getId)}} definitions := {level.definitions with only := args.map (·.getId)}}
/-- Define which tab of Lemmas is opened by default. Usage: `TheoremTab "Nat"`. /-- Define which tab of Lemmas is opened by default. Usage: `LemmaTab "Nat"`.
If omitted, the current tab will remain open. -/ If omitted, the current tab will remain open. -/
elab "TheoremTab" category:str : command => elab "LemmaTab" category:str : command =>
modifyCurLevel fun level => pure {level with lemmaTab := category.getString}
/-! DEPRECATED -/
elab doc:docComment ? "LemmaDoc" name:ident "as" displayName:str "in" category:str content:str ? :
command => do
logWarning "Deprecated. Has been renamed to `TheoremDoc`"
let doc ← parseDocCommentLegacy doc content
modifyEnv (inventoryTemplateExt.addEntry · {
type := .Lemma
name := name.getId
category := category.getString
displayName := displayName.getString
content := doc })
elab "NewLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `NewTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.new) "NewLemma"
for name in ↑args do
try let _decl ← getConstInfo name.getId catch
| _ => logErrorAt name m!"unknown identifier '{name}'."
checkInventoryDoc .Lemma name -- TODO: Add (template := "[mathlib]")
modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with new := args.map (·.getId)}}
elab "DisabledLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `DisabledTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.disabled) "DisabledLemma"
for name in ↑args do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with disabled := args.map (·.getId)}}
elab "OnlyLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `OnlyTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.only) "OnlyLemma"
for name in ↑args do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with only := args.map (·.getId)}}
elab "LemmaTab" category:str : command => do
logWarning "Deprecated. Has been renamed to `TheoremTab`"
modifyCurLevel fun level => pure {level with lemmaTab := category.getString} modifyCurLevel fun level => pure {level with lemmaTab := category.getString}
/-! # Exercise Statement -/ /-! # Exercise Statement -/
@@ -837,7 +781,6 @@ elab "MakeGame" : command => do
name := item name := item
displayName := data.displayName displayName := data.displayName
category := data.category category := data.category
altTitle := data.statement
hidden := hiddenItems.contains item }) hidden := hiddenItems.contains item })
@@ -857,7 +800,6 @@ elab "MakeGame" : command => do
displayName := data.displayName displayName := data.displayName
category := data.category category := data.category
locked := false locked := false
altTitle := data.statement
hidden := hiddenItems.contains item } hidden := hiddenItems.contains item }
itemsInWorld := itemsInWorld.insert worldId items itemsInWorld := itemsInWorld.insert worldId items
@@ -877,8 +819,7 @@ elab "MakeGame" : command => do
displayName := data.displayName displayName := data.displayName
category := data.category category := data.category
locked := false locked := false
altTitle := data.statement hidden := levelInfo.hidden.contains item }
hidden := hiddenItems.contains item }
-- add the exercise statement from the previous level -- add the exercise statement from the previous level
-- if it was named -- if it was named
@@ -891,7 +832,6 @@ elab "MakeGame" : command => do
name := name name := name
displayName := data.displayName displayName := data.displayName
category := data.category category := data.category
altTitle := data.statement
locked := false } locked := false }
-- add marks for `disabled` and `new` lemmas here, so that they only apply to -- add marks for `disabled` and `new` lemmas here, so that they only apply to
@@ -911,16 +851,4 @@ elab "MakeGame" : command => do
return level.setComputedInventory inventoryType itemsArray return level.setComputedInventory inventoryType itemsArray
allItemsByType := allItemsByType.insert inventoryType allItems allItemsByType := allItemsByType.insert inventoryType allItems
let getTiles (type : InventoryType) : CommandElabM (Array InventoryTile) := do saveGameData allItemsByType
(allItemsByType.findD type {}).toArray.mapM (fun name => do
let some item ← getInventoryItem? name type
| throwError "Expected item to exist: {name}"
return item.toTile)
let inventory : InventoryOverview := {
lemmas := (← getTiles .Lemma).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
tactics := (← getTiles .Tactic).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
definitions := (← getTiles .Definition).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
lemmaTab := none
}
saveGameData allItemsByType inventory
-2
View File
@@ -106,8 +106,6 @@ structure InventoryTile where
new := false new := false
/-- hide the item in the inventory display -/ /-- hide the item in the inventory display -/
hidden := false hidden := false
/-- hover text -/
altTitle : String := default
deriving ToJson, FromJson, Repr, Inhabited deriving ToJson, FromJson, Repr, Inhabited
def InventoryItem.toTile (item : InventoryItem) : InventoryTile := { def InventoryItem.toTile (item : InventoryItem) : InventoryTile := {
-2
View File
@@ -478,8 +478,6 @@ section Initialization
return (ctx, return (ctx,
{ doc := doc { doc := doc
initHeaderStx := headerStx initHeaderStx := headerStx
currHeaderStx := headerStx
importCachingTask? := none
pendingRequests := RBMap.empty pendingRequests := RBMap.empty
rpcSessions := RBMap.empty rpcSessions := RBMap.empty
}) })
+2 -2
View File
@@ -46,8 +46,8 @@ partial def matchExpr (pattern : Expr) (e : Expr) (bij : FVarBijection := {}) :
| .bvar i1, .bvar i2 => if i1 == i2 then bij else none | .bvar i1, .bvar i2 => if i1 == i2 then bij else none
| .fvar i1, .fvar i2 => bij.insert? i1 i2 | .fvar i1, .fvar i2 => bij.insert? i1 i2
| .mvar _, .mvar _ => bij | .mvar _, .mvar _ => bij
| .sort _u1, .sort _u2 => bij -- TODO? | .sort u1, .sort u2 => bij -- TODO?
| .const n1 _ls1, .const n2 _ls2 => | .const n1 ls1, .const n2 ls2 =>
if n1 == n2 then bij else none -- && (← (ls1.zip ls2).allM fun (l1, l2) => Meta.isLevelDefEq l1 l2) if n1 == n2 then bij else none -- && (← (ls1.zip ls2).allM fun (l1, l2) => Meta.isLevelDefEq l1 l2)
| .app f1 a1, .app f2 a2 => | .app f1 a1, .app f2 a2 =>
some bij some bij
+12 -2
View File
@@ -27,8 +27,7 @@ def copyImages : IO Unit := do
#eval IO.FS.createDirAll ".lake/gamedata/" #eval IO.FS.createDirAll ".lake/gamedata/"
-- TODO: register all of this as ToJson instance? -- TODO: register all of this as ToJson instance?
def saveGameData (allItemsByType : HashMap InventoryType (HashSet Name)) def saveGameData (allItemsByType : HashMap InventoryType (HashSet Name)) : CommandElabM Unit := do
(inventory : InventoryOverview): CommandElabM Unit := do
let game ← getCurGame let game ← getCurGame
let env ← getEnv let env ← getEnv
let path : System.FilePath := s!"{← IO.currentDir}" / ".lake" / "gamedata" let path : System.FilePath := s!"{← IO.currentDir}" / ".lake" / "gamedata"
@@ -52,4 +51,15 @@ def saveGameData (allItemsByType : HashMap InventoryType (HashSet Name))
| throwError "Expected item to exist: {name}" | throwError "Expected item to exist: {name}"
IO.FS.writeFile (path / s!"doc__{inventoryType}__{name}.json") (toString (toJson item)) IO.FS.writeFile (path / s!"doc__{inventoryType}__{name}.json") (toString (toJson item))
let getTiles (type : InventoryType) : CommandElabM (Array InventoryTile) := do
(allItemsByType.findD type {}).toArray.mapM (fun name => do
let some item ← getInventoryItem? name type
| throwError "Expected item to exist: {name}"
return item.toTile)
let inventory : InventoryOverview := {
lemmas := ← getTiles .Lemma
tactics := ← getTiles .Tactic
definitions := ← getTiles .Definition
lemmaTab := none
}
IO.FS.writeFile (path / s!"inventory.json") (toString (toJson inventory)) IO.FS.writeFile (path / s!"inventory.json") (toString (toJson inventory))
+1 -1
View File
@@ -103,7 +103,7 @@ def initAndRunWatchdog (args : List String) (i o e : FS.Stream) : IO Unit := do
let gameName := if args.length < 4 then defaultGameName else args[3]! let gameName := if args.length < 4 then defaultGameName else args[3]!
let workerPath := "./gameserver" let workerPath := "./gameserver"
-- TODO: Do the following commands slow us down? -- TODO: Do the following commands slow us down?
let srcSearchPath ← initSrcSearchPath let srcSearchPath ← initSrcSearchPath (← getBuildDir)
let references ← IO.mkRef (← loadReferences) let references ← IO.mkRef (← loadReferences)
let fileWorkersRef ← IO.mkRef (RBMap.empty : FileWorkerMap) let fileWorkersRef ← IO.mkRef (RBMap.empty : FileWorkerMap)
let i ← maybeTee "wdIn.txt" false i let i ← maybeTee "wdIn.txt" false i
+2 -2
View File
@@ -4,10 +4,10 @@
[{"url": "https://github.com/leanprover/std4.git", [{"url": "https://github.com/leanprover/std4.git",
"type": "git", "type": "git",
"subDir": null, "subDir": null,
"rev": "ce2db21d86502e00c4761da5ade58a61612de656", "rev": "2e4a3586a8f16713f16b2d2b3af3d8e65f3af087",
"name": "std", "name": "std",
"manifestFile": "lake-manifest.json", "manifestFile": "lake-manifest.json",
"inputRev": "v4.4.0-rc1", "inputRev": "v4.3.0",
"inherited": false, "inherited": false,
"configFile": "lakefile.lean"}], "configFile": "lakefile.lean"}],
"name": "GameServer", "name": "GameServer",
+1 -1
View File
@@ -1 +1 @@
leanprover/lean4:v4.4.0-rc1 leanprover/lean4:v4.3.0