@lobehub/chat 1.123.4 → 1.124.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 (149) hide show
  1. package/.env.example +5 -0
  2. package/CHANGELOG.md +58 -0
  3. package/Dockerfile +2 -0
  4. package/Dockerfile.database +2 -0
  5. package/Dockerfile.pglite +2 -0
  6. package/changelog/v1.json +21 -0
  7. package/docs/self-hosting/environment-variables/model-provider.mdx +18 -0
  8. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +20 -0
  9. package/locales/ar/chat.json +8 -2
  10. package/locales/ar/editor.json +47 -0
  11. package/locales/bg-BG/chat.json +8 -2
  12. package/locales/bg-BG/editor.json +47 -0
  13. package/locales/de-DE/chat.json +8 -2
  14. package/locales/de-DE/editor.json +47 -0
  15. package/locales/en-US/chat.json +8 -2
  16. package/locales/en-US/editor.json +47 -0
  17. package/locales/es-ES/chat.json +8 -2
  18. package/locales/es-ES/editor.json +47 -0
  19. package/locales/es-ES/models.json +3 -1
  20. package/locales/fa-IR/chat.json +8 -2
  21. package/locales/fa-IR/editor.json +47 -0
  22. package/locales/fr-FR/chat.json +8 -2
  23. package/locales/fr-FR/editor.json +47 -0
  24. package/locales/it-IT/chat.json +8 -2
  25. package/locales/it-IT/editor.json +47 -0
  26. package/locales/ja-JP/chat.json +8 -2
  27. package/locales/ja-JP/editor.json +47 -0
  28. package/locales/ko-KR/chat.json +8 -2
  29. package/locales/ko-KR/editor.json +47 -0
  30. package/locales/ko-KR/models.json +3 -1
  31. package/locales/nl-NL/chat.json +8 -2
  32. package/locales/nl-NL/editor.json +47 -0
  33. package/locales/nl-NL/models.json +3 -1
  34. package/locales/pl-PL/chat.json +8 -2
  35. package/locales/pl-PL/editor.json +47 -0
  36. package/locales/pt-BR/chat.json +8 -2
  37. package/locales/pt-BR/editor.json +47 -0
  38. package/locales/ru-RU/chat.json +8 -2
  39. package/locales/ru-RU/editor.json +47 -0
  40. package/locales/tr-TR/chat.json +8 -2
  41. package/locales/tr-TR/editor.json +47 -0
  42. package/locales/vi-VN/chat.json +8 -2
  43. package/locales/vi-VN/editor.json +47 -0
  44. package/locales/zh-CN/chat.json +8 -2
  45. package/locales/zh-CN/editor.json +47 -0
  46. package/locales/zh-CN/modelProvider.json +1 -1
  47. package/locales/zh-TW/chat.json +8 -2
  48. package/locales/zh-TW/editor.json +47 -0
  49. package/locales/zh-TW/models.json +3 -1
  50. package/next.config.ts +4 -0
  51. package/package.json +4 -2
  52. package/packages/const/src/layoutTokens.ts +1 -0
  53. package/packages/model-bank/src/aiModels/aihubmix.ts +38 -4
  54. package/packages/model-bank/src/aiModels/groq.ts +26 -8
  55. package/packages/model-bank/src/aiModels/hunyuan.ts +3 -3
  56. package/packages/model-bank/src/aiModels/modelscope.ts +13 -2
  57. package/packages/model-bank/src/aiModels/moonshot.ts +25 -5
  58. package/packages/model-bank/src/aiModels/novita.ts +40 -9
  59. package/packages/model-bank/src/aiModels/openrouter.ts +0 -13
  60. package/packages/model-bank/src/aiModels/qwen.ts +62 -1
  61. package/packages/model-bank/src/aiModels/siliconcloud.ts +20 -0
  62. package/packages/model-bank/src/aiModels/volcengine.ts +141 -15
  63. package/packages/model-runtime/src/newapi/index.test.ts +49 -42
  64. package/packages/model-runtime/src/newapi/index.ts +124 -143
  65. package/packages/types/src/index.ts +1 -0
  66. package/packages/utils/src/index.ts +1 -0
  67. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/{Footer/MessageFromUrl.tsx → MessageFromUrl.tsx} +3 -2
  68. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +129 -28
  69. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +44 -66
  70. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +141 -0
  71. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +7 -1
  72. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/InboxWelcome/QuestionSuggest.tsx +3 -2
  73. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/OpeningQuestions.tsx +3 -2
  74. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +18 -2
  75. package/src/app/[variants]/(main)/settings/provider/(detail)/newapi/page.tsx +1 -1
  76. package/src/config/llm.ts +8 -0
  77. package/src/features/ChatInput/ActionBar/STT/common.tsx +41 -47
  78. package/src/features/ChatInput/{Topic → ActionBar/SaveTopic}/index.tsx +15 -4
  79. package/src/features/ChatInput/ActionBar/Typo/index.tsx +22 -0
  80. package/src/features/ChatInput/ActionBar/components/Action.tsx +4 -0
  81. package/src/features/ChatInput/ActionBar/config.ts +7 -1
  82. package/src/features/ChatInput/ActionBar/index.tsx +40 -51
  83. package/src/features/ChatInput/ChatInputProvider.tsx +54 -0
  84. package/src/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +20 -11
  85. package/src/features/ChatInput/Desktop/FilePreview/FileList.tsx +16 -15
  86. package/src/features/ChatInput/Desktop/index.tsx +94 -69
  87. package/src/features/ChatInput/InputEditor/index.tsx +134 -0
  88. package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/File.tsx +1 -2
  89. package/src/features/ChatInput/Mobile/FilePreview/index.tsx +44 -0
  90. package/src/features/ChatInput/Mobile/index.tsx +72 -0
  91. package/src/features/ChatInput/SendArea/ExpandButton.tsx +30 -0
  92. package/src/features/ChatInput/SendArea/SendButton.tsx +29 -0
  93. package/src/features/ChatInput/SendArea/ShortcutHint.tsx +52 -0
  94. package/src/features/ChatInput/SendArea/index.tsx +36 -0
  95. package/src/features/ChatInput/StoreUpdater.tsx +41 -0
  96. package/src/features/ChatInput/TypoBar/index.tsx +139 -0
  97. package/src/features/ChatInput/hooks/useChatInputEditor.ts +36 -0
  98. package/src/features/ChatInput/index.ts +7 -0
  99. package/src/features/ChatInput/store/action.ts +75 -0
  100. package/src/features/ChatInput/store/index.ts +23 -0
  101. package/src/features/ChatInput/store/initialState.ts +54 -0
  102. package/src/features/ChatInput/store/selectors.ts +5 -0
  103. package/src/features/Conversation/components/BackBottom/style.ts +1 -1
  104. package/src/features/Conversation/components/SkeletonList.tsx +10 -3
  105. package/src/features/Conversation/components/VirtualizedList/index.tsx +53 -44
  106. package/src/features/Conversation/components/WideScreenContainer/index.tsx +43 -0
  107. package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +49 -42
  108. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +48 -22
  109. package/src/features/Portal/Thread/Chat/index.tsx +2 -2
  110. package/src/features/Portal/Thread/Header/index.tsx +1 -1
  111. package/src/hooks/useHotkeys/chatScope.ts +5 -3
  112. package/src/layout/GlobalProvider/Editor.tsx +27 -0
  113. package/src/layout/GlobalProvider/Locale.tsx +3 -23
  114. package/src/locales/default/chat.ts +8 -2
  115. package/src/locales/default/editor.ts +47 -0
  116. package/src/locales/default/index.ts +2 -0
  117. package/src/locales/default/modelProvider.ts +1 -1
  118. package/src/services/aiChat.ts +8 -2
  119. package/src/store/chat/slices/aiChat/actions/__tests__/cancel-functionality.test.ts +107 -0
  120. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +394 -40
  121. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +175 -35
  122. package/src/store/chat/slices/aiChat/initialState.ts +19 -0
  123. package/src/store/chat/slices/aiChat/selectors.ts +18 -0
  124. package/src/store/global/action.test.ts +6 -5
  125. package/src/store/global/actions/__tests__/general.test.ts +6 -6
  126. package/src/store/global/actions/workspacePane.ts +6 -0
  127. package/src/store/global/initialState.ts +2 -4
  128. package/src/store/global/selectors/systemStatus.test.ts +1 -2
  129. package/src/store/global/selectors/systemStatus.ts +2 -5
  130. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +0 -104
  131. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -40
  132. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +0 -125
  133. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +0 -332
  134. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +0 -29
  135. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +0 -33
  136. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +0 -41
  137. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +0 -156
  138. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +0 -33
  139. package/src/features/ChatInput/Desktop/Header/index.tsx +0 -30
  140. package/src/features/ChatInput/Desktop/InputArea/index.tsx +0 -143
  141. package/src/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts +0 -45
  142. package/src/features/ChatInput/Desktop/useAutoFocus.ts +0 -13
  143. package/src/features/ChatInput/useSend.ts +0 -102
  144. package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +0 -90
  145. package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +0 -30
  146. package/src/libs/trpc/client/types.ts +0 -18
  147. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/Image.tsx +0 -0
  148. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/index.tsx +0 -0
  149. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/style.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { ActionIcon, Alert, Button, Dropdown, Highlighter } from '@lobehub/ui';
1
+ import { Alert, Button, Highlighter } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { Mic, MicOff } from 'lucide-react';
4
4
  import { memo, useState } from 'react';
@@ -7,6 +7,8 @@ import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import { ChatMessageError } from '@/types/message';
9
9
 
10
+ import Action from '../components/Action';
11
+
10
12
  const useStyles = createStyles(({ css, token }) => ({
11
13
  recording: css`
12
14
  width: 8px;
@@ -49,35 +51,36 @@ const CommonSTT = memo<{
49
51
  };
50
52
 
51
53
  return (
52
- <Dropdown
53
- menu={{
54
- // @ts-expect-error 等待 antd 修复
55
- activeKey: 'time',
56
- items: [
57
- {
58
- key: 'title',
59
- label: (
60
- <Flexbox>
61
- <div style={{ fontWeight: 'bolder' }}>{t('stt.action')}</div>
62
- </Flexbox>
63
- ),
64
- },
65
- {
66
- key: 'time',
67
- label: (
68
- <Flexbox align={'center'} gap={8} horizontal>
69
- <div className={styles.recording} />
70
- {time > 0 ? formattedTime : t(isRecording ? 'stt.loading' : 'stt.prettifying')}
71
- </Flexbox>
72
- ),
73
- },
74
- ],
75
- }}
76
- onOpenChange={handleDropdownVisibleChange}
77
- open={dropdownOpen || !!error || isRecording || isLoading}
78
- placement={mobile ? 'topRight' : 'top'}
79
- popupRender={
80
- error
54
+ <Action
55
+ active={isRecording}
56
+ dropdown={{
57
+ menu: {
58
+ // @ts-expect-error 等待 antd 修复
59
+ activeKey: 'time',
60
+ items: [
61
+ {
62
+ key: 'title',
63
+ label: (
64
+ <Flexbox>
65
+ <div style={{ fontWeight: 'bolder' }}>{t('stt.action')}</div>
66
+ </Flexbox>
67
+ ),
68
+ },
69
+ {
70
+ key: 'time',
71
+ label: (
72
+ <Flexbox align={'center'} gap={8} horizontal>
73
+ <div className={styles.recording} />
74
+ {time > 0 ? formattedTime : t(isRecording ? 'stt.loading' : 'stt.prettifying')}
75
+ </Flexbox>
76
+ ),
77
+ },
78
+ ],
79
+ },
80
+ onOpenChange: handleDropdownVisibleChange,
81
+ open: dropdownOpen || !!error || isRecording || isLoading,
82
+ placement: mobile ? 'topRight' : 'top',
83
+ popupRender: error
81
84
  ? () => (
82
85
  <Alert
83
86
  action={
@@ -103,23 +106,14 @@ const CommonSTT = memo<{
103
106
  type="error"
104
107
  />
105
108
  )
106
- : undefined
107
- }
108
- trigger={['click']}
109
- >
110
- <ActionIcon
111
- active={isRecording}
112
- icon={isLoading ? MicOff : Mic}
113
- onClick={handleTriggerStartStop}
114
- size={mobile ? { blockSize: 36, size: 16 } : 22}
115
- style={{ flex: 'none' }}
116
- title={dropdownOpen ? '' : desc}
117
- tooltipProps={{
118
- placement: 'bottom',
119
- }}
120
- variant={mobile ? 'outlined' : 'borderless'}
121
- />
122
- </Dropdown>
109
+ : undefined,
110
+ trigger: ['click'],
111
+ }}
112
+ icon={isLoading ? MicOff : Mic}
113
+ onClick={handleTriggerStartStop}
114
+ title={dropdownOpen ? undefined : desc}
115
+ variant={mobile ? 'outlined' : 'borderless'}
116
+ />
123
117
  );
124
118
  },
125
119
  );
@@ -1,4 +1,4 @@
1
- import { ActionIcon, Button, Hotkey, Tooltip } from '@lobehub/ui';
1
+ import { ActionIcon, Hotkey } from '@lobehub/ui';
2
2
  import { Popconfirm } from 'antd';
3
3
  import { LucideGalleryVerticalEnd, LucideMessageSquarePlus } from 'lucide-react';
4
4
  import { memo, useState } from 'react';
@@ -54,11 +54,22 @@ const SaveTopic = memo<{ mobile?: boolean }>(({ mobile }) => {
54
54
  );
55
55
  } else {
56
56
  return (
57
- <Tooltip hotkey={hotkey} title={desc}>
58
- <Button aria-label={desc} icon={icon} loading={isValidating} onClick={() => mutate()} />
59
- </Tooltip>
57
+ <ActionIcon
58
+ aria-label={desc}
59
+ icon={icon}
60
+ loading={isValidating}
61
+ onClick={() => mutate()}
62
+ size={{ blockSize: 32, size: 16, strokeWidth: 2.3 }}
63
+ title={desc}
64
+ tooltipProps={{
65
+ hotkey,
66
+ }}
67
+ variant={'outlined'}
68
+ />
60
69
  );
61
70
  }
62
71
  });
63
72
 
73
+ SaveTopic.displayName = 'SaveTopic';
74
+
64
75
  export default SaveTopic;
@@ -0,0 +1,22 @@
1
+ import { TypeIcon } from 'lucide-react';
2
+ import { memo } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import { useChatInputStore } from '../../store';
6
+ import Action from '../components/Action';
7
+
8
+ const Typo = memo(() => {
9
+ const { t } = useTranslation('editor');
10
+ const [showTypoBar, setShowTypoBar] = useChatInputStore((s) => [s.showTypoBar, s.setShowTypoBar]);
11
+
12
+ return (
13
+ <Action
14
+ active={showTypoBar}
15
+ icon={TypeIcon}
16
+ onClick={() => setShowTypoBar(!showTypoBar)}
17
+ title={t(showTypoBar ? 'actions.typobar.off' : 'actions.typobar.on')}
18
+ />
19
+ );
20
+ });
21
+
22
+ export default Typo;
@@ -51,6 +51,10 @@ const Action = memo<ActionProps>(
51
51
  placement: 'bottom',
52
52
  }}
53
53
  {...rest}
54
+ size={{
55
+ blockSize: 36,
56
+ size: 20,
57
+ }}
54
58
  />
55
59
  );
56
60
 
@@ -4,9 +4,11 @@ import Knowledge from './Knowledge';
4
4
  import Model from './Model';
5
5
  import Params from './Params';
6
6
  import STT from './STT';
7
+ import SaveTopic from './SaveTopic';
7
8
  import Search from './Search';
8
9
  import { MainToken, PortalToken } from './Token';
9
10
  import Tools from './Tools';
11
+ import Typo from './Typo';
10
12
  import Upload from './Upload';
11
13
 
12
14
  export const actionMap = {
@@ -18,10 +20,14 @@ export const actionMap = {
18
20
  model: Model,
19
21
  params: Params,
20
22
  portalToken: PortalToken,
23
+ saveTopic: SaveTopic,
21
24
  search: Search,
22
25
  stt: STT,
23
26
  temperature: Params,
24
27
  tools: Tools,
28
+ typo: Typo,
25
29
  } as const;
26
30
 
27
- export type ActionKeys = keyof typeof actionMap;
31
+ export type ActionKey = keyof typeof actionMap;
32
+
33
+ export type ActionKeys = ActionKey | ActionKey[] | '---';
@@ -1,55 +1,44 @@
1
- import { ChatInputActionBar } from '@lobehub/ui/chat';
2
- import { ReactNode, memo } from 'react';
1
+ import { ChatInputActions, type ChatInputActionsProps } from '@lobehub/editor/react';
2
+ import { memo, useMemo } from 'react';
3
3
 
4
- import { ActionKeys, actionMap } from './config';
4
+ import { ActionKeys, actionMap } from '../ActionBar/config';
5
+ import { useChatInputStore } from '../store';
5
6
 
6
- const RenderActionList = ({ dataSource }: { dataSource: ActionKeys[] }) => (
7
- <>
8
- {dataSource.map((key) => {
9
- const Render = actionMap[key];
10
- return <Render key={key} />;
11
- })}
12
- </>
13
- );
14
-
15
- export interface ActionBarProps {
16
- leftActions: ActionKeys[];
17
- leftAreaEndRender?: ReactNode;
18
- leftAreaStartRender?: ReactNode;
19
- padding?: number | string;
20
- rightActions: ActionKeys[];
21
- rightAreaEndRender?: ReactNode;
22
- rightAreaStartRender?: ReactNode;
23
- }
24
-
25
- const ActionBar = memo<ActionBarProps>(
26
- ({
27
- padding = '0 8px',
28
- rightAreaStartRender,
29
- rightAreaEndRender,
30
- leftAreaStartRender,
31
- leftAreaEndRender,
32
- leftActions,
33
- rightActions,
34
- }) => (
35
- <ChatInputActionBar
36
- leftAddons={
37
- <>
38
- {leftAreaStartRender}
39
- <RenderActionList dataSource={leftActions} />
40
- {leftAreaEndRender}
41
- </>
7
+ const mapActionsToItems = (keys: ActionKeys[]): ChatInputActionsProps['items'] =>
8
+ keys.map((actionKey, index) => {
9
+ if (typeof actionKey === 'string') {
10
+ if (actionKey === '---') {
11
+ return {
12
+ key: `divider-${index}`,
13
+ type: 'divider',
14
+ };
42
15
  }
43
- padding={padding}
44
- rightAddons={
45
- <>
46
- {rightAreaStartRender}
47
- <RenderActionList dataSource={rightActions} />
48
- {rightAreaEndRender}
49
- </>
50
- }
51
- />
52
- ),
53
- );
16
+ const Render = actionMap[actionKey];
17
+ return {
18
+ alwaysDisplay: actionKey === 'mainToken',
19
+ children: <Render key={actionKey} />,
20
+ key: actionKey,
21
+ };
22
+ } else {
23
+ return {
24
+ children: actionKey.map((groupActionKey) => {
25
+ const Render = actionMap[groupActionKey];
26
+ return {
27
+ children: <Render key={groupActionKey} />,
28
+ key: groupActionKey,
29
+ };
30
+ }),
31
+ key: `group-${index}`,
32
+ type: 'collapse',
33
+ };
34
+ }
35
+ });
36
+
37
+ const ActionToolbar = memo(() => {
38
+ const leftActions = useChatInputStore((s) => s.leftActions);
39
+ const mobile = useChatInputStore((s) => s.mobile);
40
+ const items = useMemo(() => mapActionsToItems(leftActions), [leftActions]);
41
+ return <ChatInputActions collapseOffset={mobile ? 48 : 80} items={items} />;
42
+ });
54
43
 
55
- export default ActionBar;
44
+ export default ActionToolbar;
@@ -0,0 +1,54 @@
1
+ import { useEditor } from '@lobehub/editor/react';
2
+ import { ReactNode, memo, useRef } from 'react';
3
+
4
+ import StoreUpdater, { StoreUpdaterProps } from './StoreUpdater';
5
+ import { Provider, createStore } from './store';
6
+
7
+ interface ChatInputProviderProps extends StoreUpdaterProps {
8
+ children: ReactNode;
9
+ }
10
+
11
+ export const ChatInputProvider = memo<ChatInputProviderProps>(
12
+ ({
13
+ children,
14
+ leftActions,
15
+ rightActions,
16
+ mobile,
17
+ sendButtonProps,
18
+ onSend,
19
+ sendMenu,
20
+ chatInputEditorRef,
21
+ onMarkdownContentChange,
22
+ }) => {
23
+ const editor = useEditor();
24
+ const slashMenuRef = useRef<HTMLDivElement>(null);
25
+
26
+ return (
27
+ <Provider
28
+ createStore={() =>
29
+ createStore({
30
+ editor,
31
+ leftActions,
32
+ mobile,
33
+ rightActions,
34
+ sendButtonProps,
35
+ sendMenu,
36
+ slashMenuRef,
37
+ })
38
+ }
39
+ >
40
+ <StoreUpdater
41
+ chatInputEditorRef={chatInputEditorRef}
42
+ leftActions={leftActions}
43
+ mobile={mobile}
44
+ onMarkdownContentChange={onMarkdownContentChange}
45
+ onSend={onSend}
46
+ rightActions={rightActions}
47
+ sendButtonProps={sendButtonProps}
48
+ sendMenu={sendMenu}
49
+ />
50
+ {children}
51
+ </Provider>
52
+ );
53
+ },
54
+ );
@@ -1,4 +1,4 @@
1
- import { ActionIcon, Text } from '@lobehub/ui';
1
+ import { ActionIcon, Block, Text } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { Trash2Icon } from 'lucide-react';
4
4
  import { memo } from 'react';
@@ -26,17 +26,13 @@ const useStyles = createStyles(({ css, token }) => ({
26
26
  ${token.boxShadowTertiary};
27
27
  `,
28
28
  container: css`
29
+ user-select: none;
30
+
29
31
  position: relative;
30
32
 
31
33
  width: 180px;
32
34
  height: 64px;
33
35
  border-radius: 8px;
34
-
35
- background: ${token.colorBgContainer};
36
-
37
- :hover {
38
- background: ${token.colorBgElevated};
39
- }
40
36
  `,
41
37
  image: css`
42
38
  margin-block: 0 !important;
@@ -58,15 +54,28 @@ const FileItem = memo<FileItemProps>((props) => {
58
54
  const [removeChatUploadFile] = useFileStore((s) => [s.removeChatUploadFile]);
59
55
 
60
56
  return (
61
- <Flexbox align={'center'} className={styles.container} horizontal>
57
+ <Block align={'center'} className={styles.container} horizontal variant={'outlined'}>
62
58
  <Center flex={1} height={64} padding={4} style={{ maxWidth: 64 }}>
63
59
  <Content {...props} />
64
60
  </Center>
65
61
  <Flexbox flex={1} gap={4} style={{ paddingBottom: 4, paddingInline: 4 }}>
66
- <Text ellipsis={{ tooltip: true }} style={{ fontSize: 12, maxWidth: 100 }}>
62
+ <Text
63
+ ellipsis={{
64
+ tooltip: {
65
+ styles: {
66
+ body: {
67
+ fontSize: 12,
68
+ whiteSpace: 'balance',
69
+ wordBreak: 'break-all',
70
+ },
71
+ },
72
+ title: file.name,
73
+ },
74
+ }}
75
+ style={{ fontSize: 12, maxWidth: 88 }}
76
+ >
67
77
  {file.name}
68
78
  </Text>
69
-
70
79
  <UploadDetail size={file.size} status={status} tasks={tasks} uploadState={uploadState} />
71
80
  </Flexbox>
72
81
  <Flexbox className={styles.actions}>
@@ -80,7 +89,7 @@ const FileItem = memo<FileItemProps>((props) => {
80
89
  title={t('delete', { ns: 'common' })}
81
90
  />
82
91
  </Flexbox>
83
- </Flexbox>
92
+ </Block>
84
93
  );
85
94
  });
86
95
 
@@ -1,42 +1,43 @@
1
+ import { ScrollShadow } from '@lobehub/ui';
1
2
  import { createStyles } from 'antd-style';
2
- import { lighten } from 'polished';
3
3
  import { memo } from 'react';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
+ import { useChatInputStore } from '@/features/ChatInput/store';
6
7
  import { fileChatSelectors, useFileStore } from '@/store/file';
7
8
 
8
9
  import FileItem from './FileItem';
9
10
 
10
- const useStyles = createStyles(({ css, token }) => ({
11
+ const useStyles = createStyles(({ css }) => ({
11
12
  container: css`
12
13
  overflow-x: scroll;
13
-
14
14
  width: 100%;
15
- border-start-start-radius: 8px;
16
- border-start-end-radius: 8px;
17
-
18
- background: ${lighten(0.01, token.colorBgLayout)};
19
15
  `,
20
16
  }));
21
17
 
22
18
  const FileList = memo(() => {
19
+ const expand = useChatInputStore((s) => s.expand);
20
+
23
21
  const inputFilesList = useFileStore(fileChatSelectors.chatUploadFileList);
24
22
  const showFileList = useFileStore(fileChatSelectors.chatUploadFileListHasItem);
25
23
  const { styles } = useStyles();
26
24
 
27
- if (!inputFilesList.length) return null;
25
+ if (!inputFilesList.length || !showFileList) return null;
28
26
 
29
27
  return (
30
- <Flexbox
28
+ <ScrollShadow
31
29
  className={styles.container}
32
- gap={6}
30
+ hideScrollBar
33
31
  horizontal
34
- padding={showFileList ? '16px 16px 12px' : 0}
32
+ orientation={'horizontal'}
33
+ size={8}
35
34
  >
36
- {inputFilesList.map((item) => (
37
- <FileItem key={item.id} {...item} />
38
- ))}
39
- </Flexbox>
35
+ <Flexbox gap={6} horizontal paddingBlock={8} paddingInline={expand ? 0 : 12}>
36
+ {inputFilesList.map((item) => (
37
+ <FileItem key={item.id} {...item} />
38
+ ))}
39
+ </Flexbox>
40
+ </ScrollShadow>
40
41
  );
41
42
  });
42
43
 
@@ -1,82 +1,107 @@
1
1
  'use client';
2
2
 
3
- import { DraggablePanel } from '@lobehub/ui';
4
- import { ReactNode, memo, useCallback, useState } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
3
+ import { ChatInput, ChatInputActionBar } from '@lobehub/editor/react';
4
+ import { Text } from '@lobehub/ui';
5
+ import { createStyles } from 'antd-style';
6
+ import { memo, useEffect } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
6
9
 
7
- import { CHAT_TEXTAREA_HEIGHT, CHAT_TEXTAREA_MAX_HEIGHT } from '@/const/layoutTokens';
10
+ import { useChatInputStore } from '@/features/ChatInput/store';
11
+ import { useChatStore } from '@/store/chat';
12
+ import { chatSelectors } from '@/store/chat/selectors';
8
13
 
9
- import { ActionKeys } from '../ActionBar/config';
10
- import LocalFiles from './FilePreview';
11
- import Head from './Header';
14
+ import ActionBar from '../ActionBar';
15
+ import InputEditor from '../InputEditor';
16
+ import SendArea from '../SendArea';
17
+ import TypoBar from '../TypoBar';
18
+ import FilePreview from './FilePreview';
12
19
 
13
- export type FooterRender = (params: {
14
- expand: boolean;
15
- onExpandChange: (expand: boolean) => void;
16
- }) => ReactNode;
20
+ const useStyles = createStyles(({ css, token }) => ({
21
+ container: css`
22
+ .show-on-hover {
23
+ opacity: 0;
24
+ }
17
25
 
18
- interface DesktopChatInputProps {
19
- inputHeight: number;
20
- leftActions: ActionKeys[];
21
- onInputHeightChange?: (height: number) => void;
22
- renderFooter: FooterRender;
23
- renderTextArea: (onSend: () => void) => ReactNode;
24
- rightActions: ActionKeys[];
25
- }
26
+ &:hover {
27
+ .show-on-hover {
28
+ opacity: 1;
29
+ }
30
+ }
31
+ `,
32
+ footnote: css`
33
+ font-size: 10px;
34
+ `,
35
+ fullscreen: css`
36
+ position: absolute;
37
+ z-index: 100;
38
+ inset: 0;
26
39
 
27
- const DesktopChatInput = memo<DesktopChatInputProps>(
28
- ({
29
- leftActions,
30
- rightActions,
31
- renderTextArea,
32
- inputHeight,
33
- onInputHeightChange,
34
- renderFooter,
35
- }) => {
36
- const [expand, setExpand] = useState<boolean>(false);
37
- const onSend = useCallback(() => {
38
- setExpand(false);
39
- }, []);
40
+ width: 100%;
41
+ height: 100%;
42
+ padding: 12px;
40
43
 
41
- return (
42
- <>
43
- {!expand && leftActions.includes('fileUpload') && <LocalFiles />}
44
- <DraggablePanel
45
- fullscreen={expand}
46
- maxHeight={CHAT_TEXTAREA_MAX_HEIGHT}
47
- minHeight={CHAT_TEXTAREA_HEIGHT}
48
- onSizeChange={(_, size) => {
49
- if (!size) return;
50
- const height =
51
- typeof size.height === 'string' ? Number.parseInt(size.height) : size.height;
52
- if (!height) return;
44
+ background: ${token.colorBgContainerSecondary};
45
+ `,
46
+ }));
53
47
 
54
- onInputHeightChange?.(height);
55
- }}
56
- placement="bottom"
57
- size={{ height: inputHeight, width: '100%' }}
58
- style={{ zIndex: 10 }}
59
- >
60
- <Flexbox
61
- gap={8}
62
- height={'100%'}
63
- paddingBlock={'4px 16px'}
64
- style={{ minHeight: CHAT_TEXTAREA_HEIGHT, position: 'relative' }}
65
- >
66
- <Head
67
- expand={expand}
68
- leftActions={leftActions}
69
- rightActions={rightActions}
70
- setExpand={setExpand}
48
+ const DesktopChatInput = memo<{ showFootnote?: boolean }>(({ showFootnote }) => {
49
+ const { t } = useTranslation('chat');
50
+ const [slashMenuRef, expand, showTypoBar, editor, leftActions] = useChatInputStore((s) => [
51
+ s.slashMenuRef,
52
+ s.expand,
53
+ s.showTypoBar,
54
+ s.editor,
55
+ s.leftActions,
56
+ ]);
57
+
58
+ const { styles, cx } = useStyles();
59
+
60
+ const chatKey = useChatStore(chatSelectors.currentChatKey);
61
+
62
+ useEffect(() => {
63
+ if (editor) editor.focus();
64
+ }, [chatKey, editor]);
65
+
66
+ const fileNode = leftActions.flat().includes('fileUpload') && <FilePreview />;
67
+
68
+ return (
69
+ <>
70
+ {!expand && fileNode}
71
+ <Flexbox
72
+ className={cx(styles.container, expand && styles.fullscreen)}
73
+ gap={8}
74
+ paddingBlock={showFootnote ? '0 8px' : '0 12px'}
75
+ paddingInline={12}
76
+ >
77
+ <ChatInput
78
+ footer={
79
+ <ChatInputActionBar
80
+ left={<ActionBar />}
81
+ right={<SendArea />}
82
+ style={{
83
+ paddingRight: 8,
84
+ }}
71
85
  />
72
- {renderTextArea(onSend)}
73
- {renderFooter({ expand, onExpandChange: setExpand })}
74
- </Flexbox>
75
- </DraggablePanel>
76
- </>
77
- );
78
- },
79
- );
86
+ }
87
+ fullscreen={expand}
88
+ header={showTypoBar && <TypoBar />}
89
+ slashMenuRef={slashMenuRef}
90
+ >
91
+ {expand && fileNode}
92
+ <InputEditor />
93
+ </ChatInput>
94
+ {showFootnote && !expand && (
95
+ <Center style={{ pointerEvents: 'none', zIndex: 100 }}>
96
+ <Text className={styles.footnote} type={'secondary'}>
97
+ {t('input.disclaimer')}
98
+ </Text>
99
+ </Center>
100
+ )}
101
+ </Flexbox>
102
+ </>
103
+ );
104
+ });
80
105
 
81
106
  DesktopChatInput.displayName = 'DesktopChatInput';
82
107