1
0
Fork 0

clean up horizontal layout styles, use constants for spacing

This commit is contained in:
Sean Sube 2024-01-12 19:01:15 -06:00
parent f5506b17f0
commit f30c5f2d31
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
37 changed files with 205 additions and 220 deletions

View File

@ -2,6 +2,8 @@
import { doesExist, InvalidArgumentError, Maybe } from '@apextoaster/js-utils'; import { doesExist, InvalidArgumentError, Maybe } from '@apextoaster/js-utils';
import { ServerParams } from '../config.js'; import { ServerParams } from '../config.js';
import { FIXED_FLOAT, FIXED_INTEGER, STATUS_SUCCESS } from '../constants.js';
import { JobResponse, JobResponseWithRetry, SuccessJobResponse } from '../types/api-v2.js';
import { import {
FilterResponse, FilterResponse,
ModelResponse, ModelResponse,
@ -24,22 +26,6 @@ import {
} from '../types/params.js'; } from '../types/params.js';
import { range } from '../utils.js'; import { range } from '../utils.js';
import { ApiClient } from './base.js'; import { ApiClient } from './base.js';
import { JobResponse, JobResponseWithRetry, SuccessJobResponse } from '../types/api-v2.js';
/**
* Fixed precision for integer parameters.
*/
export const FIXED_INTEGER = 0;
/**
* Fixed precision for float parameters.
*
* The GUI limits the input steps based on the server parameters, but this does limit
* the maximum precision that can be sent back to the server, and may have to be
* increased in the future.
*/
export const FIXED_FLOAT = 2;
export const STATUS_SUCCESS = 200;
export function equalResponse(a: JobResponse, b: JobResponse): boolean { export function equalResponse(a: JobResponse, b: JobResponse): boolean {
return a.name === b.name; return a.name === b.name;

View File

@ -1,16 +1,17 @@
import { mustExist } from '@apextoaster/js-utils'; import { mustExist } from '@apextoaster/js-utils';
import { Grid, Typography } from '@mui/material'; import { Grid, Typography } from '@mui/material';
import { ReactNode, useContext } from 'react';
import * as React from 'react'; import * as React from 'react';
import { ReactNode, useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { STANDARD_SPACING } from '../constants.js';
import { OnnxState, StateContext } from '../state/full.js'; import { OnnxState, StateContext } from '../state/full.js';
import { JobStatus } from '../types/api-v2.js';
import { ErrorCard } from './card/ErrorCard.js'; import { ErrorCard } from './card/ErrorCard.js';
import { ImageCard } from './card/ImageCard.js'; import { ImageCard } from './card/ImageCard.js';
import { LoadingCard } from './card/LoadingCard.js'; import { LoadingCard } from './card/LoadingCard.js';
import { JobStatus } from '../types/api-v2.js';
export interface ImageHistoryProps { export interface ImageHistoryProps {
width: number; width: number;
@ -50,7 +51,7 @@ export function ImageHistory(props: ImageHistoryProps) {
return <Grid return <Grid
container container
spacing={2} spacing={STANDARD_SPACING}
>{ >{
// eslint-disable-next-line @typescript-eslint/no-magic-numbers // eslint-disable-next-line @typescript-eslint/no-magic-numbers
children.map(([key, child]) => <Grid item key={key} xs={12 / width}>{child}</Grid>) children.map(([key, child]) => <Grid item key={key} xs={12 / width}>{child}</Grid>)

View File

@ -2,6 +2,8 @@ import { Box, CircularProgress, Stack, Typography } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../constants';
export function LoadingScreen() { export function LoadingScreen() {
const { t } = useTranslation(); const { t } = useTranslation();
@ -13,7 +15,7 @@ export function LoadingScreen() {
}}> }}>
<Stack <Stack
direction='column' direction='column'
spacing={2} spacing={STANDARD_SPACING}
sx={{ alignItems: 'center' }} sx={{ alignItems: 'center' }}
> >
<CircularProgress /> <CircularProgress />

View File

@ -2,6 +2,7 @@ import { Box, Button, Container, Stack, Typography } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { STANDARD_MARGIN, STANDARD_SPACING } from '../constants.js';
import { STATE_KEY } from '../state/full.js'; import { STATE_KEY } from '../state/full.js';
import { Logo } from './Logo.js'; import { Logo } from './Logo.js';
@ -20,11 +21,11 @@ export function OnnxError(props: OnnxErrorProps) {
return ( return (
<Container> <Container>
<Box sx={{ my: 4 }}> <Box sx={{ my: STANDARD_MARGIN }}>
<Logo /> <Logo />
</Box> </Box>
<Box sx={{ my: 4 }}> <Box sx={{ my: STANDARD_MARGIN }}>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
{props.children} {props.children}
<Typography variant='body1'> <Typography variant='body1'>
This is a web UI for running ONNX models with GPU acceleration or in software, running locally or on a This is a web UI for running ONNX models with GPU acceleration or in software, running locally or on a

View File

@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-magic-numbers */
import { mustExist } from '@apextoaster/js-utils'; import { mustExist } from '@apextoaster/js-utils';
import { TabContext, TabList, TabPanel } from '@mui/lab'; import { TabContext, TabList, TabPanel } from '@mui/lab';
import { Box, Container, CssBaseline, Divider, Stack, Tab, useMediaQuery } from '@mui/material'; import { Box, Container, CssBaseline, Divider, Stack, Tab, useMediaQuery } from '@mui/material';
import { Breakpoint, SxProps, Theme, ThemeProvider, createTheme } from '@mui/material/styles'; import { SxProps, Theme, ThemeProvider, createTheme } from '@mui/material/styles';
import { Allotment } from 'allotment'; import { Allotment } from 'allotment';
import * as React from 'react'; import * as React from 'react';
import { useContext, useMemo } from 'react'; import { useContext, useMemo } from 'react';
import { useHash } from 'react-use/lib/useHash'; import { useHash } from 'react-use/lib/useHash';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow';
import { LAYOUT_MIN, LAYOUT_PROPORTIONS, LAYOUT_STYLES, STANDARD_MARGIN, STANDARD_SPACING } from '../constants.js';
import { Motd } from '../Motd.js'; import { Motd } from '../Motd.js';
import { OnnxState, StateContext } from '../state/full.js'; import { OnnxState, StateContext } from '../state/full.js';
import { Layout } from '../state/settings.js'; import { Layout } from '../state/settings.js';
@ -36,7 +35,10 @@ export function OnnxWeb(props: OnnxWebProps) {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const store = mustExist(useContext(StateContext)); const store = mustExist(useContext(StateContext));
const stateTheme = useStore(store, selectTheme); const stateTheme = useStore(store, selectTheme);
const layout = useStore(store, selectLayout, shallow); const historyWidth = useStore(store, selectHistoryWidth);
const direction = useStore(store, selectDirection);
const layout = LAYOUT_STYLES[direction];
const theme = useMemo( const theme = useMemo(
() => createTheme({ () => createTheme({
@ -47,17 +49,17 @@ export function OnnxWeb(props: OnnxWebProps) {
[prefersDarkMode, stateTheme], [prefersDarkMode, stateTheme],
); );
const historyStyle: SxProps<Theme> = LAYOUT_STYLES[layout.direction].history.style; const historyStyle: SxProps<Theme> = layout.history.style;
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<CssBaseline /> <CssBaseline />
<Container maxWidth={LAYOUT_STYLES[layout.direction].container}> <Container maxWidth={layout.container}>
<Box sx={{ my: 4 }}> <Box sx={{ my: STANDARD_MARGIN }}>
<Logo /> <Logo />
</Box> </Box>
{props.motd && <Motd />} {props.motd && <Motd />}
{renderBody(layout, historyStyle)} {renderBody(direction, historyStyle, historyWidth)}
</Container> </Container>
</ThemeProvider> </ThemeProvider>
); );
@ -67,52 +69,19 @@ export function selectTheme(state: OnnxState) {
return state.theme; return state.theme;
} }
export function selectLayout(state: OnnxState) { export function selectDirection(state: OnnxState) {
return { return state.layout;
direction: state.layout,
width: state.historyWidth,
};
} }
export const LAYOUT_STYLES = { export function selectHistoryWidth(state: OnnxState) {
horizontal: { return state.historyWidth;
container: false, }
control: {
width: '30%',
},
direction: 'row',
divider: 'vertical',
history: {
style: {
marginLeft: 4,
maxHeight: '85vb',
overflowY: 'auto',
},
width: 4,
},
},
vertical: {
container: 'lg' as Breakpoint,
control: {
width: undefined,
},
direction: 'column',
divider: 'horizontal',
history: {
style: {
mx: 4,
my: 4,
},
width: 2,
},
},
} as const;
function renderBody(layout: ReturnType<typeof selectLayout>, historyStyle: SxProps<Theme>) { function renderBody(direction: Layout, historyStyle: SxProps<Theme>, historyWidth: number) {
if (layout.direction === 'vertical') { if (direction === 'vertical') {
return <VerticalBody {...layout} style={historyStyle} />; return <VerticalBody direction={direction} style={historyStyle} width={historyWidth} />;
} else { } else {
return <HorizontalBody {...layout} style={historyStyle} />; return <HorizontalBody direction={direction} style={historyStyle} width={historyWidth} />;
} }
} }
@ -126,9 +95,9 @@ export interface BodyProps {
export function HorizontalBody(props: BodyProps) { export function HorizontalBody(props: BodyProps) {
const layout = LAYOUT_STYLES[props.direction]; const layout = LAYOUT_STYLES[props.direction];
return <Allotment separator className='body-allotment' minSize={300}> return <Allotment separator className='body-allotment' minSize={LAYOUT_MIN} defaultSizes={LAYOUT_PROPORTIONS} snap>
<TabGroup direction={props.direction} /> <TabGroup direction={props.direction} />
<Box sx={layout.history.style}> <Box className='box-history' sx={layout.history.style}>
<ImageHistory width={props.width} /> <ImageHistory width={props.width} />
</Box> </Box>
</Allotment>; </Allotment>;
@ -137,10 +106,10 @@ export function HorizontalBody(props: BodyProps) {
export function VerticalBody(props: BodyProps) { export function VerticalBody(props: BodyProps) {
const layout = LAYOUT_STYLES[props.direction]; const layout = LAYOUT_STYLES[props.direction];
return <Stack direction={layout.direction} spacing={2}> return <Stack direction={layout.direction} spacing={STANDARD_SPACING}>
<TabGroup direction={props.direction} /> <TabGroup direction={props.direction} />
<Divider flexItem variant='middle' orientation={layout.divider} /> <Divider flexItem variant='middle' orientation={layout.divider} />
<Box sx={layout.history.style}> <Box className='box-history' sx={layout.history.style}>
<ImageHistory width={props.width} /> <ImageHistory width={props.width} />
</Box> </Box>
</Stack>; </Stack>;
@ -155,7 +124,7 @@ export function TabGroup(props: TabGroupProps) {
const [hash, setHash] = useHash(); const [hash, setHash] = useHash();
return <Stack direction='column' minWidth={layout.control.width} sx={{ mx: 4 }}> return <Stack direction='column' minWidth={layout.control.width} sx={{ mx: STANDARD_MARGIN }}>
<TabContext value={getTab(hash)}> <TabContext value={getTab(hash)}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}> <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList onChange={(_e, idx) => { <TabList onChange={(_e, idx) => {

View File

@ -21,12 +21,15 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { STANDARD_SPACING } from '../constants.js';
import { OnnxState, StateContext } from '../state/full.js'; import { OnnxState, StateContext } from '../state/full.js';
import { ImageMetadata } from '../types/api.js'; import { AnyImageMetadata } from '../types/api-v2.js';
import { DeepPartial } from '../types/model.js'; import { DeepPartial } from '../types/model.js';
import { BaseImgParams, HighresParams, ModelParams, Txt2ImgParams, UpscaleParams } from '../types/params.js'; import { BaseImgParams, HighresParams, ModelParams, Txt2ImgParams, UpscaleParams } from '../types/params.js';
import { downloadAsJson } from '../utils.js'; import { downloadAsJson } from '../utils.js';
export type PartialImageMetadata = DeepPartial<AnyImageMetadata>;
export const ALLOWED_EXTENSIONS = ['.json','.jpg','.jpeg','.png','.txt','.webp']; export const ALLOWED_EXTENSIONS = ['.json','.jpg','.jpeg','.png','.txt','.webp'];
export const EXTENSION_FILTER = ALLOWED_EXTENSIONS.join(','); export const EXTENSION_FILTER = ALLOWED_EXTENSIONS.join(',');
@ -51,7 +54,7 @@ export function Profiles(props: ProfilesProps) {
const [profileName, setProfileName] = useState(''); const [profileName, setProfileName] = useState('');
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2}> return <Stack direction='row' spacing={STANDARD_SPACING}>
<Autocomplete <Autocomplete
id='profile-select' id='profile-select'
options={profiles} options={profiles}
@ -193,7 +196,7 @@ export function selectProfiles(state: OnnxState) {
return state.profiles; return state.profiles;
} }
export async function loadParamsFromFile(file: File): Promise<DeepPartial<ImageMetadata>> { export async function loadParamsFromFile(file: File): Promise<PartialImageMetadata> {
const parts = file.name.toLocaleLowerCase().split('.'); const parts = file.name.toLocaleLowerCase().split('.');
const ext = parts[parts.length - 1]; const ext = parts[parts.length - 1];
@ -211,7 +214,7 @@ export async function loadParamsFromFile(file: File): Promise<DeepPartial<ImageM
} }
} }
export async function parseImageParams(file: File): Promise<DeepPartial<ImageMetadata>> { export async function parseImageParams(file: File): Promise<PartialImageMetadata> {
const tags = await ExifReader.load(file); const tags = await ExifReader.load(file);
// some parsers expect uppercase, some use lowercase, read both // some parsers expect uppercase, some use lowercase, read both
@ -251,8 +254,8 @@ export function decodeTag(tag: Maybe<ExifReader.XmpTag | (ExifReader.NumberTag &
throw new InvalidArgumentError('tag value cannot be decoded'); throw new InvalidArgumentError('tag value cannot be decoded');
} }
export async function parseJSONParams(json: string): Promise<DeepPartial<ImageMetadata>> { export async function parseJSONParams(json: string): Promise<PartialImageMetadata> {
const data = JSON.parse(json) as DeepPartial<ImageMetadata>; const data = JSON.parse(json) as PartialImageMetadata;
const params: Partial<Txt2ImgParams> = { const params: Partial<Txt2ImgParams> = {
...data.params, ...data.params,
}; };
@ -276,7 +279,7 @@ export function isProbablyJSON(maybeJSON: unknown): boolean {
export const NEGATIVE_PROMPT_TAG = 'Negative prompt:'; export const NEGATIVE_PROMPT_TAG = 'Negative prompt:';
export async function parseAutoComment(comment: string): Promise<DeepPartial<ImageMetadata>> { export async function parseAutoComment(comment: string): Promise<PartialImageMetadata> {
if (isProbablyJSON(comment)) { if (isProbablyJSON(comment)) {
return parseJSONParams(comment); return parseJSONParams(comment);
} }

View File

@ -11,6 +11,7 @@ import { shallow } from 'zustand/shallow';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { FailedJobResponse, JobStatus, RetryParams, UnknownJobResponse } from '../../types/api-v2.js'; import { FailedJobResponse, JobStatus, RetryParams, UnknownJobResponse } from '../../types/api-v2.js';
import { STANDARD_SPACING } from '../../constants.js';
export interface ErrorCardProps { export interface ErrorCardProps {
image: FailedJobResponse | UnknownJobResponse; image: FailedJobResponse | UnknownJobResponse;
@ -48,7 +49,7 @@ export function ErrorCard(props: ErrorCardProps) {
}}> }}>
<Stack <Stack
direction='column' direction='column'
spacing={2} spacing={STANDARD_SPACING}
sx={{ alignItems: 'center' }} sx={{ alignItems: 'center' }}
> >
<Alert severity='error'> <Alert severity='error'>
@ -56,7 +57,7 @@ export function ErrorCard(props: ErrorCardProps) {
<br /> <br />
{t(getImageErrorReason(image))} {t(getImageErrorReason(image))}
</Alert> </Alert>
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<Tooltip title={t('tooltip.retry')}> <Tooltip title={t('tooltip.retry')}>
<IconButton onClick={() => retry.mutate()}> <IconButton onClick={() => retry.mutate()}>
<Replay /> <Replay />

View File

@ -10,7 +10,7 @@ import { shallow } from 'zustand/shallow';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { range, visibleIndex } from '../../utils.js'; import { range, visibleIndex } from '../../utils.js';
import { BLEND_SOURCES } from '../../constants.js'; import { BLEND_SOURCES, STANDARD_SPACING } from '../../constants.js';
import { JobResponse, SuccessJobResponse } from '../../types/api-v2.js'; import { JobResponse, SuccessJobResponse } from '../../types/api-v2.js';
export interface ImageCardProps { export interface ImageCardProps {
@ -120,7 +120,7 @@ export function ImageCard(props: ImageCardProps) {
/> />
<CardContent> <CardContent>
<Box textAlign='center'> <Box textAlign='center'>
<Grid container spacing={2}> <Grid container spacing={STANDARD_SPACING}>
<GridItem xs={4}> <GridItem xs={4}>
<Tooltip title={t('tooltip.previous')}> <Tooltip title={t('tooltip.previous')}>
<IconButton onClick={() => { <IconButton onClick={() => {

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { POLL_TIME } from '../../config.js'; import { POLL_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { JobResponse, JobStatus } from '../../types/api-v2.js'; import { JobResponse, JobStatus } from '../../types/api-v2.js';
import { visibleIndex } from '../../utils.js'; import { visibleIndex } from '../../utils.js';
@ -71,7 +71,7 @@ export function LoadingCard(props: LoadingCardProps) {
}}> }}>
<Stack <Stack
direction='column' direction='column'
spacing={2} spacing={STANDARD_SPACING}
sx={{ alignItems: 'center' }} sx={{ alignItems: 'center' }}
> >
{renderProgress()} {renderProgress()}

View File

@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { STALE_TIME } from '../../config.js'; import { STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { BaseImgParams } from '../../types/params.js'; import { BaseImgParams } from '../../types/params.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
@ -46,7 +46,7 @@ export function ImageControl(props: ImageControlProps) {
staleTime: STALE_TIME, staleTime: STALE_TIME,
}); });
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<Stack direction='row' spacing={4}> <Stack direction='row' spacing={4}>
<QueryList <QueryList
id='schedulers' id='schedulers'

View File

@ -5,11 +5,11 @@ import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STALE_TIME } from '../../config.js'; import { STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext } from '../../state/full.js'; import { ClientContext } from '../../state/full.js';
import { JobType } from '../../types/api-v2.js';
import { ModelParams } from '../../types/params.js'; import { ModelParams } from '../../types/params.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { JobType } from '../../types/api-v2.js';
export interface ModelControlProps { export interface ModelControlProps {
model: ModelParams; model: ModelParams;
@ -35,7 +35,7 @@ export function ModelControl(props: ModelControlProps) {
staleTime: STALE_TIME, staleTime: STALE_TIME,
}); });
return <Stack direction='row' spacing={2}> return <Stack direction='row' spacing={STANDARD_SPACING}>
<QueryList <QueryList
id='platforms' id='platforms'
labelKey='platform' labelKey='platform'

View File

@ -7,6 +7,7 @@ import { useStore } from 'zustand';
import { PipelineGrid } from '../../client/utils.js'; import { PipelineGrid } from '../../client/utils.js';
import { OnnxState, StateContext } from '../../state/full.js'; import { OnnxState, StateContext } from '../../state/full.js';
import { VARIABLE_PARAMETERS } from '../../types/chain.js'; import { VARIABLE_PARAMETERS } from '../../types/chain.js';
import { STANDARD_SPACING } from '../../constants.js';
export interface VariableControlProps { export interface VariableControlProps {
selectGrid: (state: OnnxState) => PipelineGrid; selectGrid: (state: OnnxState) => PipelineGrid;
@ -20,7 +21,7 @@ export function VariableControl(props: VariableControlProps) {
const grid = useStore(store, props.selectGrid); const grid = useStore(store, props.selectGrid);
const stack = [ const stack = [
<Stack direction='row' spacing={2} key='variable-enable'> <Stack direction='row' spacing={STANDARD_SPACING} key='variable-enable'>
<FormControl> <FormControl>
<FormControlLabel <FormControlLabel
label='Grid Mode' label='Grid Mode'
@ -37,7 +38,7 @@ export function VariableControl(props: VariableControlProps) {
if (grid.enabled) { if (grid.enabled) {
stack.push( stack.push(
<Stack direction='row' spacing={2} key='variable-row'> <Stack direction='row' spacing={STANDARD_SPACING} key='variable-row'>
<FormControl> <FormControl>
<InputLabel id='TODO'>Columns</InputLabel> <InputLabel id='TODO'>Columns</InputLabel>
<Select onChange={(event) => props.setGrid({ <Select onChange={(event) => props.setGrid({
@ -56,7 +57,7 @@ export function VariableControl(props: VariableControlProps) {
}, },
})} /> })} />
</Stack>, </Stack>,
<Stack direction='row' spacing={2} key='variable-column'> <Stack direction='row' spacing={STANDARD_SPACING} key='variable-column'>
<FormControl> <FormControl>
<InputLabel id='TODO'>Rows</InputLabel> <InputLabel id='TODO'>Rows</InputLabel>
<Select onChange={(event) => props.setGrid({ <Select onChange={(event) => props.setGrid({
@ -78,7 +79,7 @@ export function VariableControl(props: VariableControlProps) {
); );
} }
return <Stack direction='column' spacing={2}>{...stack}</Stack>; return <Stack direction='column' spacing={STANDARD_SPACING}>{...stack}</Stack>;
} }
export function parameterList(exclude?: Array<string>) { export function parameterList(exclude?: Array<string>) {

View File

@ -1,7 +1,7 @@
import { Alert, AlertTitle, Typography } from '@mui/material'; import { Alert, AlertTitle, Typography } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { PARAM_VERSION } from '../../config.js'; import { PARAM_VERSION } from '../../constants';
export interface ParamsVersionErrorProps { export interface ParamsVersionErrorProps {
root: string; root: string;

View File

@ -5,6 +5,7 @@ import { memo, useContext, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { STANDARD_SPACING } from '../../constants.js';
import { OnnxState, StateContext } from '../../state/full.js'; import { OnnxState, StateContext } from '../../state/full.js';
export interface EditableListProps<T> { export interface EditableListProps<T> {
@ -30,7 +31,7 @@ export function EditableList<T>(props: EditableListProps<T>) {
const [nextSource, setNextSource] = useState(''); const [nextSource, setNextSource] = useState('');
const RenderMemo = useMemo(() => memo(renderItem), [renderItem]); const RenderMemo = useMemo(() => memo(renderItem), [renderItem]);
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
{items.map((model, idx) => {items.map((model, idx) =>
<RenderMemo <RenderMemo
key={idx} key={idx}
@ -39,7 +40,7 @@ export function EditableList<T>(props: EditableListProps<T>) {
onRemove={removeItem} onRemove={removeItem}
/> />
)} )}
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<TextField <TextField
label={t('extras.label')} label={t('extras.label')}
variant='outlined' variant='outlined'

View File

@ -4,6 +4,8 @@ import { Button, Stack, Typography } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../constants';
export interface ImageInputProps { export interface ImageInputProps {
filter: string; filter: string;
image?: Maybe<Blob>; image?: Maybe<Blob>;
@ -35,7 +37,7 @@ export function ImageInput(props: ImageInputProps) {
} }
} }
return <Stack direction='row' spacing={2}> return <Stack direction='row' spacing={STANDARD_SPACING}>
<Stack> <Stack>
<Button component='label' startIcon={<PhotoCamera />} variant='outlined'> <Button component='label' startIcon={<PhotoCamera />} variant='outlined'>
{props.label} {props.label}

View File

@ -5,7 +5,7 @@ import { throttle } from 'lodash';
import React, { RefObject, useContext, useEffect, useMemo, useRef } from 'react'; import React, { RefObject, useContext, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SAVE_TIME } from '../../config.js'; import { SAVE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ConfigContext, LoggerContext, StateContext } from '../../state/full.js'; import { ConfigContext, LoggerContext, StateContext } from '../../state/full.js';
import { BrushParams } from '../../types/params.js'; import { BrushParams } from '../../types/params.js';
import { imageFromBlob } from '../../utils.js'; import { imageFromBlob } from '../../utils.js';
@ -255,8 +255,8 @@ export function MaskCanvas(props: MaskCanvasProps) {
display: 'none', display: 'none',
}; };
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<Button <Button
variant='outlined' variant='outlined'
startIcon={<Download />} startIcon={<Download />}
@ -348,7 +348,7 @@ export function MaskCanvas(props: MaskCanvasProps) {
}} }}
/> />
</Stack> </Stack>
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<Button <Button
variant='outlined' variant='outlined'
startIcon={<FormatColorFill />} startIcon={<FormatColorFill />}

View File

@ -3,6 +3,8 @@ import { Slider, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../constants';
export function parseNumber(num: string, decimal = false): number { export function parseNumber(num: string, decimal = false): number {
if (decimal) { if (decimal) {
return parseFloat(num); return parseFloat(num);
@ -29,7 +31,7 @@ export function NumericField(props: ImageControlProps) {
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<TextField <TextField
error={error} error={error}
label={label} label={label}

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { STALE_TIME } from '../../config.js'; import { STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, OnnxState, StateContext } from '../../state/full.js';
import { ModelResponse, NetworkModel } from '../../types/api.js'; import { ModelResponse, NetworkModel } from '../../types/api.js';
import { QueryMenu, QueryMenuComplete, QueryMenuFilter } from '../input/QueryMenu.js'; import { QueryMenu, QueryMenuComplete, QueryMenuFilter } from '../input/QueryMenu.js';
@ -55,7 +55,7 @@ export function PromptTextBlock(props: PromptTextBlockProps) {
return []; return [];
}, [models, prompt]); }, [models, prompt]);
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<TextField <TextField
label={t('parameter.prompt')} label={t('parameter.prompt')}
variant='outlined' variant='outlined'
@ -67,7 +67,7 @@ export function PromptTextBlock(props: PromptTextBlockProps) {
}); });
}} }}
/> />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
{tokens.map((token) => <Chip {tokens.map((token) => <Chip
color={prompt.includes(token) ? 'primary' : 'default'} color={prompt.includes(token) ? 'primary' : 'default'}
label={token} label={token}
@ -139,13 +139,13 @@ export function PromptInput(props: PromptInputProps) {
result: wildcards, result: wildcards,
}), [wildcards.status]); }), [wildcards.status]);
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<PromptTextBlock <PromptTextBlock
models={models.data} models={models.data}
onChange={onChange} onChange={onChange}
selector={selector} selector={selector}
/> />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<ModelMenu <ModelMenu
id='inversion' id='inversion'
labelKey='model.inversion' labelKey='model.inversion'

View File

@ -2,6 +2,7 @@ import { Button, MenuItem, Select, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../../constants.js';
import { CorrectionArch, CorrectionModel, ModelFormat } from '../../../types/model.js'; import { CorrectionArch, CorrectionModel, ModelFormat } from '../../../types/model.js';
export interface CorrectionModelInputProps { export interface CorrectionModelInputProps {
@ -16,7 +17,7 @@ export function CorrectionModelInput(props: CorrectionModelInputProps) {
const { key, model, onChange, onRemove } = props; const { key, model, onChange, onRemove } = props;
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2} key={key}> return <Stack direction='row' spacing={STANDARD_SPACING} key={key}>
<TextField <TextField
label={t('extras.label')} label={t('extras.label')}
value={model.label} value={model.label}

View File

@ -2,6 +2,7 @@ import { Button, MenuItem, Select, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../../constants.js';
import { DiffusionModel, ModelFormat } from '../../../types/model.js'; import { DiffusionModel, ModelFormat } from '../../../types/model.js';
export interface DiffusionModelInputProps { export interface DiffusionModelInputProps {
@ -16,7 +17,7 @@ export function DiffusionModelInput(props: DiffusionModelInputProps) {
const { key, model, onChange, onRemove } = props; const { key, model, onChange, onRemove } = props;
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2} key={key}> return <Stack direction='row' spacing={STANDARD_SPACING} key={key}>
<TextField <TextField
label={t('extras.label')} label={t('extras.label')}
value={model.label} value={model.label}

View File

@ -2,6 +2,7 @@ import { Button, MenuItem, Select, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../../constants.js';
import { ExtraNetwork, ModelFormat, NetworkModel, NetworkType } from '../../../types/model.js'; import { ExtraNetwork, ModelFormat, NetworkModel, NetworkType } from '../../../types/model.js';
export interface ExtraNetworkInputProps { export interface ExtraNetworkInputProps {
@ -16,7 +17,7 @@ export function ExtraNetworkInput(props: ExtraNetworkInputProps) {
const { key, model, onChange, onRemove } = props; const { key, model, onChange, onRemove } = props;
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2} key={key}> return <Stack direction='row' spacing={STANDARD_SPACING} key={key}>
<TextField <TextField
label={t('extras.label')} label={t('extras.label')}
value={model.label} value={model.label}

View File

@ -2,6 +2,7 @@ import { Button, MenuItem, Select, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../../constants.js';
import { AnyFormat, ExtraSource } from '../../../types/model.js'; import { AnyFormat, ExtraSource } from '../../../types/model.js';
export interface ExtraSourceInputProps { export interface ExtraSourceInputProps {
@ -16,7 +17,7 @@ export function ExtraSourceInput(props: ExtraSourceInputProps) {
const { key, model, onChange, onRemove } = props; const { key, model, onChange, onRemove } = props;
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2} key={key}> return <Stack direction='row' spacing={STANDARD_SPACING} key={key}>
<TextField <TextField
label={t('extras.name')} label={t('extras.name')}
value={model.name} value={model.name}

View File

@ -2,6 +2,7 @@ import { Button, MenuItem, Select, Stack, TextField } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { STANDARD_SPACING } from '../../../constants.js';
import { ModelFormat, UpscalingArch, UpscalingModel } from '../../../types/model.js'; import { ModelFormat, UpscalingArch, UpscalingModel } from '../../../types/model.js';
import { NumericField } from '../NumericField.js'; import { NumericField } from '../NumericField.js';
@ -17,7 +18,7 @@ export function UpscalingModelInput(props: UpscalingModelInputProps) {
const { key, model, onChange, onRemove } = props; const { key, model, onChange, onRemove } = props;
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={2} key={key}> return <Stack direction='row' spacing={STANDARD_SPACING} key={key}>
<TextField <TextField
label={t('extras.label')} label={t('extras.label')}
value={model.label} value={model.label}

View File

@ -1,3 +1,8 @@
.body-allotment { .body-allotment {
height: 85vb; height: 90vb;
} }
.box-history {
max-height: 90vh;
overflow-y: auto;
}

View File

@ -7,8 +7,7 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { IMAGE_FILTER } from '../../config.js'; import { BLEND_SOURCES, IMAGE_FILTER, STANDARD_SPACING } from '../../constants.js';
import { BLEND_SOURCES } from '../../constants.js';
import { ClientContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, OnnxState, StateContext } from '../../state/full.js';
import { TabState } from '../../state/types.js'; import { TabState } from '../../state/types.js';
import { BlendParams, BrushParams, ModelParams, UpscaleParams } from '../../types/params.js'; import { BlendParams, BrushParams, ModelParams, UpscaleParams } from '../../types/params.js';
@ -44,7 +43,7 @@ export function Blend() {
const sources = mustDefault(blend.sources, []); const sources = mustDefault(blend.sources, []);
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
{range(BLEND_SOURCES).map((idx) => {range(BLEND_SOURCES).map((idx) =>
<ImageInput <ImageInput
key={`source-${idx.toFixed(0)}`} key={`source-${idx.toFixed(0)}`}

View File

@ -7,9 +7,10 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { IMAGE_FILTER, STALE_TIME } from '../../config.js'; import { IMAGE_FILTER, STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { TabState } from '../../state/types.js'; import { TabState } from '../../state/types.js';
import { JobType } from '../../types/api-v2.js';
import { HighresParams, Img2ImgParams, ModelParams, UpscaleParams } from '../../types/params.js'; import { HighresParams, Img2ImgParams, ModelParams, UpscaleParams } from '../../types/params.js';
import { Profiles } from '../Profiles.js'; import { Profiles } from '../Profiles.js';
import { HighresControl } from '../control/HighresControl.js'; import { HighresControl } from '../control/HighresControl.js';
@ -19,7 +20,6 @@ import { UpscaleControl } from '../control/UpscaleControl.js';
import { ImageInput } from '../input/ImageInput.js'; import { ImageInput } from '../input/ImageInput.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { JobType } from '../../types/api-v2.js';
export function Img2Img() { export function Img2Img() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
@ -57,7 +57,7 @@ export function Img2Img() {
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
<Profiles <Profiles
selectHighres={selectHighres} selectHighres={selectHighres}
selectModel={selectModel} selectModel={selectModel}
@ -80,7 +80,7 @@ export function Img2Img() {
}} }}
/> />
<ImageControl selector={selectParams} onChange={setImg2Img} /> <ImageControl selector={selectParams} onChange={setImg2Img} />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<QueryList <QueryList
id='control' id='control'
labelKey='model.control' labelKey='model.control'

View File

@ -7,9 +7,10 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { IMAGE_FILTER, STALE_TIME } from '../../config.js'; import { IMAGE_FILTER, STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { TabState } from '../../state/types.js'; import { TabState } from '../../state/types.js';
import { JobType } from '../../types/api-v2.js';
import { BrushParams, HighresParams, InpaintParams, ModelParams, UpscaleParams } from '../../types/params.js'; import { BrushParams, HighresParams, InpaintParams, ModelParams, UpscaleParams } from '../../types/params.js';
import { Profiles } from '../Profiles.js'; import { Profiles } from '../Profiles.js';
import { HighresControl } from '../control/HighresControl.js'; import { HighresControl } from '../control/HighresControl.js';
@ -21,7 +22,6 @@ import { ImageInput } from '../input/ImageInput.js';
import { MaskCanvas } from '../input/MaskCanvas.js'; import { MaskCanvas } from '../input/MaskCanvas.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { JobType } from '../../types/api-v2.js';
export function Inpaint() { export function Inpaint() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
@ -89,7 +89,7 @@ export function Inpaint() {
} }
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
<Profiles <Profiles
selectHighres={selectHighres} selectHighres={selectHighres}
selectModel={selectModel} selectModel={selectModel}
@ -153,7 +153,7 @@ export function Inpaint() {
}); });
}} }}
/> />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<QueryList <QueryList
id='masks' id='masks'
labelKey={'maskFilter'} labelKey={'maskFilter'}
@ -200,7 +200,7 @@ export function Inpaint() {
} }
</Select> </Select>
</FormControl> </FormControl>
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<FormControlLabel <FormControlLabel
label={t('parameter.fillColor')} label={t('parameter.fillColor')}
sx={{ mx: 1 }} sx={{ mx: 1 }}

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { STALE_TIME } from '../../config.js'; import { STALE_TIME, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, OnnxState, StateContext } from '../../state/full.js';
import { import {
CorrectionModel, CorrectionModel,
@ -111,13 +111,13 @@ export function Models() {
}, [result.status]); }, [result.status]);
if (result.status === 'error') { if (result.status === 'error') {
return <Stack spacing={2} direction='row' sx={{ alignItems: 'center' }}> return <Stack spacing={STANDARD_SPACING} direction='row' sx={{ alignItems: 'center' }}>
<Alert severity='error'>Error</Alert> <Alert severity='error'>Error</Alert>
</Stack>; </Stack>;
} }
if (result.status === 'loading') { if (result.status === 'loading') {
return <Stack spacing={2} direction='row' sx={{ alignItems: 'center' }}> return <Stack spacing={STANDARD_SPACING} direction='row' sx={{ alignItems: 'center' }}>
<CircularProgress /> <CircularProgress />
</Stack>; </Stack>;
} }
@ -127,7 +127,7 @@ export function Models() {
// TODO: do something with resp // TODO: do something with resp
} }
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<Accordion> <Accordion>
<AccordionSummary> <AccordionSummary>
{t('modelType.diffusion', { count: 10 })} {t('modelType.diffusion', { count: 10 })}

View File

@ -11,6 +11,7 @@ import { ConfigContext, StateContext, STATE_KEY } from '../../state/full.js';
import { getTheme } from '../utils.js'; import { getTheme } from '../utils.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { downloadAsJson } from '../../utils.js'; import { downloadAsJson } from '../../utils.js';
import { STANDARD_SPACING } from '../../constants.js';
function removeBlobs(key: string, value: unknown): unknown { function removeBlobs(key: string, value: unknown): unknown {
if (value instanceof Blob || value instanceof File) { if (value instanceof Blob || value instanceof File) {
@ -39,7 +40,7 @@ export function Settings() {
const [root, setRoot] = useState(getApiRoot(config)); const [root, setRoot] = useState(getApiRoot(config));
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack spacing={2}> return <Stack spacing={STANDARD_SPACING}>
<NumericField <NumericField
label={t('setting.history.limit')} label={t('setting.history.limit')}
min={2} min={2}
@ -67,7 +68,7 @@ export function Settings() {
scheduler: event.target.value, scheduler: event.target.value,
}); });
}} /> }} />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<TextField variant='outlined' label={t('setting.server')} value={root} onChange={(event) => { <TextField variant='outlined' label={t('setting.server')} value={root} onChange={(event) => {
setRoot(event.target.value); setRoot(event.target.value);
}} /> }} />
@ -82,7 +83,7 @@ export function Settings() {
{config.params.version} {config.params.version}
</Alert> </Alert>
</Stack> </Stack>
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<TextField variant='outlined' label={t('setting.state.label')} value={json} onChange={(event) => { <TextField variant='outlined' label={t('setting.state.label')} value={json} onChange={(event) => {
setJson(event.target.value); setJson(event.target.value);
}} /> }} />
@ -109,7 +110,7 @@ export function Settings() {
}} }}
/> />
} label={t('setting.darkMode')} /> } label={t('setting.darkMode')} />
<Stack direction='row' spacing={2}> <Stack direction='row' spacing={STANDARD_SPACING}>
<Button onClick={() => state.resetTxt2Img()} color='warning'>{t('setting.reset.txt2img')}</Button> <Button onClick={() => state.resetTxt2Img()} color='warning'>{t('setting.reset.txt2img')}</Button>
<Button onClick={() => state.resetImg2Img()} color='warning'>{t('setting.reset.img2img')}</Button> <Button onClick={() => state.resetImg2Img()} color='warning'>{t('setting.reset.img2img')}</Button>
<Button onClick={() => state.resetInpaint()} color='warning'>{t('setting.reset.inpaint')}</Button> <Button onClick={() => state.resetInpaint()} color='warning'>{t('setting.reset.inpaint')}</Button>

View File

@ -8,8 +8,10 @@ import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { PipelineGrid, makeTxt2ImgGridPipeline } from '../../client/utils.js'; import { PipelineGrid, makeTxt2ImgGridPipeline } from '../../client/utils.js';
import { STANDARD_SPACING } from '../../constants.js';
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state/full.js';
import { TabState } from '../../state/types.js'; import { TabState } from '../../state/types.js';
import { JobType } from '../../types/api-v2.js';
import { HighresParams, ModelParams, Txt2ImgParams, UpscaleParams } from '../../types/params.js'; import { HighresParams, ModelParams, Txt2ImgParams, UpscaleParams } from '../../types/params.js';
import { Profiles } from '../Profiles.js'; import { Profiles } from '../Profiles.js';
import { HighresControl } from '../control/HighresControl.js'; import { HighresControl } from '../control/HighresControl.js';
@ -18,7 +20,6 @@ import { ModelControl } from '../control/ModelControl.js';
import { UpscaleControl } from '../control/UpscaleControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js';
import { VariableControl } from '../control/VariableControl.js'; import { VariableControl } from '../control/VariableControl.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { JobType } from '../../types/api-v2.js';
export function SizeControl() { export function SizeControl() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
@ -88,7 +89,7 @@ export function Txt2Img() {
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
<Profiles <Profiles
selectHighres={selectHighres} selectHighres={selectHighres}
selectModel={selectModel} selectModel={selectModel}

View File

@ -7,9 +7,10 @@ import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { IMAGE_FILTER } from '../../config.js'; import { IMAGE_FILTER, STANDARD_SPACING } from '../../constants.js';
import { ClientContext, OnnxState, StateContext } from '../../state/full.js'; import { ClientContext, OnnxState, StateContext } from '../../state/full.js';
import { TabState } from '../../state/types.js'; import { TabState } from '../../state/types.js';
import { JobType } from '../../types/api-v2.js';
import { HighresParams, ModelParams, UpscaleParams, UpscaleReqParams } from '../../types/params.js'; import { HighresParams, ModelParams, UpscaleParams, UpscaleReqParams } from '../../types/params.js';
import { Profiles } from '../Profiles.js'; import { Profiles } from '../Profiles.js';
import { HighresControl } from '../control/HighresControl.js'; import { HighresControl } from '../control/HighresControl.js';
@ -17,7 +18,6 @@ import { ModelControl } from '../control/ModelControl.js';
import { UpscaleControl } from '../control/UpscaleControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js';
import { ImageInput } from '../input/ImageInput.js'; import { ImageInput } from '../input/ImageInput.js';
import { PromptInput } from '../input/PromptInput.js'; import { PromptInput } from '../input/PromptInput.js';
import { JobType } from '../../types/api-v2.js';
export function Upscale() { export function Upscale() {
async function uploadSource() { async function uploadSource() {
@ -43,7 +43,7 @@ export function Upscale() {
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={STANDARD_SPACING}>
<Profiles <Profiles
selectHighres={selectHighres} selectHighres={selectHighres}
selectModel={selectModel} selectModel={selectModel}

View File

@ -1,7 +1,6 @@
import { doesExist, Maybe } from '@apextoaster/js-utils'; import { doesExist, Maybe } from '@apextoaster/js-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
import { STATUS_SUCCESS } from './client/api.js';
import { import {
HighresParams, HighresParams,
Img2ImgParams, Img2ImgParams,
@ -11,6 +10,7 @@ import {
Txt2ImgParams, Txt2ImgParams,
UpscaleParams, UpscaleParams,
} from './types/params.js'; } from './types/params.js';
import { STATUS_SUCCESS } from './constants.js';
export interface ConfigBoolean { export interface ConfigBoolean {
default: boolean; default: boolean;
@ -93,13 +93,6 @@ export interface Config<T = ClientParams> {
params: T; params: T;
} }
export const IMAGE_FILTER = '.bmp, .jpg, .jpeg, .png';
export const PARAM_VERSION = '>=0.10.0';
export const STALE_TIME = 300_000; // 5 minutes
export const POLL_TIME = 5_000; // 5 seconds
export const SAVE_TIME = 5_000; // 5 seconds
export async function loadConfig(): Promise<Config> { export async function loadConfig(): Promise<Config> {
const configPath = new URL('./config.json', window.location.href); const configPath = new URL('./config.json', window.location.href);
const configReq = await fetch(configPath); const configReq = await fetch(configPath);

View File

@ -1,3 +1,4 @@
import { Breakpoint } from '@mui/material/styles';
export const BLEND_SOURCES = 2; export const BLEND_SOURCES = 2;
@ -29,3 +30,66 @@ export const DEFAULT_HISTORY = {
*/ */
scrollback: 4, scrollback: 4,
}; };
export const STANDARD_MARGIN = 4; // translated into 32px by mui
export const STANDARD_SPACING = 2;
export const LAYOUT_MIN = 300;
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
export const LAYOUT_PROPORTIONS = [100, 200];
export const LAYOUT_STYLES = {
horizontal: {
container: false,
control: {
width: '30%',
},
direction: 'row',
divider: 'vertical',
history: {
style: {
ml: STANDARD_MARGIN,
},
width: 4,
},
},
vertical: {
container: 'lg' as Breakpoint,
control: {
width: undefined,
},
direction: 'column',
divider: 'horizontal',
history: {
style: {
mx: STANDARD_MARGIN,
my: STANDARD_MARGIN,
},
width: 2,
},
},
} as const;
export const INITIAL_LOAD_TIMEOUT = 5_000;
export const STALE_TIME = 300_000; // 5 minutes
export const POLL_TIME = 5_000; // 5 seconds
export const SAVE_TIME = 5_000; // 5 seconds
export const IMAGE_FILTER = '.bmp, .jpg, .jpeg, .png';
export const PARAM_VERSION = '>=0.10.0';
/**
* Fixed precision for integer parameters.
*/
export const FIXED_INTEGER = 0;
/**
* Fixed precision for float parameters.
*
* The GUI limits the input steps based on the server parameters, but this does limit
* the maximum precision that can be sent back to the server, and may have to be
* increased in the future.
*/
export const FIXED_FLOAT = 2;
export const STATUS_SUCCESS = 200;

View File

@ -18,7 +18,7 @@ import { ServerParamsError } from './components/error/ServerParams.js';
import { LoadingScreen } from './components/LoadingScreen.js'; import { LoadingScreen } from './components/LoadingScreen.js';
import { OnnxError } from './components/OnnxError.js'; import { OnnxError } from './components/OnnxError.js';
import { OnnxWeb } from './components/OnnxWeb.js'; import { OnnxWeb } from './components/OnnxWeb.js';
import { Config, getApiRoot, isDebug, loadConfig, mergeConfig, PARAM_VERSION, ServerParams } from './config.js'; import { Config, getApiRoot, isDebug, loadConfig, mergeConfig, ServerParams } from './config.js';
import { import {
ClientContext, ClientContext,
ConfigContext, ConfigContext,
@ -31,8 +31,7 @@ import {
} from './state/full.js'; } from './state/full.js';
import { I18N_STRINGS } from './strings/all.js'; import { I18N_STRINGS } from './strings/all.js';
import { applyStateMigrations, UnknownState } from './state/migration/default.js'; import { applyStateMigrations, UnknownState } from './state/migration/default.js';
import { INITIAL_LOAD_TIMEOUT, PARAM_VERSION } from './constants.js';
export const INITIAL_LOAD_TIMEOUT = 5_000;
export async function renderApp(config: Config, params: ServerParams, logger: Logger, client: ApiClient) { export async function renderApp(config: Config, params: ServerParams, logger: Logger, client: ApiClient) {
const completeConfig = mergeConfig(config, params); const completeConfig = mergeConfig(config, params);

View File

@ -1,15 +1,9 @@
import { Maybe } from '@apextoaster/js-utils'; import { Maybe } from '@apextoaster/js-utils';
import { ImageResponse, ReadyResponse, RetryParams } from '../types/api.js'; import { RetryParams } from '../types/api.js';
import { Slice } from './types.js'; import { Slice } from './types.js';
import { DEFAULT_HISTORY } from '../constants.js'; import { DEFAULT_HISTORY } from '../constants.js';
import { JobResponse } from '../types/api-v2.js'; import { JobResponse } from '../types/api-v2.js';
export interface HistoryItem {
image: ImageResponse;
ready: Maybe<ReadyResponse>;
retry: Maybe<RetryParams>;
}
export interface HistoryItemV2 { export interface HistoryItemV2 {
image: JobResponse; image: JobResponse;
retry: Maybe<RetryParams>; retry: Maybe<RetryParams>;

View File

@ -34,6 +34,13 @@ export interface ImageMetadata<TParams extends BaseImgParams, TType extends JobT
type: TType; type: TType;
} }
export type AnyImageMetadata
= ImageMetadata<Txt2ImgParams, JobType.TXT2IMG>
| ImageMetadata<Img2ImgParams, JobType.IMG2IMG>
| ImageMetadata<InpaintParams, JobType.INPAINT>
| ImageMetadata<BaseImgParams, JobType.UPSCALE>
| ImageMetadata<BaseImgParams, JobType.BLEND>;
export enum JobStatus { export enum JobStatus {
PENDING = 'pending', PENDING = 'pending',
RUNNING = 'running', RUNNING = 'running',
@ -143,13 +150,7 @@ export interface SuccessBlendJobResponse extends BaseJobResponse {
export interface SuccessChainJobResponse extends BaseJobResponse { export interface SuccessChainJobResponse extends BaseJobResponse {
status: JobStatus.SUCCESS; status: JobStatus.SUCCESS;
outputs: Array<string>; outputs: Array<string>;
metadata: Array< metadata: Array<AnyImageMetadata>;
ImageMetadata<Txt2ImgParams, JobType.TXT2IMG>
| ImageMetadata<Img2ImgParams, JobType.IMG2IMG>
| ImageMetadata<InpaintParams, JobType.INPAINT>
| ImageMetadata<BaseImgParams, JobType.UPSCALE>
| ImageMetadata<BaseImgParams, JobType.BLEND>
>;
} }
/** /**

View File

@ -1,38 +1,15 @@
import { import {
BaseImgParams, BlendParams,
ModelParams,
Txt2ImgParams,
UpscaleParams,
HighresParams, HighresParams,
Img2ImgParams, Img2ImgParams,
InpaintParams, InpaintParams,
ModelParams,
OutpaintParams, OutpaintParams,
Txt2ImgParams,
UpscaleParams,
UpscaleReqParams, UpscaleReqParams,
BlendParams,
ImageSize,
} from './params.js'; } from './params.js';
/**
* Output image data within the response.
*
* @deprecated
*/
export interface ImageOutput {
key: string;
url: string;
}
/**
* General response for most image requests.
*
* @deprecated
*/
export interface ImageResponse {
outputs: Array<ImageOutput>;
params: Required<BaseImgParams> & Required<ModelParams>;
size: ImageSize;
}
/** /**
* Status response from the ready endpoint. * Status response from the ready endpoint.
*/ */
@ -122,26 +99,3 @@ export type RetryParams = {
params: BlendParams; params: BlendParams;
upscale?: UpscaleParams; upscale?: UpscaleParams;
}; };
/**
* Status response from the image endpoint, with parameters to retry the job if it fails.
*
* @deprecated
*/
export interface ImageResponseWithRetry {
image: ImageResponse;
retry: RetryParams;
}
/**
* @deprecated
*/
export interface ImageMetadata {
highres: HighresParams;
outputs: string | Array<string>;
params: Txt2ImgParams | Img2ImgParams | InpaintParams;
upscale: UpscaleParams;
input_size: ImageSize;
size: ImageSize;
}