@lobehub/chat 0.160.4 → 0.160.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/docs/self-hosting/environment-variables/analytics.mdx +12 -35
- package/docs/self-hosting/environment-variables/analytics.zh-CN.mdx +13 -34
- package/locales/ar/error.json +2 -0
- package/locales/bg-BG/error.json +2 -0
- package/locales/de-DE/error.json +2 -0
- package/locales/en-US/error.json +2 -0
- package/locales/es-ES/error.json +2 -0
- package/locales/fr-FR/error.json +2 -0
- package/locales/it-IT/error.json +2 -0
- package/locales/ja-JP/error.json +2 -0
- package/locales/ko-KR/error.json +2 -0
- package/locales/nl-NL/error.json +2 -0
- package/locales/pl-PL/error.json +2 -0
- package/locales/pt-BR/error.json +2 -0
- package/locales/ru-RU/error.json +2 -0
- package/locales/tr-TR/error.json +2 -0
- package/locales/vi-VN/error.json +2 -0
- package/locales/zh-CN/error.json +2 -0
- package/locales/zh-TW/error.json +2 -0
- package/package.json +3 -3
- package/src/app/(loading)/Redirect.tsx +8 -0
- package/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx +42 -75
- package/src/app/(main)/market/_layout/Desktop/index.tsx +24 -20
- package/src/app/(main)/settings/llm/Azure/index.tsx +1 -0
- package/src/app/(main)/settings/llm/OpenAI/index.tsx +0 -1
- package/src/app/layout.tsx +3 -1
- package/src/app/trpc/edge/[trpc]/route.ts +1 -1
- package/src/components/Analytics/Clarity.tsx +28 -0
- package/src/components/Analytics/Google.tsx +2 -9
- package/src/components/Analytics/Plausible.tsx +7 -12
- package/src/components/Analytics/Posthog.tsx +9 -10
- package/src/components/Analytics/Umami.tsx +7 -6
- package/src/components/Analytics/Vercel.tsx +2 -4
- package/src/components/Analytics/index.tsx +29 -14
- package/src/components/FetchErrorNotification/Description.tsx +48 -0
- package/src/components/FetchErrorNotification/index.tsx +15 -0
- package/src/components/ModelProviderIcon/index.tsx +6 -1
- package/src/components/Notification/index.tsx +95 -0
- package/src/config/__tests__/analytics.test.ts +70 -0
- package/src/config/__tests__/client.test.ts +0 -6
- package/src/config/analytics.ts +148 -0
- package/src/config/client.ts +0 -35
- package/src/config/server/index.ts +1 -3
- package/src/config/server/provider.ts +1 -0
- package/src/database/client/models/__tests__/message.test.ts +2 -2
- package/src/database/client/models/message.ts +2 -2
- package/src/features/User/UserAvatar.tsx +4 -3
- package/src/locales/default/error.ts +3 -1
- package/src/middleware.ts +10 -1
- package/src/server/globalConfig/index.ts +2 -0
- package/src/server/routers/edge/config/__snapshots__/index.test.ts.snap +1 -0
- package/src/services/file/type.ts +2 -3
- package/src/services/message/client.test.ts +151 -23
- package/src/services/message/client.ts +9 -5
- package/src/services/message/type.ts +10 -3
- package/src/services/upload.ts +4 -4
- package/src/services/user/client.ts +17 -1
- package/src/services/user/type.ts +14 -0
- package/src/store/chat/slices/enchance/action.test.ts +4 -3
- package/src/store/chat/slices/enchance/action.ts +3 -2
- package/src/store/chat/slices/plugin/action.test.ts +3 -5
- package/src/store/chat/slices/plugin/action.ts +1 -1
- package/src/store/market/action.ts +8 -0
- package/src/store/user/slices/auth/selectors.ts +1 -1
- package/src/store/user/slices/preference/action.test.ts +4 -9
- package/src/store/user/slices/preference/action.ts +17 -20
- package/src/store/user/slices/settings/selectors/modelConfig.test.ts +29 -1
- package/src/store/user/slices/settings/selectors/modelConfig.ts +1 -1
- package/src/tools/dalle/Render/Item/Error.tsx +1 -1
- package/src/types/files.ts +33 -0
- package/src/config/server/analytics.ts +0 -32
- /package/src/types/{user.ts → user/index.ts} +0 -0
|
@@ -10,29 +10,33 @@ import Hero from './Hero';
|
|
|
10
10
|
|
|
11
11
|
const Layout = ({ children, detail }: LayoutProps) => {
|
|
12
12
|
return (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<Flexbox
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
13
|
+
<>
|
|
14
|
+
<Flexbox
|
|
15
|
+
height={'100%'}
|
|
16
|
+
id={'lobe-market-container'}
|
|
17
|
+
style={{ position: 'relative' }}
|
|
18
|
+
width={'100%'}
|
|
19
|
+
>
|
|
20
|
+
<Header />
|
|
21
|
+
<Flexbox height={'100%'} horizontal style={{ position: 'relative' }} width={'100%'}>
|
|
22
|
+
<Flexbox
|
|
23
|
+
align={'center'}
|
|
24
|
+
flex={1}
|
|
25
|
+
padding={16}
|
|
26
|
+
style={{ overflowX: 'hidden', overflowY: 'scroll', position: 'relative' }}
|
|
27
|
+
>
|
|
28
|
+
<SafeSpacing />
|
|
29
|
+
<Flexbox gap={16} style={{ maxWidth: MAX_WIDTH, position: 'relative', width: '100%' }}>
|
|
30
|
+
<Hero />
|
|
31
|
+
{children}
|
|
32
|
+
</Flexbox>
|
|
31
33
|
</Flexbox>
|
|
34
|
+
<DetailSidebar>{detail}</DetailSidebar>
|
|
32
35
|
</Flexbox>
|
|
33
|
-
<DetailSidebar>{detail}</DetailSidebar>
|
|
34
36
|
</Flexbox>
|
|
35
|
-
|
|
37
|
+
{/* ↓ cloud slot ↓ */}
|
|
38
|
+
{/* ↑ cloud slot ↑ */}
|
|
39
|
+
</>
|
|
36
40
|
);
|
|
37
41
|
};
|
|
38
42
|
|
|
@@ -95,6 +95,7 @@ const AzureOpenAIProvider = memo(() => {
|
|
|
95
95
|
placeholder: t('azure.modelListPlaceholder'),
|
|
96
96
|
}}
|
|
97
97
|
provider={providerKey}
|
|
98
|
+
showBrowserRequest
|
|
98
99
|
title={
|
|
99
100
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
100
101
|
<Azure.Combine size={22} type={'color'}></Azure.Combine>
|
package/src/app/layout.tsx
CHANGED
|
@@ -10,6 +10,8 @@ import AuthProvider from '@/layout/AuthProvider';
|
|
|
10
10
|
import GlobalProvider from '@/layout/GlobalProvider';
|
|
11
11
|
import { isMobileDevice } from '@/utils/responsive';
|
|
12
12
|
|
|
13
|
+
const inVercel = process.env.VERCEL === '1';
|
|
14
|
+
|
|
13
15
|
type RootLayoutProps = {
|
|
14
16
|
children: ReactNode;
|
|
15
17
|
modal: ReactNode;
|
|
@@ -31,7 +33,7 @@ const RootLayout = async ({ children, modal }: RootLayoutProps) => {
|
|
|
31
33
|
</AuthProvider>
|
|
32
34
|
</GlobalProvider>
|
|
33
35
|
<Analytics />
|
|
34
|
-
<SpeedInsights />
|
|
36
|
+
{inVercel && <SpeedInsights />}
|
|
35
37
|
</body>
|
|
36
38
|
</html>
|
|
37
39
|
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Script from 'next/script';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
|
|
6
|
+
interface ClarityProps {
|
|
7
|
+
projectId?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Clarity = memo<ClarityProps>(
|
|
11
|
+
({ projectId }) =>
|
|
12
|
+
projectId && (
|
|
13
|
+
<Script
|
|
14
|
+
dangerouslySetInnerHTML={{
|
|
15
|
+
__html: `
|
|
16
|
+
(function(c,l,a,r,i,t,y){
|
|
17
|
+
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
|
18
|
+
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
|
19
|
+
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
|
20
|
+
})(window, document, "clarity", "script", "${projectId}");
|
|
21
|
+
`,
|
|
22
|
+
}}
|
|
23
|
+
id="clarity-script"
|
|
24
|
+
/>
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export default Clarity;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { GoogleAnalytics as GA } from '@next/third-parties/google';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { analyticsEnv } from '@/config/analytics';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
if (ENABLE_GOOGLE_ANALYTICS && !GOOGLE_ANALYTICS_MEASUREMENT_ID)
|
|
8
|
-
throw new Error(
|
|
9
|
-
'You have enable the google analytics but not provided the google analytics id. Please provide the google analytics id in your env',
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
const GoogleAnalytics = () => <GA gaId={GOOGLE_ANALYTICS_MEASUREMENT_ID!} />;
|
|
5
|
+
const GoogleAnalytics = () => <GA gaId={analyticsEnv.GOOGLE_ANALYTICS_MEASUREMENT_ID!} />;
|
|
13
6
|
|
|
14
7
|
export default GoogleAnalytics;
|
|
@@ -3,19 +3,14 @@
|
|
|
3
3
|
import Script from 'next/script';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface PlausibleAnalyticsProps {
|
|
7
|
+
domain?: string;
|
|
8
|
+
scriptBaseUrl: string;
|
|
9
|
+
}
|
|
7
10
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
() =>
|
|
12
|
-
PLAUSIBLE_DOMAIN && (
|
|
13
|
-
<Script
|
|
14
|
-
data-domain={PLAUSIBLE_DOMAIN}
|
|
15
|
-
defer
|
|
16
|
-
src={`${PLAUSIBLE_SCRIPT_BASE_URL}/js/script.js`}
|
|
17
|
-
/>
|
|
18
|
-
),
|
|
11
|
+
const PlausibleAnalytics = memo<PlausibleAnalyticsProps>(
|
|
12
|
+
({ domain, scriptBaseUrl }) =>
|
|
13
|
+
domain && <Script data-domain={domain} defer src={`${scriptBaseUrl}/js/script.js`} />,
|
|
19
14
|
);
|
|
20
15
|
|
|
21
16
|
export default PlausibleAnalytics;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import posthog from 'posthog-js';
|
|
4
|
-
import {
|
|
4
|
+
import { memo, useEffect } from 'react';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface PostHogProps {
|
|
7
|
+
debug: boolean;
|
|
8
|
+
host: string;
|
|
9
|
+
token?: string;
|
|
10
|
+
}
|
|
7
11
|
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
const PostHog: FC = memo(() => {
|
|
12
|
+
const PostHog = memo<PostHogProps>(({ token, host, debug }) => {
|
|
11
13
|
useEffect(() => {
|
|
12
|
-
if (!
|
|
14
|
+
if (!token) return;
|
|
13
15
|
|
|
14
|
-
posthog.init(
|
|
15
|
-
api_host: POSTHOG_HOST ?? 'https://app.posthog.com',
|
|
16
|
-
debug: POSTHOG_DEBUG,
|
|
17
|
-
});
|
|
16
|
+
posthog.init(token, { api_host: host, debug });
|
|
18
17
|
}, []);
|
|
19
18
|
|
|
20
19
|
return null;
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import Script from 'next/script';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface UmamiAnalyticsProps {
|
|
7
|
+
scriptUrl: string;
|
|
8
|
+
websiteId?: string;
|
|
9
|
+
}
|
|
7
10
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
() =>
|
|
12
|
-
UMAMI_WEBSITE_ID && <Script data-website-id={UMAMI_WEBSITE_ID} defer src={UMAMI_SCRIPT_URL} />,
|
|
11
|
+
const UmamiAnalytics = memo<UmamiAnalyticsProps>(
|
|
12
|
+
({ scriptUrl, websiteId }) =>
|
|
13
|
+
websiteId && <Script data-website-id={websiteId} defer src={scriptUrl} />,
|
|
13
14
|
);
|
|
14
15
|
|
|
15
16
|
export default UmamiAnalytics;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Analytics } from '@vercel/analytics/react';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { analyticsEnv } from '@/config/analytics';
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const VercelAnalytics = memo(() => <Analytics debug={VERCEL_DEBUG} />);
|
|
6
|
+
const VercelAnalytics = memo(() => <Analytics debug={analyticsEnv.DEBUG_VERCEL_ANALYTICS} />);
|
|
9
7
|
|
|
10
8
|
export default VercelAnalytics;
|
|
@@ -1,27 +1,42 @@
|
|
|
1
1
|
import dynamic from 'next/dynamic';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { getServerConfig } from '@/config/server';
|
|
3
|
+
import { analyticsEnv } from '@/config/analytics';
|
|
5
4
|
|
|
6
5
|
import Google from './Google';
|
|
7
6
|
import Vercel from './Vercel';
|
|
8
7
|
|
|
9
|
-
const Plausible = dynamic(() => import('./Plausible')
|
|
10
|
-
const Posthog = dynamic(() => import('./Posthog')
|
|
11
|
-
const Umami = dynamic(() => import('./Umami')
|
|
12
|
-
|
|
13
|
-
const { ANALYTICS_POSTHOG, ANALYTICS_PLAUSIBLE, ANALYTICS_UMAMI } = getClientConfig();
|
|
14
|
-
|
|
15
|
-
const { ENABLE_VERCEL_ANALYTICS, ENABLE_GOOGLE_ANALYTICS } = getServerConfig();
|
|
8
|
+
const Plausible = dynamic(() => import('./Plausible'));
|
|
9
|
+
const Posthog = dynamic(() => import('./Posthog'));
|
|
10
|
+
const Umami = dynamic(() => import('./Umami'));
|
|
11
|
+
const Clarity = dynamic(() => import('./Clarity'));
|
|
16
12
|
|
|
17
13
|
const Analytics = () => {
|
|
18
14
|
return (
|
|
19
15
|
<>
|
|
20
|
-
{ENABLE_VERCEL_ANALYTICS && <Vercel />}
|
|
21
|
-
{ENABLE_GOOGLE_ANALYTICS && <Google />}
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
{analyticsEnv.ENABLE_VERCEL_ANALYTICS && <Vercel />}
|
|
17
|
+
{analyticsEnv.ENABLE_GOOGLE_ANALYTICS && <Google />}
|
|
18
|
+
{analyticsEnv.ENABLED_PLAUSIBLE_ANALYTICS && (
|
|
19
|
+
<Plausible
|
|
20
|
+
domain={analyticsEnv.PLAUSIBLE_DOMAIN}
|
|
21
|
+
scriptBaseUrl={analyticsEnv.PLAUSIBLE_SCRIPT_BASE_URL}
|
|
22
|
+
/>
|
|
23
|
+
)}
|
|
24
|
+
{analyticsEnv.ENABLED_POSTHOG_ANALYTICS && (
|
|
25
|
+
<Posthog
|
|
26
|
+
debug={analyticsEnv.DEBUG_POSTHOG_ANALYTICS}
|
|
27
|
+
host={analyticsEnv.POSTHOG_HOST!}
|
|
28
|
+
token={analyticsEnv.POSTHOG_KEY}
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
{analyticsEnv.ENABLED_UMAMI_ANALYTICS && (
|
|
32
|
+
<Umami
|
|
33
|
+
scriptUrl={analyticsEnv.UMAMI_SCRIPT_URL}
|
|
34
|
+
websiteId={analyticsEnv.UMAMI_WEBSITE_ID}
|
|
35
|
+
/>
|
|
36
|
+
)}
|
|
37
|
+
{analyticsEnv.ENABLED_CLARITY_ANALYTICS && (
|
|
38
|
+
<Clarity projectId={analyticsEnv.CLARITY_PROJECT_ID} />
|
|
39
|
+
)}
|
|
25
40
|
</>
|
|
26
41
|
);
|
|
27
42
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
|
2
|
+
import { Skeleton } from 'antd';
|
|
3
|
+
import { css, cx } from 'antd-style';
|
|
4
|
+
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
5
|
+
import dynamic from 'next/dynamic';
|
|
6
|
+
import { memo, useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
const container = css`
|
|
11
|
+
pre.shiki {
|
|
12
|
+
padding: 8px !important;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const Highlighter = dynamic(() => import('@lobehub/ui/es/Highlighter'), {
|
|
17
|
+
loading: () => <Skeleton avatar={false} title={false} />,
|
|
18
|
+
ssr: false,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const Description = memo<{ message: string; status: number }>(({ message, status }) => {
|
|
22
|
+
const { t } = useTranslation('error');
|
|
23
|
+
const [show, setShow] = useState(false);
|
|
24
|
+
return (
|
|
25
|
+
<Flexbox gap={8}>
|
|
26
|
+
{t(`response.${status}` as any)}
|
|
27
|
+
<Flexbox
|
|
28
|
+
gap={4}
|
|
29
|
+
horizontal
|
|
30
|
+
onClick={() => {
|
|
31
|
+
setShow(!show);
|
|
32
|
+
}}
|
|
33
|
+
style={{ cursor: 'pointer', fontSize: 12 }}
|
|
34
|
+
>
|
|
35
|
+
{t('fetchErrorDetail')} <Icon icon={show ? ChevronUp : ChevronDown} />
|
|
36
|
+
</Flexbox>
|
|
37
|
+
<Highlighter
|
|
38
|
+
className={cx(container)}
|
|
39
|
+
language={'text'}
|
|
40
|
+
style={{ display: show ? undefined : 'none', maxHeight: 80 }}
|
|
41
|
+
>
|
|
42
|
+
{message}
|
|
43
|
+
</Highlighter>
|
|
44
|
+
</Flexbox>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export default Description;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { t } from 'i18next';
|
|
2
|
+
|
|
3
|
+
import { notification } from '@/components/AntdStaticMethods';
|
|
4
|
+
|
|
5
|
+
import Description from './Description';
|
|
6
|
+
|
|
7
|
+
export const fetchErrorNotification = {
|
|
8
|
+
error: ({ status, errorMessage }: { errorMessage: string; status: number }) => {
|
|
9
|
+
notification.error({
|
|
10
|
+
description: <Description message={errorMessage} status={status} />,
|
|
11
|
+
message: t('fetchError', { ns: 'error' }),
|
|
12
|
+
type: 'error',
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
ZeroOne,
|
|
17
17
|
Zhipu,
|
|
18
18
|
} from '@lobehub/icons';
|
|
19
|
+
import { Logo } from '@lobehub/ui';
|
|
19
20
|
import { memo } from 'react';
|
|
20
21
|
import { Center } from 'react-layout-kit';
|
|
21
22
|
|
|
@@ -27,6 +28,10 @@ interface ModelProviderIconProps {
|
|
|
27
28
|
|
|
28
29
|
const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
|
|
29
30
|
switch (provider) {
|
|
31
|
+
case 'lobehub': {
|
|
32
|
+
return <Logo size={20} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
case ModelProvider.ZhiPu: {
|
|
31
36
|
return <Zhipu size={20} />;
|
|
32
37
|
}
|
|
@@ -38,7 +43,7 @@ const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
|
|
|
38
43
|
case ModelProvider.DeepSeek: {
|
|
39
44
|
return <DeepSeek size={20} />;
|
|
40
45
|
}
|
|
41
|
-
|
|
46
|
+
|
|
42
47
|
case ModelProvider.Google: {
|
|
43
48
|
return (
|
|
44
49
|
<Center height={20} width={20}>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import { XIcon } from 'lucide-react';
|
|
6
|
+
import { rgba } from 'polished';
|
|
7
|
+
import { memo } from 'react';
|
|
8
|
+
import { Flexbox, FlexboxProps } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
|
11
|
+
cancelIcon: css`
|
|
12
|
+
position: absolute;
|
|
13
|
+
z-index: 100;
|
|
14
|
+
top: 8px;
|
|
15
|
+
right: 8px;
|
|
16
|
+
`,
|
|
17
|
+
container: css`
|
|
18
|
+
position: absolute;
|
|
19
|
+
z-index: 1100;
|
|
20
|
+
bottom: 16px;
|
|
21
|
+
inset-inline-end: 20px;
|
|
22
|
+
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
|
|
25
|
+
background: ${token.colorBgContainer};
|
|
26
|
+
border: 1px solid ${token.colorSplit};
|
|
27
|
+
border-radius: 8px;
|
|
28
|
+
box-shadow: ${token.boxShadowSecondary};
|
|
29
|
+
`,
|
|
30
|
+
mobileContainer: css`
|
|
31
|
+
bottom: 8px;
|
|
32
|
+
inset-inline-start: 8px;
|
|
33
|
+
`,
|
|
34
|
+
wrapper: css`
|
|
35
|
+
background: linear-gradient(
|
|
36
|
+
180deg,
|
|
37
|
+
${rgba(token.colorBgContainer, 0)},
|
|
38
|
+
${token.colorBgContainer} ${isDarkMode ? '80' : '140'}px
|
|
39
|
+
),
|
|
40
|
+
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cg fill='${token.colorFillTertiary}' %3E %3Cpolygon fill-rule='evenodd' points='8 4 12 6 8 8 6 12 4 8 0 6 4 4 6 0 8 4'/%3E%3C/g%3E%3C/svg%3E");
|
|
41
|
+
`,
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
interface NotificationProps extends FlexboxProps {
|
|
45
|
+
height?: number | string;
|
|
46
|
+
mobile?: boolean;
|
|
47
|
+
onCancel?: (show?: boolean) => void;
|
|
48
|
+
show: boolean;
|
|
49
|
+
showCloseIcon?: boolean;
|
|
50
|
+
width?: number | string;
|
|
51
|
+
wrapper?: FlexboxProps;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const Notification = memo<NotificationProps>(
|
|
55
|
+
({
|
|
56
|
+
mobile,
|
|
57
|
+
children,
|
|
58
|
+
show,
|
|
59
|
+
onCancel,
|
|
60
|
+
showCloseIcon = true,
|
|
61
|
+
width = 422,
|
|
62
|
+
height = 'auto',
|
|
63
|
+
wrapper = {},
|
|
64
|
+
className,
|
|
65
|
+
...rest
|
|
66
|
+
}) => {
|
|
67
|
+
const { styles, cx } = useStyles();
|
|
68
|
+
const { className: wrapperClassName, ...restWrapper } = wrapper;
|
|
69
|
+
return (
|
|
70
|
+
show && (
|
|
71
|
+
<Flexbox
|
|
72
|
+
className={cx(styles.container, mobile && styles.mobileContainer, className)}
|
|
73
|
+
height={height}
|
|
74
|
+
width={mobile ? 'calc(100% - 16px)' : width}
|
|
75
|
+
{...rest}
|
|
76
|
+
>
|
|
77
|
+
{showCloseIcon && (
|
|
78
|
+
<ActionIcon className={styles.cancelIcon} icon={XIcon} onClick={() => onCancel?.()} />
|
|
79
|
+
)}
|
|
80
|
+
<Flexbox
|
|
81
|
+
className={cx(styles.wrapper, wrapperClassName)}
|
|
82
|
+
gap={16}
|
|
83
|
+
horizontal
|
|
84
|
+
padding={'20px 20px 16px'}
|
|
85
|
+
{...restWrapper}
|
|
86
|
+
>
|
|
87
|
+
{children}
|
|
88
|
+
</Flexbox>
|
|
89
|
+
</Flexbox>
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
export default Notification;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { analyticsEnv, getAnalyticsConfig } from '../analytics';
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// 在每个测试用例之前,清除所有的 console.warn mock
|
|
8
|
+
console.warn = vi.fn();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
// 在每个测试用例之后,恢复所有的环境变量
|
|
13
|
+
vi.resetModules();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('getAnalyticsConfig', () => {
|
|
17
|
+
it('should return the correct analytics config', () => {
|
|
18
|
+
// 设置环境变量
|
|
19
|
+
process.env.PLAUSIBLE_DOMAIN = 'example.com';
|
|
20
|
+
process.env.POSTHOG_KEY = 'posthog_key';
|
|
21
|
+
process.env.UMAMI_WEBSITE_ID = 'umami_id';
|
|
22
|
+
process.env.CLARITY_PROJECT_ID = 'clarity_id';
|
|
23
|
+
process.env.ENABLE_VERCEL_ANALYTICS = '1';
|
|
24
|
+
process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID = 'ga_id';
|
|
25
|
+
|
|
26
|
+
const config = getAnalyticsConfig();
|
|
27
|
+
|
|
28
|
+
expect(config).toEqual({
|
|
29
|
+
ENABLED_PLAUSIBLE_ANALYTICS: true,
|
|
30
|
+
PLAUSIBLE_DOMAIN: 'example.com',
|
|
31
|
+
PLAUSIBLE_SCRIPT_BASE_URL: 'https://plausible.io',
|
|
32
|
+
ENABLED_POSTHOG_ANALYTICS: true,
|
|
33
|
+
POSTHOG_KEY: 'posthog_key',
|
|
34
|
+
POSTHOG_HOST: 'https://app.posthog.com',
|
|
35
|
+
DEBUG_POSTHOG_ANALYTICS: false,
|
|
36
|
+
ENABLED_UMAMI_ANALYTICS: true,
|
|
37
|
+
UMAMI_SCRIPT_URL: 'https://analytics.umami.is/script.js',
|
|
38
|
+
UMAMI_WEBSITE_ID: 'umami_id',
|
|
39
|
+
ENABLED_CLARITY_ANALYTICS: true,
|
|
40
|
+
CLARITY_PROJECT_ID: 'clarity_id',
|
|
41
|
+
ENABLE_VERCEL_ANALYTICS: true,
|
|
42
|
+
DEBUG_VERCEL_ANALYTICS: false,
|
|
43
|
+
ENABLE_GOOGLE_ANALYTICS: true,
|
|
44
|
+
GOOGLE_ANALYTICS_MEASUREMENT_ID: 'ga_id',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should use deprecated env vars and log warnings', () => {
|
|
49
|
+
process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN = 'deprecated.com';
|
|
50
|
+
process.env.NEXT_PUBLIC_POSTHOG_KEY = 'deprecated_key';
|
|
51
|
+
process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID = 'deprecated_id';
|
|
52
|
+
|
|
53
|
+
const config = getAnalyticsConfig();
|
|
54
|
+
|
|
55
|
+
expect(config.ENABLED_PLAUSIBLE_ANALYTICS).toBeTruthy();
|
|
56
|
+
expect(config.ENABLED_POSTHOG_ANALYTICS).toBeTruthy();
|
|
57
|
+
expect(config.ENABLED_UMAMI_ANALYTICS).toBeTruthy();
|
|
58
|
+
|
|
59
|
+
expect(console.warn).toHaveBeenCalledTimes(3);
|
|
60
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
61
|
+
'NEXT_PUBLIC_PLAUSIBLE_DOMAIN is deprecated. Please use PLAUSIBLE_DOMAIN instead. We will remove it in LobeChat 1.0',
|
|
62
|
+
);
|
|
63
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
64
|
+
'NEXT_PUBLIC_POSTHOG_KEY is deprecated. Please use POSTHOG_KEY instead. We will remove it in LobeChat 1.0',
|
|
65
|
+
);
|
|
66
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
67
|
+
'NEXT_PUBLIC_UMAMI_WEBSITE_ID is deprecated. Please use UMAMI_WEBSITE_ID instead. We will remove it in LobeChat 1.0',
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -18,9 +18,6 @@ describe('getClientConfig', () => {
|
|
|
18
18
|
process.env.NEXT_PUBLIC_I18N_DEBUG_SERVER = '1';
|
|
19
19
|
|
|
20
20
|
const config = getClientConfig();
|
|
21
|
-
expect(config.ANALYTICS_PLAUSIBLE).toBe(true);
|
|
22
|
-
expect(config.ANALYTICS_POSTHOG).toBe(true);
|
|
23
|
-
expect(config.POSTHOG_DEBUG).toBe(true);
|
|
24
21
|
expect(config.I18N_DEBUG).toBe(true);
|
|
25
22
|
expect(config.I18N_DEBUG_BROWSER).toBe(true);
|
|
26
23
|
expect(config.I18N_DEBUG_SERVER).toBe(true);
|
|
@@ -36,9 +33,6 @@ describe('getClientConfig', () => {
|
|
|
36
33
|
|
|
37
34
|
const config = getClientConfig();
|
|
38
35
|
|
|
39
|
-
expect(config.ANALYTICS_PLAUSIBLE).toBe(false);
|
|
40
|
-
expect(config.ANALYTICS_POSTHOG).toBe(false);
|
|
41
|
-
expect(config.POSTHOG_DEBUG).toBe(false);
|
|
42
36
|
expect(config.I18N_DEBUG).toBe(false);
|
|
43
37
|
expect(config.I18N_DEBUG_BROWSER).toBe(false);
|
|
44
38
|
expect(config.I18N_DEBUG_SERVER).toBe(false);
|