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:
Tyler Wilding
2024-11-15 20:32:20 -05:00
committed by GitHub
parent 798a9b4b5e
commit 3030d7933b
31 changed files with 13182 additions and 1919 deletions

View File

@@ -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

View File

@@ -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,
],
};

View File

@@ -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
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View 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>
);
}

View File

@@ -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>
&nbsp;
{pullRequest.additions}
</span>
<span style={{ marginLeft: "0.5em", color: "#dd4a48" }}>
<IconContext.Provider
value={{ style: { verticalAlign: "middle" } }}
>
<GoDash size={24}></GoDash>
</IconContext.Provider>
&nbsp;
{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>
);
}

View File

@@ -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>
);
}

View File

@@ -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} />
)}
&nbsp;
{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} />}
&nbsp;
{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>
);
}

View File

@@ -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;
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -1,4 +0,0 @@
.pr-link {
margin-bottom: 1em !important;
font-weight: 700;
}

View File

@@ -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>
);
}

View File

@@ -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;
}

View File

@@ -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>
);

View File

@@ -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() {
}}
/>
&nbsp;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() {
}}
/>
&nbsp;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>
);

View File

@@ -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&nbsp;
</StyledGradientTitle>
<StyledTitle css={{ mb: 0 }}>is an open source&nbsp;</StyledTitle>
<StyledGradientTitle css={{ mb: 0 }}>
</h1>
<h1 className={`${StyledTitle} mb-0`}>is an open source&nbsp;</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&nbsp;
<NumberTicker numberFunc={getPlayableGameCount} />
&nbsp;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>
);

View File

@@ -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() {

View File

@@ -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 (
<>

View File

@@ -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
View 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
View File

@@ -0,0 +1,3 @@
node_modules/
yarn.lock
*.pem

3
util/mock-api/README.md Normal file
View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
View 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}`);
});

3319
yarn.lock

File diff suppressed because it is too large Load Diff