diff --git a/public/images/minecoin.png b/public/images/minecoin.png
new file mode 100644
index 00000000..af868fa3
Binary files /dev/null and b/public/images/minecoin.png differ
diff --git a/src/app/App.tsx b/src/app/App.tsx
index e66623ee..68612315 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -1,11 +1,16 @@
import type { RouterOnChangeArgs } from 'preact-router'
import { Router } from 'preact-router'
+import { useCallback, useEffect, useMemo, useState } from 'preact/hooks'
import '../styles/global.css'
import '../styles/nodes.css'
import { Analytics } from './Analytics.js'
-import { cleanUrl } from './Utils.js'
import { Header } from './components/index.js'
+import { TextComponent } from './components/TextComponent.jsx'
import { Changelog, Customized, Generator, Generators, Guide, Guides, Home, LegacyPartners, Partners, Sounds, Transformation, Versions, WhatsNew, Worldgen } from './pages/index.js'
+import { cleanUrl } from './Utils.js'
+
+const DEMO_KEY = 'misode_demo_2024'
+const DEMO_INITIAL_SECONDS = 300
export function App() {
const changeRoute = (e: RouterOnChangeArgs) => {
@@ -14,6 +19,33 @@ export function App() {
setTimeout(() => Analytics.pageview(cleanUrl(e.url)))
}
+ const [demoTimer, setDemoTimer] = useState(DEMO_INITIAL_SECONDS)
+
+ useEffect(() => {
+ const storedKey = localStorage.getItem(DEMO_KEY)
+ if (storedKey !== null) {
+ setDemoTimer(parseInt(storedKey))
+ }
+ const interval = setInterval(() => {
+ setDemoTimer(timer => {
+ const newTimer = Math.max(0, timer - 1)
+ localStorage.setItem(DEMO_KEY, newTimer.toFixed())
+ return newTimer
+ })
+ }, 1000)
+ return () => clearInterval(interval)
+ }, [])
+
+ const resetDemo = useCallback(() => {
+ setDemoTimer(DEMO_INITIAL_SECONDS)
+ }, [])
+
+ const formattedRemainingTime = useMemo(() => {
+ const minutes = Math.floor(demoTimer / 60).toFixed().padStart(2, '0')
+ const seconds = (demoTimer % 60).toFixed().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }, [demoTimer])
+
return <>
+