This commit is contained in:
He4eT 2023-05-21 16:46:30 +03:00 committed by Alexey
commit 7a96d99055
21 changed files with 174 additions and 170 deletions

View file

@ -1,6 +1,6 @@
import { h } from 'preact' import { h } from 'preact'
export default function ({ theme, setLocation, buildLink }) { export default function LocalFileSelector ({ theme, setLocation, buildLink }) {
const fileInputHandler = ({ target }) => { const fileInputHandler = ({ target }) => {
const file = target.files[0] const file = target.files[0]
const url = `${URL.createObjectURL(file)}#${file.name}` const url = `${URL.createObjectURL(file)}#${file.name}`

View file

@ -1,6 +1,6 @@
import { h } from 'preact' import { h } from 'preact'
export default function ({ theme, setLocation, buildLink }) { export default function TargetURLSelector ({ theme, setLocation, buildLink }) {
const urlRE = /^(http|https):\/\/[^ "]+$/ const urlRE = /^(http|https):\/\/[^ "]+$/
const onKeyPress = ({ keyCode, target }) => { const onKeyPress = ({ keyCode, target }) => {

View file

@ -2,10 +2,11 @@ import { h } from 'preact'
import { Link } from 'wouter-preact' import { Link } from 'wouter-preact'
import { import {
buildPlayLinkHref buildPlayLinkHref,
} from '~/src/utils/utils.routing' } from '~/src/utils/utils.routing'
export default ({ name, ifdb, url }) => ( export default function GameEntry ({ name, ifdb, url }) {
return (
<div> <div>
<h4>{name}</h4> <h4>{name}</h4>
<a <a
@ -21,3 +22,4 @@ export default ({ name, ifdb, url }) => (
</Link> </Link>
</div> </div>
) )
}

View file

@ -3,7 +3,7 @@ import { useEffect, useState } from 'preact/hooks'
import TextMessage from './TextMessage' import TextMessage from './TextMessage'
export default function ({ inbox, currentWindow }) { export default function GridBuffer ({ inbox, currentWindow }) {
const [prevMessages, setPrevMessages] = useState([]) const [prevMessages, setPrevMessages] = useState([])
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
@ -14,7 +14,7 @@ export default function ({ inbox, currentWindow }) {
const currentInbox = currentInboxObj?.lines ?? [] const currentInbox = currentInboxObj?.lines ?? []
const newOrPrev = (cur, prev) => i => { const newOrPrev = (cur, prev) => (i) => {
const byId = (list, i) => const byId = (list, i) =>
list.find(({ line }) => line === i) list.find(({ line }) => line === i)
@ -31,23 +31,23 @@ export default function ({ inbox, currentWindow }) {
const rawMessagesContent = const rawMessagesContent =
rawMessages rawMessages
.map(x => x.content) .map((x) => x.content)
.map(([x]) => x) .map(([x]) => x)
.map(({ text }) => text) .map(({ text }) => text)
.map(text => text.trim()) .map((text) => text.trim())
const isEmpty = const isEmpty =
rawMessagesContent rawMessagesContent
.map(text => text.length) .map((text) => text.length)
.every(l => l === 0) .every((l) => l === 0)
const messages = const messages =
rawMessagesContent rawMessagesContent
.map(text => .map((text) =>
text.replace(' ', ' / ')) text.replace(' ', ' / '))
.map(text => ({ .map((text) => ({
style: 'grid', style: 'grid',
text text,
})) }))
setMessages(isEmpty ? [] : messages) setMessages(isEmpty ? [] : messages)

View file

@ -33,11 +33,11 @@ const keyNames = {
} }
/* eslint-enable */ /* eslint-enable */
export default function ({ export default function InputBox ({
inputType, inputType,
windows, windows,
currentWindowId, currentWindowId,
sendMessage sendMessage,
}) { }) {
const [targetWindow, setTargetWindow] = useState(null) const [targetWindow, setTargetWindow] = useState(null)
const [inputText, setInputText] = useState('') const [inputText, setInputText] = useState('')
@ -56,7 +56,7 @@ export default function ({
id === currentWindowId)) id === currentWindowId))
}, [currentWindowId, windows]) }, [currentWindowId, windows])
const send = message => { const send = (message) => {
sendMessage( sendMessage(
message, message,
inputType, inputType,
@ -65,12 +65,12 @@ export default function ({
setInputText('') setInputText('')
} }
const charHandler = event => const charHandler = (event) =>
(event.keyCode === 229 (event.keyCode === 229
? charHandlerMobile ? charHandlerMobile
: charHandlerDefault)(event) : charHandlerDefault)(event)
const charHandlerDefault = event => { const charHandlerDefault = (event) => {
event.preventDefault() event.preventDefault()
const key = const key =
@ -80,8 +80,8 @@ export default function ({
send(key) send(key)
} }
const charHandlerMobile = event => const charHandlerMobile = (event) =>
setTimeout(_ => { setTimeout(() => {
send(event.target.value.slice(-1).toUpperCase()) send(event.target.value.slice(-1).toUpperCase())
setInputText('') setInputText('')
}) })
@ -96,7 +96,7 @@ export default function ({
if (keyCode === keyCodes.KEY_UP) { if (keyCode === keyCodes.KEY_UP) {
setInputText(lastInput) setInputText(lastInput)
setTimeout(_ => { setTimeout(() => {
const end = lastInput.length const end = lastInput.length
inputEl.current.setSelectionRange(end, end) inputEl.current.setSelectionRange(end, end)
}, 0) }, 0)
@ -113,16 +113,16 @@ export default function ({
autocorrect: 'off', autocorrect: 'off',
spellcheck: 'false', spellcheck: 'false',
placeholder: 'Press any key here', placeholder: 'Press any key here',
onKeyDown: charHandler onKeyDown: charHandler,
}, },
line: { line: {
placeholder: ' > ', placeholder: ' > ',
onKeyDown: lineArrowHandler, onKeyDown: lineArrowHandler,
onKeyPress: lineHandler onKeyPress: lineHandler,
} },
} }
const enterFullscreen = _ => const enterFullscreen = () =>
document.documentElement.requestFullscreen() document.documentElement.requestFullscreen()
return ( return (

View file

@ -15,7 +15,7 @@ import './player.css'
const INITIAL_STATUS = { const INITIAL_STATUS = {
stage: 'loading', stage: 'loading',
details: ['Preparing'] details: ['Preparing'],
} }
const runMachine = ({ engine: Engine, file, handlers }) => { const runMachine = ({ engine: Engine, file, handlers }) => {
@ -28,8 +28,8 @@ const runMachine = ({ engine: Engine, file, handlers }) => {
return { sendFn, instance: vm } return { sendFn, instance: vm }
} }
export default function ({ export default function Player ({
vmParts: { file, engine }, singleWindow vmParts: { file, engine }, singleWindow,
}) { }) {
const [status, setStatus] = useState(INITIAL_STATUS) const [status, setStatus] = useState(INITIAL_STATUS)
@ -50,28 +50,28 @@ export default function ({
setWindows, setWindows,
setCurrentWindowId, setCurrentWindowId,
setInputType, setInputType,
setInbox setInbox,
}) }),
}) })
setVm(vm) setVm(vm)
}, [file, engine]) }, [file, engine])
useEffect(() => { useEffect(() => {
setSendMessage(_ => vm setSendMessage(() => vm
? vm.sendFn ? vm.sendFn
: null) : null)
}, [vm]) }, [vm])
const textWindow = inbox => currentWindow => { const textWindow = (inbox) => (currentWindow) => {
const props = { const props = {
inbox, inbox,
currentWindow currentWindow,
} }
return ({ return ({
buffer: <TextBuffer {...props} />, buffer: <TextBuffer {...props} />,
grid: <GridBuffer {...props} /> grid: <GridBuffer {...props} />,
})[currentWindow.type] })[currentWindow.type]
} }
@ -86,14 +86,14 @@ export default function ({
.sort(byTop) .sort(byTop)
.filter(singleWindow .filter(singleWindow
? ({ id }) => id === currentWindowId ? ({ id }) => id === currentWindowId
: _ => true) : () => true)
.map(textWindow(inbox))} .map(textWindow(inbox))}
</section> </section>
<InputBox {...{ <InputBox {...{
inputType, inputType,
windows, windows,
currentWindowId, currentWindowId,
sendMessage sendMessage,
}} /> }} />
</section>) </section>)
} }

View file

@ -1,12 +1,12 @@
import { h } from 'preact' import { h } from 'preact'
import { Link } from 'wouter-preact' import { Link } from 'wouter-preact'
const fail = details => ( const fail = (details) => (
<div className='status fail'> <div className='status fail'>
<h1> <h1>
Error Error
</h1> </h1>
{details.map(x => (<p key={x}>{x}</p>))} {details.map((x) => (<p key={x}>{x}</p>))}
<hr /> <hr />
<Link href='/'> <Link href='/'>
Home Home
@ -21,9 +21,9 @@ const fail = details => (
</div> </div>
) )
const loading = details => ( const loading = (details) => (
<div className='status loading'> <div className='status loading'>
{details.map(x => (<div key={x}>{x}</div>))} {details.map((x) => (<div key={x}>{x}</div>))}
</div> </div>
) )

View file

@ -3,10 +3,10 @@ import { useEffect, useRef, useState } from 'preact/hooks'
import TextMessage from './TextMessage' import TextMessage from './TextMessage'
const isFakeStatus = w => const isFakeStatus = (w) =>
w.height < 5 w.height < 5
const trimInputPrompt = messages => const trimInputPrompt = (messages) =>
messages.length < 1 messages.length < 1
? messages ? messages
: messages.slice(-1)[0].text === '>' : messages.slice(-1)[0].text === '>'
@ -21,7 +21,7 @@ const parseInbox = (inbox, currentWindow) => {
if (!currentInbox) { if (!currentInbox) {
return { return {
clear: false, clear: false,
incoming: [] incoming: [],
} }
} }
@ -45,11 +45,11 @@ const parseInbox = (inbox, currentWindow) => {
incoming, incoming,
clear: isFakeStatus(currentWindow) clear: isFakeStatus(currentWindow)
? true ? true
: currentInbox.clear : currentInbox.clear,
} }
} }
export default function ({ inbox, currentWindow }) { export default function TextBuffer ({ inbox, currentWindow }) {
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
const textBufferEl = useRef(null) const textBufferEl = useRef(null)
@ -57,7 +57,7 @@ export default function ({ inbox, currentWindow }) {
const { incoming, clear } = const { incoming, clear } =
parseInbox(inbox, currentWindow) parseInbox(inbox, currentWindow)
setMessages(messages => clear setMessages((messages) => clear
? incoming ? incoming
: messages.concat(incoming)) : messages.concat(incoming))

View file

@ -1,6 +1,6 @@
import { h } from 'preact' import { h } from 'preact'
export default function ({ style, text }) { export default function TextMessage ({ style, text }) {
const defaultContent = ( const defaultContent = (
<span className={['message', style].join(' ')}> <span className={['message', style].join(' ')}>
{text} {text}
@ -11,6 +11,6 @@ export default function ({ style, text }) {
input: (<span className='message input'>&gt; {text}</span>), input: (<span className='message input'>&gt; {text}</span>),
subheader: (<strong className='message subheader'>{text}</strong>), subheader: (<strong className='message subheader'>{text}</strong>),
emphasized: (<em className='message emphasized'>{text}</em>), emphasized: (<em className='message emphasized'>{text}</em>),
endOfLine: (<br />) endOfLine: (<br />),
})[style] || defaultContent })[style] || defaultContent
} }

View file

@ -8,16 +8,16 @@ import Status from './Status'
const INITIAL_STATUS = { const INITIAL_STATUS = {
stage: 'loading', stage: 'loading',
details: ['Loading'] details: ['Loading'],
} }
const prepareVM = ({ url, setStatus, setParts }) => { const prepareVM = ({ url, setStatus, setParts }) => {
const st = (stage, details) => args => { const st = (stage, details) => (args) => {
setStatus({ stage, details: [details] }) setStatus({ stage, details: [details] })
return args return args
} }
const cleanUrl = url => const cleanUrl = (url) =>
url.startsWith('blob:') url.startsWith('blob:')
? url.replace(/#(.*)$/g, '') ? url.replace(/#(.*)$/g, '')
: url : url
@ -27,21 +27,21 @@ const prepareVM = ({ url, setStatus, setParts }) => {
.then(cleanUrl) .then(cleanUrl)
.then(fetch) .then(fetch)
.then(st('loading', 'Processing file')) .then(st('loading', 'Processing file'))
.then(response => response.arrayBuffer()) .then((response) => response.arrayBuffer())
.then(arrayBuffer => new Uint8Array(arrayBuffer)) .then((arrayBuffer) => new Uint8Array(arrayBuffer))
.then(st('loading', 'Downloading engine')) .then(st('loading', 'Downloading engine'))
.then(file => setParts({ .then((file) => setParts({
file, file,
engine: engineByFilename(url) engine: engineByFilename(url),
})) }))
.then(st('loading', 'Running')) .then(st('loading', 'Running'))
.catch(e => { .catch((e) => {
console.error(e) console.error(e)
setStatus({ stage: 'fail', details: [e.message, url] }) setStatus({ stage: 'fail', details: [e.message, url] })
}) })
} }
export default function ({ url, singleWindow }) { export default function UrlPlayer ({ url, singleWindow }) {
const [status, setStatus] = useState(INITIAL_STATUS) const [status, setStatus] = useState(INITIAL_STATUS)
const [vmParts, setParts] = useState(null) const [vmParts, setParts] = useState(null)
@ -55,7 +55,7 @@ export default function ({ url, singleWindow }) {
return vmParts return vmParts
? (<Player {...{ ? (<Player {...{
vmParts, vmParts,
singleWindow singleWindow,
}} />) }} />)
: (<Status {...status} />) : (<Status {...status} />)
} }

View file

@ -7,27 +7,27 @@ const formats = [
{ {
id: 'bocfel', id: 'bocfel',
extensions: /z([3458]|blorb)$/, extensions: /z([3458]|blorb)$/,
engine: bocfel engine: bocfel,
}, },
{ {
id: 'git', id: 'git',
extensions: /(gblorb|ulx)$/, extensions: /(gblorb|ulx)$/,
engine: git engine: git,
}, },
{ {
id: 'hugo', id: 'hugo',
extensions: /hex$/, extensions: /hex$/,
engine: hugo engine: hugo,
}, },
{ {
id: 'tads', id: 'tads',
extensions: /(gam|t3)$/, extensions: /(gam|t3)$/,
engine: tads engine: tads,
} },
] ]
export const engineByFilename = filename => { export const engineByFilename = (filename) => {
const format = formats.find(x => const format = formats.find((x) =>
x.extensions.test(filename)) x.extensions.test(filename))
if (format) { if (format) {

View file

@ -1,6 +1,6 @@
import { import {
compressToUTF16 as encode, compressToUTF16 as encode,
decompressFromUTF16 as decode decompressFromUTF16 as decode,
} from 'lz-string' } from 'lz-string'
export const Handlers = ({ export const Handlers = ({
@ -8,33 +8,33 @@ export const Handlers = ({
setWindows, setWindows,
setCurrentWindowId, setCurrentWindowId,
setInputType, setInputType,
setInbox setInbox,
}) => ({ }) => ({
onInit: _ => { onInit: () => {
setStatus({ stage: 'ready' }) setStatus({ stage: 'ready' })
}, },
/* */ /* */
onUpdateWindows: windows => { onUpdateWindows: (windows) => {
setWindows(windows) setWindows(windows)
}, },
onUpdateInputs: data => { onUpdateInputs: (data) => {
if (data.length === 0) return null if (data.length === 0) return null
const { type, id } = data[0] const { type, id } = data[0]
setCurrentWindowId(id) setCurrentWindowId(id)
setInputType(type) setInputType(type)
}, },
onUpdateContent: inbox => { onUpdateContent: (inbox) => {
setInbox(inbox) setInbox(inbox)
}, },
onDisable: _ => { onDisable: () => {
setInputType(null) setInputType(null)
}, },
/* */ /* */
onFileNameRequest: (tosave, usage, _, setFileName) => { onFileNameRequest: (tosave, usage, _, setFileName) => {
setFileName({ setFileName({
usage, usage,
filename: prompt('Enter the filename') filename: prompt('Enter the filename'),
}) })
}, },
onFileRead: ({ filename }) => { onFileRead: ({ filename }) => {
@ -45,7 +45,7 @@ export const Handlers = ({
localStorage.setItem(`fake-fs/${filename}`, encode(content)) localStorage.setItem(`fake-fs/${filename}`, encode(content))
}, },
/* */ /* */
onExit: _ => { onExit: () => {
setInputType(null) setInputType(null)
} },
}) })

View file

@ -1,7 +1,7 @@
import { h } from 'preact' import { h } from 'preact'
export default function ({ themeEngine }) { export default function ThemeSelector ({ themeEngine }) {
const options = themeEngine.themes.map(theme => ( const options = themeEngine.themes.map((theme) => (
<option <option
key={theme} key={theme}
value={theme}> value={theme}>

View file

@ -3,10 +3,10 @@ import { Route, Router, Switch } from 'wouter-preact'
import { import {
useHashLocation, useHashLocation,
extractView extractView,
} from '~/src/utils/utils.routing' } from '~/src/utils/utils.routing'
import { import {
useThemeEngine useThemeEngine,
} from '~/src/themes/themes' } from '~/src/themes/themes'
import HomeView from '~/src/views/HomeView/HomeView' import HomeView from '~/src/views/HomeView/HomeView'
@ -21,12 +21,14 @@ function App () {
const themeEngine = useThemeEngine() const themeEngine = useThemeEngine()
const [location] = useHashLocation() const [location] = useHashLocation()
const playerView = (themeEngine, singleWindow) => params => const playerView = (themeEngine, singleWindow) =>
(<PlayerView {...{ function view (params) {
return (<PlayerView {...{
...themeEngine, ...themeEngine,
...params, ...params,
singleWindow singleWindow,
}} />) }} />)
}
return ( return (
<Router hook={useHashLocation}> <Router hook={useHashLocation}>
@ -38,7 +40,7 @@ function App () {
<Switch> <Switch>
<Route path='/'> <Route path='/'>
<HomeView {...{ <HomeView {...{
themeEngine themeEngine,
}} /> }} />
</Route> </Route>
<Route path='/games/'> <Route path='/games/'>

View file

@ -13,7 +13,7 @@ const themes = [
'redrum', 'redrum',
'toxin', 'toxin',
'valve', 'valve',
'wasp' 'wasp',
] ]
const LS_THEME_KEY = 'elseifplayer/theme' const LS_THEME_KEY = 'elseifplayer/theme'
@ -24,7 +24,7 @@ const getSavedTheme = () => {
return savedTheme || DEFAULT_THEME return savedTheme || DEFAULT_THEME
} }
const assertTheme = theme => const assertTheme = (theme) =>
themes.includes(theme) themes.includes(theme)
? theme ? theme
: getSavedTheme() : getSavedTheme()
@ -33,7 +33,7 @@ export const useThemeEngine = (initialTheme = getSavedTheme()) => {
const [currentTheme, setCurrentTheme] = const [currentTheme, setCurrentTheme] =
useState(initialTheme) useState(initialTheme)
const setTheme = theme => { const setTheme = (theme) => {
const newTheme = assertTheme(theme) const newTheme = assertTheme(theme)
setCurrentTheme(newTheme) setCurrentTheme(newTheme)

View file

@ -1,5 +1,5 @@
import { import {
useState, useEffect, useCallback useState, useEffect, useCallback,
} from 'preact/hooks' } from 'preact/hooks'
export const useHashLocation = () => { export const useHashLocation = () => {
@ -16,7 +16,7 @@ export const useHashLocation = () => {
return () => window.removeEventListener('hashchange', handler) return () => window.removeEventListener('hashchange', handler)
}, []) }, [])
const navigate = useCallback(to => const navigate = useCallback((to) =>
(window.location.hash = to.replace('#/', '')), []) (window.location.hash = to.replace('#/', '')), [])
return [loc, navigate] return [loc, navigate]
} }
@ -24,7 +24,7 @@ export const useHashLocation = () => {
export const buildPlayLinkHref = ({ url }) => export const buildPlayLinkHref = ({ url }) =>
`/#/play/${encodeURIComponent(url)}` `/#/play/${encodeURIComponent(url)}`
export const extractView = location => { export const extractView = (location) => {
const currentView = location.split('/').filter(Boolean)[0] const currentView = location.split('/').filter(Boolean)[0]
return currentView || '' return currentView || ''
} }

View file

@ -11,10 +11,10 @@ import './GamesView.css'
const tutorialGame = { const tutorialGame = {
name: 'The Dreamhold', name: 'The Dreamhold',
ifdb: 'https://ifdb.org/viewgame?id=3myqnrs64nbtwdaz', ifdb: 'https://ifdb.org/viewgame?id=3myqnrs64nbtwdaz',
url: 'https://mirror.ifarchive.org/if-archive/games/zcode/dreamhold.z8' url: 'https://mirror.ifarchive.org/if-archive/games/zcode/dreamhold.z8',
} }
export default function () { export default function GamesView () {
return ( return (
<main className='view games'> <main className='view games'>
@ -46,7 +46,7 @@ export default function () {
<ul> <ul>
<li> <li>
<GameEntry {...{ <GameEntry {...{
...tutorialGame ...tutorialGame,
}} /> }} />
</li> </li>
</ul> </ul>
@ -73,10 +73,10 @@ export default function () {
</p> </p>
<ol> <ol>
{top2019.map(game => ( {top2019.map((game) => (
<li key={game[0]}> <li key={game[0]}>
<GameEntry {...{ <GameEntry {...{
...game ...game,
}} /> }} />
</li> </li>
))} ))}

View file

@ -3,18 +3,18 @@ export default [
/* Check with cheap-glk */ /* Check with cheap-glk */
'Counterfeit Monkey', 'Counterfeit Monkey',
'https://ifdb.org/viewgame?id=aearuuxv83plclpl', 'https://ifdb.org/viewgame?id=aearuuxv83plclpl',
'https://mirror.ifarchive.org/if-archive/games/glulx/CounterfeitMonkey.gblorb' 'https://mirror.ifarchive.org/if-archive/games/glulx/CounterfeitMonkey.gblorb',
], ],
[ [
'Lost Pig', 'Lost Pig',
'https://ifdb.org/viewgame?id=mohwfk47yjzii14w', 'https://ifdb.org/viewgame?id=mohwfk47yjzii14w',
'https://mirror.ifarchive.org/if-archive/games/zcode/LostPig.z8' 'https://mirror.ifarchive.org/if-archive/games/zcode/LostPig.z8',
], ],
[ [
/* Works. Check inputs */ /* Works. Check inputs */
'Anchorhead', 'Anchorhead',
'https://ifdb.org/viewgame?id=op0uw1gn1tjqmjt7', 'https://ifdb.org/viewgame?id=op0uw1gn1tjqmjt7',
'https://ifarchive.org/if-archive/games/zcode/anchor.z8' 'https://ifarchive.org/if-archive/games/zcode/anchor.z8',
], ],
/* [ /* [
'80 DAYS', '80 DAYS',
@ -24,18 +24,18 @@ export default [
[ [
'Galatea', 'Galatea',
'https://ifdb.org/viewgame?id=urxrv27t7qtu52lb', 'https://ifdb.org/viewgame?id=urxrv27t7qtu52lb',
'https://mirror.ifarchive.org/if-archive/games/zcode/Galatea.zblorb' 'https://mirror.ifarchive.org/if-archive/games/zcode/Galatea.zblorb',
], ],
[ [
/* Works. Check inputs */ /* Works. Check inputs */
'Photopia', 'Photopia',
'https://ifdb.org/viewgame?id=ju778uv5xaswnlpl', 'https://ifdb.org/viewgame?id=ju778uv5xaswnlpl',
'https://mirror.ifarchive.org/if-archive/games/zcode/photopia.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/photopia.z5',
], ],
[ [
'Spider and Web', 'Spider and Web',
'https://ifdb.org/viewgame?id=2xyccw3pe0uovfad', 'https://ifdb.org/viewgame?id=2xyccw3pe0uovfad',
'https://mirror.ifarchive.org/if-archive/games/zcode/Tangle.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/Tangle.z5',
], ],
/* [ /* [
'Trinity', 'Trinity',
@ -60,12 +60,12 @@ export default [
[ [
'Slouching Towards Bedlam', 'Slouching Towards Bedlam',
'https://ifdb.org/viewgame?id=032krqe6bjn5au78', 'https://ifdb.org/viewgame?id=032krqe6bjn5au78',
'https://mirror.ifarchive.org/if-archive/games/competition2003/zcode/slouch/slouch.z5' 'https://mirror.ifarchive.org/if-archive/games/competition2003/zcode/slouch/slouch.z5',
], ],
[ [
'Curses!', 'Curses!',
'https://ifdb.org/viewgame?id=plvzam05bmz3enh8', 'https://ifdb.org/viewgame?id=plvzam05bmz3enh8',
'https://mirror.ifarchive.org/if-archive/games/zcode/curses.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/curses.z5',
], ],
/* [ /* [
'howling dogs', 'howling dogs',
@ -75,12 +75,12 @@ export default [
[ [
'Violet', 'Violet',
'https://ifdb.org/viewgame?id=4glrrfh7wrp9zz7b', 'https://ifdb.org/viewgame?id=4glrrfh7wrp9zz7b',
'https://mirror.ifarchive.org/if-archive/games/zcode/Violet.zblorb' 'https://mirror.ifarchive.org/if-archive/games/zcode/Violet.zblorb',
], ],
[ [
'The Wizard Sniffer', 'The Wizard Sniffer',
'https://ifdb.org/viewgame?id=uq18rw9gt8j58da', 'https://ifdb.org/viewgame?id=uq18rw9gt8j58da',
'https://ifarchive.org/if-archive/games/competition2017/The%20Wizard%20Sniffer/The_Wizard_Sniffer.gblorb' 'https://ifarchive.org/if-archive/games/competition2017/The%20Wizard%20Sniffer/The_Wizard_Sniffer.gblorb',
], ],
/* [ /* [
'Eat Me', 'Eat Me',
@ -100,12 +100,12 @@ export default [
[ [
'Shade', 'Shade',
'https://ifdb.org/viewgame?id=hsfc7fnl40k4a30q', 'https://ifdb.org/viewgame?id=hsfc7fnl40k4a30q',
'https://mirror.ifarchive.org/if-archive/games/zcode/shade.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/shade.z5',
], ],
[ [
'Vespers', 'Vespers',
'https://ifdb.org/viewgame?id=6dj2vguyiagrhvc2', 'https://ifdb.org/viewgame?id=6dj2vguyiagrhvc2',
'https://mirror.ifarchive.org/if-archive/games/zcode/vespers.z8' 'https://mirror.ifarchive.org/if-archive/games/zcode/vespers.z8',
], ],
/* [ /* [
'Will Not Let Me Go', 'Will Not Let Me Go',
@ -135,7 +135,7 @@ export default [
[ [
'Savoir-Faire', 'Savoir-Faire',
'https://ifdb.org/viewgame?id=p0cizeb3kiwzlm2p', 'https://ifdb.org/viewgame?id=p0cizeb3kiwzlm2p',
'https://mirror.ifarchive.org/if-archive/games/zcode/Savoir-Faire.zblorb' 'https://mirror.ifarchive.org/if-archive/games/zcode/Savoir-Faire.zblorb',
], ],
/* [ /* [
'With Those We Love Alive', 'With Those We Love Alive',
@ -145,7 +145,7 @@ export default [
[ [
'Aisle', 'Aisle',
'https://ifdb.org/viewgame?id=j49crlvd62mhwuzu', 'https://ifdb.org/viewgame?id=j49crlvd62mhwuzu',
'https://mirror.ifarchive.org/if-archive/games/zcode/Aisle.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/Aisle.z5',
], ],
/* [ /* [
'Blue Lacuna', 'Blue Lacuna',
@ -155,7 +155,7 @@ export default [
[ [
'Gun Mute', 'Gun Mute',
'https://ifdb.org/viewgame?id=xwedbibfksczn7eq', 'https://ifdb.org/viewgame?id=xwedbibfksczn7eq',
'https://mirror.ifarchive.org/if-archive/games/tads/GunMute.t3' 'https://mirror.ifarchive.org/if-archive/games/tads/GunMute.t3',
], ],
/* [ /* [
'The King of Shreds and Patches', 'The King of Shreds and Patches',
@ -180,7 +180,7 @@ export default [
[ [
'A Beauty Cold and Austere', 'A Beauty Cold and Austere',
'https://ifdb.org/viewgame?id=y9y7jozi0l76bb82', 'https://ifdb.org/viewgame?id=y9y7jozi0l76bb82',
'https://ifarchive.org/if-archive/games/competition2017/A%20Beauty%20Cold%20and%20Austere/A_Beauty_Cold_and_Austere.gblorb' 'https://ifarchive.org/if-archive/games/competition2017/A%20Beauty%20Cold%20and%20Austere/A_Beauty_Cold_and_Austere.gblorb',
], ],
/* [ /* [
'Cactus Blue Motel', 'Cactus Blue Motel',
@ -190,7 +190,7 @@ export default [
[ [
'Coloratura', 'Coloratura',
'https://ifdb.org/viewgame?id=g0fl99ovcrq2sqzk', 'https://ifdb.org/viewgame?id=g0fl99ovcrq2sqzk',
'https://mirror.ifarchive.org/if-archive/games/competition2013/glulx/coloratura/Coloratura.gblorb' 'https://mirror.ifarchive.org/if-archive/games/competition2013/glulx/coloratura/Coloratura.gblorb',
], ],
/* [ /* [
'Harmonia', 'Harmonia',
@ -200,12 +200,12 @@ export default [
[ [
'Lime Ergot', 'Lime Ergot',
'https://ifdb.org/viewgame?id=b8mb4fcwmf1hrxl', 'https://ifdb.org/viewgame?id=b8mb4fcwmf1hrxl',
'https://mirror.ifarchive.org/if-archive/games/glulx/Lime_Ergot.gblorb' 'https://mirror.ifarchive.org/if-archive/games/glulx/Lime_Ergot.gblorb',
], ],
[ [
'Rameses', 'Rameses',
'https://ifdb.org/viewgame?id=0stz0hr7a98bp9mp', 'https://ifdb.org/viewgame?id=0stz0hr7a98bp9mp',
'https://mirror.ifarchive.org/if-archive/games/zcode/rameses.zblorb' 'https://mirror.ifarchive.org/if-archive/games/zcode/rameses.zblorb',
], ],
/* [ /* [
'Spellbreaker', 'Spellbreaker',
@ -220,7 +220,7 @@ export default [
[ [
'The Wand', 'The Wand',
'https://ifdb.org/viewgame?id=2jil5vbxmbv8riv1', 'https://ifdb.org/viewgame?id=2jil5vbxmbv8riv1',
'https://ifarchive.org/if-archive/games/glulx/Wand.ulx' 'https://ifarchive.org/if-archive/games/glulx/Wand.ulx',
], ],
/* [ /* [
'Zork I', 'Zork I',
@ -230,17 +230,17 @@ export default [
[ [
'1893: A World\'s Fair Mystery', '1893: A World\'s Fair Mystery',
'https://ifdb.org/viewgame?id=00e0t7swrris5pg6', 'https://ifdb.org/viewgame?id=00e0t7swrris5pg6',
'https://mirror.ifarchive.org/if-archive/games/tads/1893.gam' 'https://mirror.ifarchive.org/if-archive/games/tads/1893.gam',
], ],
[ [
'Adventure', 'Adventure',
'https://ifdb.org/viewgame?id=fft6pu91j85y4acv', 'https://ifdb.org/viewgame?id=fft6pu91j85y4acv',
'https://mirror.ifarchive.org/if-archive/games/zcode/Advent.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/Advent.z5',
], ],
[ [
'Alias \'The Magpie\'', 'Alias \'The Magpie\'',
'https://ifdb.org/viewgame?id=yspn49v69hzc8rtb', 'https://ifdb.org/viewgame?id=yspn49v69hzc8rtb',
'https://ifarchive.org/if-archive/games/competition2018/Alias%20The%20Magpie/Alias%20%27The%20Magpie%27.gblorb' 'https://ifarchive.org/if-archive/games/competition2018/Alias%20The%20Magpie/Alias%20%27The%20Magpie%27.gblorb',
], ],
/* [ /* [
'De Baron', 'De Baron',
@ -255,22 +255,22 @@ export default [
[ [
'Cragne Manor', 'Cragne Manor',
'https://ifdb.org/viewgame?id=4x7nltu8p851tn4x', 'https://ifdb.org/viewgame?id=4x7nltu8p851tn4x',
'https://mirror.ifarchive.org/if-archive/games/glulx/cragne.gblorb' 'https://mirror.ifarchive.org/if-archive/games/glulx/cragne.gblorb',
], ],
[ [
'The Edifice', 'The Edifice',
'https://ifdb.org/viewgame?id=4tb9soabrb4apqzd', 'https://ifdb.org/viewgame?id=4tb9soabrb4apqzd',
'https://mirror.ifarchive.org/if-archive/games/zcode/edifice.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/edifice.z5',
], ],
[ [
'Endless, Nameless', 'Endless, Nameless',
'https://ifdb.org/viewgame?id=7vtm1rq16hh3xch', 'https://ifdb.org/viewgame?id=7vtm1rq16hh3xch',
'https://ifarchive.org/if-archive/games/zcode/nameless.z8' 'https://ifarchive.org/if-archive/games/zcode/nameless.z8',
], ],
[ [
'Everybody Dies', 'Everybody Dies',
'https://ifdb.org/viewgame?id=lyblvftb8xtlo0a1', 'https://ifdb.org/viewgame?id=lyblvftb8xtlo0a1',
'https://mirror.ifarchive.org/if-archive/games/competition2008/glulx/everybodydies/EverybodyDies.gblorb' 'https://mirror.ifarchive.org/if-archive/games/competition2008/glulx/everybodydies/EverybodyDies.gblorb',
], ],
/* [ /* [
'Fallen London', 'Fallen London',
@ -280,12 +280,12 @@ export default [
[ [
'Foo Foo', 'Foo Foo',
'https://ifdb.org/viewgame?id=ec6x9y8qcmsrxob9', 'https://ifdb.org/viewgame?id=ec6x9y8qcmsrxob9',
'https://ifarchive.org/if-archive/games/springthing/2016/FooFoo.gblorb' 'https://ifarchive.org/if-archive/games/springthing/2016/FooFoo.gblorb',
], ],
[ [
'The Gostak', 'The Gostak',
'https://ifdb.org/viewgame?id=w5s3sv43s3p98v45', 'https://ifdb.org/viewgame?id=w5s3sv43s3p98v45',
'https://mirror.ifarchive.org/if-archive/games/zcode/gostak.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/gostak.z5',
], ],
/* [ /* [
'The Hitchhiker\'s Guide to the Galaxy', 'The Hitchhiker\'s Guide to the Galaxy',
@ -305,27 +305,27 @@ export default [
[ [
'Inside the Facility', 'Inside the Facility',
'https://ifdb.org/viewgame?id=stsdri5zh7a4i5my', 'https://ifdb.org/viewgame?id=stsdri5zh7a4i5my',
'https://ifarchive.org/if-archive/games/competition2016/Inside%20the%20Facility/Facility.z8' 'https://ifarchive.org/if-archive/games/competition2016/Inside%20the%20Facility/Facility.z8',
], ],
[ [
'Junior Arithmancer', 'Junior Arithmancer',
'https://ifdb.org/viewgame?id=pw1rbjt1t4n4n87s', 'https://ifdb.org/viewgame?id=pw1rbjt1t4n4n87s',
'https://ifarchive.org/if-archive/games/competition2018/Junior%20Arithmancer/Junior_Arithmancer.gblorb' 'https://ifarchive.org/if-archive/games/competition2018/Junior%20Arithmancer/Junior_Arithmancer.gblorb',
], ],
[ [
'Make It Good', 'Make It Good',
'https://ifdb.org/viewgame?id=jdrbw1htq4ah8q57', 'https://ifdb.org/viewgame?id=jdrbw1htq4ah8q57',
'https://mirror.ifarchive.org/if-archive/games/zcode/MakeItGood.zblorb' 'https://mirror.ifarchive.org/if-archive/games/zcode/MakeItGood.zblorb',
], ],
[ [
'Sub Rosa', 'Sub Rosa',
'https://ifdb.org/viewgame?id=73nvz9yui87ub3sd', 'https://ifdb.org/viewgame?id=73nvz9yui87ub3sd',
'https://mirror.ifarchive.org/if-archive/games/glulx/Sub_Rosa.gblorb' 'https://mirror.ifarchive.org/if-archive/games/glulx/Sub_Rosa.gblorb',
], ],
[ [
'Suveh Nux', 'Suveh Nux',
'https://ifdb.org/viewgame?id=xkai23ry99qdxce3', 'https://ifdb.org/viewgame?id=xkai23ry99qdxce3',
'https://mirror.ifarchive.org/if-archive/games/zcode/suvehnux.z5' 'https://mirror.ifarchive.org/if-archive/games/zcode/suvehnux.z5',
], ],
/* [ /* [
'their angelical understanding', 'their angelical understanding',
@ -340,6 +340,6 @@ export default [
[ [
'Varicella', 'Varicella',
'https://ifdb.org/viewgame?id=ywwlr3tpxnktjasd', 'https://ifdb.org/viewgame?id=ywwlr3tpxnktjasd',
'https://mirror.ifarchive.org/if-archive/games/zcode/vgame.z8' 'https://mirror.ifarchive.org/if-archive/games/zcode/vgame.z8',
] ],
].map(([name, ifdb, url]) => ({ name, ifdb, url })) ].map(([name, ifdb, url]) => ({ name, ifdb, url }))

View file

@ -3,7 +3,7 @@ import { Link } from 'wouter-preact'
import { import {
useHashLocation, useHashLocation,
buildPlayLinkHref buildPlayLinkHref,
} from '~/src/utils/utils.routing' } from '~/src/utils/utils.routing'
import LocalFileSelector from import LocalFileSelector from
@ -15,7 +15,7 @@ import ThemeSelector from
import './HomeView.css' import './HomeView.css'
export default function ({ themeEngine }) { export default function HomeView ({ themeEngine }) {
const setLocation = useHashLocation()[1] const setLocation = useHashLocation()[1]
return ( return (
@ -46,7 +46,7 @@ export default function ({ themeEngine }) {
</h2> </h2>
<ThemeSelector {...{ <ThemeSelector {...{
themeEngine themeEngine,
}} /> }} />
<p> <p>
@ -97,7 +97,7 @@ export default function ({ themeEngine }) {
<LocalFileSelector {...{ <LocalFileSelector {...{
setLocation, setLocation,
buildLink: buildPlayLinkHref, buildLink: buildPlayLinkHref,
theme: themeEngine.currentTheme theme: themeEngine.currentTheme,
}} /> }} />
</label> </label>
</p> </p>
@ -108,7 +108,7 @@ export default function ({ themeEngine }) {
<TargetURLSelector {...{ <TargetURLSelector {...{
setLocation, setLocation,
buildLink: buildPlayLinkHref, buildLink: buildPlayLinkHref,
theme: themeEngine.currentTheme theme: themeEngine.currentTheme,
}} /> }} />
</label> </label>
</p> </p>

View file

@ -1,8 +1,8 @@
import { h } from 'preact' import { h } from 'preact'
import { Link } from 'wouter-preact' import { Link } from 'wouter-preact'
export default () => ( export default function NotFoundView () {
<main> return <main>
<div className='status'> <div className='status'>
<h1> <h1>
404 404
@ -23,4 +23,4 @@ export default () => (
</a> </a>
</div> </div>
</main> </main>
) }

View file

@ -5,10 +5,10 @@ import UrlPlayer from '~/src/components/Player/UrlPlayer'
import './PlayerView.css' import './PlayerView.css'
const decode = encodedUrl => decodeURIComponent(encodedUrl) const decode = (encodedUrl) => decodeURIComponent(encodedUrl)
export default function ({ export default function PlayerView ({
setTheme, theme, encodedUrl, singleWindow setTheme, theme, encodedUrl, singleWindow,
}) { }) {
useEffect(() => setTheme(theme), [setTheme, theme]) useEffect(() => setTheme(theme), [setTheme, theme])
@ -22,7 +22,7 @@ export default function ({
<main> <main>
<UrlPlayer {...{ <UrlPlayer {...{
url: targetUrl, url: targetUrl,
singleWindow singleWindow,
}} /> }} />
</main> </main>
) )