main
lvchenpeng 2 months ago
parent c609367e9e
commit dc31663d1b

@ -8,7 +8,7 @@ services:
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
- '9001:9001' # MinIO Console
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
# - '${LOBE_PORT}:3210' # LobeChat
- '${LOBE_PORT}:3210' # LobeChat
command: tail -f /dev/null
networks:
- lobe-network
@ -90,76 +90,76 @@ services:
env_file:
- .env
# lobe:
# image: lobehub/lobe-chat-database
# container_name: lobe-chat
# network_mode: 'service:network-service'
# depends_on:
# postgresql:
# condition: service_healthy
# network-service:
# condition: service_started
# minio:
# condition: service_started
# casdoor:
# condition: service_started
lobe:
image: lobehub/lobe-chat-database
container_name: lobe-chat
network_mode: 'service:network-service'
depends_on:
postgresql:
condition: service_healthy
network-service:
condition: service_started
minio:
condition: service_started
casdoor:
condition: service_started
# environment:
# - 'NEXT_AUTH_SSO_PROVIDERS=casdoor'
# - 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
# - 'NEXT_AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
# - 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
# - 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
# - 'S3_ENABLE_PATH_STYLE=1'
# - 'S3_ACCESS_KEY=${MINIO_ROOT_USER}'
# - 'S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}'
# - 'S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}'
# - 'LLM_VISION_IMAGE_USE_BASE64=1'
# - 'S3_SET_ACL=0'
# - 'SEARXNG_URL=http://searxng:8080'
# env_file:
# - .env
# restart: always
# entrypoint: >
# /bin/sh -c "
# /bin/node /app/startServer.js &
# LOBE_PID=\$!
# sleep 3
# if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
# echo '⚠Warining: Unable to fetch OIDC configuration from Casdoor'
# echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
# echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
# echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
# echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# else
# if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
# printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
# echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
# echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
# echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# printf '❌错误Auth 的 issuer 冲突OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
# echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
# echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
# echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# fi
# fi
# if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/minio/health/live 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
# echo '⚠Warining: Unable to fetch MinIO health status'
# echo 'Request URL: ${S3_ENDPOINT}/minio/health/live'
# echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# echo '⚠️注意:无法获取 MinIO 健康状态'
# echo '请求 URL: ${S3_ENDPOINT}/minio/health/live'
# echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
# echo ''
# fi
# wait \$LOBE_PID
# "
environment:
- 'NEXT_AUTH_SSO_PROVIDERS=casdoor'
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
- 'NEXT_AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
- 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
- 'S3_ENABLE_PATH_STYLE=1'
- 'S3_ACCESS_KEY=${MINIO_ROOT_USER}'
- 'S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}'
- 'S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}'
- 'LLM_VISION_IMAGE_USE_BASE64=1'
- 'S3_SET_ACL=0'
- 'SEARXNG_URL=http://searxng:8080'
env_file:
- .env
restart: always
entrypoint: >
/bin/sh -c "
/bin/node /app/startServer.js &
LOBE_PID=\$!
sleep 3
if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
echo '⚠Warining: Unable to fetch OIDC configuration from Casdoor'
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
else
if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
printf '❌错误Auth 的 issuer 冲突OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
fi
fi
if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/minio/health/live 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
echo '⚠Warining: Unable to fetch MinIO health status'
echo 'Request URL: ${S3_ENDPOINT}/minio/health/live'
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
echo '⚠️注意:无法获取 MinIO 健康状态'
echo '请求 URL: ${S3_ENDPOINT}/minio/health/live'
echo '了解更多https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
echo ''
fi
wait \$LOBE_PID
"
volumes:
data:

@ -42,7 +42,7 @@
"db:push-test": "NODE_ENV=test drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:z-pull": "drizzle-kit introspect",
"dev": "next dev --turbopack -p 3210",
"dev": "next dev -p 3212",
"docs:i18n": "lobe-i18n md && npm run lint:md && npm run lint:mdx",
"docs:seo": "lobe-seo && npm run lint:mdx",
"i18n": "npm run workflow:i18n && lobe-i18n",
@ -59,7 +59,7 @@
"self-hosting:docker": "docker build -t lobe-chat:local .",
"self-hosting:docker-cn": "docker build -t lobe-chat-local --build-arg USE_CN_MIRROR=true .",
"self-hosting:docker-cn@database": "docker build -t lobe-chat-database-local -f Dockerfile.database --build-arg USE_CN_MIRROR=true .",
"start": "next start -p 3210",
"start": "next start -p 3212",
"stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
"test": "npm run test-app && npm run test-server",
"test-app": "vitest run --config vitest.config.ts",
@ -153,9 +153,12 @@
"ai": "^3.4.33",
"antd": "^5.23.0",
"antd-style": "^3.7.1",
"axios": "^1.8.3",
"brotli-wasm": "^3.0.1",
"chroma-js": "^3.1.2",
"countries-and-timezones": "^3.7.2",
"csv-string": "^4.1.1",
"csv-stringify": "^6.5.2",
"dayjs": "^1.11.13",
"debug": "^4.4.0",
"dexie": "^3.2.7",
@ -185,7 +188,7 @@
"mdast-util-to-markdown": "^2.1.2",
"modern-screenshot": "^4.5.5",
"nanoid": "^5.0.9",
"next": "^15.2.0",
"next": "latest",
"next-auth": "beta",
"next-mdx-remote": "^5.0.0",
"nextjs-toploader": "^3.7.15",

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,5 +1,5 @@
import { ActionIcon } from '@lobehub/ui';
import { Tooltip } from 'antd';
import { Tooltip, Image } from 'antd';
import { LucideX } from 'lucide-react';
import { Suspense, memo } from 'react';
import { useTranslation } from 'react-i18next';

@ -0,0 +1,29 @@
import { ActionIcon } from '@lobehub/ui';
import { Tooltip, Image } from 'antd';
import { LucideX } from 'lucide-react';
import { Suspense, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import UserAvatar from '@/features/User/UserAvatar';
import UserPanel from '@/features/User/UserPanel';
import { useUserStore } from '@/store/user';
import { preferenceSelectors } from '@/store/user/selectors';
const Images = memo(() => {
const { t } = useTranslation('common');
const hideSettingsMoveGuide = useUserStore(preferenceSelectors.hideSettingsMoveGuide);
const updateGuideState = useUserStore((s) => s.updateGuideState);
const content = (
<Image alt={""} preview={false} src="/images/logo.png" />
);
return (
content
);
});
Images.displayName = 'Images';
export default Images;

@ -1,13 +1,37 @@
import { ActionIcon } from '@lobehub/ui';
import { Compass, FolderClosed, MessageSquare } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { useSessionStore } from '@/store/session';
import { useUserStore } from '@/store/user';
import { createStyles } from 'antd-style';
import { Image } from "antd";
const useStyles = createStyles(({ css }) => ({
iconImg: css`
width: 48px;
height: 48px;
`,
iconSelectText: css`
color: #0044FF;
`,
iconText: css`
text-align: center;
color: #fff;
`,
linkUrl: css`
width: 100%;
display: inline-block;
`,
linkclic: css`
background-color: #fff;
`,
}));
export interface TopActionProps {
isPinned?: boolean | null;
@ -18,45 +42,59 @@ const TopActions = memo<TopActionProps>(({ tab, isPinned }) => {
const { t } = useTranslation('common');
const switchBackToChat = useGlobalStore((s) => s.switchBackToChat);
const { showMarket, enableKnowledgeBase } = useServerConfigStore(featureFlagsSelectors);
const { styles, cx } = useStyles()
const [value, setValue] = useState("chat")
return (
<>
<Link
aria-label={t('tab.chat')}
className={value === '/chat' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)}
href={'/chat'}
onClick={(e) => {
e.preventDefault();
setValue("/chat")
console.log(value)
switchBackToChat(useSessionStore.getState().activeId);
}}
>
<ActionIcon
active={tab === SidebarTabKey.Chat && !isPinned}
icon={MessageSquare}
placement={'right'}
size="large"
title={t('tab.chat')}
/>
<Image alt={"chat"} className={cx(styles.iconImg)} preview={false} src="/images/hh.png" />
<div className={value === '/chat' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
{enableKnowledgeBase && (
<Link aria-label={t('tab.files')} href={'/files'}>
<ActionIcon
active={tab === SidebarTabKey.Files}
icon={FolderClosed}
placement={'right'}
size="large"
title={t('tab.files')}
/>
<Link aria-label={t('tab.files')} className={value === '/discover/assistants' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={`/discover/assistants`} onClick={() => {setValue("/discover/assistants")}}>
<Image alt={"files"} className={cx(styles.iconImg)} preview={false} src="/images/zs.png" />
<div className={value === '/discover/assistants' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
)}
{showMarket && (
<Link aria-label={t('tab.discover')} className={value === '/applicationset' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={'/applicationset'} onClick={() => {setValue("/applicationset")}}>
<Image alt={"discover"} className={cx(styles.iconImg)} preview={false} src="/images/gj.png" />
<div className={value === '/applicationset' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }>AI</div>
</Link>
)}
{showMarket && (
<Link aria-label={t('tab.model')} className={value === '/model' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={`/discover/models`} onClick={() => {setValue("/model")}}>
<Image alt={"model"} className={cx(styles.iconImg)} preview={false} src="/images/mx.png" />
<div className={value === '/model' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
)}
{showMarket && (
<Link aria-label={t('tab.robot')} className={value === '/robot' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={'/robot'} onClick={() => {setValue("/robot")}}>
<Image alt={"robot"} className={cx(styles.iconImg)} preview={false} src="/images/szr.png" />
<div className={value === '/robot' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
)}
{showMarket && (
<Link aria-label={t('tab.plugins')} className={value === '/plugins' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={`/discover/plugins`} onClick={() => {setValue("/plugins")}}>
<Image alt={"plugins"} className={cx(styles.iconImg)} preview={false} src="/images/cj.png" />
<div className={value === '/plugins' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
)}
{showMarket && (
<Link aria-label={t('tab.discover')} href={'/discover'}>
<ActionIcon
active={tab === SidebarTabKey.Discover}
icon={Compass}
placement={'right'}
size="large"
title={t('tab.discover')}
/>
<Link aria-label={t('tab.knowledge')} className={value === '/knowledge' ? cx(styles.linkUrl, styles.linkclic) : cx(styles.linkUrl)} href={'/knowledge'} onClick={() => {setValue("/knowledge")}}>
<Image className={cx(styles.iconImg)} preview={false} src="/images/zsk.png" />
<div className={value === '/knowledge' ? cx(styles.iconText, styles.iconSelectText) : cx(styles.iconText) }></div>
</Link>
)}
</>

@ -13,6 +13,7 @@ import Avatar from './Avatar';
import BottomActions from './BottomActions';
import PinList from './PinList';
import TopActions from './TopActions';
import {Image} from "antd";
const Top = () => {
const [isPinned] = useQueryState('pinned', parseAsBoolean);
@ -28,13 +29,12 @@ const Nav = memo(() => {
return (
!inZenMode && (
<SideNav
avatar={<Avatar />}
bottomActions={<BottomActions />}
style={{ height: '100%', zIndex: 100 }}
avatar={<Image alt={""} preview={false} src="/images/logo.png" />}
style={{ backgroundColor: '#2E62FF', height: '100%', width: '100px', zIndex: 100 }}
topActions={
<Suspense>
<Top />
{showPinList && <PinList />}
{/*{showPinList && <PinList />}*/}
</Suspense>
}
/>

@ -1,6 +1,6 @@
'use client';
import { useTheme } from 'antd-style';
import { createStyles, useTheme } from 'antd-style';
import dynamic from 'next/dynamic';
import { PropsWithChildren, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
@ -8,16 +8,77 @@ import { Flexbox } from 'react-layout-kit';
import { BANNER_HEIGHT } from '@/features/AlertBanner/CloudBanner';
import { usePlatform } from '@/hooks/usePlatform';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { Divider } from "antd";
import Avatar from './SideBar/Avatar';
import SideBar from './SideBar';
const title:{[key: string]: string } = {
"/chat": '会话',
"/discover/assistants": "助手",
"/files": "工具",
"/knowledge": "知识库",
"/model": "模型",
"/plugins": "插件",
"/power": "算力",
"/robot": "数字人",
}
const useStyles = createStyles(({ css }) => ({
dividerCen: css`
width: 1px;
height: 26px;
background-color: #d8d8d8;
margin: 8px 16px;
`,
inp: css`
width: 283px;
margin-right: 43px;
background: #F3F6FF;
&:hover, &:active, &:focus {
border-color: transparent !important;
background: #F3F6FF !important;
}
`,
ledDiv: css `
width: 50%;
display: inline-block;
`,
leftTitle: css`
font-size: 20px;
color: #3d3d3d;
display: inline-block
`,
nameSpn: css`
margin-right: 16px;
margin-left: 18px;
display: inline-block;
`,
serchIcon: css`
color: #BCCEFF;
font-size: 24px;
`,
topCenten: css`
width: 100%;
line-height: 60px;
background: #FFFFFF;
box-sizing: border-box;
/* 分割线颜色 */
border-width: 0px 0px 2px 0px;
border-style: solid;
border-color: rgba(187, 204, 253, 0.24);
`,
}))
const CloudBanner = dynamic(() => import('@/features/AlertBanner/CloudBanner'));
const Layout = memo<PropsWithChildren>(({ children }) => {
const { isPWA } = usePlatform();
const theme = useTheme();
const { styles, cx } = useStyles()
const { showCloudPromotion } = useServerConfigStore(featureFlagsSelectors);
// const pathName = window?.location?.pathname ?? '/welcome'
const pathName = '/welcome'
return (
<>
@ -32,7 +93,21 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
width={'100%'}
>
<SideBar />
{children}
<div style={{width: '100%'}}>
<div style={{ width: '100%' }}>
<div className={cx(styles.topCenten)}>
<div className={cx(styles.ledDiv)} style={{ width: 'calc(100% - 60px)'}}>
<span className={cx(styles.leftTitle)} style={{ marginLeft: '16px' }}>AI</span>
<Divider className={cx(styles.dividerCen)} type="vertical" />
<span className={cx(styles.leftTitle)}>{title[pathName]}</span>
</div>
<div className={cx(styles.ledDiv)} style={{lineHeight: '60px', width: '60px', verticalAlign: 'middle'}}>
<Avatar />
</div>
</div>
</div>
{children}
</div>
</Flexbox>
</>
);

@ -0,0 +1,50 @@
// import StructuredData from '@/components/StructuredData';
import { Locales } from '@/locales/resources';
// import { ldModule } from '@/server/ld';
import { metadataModule } from '@/server/metadata';
// import { DiscoverService } from '@/server/services/discover';
import { translation } from '@/server/translation';
// import { isMobileDevice } from '@/utils/responsive';
import ApplicationSet from "./applicationset";
// import {Button} from "antd";
type Props = { searchParams: { hl?: Locales } };
export const generateMetadata = async ({ searchParams }: Props) => {
const { t, locale } = await translation('metadata', searchParams?.hl);
return metadataModule.generate({
alternate: true,
description: t('files.description'),
locale,
title: t('files.title'),
url: '/files',
});
};
const Page = async () => {
// const { t, locale } = await translation('metadata', searchParams?.hl);
// const mobile = isMobileDevice();
// const discoverService = new DiscoverService();
// const items = await discoverService.getAssistantList(locale);
// const ld = ldModule.generate({
// description: t('files.description'),
// title: t('files.title'),
// url: '/files',
// webpage: {
// enable: true,
// search: '/files/search',
// },
// });
return (
<ApplicationSet />
);
};
Page.DisplayName = 'DiscoverAssistants';
export default Page;

@ -12,6 +12,7 @@ import { useSendMessage } from '@/features/ChatInput/useSend';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { isMacOS } from '@/utils/platform';
import {HighlightOutlined, SendOutlined} from "@ant-design/icons";
import MessageFromUrl from './MessageFromUrl';
import SendMore from './SendMore';
@ -24,8 +25,23 @@ const useStyles = createStyles(({ css, prefixCls, token }) => {
width: 28px;
}
`,
btn: css`
width: 106px;
border: 1px solid #2E62FF !important;
color: #2E62FF !important;
font-size: 16px;
`,
btnSend: css`
width: 106px;
background: #507BFF !important;
color: #fff !important;
font-size: 16px;
`,
loadingButton: css`
width: 106px;
display: flex;
background: #507BFF !important;
color: #fff !important;
align-items: center;
`,
overrideAntdIcon: css`
@ -47,9 +63,10 @@ const useStyles = createStyles(({ css, prefixCls, token }) => {
interface FooterProps {
expand: boolean;
onExpandChange: (expand: boolean) => void;
clearClick: () => void
}
const Footer = memo<FooterProps>(({ onExpandChange, expand }) => {
const Footer = memo<FooterProps>(({ onExpandChange, expand,clearClick }) => {
const { t } = useTranslation('chat');
const { styles } = useStyles();
@ -67,6 +84,10 @@ const Footer = memo<FooterProps>(({ onExpandChange, expand }) => {
setIsMac(isMacOS());
}, [setIsMac]);
const handleClickClear = () => {
clearClick()
}
return (
<>
<Suspense fallback={null}>
@ -80,40 +101,51 @@ const Footer = memo<FooterProps>(({ onExpandChange, expand }) => {
gap={8}
horizontal
padding={'0 24px'}
style={{ display: 'inline-block', height: "90px", width: "100%", textAlign: 'right' }}
>
<Flexbox align={'center'} gap={8} horizontal style={{ overflow: 'hidden' }}>
{expand && <LocalFiles />}
</Flexbox>
<Flexbox align={'center'} flex={'none'} gap={8} horizontal>
<ShortcutHint />
<SaveTopic />
<Flexbox style={{ minWidth: 92 }}>
{isAIGenerating ? (
{/* <Flexbox align={'center'} gap={8} horizontal style={{ overflow: 'hidden' }}>*/}
{/* {expand && <LocalFiles />}*/}
{/* </Flexbox>*/}
{/* <Flexbox align={'center'} flex={'none'} gap={8} horizontal>*/}
{/* <ShortcutHint />*/}
{/* <SaveTopic />*/}
{/* <Flexbox style={{ minWidth: 92 }}>*/}
<div style={{ display: 'inline-block', marginRight: '20px'}}>
<Button className={styles.btn} onClick={() => handleClickClear()}>
<div><HighlightOutlined style={{ fontSize: "20px"}} /></div>
{t('input.clear')}
</Button>
</div>
<div style={{ display: 'inline-block'}}>
{isAIGenerating ? (
<Button
className={styles.loadingButton}
icon={<StopLoadingIcon />}
onClick={stopGenerateMessage}
>
{t('input.stop')}
</Button>
) : (
<Space.Compact>
<Button
className={styles.loadingButton}
icon={<StopLoadingIcon />}
onClick={stopGenerateMessage}
className={styles.btnSend}
disabled={!canSend}
loading={!canSend}
onClick={() => {
sendMessage();
onExpandChange?.(false);
}}
type={'primary'}
>
{t('input.stop')}
<div><SendOutlined style={{ fontSize: "20px"}} /></div>
{t('input.send')}
</Button>
) : (
<Space.Compact>
<Button
disabled={!canSend}
loading={!canSend}
onClick={() => {
sendMessage();
onExpandChange?.(false);
}}
type={'primary'}
>
{t('input.send')}
</Button>
<SendMore disabled={!canSend} isMac={isMac} />
</Space.Compact>
)}
</Flexbox>
</Flexbox>
{/*<SendMore disabled={!canSend} isMac={isMac} />*/}
</Space.Compact>
)}
</div>
{/* </Flexbox>*/}
{/*</Flexbox>*/}
</Flexbox>
</>
);

@ -9,6 +9,7 @@ import { systemStatusSelectors } from '@/store/global/selectors';
import Footer from './Footer';
import TextArea from './TextArea';
import {useChatStore} from "@/store/chat";
const leftActions = [
'model',
@ -24,16 +25,21 @@ const leftActions = [
const rightActions = ['clear'] as ActionKeys[];
const renderTextArea = (onSend: () => void) => <TextArea onSend={onSend} />;
const renderFooter: FooterRender = ({ expand, onExpandChange }) => (
<Footer expand={expand} onExpandChange={onExpandChange} />
);
const Desktop = memo(() => {
const [updateInputMessage] = useChatStore((s) => [s.updateInputMessage])
const [inputHeight, updatePreference] = useGlobalStore((s) => [
systemStatusSelectors.inputHeight(s),
s.updateSystemStatus,
]);
const renderTextArea = (onSend: () => void) => <TextArea onSend={onSend} />;
const renderFooter: FooterRender = ({ expand, onExpandChange }) => (
<Footer clearClick={() => clearClick()} expand={expand} onExpandChange={onExpandChange} />
);
const clearClick = () => {
updateInputMessage("")
}
return (
<DesktopChatInput

@ -3,6 +3,7 @@ import React, { Suspense, lazy } from 'react';
import { DynamicLayoutProps } from '@/types/next';
import { RouteVariants } from '@/utils/server/routeVariants';
import { Image } from "antd";
import Desktop from './_layout/Desktop';
import Mobile from './_layout/Mobile';
@ -19,6 +20,7 @@ const Topic = async (props: DynamicLayoutProps) => {
return (
<>
{!isMobile && <SystemRole />}
<div><Image alt="头像" preview={false} src="/images/zsImage.png" /></div>
<Layout>
<Suspense fallback={<SkeletonList />}>
<TopicContent />

@ -2,6 +2,7 @@
import { EmptyCard } from '@lobehub/ui';
import { useThemeMode } from 'antd-style';
import { Image } from 'antd';
import React, { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

@ -27,13 +27,13 @@ const HeaderAction = memo(() => {
return (
<Flexbox gap={4} horizontal>
<ShareButton />
{isAgentEditable && <SettingButton />}
<ActionIcon
icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
onClick={() => toggleConfig()}
size={DESKTOP_HEADER_ICON_SIZE}
title={t('roleAndArchive')}
/>
{isAgentEditable && <SettingButton />}
</Flexbox>
);
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save