@lobehub/chat 1.27.3 → 1.28.1

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 (81) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/chat.json +7 -1
  3. package/locales/ar/models.json +3 -0
  4. package/locales/bg-BG/chat.json +7 -1
  5. package/locales/bg-BG/models.json +3 -0
  6. package/locales/de-DE/chat.json +7 -1
  7. package/locales/de-DE/models.json +3 -0
  8. package/locales/en-US/chat.json +7 -1
  9. package/locales/en-US/models.json +3 -0
  10. package/locales/es-ES/chat.json +7 -1
  11. package/locales/es-ES/models.json +3 -0
  12. package/locales/fa-IR/chat.json +7 -1
  13. package/locales/fa-IR/models.json +3 -0
  14. package/locales/fr-FR/chat.json +7 -1
  15. package/locales/fr-FR/models.json +3 -0
  16. package/locales/it-IT/chat.json +7 -1
  17. package/locales/it-IT/models.json +3 -0
  18. package/locales/ja-JP/chat.json +7 -1
  19. package/locales/ja-JP/models.json +3 -0
  20. package/locales/ko-KR/chat.json +7 -1
  21. package/locales/ko-KR/models.json +3 -0
  22. package/locales/nl-NL/chat.json +7 -1
  23. package/locales/nl-NL/models.json +3 -0
  24. package/locales/pl-PL/chat.json +7 -1
  25. package/locales/pl-PL/models.json +3 -0
  26. package/locales/pt-BR/chat.json +7 -1
  27. package/locales/pt-BR/models.json +3 -0
  28. package/locales/ru-RU/chat.json +7 -1
  29. package/locales/ru-RU/models.json +3 -0
  30. package/locales/tr-TR/chat.json +7 -1
  31. package/locales/tr-TR/models.json +3 -0
  32. package/locales/vi-VN/chat.json +7 -1
  33. package/locales/vi-VN/models.json +3 -0
  34. package/locales/zh-CN/chat.json +7 -1
  35. package/locales/zh-CN/models.json +3 -0
  36. package/locales/zh-TW/chat.json +7 -1
  37. package/locales/zh-TW/models.json +3 -0
  38. package/package.json +1 -1
  39. package/src/app/(main)/chat/(workspace)/features/ShareButton/index.tsx +2 -1
  40. package/src/database/server/migrations/0010_add_accessed_at_and_clean_tables.sql +26 -0
  41. package/src/database/server/migrations/meta/0010_snapshot.json +3184 -0
  42. package/src/database/server/migrations/meta/_journal.json +7 -0
  43. package/src/database/server/models/__tests__/session.test.ts +0 -2
  44. package/src/database/server/models/__tests__/topic.test.ts +2 -0
  45. package/src/database/server/schemas/lobechat/_helpers.ts +8 -0
  46. package/src/database/server/schemas/lobechat/agent.ts +6 -7
  47. package/src/database/server/schemas/lobechat/asyncTask.ts +2 -3
  48. package/src/database/server/schemas/lobechat/file.ts +5 -5
  49. package/src/database/server/schemas/lobechat/index.ts +0 -1
  50. package/src/database/server/schemas/lobechat/message.ts +2 -3
  51. package/src/database/server/schemas/lobechat/rag.ts +4 -6
  52. package/src/database/server/schemas/lobechat/ragEvals.ts +5 -7
  53. package/src/database/server/schemas/lobechat/relations.ts +0 -33
  54. package/src/database/server/schemas/lobechat/session.ts +3 -5
  55. package/src/database/server/schemas/lobechat/topic.ts +7 -8
  56. package/src/database/server/schemas/lobechat/user.ts +3 -5
  57. package/src/{app/(main)/chat/(workspace)/features/ShareButton → features/ShareModal/ShareImage}/Preview.tsx +5 -3
  58. package/src/features/ShareModal/ShareImage/index.tsx +103 -0
  59. package/src/{app/(main)/chat/(workspace)/features/ShareButton → features/ShareModal/ShareImage}/style.ts +1 -23
  60. package/src/features/ShareModal/ShareJSON/Preview.tsx +18 -0
  61. package/src/features/ShareModal/ShareJSON/generateMessages.test.ts +135 -0
  62. package/src/features/ShareModal/ShareJSON/generateMessages.ts +35 -0
  63. package/src/features/ShareModal/ShareJSON/index.tsx +99 -0
  64. package/src/features/ShareModal/ShareJSON/type.ts +4 -0
  65. package/src/features/ShareModal/ShareText/Preview.tsx +16 -0
  66. package/src/features/ShareModal/ShareText/index.tsx +119 -0
  67. package/src/features/ShareModal/ShareText/template.test.ts +178 -0
  68. package/src/features/ShareModal/ShareText/template.ts +79 -0
  69. package/src/features/ShareModal/ShareText/type.ts +6 -0
  70. package/src/features/ShareModal/index.tsx +69 -0
  71. package/src/features/ShareModal/style.ts +30 -0
  72. package/src/locales/default/chat.ts +7 -1
  73. package/src/services/__tests__/share.test.ts +35 -105
  74. package/src/services/share.ts +0 -30
  75. package/src/store/chat/slices/share/action.test.ts +7 -198
  76. package/src/store/chat/slices/share/action.ts +9 -113
  77. package/src/utils/client/exportFile.ts +20 -0
  78. package/src/app/(main)/chat/(workspace)/features/ShareButton/ShareModal.tsx +0 -164
  79. package/src/database/server/schemas/lobechat/discover.ts +0 -84
  80. /package/src/{app/(main)/chat/(workspace)/features/ShareButton → features/ShareModal/ShareImage}/type.ts +0 -0
  81. /package/src/{app/(main)/chat/(workspace)/features/ShareButton → features/ShareModal/ShareImage}/useScreenshot.ts +0 -0
@@ -70,6 +70,13 @@
70
70
  "when": 1729699958471,
71
71
  "tag": "0009_remove_unused_user_tables",
72
72
  "breakpoints": true
73
+ },
74
+ {
75
+ "idx": 10,
76
+ "version": "7",
77
+ "when": 1730900133049,
78
+ "tag": "0010_add_accessed_at_and_clean_tables",
79
+ "breakpoints": true
73
80
  }
74
81
  ],
75
82
  "version": "6"
@@ -9,7 +9,6 @@ import {
9
9
  agents,
10
10
  agentsToSessions,
11
11
  messages,
12
- plugins,
13
12
  sessionGroups,
14
13
  sessions,
15
14
  topics,
@@ -30,7 +29,6 @@ const userId = 'session-user';
30
29
  const sessionModel = new SessionModel(userId);
31
30
 
32
31
  beforeEach(async () => {
33
- await serverDB.delete(plugins);
34
32
  await serverDB.delete(users);
35
33
  // 并创建初始用户
36
34
  await serverDB.insert(users).values({ id: userId });
@@ -430,6 +430,7 @@ describe('TopicModel', () => {
430
430
  clientId: null,
431
431
  createdAt: expect.any(Date),
432
432
  updatedAt: expect.any(Date),
433
+ accessedAt: expect.any(Date),
433
434
  });
434
435
 
435
436
  // 断言 topic 已在数据库中创建
@@ -476,6 +477,7 @@ describe('TopicModel', () => {
476
477
  userId,
477
478
  createdAt: expect.any(Date),
478
479
  updatedAt: expect.any(Date),
480
+ accessedAt: expect.any(Date),
479
481
  });
480
482
 
481
483
  // 断言 topic 已在数据库中创建
@@ -4,3 +4,11 @@ export const timestamptz = (name: string) => timestamp(name, { withTimezone: tru
4
4
 
5
5
  export const createdAt = () => timestamptz('created_at').notNull().defaultNow();
6
6
  export const updatedAt = () => timestamptz('updated_at').notNull().defaultNow();
7
+ export const accessedAt = () => timestamptz('accessed_at').notNull().defaultNow();
8
+
9
+ // columns.helpers.ts
10
+ export const timestamps = {
11
+ accessedAt: accessedAt(),
12
+ createdAt: createdAt(),
13
+ updatedAt: updatedAt(),
14
+ };
@@ -5,7 +5,7 @@ import { createInsertSchema } from 'drizzle-zod';
5
5
  import { LobeAgentChatConfig, LobeAgentTTSConfig } from '@/types/agent';
6
6
 
7
7
  import { idGenerator, randomSlug } from '../../utils/idGenerator';
8
- import { createdAt, updatedAt } from './_helpers';
8
+ import { timestamps } from './_helpers';
9
9
  import { files, knowledgeBases } from './file';
10
10
  import { users } from './user';
11
11
 
@@ -41,8 +41,7 @@ export const agents = pgTable('agents', {
41
41
  systemRole: text('system_role'),
42
42
  tts: jsonb('tts').$type<LobeAgentTTSConfig>(),
43
43
 
44
- createdAt: createdAt(),
45
- updatedAt: updatedAt(),
44
+ ...timestamps,
46
45
  });
47
46
 
48
47
  export const insertAgentSchema = createInsertSchema(agents);
@@ -63,8 +62,8 @@ export const agentsKnowledgeBases = pgTable(
63
62
  .references(() => users.id, { onDelete: 'cascade' })
64
63
  .notNull(),
65
64
  enabled: boolean('enabled').default(true),
66
- createdAt: createdAt(),
67
- updatedAt: updatedAt(),
65
+
66
+ ...timestamps,
68
67
  },
69
68
  (t) => ({
70
69
  pk: primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
@@ -84,8 +83,8 @@ export const agentsFiles = pgTable(
84
83
  userId: text('user_id')
85
84
  .references(() => users.id, { onDelete: 'cascade' })
86
85
  .notNull(),
87
- createdAt: createdAt(),
88
- updatedAt: updatedAt(),
86
+
87
+ ...timestamps,
89
88
  },
90
89
  (t) => ({
91
90
  pk: primaryKey({ columns: [t.fileId, t.agentId, t.userId] }),
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
2
  import { integer, jsonb, pgTable, text, uuid } from 'drizzle-orm/pg-core';
3
3
 
4
- import { createdAt, updatedAt } from './_helpers';
4
+ import { timestamps } from './_helpers';
5
5
  import { users } from './user';
6
6
 
7
7
  export const asyncTasks = pgTable('async_tasks', {
@@ -16,8 +16,7 @@ export const asyncTasks = pgTable('async_tasks', {
16
16
  .notNull(),
17
17
  duration: integer('duration'),
18
18
 
19
- createdAt: createdAt(),
20
- updatedAt: updatedAt(),
19
+ ...timestamps,
21
20
  });
22
21
 
23
22
  export type NewAsyncTaskItem = typeof asyncTasks.$inferInsert;
@@ -12,7 +12,7 @@ import {
12
12
  import { createInsertSchema } from 'drizzle-zod';
13
13
 
14
14
  import { idGenerator } from '../../utils/idGenerator';
15
- import { createdAt, updatedAt } from './_helpers';
15
+ import { accessedAt, createdAt, timestamps } from './_helpers';
16
16
  import { asyncTasks } from './asyncTask';
17
17
  import { chunks } from './rag';
18
18
  import { users } from './user';
@@ -23,7 +23,9 @@ export const globalFiles = pgTable('global_files', {
23
23
  size: integer('size').notNull(),
24
24
  url: text('url').notNull(),
25
25
  metadata: jsonb('metadata'),
26
+
26
27
  createdAt: createdAt(),
28
+ accessedAt: accessedAt(),
27
29
  });
28
30
 
29
31
  export type NewGlobalFile = typeof globalFiles.$inferInsert;
@@ -51,8 +53,7 @@ export const files = pgTable('files', {
51
53
  onDelete: 'set null',
52
54
  }),
53
55
 
54
- createdAt: createdAt(),
55
- updatedAt: updatedAt(),
56
+ ...timestamps,
56
57
  });
57
58
 
58
59
  export type NewFile = typeof files.$inferInsert;
@@ -91,8 +92,7 @@ export const knowledgeBases = pgTable('knowledge_bases', {
91
92
 
92
93
  settings: jsonb('settings'),
93
94
 
94
- createdAt: createdAt(),
95
- updatedAt: updatedAt(),
95
+ ...timestamps,
96
96
  });
97
97
 
98
98
  export const insertKnowledgeBasesSchema = createInsertSchema(knowledgeBases);
@@ -1,6 +1,5 @@
1
1
  export * from './agent';
2
2
  export * from './asyncTask';
3
- export * from './discover';
4
3
  export * from './file';
5
4
  export * from './message';
6
5
  export * from './nextauth';
@@ -13,7 +13,7 @@ import {
13
13
  import { createSelectSchema } from 'drizzle-zod';
14
14
 
15
15
  import { idGenerator } from '../../utils/idGenerator';
16
- import { createdAt, updatedAt } from './_helpers';
16
+ import { timestamps } from './_helpers';
17
17
  import { agents } from './agent';
18
18
  import { files } from './file';
19
19
  import { chunks, embeddings } from './rag';
@@ -58,8 +58,7 @@ export const messages = pgTable(
58
58
  // used for group chat
59
59
  agentId: text('agent_id').references(() => agents.id, { onDelete: 'set null' }),
60
60
 
61
- createdAt: createdAt(),
62
- updatedAt: updatedAt(),
61
+ ...timestamps,
63
62
  },
64
63
  (table) => ({
65
64
  createdAtIdx: index('messages_created_at_idx').on(table.createdAt),
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
2
  import { integer, jsonb, pgTable, text, uuid, varchar, vector } from 'drizzle-orm/pg-core';
3
3
 
4
- import { createdAt, updatedAt } from './_helpers';
4
+ import { timestamps } from './_helpers';
5
5
  import { files } from './file';
6
6
  import { users } from './user';
7
7
 
@@ -13,10 +13,9 @@ export const chunks = pgTable('chunks', {
13
13
  index: integer('index'),
14
14
  type: varchar('type'),
15
15
 
16
- createdAt: createdAt(),
17
- updatedAt: updatedAt(),
18
-
19
16
  userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
17
+
18
+ ...timestamps,
20
19
  });
21
20
 
22
21
  export type NewChunkItem = typeof chunks.$inferInsert & { fileId?: string };
@@ -28,8 +27,7 @@ export const unstructuredChunks = pgTable('unstructured_chunks', {
28
27
  index: integer('index'),
29
28
  type: varchar('type'),
30
29
 
31
- createdAt: createdAt(),
32
- updatedAt: updatedAt(),
30
+ ...timestamps,
33
31
 
34
32
  parentId: varchar('parent_id'),
35
33
  compositeId: uuid('composite_id').references(() => chunks.id, { onDelete: 'cascade' }),
@@ -4,7 +4,7 @@ import { integer, jsonb, pgTable, text, uuid } from 'drizzle-orm/pg-core';
4
4
  import { DEFAULT_EMBEDDING_MODEL, DEFAULT_MODEL } from '@/const/settings';
5
5
  import { EvalEvaluationStatus } from '@/types/eval';
6
6
 
7
- import { createdAt, updatedAt } from './_helpers';
7
+ import { timestamps } from './_helpers';
8
8
  import { knowledgeBases } from './file';
9
9
  import { embeddings } from './rag';
10
10
  import { users } from './user';
@@ -20,8 +20,7 @@ export const evalDatasets = pgTable('rag_eval_datasets', {
20
20
  }),
21
21
  userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
22
22
 
23
- updatedAt: updatedAt(),
24
- createdAt: createdAt(),
23
+ ...timestamps,
25
24
  });
26
25
 
27
26
  export type NewEvalDatasetsItem = typeof evalDatasets.$inferInsert;
@@ -39,7 +38,7 @@ export const evalDatasetRecords = pgTable('rag_eval_dataset_records', {
39
38
  metadata: jsonb('metadata'),
40
39
 
41
40
  userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
42
- createdAt: createdAt(),
41
+ ...timestamps,
43
42
  });
44
43
 
45
44
  export type NewEvalDatasetRecordsItem = typeof evalDatasetRecords.$inferInsert;
@@ -64,8 +63,7 @@ export const evalEvaluation = pgTable('rag_eval_evaluations', {
64
63
  embeddingModel: text('embedding_model').$defaultFn(() => DEFAULT_EMBEDDING_MODEL),
65
64
 
66
65
  userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
67
- createdAt: createdAt(),
68
- updatedAt: updatedAt(),
66
+ ...timestamps,
69
67
  });
70
68
 
71
69
  export type NewEvalEvaluationItem = typeof evalEvaluation.$inferInsert;
@@ -98,7 +96,7 @@ export const evaluationRecords = pgTable('rag_eval_evaluation_records', {
98
96
  .notNull(),
99
97
 
100
98
  userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
101
- createdAt: createdAt(),
99
+ ...timestamps,
102
100
  });
103
101
 
104
102
  export type NewEvaluationRecordsItem = typeof evaluationRecords.$inferInsert;
@@ -4,7 +4,6 @@ import { pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
4
4
 
5
5
  import { agents, agentsFiles, agentsKnowledgeBases } from './agent';
6
6
  import { asyncTasks } from './asyncTask';
7
- import { agentsTags, plugins, pluginsTags, tags } from './discover';
8
7
  import { files, knowledgeBases } from './file';
9
8
  import { messages, messagesFiles } from './message';
10
9
  import { unstructuredChunks } from './rag';
@@ -48,26 +47,6 @@ export const topicRelations = relations(topics, ({ one }) => ({
48
47
  }),
49
48
  }));
50
49
 
51
- export const pluginsRelations = relations(plugins, ({ many }) => ({
52
- pluginsTags: many(pluginsTags),
53
- }));
54
-
55
- export const pluginsTagsRelations = relations(pluginsTags, ({ one }) => ({
56
- plugin: one(plugins, {
57
- fields: [pluginsTags.pluginId],
58
- references: [plugins.id],
59
- }),
60
- tag: one(tags, {
61
- fields: [pluginsTags.tagId],
62
- references: [tags.id],
63
- }),
64
- }));
65
-
66
- export const tagsRelations = relations(tags, ({ many }) => ({
67
- agentsTags: many(agentsTags),
68
- pluginsTags: many(pluginsTags),
69
- }));
70
-
71
50
  export const messagesRelations = relations(messages, ({ many, one }) => ({
72
51
  filesToMessages: many(messagesFiles),
73
52
 
@@ -91,7 +70,6 @@ export const agentsRelations = relations(agents, ({ many }) => ({
91
70
  agentsToSessions: many(agentsToSessions),
92
71
  knowledgeBases: many(agentsKnowledgeBases),
93
72
  files: many(agentsFiles),
94
- agentsTags: many(agentsTags),
95
73
  }));
96
74
 
97
75
  export const agentsToSessionsRelations = relations(agentsToSessions, ({ one }) => ({
@@ -116,17 +94,6 @@ export const agentsKnowledgeBasesRelations = relations(agentsKnowledgeBases, ({
116
94
  }),
117
95
  }));
118
96
 
119
- export const agentsTagsRelations = relations(agentsTags, ({ one }) => ({
120
- agent: one(agents, {
121
- fields: [agentsTags.agentId],
122
- references: [agents.id],
123
- }),
124
- tag: one(tags, {
125
- fields: [agentsTags.tagId],
126
- references: [tags.id],
127
- }),
128
- }));
129
-
130
97
  export const sessionsRelations = relations(sessions, ({ many, one }) => ({
131
98
  filesToSessions: many(filesToSessions),
132
99
  agentsToSessions: many(agentsToSessions),
@@ -3,7 +3,7 @@ import { boolean, integer, pgTable, text, unique, uniqueIndex, varchar } from 'd
3
3
  import { createInsertSchema } from 'drizzle-zod';
4
4
 
5
5
  import { idGenerator, randomSlug } from '../../utils/idGenerator';
6
- import { createdAt, updatedAt } from './_helpers';
6
+ import { timestamps } from './_helpers';
7
7
  import { users } from './user';
8
8
 
9
9
  // ======= sessionGroups ======= //
@@ -22,8 +22,7 @@ export const sessionGroups = pgTable(
22
22
  .notNull(),
23
23
 
24
24
  clientId: text('client_id'),
25
- createdAt: createdAt(),
26
- updatedAt: updatedAt(),
25
+ ...timestamps,
27
26
  },
28
27
  (table) => ({
29
28
  clientIdUnique: unique('session_group_client_id_user_unique').on(table.clientId, table.userId),
@@ -60,8 +59,7 @@ export const sessions = pgTable(
60
59
  clientId: text('client_id'),
61
60
  pinned: boolean('pinned').default(false),
62
61
 
63
- createdAt: createdAt(),
64
- updatedAt: updatedAt(),
62
+ ...timestamps,
65
63
  },
66
64
  (t) => ({
67
65
  slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
@@ -1,27 +1,26 @@
1
- // ======== topics ======= //
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
2
  import { boolean, pgTable, text, unique } from 'drizzle-orm/pg-core';
3
3
 
4
4
  import { idGenerator } from '../../utils/idGenerator';
5
- import { createdAt, updatedAt } from './_helpers';
5
+ import { timestamps } from './_helpers';
6
6
  import { sessions } from './session';
7
7
  import { users } from './user';
8
8
 
9
9
  export const topics = pgTable(
10
10
  'topics',
11
11
  {
12
- clientId: text('client_id'),
13
- createdAt: createdAt(),
14
- favorite: boolean('favorite').default(false),
15
12
  id: text('id')
16
13
  .$defaultFn(() => idGenerator('topics'))
17
14
  .primaryKey(),
18
- sessionId: text('session_id').references(() => sessions.id, { onDelete: 'cascade' }),
19
15
  title: text('title'),
20
-
21
- updatedAt: updatedAt(),
16
+ favorite: boolean('favorite').default(false),
17
+ sessionId: text('session_id').references(() => sessions.id, { onDelete: 'cascade' }),
22
18
  userId: text('user_id')
23
19
  .references(() => users.id, { onDelete: 'cascade' })
24
20
  .notNull(),
21
+ clientId: text('client_id'),
22
+
23
+ ...timestamps,
25
24
  },
26
25
  (t) => ({
27
26
  clientIdUnique: unique('topic_client_id_user_id_unique').on(t.clientId, t.userId),
@@ -5,7 +5,7 @@ import { boolean, jsonb, pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
5
5
  import { DEFAULT_PREFERENCE } from '@/const/user';
6
6
  import { CustomPluginParams } from '@/types/tool/plugin';
7
7
 
8
- import { createdAt, timestamptz, updatedAt } from './_helpers';
8
+ import { timestamps, timestamptz } from './_helpers';
9
9
 
10
10
  export const users = pgTable('users', {
11
11
  id: text('id').primaryKey().notNull(),
@@ -27,8 +27,7 @@ export const users = pgTable('users', {
27
27
 
28
28
  preference: jsonb('preference').$defaultFn(() => DEFAULT_PREFERENCE),
29
29
 
30
- createdAt: createdAt(),
31
- updatedAt: updatedAt(),
30
+ ...timestamps,
32
31
  });
33
32
 
34
33
  export type NewUser = typeof users.$inferInsert;
@@ -61,8 +60,7 @@ export const installedPlugins = pgTable(
61
60
  settings: jsonb('settings'),
62
61
  customParams: jsonb('custom_params').$type<CustomPluginParams>(),
63
62
 
64
- createdAt: createdAt(),
65
- updatedAt: updatedAt(),
63
+ ...timestamps,
66
64
  },
67
65
  (self) => ({
68
66
  id: primaryKey({ columns: [self.userId, self.identifier] }),
@@ -4,7 +4,7 @@ import { memo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
- import pkg from '@/../package.json';
7
+ import PluginTag from '@/app/(main)/chat/(workspace)/features/PluginTag';
8
8
  import { ProductLogo } from '@/components/Branding';
9
9
  import ChatList from '@/features/Conversation/components/ChatList';
10
10
  import { useAgentStore } from '@/store/agent';
@@ -12,7 +12,8 @@ import { agentSelectors } from '@/store/agent/selectors';
12
12
  import { useSessionStore } from '@/store/session';
13
13
  import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
14
14
 
15
- import PluginTag from '../PluginTag';
15
+ import pkg from '../../../../package.json';
16
+ import { useContainerStyles } from '../style';
16
17
  import { useStyles } from './style';
17
18
  import { FieldType } from './type';
18
19
 
@@ -32,12 +33,13 @@ const Preview = memo<FieldType & { title?: string }>(
32
33
 
33
34
  const { t } = useTranslation('chat');
34
35
  const { styles } = useStyles(withBackground);
36
+ const { styles: containerStyles } = useContainerStyles();
35
37
 
36
38
  const displayTitle = isInbox ? t('inbox.title') : title;
37
39
  const displayDesc = isInbox ? t('inbox.desc') : description;
38
40
 
39
41
  return (
40
- <div className={styles.preview}>
42
+ <div className={containerStyles.preview}>
41
43
  <div className={withBackground ? styles.background : undefined} id={'preview'}>
42
44
  <Flexbox className={styles.container} gap={16}>
43
45
  <div className={styles.header}>
@@ -0,0 +1,103 @@
1
+ import { Form, type FormItemProps } from '@lobehub/ui';
2
+ import { Button, Segmented, SegmentedProps, Switch } from 'antd';
3
+ import { memo, useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { FORM_STYLE } from '@/const/layoutTokens';
8
+ import { useIsMobile } from '@/hooks/useIsMobile';
9
+
10
+ import Preview from './Preview';
11
+ import { FieldType, ImageType } from './type';
12
+ import { useScreenshot } from './useScreenshot';
13
+
14
+ export const imageTypeOptions: SegmentedProps['options'] = [
15
+ {
16
+ label: 'JPG',
17
+ value: ImageType.JPG,
18
+ },
19
+ {
20
+ label: 'PNG',
21
+ value: ImageType.PNG,
22
+ },
23
+ {
24
+ label: 'SVG',
25
+ value: ImageType.SVG,
26
+ },
27
+ {
28
+ label: 'WEBP',
29
+ value: ImageType.WEBP,
30
+ },
31
+ ];
32
+
33
+ const DEFAULT_FIELD_VALUE: FieldType = {
34
+ imageType: ImageType.JPG,
35
+ withBackground: true,
36
+ withFooter: true,
37
+ withPluginInfo: false,
38
+ withSystemRole: false,
39
+ };
40
+
41
+ const ShareImage = memo(() => {
42
+ const [fieldValue, setFieldValue] = useState<FieldType>(DEFAULT_FIELD_VALUE);
43
+ const { t } = useTranslation('chat');
44
+ const { loading, onDownload, title } = useScreenshot(fieldValue.imageType);
45
+
46
+ const settings: FormItemProps[] = [
47
+ {
48
+ children: <Switch />,
49
+ label: t('shareModal.withSystemRole'),
50
+ minWidth: undefined,
51
+ name: 'withSystemRole',
52
+ valuePropName: 'checked',
53
+ },
54
+ {
55
+ children: <Switch />,
56
+ label: t('shareModal.withBackground'),
57
+ minWidth: undefined,
58
+ name: 'withBackground',
59
+ valuePropName: 'checked',
60
+ },
61
+ {
62
+ children: <Switch />,
63
+ label: t('shareModal.withFooter'),
64
+ minWidth: undefined,
65
+ name: 'withFooter',
66
+ valuePropName: 'checked',
67
+ },
68
+ {
69
+ children: <Segmented options={imageTypeOptions} />,
70
+ label: t('shareModal.imageType'),
71
+ minWidth: undefined,
72
+ name: 'imageType',
73
+ },
74
+ ];
75
+
76
+ const isMobile = useIsMobile();
77
+ return (
78
+ <Flexbox gap={16} horizontal={!isMobile}>
79
+ <Preview title={title} {...fieldValue} />
80
+ <Flexbox gap={16}>
81
+ <Form
82
+ initialValues={DEFAULT_FIELD_VALUE}
83
+ items={settings}
84
+ itemsType={'flat'}
85
+ onValuesChange={(_, v) => setFieldValue(v)}
86
+ {...FORM_STYLE}
87
+ />
88
+ <Button
89
+ block
90
+ loading={loading}
91
+ onClick={onDownload}
92
+ size={'large'}
93
+ style={isMobile ? { bottom: 0, position: 'sticky' } : undefined}
94
+ type={'primary'}
95
+ >
96
+ {t('shareModal.download')}
97
+ </Button>
98
+ </Flexbox>
99
+ </Flexbox>
100
+ );
101
+ });
102
+
103
+ export default ShareImage;
@@ -2,7 +2,7 @@ import { createStyles } from 'antd-style';
2
2
 
3
3
  import { imageUrl } from '@/const/url';
4
4
 
5
- export const useStyles = createStyles(({ css, token, stylish, cx }, withBackground: boolean) => ({
5
+ export const useStyles = createStyles(({ css, token, cx }, withBackground: boolean) => ({
6
6
  background: css`
7
7
  padding: 24px;
8
8
 
@@ -33,28 +33,6 @@ export const useStyles = createStyles(({ css, token, stylish, cx }, withBackgrou
33
33
  background: ${token.colorBgContainer};
34
34
  border-block-end: 1px solid ${token.colorBorder};
35
35
  `,
36
- preview: cx(
37
- stylish.noScrollbar,
38
- css`
39
- overflow: hidden scroll;
40
-
41
- width: 100%;
42
- max-height: 40dvh;
43
-
44
- background: ${token.colorBgLayout};
45
- border: 1px solid ${token.colorBorder};
46
- border-radius: ${token.borderRadiusLG}px;
47
-
48
- * {
49
- pointer-events: none;
50
-
51
- ::-webkit-scrollbar {
52
- width: 0 !important;
53
- height: 0 !important;
54
- }
55
- }
56
- `,
57
- ),
58
36
  role: css`
59
37
  margin-block-start: 12px;
60
38
  padding-block-start: 12px;
@@ -0,0 +1,18 @@
1
+ import { Highlighter } from '@lobehub/ui';
2
+ import { memo } from 'react';
3
+
4
+ import { useContainerStyles } from '../style';
5
+
6
+ const Preview = memo<{ content: string }>(({ content }) => {
7
+ const { styles } = useContainerStyles();
8
+
9
+ return (
10
+ <div className={styles.preview}>
11
+ <Highlighter language={'json'} wrap>
12
+ {content}
13
+ </Highlighter>
14
+ </div>
15
+ );
16
+ });
17
+
18
+ export default Preview;