Commit 3af09475 authored by luopl's avatar luopl
Browse files

"Initial commit"

parents
Pipeline #3140 canceled with stages
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import App from '@/App.tsx';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Toolbar from '@/common/components/toolbar/Toolbar';
import DemoVideoEditor from '@/common/components/video/editor/DemoVideoEditor';
import useInputVideo from '@/common/components/video/useInputVideo';
import StatsView from '@/debug/stats/StatsView';
import {VideoData} from '@/demo/atoms';
import DemoPageLayout from '@/layouts/DemoPageLayout';
import {DemoPageQuery} from '@/routes/__generated__/DemoPageQuery.graphql';
import {useEffect, useMemo} from 'react';
import {graphql, useLazyLoadQuery} from 'react-relay';
import {Location, useLocation} from 'react-router-dom';
type LocationState = {
video?: VideoData;
};
export default function DemoPage() {
const {state} = useLocation() as Location<LocationState>;
const data = useLazyLoadQuery<DemoPageQuery>(
graphql`
query DemoPageQuery {
defaultVideo {
path
posterPath
url
posterUrl
height
width
}
}
`,
{},
);
const {setInputVideo} = useInputVideo();
const video = useMemo(() => {
return state?.video ?? data.defaultVideo;
}, [state, data]);
useEffect(() => {
setInputVideo(video);
}, [video, setInputVideo]);
return (
<DemoPageLayout>
<StatsView />
<Toolbar />
<DemoVideoEditor video={video} />
</DemoPageLayout>
);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import LoadingStateScreen from '@/common/loading/LoadingStateScreen';
import DemoPage from '@/routes/DemoPage';
import stylex from '@stylexjs/stylex';
import {isFirefox} from 'react-device-detect';
const styles = stylex.create({
link: {
textDecorationLine: 'underline',
color: '#A7B3BF',
},
});
const REQUIRED_WINDOW_APIS = ['VideoEncoder', 'VideoDecoder', 'VideoFrame'];
function isBrowserSupported() {
for (const api of REQUIRED_WINDOW_APIS) {
if (!(api in window)) {
return false;
}
}
// Test if transferControlToOffscreen is supported. For example, this will
// fail on iOS version < 16.4
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/transferControlToOffscreen
const canvas = document.createElement('canvas');
if (typeof canvas.transferControlToOffscreen !== 'function') {
return false;
}
return true;
}
export default function DemoPageWrapper() {
const isBrowserUnsupported = !isBrowserSupported();
if (isBrowserUnsupported && isFirefox) {
const nightlyUrl = 'https://wiki.mozilla.org/Nightly';
return (
<LoadingStateScreen
title="Sorry Firefox!"
description={
<div>
This version of Firefox doesn’t support the video features we’ll
need to run this demo. You can either update Firefox to the latest
nightly build{' '}
<a
{...stylex.props(styles.link)}
href={nightlyUrl}
target="_blank"
rel="noreferrer">
here
</a>
, or try again using Chrome or Safari.
</div>
}
linkProps={{to: '..', label: 'Back to homepage'}}
/>
);
}
if (isBrowserUnsupported) {
return (
<LoadingStateScreen
title="Uh oh, this browser isn’t supported."
description="This browser doesn’t support the video features we’ll need to run this demo. Try again using Chrome, Safari, or Firefox Nightly."
linkProps={{to: '..', label: 'Back to homepage'}}
/>
);
}
return <DemoPage />;
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import LoadingStateScreen from '@/common/loading/LoadingStateScreen';
export default function PageNotFoundPage() {
return (
<LoadingStateScreen
title="Page not found"
description="It looks like you might be in the wrong place."
linkProps={{
to: '..',
label: 'Click here to access the SAM 2 Demo',
}}
/>
);
}
/**
* @generated SignedSource<<f457eacd20a61cba601921caee2a18f5>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest, Query } from 'relay-runtime';
export type DemoPageQuery$variables = Record<PropertyKey, never>;
export type DemoPageQuery$data = {
readonly defaultVideo: {
readonly height: number;
readonly path: string;
readonly posterPath: string | null | undefined;
readonly posterUrl: string;
readonly url: string;
readonly width: number;
};
};
export type DemoPageQuery = {
response: DemoPageQuery$data;
variables: DemoPageQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"alias": null,
"args": null,
"concreteType": "Video",
"kind": "LinkedField",
"name": "defaultVideo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "path",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "posterPath",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "url",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "posterUrl",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "height",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "width",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "DemoPageQuery",
"selections": (v0/*: any*/),
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [],
"kind": "Operation",
"name": "DemoPageQuery",
"selections": (v0/*: any*/)
},
"params": {
"cacheID": "71cbafce4d2d047acdc54d86504f2d2e",
"id": null,
"metadata": {},
"name": "DemoPageQuery",
"operationKind": "query",
"text": "query DemoPageQuery {\n defaultVideo {\n path\n posterPath\n url\n posterUrl\n height\n width\n }\n}\n"
}
};
})();
(node as any).hash = "63c9465d78b30d42d6fc11e50a9af142";
export default node;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Tooltip from '@/common/components/Tooltip';
import {ArrowPathIcon, CheckIcon, XMarkIcon} from '@heroicons/react/24/solid';
import {ChangeEvent, KeyboardEvent, useEffect, useMemo, useState} from 'react';
import {Button, Form, Input, Join} from 'react-daisyui';
type Props<T extends string | number> = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'size' | 'color' | 'onChange'
> & {
label: string;
defaultValue: T;
initialValue: T;
onChange: (value: string) => void;
};
function getStep(value: number) {
const stringValue = String(value);
const decimals = stringValue.split('.')[1];
if (decimals != null) {
// Not using 0.1 ** decimals.length because this will result in rounding
// errors, e.g., 0.1 ** 2 => 0.010000000000000002.
return 1 / 10 ** decimals.length;
}
return 1;
}
export default function ApprovableInput<T extends string | number>({
label,
defaultValue,
initialValue,
onChange,
...otherProps
}: Props<T>) {
const [value, setValue] = useState<string>(`${initialValue}`);
useEffect(() => {
setValue(`${initialValue}`);
}, [initialValue]);
const step = useMemo(() => {
return typeof defaultValue === 'number' && isFinite(defaultValue)
? getStep(defaultValue)
: undefined;
}, [defaultValue]);
return (
<div>
<Form.Label className="flex-col items-start gap-2" title={label}>
<Join className="w-full">
<Input
{...otherProps}
className="w-full join-item"
value={value}
step={step}
placeholder={`${defaultValue}`}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
}}
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
event.preventDefault();
onChange(value);
}
}}
/>
<Tooltip message="Reset to default">
<Button
className="join-item"
onClick={event => {
event.preventDefault();
setValue(`${defaultValue}`);
}}>
<ArrowPathIcon className="h-4 w-4" />
</Button>
</Tooltip>
<Tooltip message="Revert change">
<Button
className="join-item"
color="neutral"
disabled={initialValue == value}
onClick={event => {
event.preventDefault();
setValue(`${initialValue}`);
}}>
<XMarkIcon className="h-4 w-4" />
</Button>
</Tooltip>
<Tooltip message="Apply change">
<Button
className="join-item"
color="primary"
disabled={initialValue == value}
onClick={event => {
event.preventDefault();
onChange(value);
}}>
<CheckIcon className="h-4 w-4" />
</Button>
</Tooltip>
</Join>
</Form.Label>
</div>
);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {INFERENCE_API_ENDPOINT, VIDEO_API_ENDPOINT} from '@/demo/DemoConfig';
import ApprovableInput from '@/settings/ApprovableInput';
import useSettingsContext from '@/settings/useSettingsContext';
export default function SAMVSettings() {
const {settings, dispatch} = useSettingsContext();
return (
<div>
<ApprovableInput
label="Video API Endpoint"
defaultValue={VIDEO_API_ENDPOINT}
initialValue={settings.videoAPIEndpoint}
onChange={url => dispatch({type: 'change-video-api-endpoint', url})}
/>
<ApprovableInput
label="Inference API Endpoint"
defaultValue={INFERENCE_API_ENDPOINT}
initialValue={settings.inferenceAPIEndpoint}
onChange={url => dispatch({type: 'change-inference-api-endpoint', url})}
/>
</div>
);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import emptyFunction from '@/common/utils/emptyFunction';
import {INFERENCE_API_ENDPOINT, VIDEO_API_ENDPOINT} from '@/demo/DemoConfig';
import SettingsModal from '@/settings/SettingsModal';
import {
Action,
DEFAULT_SETTINGS,
Settings,
settingsReducer,
} from '@/settings/SettingsReducer';
import {
PropsWithChildren,
createContext,
useCallback,
useMemo,
useRef,
} from 'react';
import {useImmerReducer} from 'use-immer';
type ContextProps = {
settings: Settings;
dispatch: React.Dispatch<Action>;
openModal: () => void;
closeModal: () => void;
hasChanged: boolean;
};
export const SettingsContext = createContext<ContextProps>({
settings: DEFAULT_SETTINGS,
dispatch: emptyFunction,
openModal: emptyFunction,
closeModal: emptyFunction,
hasChanged: false,
});
type Props = PropsWithChildren;
export default function SettingsContextProvider({children}: Props) {
const [state, dispatch] = useImmerReducer(
settingsReducer,
DEFAULT_SETTINGS,
settings => {
// Load the settings from local storage. Eventually use the reducer init
// to handle initial loading.
return settingsReducer(settings, {type: 'load-state'});
},
);
const modalRef = useRef<HTMLDialogElement>(null);
const openModal = useCallback(() => {
modalRef.current?.showModal();
}, [modalRef]);
const handleCloseModal = useCallback(() => {
modalRef.current?.close();
}, [modalRef]);
const hasChanged = useMemo(() => {
return (
VIDEO_API_ENDPOINT !== state.videoAPIEndpoint ||
INFERENCE_API_ENDPOINT !== state.inferenceAPIEndpoint
);
}, [state.videoAPIEndpoint, state.inferenceAPIEndpoint]);
const value = useMemo(
() => ({
settings: state,
dispatch,
openModal,
closeModal: handleCloseModal,
hasChanged,
}),
[state, dispatch, openModal, handleCloseModal, hasChanged],
);
return (
<SettingsContext.Provider value={value}>
{children}
<SettingsModal ref={modalRef} />
</SettingsContext.Provider>
);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {DEMO_FRIENDLY_NAME} from '@/demo/DemoConfig';
import SAM2Settings from '@/settings/SAM2Settings';
import {XMarkIcon} from '@heroicons/react/24/solid';
import {forwardRef, useState} from 'react';
import {Button, Modal} from 'react-daisyui';
import useSettingsContext from './useSettingsContext';
type Props = unknown;
type Config = {
key: 'sam2';
title: string;
component: React.ElementType;
};
const SettingsConfig: Config[] = [
{
key: 'sam2',
title: DEMO_FRIENDLY_NAME,
component: SAM2Settings,
},
];
export default forwardRef<HTMLDialogElement, Props>(
function SettingsModal(_props, ref) {
const {closeModal} = useSettingsContext();
const [activeConfig, setActiveConfig] = useState<Config>(SettingsConfig[0]);
const SettingsComponent = activeConfig.component;
return (
<Modal
data-testid="settings-modal"
ref={ref}
className="lg:absolute lg:top-10 lg:w-11/12 lg:max-w-4xl flex flex-col"
responsive={true}>
<Button
size="sm"
color="ghost"
shape="circle"
className="absolute right-2 top-2"
startIcon={<XMarkIcon className="w-6 h-6" />}
onClick={closeModal}
/>
<Modal.Header className="font-bold">Settings</Modal.Header>
<Modal.Body className="flex flex-col grow overflow-hidden">
<div className="flex flex-col md:lg:flex-row gap-4 md:lg:gap-12 overflow-hidden">
<div className="flex flex-row shrink-0 md:lg:flex-col gap-4 md:lg:py-2 overflow-x-auto">
{SettingsConfig.map(config => (
<div
key={config.key}
data-testid={`show-settings-${config.key}`}
className={`cursor-pointer whitespace-nowrap ${
activeConfig.key === config.key && 'text-primary'
} ${
activeConfig.key === config.key &&
'sm:underline md:lg:no-underline sm:underline-offset-4'
}`}
onClick={() => setActiveConfig(config)}>
{config.title}
</div>
))}
</div>
<div
data-testid={`settings-${activeConfig.key}`}
className="overflow-hidden overflow-y-auto grow md:lg:pt-2">
<div className="flex flex-col grow-0 flex-1">
<h1 className="hidden md:lg:block">{activeConfig.title}</h1>
<SettingsComponent />
</div>
</div>
</div>
</Modal.Body>
<Modal.Actions className="shrink-0">
<Button onClick={closeModal}>Close</Button>
</Modal.Actions>
</Modal>
);
},
);
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {INFERENCE_API_ENDPOINT, VIDEO_API_ENDPOINT} from '@/demo/DemoConfig';
export type Settings = {
videoAPIEndpoint: string;
inferenceAPIEndpoint: string;
};
// Key used to store the settings in the browser's local storage.
export const SAM2_SETTINGS_KEY = 'SAM2_SETTINGS_KEY';
export type Action =
| {type: 'load-state'}
| {type: 'change-video-api-endpoint'; url: string}
| {type: 'change-inference-api-endpoint'; url: string};
export const DEFAULT_SETTINGS: Settings = {
videoAPIEndpoint: VIDEO_API_ENDPOINT,
inferenceAPIEndpoint: INFERENCE_API_ENDPOINT,
};
export function settingsReducer(state: Settings, action: Action): Settings {
function storeSettings(newState: Settings): void {
localStorage.setItem(SAM2_SETTINGS_KEY, JSON.stringify(newState));
}
switch (action.type) {
case 'load-state': {
try {
const serializedSettings = localStorage.getItem(SAM2_SETTINGS_KEY);
if (serializedSettings != null) {
return JSON.parse(serializedSettings) as Settings;
} else {
// Store default settings in local storage. This will populate the
// settings in the local storage on first app load or when user
// cleared the browser cache.
storeSettings(state);
}
} catch {
// Could not parse settings. Using default settings instead.
}
return state;
}
case 'change-video-api-endpoint':
state.videoAPIEndpoint = action.url;
break;
case 'change-inference-api-endpoint':
state.inferenceAPIEndpoint = action.url;
break;
}
// Store the settings state on every change
storeSettings(state);
return state;
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {useContext} from 'react';
import {SettingsContext} from '@/settings/SettingsContextProvider';
export default function useSettingsContext() {
return useContext(SettingsContext);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const THEME_COLORS = [
'#3880F3',
'#F0AA19',
'#00D2BE',
'#28D232',
'#8773FF',
'#00C8F0',
'#FA8719',
'#E6193B',
'#FA7DC8',
];
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const BLUE_PINK_FILL = 'from-[#595FEF] from-40% to-[#FB73A5]';
export const BLUE_PINK_FILL_BR =
'bg-gradient-to-br from-[#595FEF] from-30% to-[#FB73A5]';
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as stylex from '@stylexjs/stylex';
export const spacing = stylex.defineVars({
'0': '0rem',
'0.5': '0.125rem',
'1': '0.25rem',
'1.5': '0.375rem',
'2': '0.5rem',
'2.5': '0.625rem',
'3': '0.75rem',
'3.5': '0.875rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'7': '1.75rem',
'8': '2rem',
'9': '2.25rem',
'10': '2.5rem',
'11': '2.75rem',
'12': '3rem',
});
export const gap = stylex.defineVars({
4: '1rem' /* 16px */,
});
export const w = stylex.defineVars({
full: '100%',
12: '3rem' /* 48px */,
96: '24rem' /* 384px */,
});
export const m = stylex.defineVars({
3: '0.75rem' /* 12px */,
});
export const fontSize = stylex.defineVars({
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
});
export const fontWeight = stylex.defineVars({
thin: 100,
extralight: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
});
export const color = stylex.defineVars({
subtitle: 'rgb(107 114 128)',
'gray-900': 'rgb(17 24 39)',
'gray-800': 'rgb(26 28 31)',
'gray-700': 'rgb(55 62 65)',
'blue-600': 'rgb(37 99 235)',
});
export const screenSizes = {
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
'2xl': 1536,
};
export const borderRadius = stylex.defineVars({
sm: '0.125rem',
md: '0.375rem',
lg: '0.5rem',
xl: '0.75rem',
});
export const top = stylex.defineVars({
0: 0,
1: '0.25rem' /* 4px */,
2: '0.5rem' /* 8px */,
});
export const right = stylex.defineVars({
0: 0,
1: '0.25rem' /* 4px */,
2: '0.5rem' /* 8px */,
});
export const gradients = stylex.defineVars({
rainbow:
'linear-gradient(#000, #000) padding-box, linear-gradient(to right bottom, #FB73A5,#595FEF,#94EAE2,#FCCB6B) border-box',
rainbowReverse:
'linear-gradient(#000, #000) padding-box, linear-gradient(to left top, #FB73A5,#595FEF,#94EAE2,#FCCB6B) border-box',
yellowTeal:
'linear-gradient(#000, #000) padding-box, linear-gradient(to right bottom, #94EAE2,#FCCB6B) border-box',
});
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'mp4box' {
export interface MP4MediaTrackEdit {
media_rate_fraction: number;
media_rate_integer: number;
media_time: number;
segment_duration: number;
}
export interface MP4MediaTrack {
id: number;
created: Date;
modified: Date;
movie_duration: number;
movie_timescale: number;
layer: number;
alternate_group: number;
volume: number;
track_width: number;
track_height: number;
timescale: number;
duration: number;
bitrate: number;
codec: string;
language: string;
nb_samples: number;
samples_duration: number;
edits: MP4MediaTrackEdit[];
}
export interface MP4VideoData {
width: number;
height: number;
}
export interface MP4VideoTrack extends MP4MediaTrack {
video: MP4VideoData;
}
export interface MP4AudioData {
sample_rate: number;
channel_count: number;
sample_size: number;
}
export interface MP4AudioTrack extends MP4MediaTrack {
audio: MP4AudioData;
}
export type MP4Track = MP4VideoTrack | MP4AudioTrack;
export interface MP4Info {
duration: number;
timescale: number;
fragment_duration: number;
isFragmented: boolean;
isProgressive: boolean;
hasIOD: boolean;
brands: string[];
created: Date;
modified: Date;
tracks: MP4Track[];
audioTracks: MP4AudioTrack[];
videoTracks: MP4VideoTrack[];
otherTracks: MP4VideoTrack[];
}
export interface MP4Sample {
alreadyRead: number;
chunk_index: number;
chunk_run_index: number;
cts: number;
data: Uint8Array;
degradation_priority: number;
depends_on: number;
description: unknown;
description_index: number;
dts: number;
duration: number;
has_redundancy: number;
is_depended_on: number;
is_leading: number;
is_sync: boolean;
number: number;
offset: number;
size: number;
timescale: number;
track_id: number;
}
export type MP4ArrayBuffer = ArrayBuffer & {fileStart: number};
export class DataStream {
static BIG_ENDIAN: boolean;
static LITTLE_ENDIAN: boolean;
buffer: ArrayBuffer;
constructor(
arrayBuffer?: ArrayBuffer,
byteOffset: number,
endianness: boolean,
): void;
}
export interface Trak {
mdia?: {
minf?: {
stbl?: {
stsd?: {
entries: {
avcC?: {
write: (stream: DataStream) => void;
};
hvcC?: {
write: (stream: DataStream) => void;
};
}[];
};
};
};
};
}
export namespace BoxParser {
export class Box {
size?: number;
data?: Uint8Array;
constructor(type?: string, size?: number);
add(name: string): Box;
addBox(box: Box): Box;
addEntry(value: string, prop?: string): void;
write(stream: DataStream): void;
writeHeader(stream: DataStream, msg?: string): void;
computeSize(): void;
}
export class ContainerBox extends Box {}
export class avcCBox extends ContainerBox {}
export class hvcCBox extends ContainerBox {}
export class vpcCBox extends ContainerBox {}
export class av1CBox extends ContainerBox {}
}
export interface TrackOptions {
id?: number;
type?: string;
width?: number;
height?: number;
duration?: number;
layer?: number;
timescale?: number;
media_duration?: number;
language?: string;
hdlr?: string;
// video
avcDecoderConfigRecord?: BufferSource;
// audio
balance?: number;
channel_count?: number;
samplesize?: number;
samplerate?: number;
//captions
namespace?: string;
schema_location?: string;
auxiliary_mime_types?: string;
description?: BoxParser.Box;
description_boxes?: BoxParser.Box[];
default_sample_description_index_id?: number;
default_sample_duration?: number;
default_sample_size?: number;
default_sample_flags?: number;
}
export interface SampleOptions {
sample_description_index?: number;
duration?: number;
cts?: number;
dts?: number;
is_sync?: boolean;
is_leading?: number;
depends_on?: number;
is_depended_on?: number;
has_redundancy?: number;
degradation_priority?: number;
}
export interface Sample {
number: number;
track_id: number;
timescale: number;
description_index: number;
description: {
avcC?: BoxParser.avcCBox; // h.264
hvcC?: BoxParser.hvcCBox; // hevc
vpcC?: BoxParser.vpcCBox; // vp9
av1C?: BoxParser.av1CBox; // av1
};
data: ArrayBuffer;
size: number;
alreadyRead?: number;
duration: number;
cts: number;
dts: number;
is_sync: boolean;
is_leading?: number;
depends_on?: number;
is_depended_on?: number;
has_redundancy?: number;
degradation_priority?: number;
offset?: number;
}
export interface MP4File {
getBuffer(): MP4ArrayBuffer;
addTrack(options?: TrackOptions): number;
addSample(
track: number,
data: ArrayBuffer,
options?: SampleOptions,
): Sample;
addSample(
trackID: number,
uint8: Uint8Array,
arg2: {duration: number; is_sync: boolean},
): void;
onMoovStart?: () => void;
onReady?: (info: MP4Info) => void;
onError?: (e: string) => void;
onSamples?: (id: number, user: unknown, samples: MP4Sample[]) => unknown;
appendBuffer(data: MP4ArrayBuffer): number;
save(fileName: string): void;
start(): void;
stop(): void;
/**
* Indicates that the next samples to process (for extraction or
* segmentation) start at the given time (Number, in seconds) or at the
* time of the previous Random Access Point (if useRap is true, default
* is false). Returns the offset in the file of the next bytes to be
* provided via appendBuffer.
*
* @param time - Start at the given time (Number, in seconds)
* @param useRap - Random Access Point (if useRap is true, default is false)
* @returns Returns the offset in the file of the next bytes to be provided via appendBuffer.
*/
seek: (time: number, useRap: boolean) => number;
flush(): void;
releaseUsedSamples(trackId: number, sampleNumber: number): void;
setExtractionOptions(
trackId: number,
user?: unknown,
options?: {nbSamples?: number; rapAlignment?: number},
): void;
getTrackById(trackId: number): Trak;
}
export function createFile(): MP4File;
export {};
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="vite/client" />
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import tailwindCSSTypography from '@tailwindcss/typography';
import daisyui from 'daisyui';
import * as daisyColorThemes from 'daisyui/src/theming/themes';
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'node_modules/daisyui/dist/**/*.js',
'node_modules/react-daisyui/dist/**/*.js',
],
daisyui: {
styled: true,
themes: [
{
light: {
...daisyColorThemes['[data-theme=light]'],
'base-100': '#FFFFFF',
'base-200': '#F1F4F7',
'base-300': '#DEE3E9',
primary: '#0064E0',
'primary-content': '#FFFFFF',
secondary: '#0F191E',
'secondary-content': '#FFFFFF',
accent: '#6441D2',
'accent-content': '#FFFFFF',
info: '#009B9B',
'info-content': '#FFFFFF',
success: '#0F9B14',
'success-content': '#FFFFFF',
warning: '#FA8719',
'warning-content': '#FFFFFF',
error: '#C80A28',
'error-content': '#FFFFFF',
'--rounded-box': '0.35rem', // border radius rounded-box utility class, used in card and other large boxes
'--rounded-btn': '0.35rem', // border radius rounded-btn utility class, used in buttons and similar element
'--rounded-badge': '1rem', // border radius rounded-badge utility class, used in badges and similar
},
},
'dark',
],
},
theme: {
fontSize: {
xs: ['0.75rem', {lineHeight: '1.5'}],
sm: ['0.875rem', {lineHeight: '1.5'}],
base: ['1rem', {lineHeight: '1.5'}],
lg: ['1.125rem', {lineHeight: '1.2', fontWeight: 500}],
xl: ['1.25rem', {lineHeight: '1.2', fontWeight: 500}],
'2xl': [
'1.5rem',
{lineHeight: '1.2', fontWeight: 500, letterSpacing: '0.005rem'},
],
'3xl': [
'2.25rem',
{lineHeight: '1.2', fontWeight: 500, letterSpacing: '0.01rem'},
],
'4xl': [
'3rem',
{lineHeight: '1.2', fontWeight: 500, letterSpacing: '0.016rem'},
],
'5xl': [
'4rem',
{lineHeight: '1.2', fontWeight: 400, letterSpacing: '0.016rem'},
],
'6xl': [
'5rem',
{lineHeight: '1.2', fontWeight: 400, letterSpacing: '0.016rem'},
],
},
extend: {
colors: {
graydark: {
50: '#f1f4f7',
100: '#DEE3E9',
200: '#CBD2D9',
300: '#A7B3BF',
400: '#8595A4',
500: '#667788',
600: '#465A69',
700: '#343845',
800: '#1A1C1F',
900: '#0F191E',
},
},
lineHeight: {
tight: 1.2,
},
backgroundImage: {
dot: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAABdJREFUGBljYGBg+A/FQAoTMGEKDUcRAATwAgFGIXEOAAAAAElFTkSuQmCC)',
},
keyframes: {
wiggle: {
'0%, 100%': {transform: 'rotate(-3deg)'},
'50%': {transform: 'rotate(3deg)'},
},
},
animation: {
wiggle: 'wiggle .25s ease-in-out',
},
typography: {
DEFAULT: {
css: {
maxWidth: '100%', // add required value here
a: {
textDecoration: 'none',
},
},
},
},
},
},
plugins: [tailwindCSSTypography, daisyui],
};
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable", "webworker"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true, // esModuleInterop true required for Jest
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": "./src",
"paths": {
"mp4box": ["types/mp4box"],
"@/*": ["*"]
}
},
"include": ["src"],
"references": [{"path": "./tsconfig.node.json"}]
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"target": "ES2017",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strictNullChecks": true
},
"include": ["vite.config.ts", "schemas"]
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import react from '@vitejs/plugin-react';
import jotaiDebugLabel from 'jotai/babel/plugin-debug-label';
import jotaiReactRefresh from 'jotai/babel/plugin-react-refresh';
import path from 'path';
import {defineConfig} from 'vite';
import babel from 'vite-plugin-babel';
import relay from 'vite-plugin-relay';
import {stylexPlugin} from 'vite-plugin-stylex-dev';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
plugins: [
react({
babel: {
plugins: [jotaiDebugLabel, jotaiReactRefresh],
},
}),
stylexPlugin(),
relay,
babel(),
],
worker: {
plugins: () => [relay],
},
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment