mirror of
https://github.com/pmret/website.git
synced 2024-11-09 20:43:07 +01:00
Merge branch 'main' of github.com:pmret/website into main
This commit is contained in:
commit
ca87ffe3f8
75
src/Contributors.jsx
Normal file
75
src/Contributors.jsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import React, { useState } from "react"
|
||||||
|
import { createPortal } from "react-dom"
|
||||||
|
import clsx from "clsx"
|
||||||
|
|
||||||
|
import cdownURL from "./cdown.svg"
|
||||||
|
|
||||||
|
function CDown() {
|
||||||
|
return <img src={cdownURL} style={{ width: "1em", height: "1em", verticalAlign: "-5px" }}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
const contributors = [
|
||||||
|
{
|
||||||
|
name: "Ethan",
|
||||||
|
avatar: "https://avatars2.githubusercontent.com/u/2985314?s=400",
|
||||||
|
url: "https://github.com/ethteck",
|
||||||
|
description: <div>
|
||||||
|
</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stuckpixel",
|
||||||
|
avatar: "https://avatars3.githubusercontent.com/u/3634616?s=400",
|
||||||
|
url: "https://github.com/pixel-stuck",
|
||||||
|
description: <div>
|
||||||
|
</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alex",
|
||||||
|
avatar: "https://avatars3.githubusercontent.com/u/9429556?s=400",
|
||||||
|
url: "https://github.com/nanaian",
|
||||||
|
description: <div>
|
||||||
|
</div>,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Contributors({ captionPortal }) {
|
||||||
|
const [selected, setSelected] = useState(0)
|
||||||
|
const { name, description, url } = contributors[selected]
|
||||||
|
|
||||||
|
return <div style={{ display: "flex", flexDirection: "row", width: "100%", alignItems: "center", justifyContent: "center" }}>
|
||||||
|
<div style={{ padding: ".5em", display: "flex", flexDirection: "column", alignItems: "center" }}>
|
||||||
|
<div className="avatars">
|
||||||
|
{contributors.map((contributor, i) => {
|
||||||
|
let t = (Math.PI * 2) * ((i - selected) / contributors.length) + Math.PI/2
|
||||||
|
|
||||||
|
let x = 150 * Math.cos(t) + 200
|
||||||
|
let y = 140 * Math.sin(t) + 200
|
||||||
|
|
||||||
|
return <img
|
||||||
|
key={contributor.name}
|
||||||
|
className={clsx("avatar", { inactive: i !== selected })}
|
||||||
|
onClick={() => setSelected(i)}
|
||||||
|
src={contributor.avatar}
|
||||||
|
alt={contributor.name}
|
||||||
|
style={{ top: y + "px", left: x + "px", zIndex: y }}
|
||||||
|
/>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="teal" style={{ width: "80%", cursor: "pointer" }} onClick={() => window.open(url)}>
|
||||||
|
{name}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="shadow-box" style={{ width: "27em", height: "20em" }}>
|
||||||
|
<div className="shadow-box-inner" style={{ backgroundImage: `url(http://placekitten.com/800/${600 + Math.floor(Math.random() * 100)})`, backgroundSize: "cover" }}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{captionPortal.current && createPortal(<div>
|
||||||
|
{description}
|
||||||
|
</div>, captionPortal.current)}
|
||||||
|
</div>
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react"
|
import React, { useState, useEffect } from "react"
|
||||||
import { createPortal } from "react-dom"
|
import { createPortal } from "react-dom"
|
||||||
import { Area, XAxis, YAxis, AreaChart, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
|
import { Area, XAxis, YAxis, AreaChart, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
|
||||||
import { scalePow } from "d3-scale"
|
import { scalePow } from "d3-scale"
|
||||||
@ -19,11 +19,16 @@ const csvVersions = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
const colors = {
|
||||||
const csv = await fetch("https://papermar.io/reports/progress.csv")
|
yellow: { stroke: "#e3ac34", fill: "#edc97e" },
|
||||||
|
green: { stroke: "#40e334", fill: "#91eb7f" },
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchData(version) {
|
||||||
|
const csv = await fetch(`https://papermar.io/reports/progress_${version}.csv`)
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
|
|
||||||
return csv
|
const rows = csv
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter(row => row.length)
|
.filter(row => row.length)
|
||||||
.map(row => {
|
.map(row => {
|
||||||
@ -35,29 +40,29 @@ async function fetchData() {
|
|||||||
obj[key] = transform(data.shift())
|
obj[key] = transform(data.shift())
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.percentBytes = Math.round((obj.matchingBytes / obj.totalBytes) * 100)
|
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const latest = rows[rows.length - 1]
|
||||||
|
for (const row of rows) {
|
||||||
|
row.percentBytes = (row.matchingBytes / latest.totalBytes) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
let cachedData = null
|
export default function ProgressPane({ captionPortal, nonce, color, version }) {
|
||||||
|
const [data, setData] = useState([])
|
||||||
export default function ProgressPane({ captionPortal, nonce }) {
|
|
||||||
const [data, setData] = useState(cachedData)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData()
|
fetchData(version)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
cachedData = data
|
setData(data)
|
||||||
setData(cachedData)
|
|
||||||
})
|
})
|
||||||
}, [])
|
}, [version])
|
||||||
|
|
||||||
// TODO: cute spin animation when data loads
|
|
||||||
|
|
||||||
return <div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
return <div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
{data && <DataView data={data} nonce={nonce} captionPortal={captionPortal}/>}
|
{data && <DataView data={data} nonce={nonce} captionPortal={captionPortal} color={color}/>}
|
||||||
{!data && "Loading..."}
|
{!data && "Loading..."}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -73,9 +78,10 @@ const monthDates = []
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function DataView({ data, captionPortal, nonce }) {
|
function DataView({ data, captionPortal, nonce, color }) {
|
||||||
const latest = data[data.length - 1]
|
const latest = data[data.length - 1]
|
||||||
const oldest = data[0]
|
const oldest = data[0]
|
||||||
|
const { stroke, fill } = colors[color]
|
||||||
|
|
||||||
const [selectedEntry, setSelectedEntry] = useState(latest)
|
const [selectedEntry, setSelectedEntry] = useState(latest)
|
||||||
|
|
||||||
@ -89,18 +95,9 @@ function DataView({ data, captionPortal, nonce }) {
|
|||||||
return <span/>
|
return <span/>
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxPercent = Math.ceil(latest.percentBytes / 25) * 25
|
const maxPercent = latest ? Math.ceil(latest.percentBytes / 25) * 25 : 25
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{/*<table width="250" className="outline-invert">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Matched</td>
|
|
||||||
<td className="thin align-right">{Math.round((latest.matchingBytes / latest.totalBytes) * 10000) / 100}%</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>*/}
|
|
||||||
|
|
||||||
<div className="shadow-box flex-grow">
|
<div className="shadow-box flex-grow">
|
||||||
<div className="shadow-box-inner" style={{ paddingRight: ".7em", paddingTop: ".7em", "--text-outline": "transparent", background: "#e2e1d8" }}>
|
<div className="shadow-box-inner" style={{ paddingRight: ".7em", paddingTop: ".7em", "--text-outline": "transparent", background: "#e2e1d8" }}>
|
||||||
<div className="progress-chart">
|
<div className="progress-chart">
|
||||||
@ -115,8 +112,8 @@ function DataView({ data, captionPortal, nonce }) {
|
|||||||
type="linear"
|
type="linear"
|
||||||
dataKey="percentBytes"
|
dataKey="percentBytes"
|
||||||
unit="%"
|
unit="%"
|
||||||
stroke="#e3ac34" strokeWidth={2}
|
stroke={stroke} strokeWidth={2}
|
||||||
fill="#edc97e"
|
fill={fill}
|
||||||
dot={true}
|
dot={true}
|
||||||
isAnimationActive={false}
|
isAnimationActive={false}
|
||||||
/>
|
/>
|
||||||
@ -127,7 +124,7 @@ function DataView({ data, captionPortal, nonce }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="shadow-box-title yellow">
|
<button className={"shadow-box-title " + color}>
|
||||||
{selectedEntry ? formatTimestamp(selectedEntry.timestamp, {
|
{selectedEntry ? formatTimestamp(selectedEntry.timestamp, {
|
||||||
dateStyle: "long",
|
dateStyle: "long",
|
||||||
timeStyle: "short",
|
timeStyle: "short",
|
||||||
@ -162,7 +159,7 @@ function EntryInfo({ entry, isLatest }) {
|
|||||||
/*const [commitMessage, setCommitMessage] = useState(null)
|
/*const [commitMessage, setCommitMessage] = useState(null)
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
fetch(`https://api.github.com/repos/ethteck/papermario/commits/${entry.commit}`)
|
fetch(`https://api.github.com/repos/pmret/papermario/commits/${entry.commit}`)
|
||||||
.then(resp => resp.json())
|
.then(resp => resp.json())
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
setCommitMessage(resp.commit.message.split("\n")[0])
|
setCommitMessage(resp.commit.message.split("\n")[0])
|
||||||
@ -170,7 +167,7 @@ function EntryInfo({ entry, isLatest }) {
|
|||||||
}, [entry.commit])*/
|
}, [entry.commit])*/
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<a href={`https://github.com/ethteck/papermario/commit/${entry.commit}`}>
|
<a href={`https://github.com/pmret/papermario/commit/${entry.commit}`}>
|
||||||
{entry.commit.substr(0, 8)}
|
{entry.commit.substr(0, 8)}
|
||||||
</a>
|
</a>
|
||||||
{isLatest && " (latest)"}
|
{isLatest && " (latest)"}
|
||||||
@ -179,8 +176,8 @@ function EntryInfo({ entry, isLatest }) {
|
|||||||
<tr>
|
<tr>
|
||||||
<td width="200">Matched</td>
|
<td width="200">Matched</td>
|
||||||
<td className="thin align-right">
|
<td className="thin align-right">
|
||||||
{Math.round((entry.matchingBytes / entry.totalBytes) * 10000) / 100}% bytes
|
{Math.round(entry.percentBytes * 100) / 100}% bytes
|
||||||
({entry.matchingFuncs}/{entry.totalFuncs} split functions)
|
({entry.matchingFuncs}/{entry.totalFuncs} functions)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
BIN
src/bg/green-checker.png
Normal file
BIN
src/bg/green-checker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 B |
BIN
src/bg/mrn-clouds.png
Normal file
BIN
src/bg/mrn-clouds.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
BIN
src/bg/mrn-toad-town.png
Normal file
BIN
src/bg/mrn-toad-town.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
BIN
src/bg/teal-checker.png
Normal file
BIN
src/bg/teal-checker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 B |
19
src/cdown.svg
Normal file
19
src/cdown.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="62.695" height="62.695" version="1.1" viewBox="0 0 16.588 16.588" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="a" x1="72.973" x2="60.528" y1="266.26" y2="253.34" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#a98723" offset="0"/>
|
||||||
|
<stop stop-color="#e7ce31" offset=".2617"/>
|
||||||
|
<stop stop-color="#e7ce31" offset=".66415"/>
|
||||||
|
<stop stop-color="#f8efd5" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(-58.657 -251.24)">
|
||||||
|
<g>
|
||||||
|
<circle cx="66.951" cy="259.53" r="7.5003" fill="url(#a)" stroke="#623826" stroke-linecap="square" stroke-width="1.5875" style="paint-order:normal"/>
|
||||||
|
<path d="m63.138 257.4h7.7581l0.02305 2.018-3.9191 4.0028-3.862-3.959z" fill="none" stroke="#986c2b" stroke-width="2.0361"/>
|
||||||
|
<path d="m63.138 257.4h7.7581l0.02305 2.018-3.9191 4.0028-3.862-3.959z" fill="none" stroke="#fff" stroke-width="1.3616"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
120
src/index.css
120
src/index.css
@ -7,7 +7,7 @@ html {
|
|||||||
--dark: #313131;
|
--dark: #313131;
|
||||||
--light: #d6d6ce;
|
--light: #d6d6ce;
|
||||||
|
|
||||||
font-size: 28px;
|
font-size: 32px;
|
||||||
font-family: "Paper Mario Dialog Redesigned", sans-serif;
|
font-family: "Paper Mario Dialog Redesigned", sans-serif;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@ -15,11 +15,35 @@ html {
|
|||||||
color: var(--light);
|
color: var(--light);
|
||||||
--text-outline: var(--dark);
|
--text-outline: var(--dark);
|
||||||
|
|
||||||
background: #090942; /* TODO: dynamic star bg */
|
background-color: #002c02;
|
||||||
|
background-image: url(bg/mrn-toad-town.png);
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
background-image: url(bg/mrn-clouds.png);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-size: contain;
|
||||||
|
animation: clouds linear 30s infinite normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes clouds {
|
||||||
|
from {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
background-position: 100vw 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@ -27,6 +51,10 @@ html {
|
|||||||
text-shadow: var(--text-outline) 3px 0px 0px, var(--text-outline) 2.83487px 0.981584px 0px, var(--text-outline) 2.35766px 1.85511px 0px, var(--text-outline) 1.62091px 2.52441px 0px, var(--text-outline) 0.705713px 2.91581px 0px, var(--text-outline) -0.287171px 2.98622px 0px, var(--text-outline) -1.24844px 2.72789px 0px, var(--text-outline) -2.07227px 2.16926px 0px, var(--text-outline) -2.66798px 1.37182px 0px, var(--text-outline) -2.96998px 0.42336px 0px, var(--text-outline) -2.94502px -0.571704px 0px, var(--text-outline) -2.59586px -1.50383px 0px, var(--text-outline) -1.96093px -2.27041px 0px, var(--text-outline) -1.11013px -2.78704px 0px, var(--text-outline) -0.137119px -2.99686px 0px, var(--text-outline) 0.850987px -2.87677px 0px, var(--text-outline) 1.74541px -2.43999px 0px, var(--text-outline) 2.44769px -1.73459px 0px, var(--text-outline) 2.88051px -0.838247px 0px;
|
text-shadow: var(--text-outline) 3px 0px 0px, var(--text-outline) 2.83487px 0.981584px 0px, var(--text-outline) 2.35766px 1.85511px 0px, var(--text-outline) 1.62091px 2.52441px 0px, var(--text-outline) 0.705713px 2.91581px 0px, var(--text-outline) -0.287171px 2.98622px 0px, var(--text-outline) -1.24844px 2.72789px 0px, var(--text-outline) -2.07227px 2.16926px 0px, var(--text-outline) -2.66798px 1.37182px 0px, var(--text-outline) -2.96998px 0.42336px 0px, var(--text-outline) -2.94502px -0.571704px 0px, var(--text-outline) -2.59586px -1.50383px 0px, var(--text-outline) -1.96093px -2.27041px 0px, var(--text-outline) -1.11013px -2.78704px 0px, var(--text-outline) -0.137119px -2.99686px 0px, var(--text-outline) 0.850987px -2.87677px 0px, var(--text-outline) 1.74541px -2.43999px 0px, var(--text-outline) 2.44769px -1.73459px 0px, var(--text-outline) 2.88051px -0.838247px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
a:any-link {
|
a:any-link {
|
||||||
color: #3796ff;
|
color: #3796ff;
|
||||||
--text-outline: #d3e5f9;
|
--text-outline: #d3e5f9;
|
||||||
@ -38,7 +66,6 @@ button {
|
|||||||
|
|
||||||
border-radius: 100em;
|
border-radius: 100em;
|
||||||
padding: 0 .4em .2em .4em;
|
padding: 0 .4em .2em .4em;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
filter: brightness(1.0);
|
filter: brightness(1.0);
|
||||||
will-change: filter, box-shadow;
|
will-change: filter, box-shadow;
|
||||||
@ -50,6 +77,9 @@ button {
|
|||||||
border-right: 2px solid;
|
border-right: 2px solid;
|
||||||
|
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.red {
|
button.red {
|
||||||
@ -84,6 +114,22 @@ button.github {
|
|||||||
border-right-color: #171515;
|
border-right-color: #171515;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.teal {
|
||||||
|
border-top-color: #22eec9;
|
||||||
|
border-left-color: #22eec9;
|
||||||
|
background: #14a98e;
|
||||||
|
border-bottom-color: #076d5a;
|
||||||
|
border-right-color: #076d5a;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.green {
|
||||||
|
border-top-color: #68ff51;
|
||||||
|
border-left-color: #68ff51;
|
||||||
|
background: #47b836;
|
||||||
|
border-bottom-color: #2a791e;
|
||||||
|
border-right-color: #2a791e;
|
||||||
|
}
|
||||||
|
|
||||||
button.inactive {
|
button.inactive {
|
||||||
filter: brightness(0.6);
|
filter: brightness(0.6);
|
||||||
}
|
}
|
||||||
@ -95,6 +141,7 @@ button:hover {
|
|||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
margin-right: .25em;
|
margin-right: .25em;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
@ -108,11 +155,10 @@ button:hover {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
width: 1400px;
|
width: 80%;
|
||||||
max-width: calc(100vw - 10vh);
|
max-width: 95%;
|
||||||
|
height: 80%;
|
||||||
height: 1200px;
|
max-height: 950%;
|
||||||
max-height: calc(100vh - 10vw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
@ -137,6 +183,7 @@ main {
|
|||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
/*padding-bottom: 2.5em;*/
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
@ -148,6 +195,10 @@ main {
|
|||||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main > :last-child {
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
main.red {
|
main.red {
|
||||||
background-color: #e2b6b3;
|
background-color: #e2b6b3;
|
||||||
background-image: url(bg/red-checker.png);
|
background-image: url(bg/red-checker.png);
|
||||||
@ -158,6 +209,16 @@ main.yellow {
|
|||||||
background-image: url(bg/yellow-checker.png);
|
background-image: url(bg/yellow-checker.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main.teal {
|
||||||
|
background-color: #cad9d6;
|
||||||
|
background-image: url(bg/teal-checker.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
main.green {
|
||||||
|
background-color: #68ff51;
|
||||||
|
background-image: url(bg/green-checker.png);
|
||||||
|
}
|
||||||
|
|
||||||
main > * {
|
main > * {
|
||||||
image-rendering: initial;
|
image-rendering: initial;
|
||||||
}
|
}
|
||||||
@ -231,14 +292,18 @@ button.shadow-box-title {
|
|||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-size: 48px;
|
background-size: 48px;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|
||||||
|
-ms-interpolation-mode: nearest-neighbor;
|
||||||
|
image-rendering: optimizespeed;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
|
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
width: 85%;
|
width: 85%;
|
||||||
height: 3rem;
|
height: 4rem;
|
||||||
|
|
||||||
padding: 8px 16px;
|
padding: .5em 1em;
|
||||||
margin-top: -14px;
|
margin-top: -2em;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
@ -247,7 +312,7 @@ button.shadow-box-title {
|
|||||||
.progress-chart {
|
.progress-chart {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
font-size: 12px;
|
font-size: 18px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -269,3 +334,34 @@ button.shadow-box-title {
|
|||||||
.flex-spacer { width: 1em; height: 1em }
|
.flex-spacer { width: 1em; height: 1em }
|
||||||
|
|
||||||
.align-right { text-align: right }
|
.align-right { text-align: right }
|
||||||
|
|
||||||
|
.avatars {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
border-radius: 100em;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
will-change: filter, top, left;
|
||||||
|
transition: 200ms filter, 200ms top, 200ms left;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.inactive {
|
||||||
|
filter: brightness(0.7) saturate(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar:hover {
|
||||||
|
filter: brightness(1.0);
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<title>Paper Mario Reverse Engineering</title>
|
<title>Paper Mario Decompilation</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="index.css"/>
|
<link rel="stylesheet" href="index.css"/>
|
||||||
<script async defer src="main.jsx"></script>
|
<script async defer src="main.jsx"></script>
|
||||||
|
14
src/index.js
14
src/index.js
@ -1,14 +0,0 @@
|
|||||||
import moment from "moment"
|
|
||||||
|
|
||||||
import("./progress.js").then(async ({ fetchData, functionsChart }) => {
|
|
||||||
const data = await fetchData()
|
|
||||||
|
|
||||||
functionsChart(data, document.getElementById("progress-chart"))
|
|
||||||
|
|
||||||
const first = data[0]
|
|
||||||
const latest = data[data.length - 1]
|
|
||||||
|
|
||||||
document.getElementById("matched-rom-percent").innerText = Math.round((latest.matchingBytes / latest.totalBytes) * 10000) / 100 + "% matched" // TODO: include data
|
|
||||||
document.getElementById("functions-ratio").innerText = latest.matchingFuncs + "/" + latest.totalFuncs
|
|
||||||
document.getElementById("play-time").innerText = moment(latest.timestamp).from(first.timestamp, true)
|
|
||||||
}).catch(console.error)
|
|
28
src/main.jsx
28
src/main.jsx
@ -3,6 +3,7 @@ import ReactDOM from "react-dom"
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
|
||||||
import ProgressPane from "./ProgressPane"
|
import ProgressPane from "./ProgressPane"
|
||||||
|
import Contributors from "./Contributors"
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
@ -11,16 +12,28 @@ const tabs = [
|
|||||||
color: "red",
|
color: "red",
|
||||||
pane: () => <div>
|
pane: () => <div>
|
||||||
<p className="outline-invert">
|
<p className="outline-invert">
|
||||||
Welcome to the Paper Mario Reverse-Engineering website!
|
Welcome to the Paper Mario decompilation site!
|
||||||
</p>
|
</p>
|
||||||
</div>,
|
</div>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
slug: "/progress",
|
slug: "/progress-us",
|
||||||
name: "Progress",
|
name: "Progress (US)",
|
||||||
color: "yellow",
|
color: "yellow",
|
||||||
pane: (props) => <ProgressPane {...props}/>
|
pane: (props) => <ProgressPane version="us" color="yellow" {...props}/>
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: "/progress-jp",
|
||||||
|
name: "Progress (JP)",
|
||||||
|
color: "green",
|
||||||
|
pane: (props) => <ProgressPane version="jp" color="green" {...props}/>
|
||||||
|
},
|
||||||
|
/*{
|
||||||
|
slug: "/contributors",
|
||||||
|
name: "Contributors",
|
||||||
|
color: "teal",
|
||||||
|
pane: (props) => <Contributors {...props}/>
|
||||||
|
},*/
|
||||||
]
|
]
|
||||||
|
|
||||||
let routedTabIndex = tabs.findIndex(tab => tab.slug === document.location.pathname)
|
let routedTabIndex = tabs.findIndex(tab => tab.slug === document.location.pathname)
|
||||||
@ -33,7 +46,7 @@ function App() {
|
|||||||
const [flip, setFlip] = useState(false)
|
const [flip, setFlip] = useState(false)
|
||||||
const pane = useRef()
|
const pane = useRef()
|
||||||
|
|
||||||
function switchToTab(index) {
|
function switchToTab(index, pushState) {
|
||||||
if (index === paneIndex || index === tabIndex) return
|
if (index === paneIndex || index === tabIndex) return
|
||||||
|
|
||||||
console.info("switching to tab", index)
|
console.info("switching to tab", index)
|
||||||
@ -41,6 +54,7 @@ function App() {
|
|||||||
setRotation(rotation - 180)
|
setRotation(rotation - 180)
|
||||||
setTabIndex(index)
|
setTabIndex(index)
|
||||||
|
|
||||||
|
if (pushState)
|
||||||
history.pushState(null, tabs[index].name, tabs[index].slug)
|
history.pushState(null, tabs[index].name, tabs[index].slug)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -51,7 +65,7 @@ function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function listener() {
|
function listener() {
|
||||||
switchToTab(tabs.findIndex(tab => tab.slug === document.location.pathname))
|
switchToTab(tabs.findIndex(tab => tab.slug === window.location.pathname), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("popstate", listener)
|
window.addEventListener("popstate", listener)
|
||||||
@ -67,7 +81,7 @@ function App() {
|
|||||||
key={tab.name}
|
key={tab.name}
|
||||||
className={clsx("tab", tab.color, { "inactive": index !== tabIndex })}
|
className={clsx("tab", tab.color, { "inactive": index !== tabIndex })}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
switchToTab(index)
|
switchToTab(index, true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.name}
|
{tab.name}
|
||||||
|
Loading…
Reference in New Issue
Block a user