diff --git a/examples/server/public/index.html.gz b/examples/server/public/index.html.gz
index 646988ad8..84bde767f 100644
Binary files a/examples/server/public/index.html.gz and b/examples/server/public/index.html.gz differ
diff --git a/examples/server/webui/src/components/MarkdownDisplay.tsx b/examples/server/webui/src/components/MarkdownDisplay.tsx
index 8ab8de655..814920a74 100644
--- a/examples/server/webui/src/components/MarkdownDisplay.tsx
+++ b/examples/server/webui/src/components/MarkdownDisplay.tsx
@@ -23,6 +23,7 @@ export default function MarkdownDisplay({ content }: { content: string }) {
button: (props) => (
),
+ // note: do not use "pre", "p" or other basic html elements here, it will cause the node to re-render when the message is being generated (this should be a bug with react-markdown, not sure how to fix it)
}}
>
{preprocessedContent}
diff --git a/examples/server/webui/src/components/SettingDialog.tsx b/examples/server/webui/src/components/SettingDialog.tsx
index ae8117fd2..5565ab7bb 100644
--- a/examples/server/webui/src/components/SettingDialog.tsx
+++ b/examples/server/webui/src/components/SettingDialog.tsx
@@ -3,6 +3,7 @@ import { useAppContext } from '../utils/app.context';
import { CONFIG_DEFAULT, CONFIG_INFO } from '../Config';
import { isDev } from '../Config';
import StorageUtils from '../utils/storage';
+import { isBoolean, isNumeric, isString } from '../utils/misc';
type SettKey = keyof typeof CONFIG_DEFAULT;
@@ -52,7 +53,42 @@ export default function SettingDialog({
};
const handleSave = () => {
- saveConfig(localConfig);
+ // copy the local config to prevent direct mutation
+ const newConfig: typeof CONFIG_DEFAULT = JSON.parse(
+ JSON.stringify(localConfig)
+ );
+ // validate the config
+ for (const key in newConfig) {
+ const value = newConfig[key as SettKey];
+ const mustBeBoolean = isBoolean(CONFIG_DEFAULT[key as SettKey]);
+ const mustBeString = isString(CONFIG_DEFAULT[key as SettKey]);
+ const mustBeNumeric = isNumeric(CONFIG_DEFAULT[key as SettKey]);
+ if (mustBeString) {
+ if (!isString(value)) {
+ alert(`Value for ${key} must be string`);
+ return;
+ }
+ } else if (mustBeNumeric) {
+ const trimedValue = value.toString().trim();
+ const numVal = Number(trimedValue);
+ if (isNaN(numVal) || !isNumeric(numVal) || trimedValue.length === 0) {
+ alert(`Value for ${key} must be numeric`);
+ return;
+ }
+ // force conversion to number
+ // @ts-expect-error this is safe
+ newConfig[key] = numVal;
+ } else if (mustBeBoolean) {
+ if (!isBoolean(value)) {
+ alert(`Value for ${key} must be boolean`);
+ return;
+ }
+ } else {
+ console.error(`Unknown default type for key ${key}`);
+ }
+ }
+ if (isDev) console.log('Saving config', newConfig);
+ saveConfig(newConfig);
onClose();
};
@@ -66,6 +102,11 @@ export default function SettingDialog({
onClose();
};
+ const onChange = (key: SettKey) => (value: string | boolean) => {
+ // note: we do not perform validation here, because we may get incomplete value as user is still typing it
+ setLocalConfig({ ...localConfig, [key]: value });
+ };
+
return (