Compare commits

..

9 Commits

Author SHA1 Message Date
YuTengjing d74daf6c68 📝 docs: move s3.mdx to s3/get-started.mdx 2026-01-22 11:07:11 +08:00
YuTengjing 87f2fd91b7 📝 docs: reorganize auth documentation structure
- Move auth.mdx to auth/get-started.mdx
- Rename better-auth/ to providers/
2026-01-22 10:54:15 +08:00
lobehubbot 093c24f119 📝 docs(bot): Auto sync agents & plugin to readme 2026-01-22 02:45:17 +00:00
semantic-release-bot bcf8628087 🔖 chore(release): v2.0.0-next.335 [skip ci]
## [Version 2.0.0-next.335](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.334...v2.0.0-next.335)
<sup>Released on **2026-01-22**</sup>

####  Features

- **database**: Added user memory activity.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **database**: Added user memory activity, closes [#11680](https://github.com/lobehub/lobe-chat/issues/11680) ([0160fbd](https://github.com/lobehub/lobe-chat/commit/0160fbd))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-01-22 02:43:33 +00:00
Neko 0160fbde83 feat(database): added user memory activity (#11680) 2026-01-22 10:24:50 +08:00
lobehubbot 12b1d56e33 📝 docs(bot): Auto sync agents & plugin to readme 2026-01-21 17:08:45 +00:00
semantic-release-bot 8e3d3dbb1b 🔖 chore(release): v2.0.0-next.334 [skip ci]
## [Version&nbsp;2.0.0-next.334](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.333...v2.0.0-next.334)
<sup>Released on **2026-01-21**</sup>

####  Features

- **misc**: Add platform-aware download client menu option.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add platform-aware download client menu option, closes [#11676](https://github.com/lobehub/lobe-chat/issues/11676) ([55abddc](https://github.com/lobehub/lobe-chat/commit/55abddc))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-01-21 17:07:04 +00:00
Innei 55abddc532 feat: add platform-aware download client menu option (#11676)
*  feat: add platform-aware download client menu option

* update test

Signed-off-by: Innei <tukon479@gmail.com>

---------

Signed-off-by: Innei <tukon479@gmail.com>
2026-01-22 00:49:03 +08:00
lobehubbot bae270e7da 📝 docs(bot): Auto sync agents & plugin to readme 2026-01-21 15:18:20 +00:00
71 changed files with 10940 additions and 24 deletions
+50
View File
@@ -2,6 +2,56 @@
# Changelog
## [Version 2.0.0-next.335](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.334...v2.0.0-next.335)
<sup>Released on **2026-01-22**</sup>
#### ✨ Features
- **database**: Added user memory activity.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's improved
- **database**: Added user memory activity, closes [#11680](https://github.com/lobehub/lobe-chat/issues/11680) ([0160fbd](https://github.com/lobehub/lobe-chat/commit/0160fbd))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
## [Version 2.0.0-next.334](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.333...v2.0.0-next.334)
<sup>Released on **2026-01-21**</sup>
#### ✨ Features
- **misc**: Add platform-aware download client menu option.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's improved
- **misc**: Add platform-aware download client menu option, closes [#11676](https://github.com/lobehub/lobe-chat/issues/11676) ([55abddc](https://github.com/lobehub/lobe-chat/commit/55abddc))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
## [Version 2.0.0-next.333](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.332...v2.0.0-next.333)
<sup>Released on **2026-01-21**</sup>
+20
View File
@@ -1,4 +1,24 @@
[
{
"children": {},
"date": "2026-01-22",
"version": "2.0.0-next.335"
},
{
"children": {
"features": ["Add platform-aware download client menu option."]
},
"date": "2026-01-21",
"version": "2.0.0-next.334"
},
{
"children": {
"features": ["Update the sandbox preinstall libs in sys role."],
"fixes": ["Fix multi tasks no summary issue."]
},
"date": "2026-01-21",
"version": "2.0.0-next.333"
},
{
"children": {
"fixes": ["Improve e2e server and complete i18n resources."]
+34
View File
@@ -1235,6 +1235,40 @@ table user_memories {
}
}
table user_memories_activities {
id varchar(255) [pk, not null]
user_id text
user_memory_id varchar(255)
metadata jsonb
tags text[]
type varchar(255) [not null]
status varchar(255) [not null, default: 'pending']
timezone varchar(255)
starts_at "timestamp with time zone"
ends_at "timestamp with time zone"
associated_objects jsonb
associated_subjects jsonb
associated_locations jsonb
notes text
narrative text
narrative_vector vector(1024)
feedback text
feedback_vector vector(1024)
captured_at "timestamp with time zone" [not null, default: `now()`]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
narrative_vector [name: 'user_memories_activities_narrative_vector_index']
feedback_vector [name: 'user_memories_activities_feedback_vector_index']
type [name: 'user_memories_activities_type_index']
user_id [name: 'user_memories_activities_user_id_index']
user_memory_id [name: 'user_memories_activities_user_memory_id_index']
status [name: 'user_memories_activities_status_index']
}
}
table user_memories_contexts {
id varchar(255) [pk, not null]
user_id text
+1
View File
@@ -179,6 +179,7 @@
"delete": "حذف",
"document": "دليل المستخدم",
"download": "تنزيل",
"downloadClient": "تنزيل العميل",
"duplicate": "تكرار",
"edit": "تعديل",
"errors.invalidFileFormat": "تنسيق الملف غير صالح",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Изтрий",
"document": "Ръководство за потребителя",
"download": "Изтегли",
"downloadClient": "Изтегли клиент",
"duplicate": "Дублирай",
"edit": "Редактирай",
"errors.invalidFileFormat": "Невалиден файлов формат",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Löschen",
"document": "Benutzerhandbuch",
"download": "Herunterladen",
"downloadClient": "Client herunterladen",
"duplicate": "Duplizieren",
"edit": "Bearbeiten",
"errors.invalidFileFormat": "Ungültiges Dateiformat",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Delete",
"document": "User Manual",
"download": "Download",
"downloadClient": "Download Client",
"duplicate": "Duplicate",
"edit": "Edit",
"errors.invalidFileFormat": "Invalid file format",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Eliminar",
"document": "Manual de usuario",
"download": "Descargar",
"downloadClient": "Descargar cliente",
"duplicate": "Duplicar",
"edit": "Editar",
"errors.invalidFileFormat": "Formato de archivo no válido",
+1
View File
@@ -179,6 +179,7 @@
"delete": "حذف",
"document": "راهنمای کاربر",
"download": "دانلود",
"downloadClient": "دانلود کلاینت",
"duplicate": "تکراری",
"edit": "ویرایش",
"errors.invalidFileFormat": "فرمت فایل نامعتبر است",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Supprimer",
"document": "Manuel utilisateur",
"download": "Télécharger",
"downloadClient": "Télécharger le client",
"duplicate": "Dupliquer",
"edit": "Modifier",
"errors.invalidFileFormat": "Format de fichier invalide",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Elimina",
"document": "Manuale utente",
"download": "Scarica",
"downloadClient": "Scarica client",
"duplicate": "Duplica",
"edit": "Modifica",
"errors.invalidFileFormat": "Formato file non valido",
+1
View File
@@ -179,6 +179,7 @@
"delete": "削除",
"document": "ドキュメント",
"download": "ダウンロード",
"downloadClient": "クライアントをダウンロード",
"duplicate": "コピーを作成",
"edit": "編集",
"errors.invalidFileFormat": "ファイルフォーマットエラー",
+1
View File
@@ -179,6 +179,7 @@
"delete": "삭제",
"document": "사용 설명서",
"download": "다운로드",
"downloadClient": "클라이언트 다운로드",
"duplicate": "복사본 만들기",
"edit": "편집",
"errors.invalidFileFormat": "파일 형식 오류",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Verwijderen",
"document": "Gebruikershandleiding",
"download": "Downloaden",
"downloadClient": "Client downloaden",
"duplicate": "Dupliceren",
"edit": "Bewerken",
"errors.invalidFileFormat": "Ongeldig bestandsformaat",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Usuń",
"document": "Instrukcja obsługi",
"download": "Pobierz",
"downloadClient": "Pobierz klienta",
"duplicate": "Duplikuj",
"edit": "Edytuj",
"errors.invalidFileFormat": "Nieprawidłowy format pliku",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Excluir",
"document": "Manual do Usuário",
"download": "Baixar",
"downloadClient": "Baixar cliente",
"duplicate": "Duplicar",
"edit": "Editar",
"errors.invalidFileFormat": "Formato de arquivo inválido",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Удалить",
"document": "Руководство пользователя",
"download": "Скачать",
"downloadClient": "Скачать клиент",
"duplicate": "Дублировать",
"edit": "Редактировать",
"errors.invalidFileFormat": "Недопустимый формат файла",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Sil",
"document": "Kullanıcı Kılavuzu",
"download": "İndir",
"downloadClient": "İstemciyi indir",
"duplicate": "Çoğalt",
"edit": "Düzenle",
"errors.invalidFileFormat": "Geçersiz dosya formatı",
+1
View File
@@ -179,6 +179,7 @@
"delete": "Xóa",
"document": "Hướng dẫn sử dụng",
"download": "Tải xuống",
"downloadClient": "Tải xuống client",
"duplicate": "Nhân bản",
"edit": "Chỉnh sửa",
"errors.invalidFileFormat": "Định dạng tệp không hợp lệ",
+1
View File
@@ -179,6 +179,7 @@
"delete": "删除",
"document": "使用文档",
"download": "下载",
"downloadClient": "下载客户端",
"duplicate": "创建副本",
"edit": "编辑",
"errors.invalidFileFormat": "文件格式错误",
+1
View File
@@ -179,6 +179,7 @@
"delete": "刪除",
"document": "使用說明文件",
"download": "下載",
"downloadClient": "下載客戶端",
"duplicate": "建立副本",
"edit": "編輯",
"errors.invalidFileFormat": "檔案格式錯誤",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lobehub/lobehub",
"version": "2.0.0-next.333",
"version": "2.0.0-next.335",
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
"keywords": [
"framework",
+6
View File
@@ -63,3 +63,9 @@ export const AES_GCM_URL = 'https://datatracker.ietf.org/doc/html/draft-ietf-avt
export const BASE_PROVIDER_DOC_URL = 'https://lobehub.com/docs/usage/providers';
export const SITEMAP_BASE_URL = isDev ? '/sitemap.xml/' : 'sitemap';
export const CHANGELOG_URL = urlJoin(OFFICIAL_SITE, 'changelog/versions');
export const DOWNLOAD_URL = {
android: 'https://play.google.com/store/apps/details?id=com.lobehub.app',
default: urlJoin(OFFICIAL_SITE, '/download'),
ios: 'https://testflight.apple.com/join/2ZbjX4Qp',
} as const;
@@ -0,0 +1,35 @@
CREATE TABLE IF NOT EXISTS "user_memories_activities" (
"id" varchar(255) PRIMARY KEY NOT NULL,
"user_id" text,
"user_memory_id" varchar(255),
"metadata" jsonb,
"tags" text[],
"type" varchar(255) NOT NULL,
"status" varchar(255) DEFAULT 'pending' NOT NULL,
"timezone" varchar(255),
"starts_at" timestamp with time zone,
"ends_at" timestamp with time zone,
"associated_objects" jsonb,
"associated_subjects" jsonb,
"associated_locations" jsonb,
"notes" text,
"narrative" text,
"narrative_vector" vector(1024),
"feedback" text,
"feedback_vector" vector(1024),
"captured_at" timestamp with time zone DEFAULT now() NOT NULL,
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "user_memories_activities" DROP CONSTRAINT IF EXISTS "user_memories_activities_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "user_memories_activities" ADD CONSTRAINT "user_memories_activities_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "user_memories_activities" DROP CONSTRAINT IF EXISTS "user_memories_activities_user_memory_id_user_memories_id_fk";--> statement-breakpoint
ALTER TABLE "user_memories_activities" ADD CONSTRAINT "user_memories_activities_user_memory_id_user_memories_id_fk" FOREIGN KEY ("user_memory_id") REFERENCES "public"."user_memories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_narrative_vector_index" ON "user_memories_activities" USING hnsw ("narrative_vector" vector_cosine_ops);--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_feedback_vector_index" ON "user_memories_activities" USING hnsw ("feedback_vector" vector_cosine_ops);--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_type_index" ON "user_memories_activities" USING btree ("type");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_user_id_index" ON "user_memories_activities" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_user_memory_id_index" ON "user_memories_activities" USING btree ("user_memory_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_memories_activities_status_index" ON "user_memories_activities" USING btree ("status");
File diff suppressed because it is too large Load Diff
@@ -490,7 +490,14 @@
"when": 1768303764632,
"tag": "0069_add_topic_shares_table",
"breakpoints": true
},
{
"idx": 70,
"version": "7",
"when": 1768999498635,
"tag": "0070_add_user_memory_activities",
"breakpoints": true
}
],
"version": "6"
}
}
@@ -124,6 +124,70 @@ export const userMemoriesPreferences = pgTable(
],
);
export const userMemoriesActivities = pgTable(
'user_memories_activities',
{
id: varchar255('id')
.$defaultFn(() => idGenerator('memory'))
.primaryKey(),
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
userMemoryId: varchar255('user_memory_id').references(() => userMemories.id, {
onDelete: 'cascade',
}),
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
tags: text('tags').array(),
type: varchar255('type').notNull(),
status: varchar255('status').notNull().default('pending'),
timezone: varchar255('timezone'),
startsAt: timestamptz('starts_at'),
endsAt: timestamptz('ends_at'),
associatedObjects: jsonb('associated_objects').$type<{
extra?: Record<string, unknown>,
name?: string,
type?: string
}[]>(),
associatedSubjects: jsonb('associated_subjects').$type<{
extra?: Record<string, unknown>,
name?: string,
type?: string
}[]>(),
associatedLocations: jsonb('associated_locations').$type<{
address?: string;
name?: string;
tags?: string[];
type?: string;
}[]>(),
notes: text('notes'),
narrative: text('narrative'),
narrativeVector: vector('narrative_vector', { dimensions: 1024 }),
feedback: text('feedback'),
feedbackVector: vector('feedback_vector', { dimensions: 1024 }),
capturedAt: timestamptz('captured_at').notNull().defaultNow(),
...timestamps,
},
(table) => [
index('user_memories_activities_narrative_vector_index').using(
'hnsw',
table.narrativeVector.op('vector_cosine_ops'),
),
index('user_memories_activities_feedback_vector_index').using(
'hnsw',
table.feedbackVector.op('vector_cosine_ops'),
),
index('user_memories_activities_type_index').on(table.type),
index('user_memories_activities_user_id_index').on(table.userId),
index('user_memories_activities_user_memory_id_index').on(table.userMemoryId),
index('user_memories_activities_status_index').on(table.status),
],
);
export const userMemoriesIdentities = pgTable(
'user_memories_identities',
{
@@ -242,3 +306,10 @@ export type UserMemoryExperiencesWithoutVectors = Omit<
'situationVector' | 'actionVector' | 'keyLearningVector'
>;
export type NewUserMemoryExperience = typeof userMemoriesExperiences.$inferInsert;
export type UserMemoryActivity = typeof userMemoriesActivities.$inferSelect;
export type UserMemoryActivitiesWithoutVectors = Omit<
UserMemoryActivity,
'narrativeVector' | 'feedbackVector'
>;
export type NewUserMemoryActivity = typeof userMemoriesActivities.$inferInsert;
@@ -1,5 +1,5 @@
import { UTM_SOURCE , LOBE_CHAT_CLOUD } from '@lobechat/business-const';
import { OFFICIAL_URL } from '@lobechat/const';
import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
import { DOWNLOAD_URL, OFFICIAL_URL } from '@lobechat/const';
import {
Book,
CircleUserRound,
@@ -9,22 +9,29 @@ import {
FileClockIcon,
Settings2,
} from 'lucide-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { type CellProps } from '@/components/Cell';
import { DOCUMENTS, FEEDBACK } from '@/const/index';
import { usePWAInstall } from '@/hooks/usePWAInstall';
import { usePlatform } from '@/hooks/usePlatform';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { useUserStore } from '@/store/user';
import { authSelectors } from '@/store/user/selectors';
export const useCategory = (onOpenChangelogModal: () => void) => {
const navigate = useNavigate();
const { canInstall, install } = usePWAInstall();
const { t } = useTranslation(['common', 'setting', 'auth']);
const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
const [isLoginWithAuth] = useUserStore((s) => [authSelectors.isLoginWithAuth(s)]);
const { isIOS, isAndroid } = usePlatform();
const downloadUrl = useMemo(() => {
if (isIOS) return DOWNLOAD_URL.ios;
if (isAndroid) return DOWNLOAD_URL.android;
return DOWNLOAD_URL.default;
}, [isIOS, isAndroid]);
const profile: CellProps[] = [
{
@@ -47,12 +54,12 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
},
];
const pwa: CellProps[] = [
const downloadClient: CellProps[] = [
{
icon: Download,
key: 'pwa',
label: t('installPWA'),
onClick: () => install(),
key: 'download-client',
label: t('downloadClient'),
onClick: () => window.open(downloadUrl, '__blank'),
},
{
type: 'divider',
@@ -96,7 +103,7 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
/* ↓ cloud slot ↓ */
/* ↑ cloud slot ↑ */
...(canInstall ? pwa : []),
...downloadClient,
...(!hideDocs ? helps : []),
].filter(Boolean) as CellProps[];
-4
View File
@@ -9,7 +9,6 @@ import BusinessGlobalProvider from '@/business/client/BusinessGlobalProvider';
import Analytics from '@/components/Analytics';
import { DEFAULT_LANG } from '@/const/locale';
import { isDesktop } from '@/const/version';
import PWAInstall from '@/features/PWAInstall';
import AuthProvider from '@/layout/AuthProvider';
import GlobalProvider from '@/layout/GlobalProvider';
import { type Locales } from '@/locales/resources';
@@ -40,9 +39,6 @@ const RootLayout = async ({ children, params }: RootLayoutProps) => {
variants={variants}
>
<AuthProvider>{children}</AuthProvider>
<Suspense fallback={null}>
<PWAInstall />
</Suspense>
</GlobalProvider>
);
};
+18 -9
View File
@@ -1,9 +1,9 @@
import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
import { isDesktop } from '@lobechat/const';
import { DOWNLOAD_URL, isDesktop } from '@lobechat/const';
import { Flexbox, Hotkey, Icon, Tag } from '@lobehub/ui';
import { type ItemType } from 'antd/es/menu/interface';
import { Cloudy, Download, HardDriveDownload, LogOut, Settings2 } from 'lucide-react';
import { type PropsWithChildren, memo } from 'react';
import { type PropsWithChildren, memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
@@ -12,7 +12,7 @@ import type { MenuProps } from '@/components/Menu';
import { DEFAULT_DESKTOP_HOTKEY_CONFIG } from '@/const/desktop';
import { OFFICIAL_URL } from '@/const/url';
import DataImporter from '@/features/DataImporter';
import { usePWAInstall } from '@/hooks/usePWAInstall';
import { usePlatform } from '@/hooks/usePlatform';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { useUserStore } from '@/store/user';
import { authSelectors } from '@/store/user/selectors';
@@ -44,7 +44,6 @@ const NewVersionBadge = memo(
);
export const useMenu = () => {
const { canInstall, install } = usePWAInstall();
const hasNewVersion = useNewVersion();
const { t } = useTranslation(['common', 'setting', 'auth']);
const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
@@ -53,6 +52,13 @@ export const useMenu = () => {
authSelectors.isLoginWithAuth(s),
]);
const businessMenuItems = useBusinessMenuItems(isLogin);
const { isIOS, isAndroid } = usePlatform();
const downloadUrl = useMemo(() => {
if (isIOS) return DOWNLOAD_URL.ios;
if (isAndroid) return DOWNLOAD_URL.android;
return DOWNLOAD_URL.default;
}, [isIOS, isAndroid]);
const settings: MenuProps['items'] = [
{
@@ -71,12 +77,15 @@ export const useMenu = () => {
},
];
const pwa: MenuProps['items'] = [
const downloadClient: MenuProps['items'] = [
{
icon: <Icon icon={Download} />,
key: 'pwa',
label: t('installPWA'),
onClick: () => install(),
key: 'download-client',
label: (
<a href={downloadUrl} rel="noopener noreferrer" target="_blank">
{t('downloadClient')}
</a>
),
},
{
type: 'divider',
@@ -119,7 +128,7 @@ export const useMenu = () => {
...(isLogin ? settings : []),
...businessMenuItems,
...(canInstall ? pwa : []),
...(!isDesktop ? downloadClient : []),
...data,
...(!hideDocs ? helps : []),
].filter(Boolean) as MenuProps['items'];
+5
View File
@@ -25,6 +25,7 @@ describe('usePlatform', () => {
const { result } = renderHook(() => usePlatform());
expect(result.current).toEqual({
isAndroid: false,
isApple: true,
isChrome: true,
isChromium: true,
@@ -50,6 +51,7 @@ describe('usePlatform', () => {
const { result } = renderHook(() => usePlatform());
expect(result.current).toEqual({
isAndroid: false,
isApple: true,
isChrome: false,
isChromium: false,
@@ -75,6 +77,7 @@ describe('usePlatform', () => {
const { result } = renderHook(() => usePlatform());
expect(result.current).toEqual({
isAndroid: false,
isApple: false,
isChrome: false,
isChromium: true,
@@ -100,6 +103,7 @@ describe('usePlatform', () => {
const { result } = renderHook(() => usePlatform());
expect(result.current).toEqual({
isAndroid: false,
isApple: false,
isChrome: false,
isChromium: false,
@@ -125,6 +129,7 @@ describe('usePlatform', () => {
const { result } = renderHook(() => usePlatform());
expect(result.current).toEqual({
isAndroid: false,
isApple: true,
isChrome: true,
isChromium: true,
+1
View File
@@ -13,6 +13,7 @@ export const usePlatform = () => {
const browser = useRef(getBrowser());
const platformInfo = {
isAndroid: platform.current?.toLowerCase() === 'android',
isApple: platform.current && ['mac os', 'ios'].includes(platform.current?.toLowerCase()),
isArc: isArc(),
isChrome: browser.current?.toLowerCase() === 'chrome',
+1
View File
@@ -193,6 +193,7 @@ export default {
'delete': 'Delete',
'document': 'User Manual',
'download': 'Download',
'downloadClient': 'Download Client',
'duplicate': 'Duplicate',
'edit': 'Edit',
'errors.invalidFileFormat': 'Invalid file format',