diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f8e1e5446a..a79cd31715 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ concurrency: jobs: build: name: Build binary packages - runs-on: macos-11 + runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v3 @@ -42,124 +42,20 @@ jobs: path: "**/node_modules" key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: restore pkg cache - id: cache-pkg - uses: actions/cache@v3 - with: - path: "~/.pkg-cache" - key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: yarn install run: yarn install --frozen-lockfile --silent - - name: build pkgs + - name: build api server # I want to do --parallel here, but the binary downloads conflict with each other run: | echo RELEASE_PATH="${GITHUB_WORKSPACE}/releases" >> "${GITHUB_ENV}" - echo BIN_PATH="${GITHUB_WORKSPACE}/bin" >> "${GITHUB_ENV}" - yarn run lerna-run pkg + yarn run esbuild - name: Rename and move built binaries run: | - mkdir -p "${RELEASE_PATH}" "${BIN_PATH}" - - for arch in linux-arm64 linux-x64 macos-arm64 macos-x64 win-x64; do - package="api" - in_name="$package-$arch" - out_name="livepeer-$package" - archive_name="livepeer-$(echo $in_name | sed -e 's/x64/amd64/;s/win/windows/;s/macos/darwin/')" - cd ./packages/$package/bin - case "$arch" in - win-x64) - in_name="$in_name.exe" - out_name="$out_name.exe" - mv "./$in_name" "./$out_name" - zip -q9 "${RELEASE_PATH}/${archive_name}.zip" "./$out_name" - ;; - linux-*) - mv "./$in_name" "./$out_name" - tar -czvf "${RELEASE_PATH}/${archive_name}.tar.gz" "./$out_name" - ;; - macos-*) - mkdir -p "${BIN_PATH}/$arch" - mv "./$in_name" "${BIN_PATH}/$arch/$out_name" - ;; - esac - cd - - done - - - name: Upload macos binaries for codesigning (arm64) - uses: actions/upload-artifact@v3 - with: - name: macos-arm64 - path: bin/macos-arm64 - - - name: Upload macos binaries for codesigning (amd64) - uses: actions/upload-artifact@v3 - with: - name: macos-amd64 - path: bin/macos-x64 - - - name: Upload artifacts for cutting release - uses: actions/upload-artifact@v3 - with: - name: release-artifacts - path: releases/ - - macos: - strategy: - fail-fast: true - matrix: - arch: - - amd64 - - arm64 - name: Codesign and archive macOS binaries - runs-on: macos-11 - needs: build - steps: - - name: Setup env - run: | - echo ARCH="${{ matrix.arch }}" >> "$GITHUB_ENV" - - - name: Download binaries from build stage - uses: actions/download-artifact@v3 - with: - name: macos-${{ matrix.arch }} - path: bin/ - - - name: Fix file permissions - run: | - cd bin/ - chmod a+x livepeer-* - - - uses: actions-ecosystem/action-regex-match@v2 - id: match-tag - with: - text: ${{ github.ref_name }} - regex: '^(master|main|v[0-9]+\.\d+\.\d+)$' - - - name: Codesign and notarize binaries - if: ${{ steps.match-tag.outputs.match != '' }} - uses: livepeer/action-gh-codesign-apple@latest - with: - developer-certificate-id: ${{ secrets.CI_MACOS_CERTIFICATE_ID }} - developer-certificate-base64: - ${{ secrets.CI_MACOS_CERTIFICATE_BASE64 }} - developer-certificate-password: - ${{ secrets.CI_MACOS_CERTIFICATE_PASSWORD }} - app-notarization-email: ${{ secrets.CI_MACOS_NOTARIZATION_USER }} - app-notarization-password: - ${{ secrets.CI_MACOS_NOTARIZATION_PASSWORD }} - app-notarization-team-id: ${{ secrets.CI_MACOS_NOTARIZATION_TEAM_ID }} - binary-path: "bin/" - - - name: Archive built binaries - run: | - mkdir -p releases/ - cd bin/ - for file in $(find . -type f -perm -a+x); do - tar -czf "../releases/${file}-darwin-${ARCH}.tar.gz" "${file}" - done + mkdir -p "${RELEASE_PATH}" + cd ./packages/api/dist-esbuild + tar -czvf "${RELEASE_PATH}/livepeer-api.tar.gz" livepeer-api - name: Upload artifacts for cutting release uses: actions/upload-artifact@v3 @@ -178,7 +74,6 @@ jobs: runs-on: ubuntu-latest needs: - build - - macos steps: - name: Download artifacts uses: actions/download-artifact@v3 diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index be3f9e86d4..2b6ae15930 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -24,9 +24,6 @@ jobs: fail-fast: false matrix: build: - - path: packages/www - image: livepeerci/www - package: www - path: packages/api image: livepeerci/api package: api diff --git a/.github/workflows/esbuild.yaml b/.github/workflows/esbuild.yaml deleted file mode 100644 index 67426a0763..0000000000 --- a/.github/workflows/esbuild.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Just exists to make sure that the esbuild works and we haven't broken anything -name: Verify esbuild build still works - -on: - pull_request: - push: - branches: - - master - - dev - tags: - - "v*" - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - static: - name: Do an esbuild build - runs-on: ubuntu-latest - - steps: - - name: checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - # Check https://github.com/livepeer/go-livepeer/pull/1891 - # for ref value discussion - ref: ${{ github.event.pull_request.head.sha }} - - - uses: actions/setup-node@v3 - with: - node-version: "18" - cache: "yarn" - - - name: yarn install - run: yarn install --frozen-lockfile --ignore-scripts - - - name: build api dependency - run: cd packages/api && yarn run esbuild && cd - - - # make still it still runs with no node_modules and stuff - - name: copy it to a new directory and test - run: |- - mkdir -p /tmp/esbuild-test \ - && cp ./packages/api/dist-esbuild/api.js /tmp/esbuild-test/api.js \ - && cd /tmp/esbuild-test \ - && node api.js --help diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml deleted file mode 100644 index 8e0c4a9bc4..0000000000 --- a/.github/workflows/static.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Verify static build still works - -on: - pull_request: - push: - branches: - - master - - dev - tags: - - "v*" - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - static: - name: Do a static build - runs-on: ubuntu-latest - - steps: - - name: checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - # Check https://github.com/livepeer/go-livepeer/pull/1891 - # for ref value discussion - ref: ${{ github.event.pull_request.head.sha }} - - - uses: actions/setup-node@v3 - with: - node-version: "18" - cache: "yarn" - - - name: yarn install - run: yarn install --frozen-lockfile --ignore-scripts - - - name: build api dependency - run: cd packages/api && yarn run prepare - - - name: static build - run: cd packages/www && yarn run static diff --git a/.prettierignore b/.prettierignore index 8bf304c563..90c314b651 100644 --- a/.prettierignore +++ b/.prettierignore @@ -14,3 +14,6 @@ packages/api/src/schema/validators packages/api/src/schema/types.d.ts .env packages/api/src/schema/schema.yaml +packages/www/static-build +packages/www/static-build-app +packages/api/dist-esbuild/api.js diff --git a/package.json b/package.json index ce1fefb7c3..6e2dbcd5a8 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,7 @@ "prettier": "yarn run prettier:base --write", "prettier:check": "yarn run prettier:base --check", "release": "lerna version", - "pkg": "lerna run --stream build && lerna run --stream pkg", - "pkg:local": "lerna run --stream build && lerna run --stream pkg:local" + "esbuild": "cd packages/www && yarn run static && cd ../api && yarn run esbuild" }, "devDependencies": { "@commitlint/cli": "^16.2.3", diff --git a/packages/api/.prettierignore b/packages/api/.prettierignore index 1269488f7f..ef738aa029 100644 --- a/packages/api/.prettierignore +++ b/packages/api/.prettierignore @@ -1 +1,3 @@ data +dist +dist-esbuild diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index a9710a8b98..c266a172b4 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -4,12 +4,13 @@ WORKDIR /app COPY . . RUN yarn install --frozen-lockfile --ignore-scripts RUN yarn run prepare:lerna -RUN cd packages/api && yarn run pkg:local +RUN cd packages/www && yarn run static +RUN cd packages/api && yarn run esbuild -FROM ubuntu:22.04 +FROM node:18 WORKDIR app -COPY --from=builder /app/packages/api/bin/api /usr/local/bin/livepeer-api +COPY --from=builder /app/packages/api/dist-esbuild/livepeer-api /usr/local/bin/livepeer-api ENV NODE_ENV production ARG VERSION ENV VERSION ${VERSION} diff --git a/packages/api/esbuild.mjs b/packages/api/esbuild.mjs index 7b5dfd0830..6cd6f2bb62 100644 --- a/packages/api/esbuild.mjs +++ b/packages/api/esbuild.mjs @@ -1,16 +1,75 @@ -import esbuild from "esbuild"; +// Builds a single-file zero-dependency version of the API server +// with an embedded static version of the frontend. + +import * as esbuild from "esbuild"; +import { readFile, readdir } from "fs/promises"; +import { resolve, relative } from "path"; + +async function getFiles(dir) { + const dirents = await readdir(dir, { withFileTypes: true }); + let files = await Promise.all( + dirents.map((dirent) => { + const res = resolve(dir, dirent.name); + return dirent.isDirectory() ? getFiles(res) : res; + }) + ); + return Array.prototype.concat(...files).filter((f) => !f.includes("cache")); +} + +const generateFrontendManifest = async (rootPath) => { + const output = [`module.exports = {`]; + let files = await getFiles(rootPath); + for (const file of files) { + const key = relative(rootPath, file); + output.push(` "${key}": require("${file}"),`); + } + output.push(`};`); + return output.join("\n"); +}; + +let frontendBundlePlugin = { + name: "frontendBundle", + setup(build) { + build.onResolve({ filter: /.*frontend-stub$/ }, async (args) => { + return { + path: resolve(args.resolveDir, "..", "..", "www", "static-build"), + namespace: "frontendBundle", + }; + }); + + // Any files from the static-build directory should be bundled as binary + build.onLoad( + { namespace: "frontendBundle", filter: /.*static\-build/ }, + async (args) => { + return { + contents: await generateFrontendManifest(args.path), + loader: "js", + resolveDir: resolve(args.resolveDir, args.path), + }; + } + ); + + // Any files from the static-build directory should be bundled as binary + build.onLoad({ filter: /.*static\-build.+/ }, async (args) => { + const contents = await readFile(args.path); + return { + contents: contents, + loader: "binary", + }; + }); + }, +}; (async () => { await esbuild.build({ entryPoints: ["./src/cli.ts"], bundle: true, platform: "node", - outfile: "./dist-esbuild/api.js", + outfile: "./dist-esbuild/livepeer-api", target: "node18", - alias: { - "@livepeer.studio/www": "./src/frontend-stub.ts", - }, external: ["pg-native"], sourcemap: "inline", + plugins: [frontendBundlePlugin], + minify: true, }); })(); diff --git a/packages/api/package.json b/packages/api/package.json index cda7e74545..775a69ff23 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -36,8 +36,6 @@ "test:dev": "jest \"${PWD}/src\" -i --silent --watch", "test:build": "parcel build --no-autoinstall --no-minify --bundle-node-modules -t browser --out-dir ../dist-worker ../src/worker.js", "coverage": "yarn run test --coverage", - "pkg": "pkg --out-path=bin --no-bytecode --public-packages \"*\" --public . --compress GZip ", - "pkg:local": "yarn run prepare && yarn run pkg --targets=$(node -p '`node18-${process.env.GOOS ?? os.platform()}-${process.env.GOARCH ?? os.arch()}`')", "esbuild": "yarn run compile-schemas && node esbuild.mjs" }, "dependencies": { @@ -92,6 +90,7 @@ "livekit-server-sdk": "^1.2.5", "lodash": "^4.17.21", "m3u8-parser": "^4.4.0", + "mime": "^3.0.0", "minio": "^7.0.12", "morgan": "^1.9.1", "mqtt": "^4.2.6", @@ -144,7 +143,6 @@ "npm-run-all": "^4.1.5", "nyc": "^14.0.0", "parcel": "^1.12.4", - "pkg": "^5.8.1", "prettier": "^2.6.2", "redoc-cli": "^0.8.3", "tus-js-client": "^3.0.0-0", @@ -162,29 +160,12 @@ ] }, "browserslist": [ - "node 10" + "node 16" ], "types": "src/schema/types.d.ts", "nodemonConfig": { "delay": "250" }, - "pkg": { - "targets": [ - "node18-macos-arm64", - "node18-macos-x64", - "node18-linux-arm64", - "node18-linux-x64", - "node18-windows-x64" - ], - "assets": [ - "../www/.next", - "../www/next.config.js", - "../../node_modules/dom-helpers/**/*", - "../../node_modules/react-icons/**/*", - "../../node_modules/next-mdx-remote/**/*", - "../../node_modules/livekit-server-sdk/**/*" - ] - }, "jest": { "testTimeout": 30000 } diff --git a/packages/api/src/app-router.ts b/packages/api/src/app-router.ts index dada3b60a0..b9eb7a5ad5 100644 --- a/packages/api/src/app-router.ts +++ b/packages/api/src/app-router.ts @@ -25,7 +25,7 @@ import { pathJoin } from "./controllers/helpers"; import { taskScheduler } from "./task/scheduler"; import { setupTus, setupTestTus } from "./controllers/asset"; import * as fcl from "@onflow/fcl"; -import createFrontend from "@livepeer.studio/www"; +import createFrontend from "./frontend"; import { NotFoundError } from "./store/errors"; import { cache } from "./store/cache"; diff --git a/packages/api/src/frontend-stub.ts b/packages/api/src/frontend-stub.ts index 30954db034..16c06be5a1 100644 --- a/packages/api/src/frontend-stub.ts +++ b/packages/api/src/frontend-stub.ts @@ -1,6 +1,2 @@ -// Stub thing to call when we don't want the whole frontend there -export default function () { - return (req, res, next) => { - next(); - }; -} +// Empty frontend for when we don't want it packaged. +export default {}; diff --git a/packages/api/src/frontend.ts b/packages/api/src/frontend.ts new file mode 100644 index 0000000000..d2481b6d0d --- /dev/null +++ b/packages/api/src/frontend.ts @@ -0,0 +1,47 @@ +import express, { Request, Response } from "express"; +import frontend from "./frontend-stub"; +import mime from "mime"; + +// middleware that serves a Next.js static frontend which has been +// bundled into the frontend object here + +const resolveFile = (inputPath: string): string | null => { + // Exact match, easy! + if (frontend[inputPath]) { + return inputPath; + } + // /dashboard --> dashboard.html + const htmlPath = `${inputPath}.html`; + if (frontend[htmlPath]) { + return htmlPath; + } + // /dashboard/stream/abc123 --> /dashboard/stream/[id].html + const parts = inputPath.split("/").slice(0, -1); + const slugPath = `${parts.join("/")}/[id].html`; + console.log(`SLUG PATH: ${slugPath}`); + if (frontend[slugPath]) { + return slugPath; + } + // / --> /index.html + if (inputPath === "") { + return "index.html"; + } + return null; +}; + +// Stub thing to call when we don't want the whole frontend there +export default function () { + return (req: Request, res: Response, next) => { + const path = decodeURIComponent(req.path.slice(1)); + const foundPath = resolveFile(path); + if (!foundPath) { + console.log(`no match for ${path}`); + return next(); + } + console.log(`found page for ${path}`); + const page = frontend[foundPath]; + const contentType = mime.getType(foundPath); + res.header("content-type", contentType); + res.send(Buffer.from(page)); + }; +} diff --git a/packages/www/.gitignore b/packages/www/.gitignore index 5e515be53b..7d1bf596cf 100644 --- a/packages/www/.gitignore +++ b/packages/www/.gitignore @@ -48,3 +48,5 @@ bin # Sitemap public/sitemap-0.xml +static-build +static-build-app diff --git a/packages/www/Dockerfile b/packages/www/Dockerfile deleted file mode 100644 index fa1327650a..0000000000 --- a/packages/www/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:18 - -WORKDIR /app - -COPY package.json yarn.lock lerna.json ./ - -COPY . . - -RUN yarn install --frozen-lockfile - -WORKDIR /app/packages/www - -CMD ["yarn", "run", "start"] diff --git a/packages/www/components/AppPlayer/index.tsx b/packages/www/components/AppPlayer/index.tsx index 1f78f9ad33..024ce1dcc8 100644 --- a/packages/www/components/AppPlayer/index.tsx +++ b/packages/www/components/AppPlayer/index.tsx @@ -1,4 +1,5 @@ import { Player } from "@livepeer/react"; +import { isExport } from "lib/utils"; import mux from "mux-embed"; import { useCallback } from "react"; @@ -23,16 +24,18 @@ const AppPlayer = ({ type, }: AppPlayerProps) => { const mediaElementRef = useCallback((element: HTMLMediaElement) => { - mux.monitor(element, { - debug: false, - data: { - env_key: "8oj27fenun6v4ffvrgn6ehc7m", - player_name: `Studio Dashboard Player (${ - type === "asset" ? "Asset" : "Stream" - })`, - player_env: process.env.NEXT_PUBLIC_VERCEL_ENV ?? "development", - }, - }); + if (!isExport()) { + mux.monitor(element, { + debug: false, + data: { + env_key: "8oj27fenun6v4ffvrgn6ehc7m", + player_name: `Studio Dashboard Player (${ + type === "asset" ? "Asset" : "Stream" + })`, + player_env: process.env.NEXT_PUBLIC_VERCEL_ENV ?? "development", + }, + }); + } }, []); return ( diff --git a/packages/www/components/GettingStarted/index.tsx b/packages/www/components/GettingStarted/index.tsx index b80bf337bd..f3add99961 100644 --- a/packages/www/components/GettingStarted/index.tsx +++ b/packages/www/components/GettingStarted/index.tsx @@ -12,12 +12,14 @@ import { } from "@livepeer/design-system"; import Link from "next/link"; import { ArrowRightIcon, ArrowTopRightIcon } from "@radix-ui/react-icons"; +import { getBrandName } from "lib/utils"; const GettingStarted = ({ firstName = "" }) => { return ( <> - Welcome to Livepeer Studio{firstName && `, ${firstName}`} + Welcome to {getBrandName()} + {firstName && `, ${firstName}`} diff --git a/packages/www/components/Sidebar/index.tsx b/packages/www/components/Sidebar/index.tsx index fd7fff1ae6..750ba550ed 100644 --- a/packages/www/components/Sidebar/index.tsx +++ b/packages/www/components/Sidebar/index.tsx @@ -30,6 +30,7 @@ import { useApi } from "../../hooks"; import Router from "next/router"; import { RocketIcon, ChatBubbleIcon, LoopIcon } from "@radix-ui/react-icons"; import Contact from "../Contact"; +import { isExport } from "lib/utils"; export const NavLink = styled(A, { fontSize: 14, @@ -226,117 +227,123 @@ const Sidebar = ({ id }: { id: SidebarId }) => { )} - - - - - Usage - - - + {!isExport() && ( + + + + + Usage + + + + )} - - - - - Billing - - + {!isExport() && ( + + + + + Billing + + - {id?.split("/")[0] === "billing" && ( - :first-child": { - mt: "$1", - }, - }}> - - Plans - - - )} - + {id?.split("/")[0] === "billing" && ( + :first-child": { + mt: "$1", + }, + }}> + + Plans + + + )} + + )} - - - - + - Status - - + + + Status + + - - - - Changelog - - + + + Changelog + + - - - - Feature Requests - - - - + + + Feature Requests + + + + + )} ); diff --git a/packages/www/components/Site/ArrowLink/index.tsx b/packages/www/components/Site/ArrowLink/index.tsx deleted file mode 100644 index be9026d308..0000000000 --- a/packages/www/components/Site/ArrowLink/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Box, Link as A } from "@livepeer/design-system"; -import Link from "next/link"; - -const ArrowLink = ({ - href, - hideArrow = false, - color = "$hiContrast", - children, - css = {}, - target = "_self", -}) => { - return ( - - - {children} - - - - ); -}; - -export default ArrowLink; diff --git a/packages/www/components/Site/Badge/index.tsx b/packages/www/components/Site/Badge/index.tsx deleted file mode 100644 index 17be05d7b7..0000000000 --- a/packages/www/components/Site/Badge/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box } from "@livepeer/design-system"; - -const Badge = ({ children, color = "$hiContrast", css }) => ( - - {children} - -); - -export default Badge; diff --git a/packages/www/components/Site/BlogCTA/index.tsx b/packages/www/components/Site/BlogCTA/index.tsx deleted file mode 100644 index dd01b72746..0000000000 --- a/packages/www/components/Site/BlogCTA/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useEffect, useState } from "react"; -import { Flex } from "@livepeer/design-system"; -import Button from "components/Site/Button"; -import Link from "next/link"; - -const BlogCTA = ({ - variant, - internalLink, - anchorLink, - externalLink, - title, -}) => { - const [document, setDocument] = useState(null); - - useEffect(() => { - async function init() { - const response = await fetch( - `https://dp4k3mpw.api.sanity.io/v1/data/doc/production/${internalLink._ref}` - ); - const { documents } = await response.json(); - setDocument(documents[0]); - } - if (internalLink) init(); - }, [internalLink]); - - return ( - - {document && ( - - - - )} - - {externalLink && ( - - )} - - {anchorLink && ( - - - - )} - - ); -}; - -export default BlogCTA; diff --git a/packages/www/components/Site/BlogPlayer/index.tsx b/packages/www/components/Site/BlogPlayer/index.tsx deleted file mode 100644 index 85e94a8bd0..0000000000 --- a/packages/www/components/Site/BlogPlayer/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useEffect, useState } from "react"; -import { Player } from "@livepeer/react"; -import mux from "mux-embed"; -import { memo, useCallback } from "react"; - -const BlogPlayer = ({ assetId }) => { - const [playbackUrl, setPlaybackUrl] = useState(null); - - useEffect(() => { - (async () => { - const response = await fetch( - `https://dp4k3mpw.api.sanity.io/v1/data/doc/production/${assetId}` - ); - const { documents } = await response.json(); - setPlaybackUrl(`https://stream.mux.com/${documents[0].playbackId}.m3u8`); - })(); - }, []); - - const mediaElementRef = useCallback((element: HTMLMediaElement) => { - mux.monitor(element, { - debug: false, - data: { - env_key: "8oj27fenun6v4ffvrgn6ehc7m", - player_name: "Studio Blog Player", - player_env: process.env.NEXT_PUBLIC_VERCEL_ENV ?? "development", - }, - }); - }, []); - - if (!playbackUrl) return <>; - - return ( - - ); -}; - -export default BlogPlayer; diff --git a/packages/www/components/Site/BlogPostCard/Featured.tsx b/packages/www/components/Site/BlogPostCard/Featured.tsx deleted file mode 100644 index ba861d3bdd..0000000000 --- a/packages/www/components/Site/BlogPostCard/Featured.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import readingTime from "reading-time"; -import { blocksToText } from "lib/utils"; -import Link from "next/link"; -import { Flex, Box, Text, Heading, Link as A } from "@livepeer/design-system"; -import imageUrlBuilder from "@sanity/image-url"; -import { client } from "lib/client"; -import Image from "next/image"; -import TextTruncate from "react-text-truncate"; -import { urlFor } from "lib/sanity"; - -export const FeaturedBlogPostCard = ({ post }: { post: any }) => { - const text = blocksToText(post.content); - const stats = readingTime(text); - // const builder = imageUrlBuilder(client as any); - - return ( - - - {post.mainImage && ( - - {post.mainImage?.alt} - - )} - - - - {post.category.title} - - - - {post.author.image?.alt} - - - {post.author.name} - - - - {stats.text} - - - - {post.title} - - - - - - - Read more - - - - - ); -}; diff --git a/packages/www/components/Site/BlogPostCard/index.tsx b/packages/www/components/Site/BlogPostCard/index.tsx deleted file mode 100644 index 18bd5b62f5..0000000000 --- a/packages/www/components/Site/BlogPostCard/index.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import readingTime from "reading-time"; -import { blocksToText } from "lib/utils"; -import Link from "next/link"; -import { Flex, Box, Text, Heading, Link as A } from "@livepeer/design-system"; -import imageUrlBuilder from "@sanity/image-url"; -import { client } from "lib/client"; -import Image from "next/image"; -import TextTruncate from "react-text-truncate"; -import { urlFor } from "lib/sanity"; - -const BlogPostCard = ({ post, css = {} }) => { - const text = blocksToText(post.content); - const stats = readingTime(text); - return ( - - - {post.mainImage && ( - - {post.mainImage?.alt} - - )} - - - - {post.category?.title ?? ""} - - - - {post.author.image?.alt} - - - {post.author.name} - - - - {stats.text} - - - - - - - - - - - Read more - - - - - ); -}; - -export default BlogPostCard; -export { FeaturedBlogPostCard } from "./Featured"; diff --git a/packages/www/components/Site/BulletedTitle/index.tsx b/packages/www/components/Site/BulletedTitle/index.tsx deleted file mode 100644 index 270904cf6d..0000000000 --- a/packages/www/components/Site/BulletedTitle/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Box, Flex } from "@livepeer/design-system"; - -const BulletedTitle = ({ children, css }) => ( - - - {children} - -); - -export default BulletedTitle; diff --git a/packages/www/components/Site/Button/index.tsx b/packages/www/components/Site/Button/index.tsx deleted file mode 100644 index 52a07cf88d..0000000000 --- a/packages/www/components/Site/Button/index.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Box, Button as ButtonBase } from "@livepeer/design-system"; -import React from "react"; - -export const Button = React.forwardRef( - ({ arrow = false, children, css = {}, ...props }: any, forwardedRef) => ( - - - {children} - {arrow && ( - - )} - - - ) -); - -export default Button; diff --git a/packages/www/components/Site/Card/index.tsx b/packages/www/components/Site/Card/index.tsx deleted file mode 100644 index 7a364aa219..0000000000 --- a/packages/www/components/Site/Card/index.tsx +++ /dev/null @@ -1,266 +0,0 @@ -import { Box, Flex, Text, Link as A } from "@livepeer/design-system"; -import CutOut from "components/Site/CutOut"; -import Badge from "components/Site/Badge"; -import { MdCheckBoxOutlineBlank } from "react-icons/md"; - -type Cta = { - href?: string; - isExternal?: boolean; -}; - -interface Props { - bc?: string; - color?: string; - lines?: "midnight" | "tomato" | null; - lineCount?: 4 | 5; - height: number | string; - label?: string; - title?: string; - description: string; - arrow?: boolean; - cta?: Cta; -} - -export const Card = ({ - bc = "white", - color = "$loContrast", - lines = null, - lineCount = 4, - height = "auto", - label = "", - title = "", - description = "", - arrow = false, - cta = { - href: null, - isExternal: false, - }, -}: Props) => ( - - - - - - {!lines ? ( - - - {label} - - - {title} - - - {description} - - - ) : ( - - - - - {label} - - - {title} - - - - - - - - - {lineCount === 5 && ( - - )} - {arrow && ( - - - - - - )} - - - - {description} - - - - - - )} - - -); -export default Card; diff --git a/packages/www/components/Site/Cards/index.tsx b/packages/www/components/Site/Cards/index.tsx deleted file mode 100644 index 9cf306aa24..0000000000 --- a/packages/www/components/Site/Cards/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { - Container, - Box, - Flex, - Text, - Heading, - Link as A, - Button, -} from "@livepeer/design-system"; -import Guides from "components/Site/Guides"; -import Link from "next/link"; -import * as Hi from "react-icons/hi"; -import * as Fa from "react-icons/fa"; -import * as Fi from "react-icons/fi"; -import * as Md from "react-icons/md"; -import { FiArrowUpRight } from "react-icons/fi"; - -const getIconProvider = (provider) => { - if (provider === "hi") { - return Hi; - } - if (provider === "fa") { - return Fa; - } - if (provider === "fi") { - return Fi; - } - if (provider === "mdi") { - return Md; - } -}; - -const Cards = ({ - backgroundColor = "$loContrast", - title = null, - heading = null, - description = null, - items, - ctas, -}) => { - return ( - - {/* */} - - - {items.map((item, i) => { - return ( - - {item?.icon?.provider && ( - - {getIconProvider(item.icon.provider)[item.icon.name]()} - - )} - - {item.title} - - - {item.description} - - - ); - })} - - - - ); -}; - -export default Cards; diff --git a/packages/www/components/Site/CaseStudy/index.tsx b/packages/www/components/Site/CaseStudy/index.tsx deleted file mode 100644 index 364645086c..0000000000 --- a/packages/www/components/Site/CaseStudy/index.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { - Container, - Box, - Flex, - Text, - Grid, - Heading, - Link as A, -} from "@livepeer/design-system"; -import Guides from "components/Site/Guides"; -import Link from "next/link"; - -const CaseStudy = ({ - heading, - about, - testimonial, - problem, - solution, - externalLink = null, - internalLink = null, - backgroundColor = "$loContrast", -}) => { - return ( - - - - - - - Case Study - - - - {heading} - - - {about} - - - - - - - - - - Problem - - - {problem} - - - - - Solution - - - {solution} - - {externalLink && ( - - - Read the full story - - - )} - {internalLink && ( - - - Read the full story - - - )} - - - - - - - {testimonial.quote} - - - {testimonial.name} - , {testimonial.role} - - - - - - - - ); -}; - -export default CaseStudy; diff --git a/packages/www/components/Site/CenterTitle/index.tsx b/packages/www/components/Site/CenterTitle/index.tsx deleted file mode 100644 index b834c916da..0000000000 --- a/packages/www/components/Site/CenterTitle/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Box as LiveBox } from "@livepeer/design-system"; -import { PortableText } from "@portabletext/react"; -import { Text, Container, Box } from "@theme-ui/components"; - -import React from "react"; - -export default function CenterTitle({ title, portableText }) { - return ( - - - - {title} - - {portableText && } - - - ); -} diff --git a/packages/www/components/Site/CentralisedTitle/index.tsx b/packages/www/components/Site/CentralisedTitle/index.tsx deleted file mode 100644 index fedbecc248..0000000000 --- a/packages/www/components/Site/CentralisedTitle/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; -import { PortableText } from "@portabletext/react"; -import { Container, Box } from "@theme-ui/components"; -import { Text, Box as LiveBox } from "@livepeer/design-system"; -import Image from "next/image"; -import { urlFor } from "../../../lib/client"; - -const CentralisedHero = ({ title, portableText, image }) => { - return ( - - - - - - {title} - - - {portableText && } - - - {title} - - - - ); -}; -export default CentralisedHero; diff --git a/packages/www/components/Site/Code.tsx b/packages/www/components/Site/Code.tsx deleted file mode 100644 index c95f3adf7a..0000000000 --- a/packages/www/components/Site/Code.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import Highlight, { defaultProps } from "prism-react-renderer"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { Box } from "@livepeer/design-system"; - -const Code = ({ - language, - custom = true, - value, - children, - className, - ...rest -}) => { - const [copied, setCopied] = useState(false); - if (className && className.startsWith("language-")) { - language = className.replace("language-", ""); - } - - const handleCopy = useCallback(() => { - try { - navigator.clipboard.writeText(children); - setCopied(true); - } catch (error) { - console.error(error); - } - }, []); - - useEffect(() => { - if (copied) { - const timeout = setTimeout(() => { - setCopied(false); - }, 3000); - return () => { - clearTimeout(timeout); - }; - } - }, [copied]); - - const theme = useMemo(() => { - return { - plain: { - backgroundColor: custom ? "#3B375A" : "#9CDCFE", - color: custom ? "#fff" : "#1E1E1E", - }, - styles: [ - { - types: ["comment", "prolog", "doctype", "cdata", "punctuation"], - style: { - color: custom ? "#8782AC" : "rgb(0, 0, 128)", - }, - }, - { - types: ["namespace"], - style: { - opacity: 0.7, - }, - }, - { - types: ["tag", "operator", "number"], - style: { - color: custom ? "#C16AB9" : "rgb(181, 206, 168)", - }, - }, - { - types: ["property", "function"], - style: { - color: custom ? "#C4ED98" : "rgb(220, 220, 170)", - }, - }, - { - types: ["tag-id", "selector", "atrule-id"], - style: { - color: custom ? "#C4ED98" : "rgb(215, 186, 125)", - }, - }, - { - types: ["attr-name"], - style: { - color: custom ? "#C4ED98" : "rgb(156, 220, 254)", - }, - }, - { - types: [ - "boolean", - "string", - "entity", - "url", - "attr-value", - "keyword", - "control", - "directive", - "unit", - "statement", - "regex", - "at-rule", - "placeholder", - "variable", - ], - style: { - color: custom ? "#C4ED98" : "rgb(206, 145, 120)", - }, - }, - { - types: ["deleted"], - style: { - textDecorationLine: "line-through", - }, - }, - { - types: ["inserted"], - style: { - textDecorationLine: "underline", - }, - }, - { - types: ["italic"], - style: { - fontStyle: "italic", - }, - }, - { - types: ["important", "bold"], - style: { - fontWeight: "bold", - }, - }, - { - types: ["important"], - style: { - color: "#c4b9fe", - }, - }, - ], - }; - }, [custom]); - - return ( - - {({ tokens, getLineProps, getTokenProps }) => ( - - - {tokens.map((line, i) => { - // Workaround for MDX rendering trailing lines on everything - const lastLine = i === tokens.length - 1; - return ( - - {line.map((token, key) => { - if (lastLine && token.empty) { - return null; - } - return ( - - ); - })} - - ); - })} - - {custom && ( - - {copied ? "Copied" : "Copy"} - - )} - - )} - - ); -}; - -export default Code; diff --git a/packages/www/components/Site/Compare/Hero.tsx b/packages/www/components/Site/Compare/Hero.tsx deleted file mode 100644 index c27d3c323a..0000000000 --- a/packages/www/components/Site/Compare/Hero.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { - Box, - Flex, - Heading, - Text, - Container, - Button, -} from "@livepeer/design-system"; -import { FiArrowUpRight } from "react-icons/fi"; -import Link from "next/link"; - -const CompareHero = ({ - title = "Compare Livepeer Studio", - comparison = null, -}: any) => { - return ( - - - - - - {title} - - - Livepeer Studio is a new approach to video infrastructure. Learn - how its features and pricing compare to {comparison}. - - - - - - - - - - - - - - - - - - ); -}; - -export default CompareHero; diff --git a/packages/www/components/Site/Compare/Table.tsx b/packages/www/components/Site/Compare/Table.tsx deleted file mode 100644 index a89f1d93a7..0000000000 --- a/packages/www/components/Site/Compare/Table.tsx +++ /dev/null @@ -1,408 +0,0 @@ -import { CheckIcon } from "@radix-ui/react-icons"; -import { - Flex, - Box, - Text, - Status, - Badge, - Table, - Tbody, - Td, - Th, - Thead, - Tr, -} from "@livepeer/design-system"; - -const Check = () => { - return ( - - - - ); -}; -const CompareTable = ({ comparison = "mux" }) => { - return ( - - - - - - - - - - - - {/* Cost */} - - - - - - - - - - - - - - - - - {/* Performance */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* Key features */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* Onchain integrations */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Livepeer Studio - - - - - Mux - - - - - Cloudflare Stream - -
- - Cost - -
- - Transcoding (10k mins) - - - Storage (50k mins) - - - Delivery (500k mins) - - - $500 - - - $1212 - +140% - - - - $750 +50% - -
- - - Performance - - - Based on a controlled-environment test - - -
- Expected Latency - - 0.5 - 3s - - 4s - - 0.5 - 3s -
- Video Startup Time (TTFF) - - 0.2s - - 0.4s - - 0.2s -
- Uptime SLA - - - - - - -
- - Key features - -
- - Decentralized physical infrastructure network - - - -
- Open source with self-host option - - -
- Embeddable player - - - - -
- Analytics API & Visualization - - - - Available at additional cost - - -
- Transcoding API - - - - -
- Developer SDKs - - - - - - -
- In-browser Broadcasting - - - - -
- Access control - - - - -
- Multistreaming - - - - - - -
- Video clipping - - - - - - -
- Thumbnails - - - - - - -
- Captions - Coming soon! - - - -
- - Onchain features - -
- Decentralized storage integrations - - -
- Token-gated video - - -
- Wallet-based engagement analytics - - -
-
- ); -}; - -export default CompareTable; diff --git a/packages/www/components/Site/Contact/index.tsx b/packages/www/components/Site/Contact/index.tsx deleted file mode 100644 index 91dd0448fd..0000000000 --- a/packages/www/components/Site/Contact/index.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { useRef, useState, useEffect } from "react"; -import { - Box, - Text, - Grid, - Container, - TextField, - Heading, - TextArea, - Link as A, - Button, -} from "@livepeer/design-system"; -import { useHubspotForm } from "hooks"; -import Fade from "react-reveal/Fade"; -import Guides from "../Guides"; - -const Contact = ({ backgroundColor }) => { - const formEl = useRef(null); - const { data, handleSubmit } = useHubspotForm({ - portalId: process.env.NEXT_PUBLIC_HUBSPOT_PORTAL_ID, - formId: process.env.NEXT_PUBLIC_HUBSPOT_FORM_ID, - }); - - const [submitted, setSubmitted] = useState(false); - useEffect(() => { - if (data) { - setSubmitted(true); - formEl.current.reset(); - let timer = setTimeout(() => { - setSubmitted(false); - }, 4500); - return () => { - clearTimeout(timer); - }; - } - }, [data]); - - return ( - - - - - - - Get in touch - - - - - - - - - - - -