diff --git a/api/params.json b/api/params.json index 9bca6789..b35d21bf 100644 --- a/api/params.json +++ b/api/params.json @@ -1,4 +1,5 @@ { + "version": "0.5.0", "cfg": { "default": 6, "min": 1, diff --git a/gui/package.json b/gui/package.json index 9e0f7c26..1aae2d11 100644 --- a/gui/package.json +++ b/gui/package.json @@ -20,6 +20,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.2", + "semver": "^7.3.8", "tslib": "^2.4.1", "zustand": "^4.3.1" }, diff --git a/gui/src/components/OnnxError.tsx b/gui/src/components/OnnxError.tsx index 2a199cbf..afacd90a 100644 --- a/gui/src/components/OnnxError.tsx +++ b/gui/src/components/OnnxError.tsx @@ -1,7 +1,9 @@ -import { Alert, AlertTitle, Box, Container, Link, Stack, Typography } from '@mui/material'; +import { Box, Container, Link, Stack, Typography } from '@mui/material'; import * as React from 'react'; +import { ReactNode } from 'react'; export interface OnnxErrorProps { + children?: ReactNode; root: string; } @@ -17,12 +19,8 @@ export function OnnxError(props: OnnxErrorProps) { - - - Server Error - - Could not fetch parameters from the ONNX web API server at {props.root}. - + {props.children} + This is a web UI for running ONNX models with GPU acceleration or in software, running locally or on a remote machine. @@ -50,7 +48,7 @@ export function OnnxError(props: OnnxErrorProps) { If your server is running and available at {props.root}, make sure you are on - the `main` branch and try updating to the latest version: + the main branch and try updating to the latest version:
               > git branch{'\n'}
               * main{'\n'}
diff --git a/gui/src/components/error/ParamsVersion.tsx b/gui/src/components/error/ParamsVersion.tsx
new file mode 100644
index 00000000..31beb3d5
--- /dev/null
+++ b/gui/src/components/error/ParamsVersion.tsx
@@ -0,0 +1,24 @@
+import { Alert, AlertTitle, Typography } from '@mui/material';
+import * as React from 'react';
+import { PARAM_VERSION } from '../../config';
+
+export interface ParamsVersionErrorProps {
+  root: string;
+  version: string;
+}
+
+export function ParamsVersionError(props: ParamsVersionErrorProps) {
+  return 
+    
+      Parameter Version Error
+    
+    
+      The server returned parameters that are too old for the client to load.
+    
+    
+      The server parameters are version {props.version}, but this client's required version
+      is {PARAM_VERSION}. Please update your server or use a matching version of the client.
+    
+  ;
+}
+
diff --git a/gui/src/components/error/ServerParams.tsx b/gui/src/components/error/ServerParams.tsx
new file mode 100644
index 00000000..480b59b8
--- /dev/null
+++ b/gui/src/components/error/ServerParams.tsx
@@ -0,0 +1,31 @@
+import { Alert, AlertTitle, Typography } from '@mui/material';
+import * as React from 'react';
+
+export interface ServerParamsErrorProps {
+  error: unknown;
+  root: string;
+}
+
+function getErrorMessage(error: unknown): string {
+  if (error instanceof Error) {
+    return error.message;
+  } else if (typeof error === 'string') {
+    return error;
+  } else {
+    return 'unknown error';
+  }
+}
+
+export function ServerParamsError(props: ServerParamsErrorProps) {
+  return 
+    
+      Server Error
+    
+    
+      Could not fetch parameters from the ONNX web API server at {props.root}.
+    
+    
+      {getErrorMessage(props.error)}
+    
+  ;
+}
diff --git a/gui/src/config.ts b/gui/src/config.ts
index 2c6e7563..04d40034 100644
--- a/gui/src/config.ts
+++ b/gui/src/config.ts
@@ -38,7 +38,9 @@ export type ConfigParams = ConfigRanges>;
+>> & {
+  version: string;
+};
 /* eslint-enable */
 
 export interface Config {
@@ -57,7 +59,10 @@ export const DEFAULT_BRUSH = {
   color: 255,
   size: 8,
 };
+
 export const IMAGE_FILTER = '.bmp, .jpg, .jpeg, .png';
+export const PARAM_VERSION = '>=0.4.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
diff --git a/gui/src/main.tsx b/gui/src/main.tsx
index 8cf6b2b5..308f4340 100644
--- a/gui/src/main.tsx
+++ b/gui/src/main.tsx
@@ -1,16 +1,19 @@
 /* eslint-disable no-console */
-import { doesExist, mustExist } from '@apextoaster/js-utils';
+import { doesExist, mustDefault, mustExist } from '@apextoaster/js-utils';
 import { merge } from 'lodash';
 import * as React from 'react';
 import ReactDOM from 'react-dom/client';
 import { QueryClient, QueryClientProvider } from 'react-query';
+import { satisfies } from 'semver';
 import { createStore } from 'zustand';
 import { createJSONStorage, persist } from 'zustand/middleware';
 
 import { makeClient } from './client.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 { Config, loadConfig } from './config.js';
+import { Config, loadConfig, PARAM_VERSION } from './config.js';
 import { ClientContext, ConfigContext, createStateSlices, OnnxState, StateContext } from './state.js';
 
 export function getApiRoot(config: Config): string {
@@ -39,69 +42,79 @@ export async function main() {
   try {
     // load full params from the API server and merge with the initial client config
     const params = await client.params();
-    merge(params, config.params);
+    const version = mustDefault(params.version, '0.0.0');
+    if (satisfies(version, PARAM_VERSION)) {
+      // check version here
+      merge(params, config.params);
 
-    // prep zustand with a slice for each tab, using local storage
-    const {
-      createBrushSlice,
-      createDefaultSlice,
-      createHistorySlice,
-      createImg2ImgSlice,
-      createInpaintSlice,
-      createModelSlice,
-      createOutpaintSlice,
-      createTxt2ImgSlice,
-      createUpscaleSlice,
-    } = 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),
-    }), {
-      name: 'onnx-web',
-      partialize(s) {
-        return {
-          ...s,
-          img2img: {
-            ...s.img2img,
-            source: undefined,
-          },
-          inpaint: {
-            ...s.inpaint,
-            mask: undefined,
-            source: undefined,
-          },
-          upscaleTab: {
-            ...s.upscaleTab,
-            source: undefined,
-          },
-        };
-      },
-      storage: createJSONStorage(() => localStorage),
-      version: 3,
-    }));
+      // prep zustand with a slice for each tab, using local storage
+      const {
+        createBrushSlice,
+        createDefaultSlice,
+        createHistorySlice,
+        createImg2ImgSlice,
+        createInpaintSlice,
+        createModelSlice,
+        createOutpaintSlice,
+        createTxt2ImgSlice,
+        createUpscaleSlice,
+      } = 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),
+      }), {
+        name: 'onnx-web',
+        partialize(s) {
+          return {
+            ...s,
+            img2img: {
+              ...s.img2img,
+              source: undefined,
+            },
+            inpaint: {
+              ...s.inpaint,
+              mask: undefined,
+              source: undefined,
+            },
+            upscaleTab: {
+              ...s.upscaleTab,
+              source: undefined,
+            },
+          };
+        },
+        storage: createJSONStorage(() => localStorage),
+        version: 3,
+      }));
 
-    // prep react-query client
-    const query = new QueryClient();
+      // prep react-query client
+      const query = new QueryClient();
 
-    // go
-    app.render(
-      
-        
-          
-            
-          
-        
-      
-    );
+      // go
+      app.render(
+        
+          
+            
+              
+            
+          
+        
+      );
+    } else {
+      app.render(
+        
+      );
+    }
   } catch (err) {
-    app.render();
+    app.render(
+      
+    );
   }
 }
 
diff --git a/gui/yarn.lock b/gui/yarn.lock
index 7798537c..ca0f7ee2 100644
--- a/gui/yarn.lock
+++ b/gui/yarn.lock
@@ -2540,7 +2540,7 @@ semver@^6.0.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
-semver@^7.3.7:
+semver@^7.3.7, semver@^7.3.8:
   version "7.3.8"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
   integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==