From d5a3b0fed85e02639a4e079eeb11feee0a0ed3ca Mon Sep 17 00:00:00 2001 From: Sean Sube Date: Sun, 5 Mar 2023 16:23:26 -0600 Subject: [PATCH 1/4] feat(gui): add local params and API stub so client can load without a server (#181) --- api/params.json | 2 +- gui/esbuild.js | 3 +- gui/examples/config.json | 160 ++++++++++++- gui/src/{client.ts => client/api.ts} | 4 +- gui/src/client/local.ts | 59 +++++ gui/src/components/ImageCard.tsx | 2 +- gui/src/components/LoadingCard.tsx | 2 +- gui/src/components/control/ImageControl.tsx | 2 +- gui/src/config.ts | 15 +- gui/src/main.tsx | 237 +++++++++++--------- gui/src/state.ts | 4 +- 11 files changed, 365 insertions(+), 125 deletions(-) rename gui/src/{client.ts => client/api.ts} (99%) create mode 100644 gui/src/client/local.ts diff --git a/api/params.json b/api/params.json index 8303b47d3..14c3f7b47 100644 --- a/api/params.json +++ b/api/params.json @@ -1,5 +1,5 @@ { - "version": "0.7.1", + "version": "0.8.0", "batch": { "default": 1, "min": 1, diff --git a/gui/esbuild.js b/gui/esbuild.js index c2a0aa9a9..af312ccda 100644 --- a/gui/esbuild.js +++ b/gui/esbuild.js @@ -5,8 +5,7 @@ import { copy } from 'esbuild-plugin-copy'; function envTrue(key) { const val = (process.env[key] || '').toLowerCase(); - return val == '1' || val == 't' || val == 'true' || val == 'y' || val == 'yes'; - + return val === '1' || val === 't' || val === 'true' || val === 'y' || val === 'yes'; } const debug = envTrue('DEBUG'); diff --git a/gui/examples/config.json b/gui/examples/config.json index f40a1bf21..aa3e83ee0 100644 --- a/gui/examples/config.json +++ b/gui/examples/config.json @@ -3,17 +3,167 @@ "root": "http://127.0.0.1:5000" }, "params": { + "version": "0.8.0", + "batch": { + "default": 1, + "min": 1, + "max": 5, + "step": 1 + }, + "bottom": { + "default": 0, + "min": 0, + "max": 512, + "step": 8 + }, + "cfg": { + "default": 6, + "min": 1, + "max": 30, + "step": 0.1 + }, + "correction": { + "default": "", + "keys": [] + }, + "denoise": { + "default": 0.5, + "min": 0, + "max": 1, + "step": 0.1 + }, + "eta": { + "default": 0.0, + "min": 0, + "max": 1, + "step": 0.01 + }, + "faceOutscale": { + "default": 1, + "min": 1, + "max": 4, + "step": 1 + }, + "faceStrength": { + "default": 0.5, + "min": 0, + "max": 1, + "step": 0.1 + }, + "fillColor": { + "default": "#000000", + "keys": [] + }, + "filter": { + "default": "none", + "keys": [] + }, + "height": { + "default": 512, + "min": 256, + "max": 1024, + "step": 8 + }, + "inversion": { + "default": "", + "keys": [] + }, + "left": { + "default": 0, + "min": 0, + "max": 512, + "step": 8 + }, "model": { - "default": "stable-diffusion-onnx-v1-5" + "default": "stable-diffusion-onnx-v1-5", + "keys": [] + }, + "negativePrompt": { + "default": "", + "keys": [] + }, + "noise": { + "default": "histogram", + "keys": [] + }, + "outscale": { + "default": 1, + "min": 1, + "max": 4, + "step": 1 }, "platform": { - "default": "amd" + "default": "amd", + "keys": [] + }, + "prompt": { + "default": "an astronaut eating a hamburger", + "keys": [] + }, + "right": { + "default": 0, + "min": 0, + "max": 512, + "step": 8 + }, + "scale": { + "default": 1, + "min": 1, + "max": 4, + "step": 1 }, "scheduler": { - "default": "euler-a" + "default": "euler-a", + "keys": [] }, - "prompt": { - "default": "an astronaut eating a hamburger" + "seed": { + "default": -1, + "min": -1, + "max": 4294967295, + "step": 1 + }, + "steps": { + "default": 25, + "min": 1, + "max": 200, + "step": 1 + }, + "strength": { + "default": 0.5, + "min": 0, + "max": 1, + "step": 0.01 + }, + "tileOrder": { + "default": "spiral", + "keys": [ + "grid", + "spiral" + ] + }, + "top": { + "default": 0, + "min": 0, + "max": 512, + "step": 8 + }, + "upscaleOrder": { + "default": "correction-first", + "keys": [ + "correction-both", + "correction-first", + "correction-last" + ] + }, + "upscaling": { + "default": "", + "keys": [] + }, + "width": { + "default": 512, + "min": 256, + "max": 1024, + "step": 8 } } } \ No newline at end of file diff --git a/gui/src/client.ts b/gui/src/client/api.ts similarity index 99% rename from gui/src/client.ts rename to gui/src/client/api.ts index eb32d61d0..606966a12 100644 --- a/gui/src/client.ts +++ b/gui/src/client/api.ts @@ -1,8 +1,8 @@ /* eslint-disable max-lines */ import { doesExist } from '@apextoaster/js-utils'; -import { ServerParams } from './config.js'; -import { range } from './utils.js'; +import { ServerParams } from '../config.js'; +import { range } from '../utils.js'; /** * Shared parameters for anything using models, which is pretty much everything. diff --git a/gui/src/client/local.ts b/gui/src/client/local.ts new file mode 100644 index 000000000..bd5607131 --- /dev/null +++ b/gui/src/client/local.ts @@ -0,0 +1,59 @@ +import { BaseError } from 'noicejs'; +import { ApiClient } from './api.js'; + +export class NoServerError extends BaseError { + constructor() { + super('cannot connect to server'); + } +} + +/** + * @TODO client-side inference with https://www.npmjs.com/package/onnxruntime-web + */ +export const LOCAL_CLIENT = { + async masks() { + throw new NoServerError(); + }, + async blend(model, params, upscale) { + throw new NoServerError(); + }, + async img2img(model, params, upscale) { + throw new NoServerError(); + }, + async txt2img(model, params, upscale) { + throw new NoServerError(); + }, + async inpaint(model, params, upscale) { + throw new NoServerError(); + }, + async upscale(model, params, upscale) { + throw new NoServerError(); + }, + async outpaint(model, params, upscale) { + throw new NoServerError(); + }, + async noises() { + throw new NoServerError(); + }, + async params() { + throw new NoServerError(); + }, + async ready(key) { + throw new NoServerError(); + }, + async cancel(key) { + throw new NoServerError(); + }, + async models() { + throw new NoServerError(); + }, + async platforms() { + throw new NoServerError(); + }, + async schedulers() { + throw new NoServerError(); + }, + async strings() { + return {}; + }, +} as ApiClient; diff --git a/gui/src/components/ImageCard.tsx b/gui/src/components/ImageCard.tsx index e4502ebdd..a2c033260 100644 --- a/gui/src/components/ImageCard.tsx +++ b/gui/src/components/ImageCard.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useHash } from 'react-use/lib/useHash'; import { useStore } from 'zustand'; -import { ImageResponse } from '../client.js'; +import { ImageResponse } from '../client/api.js'; import { BLEND_SOURCES, ConfigContext, StateContext } from '../state.js'; import { range, visibleIndex } from '../utils.js'; diff --git a/gui/src/components/LoadingCard.tsx b/gui/src/components/LoadingCard.tsx index 727fb0f94..311088f8b 100644 --- a/gui/src/components/LoadingCard.tsx +++ b/gui/src/components/LoadingCard.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from 'react-query'; import { useStore } from 'zustand'; -import { ImageResponse } from '../client.js'; +import { ImageResponse } from '../client/api.js'; import { POLL_TIME } from '../config.js'; import { ClientContext, ConfigContext, StateContext } from '../state.js'; diff --git a/gui/src/components/control/ImageControl.tsx b/gui/src/components/control/ImageControl.tsx index 0925533fa..aca327071 100644 --- a/gui/src/components/control/ImageControl.tsx +++ b/gui/src/components/control/ImageControl.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useStore } from 'zustand'; -import { BaseImgParams } from '../../client.js'; +import { BaseImgParams } from '../../client/api.js'; import { STALE_TIME } from '../../config.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state.js'; import { NumericField } from '../input/NumericField.js'; diff --git a/gui/src/config.ts b/gui/src/config.ts index ea4fe56c6..cadd6910d 100644 --- a/gui/src/config.ts +++ b/gui/src/config.ts @@ -1,6 +1,6 @@ import { doesExist, Maybe } from '@apextoaster/js-utils'; import { merge } from 'lodash'; -import { Img2ImgParams, InpaintParams, ModelParams, OutpaintParams, STATUS_SUCCESS, Txt2ImgParams, UpscaleParams } from './client.js'; +import { Img2ImgParams, InpaintParams, ModelParams, OutpaintParams, STATUS_SUCCESS, Txt2ImgParams, UpscaleParams } from './client/api.js'; export interface ConfigNumber { default: number; @@ -113,3 +113,16 @@ export function getApiRoot(config: Config): string { return config.api.root; } } + +export function isDebug(): boolean { + const query = new URLSearchParams(window.location.search); + const debug = query.get('debug'); + + if (doesExist(debug)) { + const val = debug.toLowerCase(); + // eslint-disable-next-line no-restricted-syntax + return val === '1' || val === 't' || val === 'true' || val === 'y' || val === 'yes'; + } else { + return false; + } +} diff --git a/gui/src/main.tsx b/gui/src/main.tsx index 7f0520cb9..e44cfffac 100644 --- a/gui/src/main.tsx +++ b/gui/src/main.tsx @@ -1,5 +1,5 @@ -import { mustDefault, mustExist, timeout } from '@apextoaster/js-utils'; -import { createLogger } from 'browser-bunyan'; +import { mustDefault, mustExist, timeout, TimeoutError } from '@apextoaster/js-utils'; +import { createLogger, Logger } from 'browser-bunyan'; import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import * as React from 'react'; @@ -10,12 +10,21 @@ import { satisfies } from 'semver'; import { createStore } from 'zustand'; import { createJSONStorage, persist } from 'zustand/middleware'; -import { makeClient } from './client.js'; +import { ApiClient, makeClient } from './client/api.js'; +import { LOCAL_CLIENT } from './client/local.js'; import { ParamsVersionError } from './components/error/ParamsVersion.js'; import { ServerParamsError } from './components/error/ServerParams.js'; import { OnnxError } from './components/OnnxError.js'; import { OnnxWeb } from './components/OnnxWeb.js'; -import { getApiRoot, loadConfig, mergeConfig, PARAM_VERSION } from './config.js'; +import { + Config, + getApiRoot, + isDebug, + loadConfig, + mergeConfig, + PARAM_VERSION, + ServerParams, +} from './config.js'; import { ClientContext, ConfigContext, @@ -30,10 +39,115 @@ import { I18N_STRINGS } from './strings/all.js'; export const INITIAL_LOAD_TIMEOUT = 5_000; +export async function renderApp(config: Config, params: ServerParams, logger: Logger, client: ApiClient) { + const completeConfig = mergeConfig(config, params); + + // prep i18next + await i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + resources: I18N_STRINGS, + returnEmptyString: false, + }); + + logger.info('getting strings from server'); + const strings = await client.strings(); + for (const [lang, translation] of Object.entries(strings)) { + logger.debug({ lang, translation }, 'adding server strings'); + for (const [namespace, data] of Object.entries(translation)) { + i18n.addResourceBundle(lang, namespace, data, true); + } + } + + // prep zustand with a slice for each tab, using local storage + const { + createBrushSlice, + createDefaultSlice, + createHistorySlice, + createImg2ImgSlice, + createInpaintSlice, + createModelSlice, + createOutpaintSlice, + createTxt2ImgSlice, + createUpscaleSlice, + createBlendSlice, + createResetSlice, + } = createStateSlices(params); + const state = createStore(persist((...slice) => ({ + ...createBrushSlice(...slice), + ...createDefaultSlice(...slice), + ...createHistorySlice(...slice), + ...createImg2ImgSlice(...slice), + ...createInpaintSlice(...slice), + ...createModelSlice(...slice), + ...createTxt2ImgSlice(...slice), + ...createOutpaintSlice(...slice), + ...createUpscaleSlice(...slice), + ...createBlendSlice(...slice), + ...createResetSlice(...slice), + }), { + name: STATE_KEY, + partialize(s) { + return { + ...s, + img2img: { + ...s.img2img, + source: undefined, + }, + inpaint: { + ...s.inpaint, + mask: undefined, + source: undefined, + }, + upscaleTab: { + ...s.upscaleTab, + source: undefined, + }, + blend: { + ...s.blend, + mask: undefined, + sources: [], + } + }; + }, + storage: createJSONStorage(() => localStorage), + version: STATE_VERSION, + })); + + // prep react-query client + const query = new QueryClient(); + + const reactLogger = logger.child({ + system: 'react', + }); + + // go + return + + + + + + + + + + + + ; +} + export async function main() { + const debug = isDebug(); const logger = createLogger({ name: 'onnx-web', - level: 'debug', + level: debug ? 'debug' : 'info', }); // load config from GUI server @@ -53,116 +167,21 @@ export async function main() { const params = await timeout(INITIAL_LOAD_TIMEOUT, client.params()); const version = mustDefault(params.version, '0.0.0'); if (satisfies(version, PARAM_VERSION)) { - const completeConfig = mergeConfig(config, params); - - // prep i18next - await i18n - .use(LanguageDetector) - .use(initReactI18next) - .init({ - debug: true, - fallbackLng: 'en', - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, - resources: I18N_STRINGS, - returnEmptyString: false, - }); - - logger.info('getting strings from server'); - const strings = await client.strings(); - for (const [lang, translation] of Object.entries(strings)) { - logger.debug({ lang, translation }, 'adding server strings'); - for (const [namespace, data] of Object.entries(translation)) { - i18n.addResourceBundle(lang, namespace, data, true); - } - } - - // prep zustand with a slice for each tab, using local storage - const { - createBrushSlice, - createDefaultSlice, - createHistorySlice, - createImg2ImgSlice, - createInpaintSlice, - createModelSlice, - createOutpaintSlice, - createTxt2ImgSlice, - createUpscaleSlice, - createBlendSlice, - createResetSlice, - } = createStateSlices(params); - const state = createStore(persist((...slice) => ({ - ...createBrushSlice(...slice), - ...createDefaultSlice(...slice), - ...createHistorySlice(...slice), - ...createImg2ImgSlice(...slice), - ...createInpaintSlice(...slice), - ...createModelSlice(...slice), - ...createTxt2ImgSlice(...slice), - ...createOutpaintSlice(...slice), - ...createUpscaleSlice(...slice), - ...createBlendSlice(...slice), - ...createResetSlice(...slice), - }), { - name: STATE_KEY, - partialize(s) { - return { - ...s, - img2img: { - ...s.img2img, - source: undefined, - }, - inpaint: { - ...s.inpaint, - mask: undefined, - source: undefined, - }, - upscaleTab: { - ...s.upscaleTab, - source: undefined, - }, - blend: { - ...s.blend, - mask: undefined, - sources: [], - } - }; - }, - storage: createJSONStorage(() => localStorage), - version: STATE_VERSION, - })); - - // prep react-query client - const query = new QueryClient(); - - const reactLogger = logger.child({ - system: 'react', - }); - - // go - app.render( - - - - - - - - - - - - ); + app.render(await renderApp(config, params, logger, client)); } else { app.render( ); } } catch (err) { - app.render( - - ); + if (err instanceof TimeoutError || (err instanceof Error && err.message.includes('Failed to fetch'))) { + // params timed out, attempt to render without a server + app.render(await renderApp(config, config.params as ServerParams, logger, LOCAL_CLIENT)); + } else { + app.render( + + ); + } } } diff --git a/gui/src/state.ts b/gui/src/state.ts index cd8254d45..9c93d93f7 100644 --- a/gui/src/state.ts +++ b/gui/src/state.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ /* eslint-disable no-null/no-null */ -import { doesExist, Maybe } from '@apextoaster/js-utils'; +import { Maybe } from '@apextoaster/js-utils'; import { Logger } from 'noicejs'; import { createContext } from 'react'; import { StateCreator, StoreApi } from 'zustand'; @@ -19,7 +19,7 @@ import { Txt2ImgParams, UpscaleParams, UpscaleReqParams, -} from './client.js'; +} from './client/api.js'; import { Config, ConfigFiles, ConfigState, ServerParams } from './config.js'; /** From aed5e1bf12e4b8896aa3569646a32aaafc9fcade Mon Sep 17 00:00:00 2001 From: Sean Sube Date: Sun, 5 Mar 2023 16:41:31 -0600 Subject: [PATCH 2/4] feat(gui): add loading spinner while fetching server params --- gui/src/components/LoadingScreen.tsx | 19 +++++++++++++++++++ gui/src/main.tsx | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 gui/src/components/LoadingScreen.tsx diff --git a/gui/src/components/LoadingScreen.tsx b/gui/src/components/LoadingScreen.tsx new file mode 100644 index 000000000..2162ab454 --- /dev/null +++ b/gui/src/components/LoadingScreen.tsx @@ -0,0 +1,19 @@ +import { Box, CircularProgress, Stack } from '@mui/material'; +import * as React from 'react'; + +export function LoadingScreen() { + return + + + + ; +} diff --git a/gui/src/main.tsx b/gui/src/main.tsx index e44cfffac..97c9824c8 100644 --- a/gui/src/main.tsx +++ b/gui/src/main.tsx @@ -1,4 +1,5 @@ import { mustDefault, mustExist, timeout, TimeoutError } from '@apextoaster/js-utils'; +import { Box, CircularProgress } from '@mui/material'; import { createLogger, Logger } from 'browser-bunyan'; import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; @@ -14,6 +15,7 @@ import { ApiClient, makeClient } from './client/api.js'; import { LOCAL_CLIENT } from './client/local.js'; import { ParamsVersionError } from './components/error/ParamsVersion.js'; import { ServerParamsError } from './components/error/ServerParams.js'; +import { LoadingScreen } from './components/LoadingScreen.js'; import { OnnxError } from './components/OnnxError.js'; import { OnnxWeb } from './components/OnnxWeb.js'; import { @@ -163,6 +165,8 @@ export async function main() { try { logger.info('getting image parameters from server'); + app.render(); + // load full params from the API server and merge with the initial client config const params = await timeout(INITIAL_LOAD_TIMEOUT, client.params()); const version = mustDefault(params.version, '0.0.0'); From af05e05ea6bdefbf1205448c84e8c48ec3bfe51b Mon Sep 17 00:00:00 2001 From: Sean Sube Date: Sun, 5 Mar 2023 16:51:29 -0600 Subject: [PATCH 3/4] fix(gui): translate the initial progress screen --- gui/src/components/LoadingScreen.tsx | 10 ++++++-- gui/src/main.tsx | 36 ++++++++++++++++------------ gui/src/strings/de.ts | 1 + gui/src/strings/en.ts | 1 + gui/src/strings/es.ts | 1 + gui/src/strings/fr.ts | 1 + 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/gui/src/components/LoadingScreen.tsx b/gui/src/components/LoadingScreen.tsx index 2162ab454..8f41df300 100644 --- a/gui/src/components/LoadingScreen.tsx +++ b/gui/src/components/LoadingScreen.tsx @@ -1,12 +1,15 @@ -import { Box, CircularProgress, Stack } from '@mui/material'; +import { Box, CircularProgress, Stack, Typography } from '@mui/material'; import * as React from 'react'; +import { useTranslation } from 'react-i18next'; export function LoadingScreen() { + const { t } = useTranslation(); + return + + {t('loading.server')} + ; } diff --git a/gui/src/main.tsx b/gui/src/main.tsx index 97c9824c8..c1ac01003 100644 --- a/gui/src/main.tsx +++ b/gui/src/main.tsx @@ -44,20 +44,6 @@ export const INITIAL_LOAD_TIMEOUT = 5_000; export async function renderApp(config: Config, params: ServerParams, logger: Logger, client: ApiClient) { const completeConfig = mergeConfig(config, params); - // prep i18next - await i18n - .use(LanguageDetector) - .use(initReactI18next) - .init({ - debug: true, - fallbackLng: 'en', - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, - resources: I18N_STRINGS, - returnEmptyString: false, - }); - logger.info('getting strings from server'); const strings = await client.strings(); for (const [lang, translation] of Object.entries(strings)) { @@ -145,6 +131,12 @@ export async function renderApp(config: Config, params: ServerParams, logger: Lo ; } +export async function renderProgress() { + return + + ; +} + export async function main() { const debug = isDebug(); const logger = createLogger({ @@ -163,9 +155,23 @@ export async function main() { const appElement = mustExist(document.getElementById('app')); const app = createRoot(appElement); + // prep i18next + await i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + resources: I18N_STRINGS, + returnEmptyString: false, + }); + try { logger.info('getting image parameters from server'); - app.render(); + app.render(await renderProgress()); // load full params from the API server and merge with the initial client config const params = await timeout(INITIAL_LOAD_TIMEOUT, client.params()); diff --git a/gui/src/strings/de.ts b/gui/src/strings/de.ts index 3c2a08b64..16818afbc 100644 --- a/gui/src/strings/de.ts +++ b/gui/src/strings/de.ts @@ -44,6 +44,7 @@ export const I18N_STRINGS_DE = { loading: { cancel: 'Stornieren', progress: '{{current}} von {{total}} Schritten', + server: 'Verbindung zum Server...', unknown: 'vielen', }, mask: { diff --git a/gui/src/strings/en.ts b/gui/src/strings/en.ts index 913e6c85f..07dc24ffd 100644 --- a/gui/src/strings/en.ts +++ b/gui/src/strings/en.ts @@ -39,6 +39,7 @@ export const I18N_STRINGS_EN = { loading: { cancel: 'Cancel', progress: '{{current}} of {{total}} steps', + server: 'Connecting to server...', unknown: 'many', }, mask: { diff --git a/gui/src/strings/es.ts b/gui/src/strings/es.ts index 2b353a4f7..8a18d3f08 100644 --- a/gui/src/strings/es.ts +++ b/gui/src/strings/es.ts @@ -44,6 +44,7 @@ export const I18N_STRINGS_ES = { loading: { cancel: 'Cancelar', progress: '{{current}} de {{total}} pasos', + server: 'Conectando al servidor...', unknown: 'muchos', }, mask: { diff --git a/gui/src/strings/fr.ts b/gui/src/strings/fr.ts index e43e49993..5eb4dd791 100644 --- a/gui/src/strings/fr.ts +++ b/gui/src/strings/fr.ts @@ -44,6 +44,7 @@ export const I18N_STRINGS_FR = { loading: { cancel: 'Annuler', progress: '{{current}} des {{total}} étapes', + server: 'Connexion au serveur...', unknown: 'nombreuses', }, mask: { From f575cba7d37b2fbb21c3cee8fb89e262f1fa09a1 Mon Sep 17 00:00:00 2001 From: Sean Sube Date: Sun, 5 Mar 2023 16:54:24 -0600 Subject: [PATCH 4/4] apply lint --- gui/src/main.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/gui/src/main.tsx b/gui/src/main.tsx index c1ac01003..06e1af1c8 100644 --- a/gui/src/main.tsx +++ b/gui/src/main.tsx @@ -1,5 +1,4 @@ import { mustDefault, mustExist, timeout, TimeoutError } from '@apextoaster/js-utils'; -import { Box, CircularProgress } from '@mui/material'; import { createLogger, Logger } from 'browser-bunyan'; import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; @@ -18,15 +17,7 @@ import { ServerParamsError } from './components/error/ServerParams.js'; import { LoadingScreen } from './components/LoadingScreen.js'; import { OnnxError } from './components/OnnxError.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, PARAM_VERSION, ServerParams } from './config.js'; import { ClientContext, ConfigContext,