@lobehub/chat 0.158.0 → 0.158.2

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.
Files changed (121) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/common.json +1 -0
  3. package/locales/ar/metadata.json +18 -0
  4. package/locales/ar/plugin.json +5 -6
  5. package/locales/ar/tool.json +2 -1
  6. package/locales/bg-BG/common.json +1 -0
  7. package/locales/bg-BG/metadata.json +18 -0
  8. package/locales/bg-BG/plugin.json +5 -6
  9. package/locales/bg-BG/tool.json +2 -1
  10. package/locales/de-DE/common.json +1 -0
  11. package/locales/de-DE/metadata.json +18 -0
  12. package/locales/de-DE/plugin.json +5 -6
  13. package/locales/de-DE/tool.json +2 -1
  14. package/locales/en-US/common.json +2 -1
  15. package/locales/en-US/metadata.json +18 -0
  16. package/locales/en-US/plugin.json +5 -6
  17. package/locales/en-US/tool.json +2 -1
  18. package/locales/es-ES/common.json +1 -0
  19. package/locales/es-ES/metadata.json +18 -0
  20. package/locales/es-ES/plugin.json +5 -6
  21. package/locales/es-ES/tool.json +2 -1
  22. package/locales/fr-FR/common.json +1 -0
  23. package/locales/fr-FR/metadata.json +18 -0
  24. package/locales/fr-FR/plugin.json +5 -6
  25. package/locales/fr-FR/tool.json +2 -1
  26. package/locales/it-IT/common.json +1 -0
  27. package/locales/it-IT/metadata.json +18 -0
  28. package/locales/it-IT/plugin.json +5 -6
  29. package/locales/it-IT/tool.json +2 -1
  30. package/locales/ja-JP/common.json +1 -0
  31. package/locales/ja-JP/metadata.json +18 -0
  32. package/locales/ja-JP/plugin.json +5 -6
  33. package/locales/ja-JP/tool.json +2 -1
  34. package/locales/ko-KR/common.json +1 -0
  35. package/locales/ko-KR/metadata.json +18 -0
  36. package/locales/ko-KR/plugin.json +5 -6
  37. package/locales/ko-KR/tool.json +2 -1
  38. package/locales/nl-NL/common.json +1 -0
  39. package/locales/nl-NL/metadata.json +18 -0
  40. package/locales/nl-NL/plugin.json +63 -64
  41. package/locales/nl-NL/tool.json +2 -1
  42. package/locales/pl-PL/common.json +1 -0
  43. package/locales/pl-PL/metadata.json +18 -0
  44. package/locales/pl-PL/plugin.json +98 -99
  45. package/locales/pl-PL/tool.json +2 -1
  46. package/locales/pt-BR/common.json +1 -0
  47. package/locales/pt-BR/metadata.json +18 -0
  48. package/locales/pt-BR/plugin.json +5 -6
  49. package/locales/pt-BR/tool.json +2 -1
  50. package/locales/ru-RU/common.json +1 -0
  51. package/locales/ru-RU/metadata.json +18 -0
  52. package/locales/ru-RU/plugin.json +5 -6
  53. package/locales/ru-RU/tool.json +2 -1
  54. package/locales/tr-TR/common.json +1 -0
  55. package/locales/tr-TR/metadata.json +18 -0
  56. package/locales/tr-TR/plugin.json +5 -6
  57. package/locales/tr-TR/tool.json +2 -1
  58. package/locales/vi-VN/common.json +1 -0
  59. package/locales/vi-VN/metadata.json +18 -0
  60. package/locales/vi-VN/plugin.json +5 -6
  61. package/locales/vi-VN/tool.json +2 -1
  62. package/locales/zh-CN/common.json +1 -0
  63. package/locales/zh-CN/metadata.json +18 -0
  64. package/locales/zh-CN/plugin.json +5 -6
  65. package/locales/zh-CN/tool.json +2 -1
  66. package/locales/zh-TW/common.json +1 -0
  67. package/locales/zh-TW/metadata.json +18 -0
  68. package/locales/zh-TW/plugin.json +5 -6
  69. package/locales/zh-TW/tool.json +2 -1
  70. package/package.json +2 -1
  71. package/public/manifest.json +76 -16
  72. package/public/og/cover.png +0 -0
  73. package/public/screenshots/shot-1.desktop.png +0 -0
  74. package/public/screenshots/shot-1.mobile.png +0 -0
  75. package/public/screenshots/shot-2.desktop.png +0 -0
  76. package/public/screenshots/shot-2.mobile.png +0 -0
  77. package/public/screenshots/shot-3.desktop.png +0 -0
  78. package/public/screenshots/shot-3.mobile.png +0 -0
  79. package/public/screenshots/shot-4.desktop.png +0 -0
  80. package/public/screenshots/shot-4.mobile.png +0 -0
  81. package/public/screenshots/shot-5.desktop.png +0 -0
  82. package/public/screenshots/shot-5.mobile.png +0 -0
  83. package/src/app/(auth)/login/[[...login]]/page.tsx +14 -7
  84. package/src/app/(auth)/signup/[[...signup]]/page.tsx +14 -7
  85. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +16 -1
  86. package/src/app/(main)/(mobile)/me/(home)/page.tsx +10 -0
  87. package/src/app/(main)/(mobile)/me/data/page.tsx +10 -0
  88. package/src/app/(main)/(mobile)/me/profile/page.tsx +11 -0
  89. package/src/app/(main)/(mobile)/me/settings/page.tsx +11 -0
  90. package/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx +1 -1
  91. package/src/app/(main)/chat/(workspace)/page.tsx +21 -1
  92. package/src/app/(main)/market/page.tsx +17 -8
  93. package/src/app/(main)/profile/[[...slugs]]/page.tsx +7 -4
  94. package/src/app/(main)/settings/about/page.tsx +5 -2
  95. package/src/app/(main)/settings/agent/page.tsx +5 -3
  96. package/src/app/(main)/settings/common/page.tsx +5 -3
  97. package/src/app/(main)/settings/llm/page.tsx +5 -2
  98. package/src/app/(main)/settings/sync/page.tsx +5 -3
  99. package/src/app/(main)/settings/tts/page.tsx +5 -3
  100. package/src/app/(main)/welcome/page.tsx +19 -6
  101. package/src/app/layout.tsx +1 -1
  102. package/src/app/metadata.ts +44 -54
  103. package/src/app/page.tsx +4 -4
  104. package/src/components/StructuredData/index.tsx +12 -0
  105. package/src/features/User/UserPanel/useMenu.tsx +16 -0
  106. package/src/hooks/usePWAInstall.ts +18 -0
  107. package/src/locales/default/common.ts +2 -1
  108. package/src/locales/default/index.ts +2 -0
  109. package/src/locales/default/metadata.ts +20 -0
  110. package/src/server/ld.ts +218 -0
  111. package/src/server/metadata.ts +96 -0
  112. package/src/server/translation.ts +11 -1
  113. package/src/utils/genOG.ts +20 -0
  114. package/public/screenshots/screenshot-1.png +0 -0
  115. package/public/screenshots/screenshot-2.png +0 -0
  116. package/public/screenshots/screenshot-3.png +0 -0
  117. package/public/screenshots/screenshot-4.png +0 -0
  118. package/src/app/(auth)/login/[[...login]]/PageTitle.tsx +0 -13
  119. package/src/app/(auth)/signup/[[...signup]]/PageTitle.tsx +0 -13
  120. /package/public/icons/{maskable-icon-192x192.png → icon-192x192.maskable.png} +0 -0
  121. /package/public/icons/{maskable-icon-512x512.png → icon-512x512.maskable.png} +0 -0
@@ -2,11 +2,21 @@ import { redirect } from 'next/navigation';
2
2
  import { Center } from 'react-layout-kit';
3
3
 
4
4
  import BrandWatermark from '@/components/BrandWatermark';
5
+ import { metadataModule } from '@/server/metadata';
6
+ import { translation } from '@/server/translation';
5
7
  import { isMobileDevice } from '@/utils/responsive';
6
8
 
7
9
  import Category from './features/Category';
8
10
  import UserBanner from './features/UserBanner';
9
11
 
12
+ export const generateMetadata = async () => {
13
+ const { t } = await translation('common');
14
+ return metadataModule.generate({
15
+ title: t('tab.me'),
16
+ url: '/me',
17
+ });
18
+ };
19
+
10
20
  const Page = () => {
11
21
  const mobile = isMobileDevice();
12
22
 
@@ -1,9 +1,19 @@
1
1
  import { redirect } from 'next/navigation';
2
2
 
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
3
5
  import { isMobileDevice } from '@/utils/responsive';
4
6
 
5
7
  import Category from './features/Category';
6
8
 
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('common');
11
+ return metadataModule.generate({
12
+ title: t('userPanel.data'),
13
+ url: '/me/data',
14
+ });
15
+ };
16
+
7
17
  const Page = () => {
8
18
  const mobile = isMobileDevice();
9
19
 
@@ -1,9 +1,20 @@
1
1
  import { redirect } from 'next/navigation';
2
2
 
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
3
5
  import { isMobileDevice } from '@/utils/responsive';
4
6
 
5
7
  import Category from './features/Category';
6
8
 
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('clerk');
11
+ return metadataModule.generate({
12
+ description: t('userProfile.navbar.title'),
13
+ title: t('userProfile.navbar.description'),
14
+ url: '/me/profile',
15
+ });
16
+ };
17
+
7
18
  const Page = () => {
8
19
  const mobile = isMobileDevice();
9
20
 
@@ -1,9 +1,20 @@
1
1
  import { redirect } from 'next/navigation';
2
2
 
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
3
5
  import { isMobileDevice } from '@/utils/responsive';
4
6
 
5
7
  import Category from './features/Category';
6
8
 
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('setting');
11
+ return metadataModule.generate({
12
+ description: t('header.desc'),
13
+ title: t('header.title'),
14
+ url: '/me/settings',
15
+ });
16
+ };
17
+
7
18
  const Page = () => {
8
19
  const mobile = isMobileDevice();
9
20
 
@@ -19,7 +19,7 @@ import { preferenceSelectors } from '@/store/user/selectors';
19
19
  const useStyles = createStyles(({ css, token, isDarkMode }) => ({
20
20
  container: css`
21
21
  position: absolute;
22
- z-index: 100;
22
+ z-index: 1100;
23
23
  bottom: 16px;
24
24
  inset-inline-end: 20px;
25
25
 
@@ -1,13 +1,33 @@
1
+ import StructuredData from '@/components/StructuredData';
2
+ import { ldModule } from '@/server/ld';
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
1
5
  import { isMobileDevice } from '@/utils/responsive';
2
6
 
3
7
  import PageTitle from '../features/PageTitle';
4
8
  import TelemetryNotification from './features/TelemetryNotification';
5
9
 
6
- const Page = () => {
10
+ export const generateMetadata = async () => {
11
+ const { t } = await translation('metadata');
12
+ return metadataModule.generate({
13
+ description: t('chat.description'),
14
+ title: t('chat.title'),
15
+ url: '/chat',
16
+ });
17
+ };
18
+
19
+ const Page = async () => {
7
20
  const mobile = isMobileDevice();
21
+ const { t } = await translation('metadata');
22
+ const ld = ldModule.generate({
23
+ description: t('chat.description'),
24
+ title: t('chat.title'),
25
+ url: '/chat',
26
+ });
8
27
 
9
28
  return (
10
29
  <>
30
+ <StructuredData ld={ld} />
11
31
  <PageTitle />
12
32
  <TelemetryNotification mobile={mobile} />
13
33
  </>
@@ -1,6 +1,8 @@
1
1
  import { Flexbox } from 'react-layout-kit';
2
2
 
3
- import { getCanonicalUrl } from '@/const/url';
3
+ import StructuredData from '@/components/StructuredData';
4
+ import { ldModule } from '@/server/ld';
5
+ import { metadataModule } from '@/server/metadata';
4
6
  import { translation } from '@/server/translation';
5
7
  import { isMobileDevice } from '@/utils/responsive';
6
8
 
@@ -9,18 +11,25 @@ import AgentSearchBar from './features/AgentSearchBar';
9
11
  import TagList from './features/TagList';
10
12
 
11
13
  export const generateMetadata = async () => {
12
- const { t } = await translation('common');
13
- return {
14
- alternates: { canonical: getCanonicalUrl('/market') },
15
- title: t('tab.market'),
16
- };
14
+ const { t } = await translation('metadata');
15
+ return metadataModule.generate({
16
+ description: t('market.description'),
17
+ title: t('market.title'),
18
+ url: '/market',
19
+ });
17
20
  };
18
21
 
19
- const Page = () => {
22
+ const Page = async () => {
20
23
  const mobile = isMobileDevice();
21
-
24
+ const { t } = await translation('metadata');
25
+ const ld = ldModule.generate({
26
+ description: t('market.description'),
27
+ title: t('market.title'),
28
+ url: '/market',
29
+ });
22
30
  return (
23
31
  <>
32
+ <StructuredData ld={ld} />
24
33
  <AgentSearchBar mobile={mobile} />
25
34
  <Flexbox gap={mobile ? 16 : 24}>
26
35
  <TagList />
@@ -1,13 +1,16 @@
1
+ import { metadataModule } from '@/server/metadata';
1
2
  import { translation } from '@/server/translation';
2
3
  import { isMobileDevice } from '@/utils/responsive';
3
4
 
4
5
  import Client from './Client';
5
6
 
6
7
  export const generateMetadata = async () => {
7
- const { t } = await translation('common');
8
- return {
9
- title: t('userButton.profile'),
10
- };
8
+ const { t } = await translation('clerk');
9
+ return metadataModule.generate({
10
+ description: t('userProfile.navbar.title'),
11
+ title: t('userProfile.navbar.description'),
12
+ url: '/profile',
13
+ });
11
14
  };
12
15
 
13
16
  const Page = () => {
@@ -1,3 +1,4 @@
1
+ import { metadataModule } from '@/server/metadata';
1
2
  import { translation } from '@/server/translation';
2
3
  import { isMobileDevice } from '@/utils/responsive';
3
4
 
@@ -5,9 +6,11 @@ import Page from './index';
5
6
 
6
7
  export const generateMetadata = async () => {
7
8
  const { t } = await translation('setting');
8
- return {
9
+ return metadataModule.generate({
10
+ description: t('header.desc'),
9
11
  title: t('tab.about'),
10
- };
12
+ url: '/settings/about',
13
+ });
11
14
  };
12
15
 
13
16
  export default () => {
@@ -1,10 +1,12 @@
1
+ import { metadataModule } from '@/server/metadata';
1
2
  import { translation } from '@/server/translation';
2
3
 
3
4
  export const generateMetadata = async () => {
4
5
  const { t } = await translation('setting');
5
- return {
6
+ return metadataModule.generate({
7
+ description: t('header.desc'),
6
8
  title: t('tab.agent'),
7
- };
9
+ url: '/settings/agent',
10
+ });
8
11
  };
9
-
10
12
  export { default } from './index';
@@ -1,10 +1,12 @@
1
+ import { metadataModule } from '@/server/metadata';
1
2
  import { translation } from '@/server/translation';
2
3
 
3
4
  export const generateMetadata = async () => {
4
5
  const { t } = await translation('setting');
5
- return {
6
+ return metadataModule.generate({
7
+ description: t('header.desc'),
6
8
  title: t('tab.common'),
7
- };
9
+ url: '/settings/common',
10
+ });
8
11
  };
9
-
10
12
  export { default } from './index';
@@ -1,15 +1,18 @@
1
1
  import { notFound } from 'next/navigation';
2
2
 
3
3
  import { serverFeatureFlags } from '@/config/server/featureFlags';
4
+ import { metadataModule } from '@/server/metadata';
4
5
  import { translation } from '@/server/translation';
5
6
 
6
7
  import Page from './index';
7
8
 
8
9
  export const generateMetadata = async () => {
9
10
  const { t } = await translation('setting');
10
- return {
11
+ return metadataModule.generate({
12
+ description: t('header.desc'),
11
13
  title: t('tab.llm'),
12
- };
14
+ url: '/settings/llm',
15
+ });
13
16
  };
14
17
 
15
18
  export default () => {
@@ -1,6 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
 
3
3
  import { serverFeatureFlags } from '@/config/server/featureFlags';
4
+ import { metadataModule } from '@/server/metadata';
4
5
  import { translation } from '@/server/translation';
5
6
  import { gerServerDeviceInfo, isMobileDevice } from '@/utils/responsive';
6
7
 
@@ -8,11 +9,12 @@ import Page from './index';
8
9
 
9
10
  export const generateMetadata = async () => {
10
11
  const { t } = await translation('setting');
11
- return {
12
+ return metadataModule.generate({
13
+ description: t('header.desc'),
12
14
  title: t('tab.sync'),
13
- };
15
+ url: '/settings/sync',
16
+ });
14
17
  };
15
-
16
18
  export default () => {
17
19
  const enableWebrtc = serverFeatureFlags().enableWebrtc;
18
20
  if (!enableWebrtc) return notFound();
@@ -1,10 +1,12 @@
1
+ import { metadataModule } from '@/server/metadata';
1
2
  import { translation } from '@/server/translation';
2
3
 
3
4
  export const generateMetadata = async () => {
4
5
  const { t } = await translation('setting');
5
- return {
6
+ return metadataModule.generate({
7
+ description: t('header.desc'),
6
8
  title: t('tab.tts'),
7
- };
9
+ url: '/settings/tts',
10
+ });
8
11
  };
9
-
10
12
  export { default } from './index';
@@ -1,21 +1,34 @@
1
- import { Metadata } from 'next';
2
-
3
- import { getCanonicalUrl } from '@/const/url';
1
+ import StructuredData from '@/components/StructuredData';
2
+ import { ldModule } from '@/server/ld';
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
4
5
  import { isMobileDevice } from '@/utils/responsive';
5
6
 
6
7
  import Actions from './features/Actions';
7
8
  import Hero from './features/Hero';
8
9
  import Logo from './features/Logo';
9
10
 
10
- export const metadata: Metadata = {
11
- alternates: { canonical: getCanonicalUrl('/welcome') },
11
+ export const generateMetadata = async () => {
12
+ const { t } = await translation('metadata');
13
+ return metadataModule.generate({
14
+ description: t('welcome.description'),
15
+ title: t('welcome.title'),
16
+ url: '/welcome',
17
+ });
12
18
  };
13
19
 
14
- const Page = () => {
20
+ const Page = async () => {
15
21
  const mobile = isMobileDevice();
22
+ const { t } = await translation('metadata');
23
+ const ld = ldModule.generate({
24
+ description: t('welcome.description'),
25
+ title: t('welcome.title'),
26
+ url: '/welcome',
27
+ });
16
28
 
17
29
  return (
18
30
  <>
31
+ <StructuredData ld={ld} />
19
32
  <Logo mobile={mobile} />
20
33
  <Hero />
21
34
  <Actions mobile={mobile} />
@@ -39,7 +39,7 @@ const RootLayout = async ({ children, modal }: RootLayoutProps) => {
39
39
 
40
40
  export default RootLayout;
41
41
 
42
- export { default as metadata } from './metadata';
42
+ export { generateMetadata } from './metadata';
43
43
 
44
44
  export const generateViewport = async (): ResolvingViewport => {
45
45
  const isMobile = isMobileDevice();
@@ -3,11 +3,9 @@ import { Metadata } from 'next';
3
3
  import { getClientConfig } from '@/config/client';
4
4
  import { getServerConfig } from '@/config/server';
5
5
  import { OFFICIAL_URL } from '@/const/url';
6
-
7
- import pkg from '../../package.json';
6
+ import { translation } from '@/server/translation';
8
7
 
9
8
  const title = 'LobeChat';
10
- const { description, homepage } = pkg;
11
9
 
12
10
  const { SITE_URL = OFFICIAL_URL } = getServerConfig();
13
11
  const { BASE_PATH } = getClientConfig();
@@ -15,55 +13,47 @@ const { BASE_PATH } = getClientConfig();
15
13
  // if there is a base path, then we don't need the manifest
16
14
  const noManifest = !!BASE_PATH;
17
15
 
18
- const metadata: Metadata = {
19
- appleWebApp: {
20
- statusBarStyle: 'black-translucent',
21
- title,
22
- },
23
- description,
24
- icons: {
25
- apple: '/icons/apple-touch-icon.png',
26
- icon: '/favicon.ico',
27
- shortcut: '/favicon-32x32.ico',
28
- },
29
- manifest: noManifest ? undefined : '/manifest.json',
30
- metadataBase: new URL(SITE_URL),
31
- openGraph: {
32
- description: description,
33
- images: [
34
- {
35
- alt: title,
36
- height: 360,
37
- url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-480x270.png',
38
- width: 480,
39
- },
40
- {
41
- alt: title,
42
- height: 720,
43
- url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-960x540.png',
44
- width: 960,
45
- },
46
- ],
47
- locale: 'en-US',
48
- siteName: title,
49
- title: title,
50
- type: 'website',
51
- url: homepage,
52
- },
53
-
54
- title: {
55
- default: title,
56
- template: '%s · LobeChat',
57
- },
58
- twitter: {
59
- card: 'summary_large_image',
60
- description,
61
- images: [
62
- 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-960x540.png',
63
- ],
64
- site: '@lobehub',
65
- title,
66
- },
16
+ export const generateMetadata = async (): Promise<Metadata> => {
17
+ const { t } = await translation('metadata');
18
+ return {
19
+ appleWebApp: {
20
+ statusBarStyle: 'black-translucent',
21
+ title,
22
+ },
23
+ description: t('chat.description'),
24
+ icons: {
25
+ apple: '/icons/apple-touch-icon.png',
26
+ icon: '/favicon.ico',
27
+ shortcut: '/favicon-32x32.ico',
28
+ },
29
+ manifest: noManifest ? undefined : '/manifest.json',
30
+ metadataBase: new URL(SITE_URL),
31
+ openGraph: {
32
+ description: t('chat.description'),
33
+ images: [
34
+ {
35
+ alt: t('chat.title'),
36
+ height: 640,
37
+ url: '/og/cover.png',
38
+ width: 1200,
39
+ },
40
+ ],
41
+ locale: 'en-US',
42
+ siteName: title,
43
+ title: title,
44
+ type: 'website',
45
+ url: OFFICIAL_URL,
46
+ },
47
+ title: {
48
+ default: t('chat.title'),
49
+ template: '%s · LobeChat',
50
+ },
51
+ twitter: {
52
+ card: 'summary_large_image',
53
+ description: t('chat.description'),
54
+ images: ['/og/cover.png'],
55
+ site: '@lobehub',
56
+ title: t('chat.title'),
57
+ },
58
+ };
67
59
  };
68
-
69
- export default metadata;
package/src/app/page.tsx CHANGED
@@ -5,6 +5,10 @@ import { getCanonicalUrl } from '@/const/url';
5
5
  import Client from './(loading)/Client';
6
6
  import Redirect from './(loading)/Redirect';
7
7
 
8
+ export const metadata: Metadata = {
9
+ alternates: { canonical: getCanonicalUrl('/') },
10
+ };
11
+
8
12
  const Page = () => {
9
13
  return (
10
14
  <>
@@ -17,7 +21,3 @@ const Page = () => {
17
21
  Page.displayName = 'Loading';
18
22
 
19
23
  export default Page;
20
-
21
- export const metadata: Metadata = {
22
- alternates: { canonical: getCanonicalUrl('/') },
23
- };
@@ -0,0 +1,12 @@
1
+ import { FC } from 'react';
2
+
3
+ const StructuredData: FC<{ ld: any }> = ({ ld }) => {
4
+ return (
5
+ <script
6
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }}
7
+ id="structured-data"
8
+ type="application/ld+json"
9
+ />
10
+ );
11
+ };
12
+ export default StructuredData;
@@ -3,6 +3,7 @@ import { Badge } from 'antd';
3
3
  import {
4
4
  Book,
5
5
  CircleUserRound,
6
+ Download,
6
7
  Feather,
7
8
  HardDriveDownload,
8
9
  HardDriveUpload,
@@ -22,6 +23,7 @@ import type { MenuProps } from '@/components/Menu';
22
23
  import { DISCORD, DOCUMENTS, EMAIL_SUPPORT, GITHUB_ISSUES } from '@/const/url';
23
24
  import DataImporter from '@/features/DataImporter';
24
25
  import { useOpenSettings } from '@/hooks/useInterceptingRoutes';
26
+ import { usePWAInstall } from '@/hooks/usePWAInstall';
25
27
  import { useQueryRoute } from '@/hooks/useQueryRoute';
26
28
  import { configService } from '@/services/config';
27
29
  import { SettingsTabs } from '@/store/global/initialState';
@@ -54,6 +56,7 @@ const NewVersionBadge = memo(
54
56
 
55
57
  export const useMenu = () => {
56
58
  const router = useQueryRoute();
59
+ const { canInstall, install } = usePWAInstall();
57
60
  const hasNewVersion = useNewVersion();
58
61
  const openSettings = useOpenSettings();
59
62
  const { t } = useTranslation(['common', 'setting', 'auth']);
@@ -96,6 +99,18 @@ export const useMenu = () => {
96
99
  },
97
100
  ];
98
101
 
102
+ const pwa: MenuProps['items'] = [
103
+ {
104
+ icon: <Icon icon={Download} />,
105
+ key: 'pwa',
106
+ label: t('installPWA'),
107
+ onClick: () => install(),
108
+ },
109
+ {
110
+ type: 'divider',
111
+ },
112
+ ];
113
+
99
114
  const data: MenuProps['items'] = [
100
115
  {
101
116
  icon: <Icon icon={HardDriveUpload} />,
@@ -192,6 +207,7 @@ export const useMenu = () => {
192
207
  },
193
208
  ...(isLoginWithClerk ? profile : []),
194
209
  ...(isLogin ? settings : []),
210
+ ...(canInstall ? pwa : []),
195
211
  ...(isLogin ? data : []),
196
212
  ...helps,
197
213
  ].filter(Boolean) as MenuProps['items'];
@@ -0,0 +1,18 @@
1
+ import { pwaInstallHandler } from 'pwa-install-handler';
2
+ import { useEffect, useState } from 'react';
3
+
4
+ export const usePWAInstall = () => {
5
+ const [canInstall, setCanInstall] = useState(false);
6
+
7
+ useEffect(() => {
8
+ pwaInstallHandler.addListener(setCanInstall);
9
+ return () => {
10
+ pwaInstallHandler.removeListener(setCanInstall);
11
+ };
12
+ }, []);
13
+
14
+ return {
15
+ canInstall,
16
+ install: pwaInstallHandler.install,
17
+ };
18
+ };
@@ -59,6 +59,7 @@ export default {
59
59
  },
60
60
  title: '导入数据',
61
61
  },
62
+ installPWA: '安装浏览器应用 (PWA)',
62
63
  lang: {
63
64
  'ar': '阿拉伯语',
64
65
  'bg-BG': '保加利亚语',
@@ -138,13 +139,13 @@ export default {
138
139
  title: '同步状态',
139
140
  unconnected: { tip: '信令服务器连接失败,将无法建立点对点通信频道,请检查网络后重试' },
140
141
  },
142
+
141
143
  tab: {
142
144
  chat: '会话',
143
145
  market: '发现',
144
146
  me: '我',
145
147
  setting: '设置',
146
148
  },
147
-
148
149
  telemetry: {
149
150
  allow: '允许',
150
151
  deny: '拒绝',
@@ -6,6 +6,7 @@ import common from './common';
6
6
  import components from './components';
7
7
  import error from './error';
8
8
  import market from './market';
9
+ import metadata from './metadata';
9
10
  import migration from './migration';
10
11
  import modelProvider from './modelProvider';
11
12
  import plugin from './plugin';
@@ -20,6 +21,7 @@ const resources = {
20
21
  components,
21
22
  error,
22
23
  market,
24
+ metadata,
23
25
  migration,
24
26
  modelProvider,
25
27
  plugin,
@@ -0,0 +1,20 @@
1
+ export default {
2
+ chat: {
3
+ description: 'LobeChat 带给你最好的 ChatGPT, OLLaMA, Gemini, Claude WebUI 使用体验',
4
+ title: 'LobeChat:个人 LLM 效能工具,给自己一个更聪明的大脑',
5
+ },
6
+ market: {
7
+ description:
8
+ '内容创作、文案、问答、图像生成、视频生成、语音生成、智能 Agent、自动化工作流,定制你专属的 AI / GPTs / OLLaMA 智能助手',
9
+ title: '助手市场',
10
+ },
11
+ plugins: {
12
+ description:
13
+ '搜素、图表生成、学术、图像生成、视频生成、语音生成、自动化工作流,定制 ChatGPT / OLLaMA 专属的 ToolCall 插件能力',
14
+ title: '插件市场',
15
+ },
16
+ welcome: {
17
+ description: 'LobeChat 带给你最好的 ChatGPT, OLLaMA, Gemini, Claude WebUI 使用体验',
18
+ title: '欢迎使用 LobeChat:个人 LLM 效能工具,给自己一个更聪明的大脑',
19
+ },
20
+ };