diff --git a/apps/entropy-explorer/.gitignore b/apps/entropy-explorer/.gitignore new file mode 100644 index 0000000000..9d2ee2a739 --- /dev/null +++ b/apps/entropy-explorer/.gitignore @@ -0,0 +1 @@ +.env*.local diff --git a/apps/entropy-explorer/.prettierignore b/apps/entropy-explorer/.prettierignore new file mode 100644 index 0000000000..5f66a649b5 --- /dev/null +++ b/apps/entropy-explorer/.prettierignore @@ -0,0 +1,7 @@ +.next/ +coverage/ +node_modules/ +*.tsbuildinfo +.env*.local +.env +.DS_Store diff --git a/apps/entropy-explorer/eslint.config.js b/apps/entropy-explorer/eslint.config.js new file mode 100644 index 0000000000..7035c57cb4 --- /dev/null +++ b/apps/entropy-explorer/eslint.config.js @@ -0,0 +1 @@ +export { nextjs as default } from "@cprussin/eslint-config"; diff --git a/apps/entropy-explorer/jest.config.js b/apps/entropy-explorer/jest.config.js new file mode 100644 index 0000000000..0bac66ed0e --- /dev/null +++ b/apps/entropy-explorer/jest.config.js @@ -0,0 +1 @@ +export { nextjs as default } from "@cprussin/jest-config/next"; diff --git a/apps/entropy-explorer/next-env.d.ts b/apps/entropy-explorer/next-env.d.ts new file mode 100644 index 0000000000..1b3be0840f --- /dev/null +++ b/apps/entropy-explorer/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/entropy-explorer/next.config.js b/apps/entropy-explorer/next.config.js new file mode 100644 index 0000000000..58ecfdcf9e --- /dev/null +++ b/apps/entropy-explorer/next.config.js @@ -0,0 +1,54 @@ +const config = { + reactStrictMode: true, + + pageExtensions: ["ts", "tsx", "mdx"], + + logging: { + fetches: { + fullUrl: true, + }, + }, + + webpack(config) { + config.module.rules.push({ + test: /\.svg$/i, + use: ["@svgr/webpack"], + }); + + config.resolve.extensionAlias = { + ".js": [".js", ".ts", ".tsx"], + }; + + return config; + }, + + headers: async () => [ + { + source: "/:path*", + headers: [ + { + key: "X-XSS-Protection", + value: "1; mode=block", + }, + { + key: "Referrer-Policy", + value: "strict-origin-when-cross-origin", + }, + { + key: "Strict-Transport-Security", + value: "max-age=2592000", + }, + { + key: "X-Content-Type-Options", + value: "nosniff", + }, + { + key: "Permissions-Policy", + value: + "vibrate=(), geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), speaker=(), vibrate=(), fullscreen=self", + }, + ], + }, + ], +}; +export default config; diff --git a/apps/entropy-explorer/package.json b/apps/entropy-explorer/package.json new file mode 100644 index 0000000000..f96d2064eb --- /dev/null +++ b/apps/entropy-explorer/package.json @@ -0,0 +1,57 @@ +{ + "name": "@pythnetwork/entropy-explorer", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": "22" + }, + "scripts": { + "build:vercel": "next build", + "fix:format": "prettier --write .", + "fix:lint:eslint": "eslint --fix .", + "fix:lint:stylelint": "stylelint --fix 'src/**/*.scss'", + "pull:env": "[ $CI ] || VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_TBkf9EyQjQF37gs4Vk0sQKJj97kE vercel env pull", + "start:dev": "next dev --port 3006", + "start:prod": "next start --port 3006", + "test:format": "prettier --check .", + "test:lint:eslint": "eslint . --max-warnings 0", + "test:lint:stylelint": "stylelint 'src/**/*.scss' --max-warnings 0", + "test:types": "tsc" + }, + "dependencies": { + "@phosphor-icons/react": "catalog:", + "@pythnetwork/component-library": "workspace:*", + "clsx": "catalog:", + "connectkit": "catalog:", + "next": "catalog:", + "nuqs": "catalog:", + "react": "catalog:", + "react-aria": "catalog:", + "react-dom": "catalog:", + "viem": "catalog:", + "wagmi": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@cprussin/eslint-config": "catalog:", + "@cprussin/jest-config": "catalog:", + "@cprussin/prettier-config": "catalog:", + "@cprussin/tsconfig": "catalog:", + "@svgr/webpack": "catalog:", + "@types/jest": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "autoprefixer": "catalog:", + "eslint": "catalog:", + "jest": "catalog:", + "postcss": "catalog:", + "prettier": "catalog:", + "sass": "catalog:", + "stylelint": "catalog:", + "stylelint-config-standard-scss": "catalog:", + "typescript": "catalog:", + "vercel": "catalog:" + } +} diff --git a/apps/entropy-explorer/prettier.config.js b/apps/entropy-explorer/prettier.config.js new file mode 100644 index 0000000000..1e43aeeddb --- /dev/null +++ b/apps/entropy-explorer/prettier.config.js @@ -0,0 +1 @@ +export { base as default } from "@cprussin/prettier-config"; diff --git a/apps/entropy-explorer/public/android-chrome-192x192.png b/apps/entropy-explorer/public/android-chrome-192x192.png new file mode 100644 index 0000000000..596667cefb Binary files /dev/null and b/apps/entropy-explorer/public/android-chrome-192x192.png differ diff --git a/apps/entropy-explorer/public/android-chrome-512x512.png b/apps/entropy-explorer/public/android-chrome-512x512.png new file mode 100644 index 0000000000..cf0ba050d8 Binary files /dev/null and b/apps/entropy-explorer/public/android-chrome-512x512.png differ diff --git a/apps/entropy-explorer/public/apple-touch-icon.png b/apps/entropy-explorer/public/apple-touch-icon.png new file mode 100644 index 0000000000..1f9a443c8a Binary files /dev/null and b/apps/entropy-explorer/public/apple-touch-icon.png differ diff --git a/apps/entropy-explorer/public/favicon-16x16.png b/apps/entropy-explorer/public/favicon-16x16.png new file mode 100644 index 0000000000..85b3a417a7 Binary files /dev/null and b/apps/entropy-explorer/public/favicon-16x16.png differ diff --git a/apps/entropy-explorer/public/favicon-32x32.png b/apps/entropy-explorer/public/favicon-32x32.png new file mode 100644 index 0000000000..034afe45c1 Binary files /dev/null and b/apps/entropy-explorer/public/favicon-32x32.png differ diff --git a/apps/entropy-explorer/public/favicon-light.ico b/apps/entropy-explorer/public/favicon-light.ico new file mode 100644 index 0000000000..9c0d28fbda Binary files /dev/null and b/apps/entropy-explorer/public/favicon-light.ico differ diff --git a/apps/entropy-explorer/public/favicon.ico b/apps/entropy-explorer/public/favicon.ico new file mode 100644 index 0000000000..56072a4860 Binary files /dev/null and b/apps/entropy-explorer/public/favicon.ico differ diff --git a/apps/entropy-explorer/src/app/error.ts b/apps/entropy-explorer/src/app/error.ts new file mode 100644 index 0000000000..4129c5ca5c --- /dev/null +++ b/apps/entropy-explorer/src/app/error.ts @@ -0,0 +1,3 @@ +"use client"; + +export { ErrorPage as default } from "@pythnetwork/component-library/ErrorPage"; diff --git a/apps/entropy-explorer/src/app/global-error.tsx b/apps/entropy-explorer/src/app/global-error.tsx new file mode 100644 index 0000000000..8479e0d073 --- /dev/null +++ b/apps/entropy-explorer/src/app/global-error.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { ErrorPage } from "@pythnetwork/component-library/ErrorPage"; +import { LoggerProvider } from "@pythnetwork/component-library/useLogger"; +import type { ComponentProps } from "react"; + +const GlobalError = (props: ComponentProps) => ( + + + + + + + +); +export default GlobalError; diff --git a/apps/entropy-explorer/src/app/layout.ts b/apps/entropy-explorer/src/app/layout.ts new file mode 100644 index 0000000000..47f8c23fd1 --- /dev/null +++ b/apps/entropy-explorer/src/app/layout.ts @@ -0,0 +1,2 @@ +export { Root as default } from "../components/Root"; +export { metadata, viewport } from "../metadata"; diff --git a/apps/entropy-explorer/src/app/manifest.ts b/apps/entropy-explorer/src/app/manifest.ts new file mode 100644 index 0000000000..fa9e6f5ab9 --- /dev/null +++ b/apps/entropy-explorer/src/app/manifest.ts @@ -0,0 +1,25 @@ +import type { MetadataRoute } from "next"; + +import { metadata, viewport } from "../metadata"; + +const manifest = (): MetadataRoute.Manifest => ({ + name: metadata.applicationName, + short_name: metadata.applicationName, + description: metadata.description, + theme_color: viewport.themeColor, + background_color: viewport.themeColor, + icons: [ + { + src: "/android-chrome-192x192.png", + sizes: "192x192", + type: "image/png", + }, + { + src: "/android-chrome-512x512.png", + sizes: "512x512", + type: "image/png", + }, + ], + display: "standalone", +}); +export default manifest; diff --git a/apps/entropy-explorer/src/app/not-found.ts b/apps/entropy-explorer/src/app/not-found.ts new file mode 100644 index 0000000000..3053e90df1 --- /dev/null +++ b/apps/entropy-explorer/src/app/not-found.ts @@ -0,0 +1 @@ +export { NotFoundPage as default } from "@pythnetwork/component-library/NotFoundPage"; diff --git a/apps/entropy-explorer/src/app/page.ts b/apps/entropy-explorer/src/app/page.ts new file mode 100644 index 0000000000..b84f5687c5 --- /dev/null +++ b/apps/entropy-explorer/src/app/page.ts @@ -0,0 +1 @@ +export { Home as default } from "../components/Home"; diff --git a/apps/entropy-explorer/src/app/robots.ts b/apps/entropy-explorer/src/app/robots.ts new file mode 100644 index 0000000000..28eaa31c59 --- /dev/null +++ b/apps/entropy-explorer/src/app/robots.ts @@ -0,0 +1,11 @@ +import type { MetadataRoute } from "next"; + +import { IS_PRODUCTION_SERVER } from "../config/server"; + +const robots = (): MetadataRoute.Robots => ({ + rules: { + userAgent: "*", + ...(IS_PRODUCTION_SERVER ? { allow: "/" } : { disallow: "/" }), + }, +}); +export default robots; diff --git a/apps/entropy-explorer/src/components/Home/chain-select.module.scss b/apps/entropy-explorer/src/components/Home/chain-select.module.scss new file mode 100644 index 0000000000..a51fda260c --- /dev/null +++ b/apps/entropy-explorer/src/components/Home/chain-select.module.scss @@ -0,0 +1,15 @@ +@use "@pythnetwork/component-library/theme"; + +.searchBar { + display: grid; + grid-template-columns: max-content 1fr; + gap: theme.spacing(2); + width: 100%; +} + +.chainSelectItem { + display: grid; + grid-template-columns: max-content 1fr; + gap: theme.spacing(2); + align-items: center; +} diff --git a/apps/entropy-explorer/src/components/Home/chain-select.tsx b/apps/entropy-explorer/src/components/Home/chain-select.tsx new file mode 100644 index 0000000000..4ac63d4f98 --- /dev/null +++ b/apps/entropy-explorer/src/components/Home/chain-select.tsx @@ -0,0 +1,107 @@ +"use client"; + +import type { Props as SelectProps } from "@pythnetwork/component-library/Select"; +import { Select } from "@pythnetwork/component-library/Select"; +import { ChainIcon } from "connectkit"; +import type { ComponentProps } from "react"; +import { Suspense, useCallback, useMemo } from "react"; +import { useCollator } from "react-aria"; +import * as viemChains from "viem/chains"; + +import styles from "./chain-select.module.scss"; +import { useQuery } from "./use-query"; +import { EntropyDeployments } from "../../entropy-deployments"; +import type { ConstrainedOmit } from "../../type-utils"; + +export const ChainSelect = ( + props: ComponentProps, +) => ( + + } + > + + +); + +type Deployment = ReturnType[number]; + +const ResolvedChainSelect = ( + props: ConstrainedOmit< + SelectProps, + keyof typeof defaultProps | keyof ReturnType + >, +) => { + const resolvedProps = useResolvedProps(); + + return ({ id })), + }, ]} hideGroupLabel - show={(value) => (value === "" ? "All" : value)} + show={({ id }) => (id === "" ? "All" : id)} placement="bottom end" selectedKey={assetClass} onSelectionChange={setAssetClass} diff --git a/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx b/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx index 53aa45bd20..755450e197 100644 --- a/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx +++ b/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx @@ -3,6 +3,8 @@ import { ChartLine } from "@phosphor-icons/react/dist/ssr/ChartLine"; import { Badge } from "@pythnetwork/component-library/Badge"; import { Card } from "@pythnetwork/component-library/Card"; +import { EntityList } from "@pythnetwork/component-library/EntityList"; +import { NoResults } from "@pythnetwork/component-library/NoResults"; import { Paginator } from "@pythnetwork/component-library/Paginator"; import { SearchInput } from "@pythnetwork/component-library/SearchInput"; import { Select } from "@pythnetwork/component-library/Select"; @@ -21,7 +23,6 @@ import styles from "./price-feeds-card.module.scss"; import { useQueryParamFilterPagination } from "../../hooks/use-query-param-filter-pagination"; import { Cluster } from "../../services/pyth"; import { AssetClassBadge } from "../AssetClassBadge"; -import { EntityList } from "../EntityList"; import { FeedKey } from "../FeedKey"; import { SKELETON_WIDTH, @@ -29,7 +30,6 @@ import { LiveConfidence, LiveValue, } from "../LivePrices"; -import { NoResults } from "../NoResults"; import { PriceFeedTag } from "../PriceFeedTag"; import { PriceName } from "../PriceName"; @@ -249,7 +249,7 @@ const PriceFeedsCardContents = ({ id, ...props }: PriceFeedsCardContents) => ( onChange: props.onSearchChange, })} /> - +