code format
This commit is contained in:
		
							parent
							
								
									035c6ab8de
								
							
						
					
					
						commit
						bbad153ecb
					
				@ -23,18 +23,18 @@
 | 
			
		||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
// SOFTWARE.
 | 
			
		||||
 | 
			
		||||
import {FluentProvider, Tab, TabList, webDarkTheme, webLightTheme} from '@fluentui/react-components';
 | 
			
		||||
import {FC, useEffect, useState} from 'react';
 | 
			
		||||
import {Route, Routes, useLocation, useNavigate} from 'react-router';
 | 
			
		||||
import {pages} from './pages';
 | 
			
		||||
import {useMediaQuery} from 'usehooks-ts';
 | 
			
		||||
import {ToastContainer} from 'react-toastify';
 | 
			
		||||
import { FluentProvider, Tab, TabList, webDarkTheme, webLightTheme } from '@fluentui/react-components';
 | 
			
		||||
import { FC, useEffect, useState } from 'react';
 | 
			
		||||
import { Route, Routes, useLocation, useNavigate } from 'react-router';
 | 
			
		||||
import { pages } from './pages';
 | 
			
		||||
import { useMediaQuery } from 'usehooks-ts';
 | 
			
		||||
import { ToastContainer } from 'react-toastify';
 | 
			
		||||
import commonStore from './stores/commonStore';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
const App: FC = observer(() => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const location = useLocation();
 | 
			
		||||
  const mq = useMediaQuery('(min-width: 640px)');
 | 
			
		||||
@ -42,24 +42,24 @@ const App: FC = observer(() => {
 | 
			
		||||
  const [path, setPath] = useState<string>(pages[0].path);
 | 
			
		||||
 | 
			
		||||
  const selectTab = (selectedPath: unknown) =>
 | 
			
		||||
    typeof selectedPath === 'string' ? navigate({pathname: selectedPath}) : null;
 | 
			
		||||
    typeof selectedPath === 'string' ? navigate({ pathname: selectedPath }) : null;
 | 
			
		||||
 | 
			
		||||
  useEffect(() => setPath(location.pathname), [location]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <FluentProvider className="h-screen"
 | 
			
		||||
                    theme={commonStore.settings.darkMode ? webDarkTheme : webLightTheme}
 | 
			
		||||
                    data-theme={commonStore.settings.darkMode ? 'dark' : 'light'}>
 | 
			
		||||
      theme={commonStore.settings.darkMode ? webDarkTheme : webLightTheme}
 | 
			
		||||
      data-theme={commonStore.settings.darkMode ? 'dark' : 'light'}>
 | 
			
		||||
      <div className="flex h-full">
 | 
			
		||||
        <div className="flex flex-col w-16 sm:w-48 p-2 justify-between">
 | 
			
		||||
          <TabList
 | 
			
		||||
            size="large"
 | 
			
		||||
            appearance="subtle"
 | 
			
		||||
            selectedValue={path}
 | 
			
		||||
            onTabSelect={(_, {value}) => selectTab(value)}
 | 
			
		||||
            onTabSelect={(_, { value }) => selectTab(value)}
 | 
			
		||||
            vertical
 | 
			
		||||
          >
 | 
			
		||||
            {pages.filter(page => page.top).map(({label, path, icon}, index) => (
 | 
			
		||||
            {pages.filter(page => page.top).map(({ label, path, icon }, index) => (
 | 
			
		||||
              <Tab icon={icon} key={`${path}-${index}`} value={path}>
 | 
			
		||||
                {mq && t(label)}
 | 
			
		||||
              </Tab>
 | 
			
		||||
@ -69,10 +69,10 @@ const App: FC = observer(() => {
 | 
			
		||||
            size="large"
 | 
			
		||||
            appearance="subtle"
 | 
			
		||||
            selectedValue={path}
 | 
			
		||||
            onTabSelect={(_, {value}) => selectTab(value)}
 | 
			
		||||
            onTabSelect={(_, { value }) => selectTab(value)}
 | 
			
		||||
            vertical
 | 
			
		||||
          >
 | 
			
		||||
            {pages.filter(page => !page.top).map(({label, path, icon}, index) => (
 | 
			
		||||
            {pages.filter(page => !page.top).map(({ label, path, icon }, index) => (
 | 
			
		||||
              <Tab icon={icon} key={`${path}-${index}`} value={path}>
 | 
			
		||||
                {mq && t(label)}
 | 
			
		||||
              </Tab>
 | 
			
		||||
@ -81,8 +81,8 @@ const App: FC = observer(() => {
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="h-full w-full p-2 box-border overflow-y-hidden">
 | 
			
		||||
          <Routes>
 | 
			
		||||
            {pages.map(({path, element}, index) => (
 | 
			
		||||
              <Route key={`${path}-${index}`} path={path} element={element}/>
 | 
			
		||||
            {pages.map(({ path, element }, index) => (
 | 
			
		||||
              <Route key={`${path}-${index}`} path={path} element={element} />
 | 
			
		||||
            ))}
 | 
			
		||||
          </Routes>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import i18n, {changeLanguage} from 'i18next';
 | 
			
		||||
import {initReactI18next} from 'react-i18next';
 | 
			
		||||
import {resources} from './resources';
 | 
			
		||||
import {getUserLanguage} from '../utils';
 | 
			
		||||
import i18n, { changeLanguage } from 'i18next';
 | 
			
		||||
import { initReactI18next } from 'react-i18next';
 | 
			
		||||
import { resources } from './resources';
 | 
			
		||||
import { getUserLanguage } from '../utils';
 | 
			
		||||
 | 
			
		||||
i18n.use(initReactI18next).init({
 | 
			
		||||
  resources,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import i18n, {changeLanguage} from 'i18next';
 | 
			
		||||
import {resources} from './resources';
 | 
			
		||||
import {getUserLanguage} from '../utils';
 | 
			
		||||
import i18n, { changeLanguage } from 'i18next';
 | 
			
		||||
import { resources } from './resources';
 | 
			
		||||
import { getUserLanguage } from '../utils';
 | 
			
		||||
 | 
			
		||||
i18n.init({
 | 
			
		||||
  resources
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import zhHans from './zh-hans/main.json'
 | 
			
		||||
import zhHans from './zh-hans/main.json';
 | 
			
		||||
 | 
			
		||||
export const resources = {
 | 
			
		||||
  zh: {
 | 
			
		||||
@ -34,4 +34,4 @@ export const resources = {
 | 
			
		||||
  // zhHant: {
 | 
			
		||||
  //   translation: zhHant,
 | 
			
		||||
  // },
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import commonStore, {ModelStatus} from '../stores/commonStore';
 | 
			
		||||
import commonStore, { ModelStatus } from '../stores/commonStore';
 | 
			
		||||
 | 
			
		||||
export const readRoot = async () => {
 | 
			
		||||
  const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
 | 
			
		||||
@ -11,7 +11,7 @@ export const exit = async (timeout?: number) => {
 | 
			
		||||
    setTimeout(() => controller.abort(), timeout);
 | 
			
		||||
 | 
			
		||||
  const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
 | 
			
		||||
  return fetch(`http://127.0.0.1:${port}/exit`, {method: 'POST', signal: controller.signal});
 | 
			
		||||
  return fetch(`http://127.0.0.1:${port}/exit`, { method: 'POST', signal: controller.signal });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const switchModel = async (body: any) => {
 | 
			
		||||
@ -43,7 +43,7 @@ export const getStatus = async (timeout?: number): Promise<ModelStatus | undefin
 | 
			
		||||
 | 
			
		||||
  const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
 | 
			
		||||
  let ret: ModelStatus | undefined;
 | 
			
		||||
  await fetch(`http://127.0.0.1:${port}/status`, {signal: controller.signal}).then(r => r.json()).then(data => {
 | 
			
		||||
  await fetch(`http://127.0.0.1:${port}/status`, { signal: controller.signal }).then(r => r.json()).then(data => {
 | 
			
		||||
    ret = data.status;
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
import {FC} from 'react';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import {Dropdown, Option} from '@fluentui/react-components';
 | 
			
		||||
import { FC } from 'react';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import { Dropdown, Option } from '@fluentui/react-components';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
 | 
			
		||||
export const ConfigSelector: FC<{ size?: 'small' | 'medium' | 'large' }> = observer(({size}) => {
 | 
			
		||||
  return <Dropdown size={size} style={{minWidth: 0}} listbox={{style: {minWidth: 0}}}
 | 
			
		||||
                   value={commonStore.getCurrentModelConfig().name}
 | 
			
		||||
                   selectedOptions={[commonStore.currentModelConfigIndex.toString()]}
 | 
			
		||||
                   onOptionSelect={(_, data) => {
 | 
			
		||||
                     if (data.optionValue)
 | 
			
		||||
                       commonStore.setCurrentConfigIndex(Number(data.optionValue));
 | 
			
		||||
                   }}>
 | 
			
		||||
export const ConfigSelector: FC<{ size?: 'small' | 'medium' | 'large' }> = observer(({ size }) => {
 | 
			
		||||
  return <Dropdown size={size} style={{ minWidth: 0 }} listbox={{ style: { minWidth: 0 } }}
 | 
			
		||||
    value={commonStore.getCurrentModelConfig().name}
 | 
			
		||||
    selectedOptions={[commonStore.currentModelConfigIndex.toString()]}
 | 
			
		||||
    onOptionSelect={(_, data) => {
 | 
			
		||||
      if (data.optionValue)
 | 
			
		||||
        commonStore.setCurrentConfigIndex(Number(data.optionValue));
 | 
			
		||||
    }}>
 | 
			
		||||
    {commonStore.modelConfigs.map((config, index) =>
 | 
			
		||||
      <Option key={index} value={index.toString()}>{config.name}</Option>
 | 
			
		||||
    )}
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,25 @@
 | 
			
		||||
import {FC, useState} from 'react';
 | 
			
		||||
import {CheckIcon, CopyIcon} from '@primer/octicons-react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {ClipboardSetText} from '../../wailsjs/runtime';
 | 
			
		||||
import {ToolTipButton} from './ToolTipButton';
 | 
			
		||||
import { FC, useState } from 'react';
 | 
			
		||||
import { CheckIcon, CopyIcon } from '@primer/octicons-react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { ClipboardSetText } from '../../wailsjs/runtime';
 | 
			
		||||
import { ToolTipButton } from './ToolTipButton';
 | 
			
		||||
 | 
			
		||||
export const CopyButton: FC<{ content: string }> = ({content}) => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
export const CopyButton: FC<{ content: string }> = ({ content }) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const [copied, setCopied] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const onClick = () => {
 | 
			
		||||
    ClipboardSetText(content)
 | 
			
		||||
      .then(() => setCopied(true))
 | 
			
		||||
      .then(() =>
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          setCopied(false);
 | 
			
		||||
        }, 600)
 | 
			
		||||
      );
 | 
			
		||||
    .then(() => setCopied(true))
 | 
			
		||||
    .then(() =>
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        setCopied(false);
 | 
			
		||||
      }, 600)
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ToolTipButton desc={t('Copy')} size="small" appearance="subtle" icon={copied ? <CheckIcon/> : <CopyIcon/>}
 | 
			
		||||
                   onClick={onClick}/>
 | 
			
		||||
    <ToolTipButton desc={t('Copy')} size="small" appearance="subtle" icon={copied ? <CheckIcon /> : <CopyIcon />}
 | 
			
		||||
      onClick={onClick} />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
import {FC, ReactElement} from 'react';
 | 
			
		||||
import {Label, Tooltip} from '@fluentui/react-components';
 | 
			
		||||
import { FC, ReactElement } from 'react';
 | 
			
		||||
import { Label, Tooltip } from '@fluentui/react-components';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
 | 
			
		||||
export const Labeled: FC<{
 | 
			
		||||
  label: string; desc?: string | null, content: ReactElement, flex?: boolean, spaceBetween?: boolean
 | 
			
		||||
}> = ({
 | 
			
		||||
        label,
 | 
			
		||||
        desc,
 | 
			
		||||
        content,
 | 
			
		||||
        flex,
 | 
			
		||||
        spaceBetween
 | 
			
		||||
      }) => {
 | 
			
		||||
  label,
 | 
			
		||||
  desc,
 | 
			
		||||
  content,
 | 
			
		||||
  flex,
 | 
			
		||||
  spaceBetween
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={classnames(
 | 
			
		||||
      'items-center',
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,14 @@ import rehypeRaw from 'rehype-raw';
 | 
			
		||||
import rehypeHighlight from 'rehype-highlight';
 | 
			
		||||
import remarkGfm from 'remark-gfm';
 | 
			
		||||
import remarkBreaks from 'remark-breaks';
 | 
			
		||||
import {FC} from 'react';
 | 
			
		||||
import {ReactMarkdownOptions} from 'react-markdown/lib/react-markdown';
 | 
			
		||||
import {BrowserOpenURL} from '../../wailsjs/runtime';
 | 
			
		||||
import { FC } from 'react';
 | 
			
		||||
import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
 | 
			
		||||
import { BrowserOpenURL } from '../../wailsjs/runtime';
 | 
			
		||||
 | 
			
		||||
const Hyperlink: FC<any> = ({href, children}) => {
 | 
			
		||||
const Hyperlink: FC<any> = ({ href, children }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <span
 | 
			
		||||
      style={{color: '#8ab4f8', cursor: 'pointer'}}
 | 
			
		||||
      style={{ color: '#8ab4f8', cursor: 'pointer' }}
 | 
			
		||||
      onClick={() => {
 | 
			
		||||
        BrowserOpenURL(href);
 | 
			
		||||
      }}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import React, {CSSProperties, FC} from 'react';
 | 
			
		||||
import {Input} from '@fluentui/react-components';
 | 
			
		||||
import {SliderOnChangeData} from '@fluentui/react-slider';
 | 
			
		||||
import React, { CSSProperties, FC } from 'react';
 | 
			
		||||
import { Input } from '@fluentui/react-components';
 | 
			
		||||
import { SliderOnChangeData } from '@fluentui/react-slider';
 | 
			
		||||
 | 
			
		||||
export const NumberInput: FC<{
 | 
			
		||||
  value: number,
 | 
			
		||||
@ -9,23 +9,23 @@ export const NumberInput: FC<{
 | 
			
		||||
  step?: number,
 | 
			
		||||
  onChange?: (ev: React.ChangeEvent<HTMLInputElement>, data: SliderOnChangeData) => void
 | 
			
		||||
  style?: CSSProperties
 | 
			
		||||
}> = ({value, min, max, step, onChange, style}) => {
 | 
			
		||||
}> = ({ value, min, max, step, onChange, style }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Input type="number" style={style} value={value.toString()} min={min} max={max} step={step}
 | 
			
		||||
           onChange={(e, data) => {
 | 
			
		||||
             onChange?.(e, {value: Number(data.value)});
 | 
			
		||||
           }}
 | 
			
		||||
           onBlur={(e) => {
 | 
			
		||||
             if (onChange) {
 | 
			
		||||
               if (step) {
 | 
			
		||||
                 const offset = (min > 0 ? min : 0) - (max < 0 ? max : 0);
 | 
			
		||||
                 value = Number(((
 | 
			
		||||
                     Math.round((value - offset) / step) * step)
 | 
			
		||||
                   + offset)
 | 
			
		||||
                   .toFixed(2)); // avoid precision issues
 | 
			
		||||
               }
 | 
			
		||||
               onChange(e, {value: Math.max(Math.min(value, max), min)});
 | 
			
		||||
             }
 | 
			
		||||
           }}/>
 | 
			
		||||
      onChange={(e, data) => {
 | 
			
		||||
        onChange?.(e, { value: Number(data.value) });
 | 
			
		||||
      }}
 | 
			
		||||
      onBlur={(e) => {
 | 
			
		||||
        if (onChange) {
 | 
			
		||||
          if (step) {
 | 
			
		||||
            const offset = (min > 0 ? min : 0) - (max < 0 ? max : 0);
 | 
			
		||||
            value = Number(((
 | 
			
		||||
                Math.round((value - offset) / step) * step)
 | 
			
		||||
              + offset)
 | 
			
		||||
            .toFixed(2)); // avoid precision issues
 | 
			
		||||
          }
 | 
			
		||||
          onChange(e, { value: Math.max(Math.min(value, max), min) });
 | 
			
		||||
        }
 | 
			
		||||
      }} />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
import React, {FC, ReactElement} from 'react';
 | 
			
		||||
import {Divider, Text} from '@fluentui/react-components';
 | 
			
		||||
import React, { FC, ReactElement } from 'react';
 | 
			
		||||
import { Divider, Text } from '@fluentui/react-components';
 | 
			
		||||
 | 
			
		||||
export const Page: FC<{ title: string; content: ReactElement }> = ({title, content}) => {
 | 
			
		||||
export const Page: FC<{ title: string; content: ReactElement }> = ({ title, content }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col gap-2 p-2 h-full">
 | 
			
		||||
      <Text size={600}>{title}</Text>
 | 
			
		||||
      <Divider style={{flexGrow: 0}}/>
 | 
			
		||||
      <Divider style={{ flexGrow: 0 }} />
 | 
			
		||||
      {content}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
import {FC, useState} from 'react';
 | 
			
		||||
import {MuteIcon, UnmuteIcon} from '@primer/octicons-react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {ToolTipButton} from './ToolTipButton';
 | 
			
		||||
import { FC, useState } from 'react';
 | 
			
		||||
import { MuteIcon, UnmuteIcon } from '@primer/octicons-react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { ToolTipButton } from './ToolTipButton';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
 | 
			
		||||
const synth = window.speechSynthesis;
 | 
			
		||||
 | 
			
		||||
export const ReadButton: FC<{ content: string }> = observer(({content}) => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
export const ReadButton: FC<{ content: string }> = observer(({ content }) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const [speaking, setSpeaking] = useState(false);
 | 
			
		||||
  let lang: string = commonStore.settings.language;
 | 
			
		||||
  if (lang === 'dev')
 | 
			
		||||
@ -46,7 +46,8 @@ export const ReadButton: FC<{ content: string }> = observer(({content}) => {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle" icon={speaking ? <MuteIcon/> : <UnmuteIcon/>}
 | 
			
		||||
                   onClick={speaking ? stopSpeak : startSpeak}/>
 | 
			
		||||
    <ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle"
 | 
			
		||||
      icon={speaking ? <MuteIcon /> : <UnmuteIcon />}
 | 
			
		||||
      onClick={speaking ? stopSpeak : startSpeak} />
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
import React, { FC, MouseEventHandler, ReactElement } from 'react';
 | 
			
		||||
import commonStore, { ModelStatus } from '../stores/commonStore';
 | 
			
		||||
import { AddToDownloadList, DepCheck, FileExists, InstallPyDep, StartServer } from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
import {
 | 
			
		||||
  AddToDownloadList,
 | 
			
		||||
  DepCheck,
 | 
			
		||||
  FileExists,
 | 
			
		||||
  InstallPyDep,
 | 
			
		||||
  StartServer
 | 
			
		||||
} from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
import { Button } from '@fluentui/react-components';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import { exit, readRoot, switchModel, updateConfig } from '../apis';
 | 
			
		||||
@ -29,141 +35,141 @@ const iconModeButtonIcon: { [modelStatus: number]: ReactElement } = {
 | 
			
		||||
 | 
			
		||||
export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean }>
 | 
			
		||||
  = observer(({
 | 
			
		||||
    onClickRun,
 | 
			
		||||
    iconMode
 | 
			
		||||
  }) => {
 | 
			
		||||
    const { t } = useTranslation();
 | 
			
		||||
    const navigate = useNavigate();
 | 
			
		||||
  onClickRun,
 | 
			
		||||
  iconMode
 | 
			
		||||
}) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
    const onClickMainButton = async () => {
 | 
			
		||||
      if (commonStore.modelStatus === ModelStatus.Offline) {
 | 
			
		||||
        commonStore.setModelStatus(ModelStatus.Starting);
 | 
			
		||||
  const onClickMainButton = async () => {
 | 
			
		||||
    if (commonStore.modelStatus === ModelStatus.Offline) {
 | 
			
		||||
      commonStore.setModelStatus(ModelStatus.Starting);
 | 
			
		||||
 | 
			
		||||
        const modelConfig = commonStore.getCurrentModelConfig();
 | 
			
		||||
        let modelName = ''
 | 
			
		||||
        let modelPath = ''
 | 
			
		||||
        if (modelConfig && modelConfig.modelParameters) {
 | 
			
		||||
          modelName = modelConfig.modelParameters.modelName;
 | 
			
		||||
          modelPath = `./${manifest.localModelDir}/${modelName}`;
 | 
			
		||||
        } else {
 | 
			
		||||
          toast(t('Model Config Exception'), { type: 'error' });
 | 
			
		||||
      const modelConfig = commonStore.getCurrentModelConfig();
 | 
			
		||||
      let modelName = '';
 | 
			
		||||
      let modelPath = '';
 | 
			
		||||
      if (modelConfig && modelConfig.modelParameters) {
 | 
			
		||||
        modelName = modelConfig.modelParameters.modelName;
 | 
			
		||||
        modelPath = `./${manifest.localModelDir}/${modelName}`;
 | 
			
		||||
      } else {
 | 
			
		||||
        toast(t('Model Config Exception'), { type: 'error' });
 | 
			
		||||
        commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!commonStore.depComplete) {
 | 
			
		||||
        let depErrorMsg = '';
 | 
			
		||||
        await DepCheck().catch((e) => {
 | 
			
		||||
          depErrorMsg = e.message || e;
 | 
			
		||||
          WindowShow();
 | 
			
		||||
          if (depErrorMsg === 'python zip not found') {
 | 
			
		||||
            toastWithButton(t('Python target not found, would you like to download it?'), t('Download'), () => {
 | 
			
		||||
              toastWithButton(`${t('Downloading')} Python`, t('Check'), () => {
 | 
			
		||||
                navigate({ pathname: '/downloads' });
 | 
			
		||||
              }, { autoClose: 3000 });
 | 
			
		||||
              AddToDownloadList('python-3.10.11-embed-amd64.zip', 'https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip');
 | 
			
		||||
            });
 | 
			
		||||
          } else if (depErrorMsg.includes('DepCheck Error')) {
 | 
			
		||||
            toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
 | 
			
		||||
              InstallPyDep(commonStore.settings.cnMirror);
 | 
			
		||||
              setTimeout(WindowShow, 1000);
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            toast(depErrorMsg, { type: 'error' });
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        if (depErrorMsg) {
 | 
			
		||||
          commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        commonStore.setDepComplete(true);
 | 
			
		||||
        saveCache();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        if (!commonStore.depComplete) {
 | 
			
		||||
          let depErrorMsg = '';
 | 
			
		||||
          await DepCheck().catch((e) => {
 | 
			
		||||
            depErrorMsg = e.message || e;
 | 
			
		||||
            WindowShow();
 | 
			
		||||
            if (depErrorMsg === 'python zip not found') {
 | 
			
		||||
              toastWithButton(t('Python target not found, would you like to download it?'), t('Download'), () => {
 | 
			
		||||
                toastWithButton(`${t('Downloading')} Python`, t('Check'), () => {
 | 
			
		||||
                  navigate({ pathname: '/downloads' });
 | 
			
		||||
                }, { autoClose: 3000 });
 | 
			
		||||
                AddToDownloadList('python-3.10.11-embed-amd64.zip', 'https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip');
 | 
			
		||||
              });
 | 
			
		||||
            } else if (depErrorMsg.includes('DepCheck Error')) {
 | 
			
		||||
              toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
 | 
			
		||||
                InstallPyDep(commonStore.settings.cnMirror);
 | 
			
		||||
                setTimeout(WindowShow, 1000)
 | 
			
		||||
              });
 | 
			
		||||
            } else {
 | 
			
		||||
              toast(depErrorMsg, { type: 'error' });
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          if (depErrorMsg) {
 | 
			
		||||
            commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          commonStore.setDepComplete(true);
 | 
			
		||||
          saveCache();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!await FileExists(modelPath)) {
 | 
			
		||||
          toastWithButton(t('Model file not found'), t('Download'), () => {
 | 
			
		||||
            const downloadUrl = commonStore.modelSourceList.find(item => item.name === modelName)?.downloadUrl;
 | 
			
		||||
            if (downloadUrl) {
 | 
			
		||||
              toastWithButton(`${t('Downloading')} ${modelName}`, t('Check'), () => {
 | 
			
		||||
      if (!await FileExists(modelPath)) {
 | 
			
		||||
        toastWithButton(t('Model file not found'), t('Download'), () => {
 | 
			
		||||
          const downloadUrl = commonStore.modelSourceList.find(item => item.name === modelName)?.downloadUrl;
 | 
			
		||||
          if (downloadUrl) {
 | 
			
		||||
            toastWithButton(`${t('Downloading')} ${modelName}`, t('Check'), () => {
 | 
			
		||||
                navigate({ pathname: '/downloads' });
 | 
			
		||||
              },
 | 
			
		||||
                { autoClose: 3000 });
 | 
			
		||||
              AddToDownloadList(modelPath, downloadUrl);
 | 
			
		||||
            } else {
 | 
			
		||||
              toast(t('Can not find download url'), { type: 'error' });
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const port = modelConfig.apiParameters.apiPort;
 | 
			
		||||
 | 
			
		||||
        await exit(1000).catch(() => {
 | 
			
		||||
              { autoClose: 3000 });
 | 
			
		||||
            AddToDownloadList(modelPath, downloadUrl);
 | 
			
		||||
          } else {
 | 
			
		||||
            toast(t('Can not find download url'), { type: 'error' });
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        StartServer(port);
 | 
			
		||||
        setTimeout(WindowShow, 1000)
 | 
			
		||||
 | 
			
		||||
        let timeoutCount = 6;
 | 
			
		||||
        let loading = false;
 | 
			
		||||
        const intervalId = setInterval(() => {
 | 
			
		||||
          readRoot()
 | 
			
		||||
            .then(r => {
 | 
			
		||||
              if (r.ok && !loading) {
 | 
			
		||||
                clearInterval(intervalId);
 | 
			
		||||
                commonStore.setModelStatus(ModelStatus.Loading);
 | 
			
		||||
                loading = true;
 | 
			
		||||
        commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const port = modelConfig.apiParameters.apiPort;
 | 
			
		||||
 | 
			
		||||
      await exit(1000).catch(() => {
 | 
			
		||||
      });
 | 
			
		||||
      StartServer(port);
 | 
			
		||||
      setTimeout(WindowShow, 1000);
 | 
			
		||||
 | 
			
		||||
      let timeoutCount = 6;
 | 
			
		||||
      let loading = false;
 | 
			
		||||
      const intervalId = setInterval(() => {
 | 
			
		||||
        readRoot()
 | 
			
		||||
        .then(r => {
 | 
			
		||||
          if (r.ok && !loading) {
 | 
			
		||||
            clearInterval(intervalId);
 | 
			
		||||
            commonStore.setModelStatus(ModelStatus.Loading);
 | 
			
		||||
            loading = true;
 | 
			
		||||
            toast(t('Loading Model'), { type: 'info' });
 | 
			
		||||
            updateConfig({
 | 
			
		||||
              max_tokens: modelConfig.apiParameters.maxResponseToken,
 | 
			
		||||
              temperature: modelConfig.apiParameters.temperature,
 | 
			
		||||
              top_p: modelConfig.apiParameters.topP,
 | 
			
		||||
              presence_penalty: modelConfig.apiParameters.presencePenalty,
 | 
			
		||||
              frequency_penalty: modelConfig.apiParameters.frequencyPenalty
 | 
			
		||||
            });
 | 
			
		||||
            switchModel({
 | 
			
		||||
              model: `${manifest.localModelDir}/${modelConfig.modelParameters.modelName}`,
 | 
			
		||||
              strategy: getStrategy(modelConfig)
 | 
			
		||||
            }).then((r) => {
 | 
			
		||||
              if (r.ok) {
 | 
			
		||||
                commonStore.setModelStatus(ModelStatus.Working);
 | 
			
		||||
                toastWithButton(t('Startup Completed'), t('Chat'), () => {
 | 
			
		||||
                  navigate({ pathname: '/chat' });
 | 
			
		||||
                }, { type: 'success', autoClose: 3000 });
 | 
			
		||||
              } else if (r.status === 304) {
 | 
			
		||||
                toast(t('Loading Model'), { type: 'info' });
 | 
			
		||||
                updateConfig({
 | 
			
		||||
                  max_tokens: modelConfig.apiParameters.maxResponseToken,
 | 
			
		||||
                  temperature: modelConfig.apiParameters.temperature,
 | 
			
		||||
                  top_p: modelConfig.apiParameters.topP,
 | 
			
		||||
                  presence_penalty: modelConfig.apiParameters.presencePenalty,
 | 
			
		||||
                  frequency_penalty: modelConfig.apiParameters.frequencyPenalty
 | 
			
		||||
                });
 | 
			
		||||
                switchModel({
 | 
			
		||||
                  model: `${manifest.localModelDir}/${modelConfig.modelParameters.modelName}`,
 | 
			
		||||
                  strategy: getStrategy(modelConfig)
 | 
			
		||||
                }).then((r) => {
 | 
			
		||||
                  if (r.ok) {
 | 
			
		||||
                    commonStore.setModelStatus(ModelStatus.Working);
 | 
			
		||||
                    toastWithButton(t('Startup Completed'), t('Chat'), () => {
 | 
			
		||||
                      navigate({ pathname: '/chat' });
 | 
			
		||||
                    }, { type: 'success', autoClose: 3000 });
 | 
			
		||||
                  } else if (r.status === 304) {
 | 
			
		||||
                    toast(t('Loading Model'), { type: 'info' });
 | 
			
		||||
                  } else {
 | 
			
		||||
                    commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
                    toast(t('Failed to switch model'), { type: 'error' });
 | 
			
		||||
                  }
 | 
			
		||||
                }).catch(() => {
 | 
			
		||||
                  commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
                  toast(t('Failed to switch model'), { type: 'error' });
 | 
			
		||||
                });
 | 
			
		||||
              } else {
 | 
			
		||||
                commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
                toast(t('Failed to switch model'), { type: 'error' });
 | 
			
		||||
              }
 | 
			
		||||
            }).catch(() => {
 | 
			
		||||
              if (timeoutCount <= 0) {
 | 
			
		||||
                clearInterval(intervalId);
 | 
			
		||||
                commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
              }
 | 
			
		||||
              commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
              toast(t('Failed to switch model'), { type: 'error' });
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        }).catch(() => {
 | 
			
		||||
          if (timeoutCount <= 0) {
 | 
			
		||||
            clearInterval(intervalId);
 | 
			
		||||
            commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
          timeoutCount--;
 | 
			
		||||
        }, 1000);
 | 
			
		||||
      } else {
 | 
			
		||||
        commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
        exit();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
        timeoutCount--;
 | 
			
		||||
      }, 1000);
 | 
			
		||||
    } else {
 | 
			
		||||
      commonStore.setModelStatus(ModelStatus.Offline);
 | 
			
		||||
      exit();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
    const onClick = async (e: any) => {
 | 
			
		||||
      if (commonStore.modelStatus === ModelStatus.Offline)
 | 
			
		||||
        await onClickRun?.(e);
 | 
			
		||||
      await onClickMainButton();
 | 
			
		||||
    };
 | 
			
		||||
  const onClick = async (e: any) => {
 | 
			
		||||
    if (commonStore.modelStatus === ModelStatus.Offline)
 | 
			
		||||
      await onClickRun?.(e);
 | 
			
		||||
    await onClickMainButton();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
    return (iconMode ?
 | 
			
		||||
  return (iconMode ?
 | 
			
		||||
      <ToolTipButton disabled={commonStore.modelStatus === ModelStatus.Starting}
 | 
			
		||||
        icon={iconModeButtonIcon[commonStore.modelStatus]}
 | 
			
		||||
        desc={t(mainButtonText[commonStore.modelStatus])}
 | 
			
		||||
@ -173,5 +179,5 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
 | 
			
		||||
        onClick={onClick}>
 | 
			
		||||
        {t(mainButtonText[commonStore.modelStatus])}
 | 
			
		||||
      </Button>
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import {FC, ReactElement} from 'react';
 | 
			
		||||
import {Card, Text} from '@fluentui/react-components';
 | 
			
		||||
import { FC, ReactElement } from 'react';
 | 
			
		||||
import { Card, Text } from '@fluentui/react-components';
 | 
			
		||||
 | 
			
		||||
export const Section: FC<{
 | 
			
		||||
  title: string; desc?: string | null, content: ReactElement, outline?: boolean
 | 
			
		||||
}> =
 | 
			
		||||
  ({title, desc, content, outline = true}) => {
 | 
			
		||||
  ({ title, desc, content, outline = true }) => {
 | 
			
		||||
    return (
 | 
			
		||||
      <Card size="small" appearance={outline ? 'outline' : 'subtle'}>
 | 
			
		||||
        <div className="flex flex-col gap-5">
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import React, {FC, MouseEventHandler, ReactElement} from 'react';
 | 
			
		||||
import {Button, Tooltip} from '@fluentui/react-components';
 | 
			
		||||
import React, { FC, MouseEventHandler, ReactElement } from 'react';
 | 
			
		||||
import { Button, Tooltip } from '@fluentui/react-components';
 | 
			
		||||
 | 
			
		||||
export const ToolTipButton: FC<{
 | 
			
		||||
  text?: string | null,
 | 
			
		||||
@ -11,19 +11,19 @@ export const ToolTipButton: FC<{
 | 
			
		||||
  disabled?: boolean,
 | 
			
		||||
  onClick?: MouseEventHandler
 | 
			
		||||
}> = ({
 | 
			
		||||
        text,
 | 
			
		||||
        desc,
 | 
			
		||||
        icon,
 | 
			
		||||
        size,
 | 
			
		||||
        shape,
 | 
			
		||||
        appearance,
 | 
			
		||||
        disabled,
 | 
			
		||||
        onClick
 | 
			
		||||
      }) => {
 | 
			
		||||
  text,
 | 
			
		||||
  desc,
 | 
			
		||||
  icon,
 | 
			
		||||
  size,
 | 
			
		||||
  shape,
 | 
			
		||||
  appearance,
 | 
			
		||||
  disabled,
 | 
			
		||||
  onClick
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Tooltip content={desc} showDelay={0} hideDelay={0} relationship="label">
 | 
			
		||||
      <Button disabled={disabled} icon={icon} onClick={onClick} size={size} shape={shape}
 | 
			
		||||
              appearance={appearance}>{text}</Button>
 | 
			
		||||
        appearance={appearance}>{text}</Button>
 | 
			
		||||
    </Tooltip>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import React, {FC, useEffect, useRef} from 'react';
 | 
			
		||||
import {Slider, Text} from '@fluentui/react-components';
 | 
			
		||||
import {SliderOnChangeData} from '@fluentui/react-slider';
 | 
			
		||||
import {NumberInput} from './NumberInput';
 | 
			
		||||
import React, { FC, useEffect, useRef } from 'react';
 | 
			
		||||
import { Slider, Text } from '@fluentui/react-components';
 | 
			
		||||
import { SliderOnChangeData } from '@fluentui/react-slider';
 | 
			
		||||
import { NumberInput } from './NumberInput';
 | 
			
		||||
 | 
			
		||||
export const ValuedSlider: FC<{
 | 
			
		||||
  value: number,
 | 
			
		||||
@ -10,7 +10,7 @@ export const ValuedSlider: FC<{
 | 
			
		||||
  step?: number,
 | 
			
		||||
  input?: boolean
 | 
			
		||||
  onChange?: (ev: React.ChangeEvent<HTMLInputElement>, data: SliderOnChangeData) => void
 | 
			
		||||
}> = ({value, min, max, step, input, onChange}) => {
 | 
			
		||||
}> = ({ value, min, max, step, input, onChange }) => {
 | 
			
		||||
  const sliderRef = useRef<HTMLInputElement>(null);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (step && sliderRef.current && sliderRef.current.parentElement) {
 | 
			
		||||
@ -21,11 +21,11 @@ export const ValuedSlider: FC<{
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex items-center">
 | 
			
		||||
      <Slider ref={sliderRef} className="grow" style={{minWidth: '50%'}} value={value} min={min}
 | 
			
		||||
              max={max} step={step}
 | 
			
		||||
              onChange={onChange}/>
 | 
			
		||||
      <Slider ref={sliderRef} className="grow" style={{ minWidth: '50%' }} value={value} min={min}
 | 
			
		||||
        max={max} step={step}
 | 
			
		||||
        onChange={onChange} />
 | 
			
		||||
      {input
 | 
			
		||||
        ? <NumberInput style={{minWidth: 0}} value={value} min={min} max={max} step={step} onChange={onChange}/>
 | 
			
		||||
        ? <NumberInput style={{ minWidth: 0 }} value={value} min={min} max={max} step={step} onChange={onChange} />
 | 
			
		||||
        : <Text>{value}</Text>}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import {createRoot} from 'react-dom/client';
 | 
			
		||||
import { createRoot } from 'react-dom/client';
 | 
			
		||||
import './style.scss';
 | 
			
		||||
import 'react-toastify/dist/ReactToastify.css';
 | 
			
		||||
import App from './App';
 | 
			
		||||
import {HashRouter} from 'react-router-dom';
 | 
			
		||||
import {startup} from './startup';
 | 
			
		||||
import { HashRouter } from 'react-router-dom';
 | 
			
		||||
import { startup } from './startup';
 | 
			
		||||
import './_locales/i18n-react';
 | 
			
		||||
 | 
			
		||||
startup().then(() => {
 | 
			
		||||
@ -14,7 +14,7 @@ startup().then(() => {
 | 
			
		||||
 | 
			
		||||
  root.render(
 | 
			
		||||
    <HashRouter>
 | 
			
		||||
      <App/>
 | 
			
		||||
      <App />
 | 
			
		||||
    </HashRouter>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
import React, {FC} from 'react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {Page} from '../components/Page';
 | 
			
		||||
import React, { FC } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { Page } from '../components/Page';
 | 
			
		||||
import MarkdownRender from '../components/MarkdownRender';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
 | 
			
		||||
export type AboutContent = { [lang: string]: string }
 | 
			
		||||
 | 
			
		||||
export const About: FC = observer(() => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const lang: string = commonStore.settings.language;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -18,6 +18,6 @@ export const About: FC = observer(() => {
 | 
			
		||||
          {lang in commonStore.about ? commonStore.about[lang] : commonStore.about['en']}
 | 
			
		||||
        </MarkdownRender>
 | 
			
		||||
      </div>
 | 
			
		||||
    }/>
 | 
			
		||||
    } />
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -734,7 +734,7 @@ export const Configs: FC = observer(() => {
 | 
			
		||||
                    }).catch(e => {
 | 
			
		||||
                      toast(`${t('Convert Failed')} - ${e.message || e}`, { type: 'error' });
 | 
			
		||||
                    });
 | 
			
		||||
                    setTimeout(WindowShow, 1000)
 | 
			
		||||
                    setTimeout(WindowShow, 1000);
 | 
			
		||||
                  } else {
 | 
			
		||||
                    toast(`${t('Model Not Found')} - ${modelPath}`, { type: 'error' });
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
import React, {FC, useEffect} from 'react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {Page} from '../components/Page';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import React, { FC, useEffect } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { Page } from '../components/Page';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
import {Divider, Field, ProgressBar} from '@fluentui/react-components';
 | 
			
		||||
import {bytesToGb, bytesToKb, bytesToMb, refreshLocalModels} from '../utils';
 | 
			
		||||
import {ToolTipButton} from '../components/ToolTipButton';
 | 
			
		||||
import {Folder20Regular, Pause20Regular, Play20Regular} from '@fluentui/react-icons';
 | 
			
		||||
import {ContinueDownload, OpenFileFolder, PauseDownload} from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
import { Divider, Field, ProgressBar } from '@fluentui/react-components';
 | 
			
		||||
import { bytesToGb, bytesToKb, bytesToMb, refreshLocalModels } from '../utils';
 | 
			
		||||
import { ToolTipButton } from '../components/ToolTipButton';
 | 
			
		||||
import { Folder20Regular, Pause20Regular, Play20Regular } from '@fluentui/react-icons';
 | 
			
		||||
import { ContinueDownload, OpenFileFolder, PauseDownload } from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
 | 
			
		||||
export type DownloadStatus = {
 | 
			
		||||
  name: string;
 | 
			
		||||
@ -22,11 +22,11 @@ export type DownloadStatus = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Downloads: FC = observer(() => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const finishedModelsLen = commonStore.downloadList.filter((status) => status.done && status.name.endsWith('.pth')).length;
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (finishedModelsLen > 0)
 | 
			
		||||
      refreshLocalModels({models: commonStore.modelSourceList}, false);
 | 
			
		||||
      refreshLocalModels({ models: commonStore.modelSourceList }, false);
 | 
			
		||||
    console.log('finishedModelsLen:', finishedModelsLen);
 | 
			
		||||
  }, [finishedModelsLen]);
 | 
			
		||||
 | 
			
		||||
@ -51,26 +51,26 @@ export const Downloads: FC = observer(() => {
 | 
			
		||||
              validationState={status.done ? 'success' : 'none'}
 | 
			
		||||
            >
 | 
			
		||||
              <div className="flex items-center gap-2">
 | 
			
		||||
                <ProgressBar className="grow" value={status.progress} max={100}/>
 | 
			
		||||
                <ProgressBar className="grow" value={status.progress} max={100} />
 | 
			
		||||
                {!status.done &&
 | 
			
		||||
                  <ToolTipButton desc={status.downloading ? t('Pause') : t('Continue')}
 | 
			
		||||
                                 icon={status.downloading ? <Pause20Regular/> : <Play20Regular/>}
 | 
			
		||||
                                 onClick={() => {
 | 
			
		||||
                                   if (status.downloading)
 | 
			
		||||
                                     PauseDownload(status.url);
 | 
			
		||||
                                   else
 | 
			
		||||
                                     ContinueDownload(status.url);
 | 
			
		||||
                                 }}/>}
 | 
			
		||||
                <ToolTipButton desc={t('Open Folder')} icon={<Folder20Regular/>} onClick={() => {
 | 
			
		||||
                    icon={status.downloading ? <Pause20Regular /> : <Play20Regular />}
 | 
			
		||||
                    onClick={() => {
 | 
			
		||||
                      if (status.downloading)
 | 
			
		||||
                        PauseDownload(status.url);
 | 
			
		||||
                      else
 | 
			
		||||
                        ContinueDownload(status.url);
 | 
			
		||||
                    }} />}
 | 
			
		||||
                <ToolTipButton desc={t('Open Folder')} icon={<Folder20Regular />} onClick={() => {
 | 
			
		||||
                  OpenFileFolder(status.path);
 | 
			
		||||
                }}/>
 | 
			
		||||
                }} />
 | 
			
		||||
              </div>
 | 
			
		||||
            </Field>
 | 
			
		||||
            <Divider style={{flexGrow: 0}}/>
 | 
			
		||||
            <Divider style={{ flexGrow: 0 }} />
 | 
			
		||||
          </div>;
 | 
			
		||||
        })
 | 
			
		||||
        }
 | 
			
		||||
      </div>
 | 
			
		||||
    }/>
 | 
			
		||||
    } />
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import {CompoundButton, Link, Text} from '@fluentui/react-components';
 | 
			
		||||
import React, {FC, ReactElement} from 'react';
 | 
			
		||||
import { CompoundButton, Link, Text } from '@fluentui/react-components';
 | 
			
		||||
import React, { FC, ReactElement } from 'react';
 | 
			
		||||
import banner from '../assets/images/banner.jpg';
 | 
			
		||||
import {
 | 
			
		||||
  Chat20Regular,
 | 
			
		||||
@ -7,13 +7,13 @@ import {
 | 
			
		||||
  DocumentSettings20Regular,
 | 
			
		||||
  Storage20Regular
 | 
			
		||||
} from '@fluentui/react-icons';
 | 
			
		||||
import {useNavigate} from 'react-router';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import {RunButton} from '../components/RunButton';
 | 
			
		||||
import { useNavigate } from 'react-router';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import { RunButton } from '../components/RunButton';
 | 
			
		||||
import manifest from '../../../manifest.json';
 | 
			
		||||
import {BrowserOpenURL} from '../../wailsjs/runtime';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {ConfigSelector} from '../components/ConfigSelector';
 | 
			
		||||
import { BrowserOpenURL } from '../../wailsjs/runtime';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { ConfigSelector } from '../components/ConfigSelector';
 | 
			
		||||
import MarkdownRender from '../components/MarkdownRender';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
 | 
			
		||||
@ -31,40 +31,40 @@ const navCards: NavCard[] = [
 | 
			
		||||
    label: 'Chat',
 | 
			
		||||
    desc: 'Go to chat page',
 | 
			
		||||
    path: '/chat',
 | 
			
		||||
    icon: <Chat20Regular/>
 | 
			
		||||
    icon: <Chat20Regular />
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Configs',
 | 
			
		||||
    desc: 'Manage your configs',
 | 
			
		||||
    path: '/configs',
 | 
			
		||||
    icon: <DocumentSettings20Regular/>
 | 
			
		||||
    icon: <DocumentSettings20Regular />
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Models',
 | 
			
		||||
    desc: 'Manage models',
 | 
			
		||||
    path: '/models',
 | 
			
		||||
    icon: <DataUsageSettings20Regular/>
 | 
			
		||||
    icon: <DataUsageSettings20Regular />
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Train',
 | 
			
		||||
    desc: '',
 | 
			
		||||
    path: '/train',
 | 
			
		||||
    icon: <Storage20Regular/>
 | 
			
		||||
    icon: <Storage20Regular />
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const Home: FC = observer(() => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const lang: string = commonStore.settings.language;
 | 
			
		||||
 | 
			
		||||
  const onClickNavCard = (path: string) => {
 | 
			
		||||
    navigate({pathname: path});
 | 
			
		||||
    navigate({ pathname: path });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col justify-between h-full">
 | 
			
		||||
      <img className="rounded-xl select-none hidden sm:block" src={banner}/>
 | 
			
		||||
      <img className="rounded-xl select-none hidden sm:block" src={banner} />
 | 
			
		||||
      <div className="flex flex-col gap-2">
 | 
			
		||||
        <Text size={600} weight="medium">{t('Introduction')}</Text>
 | 
			
		||||
        <div className="h-40 overflow-y-auto overflow-x-hidden p-1">
 | 
			
		||||
@ -74,9 +74,9 @@ export const Home: FC = observer(() => {
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="grid grid-cols-2 sm:grid-cols-4 gap-5">
 | 
			
		||||
        {navCards.map(({label, path, icon, desc}, index) => (
 | 
			
		||||
        {navCards.map(({ label, path, icon, desc }, index) => (
 | 
			
		||||
          <CompoundButton icon={icon} secondaryContent={t(desc)} key={`${path}-${index}`} value={path}
 | 
			
		||||
                          size="large" onClick={() => onClickNavCard(path)}>
 | 
			
		||||
            size="large" onClick={() => onClickNavCard(path)}>
 | 
			
		||||
            {t(label)}
 | 
			
		||||
          </CompoundButton>
 | 
			
		||||
        ))}
 | 
			
		||||
@ -84,8 +84,8 @@ export const Home: FC = observer(() => {
 | 
			
		||||
      <div className="flex flex-col gap-2">
 | 
			
		||||
        <div className="flex flex-row-reverse sm:fixed bottom-2 right-2">
 | 
			
		||||
          <div className="flex gap-3">
 | 
			
		||||
            <ConfigSelector/>
 | 
			
		||||
            <RunButton/>
 | 
			
		||||
            <ConfigSelector />
 | 
			
		||||
            <RunButton />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="flex gap-4 items-end">
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import React, {FC} from 'react';
 | 
			
		||||
import React, { FC } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  createTableColumn,
 | 
			
		||||
  DataGrid,
 | 
			
		||||
@ -12,17 +12,17 @@ import {
 | 
			
		||||
  Text,
 | 
			
		||||
  Textarea
 | 
			
		||||
} from '@fluentui/react-components';
 | 
			
		||||
import {ToolTipButton} from '../components/ToolTipButton';
 | 
			
		||||
import {ArrowClockwise20Regular, ArrowDownload20Regular, Folder20Regular, Open20Regular} from '@fluentui/react-icons';
 | 
			
		||||
import {observer} from 'mobx-react-lite';
 | 
			
		||||
import { ToolTipButton } from '../components/ToolTipButton';
 | 
			
		||||
import { ArrowClockwise20Regular, ArrowDownload20Regular, Folder20Regular, Open20Regular } from '@fluentui/react-icons';
 | 
			
		||||
import { observer } from 'mobx-react-lite';
 | 
			
		||||
import commonStore from '../stores/commonStore';
 | 
			
		||||
import {BrowserOpenURL} from '../../wailsjs/runtime';
 | 
			
		||||
import {AddToDownloadList, OpenFileFolder} from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
import { BrowserOpenURL } from '../../wailsjs/runtime';
 | 
			
		||||
import { AddToDownloadList, OpenFileFolder } from '../../wailsjs/go/backend_golang/App';
 | 
			
		||||
import manifest from '../../../manifest.json';
 | 
			
		||||
import {Page} from '../components/Page';
 | 
			
		||||
import {bytesToGb, refreshModels, saveConfigs, toastWithButton} from '../utils';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {useNavigate} from 'react-router';
 | 
			
		||||
import { Page } from '../components/Page';
 | 
			
		||||
import { bytesToGb, refreshModels, saveConfigs, toastWithButton } from '../utils';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { useNavigate } from 'react-router';
 | 
			
		||||
 | 
			
		||||
export type ModelSourceItem = {
 | 
			
		||||
  name: string;
 | 
			
		||||
@ -43,7 +43,7 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
      return a.name.localeCompare(b.name);
 | 
			
		||||
    },
 | 
			
		||||
    renderHeaderCell: () => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
      return t('File');
 | 
			
		||||
    },
 | 
			
		||||
@ -69,7 +69,7 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
      return 0;
 | 
			
		||||
    },
 | 
			
		||||
    renderHeaderCell: () => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
      return t('Desc');
 | 
			
		||||
    },
 | 
			
		||||
@ -91,7 +91,7 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
      return a.size - b.size;
 | 
			
		||||
    },
 | 
			
		||||
    renderHeaderCell: () => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
      return t('Size');
 | 
			
		||||
    },
 | 
			
		||||
@ -113,7 +113,7 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
      return b.lastUpdatedMs - a.lastUpdatedMs;
 | 
			
		||||
    },
 | 
			
		||||
    renderHeaderCell: () => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
      return t('Last updated');
 | 
			
		||||
    },
 | 
			
		||||
@ -128,12 +128,12 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
      return a.isLocal ? -1 : 1;
 | 
			
		||||
    },
 | 
			
		||||
    renderHeaderCell: () => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
      return t('Actions');
 | 
			
		||||
    },
 | 
			
		||||
    renderCell: (item) => {
 | 
			
		||||
      const {t} = useTranslation();
 | 
			
		||||
      const { t } = useTranslation();
 | 
			
		||||
      const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
@ -141,21 +141,21 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
          <div className="flex gap-1">
 | 
			
		||||
            {
 | 
			
		||||
              item.isLocal &&
 | 
			
		||||
              <ToolTipButton desc={t('Open Folder')} icon={<Folder20Regular/>} onClick={() => {
 | 
			
		||||
              <ToolTipButton desc={t('Open Folder')} icon={<Folder20Regular />} onClick={() => {
 | 
			
		||||
                OpenFileFolder(`./${manifest.localModelDir}/${item.name}`);
 | 
			
		||||
              }}/>
 | 
			
		||||
              }} />
 | 
			
		||||
            }
 | 
			
		||||
            {item.downloadUrl && !item.isLocal &&
 | 
			
		||||
              <ToolTipButton desc={t('Download')} icon={<ArrowDownload20Regular/>} onClick={() => {
 | 
			
		||||
              <ToolTipButton desc={t('Download')} icon={<ArrowDownload20Regular />} onClick={() => {
 | 
			
		||||
                toastWithButton(`${t('Downloading')} ${item.name}`, t('Check'), () => {
 | 
			
		||||
                    navigate({pathname: '/downloads'});
 | 
			
		||||
                    navigate({ pathname: '/downloads' });
 | 
			
		||||
                  },
 | 
			
		||||
                  {autoClose: 3000});
 | 
			
		||||
                  { autoClose: 3000 });
 | 
			
		||||
                AddToDownloadList(`./${manifest.localModelDir}/${item.name}`, item.downloadUrl!);
 | 
			
		||||
              }}/>}
 | 
			
		||||
            {item.url && <ToolTipButton desc={t('Open Url')} icon={<Open20Regular/>} onClick={() => {
 | 
			
		||||
              }} />}
 | 
			
		||||
            {item.url && <ToolTipButton desc={t('Open Url')} icon={<Open20Regular />} onClick={() => {
 | 
			
		||||
              BrowserOpenURL(item.url!);
 | 
			
		||||
            }}/>}
 | 
			
		||||
            }} />}
 | 
			
		||||
          </div>
 | 
			
		||||
        </TableCellLayout>
 | 
			
		||||
      );
 | 
			
		||||
@ -164,7 +164,7 @@ const columns: TableColumnDefinition<ModelSourceItem>[] = [
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const Models: FC = observer(() => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Page title={t('Models')} content={
 | 
			
		||||
@ -172,39 +172,39 @@ export const Models: FC = observer(() => {
 | 
			
		||||
        <div className="flex flex-col gap-1">
 | 
			
		||||
          <div className="flex justify-between items-center">
 | 
			
		||||
            <Text weight="medium">{t('Model Source Manifest List')}</Text>
 | 
			
		||||
            <ToolTipButton desc={t('Refresh')} icon={<ArrowClockwise20Regular/>} onClick={() => {
 | 
			
		||||
            <ToolTipButton desc={t('Refresh')} icon={<ArrowClockwise20Regular />} onClick={() => {
 | 
			
		||||
              refreshModels(false);
 | 
			
		||||
              saveConfigs();
 | 
			
		||||
            }}/>
 | 
			
		||||
            }} />
 | 
			
		||||
          </div>
 | 
			
		||||
          <Text size={100}>
 | 
			
		||||
            {t('Provide JSON file URLs for the models manifest. Separate URLs with semicolons. The "models" field in JSON files will be parsed into the following table.')}
 | 
			
		||||
          </Text>
 | 
			
		||||
          <Textarea size="large" resize="vertical"
 | 
			
		||||
                    value={commonStore.modelSourceManifestList}
 | 
			
		||||
                    onChange={(e, data) => commonStore.setModelSourceManifestList(data.value)}/>
 | 
			
		||||
            value={commonStore.modelSourceManifestList}
 | 
			
		||||
            onChange={(e, data) => commonStore.setModelSourceManifestList(data.value)} />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="flex grow overflow-hidden">
 | 
			
		||||
          <DataGrid
 | 
			
		||||
            items={commonStore.modelSourceList}
 | 
			
		||||
            columns={columns}
 | 
			
		||||
            sortable={true}
 | 
			
		||||
            defaultSortState={{sortColumn: 'actions', sortDirection: 'ascending'}}
 | 
			
		||||
            style={{display: 'flex'}}
 | 
			
		||||
            defaultSortState={{ sortColumn: 'actions', sortDirection: 'ascending' }}
 | 
			
		||||
            style={{ display: 'flex' }}
 | 
			
		||||
            className="flex-col w-full"
 | 
			
		||||
          >
 | 
			
		||||
            <DataGridHeader>
 | 
			
		||||
              <DataGridRow>
 | 
			
		||||
                {({renderHeaderCell}) => (
 | 
			
		||||
                {({ renderHeaderCell }) => (
 | 
			
		||||
                  <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
 | 
			
		||||
                )}
 | 
			
		||||
              </DataGridRow>
 | 
			
		||||
            </DataGridHeader>
 | 
			
		||||
            <div className="overflow-y-auto overflow-x-hidden">
 | 
			
		||||
              <DataGridBody<ModelSourceItem>>
 | 
			
		||||
                {({item, rowId}) => (
 | 
			
		||||
                {({ item, rowId }) => (
 | 
			
		||||
                  <DataGridRow<ModelSourceItem> key={rowId}>
 | 
			
		||||
                    {({renderCell}) => (
 | 
			
		||||
                    {({ renderCell }) => (
 | 
			
		||||
                      <DataGridCell>{renderCell(item)}</DataGridCell>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </DataGridRow>
 | 
			
		||||
@ -214,6 +214,6 @@ export const Models: FC = observer(() => {
 | 
			
		||||
          </DataGrid>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    }/>
 | 
			
		||||
    } />
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
import React, {FC} from 'react';
 | 
			
		||||
import {Text} from '@fluentui/react-components';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import React, { FC } from 'react';
 | 
			
		||||
import { Text } from '@fluentui/react-components';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
export const Train: FC = () => {
 | 
			
		||||
  const {t} = useTranslation();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col box-border gap-5 p-2">
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import {ReactElement} from 'react';
 | 
			
		||||
import {Configs} from './Configs';
 | 
			
		||||
import { ReactElement } from 'react';
 | 
			
		||||
import { Configs } from './Configs';
 | 
			
		||||
import {
 | 
			
		||||
  ArrowDownload20Regular,
 | 
			
		||||
  Chat20Regular,
 | 
			
		||||
@ -10,13 +10,13 @@ import {
 | 
			
		||||
  Settings20Regular,
 | 
			
		||||
  Storage20Regular
 | 
			
		||||
} from '@fluentui/react-icons';
 | 
			
		||||
import {Home} from './Home';
 | 
			
		||||
import {Chat} from './Chat';
 | 
			
		||||
import {Models} from './Models';
 | 
			
		||||
import {Train} from './Train';
 | 
			
		||||
import {Settings} from './Settings';
 | 
			
		||||
import {About} from './About';
 | 
			
		||||
import {Downloads} from './Downloads';
 | 
			
		||||
import { Home } from './Home';
 | 
			
		||||
import { Chat } from './Chat';
 | 
			
		||||
import { Models } from './Models';
 | 
			
		||||
import { Train } from './Train';
 | 
			
		||||
import { Settings } from './Settings';
 | 
			
		||||
import { About } from './About';
 | 
			
		||||
import { Downloads } from './Downloads';
 | 
			
		||||
 | 
			
		||||
type NavigationItem = {
 | 
			
		||||
  label: string;
 | 
			
		||||
@ -30,57 +30,57 @@ export const pages: NavigationItem[] = [
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Home',
 | 
			
		||||
    path: '/',
 | 
			
		||||
    icon: <Home20Regular/>,
 | 
			
		||||
    element: <Home/>,
 | 
			
		||||
    icon: <Home20Regular />,
 | 
			
		||||
    element: <Home />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Chat',
 | 
			
		||||
    path: '/chat',
 | 
			
		||||
    icon: <Chat20Regular/>,
 | 
			
		||||
    element: <Chat/>,
 | 
			
		||||
    icon: <Chat20Regular />,
 | 
			
		||||
    element: <Chat />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Configs',
 | 
			
		||||
    path: '/configs',
 | 
			
		||||
    icon: <DocumentSettings20Regular/>,
 | 
			
		||||
    element: <Configs/>,
 | 
			
		||||
    icon: <DocumentSettings20Regular />,
 | 
			
		||||
    element: <Configs />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Models',
 | 
			
		||||
    path: '/models',
 | 
			
		||||
    icon: <DataUsageSettings20Regular/>,
 | 
			
		||||
    element: <Models/>,
 | 
			
		||||
    icon: <DataUsageSettings20Regular />,
 | 
			
		||||
    element: <Models />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Downloads',
 | 
			
		||||
    path: '/downloads',
 | 
			
		||||
    icon: <ArrowDownload20Regular/>,
 | 
			
		||||
    element: <Downloads/>,
 | 
			
		||||
    icon: <ArrowDownload20Regular />,
 | 
			
		||||
    element: <Downloads />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Train',
 | 
			
		||||
    path: '/train',
 | 
			
		||||
    icon: <Storage20Regular/>,
 | 
			
		||||
    element: <Train/>,
 | 
			
		||||
    icon: <Storage20Regular />,
 | 
			
		||||
    element: <Train />,
 | 
			
		||||
    top: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'Settings',
 | 
			
		||||
    path: '/settings',
 | 
			
		||||
    icon: <Settings20Regular/>,
 | 
			
		||||
    element: <Settings/>,
 | 
			
		||||
    icon: <Settings20Regular />,
 | 
			
		||||
    element: <Settings />,
 | 
			
		||||
    top: false
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'About',
 | 
			
		||||
    path: '/about',
 | 
			
		||||
    icon: <Info20Regular/>,
 | 
			
		||||
    element: <About/>,
 | 
			
		||||
    icon: <Info20Regular />,
 | 
			
		||||
    element: <About />,
 | 
			
		||||
    top: false
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
import commonStore from './stores/commonStore';
 | 
			
		||||
import {ReadJson} from '../wailsjs/go/backend_golang/App';
 | 
			
		||||
import {Cache, checkUpdate, downloadProgramFiles, LocalConfig, refreshModels, saveCache} from './utils';
 | 
			
		||||
import {getStatus} from './apis';
 | 
			
		||||
import {EventsOn} from '../wailsjs/runtime';
 | 
			
		||||
import {defaultModelConfigs} from './pages/Configs';
 | 
			
		||||
import { ReadJson } from '../wailsjs/go/backend_golang/App';
 | 
			
		||||
import { Cache, checkUpdate, downloadProgramFiles, LocalConfig, refreshModels, saveCache } from './utils';
 | 
			
		||||
import { getStatus } from './apis';
 | 
			
		||||
import { EventsOn } from '../wailsjs/runtime';
 | 
			
		||||
import { defaultModelConfigs } from './pages/Configs';
 | 
			
		||||
 | 
			
		||||
export async function startup() {
 | 
			
		||||
  downloadProgramFiles();
 | 
			
		||||
@ -26,13 +26,13 @@ export async function startup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function initRemoteText() {
 | 
			
		||||
  await fetch('https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json', {cache: 'no-cache'})
 | 
			
		||||
    .then(r => r.json()).then((data) => {
 | 
			
		||||
      if (data.introduction)
 | 
			
		||||
        commonStore.setIntroduction(data.introduction);
 | 
			
		||||
      if (data.about)
 | 
			
		||||
        commonStore.setAbout(data.about);
 | 
			
		||||
    }).then(saveCache);
 | 
			
		||||
  await fetch('https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json', { cache: 'no-cache' })
 | 
			
		||||
  .then(r => r.json()).then((data) => {
 | 
			
		||||
    if (data.introduction)
 | 
			
		||||
      commonStore.setIntroduction(data.introduction);
 | 
			
		||||
    if (data.about)
 | 
			
		||||
      commonStore.setAbout(data.about);
 | 
			
		||||
  }).then(saveCache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function initConfig() {
 | 
			
		||||
 | 
			
		||||
@ -19,32 +19,22 @@ export enum ModelStatus {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CommonStore {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    makeAutoObservable(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // global
 | 
			
		||||
  modelStatus: ModelStatus = ModelStatus.Offline;
 | 
			
		||||
  depComplete: boolean = false;
 | 
			
		||||
 | 
			
		||||
  // home
 | 
			
		||||
  introduction: IntroductionContent = manifest.introduction;
 | 
			
		||||
 | 
			
		||||
  // chat
 | 
			
		||||
  conversations: Conversations = {};
 | 
			
		||||
  conversationsOrder: string[] = [];
 | 
			
		||||
 | 
			
		||||
  // configs
 | 
			
		||||
  currentModelConfigIndex: number = 0;
 | 
			
		||||
  modelConfigs: ModelConfig[] = [];
 | 
			
		||||
 | 
			
		||||
  // models
 | 
			
		||||
  modelSourceManifestList: string = 'https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json;';
 | 
			
		||||
  modelSourceList: ModelSourceItem[] = [];
 | 
			
		||||
 | 
			
		||||
  // downloads
 | 
			
		||||
  downloadList: DownloadStatus[] = [];
 | 
			
		||||
 | 
			
		||||
  // settings
 | 
			
		||||
  settings: SettingsType = {
 | 
			
		||||
    language: getUserLanguage(),
 | 
			
		||||
@ -53,10 +43,13 @@ class CommonStore {
 | 
			
		||||
    giteeUpdatesSource: getUserLanguage() === 'zh',
 | 
			
		||||
    cnMirror: getUserLanguage() === 'zh'
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // about
 | 
			
		||||
  about: AboutContent = manifest.about;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    makeAutoObservable(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCurrentModelConfig = () => {
 | 
			
		||||
    return this.modelConfigs[this.currentModelConfigIndex];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,8 @@ export function getConversationPairs(records: Record[], isCompletion: boolean):
 | 
			
		||||
  } else {
 | 
			
		||||
    pairs = [];
 | 
			
		||||
    for (const record of records) {
 | 
			
		||||
      pairs.push({role: 'user', content: record.question});
 | 
			
		||||
      pairs.push({role: 'assistant', content: record.answer});
 | 
			
		||||
      pairs.push({ role: 'user', content: record.question });
 | 
			
		||||
      pairs.push({ role: 'assistant', content: record.answer });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -102,15 +102,15 @@ export async function refreshRemoteModels(cache: { models: ModelSourceItem[] })
 | 
			
		||||
    url => fetch(url, { cache: 'no-cache' }).then(r => r.json()));
 | 
			
		||||
 | 
			
		||||
  await Promise.allSettled(requests)
 | 
			
		||||
    .then((data: PromiseSettledResult<Cache>[]) => {
 | 
			
		||||
      cache.models.push(...data.flatMap(d => {
 | 
			
		||||
        if (d.status === 'fulfilled')
 | 
			
		||||
          return d.value.models;
 | 
			
		||||
        return [];
 | 
			
		||||
      }));
 | 
			
		||||
    })
 | 
			
		||||
    .catch(() => {
 | 
			
		||||
    });
 | 
			
		||||
  .then((data: PromiseSettledResult<Cache>[]) => {
 | 
			
		||||
    cache.models.push(...data.flatMap(d => {
 | 
			
		||||
      if (d.status === 'fulfilled')
 | 
			
		||||
        return d.value.models;
 | 
			
		||||
      return [];
 | 
			
		||||
    }));
 | 
			
		||||
  })
 | 
			
		||||
  .catch(() => {
 | 
			
		||||
  });
 | 
			
		||||
  cache.models = cache.models.filter((model, index, self) => {
 | 
			
		||||
    return model.name.endsWith('.pth')
 | 
			
		||||
      && index === self.findIndex(
 | 
			
		||||
@ -214,42 +214,42 @@ export async function checkUpdate(notifyEvenLatest: boolean = false) {
 | 
			
		||||
    'https://api.github.com/repos/josstorer/RWKV-Runner/releases/latest' :
 | 
			
		||||
    'https://gitee.com/api/v5/repos/josc146/RWKV-Runner/releases/latest'
 | 
			
		||||
  ).then((r) => {
 | 
			
		||||
    if (r.ok) {
 | 
			
		||||
      r.json().then((data) => {
 | 
			
		||||
        if (data.tag_name) {
 | 
			
		||||
          const versionTag = data.tag_name;
 | 
			
		||||
          if (versionTag.replace('v', '') > manifest.version) {
 | 
			
		||||
            updateUrl = !commonStore.settings.giteeUpdatesSource ?
 | 
			
		||||
              `https://github.com/josStorer/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe` :
 | 
			
		||||
              `https://gitee.com/josc146/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe`;
 | 
			
		||||
            toastWithButton(t('New Version Available') + ': ' + versionTag, t('Update'), () => {
 | 
			
		||||
              deletePythonProgramFiles();
 | 
			
		||||
              setTimeout(() => {
 | 
			
		||||
                UpdateApp(updateUrl).catch((e) => {
 | 
			
		||||
                  toast(t('Update Error, Please restart this program') + ' - ' + e.message || e, {
 | 
			
		||||
                    type: 'error',
 | 
			
		||||
                    position: 'bottom-left',
 | 
			
		||||
                    autoClose: false
 | 
			
		||||
      if (r.ok) {
 | 
			
		||||
        r.json().then((data) => {
 | 
			
		||||
          if (data.tag_name) {
 | 
			
		||||
            const versionTag = data.tag_name;
 | 
			
		||||
            if (versionTag.replace('v', '') > manifest.version) {
 | 
			
		||||
              updateUrl = !commonStore.settings.giteeUpdatesSource ?
 | 
			
		||||
                `https://github.com/josStorer/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe` :
 | 
			
		||||
                `https://gitee.com/josc146/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe`;
 | 
			
		||||
              toastWithButton(t('New Version Available') + ': ' + versionTag, t('Update'), () => {
 | 
			
		||||
                deletePythonProgramFiles();
 | 
			
		||||
                setTimeout(() => {
 | 
			
		||||
                  UpdateApp(updateUrl).catch((e) => {
 | 
			
		||||
                    toast(t('Update Error, Please restart this program') + ' - ' + e.message || e, {
 | 
			
		||||
                      type: 'error',
 | 
			
		||||
                      position: 'bottom-left',
 | 
			
		||||
                      autoClose: false
 | 
			
		||||
                    });
 | 
			
		||||
                  });
 | 
			
		||||
                });
 | 
			
		||||
              }, 500);
 | 
			
		||||
            }, {
 | 
			
		||||
              autoClose: false,
 | 
			
		||||
              position: 'bottom-left'
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            if (notifyEvenLatest) {
 | 
			
		||||
              toast(t('This is the latest version'), { type: 'success', position: 'bottom-left', autoClose: 2000 });
 | 
			
		||||
                }, 500);
 | 
			
		||||
              }, {
 | 
			
		||||
                autoClose: false,
 | 
			
		||||
                position: 'bottom-left'
 | 
			
		||||
              });
 | 
			
		||||
            } else {
 | 
			
		||||
              if (notifyEvenLatest) {
 | 
			
		||||
                toast(t('This is the latest version'), { type: 'success', position: 'bottom-left', autoClose: 2000 });
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            throw new Error('Invalid response.');
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          throw new Error('Invalid response.');
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new Error('Network response was not ok.');
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error('Network response was not ok.');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ).catch((e) => {
 | 
			
		||||
    toast(t('Updates Check Error') + ' - ' + e.message || e, { type: 'error', position: 'bottom-left' });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user