From c60e2ae87a46977ae98b934ca8e4ffd2f9730d03 Mon Sep 17 00:00:00 2001 From: Robin Fernandes Date: Thu, 20 Jul 2023 17:20:17 +1000 Subject: [PATCH] Redo darkmode using MUI's latest CSS variables approach. MUI does more of the work, no need to pass darkMode around. --- src/Analyser.tsx | 10 +-- src/App.tsx | 44 ++++++++- src/Browser.tsx | 24 +++-- src/Deforum.tsx | 41 +-------- src/DocManager.tsx | 17 ++-- src/FunctionDoc.tsx | 30 +++---- src/Labs.tsx | 17 ++-- src/ParseqUI.js | 57 +++++++----- src/UserSettings.ts | 14 ++- src/components/AudioWaveform.tsx | 6 +- src/components/GridToolTip.tsx | 6 +- src/components/Header.tsx | 55 +++++++++--- src/components/KeyframeDialogs.tsx | 8 +- src/components/MovementPreview.tsx | 7 +- src/components/ParseqGraph.tsx | 38 +++++++- src/components/ParseqGrid.tsx | 137 +++++++++++++++-------------- src/components/Prompts.tsx | 35 ++++---- src/components/StyledSwitch.tsx | 4 +- src/components/SupportParseq.tsx | 9 +- src/components/TimeSeriesUI.tsx | 3 +- src/components/UploadButton.tsx | 4 +- src/index.css | 38 ++++++-- src/index.js | 25 +----- src/robin.css | 31 ------- src/theme.ts | 74 ++++++++++++++++ 25 files changed, 425 insertions(+), 309 deletions(-) delete mode 100644 src/robin.css create mode 100644 src/theme.ts diff --git a/src/Analyser.tsx b/src/Analyser.tsx index 10dc5cd..7c4f356 100644 --- a/src/Analyser.tsx +++ b/src/Analyser.tsx @@ -1,11 +1,9 @@ -import { Alert, Box, Button, InputAdornment, MenuItem, Slider, Stack, TextField, Typography } from "@mui/material"; -import CssBaseline from '@mui/material/CssBaseline'; +import { Link, Alert, Box, Button, InputAdornment, MenuItem, Slider, Stack, TextField, Typography } from "@mui/material"; import Grid from '@mui/material/Unstable_Grid2'; import { PitchMethod } from "aubiojs"; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useSearchParams } from "react-router-dom"; import { WaveForm, WaveSurfer } from "wavesurfer-react"; -import Header from './components/Header'; import LinearWithValueLabel from "./components/LinearProgressWithLabel"; //@ts-ignore import TimelinePlugin from "wavesurfer.js/dist/plugin/wavesurfer.timeline.min"; @@ -625,7 +623,6 @@ export default function Analyser() { } return <> -
- - ⬅️ Home + ⬅️ Home
  • This legacy audio analyser is deprecated and will eventually be removed. All its functionality is now integrated into the main Parseq UI.
  • ⚠️ This feature is experimental. That's why it's quite separate from the main Parseq UI for now. The keyframes generated here can be merged into an existing Parseq document using the "Merge keyframes" button in the main UI.
  • -
  • Tempo, onset event and pitch detection use Aubio, via AubioJS. See the Aubio CLI documentation for the meaning of all parameters.
  • +
  • Tempo, onset event and pitch detection use Aubio, via AubioJS. See the Aubio CLI documentation for the meaning of all parameters.
  • Not all parameters are exposed by AubioJS. Some look like they should be, but aren't (those are grayed out here).
  • All processing runs in the browser, using web workers. This seems to be faster in Chrome and Safari compared to Firefox. You can speed things up by increasing the hop sizes to larger multiples of 2 (trading off accuracy).
  • Expects a constant Tempo. Tempo detection is not perfect, so you can override it before generating keyframes. If the first beat is not at the very beginning of the track, you will need to enter a manual offset for now.
  • diff --git a/src/App.tsx b/src/App.tsx index 69f0d12..90faf90 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,46 @@ +import { CssBaseline, GlobalStyles } from "@mui/material"; +import { Experimental_CssVarsProvider as CssVarsProvider, experimental_extendTheme as extendTheme } from "@mui/material/styles"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import Analyser from "./Analyser"; +import Browser from "./Browser"; +import Deforum from "./Deforum"; +import FunctionDoc from "./FunctionDoc"; +import Labs from "./Labs"; +import Raw from "./Raw"; import Header from "./components/Header"; +import { themeFactory } from "./theme"; const App = () => { - return <> -
    - Go home. - ; + + const theme = extendTheme(themeFactory()); + + return + + + +
    + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ; }; export default App; diff --git a/src/Browser.tsx b/src/Browser.tsx index b1bf071..c2465aa 100644 --- a/src/Browser.tsx +++ b/src/Browser.tsx @@ -1,4 +1,4 @@ -import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Stack, Tab, Tabs, Tooltip, Typography } from '@mui/material'; +import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Link, Stack, Tab, Tabs, Tooltip, Typography } from '@mui/material'; import Paper from '@mui/material/Paper'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; @@ -9,27 +9,24 @@ import TableRow from '@mui/material/TableRow'; import { faBroom, faCopy, faDownload, faTrash, faUpload } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import CssBaseline from '@mui/material/CssBaseline'; import Grid from '@mui/material/Unstable_Grid2'; import { ImportOptions, exportDB, importInto, peakImportFile } from "dexie-export-import"; import { useLiveQuery } from "dexie-react-hooks"; import { saveAs } from 'file-saver'; import _ from 'lodash'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import prettyBytes from 'pretty-bytes'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useSearchParams } from "react-router-dom"; import ReactTimeAgo from 'react-time-ago'; import { useEffectOnce } from 'react-use'; import { AdvancedParseqPrompt, AdvancedParseqPromptsV2, DocId, ParseqDoc, ParseqDocVersion, VersionId } from './ParseqUI'; -import Header from './components/Header'; import LinearProgressWithLabel from './components/LinearProgressWithLabel'; +import { convertPrompts } from './components/Prompts'; import { SmallTextField } from './components/SmallTextField'; import { TabPanel } from './components/TabPanel'; import { db } from './db'; -import { isStoragePersisted, showEstimatedQuota, persist } from './persistance'; +import { isStoragePersisted, persist, showEstimatedQuota } from './persistance'; import { navigateToClone, smartTrim } from './utils/utils'; -import prettyBytes from 'pretty-bytes'; -import React from 'react'; -import { convertPrompts } from './components/Prompts'; function VersionCount({ docId }: { docId: DocId }) { const [versionCount, setVersionCount] = useState(loading...); @@ -267,11 +264,10 @@ export default function Browser() { return -
    - + - ⬅️ Home + ⬅️ Home setActiveTab(newValue)}> @@ -293,7 +289,7 @@ export default function Browser() {
    • Showing {selectedDocVersions?.length} versions for document: {selectedDocVersions[0]?.docName}
    • -
    • Back to recent docs
    • +
    • Back to recent docs
    @@ -405,7 +401,7 @@ export default function Browser() { mostRecentVersions.map((v: VersionSummary) => { return - {v.docName} + {v.docName} {v.docId} @@ -421,7 +417,7 @@ export default function Browser() { } {v.timeSeriesNames?.length} - { - - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - const [darkMode, setDarkMode] = useState(prefersDarkMode); - const theme = useMemo(() => createTheme(themeFactory(darkMode)), [darkMode]); - const updateDarkMode = useCallback((darkMode: boolean) => { - setDarkMode(darkMode); - UserSettings.setDarkMode(darkMode); - }, []); - - // Retrieve dark mode setting from local db - UserSettings.getDarkMode().then((darkMode) => { - if (darkMode !== undefined) { - setDarkMode(darkMode); - } - }); - - return ( -
    - + return (<> {/* @ts-ignore */} - - ) + defaultTemplate='catduck'/> + ) } export default Deforum; -function themeFactory(darkMode: boolean): ThemeOptions { - return { - palette: { - mode: darkMode ? 'dark' : 'light', - background: { - default: darkMode ? '#252525' : '#fff', - } - } - } -} \ No newline at end of file diff --git a/src/DocManager.tsx b/src/DocManager.tsx index a8b9a9e..424ffb5 100644 --- a/src/DocManager.tsx +++ b/src/DocManager.tsx @@ -1,4 +1,5 @@ -import { Alert, Autocomplete, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FormControlLabel, Radio, RadioGroup, Stack, TextField, Tooltip, Typography } from '@mui/material'; +/* eslint-disable react/jsx-no-target-blank */ +import { Alert, Autocomplete, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FormControlLabel, Link, Radio, RadioGroup, Stack, TextField, Tooltip, Typography } from '@mui/material'; import CircularProgress from '@mui/material/CircularProgress'; import Grid from '@mui/material/Unstable_Grid2'; import { useLiveQuery } from "dexie-react-hooks"; @@ -19,6 +20,8 @@ import _ from 'lodash'; import { DocId, ParseqDoc, ParseqDocVersion, ParseqPersistableState, VersionId } from './ParseqUI'; import { useUserAuth } from "./UserAuthContext"; import { navigateToClone, navigateToDocId, navigateToTemplateId } from './utils/utils'; +import { experimental_extendTheme as extendTheme } from "@mui/material/styles"; +import { themeFactory } from "./theme"; export const makeDocId = (): DocId => "doc-" + uuidv4() as DocId const makeVersionId = (): VersionId => "version-" + uuidv4() as VersionId @@ -104,6 +107,7 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { const [parseqShareUrl, setParseqShareUrl] = useState(""); const [uploadStatus, setUploadStatus] = useState(defaultUploadStatus); const [loadingStatus, setLoadingStatus] = useState(false); + const theme = extendTheme(themeFactory()); //@ts-ignore - this type check is too deep down for me to figure out right now. const { user } = useUserAuth(); @@ -185,7 +189,7 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { } - Explore versions of this doc in the browser. + Explore versions of this doc in the browser. @@ -360,7 +364,7 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { } /> - Remember the prompt but not the doc name? Try the browser. + Remember the prompt but not the doc name? Try the browser. @@ -430,7 +434,7 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { if (matchRes && matchRes[1]) { setParseqShareUrl(window.location.href.replace(window.location.search, '') + `?importRemote=${matchRes[1]}&token=${token}`); setUploadStatus( -

    Upload successful. Share the URL above to load it directly into Parseq on another system.

    +

    Upload successful. Share the URL above to load it directly into Parseq on another system.

    ); } else { setUploadStatus(Unexpected response path: {url}); @@ -523,7 +527,8 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { value={editingDocName} InputProps={{ style: { fontSize: '0.75em' }, - sx: { background: (editingDocName !== activeDoc.name) ? 'ivory' : '', }, + //@ts-ignore + sx: { background: (editingDocName !== activeDoc.name) ? theme.vars.palette.unsavedbg.main : '', }, endAdornment: (editingDocName !== activeDoc.name) ? '🖊️' : '' }} size="small" @@ -557,7 +562,7 @@ export function DocManagerUI({ docId, onLoadContent, lastSaved }: MyProps) { - + diff --git a/src/FunctionDoc.tsx b/src/FunctionDoc.tsx index 17af096..62058a1 100644 --- a/src/FunctionDoc.tsx +++ b/src/FunctionDoc.tsx @@ -1,19 +1,19 @@ -import { Box, CssBaseline, FormControlLabel, Stack, Typography } from "@mui/material"; +import { Box, FormControlLabel, Link, Stack, Typography } from "@mui/material"; import Grid from '@mui/material/Unstable_Grid2'; -import Header from "./components/Header"; -import { ParseqGrid } from "./components/ParseqGrid"; -import { ParseqGraph } from "./components/ParseqGraph"; -import { GraphableData, ParseqKeyframe, ParseqKeyframes, ParseqPersistableState, RenderedData } from "./ParseqUI"; -import { useCallback, useRef, useState } from "react"; import { AgGridReact } from "ag-grid-react"; -import { parseqRender } from "./parseq-renderer"; import _ from "lodash"; -import functionLibrary, { ArgDef, ParseqFunction } from "./parseq-lang/parseq-lang-functions"; +import { useCallback, useRef, useState } from "react"; +import ReactMarkdown from 'react-markdown'; +import { useSearchParams } from "react-router-dom"; +import { GraphableData, ParseqKeyframe, ParseqKeyframes, ParseqPersistableState, RenderedData } from "./ParseqUI"; import MovementPreview from "./components/MovementPreview"; +import { ParseqGraph } from "./components/ParseqGraph"; +import { ParseqGrid } from "./components/ParseqGrid"; import StyledSwitch from "./components/StyledSwitch"; -import { useSearchParams } from "react-router-dom"; -import ReactMarkdown from 'react-markdown'; - +import functionLibrary, { ArgDef, ParseqFunction } from "./parseq-lang/parseq-lang-functions"; +import { parseqRender } from "./parseq-renderer"; +import { experimental_extendTheme as extendTheme } from "@mui/material/styles"; +import { themeFactory } from "./theme"; type MiniParseqProps = { keyframes: ParseqKeyframes @@ -197,7 +197,7 @@ const FunctionDoc = () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [searchParams, setSearchParams] = useSearchParams(); - + const theme = extendTheme(themeFactory()); const docEntries: DocEntry[] = [ { @@ -919,7 +919,7 @@ const FunctionDoc = () => { return _.chain(docEntries) .groupBy('category') .map((category) => <> - + {category[0].category} { @@ -963,7 +963,6 @@ const FunctionDoc = () => { } return <> -
    { borderColor: 'divider', }, }}> - - ⬅️ Home + ⬅️ Home {renderDocEntries()} diff --git a/src/Labs.tsx b/src/Labs.tsx index 5106ffe..7e1226a 100644 --- a/src/Labs.tsx +++ b/src/Labs.tsx @@ -1,13 +1,12 @@ -import { Box, CssBaseline, Stack } from "@mui/material"; +import { Box, Stack } from "@mui/material"; import Grid from '@mui/material/Unstable_Grid2'; -import Header from "./components/Header"; -import { ParseqGrid } from "./components/ParseqGrid"; -import { ParseqGraph } from "./components/ParseqGraph"; -import { GraphableData, ParseqKeyframe, ParseqKeyframes, ParseqPersistableState, RenderedData } from "./ParseqUI"; -import { useCallback, useRef, useState } from "react"; import { AgGridReact } from "ag-grid-react"; -import { parseqRender } from "./parseq-renderer"; import _ from "lodash"; +import { useCallback, useRef, useState } from "react"; +import { GraphableData, ParseqKeyframe, ParseqKeyframes, ParseqPersistableState, RenderedData } from "./ParseqUI"; +import { ParseqGraph } from "./components/ParseqGraph"; +import { ParseqGrid } from "./components/ParseqGrid"; +import { parseqRender } from "./parseq-renderer"; type MiniParseqProps = { keyframes: ParseqKeyframes @@ -101,7 +100,7 @@ const MiniParseq = ({ keyframes, fields }: MiniParseqProps) => { custom: "", } }, - + commonPromptPos: 'append' }, keyframeLock: "frames" } @@ -184,7 +183,6 @@ const Labs = () => { const singleValinterps = ['vibe(p=1b, c="easeOut6")']; return <> -
    { borderColor: 'divider', }, }}> - <> { diff --git a/src/ParseqUI.js b/src/ParseqUI.js index bdf6883..0b2dcb3 100644 --- a/src/ParseqUI.js +++ b/src/ParseqUI.js @@ -7,7 +7,6 @@ import WarningAmberRoundedIcon from '@mui/icons-material/WarningAmberRounded'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; -import CssBaseline from '@mui/material/CssBaseline'; import MenuItem from '@mui/material/MenuItem'; import OutlinedInput from '@mui/material/OutlinedInput'; import Paper from '@mui/material/Paper'; @@ -50,18 +49,17 @@ import { fieldNametoRGBa, getOutputTruncationLimit, getUTCTimeStamp, getVersionN import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faThumbtack } from '@fortawesome/free-solid-svg-icons' - -import 'ag-grid-community/styles/ag-grid.css'; // Core grid CSS, always needed -// Optional theme CSS +import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; -import './robin.css'; import { beatToFrame, frameToBeat, frameToSec, secToFrame, beatToSec, secToBeat, remapFrameCount } from './utils/maths'; +import { experimental_extendTheme as extendTheme } from "@mui/material/styles"; +import { themeFactory } from "./theme"; const ParseqUI = (props) => { const activeDocId = queryStringGetOrCreate('docId', makeDocId) // Will not change unless whole page is reloaded. const gridRef = useRef(); - const { defaultTemplate, darkMode } = props + const { defaultTemplate } = props const preventInitialRender = new URLSearchParams(window.location.search).get("render") === "false" || false; ////////////////////////////////////////// @@ -114,6 +112,8 @@ const ParseqUI = (props) => { const runOnceTimeout = useRef(); const _frameToRowId_cache = useRef(); + const theme = extendTheme(themeFactory()); + console.log("theme", theme); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Initialisation logic @@ -655,7 +655,6 @@ const ParseqUI = (props) => { const promptsUI = useMemo(() => (prompts && options) ? { InputLabelProps={{ shrink: true, }} InputProps={{ style: { fontSize: '0.75em' }, - sx: { background: (Number(candidateBPM) !== Number(options.bpm))? 'ivory' : '', }, + sx: { background: (Number(candidateBPM) !== Number(options.bpm))? theme.vars.palette.unsavedbg.main : '', }, endAdornment: (Number(candidateBPM) !== Number(options.bpm)) ? '🖊️' : '' }} sx={{ minWidth: 70, maxWidth: 100, }} @@ -774,7 +773,7 @@ const ParseqUI = (props) => { InputLabelProps={{ shrink: true, }} InputProps={{ style: { fontSize: '0.75em' }, - sx: { background: (Number(candidateFPS) !== Number(options.output_fps)) ? 'ivory' : '', }, + sx: { background: (Number(candidateFPS) !== Number(options.output_fps)) ? theme.vars.palette.unsavedbg.main : '', }, endAdornment: (Number(candidateFPS) !== Number(options.output_fps)) ? '🖊️' : '' }} sx={{ minWidth: 70, maxWidth: 100, }} @@ -817,7 +816,7 @@ const ParseqUI = (props) => { InputLabelProps={{ shrink: true, }} InputProps={{ style: { fontSize: '0.75em' }, - sx: { background: Number(candidateLastFrame) !== Number(lastFrame) ? 'ivory' : '', }, + sx: { background: Number(candidateLastFrame) !== Number(lastFrame) ? theme.vars.palette.unsavedbg.main : '', }, endAdornment: Number(candidateLastFrame) !== Number(lastFrame) ? '🖊️' : '' }} sx={{ minWidth: 70, maxWidth: 100, }} @@ -884,7 +883,7 @@ const ParseqUI = (props) => { label={Generate in reverse} /> - , [options, candidateBPM, candidateFPS, candidateLastFrame, lastFrame, keyframeLock, reverseRender, remapFrames, keyframes]) + , [options, candidateBPM, candidateFPS, candidateLastFrame, lastFrame, keyframeLock, reverseRender, remapFrames, keyframes, theme]) @@ -1086,7 +1085,7 @@ const ParseqUI = (props) => { height={"400px"} // TODO - make this configurable updateKeyframe={(field, index, value) => { let rowId = frameToRowId(index) - gridRef.current.api.getRowNode(rowId).setDataValue(field, value); + gridRef.current.api.getRowNode(rowId).setDataValue(field, value+""); //coerce to string }} addKeyframe={(index) => { // If this isn't already a keyframe, add a keyframe @@ -1181,8 +1180,19 @@ const ParseqUI = (props) => { { managedFields.filter((field) => showFlatSparklines ? true : getAnimatedFields(renderedData).includes(field)).sort().map((field) => - - {(displayedFields.includes(field) ? "✔️" : "") + field} + + + + {displayedFields.includes(field) ? : <>} {field} {defaultFields.find(f => f.name === field)?.labels?.includes("2D") ? [2D] : defaultFields.find(f => f.name === field)?.labels?.includes("3D") ? @@ -1190,7 +1200,7 @@ const ParseqUI = (props) => { [2D+3D] } { - + } @@ -1212,7 +1222,7 @@ const ParseqUI = (props) => { Sparklines are disabled when frame count > 1000. This will be improved in future versions of Parseq. : <> - }, [displayedFields, showFlatSparklines, renderedData, managedFields, handleClickedSparkline, sparklineData]); + }, [displayedFields, showFlatSparklines, renderedData, managedFields, handleClickedSparkline, sparklineData, theme]); @@ -1315,13 +1325,13 @@ const ParseqUI = (props) => { let miniMessage; if (enqueuedRender && !renderedErrorMessage) { - miniMessage = {icon: , color: 'rgb(255, 244, 229)', message: "Render in progres..."}; + miniMessage = {icon: , color: theme.vars.palette.warning.light, message: "Render in progres..."}; } else if (renderedErrorMessage) { - miniMessage = {icon: , color: 'rgb(253, 237, 237)', message: "Error during render. Hover for details."}; + miniMessage = {icon: , color: theme.vars.palette.error.light, message: "Error during render. Hover for details."}; } else if (needsRender) { - miniMessage = {icon: , color:'rgb(229, 246, 253)', message: "Please render to update the output."}; + miniMessage = {icon: , color: theme.vars.palette.info.light, message: "Please render to update the output."}; } else { - miniMessage = {icon: , color:'rgb(237, 247, 237)', message: "Output is up-to-date."}; + miniMessage = {icon: , color: theme.vars.palette.success.light, message: "Output is up-to-date."}; } return @@ -1332,7 +1342,7 @@ const ParseqUI = (props) => { - }, [needsRender, renderedErrorMessage, enqueuedRender]); + }, [needsRender, renderedErrorMessage, enqueuedRender, theme]); const maxiFooterContent = useMemo(() => @@ -1402,12 +1412,12 @@ const ParseqUI = (props) => { bottom: 0, left: 0, right: 0, - backgroundColor: 'rgba(200,200,200,0.85)', + backgroundColor: theme.vars.palette.footerbg.main, opacity: '99%' }}> {maxiFooterContent} {miniFooterContent} - , [pinFooter, hoverFooter, maxiFooterContent, miniFooterContent]); + , [pinFooter, hoverFooter, maxiFooterContent, miniFooterContent, theme]); @@ -1429,7 +1439,6 @@ const ParseqUI = (props) => { }}> - {docManager} diff --git a/src/UserSettings.ts b/src/UserSettings.ts index 6230046..4ae2e80 100644 --- a/src/UserSettings.ts +++ b/src/UserSettings.ts @@ -7,19 +7,15 @@ export type UserSetting = { export class UserSettings { - static setDarkMode(darkMode : boolean) { - db.parseqUserSettings.put({name:"darkMode", value: darkMode}, "darkMode").catch((e) => { + static setColorScheme(scheme : string) { + db.parseqUserSettings.put({name:"colorScheme", value: scheme}, "colorScheme").catch((e) => { console.error("Error saving setting: ", e); - }).then(() => { - console.debug("Dark mode setting saved"); - }).finally(() => { - console.debug("Dark mode setting save logic compled"); }) } - static async getDarkMode() : Promise { - const darkModeSetting = await db.parseqUserSettings.get('darkMode'); - return darkModeSetting?.value; + static async getColorScheme() : Promise { + const colorSchemeSetting = await db.parseqUserSettings.get('colorScheme'); + return colorSchemeSetting?.value; } } \ No newline at end of file diff --git a/src/components/AudioWaveform.tsx b/src/components/AudioWaveform.tsx index a56922e..90d525d 100644 --- a/src/components/AudioWaveform.tsx +++ b/src/components/AudioWaveform.tsx @@ -1,4 +1,4 @@ -import { Box, Alert, Typography, Button, Stack, TextField, MenuItem, Tab, Tabs, Tooltip } from "@mui/material"; +import { Box, Alert, Typography, Button, Stack, TextField, MenuItem, Tab, Tabs, Tooltip, Link } from "@mui/material"; import Fade from '@mui/material/Fade'; import Grid from '@mui/material/Unstable_Grid2'; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -683,7 +683,7 @@ export function AudioWaveform(props: AudioWaveformProps) { - Parseq uses Aubio.js to estimate your reference audio's BPM. The result is not always accurate, but can guide you towards a good overall BPM value for your Parseq document (which you set above the grid). Parseq does not yet support variable BPMs. + Parseq uses Aubio.js to estimate your reference audio's BPM. The result is not always accurate, but can guide you towards a good overall BPM value for your Parseq document (which you set above the grid). Parseq does not yet support variable BPMs. @@ -743,7 +743,7 @@ export function AudioWaveform(props: AudioWaveformProps) { - Parseq uses Aubio.js to detect events in your reference audio. You can move events by dragging them, add events by double-clicking, and delete events by shift-clicking. You can generate Parseq keyframes from audio events in the "Keyframe generation" tab. + Parseq uses Aubio.js to detect events in your reference audio. You can move events by dragging them, add events by double-clicking, and delete events by shift-clicking. You can generate Parseq keyframes from audio events in the "Keyframe generation" tab. diff --git a/src/components/GridToolTip.tsx b/src/components/GridToolTip.tsx index 72b7657..e4d54ae 100644 --- a/src/components/GridToolTip.tsx +++ b/src/components/GridToolTip.tsx @@ -1,12 +1,14 @@ +import { experimental_extendTheme as extendTheme } from "@mui/material/styles"; import { frameToBeat, frameToSec } from '../utils/maths'; +import { themeFactory } from "../theme"; export const GridTooltip = (props : any) => { const column = props.colDef.field; const data = props.api.getDisplayedRowAtIndex(props.rowIndex).data; - + const theme = extendTheme(themeFactory()); return ( -
    +
    {column?.endsWith('_i')?column.slice(0, -2):column}
    Frame: {data?.frame}
    Seconds: {data && frameToSec(data.frame, props.getFps()).toFixed(3)}
    diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 74943c7..48104f8 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,15 +1,17 @@ import { faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'; import { faBook, faBug, faFilm, faWaveSquare, faMoon, faLightbulb } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Box, Chip, Stack, Typography } from '@mui/material'; +import { Box, Chip, Link, Stack, SupportedColorScheme, Typography, useColorScheme, useMediaQuery } from '@mui/material'; import Grid from '@mui/material/Unstable_Grid2'; import { getAnalytics, isSupported } from "firebase/analytics"; import GitInfo from 'react-git-info/macro'; import Login from "../Login"; import { UserAuthContextProvider } from "../UserAuthContext"; import { app, auth } from '../firebase-config'; -import '../robin.css'; import { getVersionNumber } from '../utils/utils'; +import { useLocation } from 'react-router-dom'; +import { UserSettings } from '../UserSettings'; +import { useEffect } from 'react'; var analytics: any; isSupported().then((isSupported) => { @@ -26,24 +28,44 @@ const GIT_COMMIT_HASH = gitInfo.commit.hash; const GIT_COMMIT_SHORTHASH = gitInfo.commit.shortHash; const GIT_COMMIT_DATE = gitInfo.commit.date; -type HeaderProps = { - title: string, - darkMode: boolean, - updateDarkMode: (darkMode: boolean) => void -}; -export default function Header({ title, darkMode, updateDarkMode }: HeaderProps) { +export default function Header() { const displayDate = GIT_COMMIT_DATE; const displayBranch = (!GIT_BRANCH || GIT_BRANCH === 'master') ? '' : `Branch: ${GIT_BRANCH};`; - const commitLink = {GIT_COMMIT_SHORTHASH} - const changeLogLink = all changes + const commitLink = {GIT_COMMIT_SHORTHASH} + const changeLogLink = all changes + + const { colorScheme, setColorScheme } = useColorScheme(); + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + + // Retrieve color scheme setting from local db if any, and apply. + // Else, use browser settings, and default to light. + useEffect(() => { + UserSettings.getColorScheme().then((colorScheme) => { + if (colorScheme !== undefined) { + console.log("Setting color scheme based on local db"); + setColorScheme(colorScheme as SupportedColorScheme); + } else if (prefersDarkMode) { + console.log("Setting color scheme based prefers-color-scheme"); + setColorScheme('dark'); + } else { + console.log("defaulting to light scheme"); + setColorScheme('light'); + } + })}, [prefersDarkMode, setColorScheme]); + + const location = useLocation(); + // Don't render a header in raw view. + if (location.pathname === '/raw') { + return <> + } return (

    - {title} v{getVersionNumber()} + Parseq v{getVersionNumber()} [{process.env.NODE_ENV}] {displayBranch} Built {displayDate} ({commitLink} - {changeLogLink}) @@ -58,7 +80,16 @@ export default function Header({ title, darkMode, updateDarkMode }: HeaderProps) - updateDarkMode(!darkMode)} icon={} label={(darkMode?"Light":"Dark")+" Mode"}/> + {/* updateDarkMode(!darkMode)} icon={} label={(darkMode?"Light":"(wip) Dark")+" Mode"}/> */} + {/* @ts-ignore */} + } label={(colorScheme === 'dark'?"Light":"(wip) Dark")+" Mode"} + onClick={() => { + console.log("Setting color scheme"); + const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark'; + setColorScheme(newColorScheme); + UserSettings.setColorScheme(newColorScheme); + }} + /> } label="Tutorial" /> } label="Docs" /> } label="Reference" /> diff --git a/src/components/KeyframeDialogs.tsx b/src/components/KeyframeDialogs.tsx index 05f2e63..1f73ee7 100644 --- a/src/components/KeyframeDialogs.tsx +++ b/src/components/KeyframeDialogs.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-no-target-blank */ import React, { useMemo, FC, useState, useEffect } from 'react'; import { Dialog, @@ -14,6 +15,7 @@ import { Box, Stack, Alert, + Link, } from '@mui/material'; import { TabPanel } from './TabPanel'; import { SmallTextField } from './SmallTextField'; @@ -631,11 +633,7 @@ export const MergeKeyframesDialog: FC = ({ 🌪️ Merge keyframes - Merge keyframes from another source into the current document. For example: -
      -
    • Try the browser to find keyframe data from your other documents.
    • -
    • ⚠️ Experimental: try the analyser to generate keyframes from audio.
    • -
    + Merge keyframes from another source into the current document. Try the browser to find keyframe data from your other documents.
    { if (!generationBuffer || !inputBuffer) { //perspective(height) // TODO figure out how to convert deforum perspective - console.log("Creating canvas", width, height); p5.createCanvas(width, height, "webgl"); p5.imageMode("center"); p5.rectMode("center"); @@ -290,7 +289,7 @@ export const MovementPreview = ({renderedData, fps, width, height, hideWarning, return {hideWarning ||

    This experimental feature gives you a rough idea of what your camera movements will look like. - Inspired by AnimationPreview by @pharmapsychotic. + Inspired by AnimationPreview by @pharmapsychotic.

    • This is a rough reference only: the image warping algorithm is not identical to Deforum's.
    • @@ -298,7 +297,7 @@ export const MovementPreview = ({renderedData, fps, width, height, hideWarning,
    • Does not factor in FPS or perspective params (fov, near, far).
    • Using a higher cadence (~10) in this preview can make it easier to see the effect of your camera movements. However, whether you should use a high cadence during your real generation depends on how often you want the Diffusion process to run.
    - Feedback welcome on Discord or GitHub. + Feedback welcome on Discord or GitHub.
    } {sketchElem} diff --git a/src/components/ParseqGraph.tsx b/src/components/ParseqGraph.tsx index 4317738..0bdd27d 100644 --- a/src/components/ParseqGraph.tsx +++ b/src/components/ParseqGraph.tsx @@ -16,7 +16,9 @@ import debounce from 'lodash.debounce'; //@ts-ignore import range from 'lodash.range'; import { RenderedData, GraphableData } from '../ParseqUI'; - +import { Theme } from '@mui/material'; +import { SupportedColorScheme, experimental_extendTheme as extendTheme, useColorScheme } from "@mui/material/styles"; +import { themeFactory } from "../theme"; const ChartJSAddPointPlugin = { id: 'click', @@ -61,7 +63,7 @@ ChartJS.register( //@ts-ignore //Interaction.modes.interpolate = Interpolate -export class ParseqGraph extends React.Component<{ +class ParseqGraphRaw extends React.Component<{ renderedData: RenderedData, graphableData: GraphableData, displayedFields: string[], @@ -80,8 +82,12 @@ export class ParseqGraph extends React.Component<{ height: number|string; editingDisabled?: boolean; hideLegend?: boolean; + theme: Theme; + colorScheme: SupportedColorScheme; }> { + + isKeyframeWithFieldValue = (field: string, idx: number): boolean => { return this.props.renderedData.keyframes .some(frame => frame['frame'] === idx @@ -99,6 +105,7 @@ export class ParseqGraph extends React.Component<{ }; render() { + // TODO - annotation construction is horrible here, need some utility functions to reduce duplication. const promptAnnotations = this.props.promptMarkers.reduce((acc: any, marker: { x: number, label: string, color: string, top: boolean }, idx: number) => { return { @@ -228,7 +235,13 @@ export class ParseqGraph extends React.Component<{ capturedThis.props.onGraphScalesChanged(newScales); }, 100); + + const palette = this.props.theme.colorSchemes[this.props.colorScheme].palette; + let options: ChartOptions<'line'> = { + backgroundColor: palette.graphBackground.main, + borderColor: palette.graphBorder.main, + color: palette.graphFont.main, parsing: false, normalised: true, spanGaps: true, @@ -257,15 +270,24 @@ export class ParseqGraph extends React.Component<{ case 'seconds': return frameToSec(Number(value), capturedThis.props.renderedData.options.output_fps).toFixed(3); case 'beats': return frameToBeat(Number(value), capturedThis.props.renderedData.options.output_fps, capturedThis.props.renderedData.options.bpm).toFixed(2); } - } + }, + color: palette.graphFont.main, + }, + grid: { + color: palette.graphBorder.main, } }, y: { type: 'linear', ticks: { minRotation: 0, - maxRotation: 0 + maxRotation: 0, + color: palette.graphFont.main, + }, + grid: { + color: palette.graphBorder.main, } + }, }, onClick: (event: any, elements: any, chart: any) => { @@ -425,3 +447,11 @@ export class ParseqGraph extends React.Component<{ return
    ; } } + +// Wrap class component to be able to access theme - see https://reactnavigation.org/docs/use-theme/#using-with-class-component +export const ParseqGraph = function(props : any) { + const theme = extendTheme(themeFactory()); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { colorScheme, setColorScheme } = useColorScheme(); + return ; +} \ No newline at end of file diff --git a/src/components/ParseqGrid.tsx b/src/components/ParseqGrid.tsx index b6e1527..6206c96 100644 --- a/src/components/ParseqGrid.tsx +++ b/src/components/ParseqGrid.tsx @@ -6,6 +6,8 @@ import { frameToXAxisType, xAxisTypeToFrame } from '../utils/maths'; import { fieldNametoRGBa } from '../utils/utils'; import { GridTooltip } from './GridToolTip'; import { ValueParserParams, ValueSetterParams } from 'ag-grid-community'; +import { experimental_extendTheme as extendTheme, useColorScheme } from "@mui/material/styles"; +import { themeFactory } from "../theme"; const config = {} const mathjs = create(all, config) @@ -36,6 +38,11 @@ type ParseqGridProps = { export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridReady, onCellValueChanged, onCellKeyPress, onFirstDataRendered, onChangeGridCursorPosition, showCursors, keyframeLock, fps, bpm, managedFields, agGridProps, agGridStyle }: ParseqGridProps, gridRef) => { + const theme = extendTheme(themeFactory()); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const {colorScheme, setColorScheme } = useColorScheme(); + + if (!rangeSelection) { rangeSelection = {}; } @@ -46,7 +53,7 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea return; } - // Broadcast and broadcast the cursor position + // Update and broadcast the cursor position // TODO: showCursors doesn't need to be a prop, it could be local // to the parent (this isn't perf sensitive so we could call the callback unconditionally) if (showCursors) { @@ -113,7 +120,7 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea useFormatter: true, }, cellStyle: (params: { api: { getFocusedCell: () => any; }; }) => ({ - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid lightgrey' + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMinor.main, }), flex: 1, @@ -137,13 +144,13 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea cellStyle: (params: any): any => { if (isInRangeSelection(params)) { return { - backgroundColor: 'lightgrey', - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid lightgrey' + backgroundColor: theme.vars.palette.gridInfoField.light, + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMajor.main } } else { return { - backgroundColor: 'white', - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid lightgrey' + backgroundColor: theme.vars.palette.gridInfoField.main, + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid '+ theme.vars.palette.gridColSeparatorMajor.main } } }, @@ -176,12 +183,12 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea if (isInRangeSelection(params)) { return { backgroundColor: fieldNametoRGBa(field, 0.4), - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid lightgrey' + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMinor.main } } else { return { backgroundColor: fieldNametoRGBa(field, 0.1), - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid lightgrey' + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMinor.main } } }, @@ -206,12 +213,12 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea if (isInRangeSelection(params)) { return { backgroundColor: fieldNametoRGBa(field, 0.4), - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid black' + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMajor.main } } else { return { backgroundColor: fieldNametoRGBa(field, 0.1), - borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid black' + borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMajor.main } } }, @@ -221,7 +228,7 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea ]) : []) ] - }, [managedFields, isInRangeSelection, keyframeLock, fps, bpm]); + }, [managedFields, isInRangeSelection, keyframeLock, fps, bpm, theme]); const defaultColDef = useMemo(() => ({ editable: true, @@ -238,63 +245,65 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea } }), [fps, bpm]); - //@ts-ignore - return
    { - if (e.event.keyCode === 46 || e.event.keyCode === 8) { - if (rangeSelection.anchor && rangeSelection.tip) { - const x1 = Math.min(rangeSelection.anchor.x, rangeSelection.tip.x); - const x2 = Math.max(rangeSelection.anchor.x, rangeSelection.tip.x); - const y1 = Math.min(rangeSelection.anchor.y, rangeSelection.tip.y); - const y2 = Math.max(rangeSelection.anchor.y, rangeSelection.tip.y); - for (let colInstanceId = x1; colInstanceId <= x2; colInstanceId++) { - const col = e.columnApi.getAllGridColumns().find((c: any) => c.instanceId === colInstanceId); - if (col && col.visible && col.colId !== 'frame') { - for (let rowIndex = y1; rowIndex <= y2; rowIndex++) { - e.api.getDisplayedRowAtIndex(rowIndex).setDataValue(col.colId, ""); + + return
    + {/* @ts-ignore */} + { + if (e.event.keyCode === 46 || e.event.keyCode === 8) { + if (rangeSelection.anchor && rangeSelection.tip) { + const x1 = Math.min(rangeSelection.anchor.x, rangeSelection.tip.x); + const x2 = Math.max(rangeSelection.anchor.x, rangeSelection.tip.x); + const y1 = Math.min(rangeSelection.anchor.y, rangeSelection.tip.y); + const y2 = Math.max(rangeSelection.anchor.y, rangeSelection.tip.y); + for (let colInstanceId = x1; colInstanceId <= x2; colInstanceId++) { + const col = e.columnApi.getAllGridColumns().find((c: any) => c.instanceId === colInstanceId); + if (col && col.visible && col.colId !== 'frame') { + for (let rowIndex = y1; rowIndex <= y2; rowIndex++) { + e.api.getDisplayedRowAtIndex(rowIndex).setDataValue(col.colId, ""); + } } } } } - } - }} - onCellClicked={(e: any) => { - if (showCursors) { - onChangeGridCursorPosition(e.data.frame); - } - if (e.event.shiftKey) { - onSelectRange({ - anchor: { x: e.api.getFocusedCell().column.instanceId, y: e.api.getFocusedCell().rowIndex }, - tip: { x: e.column.instanceId, y: e.rowIndex } - }) - } else { - onSelectRange({}); - } - }} - onCellMouseOver={(e: any) => { - if (e.event.buttons === 1) { - onSelectRange({ - anchor: { x: e.api.getFocusedCell().column.instanceId, y: e.api.getFocusedCell().rowIndex }, - tip: { x: e.column.instanceId, y: e.rowIndex } - }) - } - }} - /> + }} + onCellClicked={(e: any) => { + if (showCursors) { + onChangeGridCursorPosition(e.data.frame); + } + if (e.event.shiftKey) { + onSelectRange({ + anchor: { x: e.api.getFocusedCell().column.instanceId, y: e.api.getFocusedCell().rowIndex }, + tip: { x: e.column.instanceId, y: e.rowIndex } + }) + } else { + onSelectRange({}); + } + }} + onCellMouseOver={(e: any) => { + if (e.event.buttons === 1) { + onSelectRange({ + anchor: { x: e.api.getFocusedCell().column.instanceId, y: e.api.getFocusedCell().rowIndex }, + tip: { x: e.column.instanceId, y: e.rowIndex } + }) + } + }} + />
    }); diff --git a/src/components/Prompts.tsx b/src/components/Prompts.tsx index 5674de9..c118fab 100644 --- a/src/components/Prompts.tsx +++ b/src/components/Prompts.tsx @@ -1,4 +1,4 @@ -import { Alert, Box, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControlLabel, MenuItem, Tooltip, Typography } from "@mui/material"; +import { Alert, Box, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControlLabel, Link, MenuItem, Tooltip, Typography } from "@mui/material"; import Button from '@mui/material/Button'; import TextField from '@mui/material/TextField'; import Grid from '@mui/material/Unstable_Grid2'; @@ -9,6 +9,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AdvancedParseqPrompt, AdvancedParseqPromptsV2, OverlapType, ParseqPrompts, SimpleParseqPrompts } from "../ParseqUI"; import StyledSwitch from './StyledSwitch'; import { frameToBeat, frameToSec } from "../utils/maths"; +import type {} from '@mui/material/themeCssVarsAugmentation'; +import { experimental_extendTheme as extendTheme } from "@mui/material/styles"; +import { themeFactory } from "../theme"; interface PromptsProps { initialPrompts: AdvancedParseqPromptsV2, @@ -16,7 +19,6 @@ interface PromptsProps { keyframeLock: 'frames' | 'beats' | 'seconds', bpm: number, fps: number, - darkMode: boolean, markDirty: (active: boolean) => void, commitChange: (event: any) => void } @@ -86,6 +88,8 @@ export function Prompts(props: PromptsProps) { const [unsavedPrompts, setUnsavedPrompts] = useState(_.cloneDeep(props.initialPrompts)); const [quickPreviewPosition, setQuickPreviewPosition] = useState(0); const [quickPreview, setQuickPreview] = useState(""); + const theme = extendTheme(themeFactory()); + // Copy the initial prompts into the unsaved prompts // unless the initial prompts have a marker indicating they have just looped around @@ -131,11 +135,6 @@ export function Prompts(props: PromptsProps) { const hasUnsavedChanges = initPrompt && (unsavedPrompt[posNegStr] !== initPrompt[posNegStr]); - const inputTextColors = { - positive: props.darkMode ? 'LightGreen' : 'DarkGreen', - negative: props.darkMode ? '#ff8d8d' : 'Firebrick' - } - return { @@ -172,7 +171,7 @@ export function Prompts(props: PromptsProps) { InputLabelProps={{ shrink: true, style: { fontSize: '0.9em' } }} size="small" variant="outlined" /> - }, [commitChanges, props, unsavedPrompts]); + }, [commitChanges, props, unsavedPrompts, theme]); const addPrompt = useCallback(() => { const newIndex = unsavedPrompts.promptList.length; @@ -220,7 +219,7 @@ export function Prompts(props: PromptsProps) { && (prompt.positive.match(/\sAND\s/) || prompt.negative.match(/\sAND\s/))) { return - Warning: Parseq uses composable diffusion to combine overlapping prompts. + Warning: Parseq uses composable diffusion to combine overlapping prompts.  {prompt.name} overlaps with the following: {overlappingPrompts.map(p => p.name).join(', ')}. But {prompt.name} also appears to contain its own composable diffusion sections (… AND …). This may lead to unexpected results. Check your rendered prompts in the preview window and consider removing the composable diffusion sections from {prompt.name} if possible. @@ -263,7 +262,7 @@ export function Prompts(props: PromptsProps) { disabled={prompt.overlap.type === "none"} inputProps={{ style: { fontFamily: 'Monospace', fontSize: '0.75em' }, - sx: { background: unsavedPrompts.promptList[promptIdx].overlap.inFrames !== props.initialPrompts.promptList[promptIdx]?.overlap?.inFrames ? 'ivory' : '', }, + sx: { background: unsavedPrompts.promptList[promptIdx].overlap.inFrames !== props.initialPrompts.promptList[promptIdx]?.overlap?.inFrames ? theme.vars.palette.unsavedbg.main : '', }, }} InputLabelProps={{ shrink: true, }} value={prompt.overlap.inFrames} @@ -305,7 +304,7 @@ export function Prompts(props: PromptsProps) { disabled={prompt.overlap.type === "none"} inputProps={{ style: { fontFamily: 'Monospace', fontSize: '0.75em' }, - sx: { background: unsavedPrompts.promptList[promptIdx].overlap.outFrames !== props.initialPrompts.promptList[promptIdx]?.overlap?.outFrames ? 'ivory' : '', }, + sx: { background: unsavedPrompts.promptList[promptIdx].overlap.outFrames !== props.initialPrompts.promptList[promptIdx]?.overlap?.outFrames ? theme.vars.palette.unsavedbg.main : '', }, }} InputLabelProps={{ shrink: true, }} value={prompt.overlap.outFrames} @@ -349,7 +348,7 @@ export function Prompts(props: PromptsProps) { disabled={prompt.overlap.type !== "custom"} inputProps={{ style: { fontFamily: 'Monospace', fontSize: '0.75em' }, - sx: { background: unsavedPrompts.promptList[promptIdx].overlap.custom !== props.initialPrompts.promptList[promptIdx]?.overlap?.custom ? 'ivory' : '', }, + sx: { background: unsavedPrompts.promptList[promptIdx].overlap.custom !== props.initialPrompts.promptList[promptIdx]?.overlap?.custom ? theme.vars.palette.unsavedbg.main : '', }, }} InputLabelProps={{ shrink: true, }} value={prompt.overlap.custom} @@ -375,7 +374,7 @@ export function Prompts(props: PromptsProps) { /> - }, [unsavedPrompts, commitChanges, props]); + }, [unsavedPrompts, commitChanges, props, theme]); const displayPrompts = useCallback((advancedPrompts: AdvancedParseqPromptsV2) => @@ -411,7 +410,7 @@ export function Prompts(props: PromptsProps) { disabled={prompt.allFrames} inputProps={{ style: { fontFamily: 'Monospace', fontSize: '0.75em' }, - sx: { background: unsavedPrompts.promptList[idx].from !== props.initialPrompts.promptList[idx]?.from ? 'ivory' : '', }, + sx: { background: unsavedPrompts.promptList[idx].from !== props.initialPrompts.promptList[idx]?.from ? theme.vars.palette.unsavedbg.main : '', }, }} InputLabelProps={{ shrink: true, }} value={prompt.from} @@ -452,7 +451,7 @@ export function Prompts(props: PromptsProps) { disabled={prompt.allFrames} inputProps={{ style: { fontFamily: 'Monospace', fontSize: '0.75em' }, - sx: { background: unsavedPrompts.promptList[idx].to !== props.initialPrompts.promptList[idx]?.to ? 'ivory' : '', }, + sx: { background: unsavedPrompts.promptList[idx].to !== props.initialPrompts.promptList[idx]?.to ? theme.vars.palette.unsavedbg.main : '', }, }} InputLabelProps={{ shrink: true, }} value={prompt.to} @@ -549,7 +548,7 @@ export function Prompts(props: PromptsProps) { } - , [delPrompt, promptInput, unsavedPrompts, props, displayFadeOptions, composableDiffusionWarning, commitChanges]); + , [delPrompt, promptInput, unsavedPrompts, props, displayFadeOptions, composableDiffusionWarning, commitChanges, theme]); const reorderPrompts = useCallback(() => { diff --git a/src/components/StyledSwitch.tsx b/src/components/StyledSwitch.tsx index a6795b5..d401065 100644 --- a/src/components/StyledSwitch.tsx +++ b/src/components/StyledSwitch.tsx @@ -15,13 +15,13 @@ const StyledSwitch = styled(Switch)(({ theme }) => ({ }, '&:before': { backgroundImage: `url('data:image/svg+xml;utf8,')`, left: 12, }, '&:after': { backgroundImage: `url('data:image/svg+xml;utf8,')`, right: 12, }, diff --git a/src/components/SupportParseq.tsx b/src/components/SupportParseq.tsx index 18c86c4..5bf7c0a 100644 --- a/src/components/SupportParseq.tsx +++ b/src/components/SupportParseq.tsx @@ -1,7 +1,8 @@ +/* eslint-disable react/jsx-no-target-blank */ import { faPatreon } from '@fortawesome/free-brands-svg-icons'; import { faCoffee } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Chip, Fade, Stack, Typography } from '@mui/material'; +import { Chip, Fade, Link, Stack, Typography } from '@mui/material'; import { supporterList } from '../data/supporterList'; import { useEffect, useState } from 'react'; @@ -39,7 +40,7 @@ const Supporters = () => { return { supporter.link - ? {supporter.name} + ? {supporter.name} : supporter.name } @@ -61,9 +62,9 @@ export default function SupportParseq() { Support Parseq:  - } label="Coffee" /> + } label="Coffee" />  /  - } label="Patreon" /> + } label="Patreon" /> ; } \ No newline at end of file diff --git a/src/components/TimeSeriesUI.tsx b/src/components/TimeSeriesUI.tsx index 5e6dc4a..52af135 100644 --- a/src/components/TimeSeriesUI.tsx +++ b/src/components/TimeSeriesUI.tsx @@ -9,6 +9,7 @@ import { DialogTitle, FormControl, FormControlLabel, + Link, MenuItem, Stack, Tab, @@ -527,7 +528,7 @@ export const TimeSeriesUI = (props: TimeSeriesUIProps) => { - Parseq uses Aubio.js extract pitch. This works best with isolated melodies (no beat). + Parseq uses Aubio.js extract pitch. This works best with isolated melodies (no beat). } diff --git a/src/components/UploadButton.tsx b/src/components/UploadButton.tsx index 166ddfc..8dbd475 100644 --- a/src/components/UploadButton.tsx +++ b/src/components/UploadButton.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-no-target-blank */ import Alert from '@mui/material/Alert'; import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; @@ -10,6 +11,7 @@ import CopyToClipboard from 'react-copy-to-clipboard'; import ReactTimeAgo from 'react-time-ago'; import { useUserAuth } from "../UserAuthContext"; import { DocId } from '../ParseqUI'; +import { Link } from '@mui/material'; type UploadButtonProps = { docId: DocId, @@ -58,7 +60,7 @@ export function UploadButton({ docId, renderedJson, autoUpload, onNewUploadStatu - See uploaded file + See uploaded file ); setLastUploadTime(Date.now()); }); diff --git a/src/index.css b/src/index.css index 27cac9a..15a84c5 100644 --- a/src/index.css +++ b/src/index.css @@ -7,13 +7,35 @@ body { -moz-osx-font-smoothing: grayscale; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; - background: rgb(235, 235, 235); - word-wrap: break-word; - box-decoration-break: clone; - padding: .1rem .1rem .1rem; - border-radius: .2rem; +.ag-theme-alpine-dark, +.ag-theme-alpine { + --ag-font-size: 10px !important; + --ag-grid-size: 3px !important; + --ag-cell-horizontal-padding: 2px !important; + --ag-header-column-separator-display: block !important; + --ag-header-column-separator-width: 1px !important; + --ag-header-column-separator-height: 100% !important; + --ag-font-family: monospace !important; } +h3 { + margin-block-start: 0.5em; + margin-block-end: 0.5em; +} + +p { + margin-block-start: 0.5em; + margin-block-end: 0.5em; +} + +.MuiMenuItem-root { + font-size: 0.75em !important; +} + +#waveform .marker-label span { + font-size: 0.5em !important; +} + +#waveform .marker-label span:hover { + font-size: 0.75em !important; +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 265c046..a5f8627 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,6 @@ import './wdyr'; import ReactDOM from 'react-dom/client'; -import { - RouterProvider, - createBrowserRouter -} from "react-router-dom"; import App from './App'; import './index.css'; //@ts-ignore @@ -12,14 +8,9 @@ import * as Sentry from "@sentry/react"; import { BrowserTracing } from "@sentry/tracing"; import TimeAgo from 'javascript-time-ago'; import en from 'javascript-time-ago/locale/en.json'; -import Analyser from './Analyser'; -import Browser from './Browser'; -import Deforum from './Deforum'; -import Labs from './Labs'; -import Raw from './Raw'; -import FunctionDoc from './FunctionDoc'; import reportWebVitals from './reportWebVitals'; + Sentry.init({ dsn: "https://4706cbba5987462184a3e541c4b8a9d4@o175750.ingest.sentry.io/4504274009325568", integrations: [new BrowserTracing()], @@ -29,19 +20,9 @@ Sentry.init({ TimeAgo.addDefaultLocale(en) -const myRouter = createBrowserRouter([ - { path: '/', element: }, - { path: '/deforum', element: }, - { path: '/browser', element: }, - { path: '/legacy', element: }, - { path: '/analyser', element: }, - { path: '/labs', element: }, - { path: '/raw', element: }, - { path: '/functionDocs', element: }, -]); - //@ts-ignore ReactDOM.createRoot(document.getElementById("root")) - .render(); + .render(); reportWebVitals(console.log); + diff --git a/src/robin.css b/src/robin.css deleted file mode 100644 index a610e24..0000000 --- a/src/robin.css +++ /dev/null @@ -1,31 +0,0 @@ -.ag-theme-alpine { - --ag-font-size: 10px !important; - --ag-grid-size: 3px !important; - --ag-cell-horizontal-padding: 2px !important; - --ag-header-column-separator-display: block !important; - --ag-header-column-separator-width: 1px !important; - --ag-header-column-separator-height: 100% !important; - --ag-font-family: monospace !important; -} - -h3 { - margin-block-start: 0.5em; - margin-block-end: 0.5em; -} - -p { - margin-block-start: 0.5em; - margin-block-end: 0.5em; -} - -.MuiMenuItem-root { - font-size: 0.75em !important; -} - -#waveform .marker-label span { - font-size: 0.5em !important; -} - -#waveform .marker-label span:hover { - font-size: 0.75em !important; -} \ No newline at end of file diff --git a/src/theme.ts b/src/theme.ts new file mode 100644 index 0000000..30940ac --- /dev/null +++ b/src/theme.ts @@ -0,0 +1,74 @@ +import { CssVarsThemeOptions, PaletteColor, createTheme } from "@mui/material"; + +declare module "@mui/material/styles" { + interface Palette { + negative: PaletteColor; + positive: PaletteColor; + unsavedbg: PaletteColor; + footerbg: PaletteColor; + fainthighlight: PaletteColor; + graphBorder: PaletteColor; + graphBackground: PaletteColor; + graphFont: PaletteColor; + gridInfoField: PaletteColor; + gridColSeparatorMajor: PaletteColor; + gridColSeparatorMinor: PaletteColor; + codeBackground: PaletteColor; + } + interface PaletteOptions { + negative: PaletteColor; + positive: PaletteColor; + unsavedbg: PaletteColor; + footerbg: PaletteColor; + faintHighlight: PaletteColor; + graphBorder: PaletteColor; + graphBackground: PaletteColor; + graphFont: PaletteColor; + gridInfoField: PaletteColor; + gridColSeparatorMajor: PaletteColor; + gridColSeparatorMinor: PaletteColor; + codeBackground: PaletteColor; + } +} + +export const themeFactory = (): CssVarsThemeOptions => { + const { palette } = createTheme(); + return { + colorSchemes: { + light: { + palette: { + background: { default: '#fff', paper: '#eee' }, + negative: palette.augmentColor({ color: { main: '#b22222' } }), + positive: palette.augmentColor({ color: { main: '#006400' } }), + unsavedbg: palette.augmentColor({ color: { main: '#FFFFF0' } }), + footerbg: palette.augmentColor({ color: { main: 'rgba(200,200,200,0.85)' } }), + faintHighlight: palette.augmentColor({ color: { main: 'rgba(225,255,225,0.2)' } }), + graphBorder: palette.augmentColor({ color: { main: 'rgba(0, 0, 0, 0.1)' } }), + graphBackground: palette.augmentColor({ color: { main: 'rgba(0, 0, 0, 0.1)' } }), + graphFont: palette.augmentColor({ color: { main: '#666' } }), + gridInfoField: palette.augmentColor({ color: { main: '#eee' } }), + gridColSeparatorMajor: palette.augmentColor({ color: { main: '#000' } }), + gridColSeparatorMinor: palette.augmentColor({ color: { main: '#ccc' } }), + codeBackground: palette.augmentColor({ color: { main: '#ccc' } }), + } + }, + dark: { + palette: { + background: { default: '#252525', paper: '#303030' }, + negative: palette.augmentColor({ color: { main: '#ff8d8d' } }), + positive: palette.augmentColor({ color: { main: '#90EE90' } }), + unsavedbg: palette.augmentColor({ color: { main: '#303020' } }), + footerbg: palette.augmentColor({ color: { main: 'rgba(55,55,55,0.85)' } }), + faintHighlight: palette.augmentColor({ color: { main: 'rgba(25,75,25,0.2)' } }), + graphBorder: palette.augmentColor({ color: { main: 'rgba(255, 255, 255, 0.2)' } }), + graphBackground: palette.augmentColor({ color: { main: 'rgba(255, 255, 255, 0.1)' } }), + graphFont: palette.augmentColor({ color: { main: '#ddd' } }), + gridInfoField: palette.augmentColor({ color: { main: '#444' } }), + gridColSeparatorMajor: palette.augmentColor({ color: { main: '#fff' } }), + gridColSeparatorMinor: palette.augmentColor({ color: { main: '#555' } }), + codeBackground: palette.augmentColor({ color: { main: '#666' } }), + } + } + } + }; +} \ No newline at end of file