mirror of
https://github.com/PCSX2/pcsx2-net-www.git
synced 2026-01-31 01:15:16 +01:00
Updating dependencies, specifically NextUI (#358)
* build(deps): bump the frontend-deps group across 1 directory with 14 updates Bumps the frontend-deps group with 14 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.4.0` | `3.5.2` | | [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) | `3.4.0` | `3.5.2` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.4.0` | `3.5.2` | | [@mdx-js/react](https://github.com/mdx-js/mdx/tree/HEAD/packages/react) | `3.0.1` | `3.1.0` | | [@nextui-org/react](https://github.com/nextui-org/nextui/tree/HEAD/packages/core/react) | `1.0.0-beta.13` | `2.4.8` | | [autoprefixer](https://github.com/postcss/autoprefixer) | `10.4.19` | `10.4.20` | | [luxon](https://github.com/moment/luxon) | `3.4.4` | `3.5.0` | | [postcss](https://github.com/postcss/postcss) | `8.4.39` | `8.4.47` | | [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer) | `2.3.1` | `2.4.0` | | [react-icons](https://github.com/react-icons/react-icons) | `5.2.1` | `5.3.0` | | [recharts](https://github.com/recharts/recharts) | `2.12.7` | `2.13.0` | | [yaml](https://github.com/eemeli/yaml) | `2.4.5` | `2.6.0` | | [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) | `3.4.0` | `3.5.2` | | [glob](https://github.com/isaacs/node-glob) | `10.4.1` | `11.0.0` | Updates `@docusaurus/core` from 3.4.0 to 3.5.2 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.5.2/packages/docusaurus) Updates `@docusaurus/plugin-client-redirects` from 3.4.0 to 3.5.2 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.5.2/packages/docusaurus-plugin-client-redirects) Updates `@docusaurus/preset-classic` from 3.4.0 to 3.5.2 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.5.2/packages/docusaurus-preset-classic) Updates `@mdx-js/react` from 3.0.1 to 3.1.0 - [Release notes](https://github.com/mdx-js/mdx/releases) - [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md) - [Commits](https://github.com/mdx-js/mdx/commits/3.1.0/packages/react) Updates `@nextui-org/react` from 1.0.0-beta.13 to 2.4.8 - [Release notes](https://github.com/nextui-org/nextui/releases) - [Changelog](https://github.com/nextui-org/nextui/blob/canary/packages/core/react/CHANGELOG.md) - [Commits](https://github.com/nextui-org/nextui/commits/@nextui-org/react@2.4.8/packages/core/react) Updates `autoprefixer` from 10.4.19 to 10.4.20 - [Release notes](https://github.com/postcss/autoprefixer/releases) - [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/autoprefixer/compare/10.4.19...10.4.20) Updates `luxon` from 3.4.4 to 3.5.0 - [Changelog](https://github.com/moment/luxon/blob/master/CHANGELOG.md) - [Commits](https://github.com/moment/luxon/compare/3.4.4...3.5.0) Updates `postcss` from 8.4.39 to 8.4.47 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.39...8.4.47) Updates `prism-react-renderer` from 2.3.1 to 2.4.0 - [Release notes](https://github.com/FormidableLabs/prism-react-renderer/releases) - [Commits](https://github.com/FormidableLabs/prism-react-renderer/compare/prism-react-renderer@2.3.1...prism-react-renderer@2.4.0) Updates `react-icons` from 5.2.1 to 5.3.0 - [Release notes](https://github.com/react-icons/react-icons/releases) - [Commits](https://github.com/react-icons/react-icons/compare/v5.2.1...v5.3.0) Updates `recharts` from 2.12.7 to 2.13.0 - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/3.x/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v2.12.7...v2.13.0) Updates `yaml` from 2.4.5 to 2.6.0 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.4.5...v2.6.0) Updates `@docusaurus/module-type-aliases` from 3.4.0 to 3.5.2 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.5.2/packages/docusaurus-module-type-aliases) Updates `glob` from 10.4.1 to 11.0.0 - [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md) - [Commits](https://github.com/isaacs/node-glob/compare/v10.4.1...v11.0.0) --- updated-dependencies: - dependency-name: "@docusaurus/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: "@docusaurus/plugin-client-redirects" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: "@mdx-js/react" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: "@nextui-org/react" dependency-type: direct:production update-type: version-update:semver-major dependency-group: frontend-deps - dependency-name: autoprefixer dependency-type: direct:production update-type: version-update:semver-patch dependency-group: frontend-deps - dependency-name: luxon dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: postcss dependency-type: direct:production update-type: version-update:semver-patch dependency-group: frontend-deps - dependency-name: prism-react-renderer dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: react-icons dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: recharts dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: yaml dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: "@docusaurus/module-type-aliases" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-deps - dependency-name: glob dependency-type: direct:development update-type: version-update:semver-major dependency-group: frontend-deps ... Signed-off-by: dependabot[bot] <support@github.com> * deps: start migrating next-ui to V2 * next-ui2: home page mostly rendering * fix css collision with docusaurus * pretty much done fixing the homepage * most of the compatibility page finished * compatibility page largely completed * download buttons working again, cleaned up navbar * fix blog and documentation components and styling * lint: formatting * fix subheading color on compat page * finish compat page and fix some mobile issues * light theme adjustments * just pagination on download page to go * downloads page finished hopefully * fix build issue * fix footer color and previous versions link --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
@@ -32,8 +32,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
# TODO - switch back to 22 https://github.com/actions/setup-node/issues/1112
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -6,6 +6,20 @@ const lightCodeTheme = themes.github;
|
||||
const darkCodeTheme = themes.dracula;
|
||||
const redirects = require("./redirects");
|
||||
|
||||
function tailwindPlugin(context, options) {
|
||||
return {
|
||||
name: "tailwind-plugin",
|
||||
configurePostCss(postcssOptions) {
|
||||
postcssOptions.plugins = [
|
||||
require("postcss-import"),
|
||||
require("tailwindcss"),
|
||||
require("autoprefixer"),
|
||||
];
|
||||
return postcssOptions;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: "PCSX2",
|
||||
@@ -92,6 +106,7 @@ const config = {
|
||||
editUrl: "https://github.com/PCSX2/pcsx2-net-www/tree/main/",
|
||||
},
|
||||
blog: {
|
||||
onUntruncatedBlogPosts: "ignore",
|
||||
path: "blog",
|
||||
blogSidebarCount: 0,
|
||||
showReadingTime: true,
|
||||
@@ -114,7 +129,7 @@ const config = {
|
||||
({
|
||||
// announcementBar: {
|
||||
// id: "announcementBar-1", // Increment on change (2.0 was 0, next announcement should be 1)
|
||||
// content: `<a href="/blog/2024/pcsx2-2-release/">PCSX2 2.0 is finally here, check out our new blog post!</a>`,
|
||||
// content: `<a class="no-underline font-medium" href="/blog/2024/pcsx2-2-release/">PCSX2 2.0 is finally here, check out our new blog post!</a>`,
|
||||
// backgroundColor: "#4765c8",
|
||||
// textColor: "#fafbfc",
|
||||
// isCloseable: true,
|
||||
@@ -346,6 +361,7 @@ const config = {
|
||||
};
|
||||
},
|
||||
}),
|
||||
tailwindPlugin,
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
31
package.json
31
package.json
@@ -17,28 +17,31 @@
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.3.2",
|
||||
"@docusaurus/plugin-client-redirects": "^3.3.2",
|
||||
"@docusaurus/preset-classic": "^3.3.2",
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
"@nextui-org/react": "1.0.0-beta.13",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"@docusaurus/core": "^3.5.2",
|
||||
"@docusaurus/plugin-client-redirects": "^3.5.2",
|
||||
"@docusaurus/preset-classic": "^3.5.2",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@nextui-org/react": "^2.4.8",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.11.9",
|
||||
"fuse.js": "^7.0.0",
|
||||
"luxon": "^3.4.4",
|
||||
"postcss": "^8.4.31",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
"luxon": "^3.5.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"postcss": "^8.4.47",
|
||||
"prism-react-renderer": "^2.4.0",
|
||||
"react": "^18.3.1",
|
||||
"react-cookie-consent": "^9.0.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.2.1",
|
||||
"react-icons": "^5.3.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"recharts": "2.12.7",
|
||||
"yaml": "^2.4.2"
|
||||
"recharts": "2.13.0",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"yaml": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.3.2",
|
||||
"glob": "10.4.1",
|
||||
"@docusaurus/module-type-aliases": "^3.5.2",
|
||||
"glob": "11.0.0",
|
||||
"prettier": "3.3.3",
|
||||
"prompts": "2.4.2",
|
||||
"webp-converter": "2.3.3"
|
||||
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
51
src/components/CompatibilityButton/index.js
Normal file
51
src/components/CompatibilityButton/index.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from "react";
|
||||
import { Button } from "@nextui-org/react";
|
||||
|
||||
const categoryColorMapping = {
|
||||
perfect: {
|
||||
on: "bg-[#ba68c8] border-none",
|
||||
off: "bg-transparent text-[#ba68c8] !border-[#ba68c8] border-solid",
|
||||
},
|
||||
playable: {
|
||||
on: "bg-[#9CCC65] text-black border-none",
|
||||
off: "bg-transparent text-[#9CCC65] !border-[#9CCC65] border-solid",
|
||||
},
|
||||
ingame: {
|
||||
on: "bg-[#29B6F6] text-black border-none",
|
||||
off: "bg-transparent text-[#29B6F6] !border-[#29B6F6] border-solid",
|
||||
},
|
||||
menus: {
|
||||
on: "bg-[#FBC02D] text-black border-none",
|
||||
off: "bg-transparent text-[#FBC02D] !border-[#FBC02D] border-solid",
|
||||
},
|
||||
intro: {
|
||||
on: "bg-[#F57C00] text-black border-none",
|
||||
off: "bg-transparent text-[#F57C00] !border-[#F57C00] border-solid",
|
||||
},
|
||||
nothing: {
|
||||
on: "bg-[#D32F2F] border-none",
|
||||
off: "bg-transparent text-[#D32F2F] !border-[#D32F2F] border-solid",
|
||||
},
|
||||
};
|
||||
|
||||
export function CompatibilityButton({
|
||||
categoryFiltered,
|
||||
disabledOrLoading,
|
||||
category,
|
||||
onPress,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
variant={"bordered"}
|
||||
disabled={disabledOrLoading}
|
||||
isLoading={disabledOrLoading}
|
||||
onPress={onPress}
|
||||
className={
|
||||
categoryColorMapping[category][categoryFiltered ? "off" : "on"]
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,111 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Table, Card, Text, Grid } from "@nextui-org/react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { ReleaseDownloadButton } from "../ReleaseDownloadButton";
|
||||
import { GoPlus, GoDash } from "react-icons/go";
|
||||
import { IconContext } from "react-icons";
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
export function PullRequestTableCard({ pullRequest }) {
|
||||
const date = DateTime.fromISO(pullRequest.updatedAt);
|
||||
const dateString = date.toLocaleString(DateTime.DATE_FULL);
|
||||
return (
|
||||
<Grid.Container css={{ mt: "0.5em" }}>
|
||||
<Grid xs={12}>
|
||||
<Card css={{ p: "$6", mw: "100%" }}>
|
||||
<Card.Header>
|
||||
<h3>
|
||||
<a href={pullRequest.link}>PR #{pullRequest.number}</a>
|
||||
<span style={{ marginLeft: "0.5em", color: "#3fb950" }}>
|
||||
<IconContext.Provider
|
||||
value={{ style: { verticalAlign: "middle" } }}
|
||||
>
|
||||
<GoPlus size={24}></GoPlus>
|
||||
</IconContext.Provider>
|
||||
|
||||
{pullRequest.additions}
|
||||
</span>
|
||||
<span style={{ marginLeft: "0.5em", color: "#dd4a48" }}>
|
||||
<IconContext.Provider
|
||||
value={{ style: { verticalAlign: "middle" } }}
|
||||
>
|
||||
<GoDash size={24}></GoDash>
|
||||
</IconContext.Provider>
|
||||
|
||||
{pullRequest.deletions}
|
||||
</span>
|
||||
</h3>
|
||||
</Card.Header>
|
||||
<Card.Body css={{ py: "$2" }}>
|
||||
<p>
|
||||
<span style={{ fontWeight: 700 }}>Last Updated At</span> -{" "}
|
||||
{dateString}
|
||||
</p>
|
||||
<ReactMarkdown>{pullRequest.body}</ReactMarkdown>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
);
|
||||
}
|
||||
|
||||
export function DownloadTableReleaseCard({
|
||||
release,
|
||||
downloadButtonText,
|
||||
isNightly,
|
||||
}) {
|
||||
return !release ? null : (
|
||||
<Grid.Container css={{ mt: "0.5em" }}>
|
||||
<Grid xs={12}>
|
||||
<Card css={{ p: "$6", mw: "100%" }}>
|
||||
<Card.Header>
|
||||
<Text h3 css={{ lineHeight: "$xs" }}>
|
||||
{release.version}
|
||||
</Text>
|
||||
</Card.Header>
|
||||
<Card.Body css={{ py: "$2" }}>
|
||||
<Text>
|
||||
<ReactMarkdown>{release.description}</ReactMarkdown>
|
||||
</Text>
|
||||
</Card.Body>
|
||||
<Card.Footer>
|
||||
<ReleaseDownloadButton
|
||||
release={release}
|
||||
buttonText={downloadButtonText}
|
||||
isNightly={isNightly}
|
||||
bordered={true}
|
||||
/>
|
||||
</Card.Footer>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSelectedCard(selectedData, tableType) {
|
||||
if (!selectedData) {
|
||||
return null;
|
||||
}
|
||||
if (tableType === "stable") {
|
||||
return (
|
||||
<DownloadTableReleaseCard
|
||||
release={selectedData}
|
||||
downloadButtonText={"Download Release"}
|
||||
isNightly={false}
|
||||
/>
|
||||
);
|
||||
} else if (tableType === "nightly") {
|
||||
return (
|
||||
<DownloadTableReleaseCard
|
||||
release={selectedData}
|
||||
downloadButtonText={"Download Release"}
|
||||
isNightly={true}
|
||||
/>
|
||||
);
|
||||
} else if (tableType === "pullRequests") {
|
||||
return <PullRequestTableCard pullRequest={selectedData} />;
|
||||
}
|
||||
}
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableColumn,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
Pagination,
|
||||
} from "@nextui-org/react";
|
||||
|
||||
export function DownloadTable({
|
||||
pageSize,
|
||||
@@ -117,118 +19,126 @@ export function DownloadTable({
|
||||
fetchMoreFunc,
|
||||
tableType,
|
||||
}) {
|
||||
// NOTE: https://github.com/nextui-org/nextui/issues/2193
|
||||
const [tableKey, setTableKey] = useState("");
|
||||
const [tableData, setTableData] = useState({ data: [] });
|
||||
const [tableLoadingState, setTableLoadingState] = useState("idle");
|
||||
const [tablePage, setTablePage] = useState(1);
|
||||
const [selectedTableRow, setSelectedTableRow] = useState(undefined);
|
||||
const [selectedVersion, setSelectedVersion] = useState(undefined);
|
||||
|
||||
const rowsPerPage = 10;
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(initialTableData);
|
||||
}, [initialTableData]);
|
||||
|
||||
return (
|
||||
<Grid xs={12}>
|
||||
<Grid.Container>
|
||||
<Grid xs={12}>
|
||||
<Table
|
||||
striped
|
||||
compact
|
||||
sticked
|
||||
selectionMode={"single"}
|
||||
color={color}
|
||||
aria-label={tableLabel}
|
||||
onSelectionChange={(selection) => {
|
||||
if (selection.size <= 0) {
|
||||
setSelectedTableRow(undefined);
|
||||
} else {
|
||||
setSelectedTableRow([...selection][0]);
|
||||
}
|
||||
}}
|
||||
css={{
|
||||
height: "auto",
|
||||
minWidth: "100%",
|
||||
display: "table",
|
||||
noMargin: true,
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Table.Header columns={tableColumns}>
|
||||
{(column) => (
|
||||
<Table.Column key={column.key}>{column.label}</Table.Column>
|
||||
)}
|
||||
</Table.Header>
|
||||
<Table.Body items={tableData.data} loadingState={tableLoadingState}>
|
||||
{(item) => (
|
||||
<Table.Row key={tableData.data.indexOf(item)}>
|
||||
{(columnKey) => (
|
||||
<Table.Cell>{renderRowFunc(item, columnKey)}</Table.Cell>
|
||||
)}
|
||||
</Table.Row>
|
||||
)}
|
||||
</Table.Body>
|
||||
<Table.Pagination
|
||||
noMargin
|
||||
align="center"
|
||||
rowsPerPage={
|
||||
tableLoadingState == "loading"
|
||||
? 2
|
||||
: Math.min(pageSize, tableData?.pageInfo?.total)
|
||||
}
|
||||
page={tablePage}
|
||||
onPageChange={async (page) => {
|
||||
setTableLoadingState("loading");
|
||||
page = page - 1;
|
||||
const newLength = (page + 1) * pageSize;
|
||||
const newOffset = page * pageSize;
|
||||
const tableRows = React.useMemo(() => {
|
||||
const start = (tablePage - 1) * rowsPerPage;
|
||||
const end = start + rowsPerPage;
|
||||
return tableData.data.slice(start, end);
|
||||
}, [tablePage, tableData]);
|
||||
|
||||
// See if we have to fetch more from the API
|
||||
const fetchMore =
|
||||
tableData.data.length < newLength ||
|
||||
Object.keys(tableData.data[newOffset]).length === 0;
|
||||
if (fetchMore) {
|
||||
const resp = await fetchMoreFunc(newOffset);
|
||||
const data = await resp.json();
|
||||
const newTableData = tableData.data;
|
||||
// If array isn't as big as the start index, we need to fill up to that point
|
||||
if (newTableData.length < newOffset) {
|
||||
for (
|
||||
let i = 0, newSize = newOffset - newTableData.length;
|
||||
i < newSize;
|
||||
i++
|
||||
) {
|
||||
newTableData.push({});
|
||||
return (
|
||||
<div className="w-full container">
|
||||
<div className="flex flex-row">
|
||||
<Table
|
||||
key={`${tableLabel}-${tableKey}`}
|
||||
isStriped
|
||||
compact
|
||||
removeWrapper
|
||||
selectionMode={"single"}
|
||||
aria-label={tableLabel}
|
||||
onSelectionChange={(selection) => {
|
||||
if (!selection.size <= 0) {
|
||||
const key = [...selection][0];
|
||||
if (key !== selectedVersion) {
|
||||
setSelectedVersion(key);
|
||||
setTableKey(crypto.randomUUID());
|
||||
}
|
||||
}
|
||||
}}
|
||||
bottomContent={
|
||||
<div className="flex w-full justify-center">
|
||||
<Pagination
|
||||
isCompact
|
||||
showControls
|
||||
showShadow
|
||||
page={tablePage}
|
||||
total={Math.ceil(tableData?.pageInfo?.total / pageSize)}
|
||||
onChange={async (page) => {
|
||||
setTableLoadingState("loadingMore");
|
||||
page = page - 1;
|
||||
const newLength = (page + 1) * pageSize;
|
||||
const newOffset = page * pageSize;
|
||||
|
||||
// See if we have to fetch more from the API
|
||||
const fetchMore =
|
||||
tableData.data.length < newLength ||
|
||||
Object.keys(tableData.data[newOffset]).length === 0;
|
||||
if (fetchMore) {
|
||||
const resp = await fetchMoreFunc(newOffset);
|
||||
const data = await resp.json();
|
||||
const newTableData = tableData.data;
|
||||
// If array isn't as big as the start index, we need to fill up to that point
|
||||
if (newTableData.length < newOffset) {
|
||||
for (
|
||||
let i = 0, newSize = newOffset - newTableData.length;
|
||||
i < newSize;
|
||||
i++
|
||||
) {
|
||||
newTableData.push({});
|
||||
}
|
||||
}
|
||||
}
|
||||
// We can then fill in the array with the indices provided
|
||||
// - if we are out of bounds, push undefined
|
||||
// - if there is a value OTHER than undefined, skip, the user is just jumping around pages
|
||||
for (let i = 0; i < data.data.length; i++) {
|
||||
if (i + newOffset >= newTableData.length) {
|
||||
newTableData.push(data.data[i]);
|
||||
} else if (
|
||||
Object.keys(newTableData[i + newOffset]).length !== 0
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
newTableData[i + newOffset] = data.data[i];
|
||||
// We can then fill in the array with the indices provided
|
||||
// - if we are out of bounds, push undefined
|
||||
// - if there is a value OTHER than undefined, skip, the user is just jumping around pages
|
||||
for (let i = 0; i < data.data.length; i++) {
|
||||
if (i + newOffset >= newTableData.length) {
|
||||
newTableData.push(data.data[i]);
|
||||
} else if (
|
||||
Object.keys(newTableData[i + newOffset]).length !== 0
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
newTableData[i + newOffset] = data.data[i];
|
||||
}
|
||||
}
|
||||
setTableData({
|
||||
data: newTableData,
|
||||
pageInfo: data.pageInfo,
|
||||
});
|
||||
}
|
||||
setTableData({
|
||||
data: newTableData,
|
||||
pageInfo: data.pageInfo,
|
||||
});
|
||||
}
|
||||
setTableLoadingState("idle");
|
||||
setTablePage(page + 1);
|
||||
}}
|
||||
total={Math.ceil(tableData?.pageInfo?.total / pageSize)}
|
||||
/>
|
||||
</Table>
|
||||
</Grid>
|
||||
{selectedTableRow === undefined
|
||||
? renderSelectedCard(selectedTableRow, tableType)
|
||||
: renderSelectedCard(tableData.data[selectedTableRow], tableType)}
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
setTableLoadingState("idle");
|
||||
setTablePage(page + 1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<TableHeader columns={tableColumns}>
|
||||
{(column) => (
|
||||
<TableColumn key={column.key}>{column.label}</TableColumn>
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody items={tableRows} loadingState={tableLoadingState}>
|
||||
{(item) => (
|
||||
<TableRow key={item.version}>
|
||||
{(columnKey) => (
|
||||
<TableCell>
|
||||
{renderRowFunc(
|
||||
item,
|
||||
columnKey,
|
||||
tableType === "nightly",
|
||||
selectedVersion !== undefined &&
|
||||
item.version === selectedVersion,
|
||||
)}
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Row, Col } from "@nextui-org/react";
|
||||
import { getCookieConsentValue } from "react-cookie-consent";
|
||||
|
||||
export function GoogleAd({ margins = "5em", alignment = "center" }) {
|
||||
export function GoogleAd() {
|
||||
const [displayAd, setDisplayAd] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -16,12 +15,8 @@ export function GoogleAd({ margins = "5em", alignment = "center" }) {
|
||||
}, [displayAd]);
|
||||
|
||||
return !displayAd ? null : (
|
||||
<Row
|
||||
justify={alignment}
|
||||
css={{ mt: margins, mb: margins, width: "auto" }}
|
||||
gap={2}
|
||||
>
|
||||
<Col span={12}>
|
||||
<div className="flex justify-center mt-5 mb-5 gap-2">
|
||||
<div className="flex-auto">
|
||||
<ins
|
||||
className="adsbygoogle"
|
||||
style={{
|
||||
@@ -34,7 +29,7 @@ export function GoogleAd({ margins = "5em", alignment = "center" }) {
|
||||
data-ad-format="auto"
|
||||
data-full-width-responsive="true"
|
||||
></ins>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Dropdown } from "@nextui-org/react";
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
DropdownTrigger,
|
||||
DropdownItem,
|
||||
DropdownSection,
|
||||
DropdownMenu,
|
||||
} from "@nextui-org/react";
|
||||
import { BsWindows, BsApple } from "react-icons/bs";
|
||||
import { FaLinux } from "react-icons/fa";
|
||||
import { IoIosCloudyNight } from "react-icons/io";
|
||||
import { GiBrickWall } from "react-icons/gi";
|
||||
import { useMediaQuery } from "../../utils/mediaQuery";
|
||||
import { semanticColors } from "@nextui-org/theme";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
// Function to get the latest release for a specific platform
|
||||
export function getLatestRelease(releases, platform) {
|
||||
@@ -46,9 +55,10 @@ function generateDropdownItems(release, os, assets, textRemovals, isNightly) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let fillColor = "var(--nextui-colors-primary)";
|
||||
let fillColor = semanticColors.dark.primary.DEFAULT;
|
||||
// TODO - based on theme!
|
||||
if (isNightly) {
|
||||
fillColor = "var(--nextui-colors-warning)";
|
||||
fillColor = semanticColors.dark.warning.DEFAULT;
|
||||
}
|
||||
|
||||
let items = [];
|
||||
@@ -99,14 +109,14 @@ function generateDropdownItems(release, os, assets, textRemovals, isNightly) {
|
||||
}
|
||||
|
||||
items.push(
|
||||
<Dropdown.Item
|
||||
<DropdownItem
|
||||
key={asset.url}
|
||||
description={release.version}
|
||||
icon={getOSIcon(os, fillColor)}
|
||||
startContent={getOSIcon(os, fillColor)}
|
||||
css={{ transition: "none" }}
|
||||
>
|
||||
{displayName}
|
||||
</Dropdown.Item>,
|
||||
</DropdownItem>,
|
||||
);
|
||||
}
|
||||
return items;
|
||||
@@ -120,6 +130,34 @@ function openAssetLink(href) {
|
||||
}).click();
|
||||
}
|
||||
|
||||
function renderDropdownItems(errorMsg, windowsItems, linuxItems, macosItems) {
|
||||
let items = [];
|
||||
if (errorMsg !== undefined) {
|
||||
items.push(<DropdownSection title={errorMsg}></DropdownSection>);
|
||||
} else {
|
||||
items.push(
|
||||
<DropdownSection
|
||||
showDivider
|
||||
title={windowsItems.length > 0 ? "Windows" : "Windows - None Available"}
|
||||
>
|
||||
{windowsItems}
|
||||
</DropdownSection>,
|
||||
<DropdownSection
|
||||
showDivider
|
||||
title={linuxItems.length > 0 ? "Linux" : "Linux - None Available"}
|
||||
>
|
||||
{linuxItems}
|
||||
</DropdownSection>,
|
||||
<DropdownSection
|
||||
title={macosItems.length > 0 ? "MacOS" : "MacOS - None Available"}
|
||||
>
|
||||
{macosItems}
|
||||
</DropdownSection>,
|
||||
);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
export function ReleaseDownloadButton({
|
||||
release,
|
||||
buttonText,
|
||||
@@ -210,64 +248,55 @@ export function ReleaseDownloadButton({
|
||||
}
|
||||
}, [release]);
|
||||
|
||||
// Render the dropdown button and menu
|
||||
return (
|
||||
<Dropdown
|
||||
isBordered
|
||||
placement={
|
||||
placement ? placement : useMediaQuery(960) ? "bottom-left" : "right-top"
|
||||
placement
|
||||
? placement
|
||||
: useMediaQuery(960)
|
||||
? "bottom-end"
|
||||
: "right-start"
|
||||
}
|
||||
classNames={{
|
||||
base: "docusaurus-reset before:bg-default-200", // change arrow background
|
||||
content: "py-1 px-1 border border-default-200",
|
||||
}}
|
||||
>
|
||||
<Dropdown.Button
|
||||
<DropdownTrigger>
|
||||
<Button
|
||||
color={isNightly ? "warning" : "primary"}
|
||||
variant="solid"
|
||||
disabled={isDisabled}
|
||||
className="border-none font-medium cursor-pointer"
|
||||
>
|
||||
{isNightly ? (
|
||||
<IoIosCloudyNight size={22} />
|
||||
) : (
|
||||
<GiBrickWall size={16} />
|
||||
)}
|
||||
|
||||
{buttonText}
|
||||
<svg
|
||||
fill="none"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M17.9188 8.17969H11.6888H6.07877C5.11877 8.17969 4.63877 9.33969 5.31877 10.0197L10.4988 15.1997C11.3288 16.0297 12.6788 16.0297 13.5088 15.1997L15.4788 13.2297L18.6888 10.0197C19.3588 9.33969 18.8788 8.17969 17.9188 8.17969Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
color={isNightly ? "warning" : "primary"}
|
||||
css={buttonStyling}
|
||||
bordered={bordered}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{isNightly ? <IoIosCloudyNight size={22} /> : <GiBrickWall size={16} />}
|
||||
|
||||
{buttonText}
|
||||
</Dropdown.Button>
|
||||
<Dropdown.Menu
|
||||
color={isNightly ? "warning" : "primary"}
|
||||
aria-label="Actions"
|
||||
css={{ $$dropdownMenuWidth: "100%" }}
|
||||
variant="faded"
|
||||
onAction={(assetUrl) => openAssetLink(assetUrl)}
|
||||
>
|
||||
<Dropdown.Section
|
||||
title={
|
||||
errorMsg === undefined
|
||||
? windowsItems.length > 0
|
||||
? "Windows"
|
||||
: "Windows - None Available"
|
||||
: errorMsg
|
||||
}
|
||||
>
|
||||
{errorMsg === undefined ? windowsItems : null}
|
||||
</Dropdown.Section>
|
||||
<Dropdown.Section
|
||||
title={
|
||||
errorMsg === undefined
|
||||
? linuxItems.length > 0
|
||||
? "Linux"
|
||||
: "Linux - None Available"
|
||||
: errorMsg
|
||||
}
|
||||
>
|
||||
{errorMsg === undefined ? linuxItems : null}
|
||||
</Dropdown.Section>
|
||||
<Dropdown.Section
|
||||
title={
|
||||
errorMsg === undefined
|
||||
? macosItems.length > 0
|
||||
? "MacOS"
|
||||
: "MacOS - None Available"
|
||||
: errorMsg
|
||||
}
|
||||
>
|
||||
{errorMsg === undefined ? macosItems : null}
|
||||
</Dropdown.Section>
|
||||
</Dropdown.Menu>
|
||||
{renderDropdownItems(errorMsg, windowsItems, linuxItems, macosItems)}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
@@ -19,8 +23,10 @@
|
||||
--navbar-svg-icons-filter: invert(0%) sepia(100%) saturate(7500%)
|
||||
hue-rotate(209deg) brightness(86%) contrast(114%);
|
||||
--nightly-button-background: #000;
|
||||
--ifm-footer-background-color: var(--nextui-colors-accents0);
|
||||
--ifm-footer-background-color: rgb(244, 244, 244);
|
||||
--card-color-background: #000;
|
||||
--home-video-background-color: #fff;
|
||||
--ifm-footer-link-color: #000;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
@@ -38,6 +44,63 @@
|
||||
hue-rotate(81deg) brightness(106%) contrast(106%);
|
||||
--nightly-button-background: var(--nextui-colors-accents0);
|
||||
--card-color-background: var(--nextui-colors-backgroundContrast);
|
||||
--home-video-background-color: #000;
|
||||
--ifm-footer-background-color: #020915;
|
||||
--ifm-footer-link-color: #fff;
|
||||
}
|
||||
|
||||
p.docusaurus-reset {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button.docusaurus-reset {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.docusaurus-reset table {
|
||||
border: none;
|
||||
border-top: none;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.docusaurus-reset table tr {
|
||||
border: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.docusaurus-reset thead tr {
|
||||
border: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
menu.docusaurus-reset {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.docusaurus-reset ol {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.docusaurus-reset ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.docusaurus-reset table tr:nth-child(2n) {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.remove-last-bottom-margin p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.theme-doc-sidebar-container nav {
|
||||
backdrop-filter: unset !important;
|
||||
}
|
||||
|
||||
/* Workaround to resolve FOUC issues */
|
||||
@@ -94,10 +157,18 @@ html.app-loaded {
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--ifm-font-family-base);
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: var(--ifm-font-family-base);
|
||||
}
|
||||
|
||||
.default-font {
|
||||
font-family: var(--ifm-font-family-base);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer.footer {
|
||||
@@ -108,7 +179,7 @@ footer.footer {
|
||||
/* Navbar */
|
||||
|
||||
.navbar__link {
|
||||
font-weight: 400;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.navbar__link--active {
|
||||
@@ -117,13 +188,13 @@ footer.footer {
|
||||
|
||||
.navbar--fixed-top {
|
||||
box-shadow: rgba(2, 1, 1, 0.1) 0px 5px 20px -5px;
|
||||
z-index: 999;
|
||||
z-index: 199;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .navbar-sidebar {
|
||||
box-shadow: rgba(2, 1, 1, 0.1) 0px 5px 20px -5px;
|
||||
z-index: 999;
|
||||
z-index: 199;
|
||||
background: transparent;
|
||||
backdrop-filter: saturate(180%) blur(10px);
|
||||
--webkit-backdrop-filter: saturate(180%) blur(10px);
|
||||
@@ -307,3 +378,63 @@ table td {
|
||||
.header-blog .margin-vert--md {
|
||||
color: rgb(227, 227, 227);
|
||||
}
|
||||
|
||||
.pr-link {
|
||||
margin-bottom: 1em !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.imgCompareContainer {
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 42.3%;
|
||||
cursor: col-resize;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.imgCompareContainer > img {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
.imgClipper {
|
||||
width: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 2px 0 red;
|
||||
}
|
||||
|
||||
.imgClipper > img {
|
||||
width: 200%;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
.imgCmpLabel {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
pointer-events: none;
|
||||
top: 1em;
|
||||
text-shadow:
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
}
|
||||
.imgCmpLabel.before {
|
||||
left: 1rem;
|
||||
}
|
||||
.imgCmpLabel.after {
|
||||
right: 1rem;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Row, Col } from "@nextui-org/react";
|
||||
import {
|
||||
BarChart,
|
||||
Bar,
|
||||
@@ -100,17 +99,17 @@ export default function Chart(props) {
|
||||
return (
|
||||
<div>
|
||||
{props.title ? (
|
||||
<Row css={{ mb: "1em", mt: "1em", textAlign: "center" }}>
|
||||
<Col span={12}>{props.title}</Col>
|
||||
</Row>
|
||||
<div className="flex justify-center my-4 text-center">
|
||||
<div className="w-full">{props.title}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<Row css={{ mb: "1em", mt: "1em" }}>
|
||||
<Col span={12}>
|
||||
<div className="flex justify-center my-4">
|
||||
<div className="w-full">
|
||||
{chartData === undefined
|
||||
? "Loading Chart Data"
|
||||
: _renderChart(chartData)}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React from "react";
|
||||
import { Grid } from "@nextui-org/react";
|
||||
|
||||
export default function Image({ children, cols, src, alt }) {
|
||||
return (
|
||||
<Grid.Container style={{ marginBottom: "1em" }}>
|
||||
<Grid xs={12} md={Math.min(12, cols ?? 12)}>
|
||||
<div className="flex flex-wrap mb-4">
|
||||
<div className={`w-full md:w-${Math.min(12, cols ?? 12)}/12`}>
|
||||
<img src={src} loading="lazy" alt={alt ?? ""} />
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import { Grid } from "@nextui-org/react";
|
||||
|
||||
export default function ImageCompare({
|
||||
children,
|
||||
@@ -9,13 +8,13 @@ export default function ImageCompare({
|
||||
altRight,
|
||||
}) {
|
||||
return (
|
||||
<Grid.Container gap={1} style={{ marginBottom: "1em" }}>
|
||||
<Grid xs={6}>
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
<div className="w-1/2">
|
||||
<img src={left} loading="lazy" alt={altLeft ?? ""} />
|
||||
</Grid>
|
||||
<Grid xs={6}>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<img src={right} loading="lazy" alt={altRight ?? ""} />
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from "react";
|
||||
import { GoGitCommit, GoGitPullRequest } from "react-icons/go";
|
||||
import { Avatar, Grid, Tooltip } from "@nextui-org/react";
|
||||
import { Avatar, Tooltip, AvatarGroup } from "@nextui-org/react";
|
||||
import { IconContext } from "react-icons";
|
||||
import styles from "./PCSX2PRLink.module.css";
|
||||
|
||||
function generatePRLinks(prNums) {
|
||||
if (!prNums) {
|
||||
@@ -82,26 +81,23 @@ function generateAuthorAvatars(authors) {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Avatar.Group
|
||||
animated={avatars.length > 1}
|
||||
style={{ marginRight: "0.5em" }}
|
||||
>
|
||||
<AvatarGroup animated={avatars.length > 1} style={{ marginRight: "0.5em" }}>
|
||||
{avatars}
|
||||
</Avatar.Group>
|
||||
</AvatarGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PCSX2PRLink({ children, prNums, shas, authors }) {
|
||||
return (
|
||||
<Grid.Container className={styles["pr-link"]}>
|
||||
<Grid xs={12} alignItems="center">
|
||||
<span style={{ marginRight: "1.5em" }}>{children}</span>
|
||||
</Grid>
|
||||
<Grid xs={12} alignItems="center" css={{ ml: "0.75em" }}>
|
||||
<div className={"pr-link flex flex-wrap"}>
|
||||
<div className="w-full flex items-center">
|
||||
<span className="mr-6">{children}</span>
|
||||
</div>
|
||||
<div className="w-full flex items-center ml-3">
|
||||
{generateAuthorAvatars(authors)}
|
||||
{generatePRLinks(prNums)}
|
||||
{generateCommitLinks(shas)}
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
.pr-link {
|
||||
margin-bottom: 1em !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import React, { useRef } from "react";
|
||||
import { Grid } from "@nextui-org/react";
|
||||
import styles from "./SliderCompare.module.css";
|
||||
|
||||
export default function SliderCompare({ children, cols, before, after }) {
|
||||
let isDragging = false;
|
||||
@@ -48,11 +46,11 @@ export default function SliderCompare({ children, cols, before, after }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid.Container style={{ marginBottom: "1em" }}>
|
||||
<Grid xs={12} md={Math.min(12, cols ?? 12)}>
|
||||
<div className="flex flex-wrap mb-4">
|
||||
<div className={`w-full md:w-${Math.min(12, cols ?? 12)}/12`}>
|
||||
<div
|
||||
ref={container}
|
||||
className={styles.imgCompareContainer}
|
||||
className={`imgCompareContainer`}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseDown={handleMouseDownOrTouchStart}
|
||||
onTouchStart={handleMouseDownOrTouchStart}
|
||||
@@ -65,7 +63,7 @@ export default function SliderCompare({ children, cols, before, after }) {
|
||||
draggable="false"
|
||||
onLoad={imageLoaded}
|
||||
/>
|
||||
<div ref={imgClipper} className={styles.imgClipper}>
|
||||
<div ref={imgClipper} className={`imgClipper`}>
|
||||
<img
|
||||
ref={beforeImg}
|
||||
src={before}
|
||||
@@ -73,11 +71,11 @@ export default function SliderCompare({ children, cols, before, after }) {
|
||||
alt=""
|
||||
draggable="false"
|
||||
/>
|
||||
<div className={`${styles.imgCompareLabel} before`}>Before</div>
|
||||
<div className={`imgCompareLabel before`}>Before</div>
|
||||
</div>
|
||||
<div className={`${styles.imgCompareLabel} after`}>After</div>
|
||||
<div className={`imgCompareLabel after`}>After</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
.imgCompareContainer {
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 42.3%;
|
||||
cursor: col-resize;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.imgCompareContainer > img {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
.imgClipper {
|
||||
width: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 2px 0 red;
|
||||
}
|
||||
|
||||
.imgClipper > img {
|
||||
width: 200%;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
.imgCmpLabel {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
pointer-events: none;
|
||||
top: 1em;
|
||||
text-shadow:
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
}
|
||||
.imgCmpLabel.before {
|
||||
left: 1rem;
|
||||
}
|
||||
.imgCmpLabel.after {
|
||||
right: 1rem;
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -2,22 +2,23 @@ import React, { useState, useEffect } from "react";
|
||||
import Layout from "@theme/Layout";
|
||||
import {
|
||||
Table,
|
||||
Grid,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableColumn,
|
||||
Pagination,
|
||||
TableRow,
|
||||
TableCell,
|
||||
Tooltip,
|
||||
Badge,
|
||||
Chip,
|
||||
Link,
|
||||
Loading,
|
||||
Input,
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Button,
|
||||
} from "@nextui-org/react";
|
||||
import { MdLibraryBooks, MdForum } from "react-icons/md";
|
||||
import Fuse from "fuse.js";
|
||||
import { DateTime } from "luxon";
|
||||
import { GoogleAd } from "../../components/GoogleAd";
|
||||
import { useMediaQuery } from "../../utils/mediaQuery";
|
||||
import { CompatibilityButton } from "../../components/CompatibilityButton";
|
||||
|
||||
function getTableData(compatData) {
|
||||
const compatRows = [];
|
||||
@@ -138,72 +139,87 @@ const renderCell = (entry, columnKey) => {
|
||||
switch (cellValue.toLowerCase()) {
|
||||
case "perfect":
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="success"
|
||||
css={{ backgroundColor: "#BA68C8" }}
|
||||
classNames={{
|
||||
base: "font-semibold bg-[#BA68C8]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
case "playable":
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="primary"
|
||||
css={{ backgroundColor: "#9CCC65", color: "#000" }}
|
||||
classNames={{
|
||||
base: "bg-[#9CCC65] text-[#000]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
case "ingame":
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="secondary"
|
||||
css={{ backgroundColor: "#29B6F6", color: "#000" }}
|
||||
classNames={{
|
||||
base: "bg-[#29B6F6] text-[#000]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
case "menus":
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="warning"
|
||||
css={{ backgroundColor: "#FBC02D", color: "#000" }}
|
||||
classNames={{
|
||||
base: "bg-[#FBC02D] text-[#000]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
case "intros":
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="warning"
|
||||
css={{ backgroundColor: "#F57C00", color: "#000" }}
|
||||
classNames={{
|
||||
base: "bg-[#F57C00] text-[#000]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Badge
|
||||
<Chip
|
||||
borderWeight="light"
|
||||
color="error"
|
||||
css={{ backgroundColor: "#D32F2F" }}
|
||||
classNames={{
|
||||
base: "bg-[#D32F2F] text-[#000]",
|
||||
content: "font-medium",
|
||||
}}
|
||||
>
|
||||
{cellValue}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
case "latest_testing":
|
||||
if (cellValue) {
|
||||
let color = "neutral";
|
||||
if (
|
||||
cellValue.version.startsWith("1.6") ||
|
||||
cellValue.version.startsWith("1.7")
|
||||
) {
|
||||
let color = "default";
|
||||
if (cellValue.version.startsWith("2.")) {
|
||||
color = "success";
|
||||
}
|
||||
if (cellValue.date) {
|
||||
@@ -213,26 +229,54 @@ const renderCell = (entry, columnKey) => {
|
||||
content={`Tested on - ${date.toLocaleString(DateTime.DATE_FULL)}`}
|
||||
placement="left"
|
||||
>
|
||||
<Badge variant="bordered" color={color}>
|
||||
<Chip
|
||||
classNames={{
|
||||
base: "border border-solid",
|
||||
content: "font-medium",
|
||||
}}
|
||||
variant="bordered"
|
||||
color={color}
|
||||
>
|
||||
{cellValue.version}
|
||||
</Badge>
|
||||
</Chip>
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Badge variant="bordered" color={color}>
|
||||
<Chip
|
||||
classNames={{
|
||||
base: "border border-solid",
|
||||
content: "font-medium",
|
||||
}}
|
||||
variant="bordered"
|
||||
color={color}
|
||||
>
|
||||
{cellValue.version}
|
||||
</Badge>
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
case "links":
|
||||
const icons = [];
|
||||
const icons = [
|
||||
<div>
|
||||
<Tooltip content={"GitHub Issues"} placement={"left"}>
|
||||
<Link
|
||||
href={encodeURI(
|
||||
`https://github.com/PCSX2/pcsx2/issues?q=is:issue ${entry.serial} OR "${entry.title}"`,
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaGithub size={22}></FaGithub>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</div>,
|
||||
];
|
||||
if (cellValue?.wiki) {
|
||||
icons.push(
|
||||
<Grid>
|
||||
<div>
|
||||
<Tooltip content={"Wiki Page"} placement={"left"}>
|
||||
<Link
|
||||
href={cellValue.wiki}
|
||||
@@ -242,12 +286,12 @@ const renderCell = (entry, columnKey) => {
|
||||
<MdLibraryBooks size={22}></MdLibraryBooks>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</Grid>,
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
if (cellValue?.forum) {
|
||||
icons.push(
|
||||
<Grid>
|
||||
<div>
|
||||
<Tooltip content={"Forum Post"} placement={"left"}>
|
||||
<Link
|
||||
href={cellValue.forum}
|
||||
@@ -257,11 +301,11 @@ const renderCell = (entry, columnKey) => {
|
||||
<MdForum size={22}></MdForum>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</Grid>,
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
if (icons.length > 0) {
|
||||
return <Grid.Container>{icons}</Grid.Container>;
|
||||
return <div class="flex flex-row flex-wrap gap-2">{icons}</div>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -285,6 +329,7 @@ const searchOptions = {
|
||||
};
|
||||
|
||||
import CompatData from "@site/static/data/compat/data.min.json";
|
||||
import { FaGithub } from "react-icons/fa";
|
||||
|
||||
export default function Compatiblity() {
|
||||
// State
|
||||
@@ -306,17 +351,32 @@ export default function Compatiblity() {
|
||||
intro: undefined,
|
||||
nothing: undefined,
|
||||
});
|
||||
const [loadingState, setLoadingState] = useState("loading");
|
||||
const [isTableLoading, setIsTableLoading] = useState(true);
|
||||
const [page, setPage] = useState(1);
|
||||
const [searchString, setSearchString] = useState("");
|
||||
const [readyToFilter, setReadyToFilter] = useState(false);
|
||||
|
||||
const rowsPerPage = 25;
|
||||
|
||||
const tableRows = React.useMemo(() => {
|
||||
const start = (page - 1) * rowsPerPage;
|
||||
const end = start + rowsPerPage;
|
||||
return filteredData.slice(start, end);
|
||||
}, [page, filteredData]);
|
||||
|
||||
const totalPages = React.useMemo(() => {
|
||||
if (Math.ceil(filteredData.length / 25) < 1) {
|
||||
return 1;
|
||||
}
|
||||
return Math.ceil(filteredData.length / 25);
|
||||
}, [filteredData]);
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(getTableData(CompatData));
|
||||
// Determine distribution of statuses
|
||||
setFilterStats(calcPercentages(CompatData));
|
||||
setFilteredData(getTableData(CompatData));
|
||||
setLoadingState("idle");
|
||||
setIsTableLoading(false);
|
||||
setReadyToFilter(true);
|
||||
}, []);
|
||||
|
||||
@@ -396,289 +456,149 @@ export default function Compatiblity() {
|
||||
title="Compatibility"
|
||||
description="Find out how well your PlayStation 2 games will run on PCSX2 and if there are any associated issues"
|
||||
>
|
||||
<main>
|
||||
<Container css={{ mt: "2em" }}>
|
||||
<Grid.Container>
|
||||
<Grid.Container>
|
||||
<Grid>
|
||||
<h1>Compatibility Data</h1>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Grid.Container>
|
||||
<Grid xs={12} css={{ color: "$accents7" }}>
|
||||
<p>
|
||||
Here is the latest data on the emulator's compatibility. Use
|
||||
the filtering and searching options below to find what you are
|
||||
interested in
|
||||
</p>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Row justify="center">
|
||||
<Col
|
||||
css={{
|
||||
"@md": {
|
||||
width: "50%",
|
||||
},
|
||||
"@mdMax": {
|
||||
width: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<main class="docusaurus-reset">
|
||||
<div className="w-full container mx-auto mt-5">
|
||||
<div class="flex flex-row">
|
||||
<h1>Compatibility Data</h1>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<p class="text-default-400">
|
||||
Here is the latest data on the emulator's compatibility. Use the
|
||||
filtering and searching options below to find what you are
|
||||
interested in
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<div class="w-full md:w-1/2">
|
||||
<GoogleAd></GoogleAd>
|
||||
</div>
|
||||
{!useMediaQuery(960) && (
|
||||
<div class="w-full md:w-1/2">
|
||||
<GoogleAd></GoogleAd>
|
||||
</Col>
|
||||
{useMediaQuery(960) ? null : (
|
||||
<Col
|
||||
css={{
|
||||
"@md": {
|
||||
width: "50%",
|
||||
},
|
||||
"@mdMax": {
|
||||
width: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GoogleAd></GoogleAd>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Grid.Container alignItems="end" css={{ mt: "2em", mb: "1em" }}>
|
||||
<Grid xs={12} lg={4}>
|
||||
<Grid.Container gap={1}>
|
||||
<Grid xs={12}>
|
||||
<Input
|
||||
label="Search by Name, Serial or CRC"
|
||||
width="100%"
|
||||
onChange={changeSearchString}
|
||||
disabled={loadingState === "loading"}
|
||||
></Input>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
<Grid xs={12} lg={8}>
|
||||
<Grid.Container alignItems="end" gap={1}>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.perfect}
|
||||
disabled={filterStats.perfect === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.perfect
|
||||
? "inherit"
|
||||
: "#BA68C8",
|
||||
color: filterOptions.perfect ? "#BA68C8" : "inherit",
|
||||
borderColor: filterOptions.perfect
|
||||
? "#BA68C8"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("perfect")}
|
||||
>
|
||||
{filterStats.perfect === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{perfectFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.playable}
|
||||
disabled={filterStats.playable === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.playable
|
||||
? "inherit"
|
||||
: "#9CCC65",
|
||||
color: filterOptions.playable ? "#9CCC65" : "#000",
|
||||
borderColor: filterOptions.playable
|
||||
? "#9CCC65"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("playable")}
|
||||
>
|
||||
{filterStats.playable === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{playableFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.ingame}
|
||||
disabled={filterStats.ingame === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.ingame
|
||||
? "inherit"
|
||||
: "#29B6F6",
|
||||
color: filterOptions.ingame ? "#29B6F6" : "#000",
|
||||
borderColor: filterOptions.ingame
|
||||
? "#29B6F6"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("ingame")}
|
||||
>
|
||||
{filterStats.ingame === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{ingameFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.menus}
|
||||
disabled={filterStats.menus === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.menus
|
||||
? "inherit"
|
||||
: "#FBC02D",
|
||||
color: filterOptions.menus ? "#FBC02D" : "#000",
|
||||
borderColor: filterOptions.menus
|
||||
? "#FBC02D"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("menus")}
|
||||
>
|
||||
{filterStats.menus === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{menusFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.intro}
|
||||
disabled={filterStats.intro === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.intro
|
||||
? "inherit"
|
||||
: "#F57C00",
|
||||
color: filterOptions.intro ? "#F57C00" : "#000",
|
||||
borderColor: filterOptions.intro
|
||||
? "#F57C00"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("intro")}
|
||||
>
|
||||
{filterStats.intro === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{introFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
bordered={filterOptions.nothing}
|
||||
disabled={filterStats.nothing === undefined}
|
||||
css={{
|
||||
backgroundColor: filterOptions.nothing
|
||||
? "inherit"
|
||||
: "#D32F2F",
|
||||
color: filterOptions.nothing ? "#D32F2F" : "inherit",
|
||||
borderColor: filterOptions.nothing
|
||||
? "#D32F2F"
|
||||
: "inherit",
|
||||
}}
|
||||
auto
|
||||
onPress={() => toggleFilter("nothing")}
|
||||
>
|
||||
{filterStats.nothing === undefined && (
|
||||
<Loading
|
||||
type="points-opacity"
|
||||
color="currentColor"
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{nothingFilterText}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Grid.Container>
|
||||
<Grid xs={12}>
|
||||
<Table
|
||||
compact
|
||||
striped
|
||||
sticked
|
||||
aria-label="Compatibility Table"
|
||||
css={{
|
||||
height: "auto",
|
||||
minWidth: "100%",
|
||||
display: "table",
|
||||
noMargin: true,
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Table.Header columns={columns}>
|
||||
{(column) => (
|
||||
<Table.Column key={column.key}>
|
||||
{column.label}
|
||||
</Table.Column>
|
||||
)}
|
||||
</Table.Header>
|
||||
<Table.Body items={filteredData} loadingState={loadingState}>
|
||||
{(item) => (
|
||||
<Table.Row key={item.key}>
|
||||
{(columnKey) => (
|
||||
<Table.Cell key={`${item.key}-${columnKey}`}>
|
||||
{renderCell(item, columnKey)}
|
||||
</Table.Cell>
|
||||
)}
|
||||
</Table.Row>
|
||||
)}
|
||||
</Table.Body>
|
||||
<Table.Pagination
|
||||
noMargin
|
||||
align="center"
|
||||
rowsPerPage={
|
||||
loadingState == "loading"
|
||||
? 2
|
||||
: Math.min(25, filteredData.length)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div class="grid gap-4 lg:grid-cols-2 md:grid-cols-1">
|
||||
<div class="lg:col-span-1 md:col-span-12 place-content-end">
|
||||
<Input
|
||||
classNames={{
|
||||
input: ["border-none", "default-font"],
|
||||
}}
|
||||
labelPlacement="outside"
|
||||
label="Search by Name, Serial or CRC"
|
||||
onChange={changeSearchString}
|
||||
disabled={isTableLoading}
|
||||
></Input>
|
||||
</div>
|
||||
<div class="grid md:col-span-12 lg:grid-rows-1 lg:col-span-1 grid-cols-1 md:grid-cols-2 md:grid-rows-3 gap-2">
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.perfect}
|
||||
disabledOrLoading={filterStats.perfect === undefined}
|
||||
category={"perfect"}
|
||||
onPress={() => toggleFilter("perfect")}
|
||||
>
|
||||
{filterStats.perfect !== undefined && (
|
||||
<span>{perfectFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.playable}
|
||||
disabledOrLoading={filterStats.playable === undefined}
|
||||
category={"playable"}
|
||||
onPress={() => toggleFilter("playable")}
|
||||
>
|
||||
{filterStats.playable !== undefined && (
|
||||
<span>{playableFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.ingame}
|
||||
disabledOrLoading={filterStats.ingame === undefined}
|
||||
category={"ingame"}
|
||||
onPress={() => toggleFilter("ingame")}
|
||||
>
|
||||
{filterStats.ingame !== undefined && (
|
||||
<span>{ingameFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.menus}
|
||||
disabledOrLoading={filterStats.menus === undefined}
|
||||
category={"menus"}
|
||||
onPress={() => toggleFilter("menus")}
|
||||
>
|
||||
{filterStats.menus !== undefined && (
|
||||
<span>{menusFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.intro}
|
||||
disabledOrLoading={filterStats.intro === undefined}
|
||||
category={"intro"}
|
||||
onPress={() => toggleFilter("intro")}
|
||||
>
|
||||
{filterStats.intro !== undefined && (
|
||||
<span>{introFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
|
||||
<CompatibilityButton
|
||||
categoryFiltered={filterOptions.nothing}
|
||||
disabledOrLoading={filterStats.nothing === undefined}
|
||||
category={"nothing"}
|
||||
onPress={() => toggleFilter("nothing")}
|
||||
>
|
||||
{filterStats.nothing !== undefined && (
|
||||
<span>{nothingFilterText}</span>
|
||||
)}
|
||||
</CompatibilityButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row flex-wrap gap-2 justify-center mt-5">
|
||||
{/* https://nextui.org/docs/components/table#paginated-table */}
|
||||
<Table
|
||||
isCompact
|
||||
fullWidth
|
||||
removeWrapper
|
||||
isStriped
|
||||
aria-label="Compatibility Table"
|
||||
bottomContent={
|
||||
<div className="flex w-full justify-center">
|
||||
<Pagination
|
||||
isCompact
|
||||
showControls
|
||||
showShadow
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
total={Math.ceil(filteredData.length / 25)}
|
||||
onChange={(page) => setPage(page)}
|
||||
total={totalPages}
|
||||
/>
|
||||
</Table>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Row justify="center">
|
||||
<Col
|
||||
css={{
|
||||
"@md": {
|
||||
width: "50%",
|
||||
},
|
||||
"@mdMax": {
|
||||
width: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GoogleAd></GoogleAd>
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid.Container>
|
||||
</Container>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<TableHeader columns={columns}>
|
||||
{(column) => (
|
||||
<TableColumn key={column.key}>{column.label}</TableColumn>
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody items={tableRows} isLoading={isTableLoading}>
|
||||
{(item) => (
|
||||
<TableRow key={item.key}>
|
||||
{(columnKey) => (
|
||||
<TableCell key={`${item.key}-${columnKey}`}>
|
||||
{renderCell(item, columnKey)}
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Layout from "@theme/Layout";
|
||||
import { Container, Text, Grid, Switch } from "@nextui-org/react";
|
||||
import { Switch } from "@nextui-org/react";
|
||||
import Admonition from "@theme/Admonition";
|
||||
import { ReleaseDownloadButton } from "../../components/ReleaseDownloadButton";
|
||||
import { DownloadTable } from "../../components/DownloadTable";
|
||||
import { getLatestRelease } from "../../components/ReleaseDownloadButton";
|
||||
import Head from "@docusaurus/Head";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { GoogleAd } from "../../components/GoogleAd";
|
||||
import useIsBrowser from "@docusaurus/useIsBrowser";
|
||||
|
||||
const releaseTableColumns = [
|
||||
{
|
||||
@@ -14,30 +16,59 @@ const releaseTableColumns = [
|
||||
label: "VERSION",
|
||||
},
|
||||
{
|
||||
key: "createdAt",
|
||||
label: "DATE",
|
||||
key: "releaseInfo",
|
||||
label: "INFO",
|
||||
},
|
||||
];
|
||||
|
||||
const renderReleaseCell = (release, columnKey) => {
|
||||
const renderReleaseCell = (release, columnKey, isNightly, isSelected) => {
|
||||
const cellValue = release[columnKey];
|
||||
switch (columnKey) {
|
||||
case "version":
|
||||
return <span className="monospaced">{cellValue}</span>;
|
||||
default:
|
||||
const date = new Date(cellValue);
|
||||
return date.toLocaleDateString(undefined, {
|
||||
const date = new Date(release.createdAt);
|
||||
const dateString = date.toLocaleDateString(undefined, {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
});
|
||||
if (!isSelected) {
|
||||
return dateString;
|
||||
} else {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="mb-2">
|
||||
<em>{dateString}</em>
|
||||
</div>
|
||||
<div className="mb-2 remove-last-bottom-margin">
|
||||
<ReactMarkdown>{release.description}</ReactMarkdown>
|
||||
</div>
|
||||
<div>
|
||||
<ReleaseDownloadButton
|
||||
release={release}
|
||||
buttonText={"Download Release"}
|
||||
isNightly={isNightly}
|
||||
bordered={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const baseApiUrl = "https://api.pcsx2.net/v1";
|
||||
let baseApiUrl = "https://api.pcsx2.net/v1";
|
||||
|
||||
export default function Downloads() {
|
||||
const isBrowser = useIsBrowser();
|
||||
|
||||
if (isBrowser && window.location.hostname === "localhost") {
|
||||
baseApiUrl = "https://localhost:8001/v1";
|
||||
}
|
||||
|
||||
const pageSize = 10;
|
||||
// State
|
||||
// - stables
|
||||
@@ -113,25 +144,19 @@ export default function Downloads() {
|
||||
content="pcsx2 downloads,pcsx2 dev builds,pcsx2 dev,pcsx2 nightlies,pcsx2 stable"
|
||||
/>
|
||||
</Head>
|
||||
<main>
|
||||
<Container css={{ mt: "2em" }}>
|
||||
<Grid.Container gap={2}>
|
||||
<Grid xs={12} md={6}>
|
||||
<Grid.Container css={{ display: "inline-block" }}>
|
||||
<Grid xs={12}>
|
||||
<Text
|
||||
h1
|
||||
size={40}
|
||||
css={{
|
||||
textGradient: "180deg, #5099ff 25%, #465eae 100%",
|
||||
}}
|
||||
weight="bold"
|
||||
>
|
||||
<main className="docusaurus-reset">
|
||||
<div className="container mt-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Left Column - Stable Releases */}
|
||||
<div>
|
||||
<div className="inline-block">
|
||||
<div>
|
||||
<h1 className="bg-clip-text text-transparent bg-gradient-to-b from-[#5099ff] to-[#465eae]">
|
||||
Stable Releases
|
||||
</Text>
|
||||
</Grid>
|
||||
{apiErrorMsg === undefined ? null : (
|
||||
<Grid xs={12}>
|
||||
</h1>
|
||||
</div>
|
||||
{apiErrorMsg !== undefined && (
|
||||
<div>
|
||||
<Admonition type="danger" title={apiErrorMsg}>
|
||||
<p>
|
||||
If the issue persists, let us know. In the meantime:
|
||||
@@ -141,8 +166,9 @@ export default function Downloads() {
|
||||
You can download releases directly from{" "}
|
||||
<a
|
||||
href="https://github.com/PCSX2/pcsx2/releases"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
@@ -150,48 +176,51 @@ export default function Downloads() {
|
||||
<li>
|
||||
<a
|
||||
href="https://stats.uptimerobot.com/GAg8AuBByx"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
Check our status page
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Admonition>
|
||||
</Grid>
|
||||
</div>
|
||||
)}
|
||||
<Grid xs={12}>
|
||||
<div>
|
||||
<p>
|
||||
Stable releases are infrequent but well tested compared to
|
||||
the nightly releases
|
||||
the nightly releases.
|
||||
</p>
|
||||
</Grid>
|
||||
<Grid xs={12}>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
If you need help using the emulator,{" "}
|
||||
<a href="/docs/">see the following article.</a>
|
||||
<a href="/docs/" className="text-blue-500 hover:underline">
|
||||
see the following article.
|
||||
</a>
|
||||
</p>
|
||||
</Grid>
|
||||
<Grid xs={12}>
|
||||
</div>
|
||||
<div>
|
||||
<Admonition type="caution">
|
||||
<p>
|
||||
If you are having trouble downloading, try disabling your
|
||||
pop-up blocker (e.g. Poper Blocker) as they are known to
|
||||
cause problems with our downloads links.
|
||||
cause problems with our download links.
|
||||
</p>
|
||||
</Admonition>
|
||||
</Grid>
|
||||
<Grid xs={12} css={{ mt: "2em" }}>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<ReleaseDownloadButton
|
||||
release={latestStableRelease}
|
||||
buttonText={"Latest Stable"}
|
||||
isNightly={false}
|
||||
errorMsg={apiErrorMsg}
|
||||
/>
|
||||
</Grid>
|
||||
<GoogleAd margins="2em"></GoogleAd>
|
||||
<Grid xs={12} css={{ mt: "1em" }}>
|
||||
<Grid.Container alignItems="center" css={{ fontWeight: 700 }}>
|
||||
</div>
|
||||
<GoogleAd margins="2em" />
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center font-bold">
|
||||
<Switch
|
||||
color="primary"
|
||||
checked={showPreviousStables}
|
||||
@@ -204,14 +233,14 @@ export default function Downloads() {
|
||||
}}
|
||||
/>
|
||||
Show Previous Versions
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
{!showPreviousStables ? null : (
|
||||
</div>
|
||||
</div>
|
||||
{showPreviousStables && (
|
||||
<>
|
||||
<Grid xs={12} css={{ mt: "2em" }}>
|
||||
<div className="mt-8">
|
||||
<h2>Previous Stable Releases</h2>
|
||||
</Grid>
|
||||
<Grid xs={12}>
|
||||
</div>
|
||||
<div>
|
||||
<DownloadTable
|
||||
pageSize={pageSize}
|
||||
tableLabel={"Previous stable releases table"}
|
||||
@@ -226,28 +255,23 @@ export default function Downloads() {
|
||||
}}
|
||||
tableType={"stable"}
|
||||
/>
|
||||
</Grid>
|
||||
<GoogleAd margins="2em"></GoogleAd>
|
||||
</div>
|
||||
<GoogleAd margins="2em" />
|
||||
</>
|
||||
)}
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
<Grid xs={12} md={6}>
|
||||
<Grid.Container css={{ display: "inline-block" }}>
|
||||
<Grid xs={12}>
|
||||
<Text
|
||||
h1
|
||||
size={40}
|
||||
css={{
|
||||
textGradient: "180deg, $warning 25%, #777500 100%",
|
||||
}}
|
||||
weight="bold"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Nightly Releases */}
|
||||
<div>
|
||||
<div className="inline-block">
|
||||
<div>
|
||||
<h1 className="bg-clip-text text-transparent bg-gradient-to-b to-[#777500] from-[#f2a40a]">
|
||||
Nightly Releases
|
||||
</Text>
|
||||
</Grid>
|
||||
{apiErrorMsg === undefined ? null : (
|
||||
<Grid xs={12}>
|
||||
</h1>
|
||||
</div>
|
||||
{apiErrorMsg !== undefined && (
|
||||
<div>
|
||||
<Admonition type="danger" title={apiErrorMsg}>
|
||||
<p>
|
||||
If the issue persists, let us know. In the meantime:
|
||||
@@ -257,8 +281,9 @@ export default function Downloads() {
|
||||
You can download releases directly from{" "}
|
||||
<a
|
||||
href="https://github.com/PCSX2/pcsx2/releases"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
@@ -266,34 +291,35 @@ export default function Downloads() {
|
||||
<li>
|
||||
<a
|
||||
href="https://stats.uptimerobot.com/GAg8AuBByx"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
Check our status page
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Admonition>
|
||||
</Grid>
|
||||
</div>
|
||||
)}
|
||||
<Grid xs={12}>
|
||||
<div>
|
||||
<p>
|
||||
There is a new nightly release anytime a change is made, so
|
||||
you are getting the latest and greatest (but sometimes
|
||||
buggy) experience
|
||||
buggy) experience.
|
||||
</p>
|
||||
</Grid>
|
||||
<Grid xs={12} css={{ mt: "2em" }}>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<ReleaseDownloadButton
|
||||
release={latestNightlyRelease}
|
||||
buttonText={"Latest Nightly"}
|
||||
isNightly={true}
|
||||
errorMsg={apiErrorMsg}
|
||||
/>
|
||||
</Grid>
|
||||
<GoogleAd margins="2em"></GoogleAd>
|
||||
<Grid xs={12} css={{ mt: "1em" }}>
|
||||
<Grid.Container alignItems="center" css={{ fontWeight: 700 }}>
|
||||
</div>
|
||||
<GoogleAd margins="2em" />
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center font-bold">
|
||||
<Switch
|
||||
color="warning"
|
||||
checked={showPreviousNightlies}
|
||||
@@ -306,14 +332,14 @@ export default function Downloads() {
|
||||
}}
|
||||
/>
|
||||
Show Previous Versions
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
{!showPreviousNightlies ? null : (
|
||||
</div>
|
||||
</div>
|
||||
{showPreviousNightlies && (
|
||||
<>
|
||||
<Grid xs={12} css={{ mt: "2em" }}>
|
||||
<div className="mt-8">
|
||||
<h2>Previous Nightly Releases</h2>
|
||||
</Grid>
|
||||
<Grid xs={12}>
|
||||
</div>
|
||||
<div>
|
||||
<DownloadTable
|
||||
pageSize={pageSize}
|
||||
tableLabel={"Previous nightly releases table"}
|
||||
@@ -328,14 +354,14 @@ export default function Downloads() {
|
||||
}}
|
||||
tableType={"nightly"}
|
||||
/>
|
||||
</Grid>
|
||||
<GoogleAd margins="2em"></GoogleAd>
|
||||
</div>
|
||||
<GoogleAd margins="2em" />
|
||||
</>
|
||||
)}
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -1,54 +1,20 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Link from "@docusaurus/Link";
|
||||
import Layout from "@theme/Layout";
|
||||
import {
|
||||
Text,
|
||||
Button,
|
||||
Row,
|
||||
Col,
|
||||
Card,
|
||||
Grid,
|
||||
Container,
|
||||
Tooltip,
|
||||
getDocumentTheme,
|
||||
} from "@nextui-org/react";
|
||||
import { Button, Card, CardFooter, Image } from "@nextui-org/react";
|
||||
import { ReleaseDownloadButton } from "../components/ReleaseDownloadButton";
|
||||
import { useTheme } from "next-themes";
|
||||
import { NumberTicker } from "../components/NumberTicker";
|
||||
import { getLatestRelease } from "../components/ReleaseDownloadButton";
|
||||
import { ReleaseDownloadButton } from "../components/ReleaseDownloadButton";
|
||||
import { GoogleAd } from "../components/GoogleAd";
|
||||
import { useMediaQuery } from "../utils/mediaQuery";
|
||||
import { styled } from "@nextui-org/react";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import { useMediaQuery } from "../utils/mediaQuery";
|
||||
|
||||
const StyledTitle = styled("h1", {
|
||||
display: "inline",
|
||||
fontWeight: "$bold",
|
||||
color: "$text",
|
||||
lh: "1.2",
|
||||
fs: "2.5rem",
|
||||
"@sm": {
|
||||
fs: "3rem",
|
||||
},
|
||||
"@lg": {
|
||||
fs: "3.5rem",
|
||||
},
|
||||
});
|
||||
|
||||
const StyledGradientTitle = styled(StyledTitle, {
|
||||
textGradient: "180deg, #5099ff 25%, #465eae 100%",
|
||||
"&::selection": {
|
||||
WebkitTextFillColor: "$colors$text",
|
||||
},
|
||||
});
|
||||
|
||||
const StyledSubtitle = styled("p", {
|
||||
pl: "$1",
|
||||
fs: "$xl",
|
||||
width: "100%",
|
||||
display: "inline-flex",
|
||||
fontWeight: "500",
|
||||
color: "$accents9",
|
||||
});
|
||||
const StyledTitle =
|
||||
"inline font-bold text-[2.5rem] leading-[1.2] text-current sm:text-[3rem] lg:text-[3.5rem]";
|
||||
const StyledGradientTitle = `${StyledTitle} bg-clip-text text-transparent bg-gradient-to-b from-[#5099ff] to-[#465eae]`;
|
||||
const StyledSubtitle =
|
||||
"pl-1 text-xl w-full inline-flex font-medium text-accents-9";
|
||||
|
||||
import CompatData from "@site/static/data/compat/data.min.json";
|
||||
|
||||
@@ -77,13 +43,21 @@ import {
|
||||
previousProgressReport,
|
||||
} from "../data/latestBlogs";
|
||||
|
||||
const baseApiUrl = "https://api.pcsx2.net/v1";
|
||||
import useIsBrowser from "@docusaurus/useIsBrowser";
|
||||
|
||||
let baseApiUrl = "https://api.pcsx2.net/v1";
|
||||
|
||||
export default function Home() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [latestStableRelease, setLatestStableRelease] = useState({});
|
||||
const [latestNightlyRelease, setLatestNightlyRelease] = useState({});
|
||||
const [apiErrorMsg, setApiErrorMsg] = useState(undefined);
|
||||
const [homeVideoPath, setHomeVideoPath] = useState("/videos/splash.webm");
|
||||
const isBrowser = useIsBrowser();
|
||||
|
||||
if (isBrowser && window.location.hostname === "localhost") {
|
||||
baseApiUrl = "https://localhost:8001/v1";
|
||||
}
|
||||
|
||||
const fetchLatestReleases = async () => {
|
||||
try {
|
||||
@@ -113,391 +87,304 @@ export default function Home() {
|
||||
useEffect(() => {
|
||||
fetchLatestReleases();
|
||||
setHomeVideoPath(
|
||||
getDocumentTheme(document?.documentElement) === "dark"
|
||||
? "/videos/splash.webm"
|
||||
: "/videos/splash-light.mp4",
|
||||
theme === "dark" ? "/videos/splash.webm" : "/videos/splash-light.mp4",
|
||||
);
|
||||
|
||||
const observer = new MutationObserver((mutation) => {
|
||||
setHomeVideoPath(
|
||||
getDocumentTheme(document?.documentElement) === "dark"
|
||||
? "/videos/splash.webm"
|
||||
: "/videos/splash-light.mp4",
|
||||
);
|
||||
});
|
||||
|
||||
// Observe the document theme changes
|
||||
observer.observe(document?.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "style", "class"],
|
||||
});
|
||||
}, []);
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
<Layout title={`Home`} description="An Open-Source Playstation 2 Emulator">
|
||||
<main>
|
||||
<main className="docusaurus-reset">
|
||||
<video
|
||||
src={useBaseUrl(homeVideoPath)}
|
||||
autoPlay={true}
|
||||
loop={true}
|
||||
muted={true}
|
||||
className="absolute h-[50vh] w-full object-contain opacity-50"
|
||||
style={{
|
||||
position: "absolute",
|
||||
height: "calc(50vh)",
|
||||
width: "100%",
|
||||
objectFit: "contain",
|
||||
filter: "opacity(50%)",
|
||||
backgroundColor: "var(--home-video-background-color)",
|
||||
}}
|
||||
/>
|
||||
<Grid.Container
|
||||
alignItems="center"
|
||||
justify="center"
|
||||
gap={2}
|
||||
css={{
|
||||
position: "relative",
|
||||
minHeight: "calc(50vh)",
|
||||
zIndex: "$2",
|
||||
"@md": {
|
||||
pl: "5em",
|
||||
pr: "5em",
|
||||
},
|
||||
width: "100%",
|
||||
margin: 0,
|
||||
}}
|
||||
<div
|
||||
className="flex items-center justify-center gap-2 relative min-h-[50vh] z-2 w-full m-0"
|
||||
style={{ paddingLeft: "5em", paddingRight: "5em" }}
|
||||
>
|
||||
<Grid xs={12} md={6} direction="column">
|
||||
<Grid style={{ textAlign: "center" }}>
|
||||
<StyledGradientTitle css={{ mb: 0 }}>
|
||||
<div className="w-full md:w-1/2 flex flex-col">
|
||||
<div className="text-center">
|
||||
<h1
|
||||
className={`${StyledGradientTitle} mb-0`}
|
||||
style={{
|
||||
backgroundImage:
|
||||
"linear-gradient(180deg, #5099ff 25%, #465eae 100%)",
|
||||
"::selection": { WebkitTextFillColor: "var(--tw-text)" },
|
||||
}}
|
||||
>
|
||||
PCSX2
|
||||
</StyledGradientTitle>
|
||||
<StyledTitle css={{ mb: 0 }}>is an open source </StyledTitle>
|
||||
<StyledGradientTitle css={{ mb: 0 }}>
|
||||
</h1>
|
||||
<h1 className={`${StyledTitle} mb-0`}>is an open source </h1>
|
||||
<h1
|
||||
className={`${StyledGradientTitle} mb-0`}
|
||||
style={{
|
||||
backgroundImage:
|
||||
"linear-gradient(180deg, #5099ff 25%, #465eae 100%)",
|
||||
"::selection": { WebkitTextFillColor: "var(--tw-text)" },
|
||||
}}
|
||||
>
|
||||
PS2 Emulator
|
||||
</StyledGradientTitle>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<StyledSubtitle css={{ justifyContent: "center" }}>
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<p className={`${StyledSubtitle} justify-center`}>
|
||||
<span>
|
||||
Supporting
|
||||
<NumberTicker numberFunc={getPlayableGameCount} />
|
||||
Games from the PS2 Library
|
||||
</span>
|
||||
</StyledSubtitle>
|
||||
</Grid>
|
||||
<Grid.Container
|
||||
direction="row"
|
||||
gap={2}
|
||||
alignItems={useMediaQuery(960) ? "center" : "flex-start"}
|
||||
justify="center"
|
||||
>
|
||||
<Grid>
|
||||
<ReleaseDownloadButton
|
||||
release={latestStableRelease}
|
||||
buttonText={"Latest Stable"}
|
||||
isNightly={false}
|
||||
isDisabled={false}
|
||||
errorMsg={apiErrorMsg}
|
||||
placement={useMediaQuery(960) ? "bottom-left" : "left-top"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2 justify-center mt-5">
|
||||
<ReleaseDownloadButton
|
||||
release={latestStableRelease}
|
||||
buttonText="Latest Stable"
|
||||
isNightly={false}
|
||||
isDisabled={false}
|
||||
errorMsg={apiErrorMsg}
|
||||
placement={useMediaQuery(960) ? "bottom-start" : "left-start"}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<ReleaseDownloadButton
|
||||
release={latestNightlyRelease}
|
||||
buttonText={"Latest Nightly"}
|
||||
buttonText="Latest Nightly"
|
||||
isNightly={true}
|
||||
errorMsg={apiErrorMsg}
|
||||
/>
|
||||
<a
|
||||
<Button
|
||||
color="secondary"
|
||||
className="mt-2 border-solid font-medium cursor-pointer hover:text-secondary hover:no-underline"
|
||||
variant="bordered"
|
||||
as={Link}
|
||||
href={useBaseUrl("/downloads")}
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<Button
|
||||
light
|
||||
color="secondary"
|
||||
css={{ minWidth: "200px", fontWeight: 700 }}
|
||||
>
|
||||
Previous Versions
|
||||
</Button>
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Container>
|
||||
<Row justify="center">
|
||||
<Col
|
||||
css={{
|
||||
"@md": {
|
||||
width: "50%",
|
||||
},
|
||||
"@mdMax": {
|
||||
width: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GoogleAd></GoogleAd>
|
||||
</Col>
|
||||
</Row>
|
||||
<Grid.Container
|
||||
gap={2}
|
||||
css={{
|
||||
"@md": {
|
||||
pl: "5em",
|
||||
pr: "5em",
|
||||
},
|
||||
"@mdMax": {
|
||||
pl: "2em",
|
||||
pr: "2em",
|
||||
},
|
||||
width: "100%",
|
||||
margin: 0,
|
||||
mt: "5em",
|
||||
}}
|
||||
>
|
||||
<Grid xs={12} direction="column">
|
||||
<StyledTitle css={{ mb: 0 }}>Recent Blog Posts</StyledTitle>
|
||||
<StyledSubtitle>
|
||||
Previous Versions
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full container mx-auto">
|
||||
{/* Google Ad Section */}
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full md:w-1/2">
|
||||
<GoogleAd />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Blog Posts Section */}
|
||||
<div className="w-full mt-20 px-8 md:px-20">
|
||||
<div className="flex flex-col">
|
||||
<h1 className={`${StyledTitle} mb-0`}>Recent Blog Posts</h1>
|
||||
<p className={`${StyledSubtitle}`}>
|
||||
Articles that go more in-depth on how things work, how they were
|
||||
fixed, or sometimes why they don't
|
||||
</StyledSubtitle>
|
||||
</Grid>
|
||||
<Grid.Container gap={2}>
|
||||
<Grid xs={12} md={6} justify="center">
|
||||
fixed, or sometimes why they don't.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-8">
|
||||
{/* Latest Blog */}
|
||||
<div className="flex justify-center">
|
||||
<a href={useBaseUrl(latestBlog.url)}>
|
||||
<Card css={{ background: "var(--card-color-background)" }}>
|
||||
<Card.Header
|
||||
css={{ position: "absolute", zIndex: 1, top: 5 }}
|
||||
>
|
||||
<Col>
|
||||
<Text
|
||||
size={12}
|
||||
weight="bold"
|
||||
transform="uppercase"
|
||||
color="#ffffffAA"
|
||||
>
|
||||
Latest Blog
|
||||
</Text>
|
||||
<Text h4 color="white">
|
||||
{latestBlog.title}
|
||||
</Text>
|
||||
</Col>
|
||||
</Card.Header>
|
||||
<Card.Image
|
||||
<Card
|
||||
radius={"md"}
|
||||
isFooterBlurred
|
||||
className="w-full h-[300px] col-span-12 sm:col-span-7"
|
||||
>
|
||||
<Image
|
||||
removeWrapper
|
||||
className="z-0 w-full h-full object-contain"
|
||||
src={latestBlog.img}
|
||||
objectFit="contain"
|
||||
width="100%"
|
||||
alt="Card image background"
|
||||
height={"300px"}
|
||||
alt="Latest blog image"
|
||||
/>
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-10 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex flex-grow gap-2 items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-base uppercase font-bold mb-0 text-white">
|
||||
Latest Blog
|
||||
</h2>
|
||||
<p className="text-sm text-white/70">
|
||||
{latestBlog.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</a>
|
||||
</Grid>
|
||||
<Grid xs={12} md={6} justify="center">
|
||||
</div>
|
||||
|
||||
{/* Previous Blog */}
|
||||
<div className="flex justify-center">
|
||||
<a href={useBaseUrl(previousBlog.url)}>
|
||||
<Card css={{ background: "var(--card-color-background)" }}>
|
||||
<Card.Header
|
||||
css={{ position: "absolute", zIndex: 1, top: 5 }}
|
||||
>
|
||||
<Col>
|
||||
<Text
|
||||
size={12}
|
||||
weight="bold"
|
||||
transform="uppercase"
|
||||
color="#ffffffAA"
|
||||
>
|
||||
Previous Blog
|
||||
</Text>
|
||||
<Text h4 color="white">
|
||||
{previousBlog.title}
|
||||
</Text>
|
||||
</Col>
|
||||
</Card.Header>
|
||||
<Card.Image
|
||||
<Card
|
||||
radius={"md"}
|
||||
isFooterBlurred
|
||||
className="w-full h-[300px] col-span-12 sm:col-span-7"
|
||||
style={{ all: "revert-layer" }}
|
||||
>
|
||||
<Image
|
||||
removeWrapper
|
||||
className="z-0 w-full h-full object-contain"
|
||||
src={previousBlog.img}
|
||||
objectFit="contain"
|
||||
width="100%"
|
||||
alt="Card image background"
|
||||
height={"300px"}
|
||||
alt="Previous blog image"
|
||||
/>
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-10 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex flex-grow gap-2 items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-base uppercase font-bold mb-0 text-white">
|
||||
Previous Blog
|
||||
</h2>
|
||||
<p className="text-sm text-white/70">
|
||||
{previousBlog.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid.Container>
|
||||
<Grid.Container
|
||||
gap={2}
|
||||
css={{
|
||||
"@md": {
|
||||
pl: "5em",
|
||||
pr: "5em",
|
||||
},
|
||||
"@mdMax": {
|
||||
pl: "2em",
|
||||
pr: "2em",
|
||||
},
|
||||
width: "100%",
|
||||
margin: 0,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Grid xs={12} direction="column">
|
||||
<StyledTitle css={{ mb: 0 }}>Recent Progress Reports</StyledTitle>
|
||||
<StyledSubtitle>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Progress Reports Section */}
|
||||
<div className="w-full px-8 md:px-20 mt-8 relative">
|
||||
<div className="flex flex-col">
|
||||
<h1 className={`${StyledTitle} mb-0`}>Recent Progress Reports</h1>
|
||||
<p className={`${StyledSubtitle}`}>
|
||||
Stay up to date on the latest improvements and fixes on the
|
||||
project
|
||||
</StyledSubtitle>
|
||||
</Grid>
|
||||
<Grid.Container gap={2}>
|
||||
<Grid xs={12} md={6} justify="center">
|
||||
project.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-8">
|
||||
{/* Latest Progress Report */}
|
||||
<div className="flex justify-center">
|
||||
<a href={useBaseUrl(latestProgressReport.url)}>
|
||||
<Card css={{ background: "var(--card-color-background)" }}>
|
||||
<Card.Header
|
||||
css={{ position: "absolute", zIndex: 1, top: 5 }}
|
||||
>
|
||||
<Col>
|
||||
<Text
|
||||
size={12}
|
||||
weight="bold"
|
||||
transform="uppercase"
|
||||
color="#ffffffAA"
|
||||
>
|
||||
Latest Progress Report
|
||||
</Text>
|
||||
<Text h4 color="white">
|
||||
{latestProgressReport.title}
|
||||
</Text>
|
||||
</Col>
|
||||
</Card.Header>
|
||||
<Card.Image
|
||||
<Card
|
||||
radius={"md"}
|
||||
isFooterBlurred
|
||||
className="w-full h-[300px] col-span-12 sm:col-span-7"
|
||||
style={{ all: "revert-layer" }}
|
||||
>
|
||||
<Image
|
||||
removeWrapper
|
||||
className="z-0 w-full h-full object-contain"
|
||||
src={latestProgressReport.img}
|
||||
objectFit="contain"
|
||||
width="100%"
|
||||
alt="Card image background"
|
||||
height={"300px"}
|
||||
alt="Latest progress report image"
|
||||
/>
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-10 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex flex-grow gap-2 items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-base uppercase font-bold mb-0 text-white">
|
||||
Latest Progress Report
|
||||
</h2>
|
||||
<p className="text-sm text-white/70">
|
||||
{latestProgressReport.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</a>
|
||||
</Grid>
|
||||
<Grid xs={12} md={6} justify="center">
|
||||
</div>
|
||||
|
||||
{/* Previous Progress Report */}
|
||||
<div className="flex justify-center">
|
||||
<a href={useBaseUrl(previousProgressReport.url)}>
|
||||
<Card css={{ background: "var(--card-color-background)" }}>
|
||||
<Card.Header
|
||||
css={{ position: "absolute", zIndex: 1, top: 5 }}
|
||||
>
|
||||
<Col>
|
||||
<Text
|
||||
size={12}
|
||||
weight="bold"
|
||||
transform="uppercase"
|
||||
color="#ffffffAA"
|
||||
>
|
||||
Previous Progress Report
|
||||
</Text>
|
||||
<Text h4 color="white">
|
||||
{previousProgressReport.title}
|
||||
</Text>
|
||||
</Col>
|
||||
</Card.Header>
|
||||
<Card.Image
|
||||
<Card
|
||||
radius={"md"}
|
||||
isFooterBlurred
|
||||
className="w-full h-[300px] col-span-12 sm:col-span-7"
|
||||
style={{ all: "revert-layer" }}
|
||||
>
|
||||
<Image
|
||||
removeWrapper
|
||||
className="z-0 w-full h-full object-contain"
|
||||
src={previousProgressReport.img}
|
||||
objectFit="contain"
|
||||
width="100%"
|
||||
alt="Card image background"
|
||||
height={"300px"}
|
||||
alt="Previous progress report image"
|
||||
/>
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-10 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex flex-grow gap-2 items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-base uppercase font-bold mb-0 text-white">
|
||||
Previous Progress Report
|
||||
</h2>
|
||||
<p className="text-sm text-white/70">
|
||||
{previousProgressReport.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid.Container>
|
||||
<Grid.Container
|
||||
gap={2}
|
||||
css={{
|
||||
"@md": {
|
||||
pl: "5em",
|
||||
pr: "5em",
|
||||
},
|
||||
"@mdMax": {
|
||||
pl: "2em",
|
||||
pr: "2em",
|
||||
},
|
||||
width: "100%",
|
||||
margin: 0,
|
||||
mt: "5em",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Grid xs={12} direction="column">
|
||||
<StyledTitle css={{ mb: 0 }}>About the Project</StyledTitle>
|
||||
<StyledSubtitle>
|
||||
Being almost as old as the console it is emulating, PCSX2 not
|
||||
only has a lot of history behind it, but a continually evolving
|
||||
future.
|
||||
</StyledSubtitle>
|
||||
</Grid>
|
||||
<Grid.Container gap={2}>
|
||||
<Grid md={4}>
|
||||
<span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* About the Project Section */}
|
||||
<div className="w-full px-8 md:px-20 mt-20 relative">
|
||||
<div className="flex flex-col">
|
||||
<h1 className={`${StyledTitle} mb-0`}>About the Project</h1>
|
||||
<p className={`${StyledSubtitle}`}>
|
||||
PCSX2 has a lot of history and an evolving future.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-8">
|
||||
<div>
|
||||
<p>
|
||||
PCSX2 is a free and open-source PlayStation 2 (PS2) emulator.
|
||||
Its purpose is to emulate the PS2's hardware, using a
|
||||
combination of MIPS CPU Interpreters, Recompilers and a
|
||||
Virtual Machine which manages hardware states and PS2 system
|
||||
combination of MIPS CPU Interpreters, Recompilers, and a
|
||||
Virtual Machine that manages hardware states and system
|
||||
memory.
|
||||
</span>
|
||||
</Grid>
|
||||
<Grid md={4}>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
The project has been running for almost 20 years. Past
|
||||
versions could only run a few public domain game demos, but
|
||||
newer versions can run most games at full speed, including
|
||||
popular titles such as Final Fantasy X and Devil May Cry 3.
|
||||
versions could only run a few game demos, but newer versions
|
||||
can run most games at full speed, including titles like Final
|
||||
Fantasy X and Devil May Cry 3.
|
||||
</p>
|
||||
</Grid>
|
||||
<Grid md={4}>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
A significant majority of the official PS2 library is
|
||||
considered playable or perfect, with the remainder at least
|
||||
making it to the menus. For more information on compatibility,
|
||||
see <Link to="/compat">here</Link>.
|
||||
A significant majority of the PS2 library is considered
|
||||
playable. For more info on compatibility, see{" "}
|
||||
<Link to="/compat">here</Link>.
|
||||
</p>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
<Grid.Container gap={2}>
|
||||
<Grid xs={12}>
|
||||
<p>
|
||||
PCSX2 allows you to play PS2 games on your PC, with many
|
||||
additional features and benefits. A few of those benefits
|
||||
include:
|
||||
<ul>
|
||||
<li>custom resolutions and upscaling</li>
|
||||
<li>virtual and sharable memory cards</li>
|
||||
<li>save-states</li>
|
||||
<li>patching system</li>
|
||||
<li>
|
||||
internal recorder to achieve lossless quality at full
|
||||
speed
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
</Grid.Container>
|
||||
{/* TODO - this page can be made more interesting once Qt comes out (showcase notable features with some visuals) */}
|
||||
<Row justify="center">
|
||||
<Col
|
||||
css={{
|
||||
"@md": {
|
||||
width: "50%",
|
||||
},
|
||||
"@mdMax": {
|
||||
width: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GoogleAd></GoogleAd>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<p>
|
||||
PCSX2 allows you to play PS2 games on your PC with added
|
||||
features like:
|
||||
<ul className="list-disc ml-5">
|
||||
<li>Custom resolutions and upscaling</li>
|
||||
<li>Virtual and sharable memory cards</li>
|
||||
<li>Save-states</li>
|
||||
<li>Patching system</li>
|
||||
<li>Internal recorder for lossless quality at full speed</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Google Ad Section */}
|
||||
<div className="flex justify-center mt-20">
|
||||
<div className="w-full md:w-1/2">
|
||||
<GoogleAd />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import BlogPostItemHeaderTitle from "@theme-original/BlogPostItem/Header/Title";
|
||||
import BlogPostItemHeaderInfo from "@theme-original/BlogPostItem/Header/Info";
|
||||
import BlogPostItemHeaderAuthors from "@theme-original/BlogPostItem/Header/Authors";
|
||||
import { useBlogPost } from "@docusaurus/theme-common/internal";
|
||||
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
|
||||
export default function BlogPostItemHeader() {
|
||||
|
||||
@@ -3,6 +3,7 @@ import NavbarNavLink from "@theme-original/NavbarItem/NavbarNavLink";
|
||||
import { Button } from "@nextui-org/react";
|
||||
import { GoHeart } from "react-icons/go";
|
||||
import Link from "@docusaurus/Link";
|
||||
import { useMediaQuery } from "../../utils/mediaQuery";
|
||||
|
||||
export default function NavbarNavLinkWrapper(props) {
|
||||
if (props.label === "Donate") {
|
||||
@@ -11,45 +12,27 @@ export default function NavbarNavLinkWrapper(props) {
|
||||
return (
|
||||
<>
|
||||
<Link {...props}>
|
||||
<GoHeart fill="var(--nextui-colors-red600)" size={20} />
|
||||
<GoHeart fill="#C20E4D" size={20} className="mr-1" />
|
||||
Donate
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
} else if (!useMediaQuery(960)) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
as="a"
|
||||
className="dark light cursor-pointer hover:no-underline dark:hover:text-red-200 light:hover:text-red-600 gap-1 font-medium border-none text-red-400 dark:bg-[#090a11] light:bg-[#ebedf0]"
|
||||
href={props.to}
|
||||
startContent={<GoHeart fill="#C20E4D" size={20} />}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Donate
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
auto
|
||||
as="a"
|
||||
css={{
|
||||
bg: "$gray50",
|
||||
color: "$text",
|
||||
maxH: "38px",
|
||||
px: "$8",
|
||||
"@mdMax": {
|
||||
d: "none",
|
||||
},
|
||||
"& .nextui-button-icon": {
|
||||
mr: "$2",
|
||||
},
|
||||
"& .nextui-button-icon svg": {
|
||||
transition: "$default",
|
||||
},
|
||||
"&:hover": {
|
||||
color: "#59c5ff",
|
||||
textDecoration: "none",
|
||||
},
|
||||
}}
|
||||
href={props.to}
|
||||
icon={<GoHeart fill="var(--nextui-colors-red600)" size={20} />}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Donate
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
createTheme,
|
||||
NextUIProvider,
|
||||
getDocumentTheme,
|
||||
} from "@nextui-org/react";
|
||||
import React, { useEffect } from "react";
|
||||
import { NextUIProvider } from "@nextui-org/react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import CookieConsent, { getCookieConsentValue } from "react-cookie-consent";
|
||||
|
||||
const lightTheme = createTheme({
|
||||
type: "light",
|
||||
theme: {
|
||||
// colors: {...}, // optional
|
||||
},
|
||||
});
|
||||
|
||||
const darkTheme = createTheme({
|
||||
type: "dark",
|
||||
theme: {
|
||||
// colors: {...}, // optional
|
||||
},
|
||||
});
|
||||
|
||||
function loadGoogleAds() {
|
||||
if (
|
||||
getCookieConsentValue("pcsx2CookieConsent") === "true" &&
|
||||
@@ -37,24 +20,18 @@ function loadGoogleAds() {
|
||||
|
||||
// Default implementation, that you can customize
|
||||
export default function Root({ children }) {
|
||||
const [isDark, setIsDark] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// App mounted, make the page visible!
|
||||
document?.documentElement?.classList.add("app-loaded");
|
||||
// you can use any storage
|
||||
let theme = window.localStorage.getItem("theme");
|
||||
setIsDark(theme === "dark");
|
||||
|
||||
const observer = new MutationObserver((mutation) => {
|
||||
let newTheme = getDocumentTheme(document?.documentElement);
|
||||
let newTheme = theme;
|
||||
if (newTheme === "dark") {
|
||||
if (!document?.documentElement.classList.contains("dark-theme")) {
|
||||
document?.documentElement.classList.add("dark-theme");
|
||||
}
|
||||
setIsDark(true);
|
||||
} else {
|
||||
setIsDark(false);
|
||||
}
|
||||
// Ensure the page is visible if the class list has changed
|
||||
if (!document?.documentElement.classList.contains("app-loaded")) {
|
||||
@@ -74,25 +51,24 @@ export default function Root({ children }) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<NextUIProvider
|
||||
theme={isDark ? darkTheme : lightTheme}
|
||||
disableBaseline={true}
|
||||
>
|
||||
<CookieConsent
|
||||
location="bottom"
|
||||
buttonText="Agree"
|
||||
declineButtonText="Decline"
|
||||
cookieName="pcsx2CookieConsent"
|
||||
enableDeclineButton={true}
|
||||
style={{ background: "#2B373B" }}
|
||||
expires={150}
|
||||
onAccept={() => {
|
||||
loadGoogleAds();
|
||||
}}
|
||||
>
|
||||
This website uses cookies to enhance the user experience.
|
||||
</CookieConsent>
|
||||
{children}
|
||||
<NextUIProvider>
|
||||
<NextThemesProvider attribute="class" defaultTheme="dark">
|
||||
<CookieConsent
|
||||
location="bottom"
|
||||
buttonText="Agree"
|
||||
declineButtonText="Decline"
|
||||
cookieName="pcsx2CookieConsent"
|
||||
enableDeclineButton={true}
|
||||
style={{ background: "#2B373B" }}
|
||||
expires={150}
|
||||
onAccept={() => {
|
||||
loadGoogleAds();
|
||||
}}
|
||||
>
|
||||
This website uses cookies to enhance the user experience.
|
||||
</CookieConsent>
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
67
tailwind.config.js
Normal file
67
tailwind.config.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const { nextui } = require("@nextui-org/react");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
container: false,
|
||||
},
|
||||
content: [
|
||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
// next-ui
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
darkMode: ["class", '[data-theme="dark"]'],
|
||||
plugins: [
|
||||
nextui({
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
background: "#FFFFFF",
|
||||
foreground: "#11181C",
|
||||
primary: {
|
||||
//... 50 to 900
|
||||
foreground: "#FFFFFF",
|
||||
DEFAULT: "#006FEE",
|
||||
},
|
||||
// ... rest of the colors
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
colors: {
|
||||
background: "#010409",
|
||||
foreground: "#ECEDEE",
|
||||
primary: {
|
||||
//... 50 to 900
|
||||
foreground: "#FFFFFF",
|
||||
DEFAULT: "#006FEE",
|
||||
},
|
||||
},
|
||||
// ... rest of the colors
|
||||
},
|
||||
},
|
||||
}),
|
||||
function ({ addComponents }) {
|
||||
addComponents({
|
||||
".container": {
|
||||
maxWidth: "100%",
|
||||
"@screen sm": {
|
||||
maxWidth: "640px",
|
||||
},
|
||||
"@screen md": {
|
||||
maxWidth: "768px",
|
||||
},
|
||||
"@screen lg": {
|
||||
maxWidth: "1280px",
|
||||
},
|
||||
"@screen xl": {
|
||||
maxWidth: "1600px",
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
],
|
||||
};
|
||||
3
util/mock-api/.gitignore
vendored
Normal file
3
util/mock-api/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
yarn.lock
|
||||
*.pem
|
||||
3
util/mock-api/README.md
Normal file
3
util/mock-api/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Probably will eventually move this to the API repo
|
||||
|
||||
But this just stands up a simple copy of the API with some fixture data to make local development easy
|
||||
2742
util/mock-api/fixtures/latest.json
Normal file
2742
util/mock-api/fixtures/latest.json
Normal file
File diff suppressed because it is too large
Load Diff
5505
util/mock-api/fixtures/nightlyReleases.json
Normal file
5505
util/mock-api/fixtures/nightlyReleases.json
Normal file
File diff suppressed because it is too large
Load Diff
1020
util/mock-api/fixtures/stableReleases.json
Normal file
1020
util/mock-api/fixtures/stableReleases.json
Normal file
File diff suppressed because it is too large
Load Diff
10
util/mock-api/package.json
Normal file
10
util/mock-api/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "mock-api",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.1"
|
||||
}
|
||||
}
|
||||
43
util/mock-api/server.js
Normal file
43
util/mock-api/server.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import express from "express";
|
||||
import https from "https";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import cors from "cors";
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
const port = 8001;
|
||||
|
||||
import stableReleases from "./fixtures/stableReleases.json" with { type: "json" };
|
||||
import nightlyReleases from "./fixtures/nightlyReleases.json" with { type: "json" };
|
||||
import latest from "./fixtures/latest.json" with { type: "json" };
|
||||
|
||||
app.get("/v1/stableReleases", (req, res) => {
|
||||
const { offset = 0 } = req.query;
|
||||
const offsetInt = parseInt(offset, 10);
|
||||
let allData = JSON.parse(JSON.stringify(stableReleases));
|
||||
allData.data = allData.data.slice(offsetInt, offsetInt + 10);
|
||||
res.json(allData);
|
||||
});
|
||||
|
||||
app.get("/v1/nightlyReleases", (req, res) => {
|
||||
const { offset = 0 } = req.query;
|
||||
const offsetInt = parseInt(offset, 10);
|
||||
let allData = JSON.parse(JSON.stringify(nightlyReleases));
|
||||
allData.data = allData.data.slice(offsetInt, offsetInt + 10);
|
||||
res.json(allData);
|
||||
});
|
||||
|
||||
app.get("/v1/latestReleasesAndPullRequests", (req, res) => {
|
||||
res.json(latest);
|
||||
});
|
||||
|
||||
const sslOptions = {
|
||||
key: fs.readFileSync(path.resolve("localhost-key.pem"), "utf8"),
|
||||
cert: fs.readFileSync(path.resolve("localhost.pem"), "utf8"),
|
||||
};
|
||||
|
||||
// Start the server
|
||||
https.createServer(sslOptions, app).listen(port, () => {
|
||||
console.log(`Server is running on https://localhost:${port}`);
|
||||
});
|
||||
Reference in New Issue
Block a user