diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index f970b52bc..99d936c29 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -3,7 +3,7 @@ import type { Config } from '@docusaurus/types'; import type * as Preset from '@docusaurus/preset-classic'; const config: Config = { - title: 'Live Compositor', + title: 'LiveCompositor', favicon: 'img/favicon.png', // Set the production url of your site here @@ -65,6 +65,25 @@ const config: Config = { ], themeConfig: { + metadata: [ + { name: 'description', content: 'Real-time, low latency, programmable video & audio mixer' }, + { name: 'keywords', content: 'video, audio, mixing, real-time, live' }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'twitter:title', content: 'LiveCompositor' }, + { + name: 'twitter:description', + content: 'Real-time, low latency, programmable video & audio mixer', + }, + { name: 'twitter:site', content: 'ElixirMembrane' }, + { name: 'og:type', content: 'website' }, + { name: 'og:image', content: 'https://compositor.live/img/logo.webp' }, + { name: 'og:title', content: 'LiveCompositor' }, + { + name: 'og:description', + content: 'Real-time, low latency, programmable video & audio mixer', + }, + { name: 'og:url', content: 'https://compositor.live/' }, + ], colorMode: { defaultMode: 'dark', }, diff --git a/docs/index.d.ts b/docs/index.d.ts index a666151b9..0a2f49a04 100644 --- a/docs/index.d.ts +++ b/docs/index.d.ts @@ -1,4 +1,2 @@ -declare module '*.png'; declare module '*.svg'; -declare module '*.jpeg'; -declare module '*.jpg'; +declare module '*.webp'; diff --git a/docs/pages/api/components/WebView.md b/docs/pages/api/components/WebView.md index 31c145747..b32bc27bc 100644 --- a/docs/pages/api/components/WebView.md +++ b/docs/pages/api/components/WebView.md @@ -4,7 +4,7 @@ hide_table_of_contents: true title: WebView --- -[Required feature: web_renderer](../../deployment/overview.md#web-renderer-support) +[Required feature: web_renderer](../../deployment/overview.md#web-renderer-support) import Docs from "@site/pages/api/generated/component-WebView.md" diff --git a/docs/src/api.ts b/docs/src/api.ts index 0afcb98d0..8b43b2aa3 100644 --- a/docs/src/api.ts +++ b/docs/src/api.ts @@ -1,3 +1,5 @@ +const BACKEND_URL: URL = new URL('http://localhost:8081'); + interface RequestObject { method: string; headers: { @@ -6,41 +8,43 @@ interface RequestObject { body: string; } -export function buildRequestSceneObject(method: string, scene: object): RequestObject { +function buildRequestSceneObject(body: object): RequestObject { return { - method: method, + method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ scene: scene }), + body: JSON.stringify(body), }; } -export async function handleRenderImageRequest( - backendUrl: URL, - requestObject: RequestObject -): Promise { - const renderImageUrl = new URL('/render_image', backendUrl); - const response = await fetch(renderImageUrl, requestObject); - if (response.status >= 400) { - const contentType = response.headers.get('Content-Type'); - const errorStatus = `Error status: ${response.status}`; - let errorMsg = ''; +async function getErrorDescription(response: Response): Promise { + const contentType = response.headers.get('Content-Type'); + const errorStatus = `Error status: ${response.status}`; + let errorMessage = ''; - if (contentType === 'application/json') { - const apiError = await response.json(); - if (apiError.stack) { - errorMsg = `Error message: ${apiError.stack.map(String).join('\n')}`; - } else { - errorMsg = `Error message: ${apiError.message}`; - } + if (contentType === 'application/json') { + const apiError = await response.json(); + if (apiError.stack) { + errorMessage = `Error message: ${apiError.stack.map(String).join('\n')}`; } else { - const txt = await response.text(); - errorMsg = `Error message: ${txt}`; + errorMessage = `Error message: ${apiError.message}`; } - - throw new Error(`${errorStatus}\n${errorMsg}`); + } else { + const txt = await response.text(); + errorMessage = `Error message: ${txt}`; } + return `${errorStatus};\n${errorMessage}`; +} +export async function renderImage(body: object): Promise { + const requestObject = buildRequestSceneObject(body); + const renderImageUrl = new URL('/render_image', BACKEND_URL); + const response = await fetch(renderImageUrl, requestObject); + + if (response.status >= 400) { + const errorDescription = await getErrorDescription(response); + throw new Error(errorDescription); + } return await response.blob(); } diff --git a/docs/src/components/PlaygroundCodeEditor.module.css b/docs/src/components/PlaygroundCodeEditor.module.css new file mode 100644 index 000000000..39ea37123 --- /dev/null +++ b/docs/src/components/PlaygroundCodeEditor.module.css @@ -0,0 +1,12 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ +.codeEditor{ + height: 100%; + width: 100%; + min-width: 300px; + min-height: 120px; + resize: none; + justify-content: center; +} diff --git a/docs/src/components/PlaygroundCodeEditor.tsx b/docs/src/components/PlaygroundCodeEditor.tsx index a5bf66dcd..5313fa53b 100644 --- a/docs/src/components/PlaygroundCodeEditor.tsx +++ b/docs/src/components/PlaygroundCodeEditor.tsx @@ -1,4 +1,4 @@ -import styles from '../pages/playground.module.css'; +import styles from './PlaygroundCodeEditor.module.css'; import { ChangeEvent, useState } from 'react'; interface PlaygroundCodeEditorProps { @@ -6,18 +6,14 @@ interface PlaygroundCodeEditorProps { initialCodeEditorContent: string; } -function PlaygroundCodeEditor({ - onChange, - initialCodeEditorContent, -}: PlaygroundCodeEditorProps): JSX.Element { - const [currCodeEditorContent, setCurrCodeEditorContent] = - useState(initialCodeEditorContent); +function PlaygroundCodeEditor({ onChange, initialCodeEditorContent }: PlaygroundCodeEditorProps) { + const [content, setContent] = useState(initialCodeEditorContent); const handleChange = (event: ChangeEvent) => { - const currCode = event.target.value; - setCurrCodeEditorContent(currCode); + const codeContent = event.target.value; + setContent(codeContent); try { - const scene = JSON.parse(currCode); + const scene = JSON.parse(codeContent); onChange(scene); } catch (error) { onChange(error); @@ -29,7 +25,7 @@ function PlaygroundCodeEditor({ className={styles.codeEditor} name="inputArea" placeholder="Enter your code to try it out" - value={currCodeEditorContent} + value={content} onChange={handleChange} /> ); diff --git a/docs/src/components/PlaygroundPreview.tsx b/docs/src/components/PlaygroundPreview.tsx index 8b8718760..89a0d6de1 100644 --- a/docs/src/components/PlaygroundPreview.tsx +++ b/docs/src/components/PlaygroundPreview.tsx @@ -1,18 +1,15 @@ interface PlaygroundPreviewProps { - responseData: { - imageUrl: string; - errorMessage: string; - }; + imageUrl?: string; + errorMessage?: string; } -function PlaygroundPreview({ responseData }: PlaygroundPreviewProps): JSX.Element { - if (responseData.errorMessage) { - return
{responseData.errorMessage}
; - } - if (responseData.imageUrl) { +function PlaygroundPreview({ imageUrl, errorMessage }: PlaygroundPreviewProps) { + if (errorMessage) { + return
{errorMessage}
; + } else if (imageUrl) { return ( ); + } else { + return null; } - return null; } export default PlaygroundPreview; diff --git a/docs/src/components/PlaygroundRenderSettings.tsx b/docs/src/components/PlaygroundRenderSettings.tsx index cac12f6b5..5840bed6a 100644 --- a/docs/src/components/PlaygroundRenderSettings.tsx +++ b/docs/src/components/PlaygroundRenderSettings.tsx @@ -1,8 +1,6 @@ -import styles from '../pages/playground.module.css'; - -function PlaygroundRenderSettings({ onSubmit }: { onSubmit: () => Promise }): JSX.Element { +function PlaygroundRenderSettings({ onSubmit }: { onSubmit: () => Promise }) { return ( -
+
Settings:
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 3755c612b..acbff1c8b 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -14,6 +14,9 @@ --ifm-color-primary-lighter: #8080f9; --ifm-color-primary-lightest: #a5abfc; --ifm-code-font-size: 95%; + --ifm-h1-font-size: 2.25rem; + --ifm-h2-font-size: 2rem; + --ifm-h3-font-size: 1.5rem; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 9e184563a..9ba32468c 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -5,7 +5,6 @@ import { GiFeather, GiBattery100, GiSpeedometer } from 'react-icons/gi'; import { IoCloudOffline } from 'react-icons/io5'; import { MdAudiotrack, MdLiveTv } from 'react-icons/md'; import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Layout from '@theme/Layout'; import Heading from '@theme/Heading'; @@ -14,12 +13,12 @@ import { PropsWithChildren } from 'react'; import { IconContext, IconType } from 'react-icons'; import MembraneLogo from '@site/static/img/membrane-logo.svg'; import SwmLogo from '@site/static/img/swm-logo.svg'; -import ComposingImg from '@site/static/img/how_it_works.png'; +import ComposingImg from '@site/static/img/how_it_works.webp'; import WebGpuLogoDark from '@site/static/img/webgpu-dark.svg'; import WebGpuLogoLight from '@site/static/img/webgpu-light.svg'; -import VideoConferencingImg from '@site/static/img/videoconferencing.jpg'; -import StreamingImg from '@site/static/img/streaming.jpg'; -import BroadcastingImg from '@site/static/img/broadcasting.jpg'; +import VideoConferencingImg from '@site/static/img/videoconferencing.webp'; +import StreamingImg from '@site/static/img/streaming.webp'; +import BroadcastingImg from '@site/static/img/broadcasting.webp'; import { useColorMode } from '@docusaurus/theme-common'; import TypewriterComponent from 'typewriter-effect'; import ExampleScene from '../components/example_scene'; @@ -85,7 +84,7 @@ function HomepageHeader() { autoPlay muted src="/video/showcase.mp4" - poster="/img/showcase_poster.jpg" + poster="/img/showcase_poster.webp" style={{ width: '100%', display: 'block' }} />
@@ -98,7 +97,7 @@ function HomepageHeader() { function HowItWorks() { return (
- + How it works?

@@ -123,7 +122,7 @@ function HowItWorks() { function UseCases() { return (

- + Use cases

@@ -156,7 +155,7 @@ function UseCaseCard(props: UseCaseCardProps) { return (

- + {props.title}
@@ -173,7 +172,7 @@ function UseCaseCard(props: UseCaseCardProps) { function VisionCards() { return (
- + Vision

@@ -216,7 +215,7 @@ function VisionCard(props: PropsWithChildren) {

- + {props.title}
@@ -234,7 +233,7 @@ type FeatureProps = { function Feature(props: PropsWithChildren) { const text = (
- + {props.text}

{props.secondaryText}

@@ -263,7 +262,7 @@ function Features() { return (
- + Capabilities

Simple, powerful, fast. Pick three.

@@ -426,7 +425,7 @@ function MembranePlugin() { function Usage() { return (
- + Usage @@ -439,7 +438,7 @@ function Usage() { function Licensing() { return (
- + Licensing
@@ -495,7 +494,7 @@ function ContactUs() {
- + Contact us
@@ -517,11 +516,8 @@ function ContactUs() { } export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); return ( - +
diff --git a/docs/src/pages/playground.module.css b/docs/src/pages/playground.module.css index ef63e2c7a..a3c9f9136 100644 --- a/docs/src/pages/playground.module.css +++ b/docs/src/pages/playground.module.css @@ -3,32 +3,31 @@ * and scoped locally. */ .page { - display: flex; - flex-direction: row; - flex-wrap: wrap; - height: 75vh; - margin: 20px; - } + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: 75vh; + margin: 20px; +} - .leftSide { - flex: 1; - margin: 5px; - } +.leftSide { + flex: 1; + margin: 5px; +} - .rightSide { - display: flex; - flex-direction: column; - flex: 1; - margin: 5px; - } +.rightSide { + display: flex; + flex-direction: column; + flex: 1; + margin: 5px; +} - .codeEditor{ - height: 100%; - width: 100%; - resize: none; - min-width: 300px; - min-height: 120px; - justify-content: center; +.codeEditorBox{ + height: 100%; + width: 100%; + min-width: 300px; + min-height: 120px; + justify-content: center; } .preview, @@ -41,17 +40,13 @@ } .settingsBox { - width: 100%; - height: 50%; + width: 100%; + height: 50%; } .preview { - border-radius: 2px; - border-style: groove; - width: 100%; - height: 50%; -} - -.settings { - margin: 10px; + border-radius: 2px; + border-style: groove; + width: 100%; + height: 50%; } diff --git a/docs/src/pages/playground.tsx b/docs/src/pages/playground.tsx index 669657eac..8eaf03e2b 100644 --- a/docs/src/pages/playground.tsx +++ b/docs/src/pages/playground.tsx @@ -7,14 +7,15 @@ import PlaygroundRenderSettings from '../components/PlaygroundRenderSettings'; import PlaygroundPreview from '../components/PlaygroundPreview'; import PlaygroundCodeEditor from '../components/PlaygroundCodeEditor'; import toast, { Toaster } from 'react-hot-toast'; -import { buildRequestSceneObject, handleRenderImageRequest } from '../api'; +import { renderImage } from '../api'; -const BACKEND_URL: URL = new URL('http://localhost:8081'); +const INITIAL_SCENE = { + type: 'view', +}; +const INITIAL_SCENE_STRING = JSON.stringify(INITIAL_SCENE, null, 2); -function Homepage(): JSX.Element { - const [scene, setScene] = useState({ - type: 'view', - }); +function Homepage() { + const [scene, setScene] = useState(INITIAL_SCENE); const [responseData, setResponseData] = useState({ imageUrl: '', @@ -25,15 +26,12 @@ function Homepage(): JSX.Element { setResponseData(prevResponseData => ({ ...prevResponseData, errorMessage: message })); }; - const handleCodeEditorChange = (scene: object | Error): void => { - setScene(scene); - }; - const handleSubmit = async (): Promise => { try { - const requestObject = buildRequestSceneObject('POST', scene); - - const blob = await handleRenderImageRequest(BACKEND_URL, requestObject); + if (scene instanceof Error) { + throw new Error(`${scene.name};\n${scene.message}`); + } + const blob = await renderImage({scene}); const imageObjectURL = URL.createObjectURL(blob); setResponseData({ imageUrl: imageObjectURL, errorMessage: '' }); @@ -46,16 +44,16 @@ function Homepage(): JSX.Element { return (
-
+
- +
@@ -65,7 +63,7 @@ function Homepage(): JSX.Element { ); } -export default function Home(): JSX.Element { +export default function Home() { const { siteConfig } = useDocusaurusContext(); return ( diff --git a/docs/static/img/broadcasting.jpg b/docs/static/img/broadcasting.jpg deleted file mode 100644 index 243d0a51c..000000000 Binary files a/docs/static/img/broadcasting.jpg and /dev/null differ diff --git a/docs/static/img/broadcasting.webp b/docs/static/img/broadcasting.webp new file mode 100644 index 000000000..76490139a Binary files /dev/null and b/docs/static/img/broadcasting.webp differ diff --git a/docs/static/img/how_it_works.png b/docs/static/img/how_it_works.png deleted file mode 100644 index 0fd260236..000000000 Binary files a/docs/static/img/how_it_works.png and /dev/null differ diff --git a/docs/static/img/how_it_works.webp b/docs/static/img/how_it_works.webp new file mode 100644 index 000000000..822f1cccd Binary files /dev/null and b/docs/static/img/how_it_works.webp differ diff --git a/docs/static/img/logo.webp b/docs/static/img/logo.webp new file mode 100644 index 000000000..ad72907d5 Binary files /dev/null and b/docs/static/img/logo.webp differ diff --git a/docs/static/img/showcase_poster.jpg b/docs/static/img/showcase_poster.jpg deleted file mode 100644 index 594019f21..000000000 Binary files a/docs/static/img/showcase_poster.jpg and /dev/null differ diff --git a/docs/static/img/showcase_poster.webp b/docs/static/img/showcase_poster.webp new file mode 100644 index 000000000..902c0cca5 Binary files /dev/null and b/docs/static/img/showcase_poster.webp differ diff --git a/docs/static/img/streaming.jpg b/docs/static/img/streaming.jpg deleted file mode 100644 index efcd48651..000000000 Binary files a/docs/static/img/streaming.jpg and /dev/null differ diff --git a/docs/static/img/streaming.webp b/docs/static/img/streaming.webp new file mode 100644 index 000000000..55dc83075 Binary files /dev/null and b/docs/static/img/streaming.webp differ diff --git a/docs/static/img/videoconferencing.jpg b/docs/static/img/videoconferencing.jpg deleted file mode 100644 index 0642d5793..000000000 Binary files a/docs/static/img/videoconferencing.jpg and /dev/null differ diff --git a/docs/static/img/videoconferencing.webp b/docs/static/img/videoconferencing.webp new file mode 100644 index 000000000..ce5c39a22 Binary files /dev/null and b/docs/static/img/videoconferencing.webp differ diff --git a/docs/static/robots.txt b/docs/static/robots.txt new file mode 100644 index 000000000..0fdebbdf8 --- /dev/null +++ b/docs/static/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: + +Sitemap: https://compositor.live/sitemap.xml