@liujitcn/kratos-taro-app-system 0.0.78 → 0.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build.mjs +2 -2
- package/dist/module.mjs +54 -6
- package/package.json +3 -3
- package/src/api/base/v1/ai_knowledge.ts +25 -0
- package/src/locales/en-US.json +13 -1
- package/src/locales/ja-JP.json +13 -1
- package/src/locales/zh-CN.json +13 -1
- package/src/locales/zh-TW.json +13 -1
- package/src/pages.ts +2 -2
- package/src/rpc/base/v1/ai_knowledge.ts +33 -0
- package/src/rpc/base/v1/ai_message.ts +1 -1
- package/src/rpc/base/v1/ai_provider.ts +1 -1
- package/src/rpc/base/v1/ai_search.ts +1 -1
- package/src/rpc/base/v1/ai_session.ts +1 -1
- package/src/rpc/base/v1/ai_tool.ts +1 -1
- package/src/rpc/base/v1/notification.ts +1 -1
- package/src/rpc/google/protobuf/empty.ts +1 -1
- package/src/rpc/google/protobuf/timestamp.ts +1 -1
- package/src/views/pages/my/my.scss +28 -109
- package/src/views/pages/my/my.tsx +62 -49
- package/src/views/pagesMember/ai/components/Composer.tsx +52 -3
- package/src/views/pagesMember/ai/components/KnowledgePicker.tsx +58 -0
- package/src/views/pagesMember/ai/components/SessionDrawer.tsx +8 -5
- package/src/views/pagesMember/ai/components/WelcomePanel.tsx +7 -4
- package/src/views/pagesMember/ai/components/composer.scss +28 -23
- package/src/views/pagesMember/ai/components/knowledge-picker.scss +139 -0
- package/src/views/pagesMember/ai/components/model-picker.scss +24 -18
- package/src/views/pagesMember/ai/components/session-drawer.scss +44 -22
- package/src/views/pagesMember/ai/components/welcome-panel.scss +28 -17
- package/src/views/pagesMember/ai/index.scss +35 -72
- package/src/views/pagesMember/ai/index.tsx +47 -14
- package/src/views/pagesMember/message/detail.tsx +43 -25
- package/src/views/pagesMember/message/index.tsx +107 -98
- package/src/views/pagesMember/message/message.scss +78 -179
- package/src/views/pagesMember/profile/profile.scss +43 -99
- package/src/views/pagesMember/profile/profile.tsx +68 -48
- package/src/views/pagesMember/settings/components/PasswordVerifyDialog.scss +14 -13
- package/src/views/pagesMember/settings/settings.scss +27 -68
- package/src/views/pagesMember/settings/settings.tsx +161 -36
|
@@ -1,29 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { CSSProperties } from 'react'
|
|
2
|
+
import { Image, ScrollView, View } from '@tarojs/components'
|
|
3
3
|
import defaultAvatar from '@liujitcn/kratos-taro-app-core/static/images/avatar.png'
|
|
4
4
|
import {
|
|
5
5
|
formatSrc,
|
|
6
|
+
getMpCapsuleRightGap,
|
|
6
7
|
navigateAppRoute,
|
|
7
8
|
navigateToLogin,
|
|
8
9
|
useI18n,
|
|
9
10
|
useSettingStore,
|
|
11
|
+
useThemeStore,
|
|
10
12
|
useUserStore,
|
|
11
13
|
} from '@liujitcn/kratos-taro-app-core'
|
|
12
|
-
import Taro from '@tarojs/taro'
|
|
13
|
-
import { useEffect } from 'react'
|
|
14
14
|
import './my.scss'
|
|
15
15
|
|
|
16
16
|
/** 个人中心首页。 */
|
|
17
17
|
export default function MyPage() {
|
|
18
|
-
const {
|
|
18
|
+
const { t } = useI18n()
|
|
19
|
+
const themeStore = useThemeStore()
|
|
19
20
|
const profile = useUserStore((state) => state.userInfo)
|
|
20
21
|
const isLoggedIn = useUserStore((state) => state.isAuthenticated())
|
|
21
22
|
const ensureAuthenticated = useUserStore((state) => state.ensureAuthenticated)
|
|
22
23
|
const aiEnabled = useSettingStore((state) => state.aiEnabled)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
24
|
+
const avatarText = (profile?.nick_name || 'U').slice(0, 1)
|
|
25
|
+
/** MP-WEIXIN 原生胶囊按钮右侧避让宽度,写入 --mp-capsule-gap 供 .k-top 预留空间。 */
|
|
26
|
+
const capsuleGap = getMpCapsuleRightGap()
|
|
27
|
+
const topNavStyle = { '--mp-capsule-gap': `${capsuleGap}px` } as CSSProperties
|
|
28
|
+
/** 设计稿齿轮线稿,随主题取色的 data URI,替代 CSS 手绘图标。 */
|
|
29
|
+
const gearIcon = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(
|
|
30
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${
|
|
31
|
+
themeStore.theme === 'light' ? '#102232' : '#e9f4f6'
|
|
32
|
+
}" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8 2 2 0 1 1-2.8 2.8 1.6 1.6 0 0 0-2.7 1.1 2 2 0 1 1-4 0 1.6 1.6 0 0 0-2.6-1.1 2 2 0 1 1-2.8-2.8A1.6 1.6 0 0 0 3 13.9a2 2 0 1 1 0-4 1.6 1.6 0 0 0 1.5-2.7 2 2 0 1 1 2.8-2.8A1.6 1.6 0 0 0 10 4.6a2 2 0 1 1 4 0 1.6 1.6 0 0 0 2.7.6 2 2 0 1 1 2.8 2.8 1.6 1.6 0 0 0-.1 2.6"/></svg>`,
|
|
33
|
+
)}`
|
|
34
|
+
/** 个人卡账号行:登录展示账号,未登录提示去登录。 */
|
|
35
|
+
const accountText =
|
|
36
|
+
isLoggedIn && profile ? profile.user_name || profile.nick_name || '' : t('system.profile.login_prompt')
|
|
27
37
|
|
|
28
38
|
const openAuthenticatedPage = (route: string) => {
|
|
29
39
|
if (!ensureAuthenticated()) {
|
|
@@ -37,54 +47,57 @@ export default function MyPage() {
|
|
|
37
47
|
<ScrollView
|
|
38
48
|
enableBackToTop
|
|
39
49
|
scrollY
|
|
40
|
-
className='my-viewport'
|
|
41
|
-
|
|
50
|
+
className={`my-viewport${process.env.TARO_ENV === 'weapp' ? ' my-viewport--weapp' : ''} ${
|
|
51
|
+
themeStore.theme === 'light' ? 'theme-light' : 'theme-dark'
|
|
52
|
+
}`}
|
|
42
53
|
>
|
|
43
|
-
<View className=
|
|
44
|
-
|
|
45
|
-
<View className='
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
<View className='my-content'>
|
|
55
|
+
<View className='k-top' style={topNavStyle}>
|
|
56
|
+
<View className='k-top__title'>{t('core.navigation.my')}</View>
|
|
57
|
+
<View className='k-ic' onClick={() => openAuthenticatedPage('app/settings')}>
|
|
58
|
+
<Image className='my-gear' src={gearIcon} mode='aspectFit' />
|
|
59
|
+
</View>
|
|
60
|
+
</View>
|
|
61
|
+
|
|
62
|
+
<View className='my-stack'>
|
|
63
|
+
<View
|
|
64
|
+
className='k-glass k-glass--lit k-pfx my-profile'
|
|
65
|
+
onClick={() => navigateAppRoute('app/profile')}
|
|
66
|
+
>
|
|
67
|
+
<View className='k-cnr k-cnr--tr' aria-hidden />
|
|
68
|
+
<View className='k-cnr k-cnr--bl' aria-hidden />
|
|
69
|
+
<View className='my-pfx-main'>
|
|
70
|
+
{isLoggedIn && profile ? (
|
|
71
|
+
<Image
|
|
72
|
+
className='k-pfx__av'
|
|
73
|
+
src={profile.avatar ? formatSrc(profile.avatar) : defaultAvatar}
|
|
74
|
+
mode='aspectFill'
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
<View className='k-pfx__av'>{avatarText}</View>
|
|
78
|
+
)}
|
|
79
|
+
<View className='my-pfx-meta'>
|
|
80
|
+
<View className='k-pfx__nm'>
|
|
81
|
+
{isLoggedIn && profile ? profile.nick_name : t('system.profile.not_logged_in')}
|
|
82
|
+
</View>
|
|
83
|
+
<View className='k-pfx__ac'>{accountText}</View>
|
|
57
84
|
</View>
|
|
58
85
|
</View>
|
|
86
|
+
<View className='k-pfx__ent'>{t('system.profile.title')} ›</View>
|
|
59
87
|
</View>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<View onClick={() =>
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{t('system.profile.not_logged_in')}
|
|
68
|
-
</View>
|
|
69
|
-
<View className='my-extra'>
|
|
70
|
-
<Text className='my-tips'>{t('system.profile.login_prompt')}</Text>
|
|
88
|
+
|
|
89
|
+
{aiEnabled ? (
|
|
90
|
+
<View className='k-aix' onClick={() => openAuthenticatedPage('app/ai')}>
|
|
91
|
+
<View className='k-aix__ic'>AI</View>
|
|
92
|
+
<View className='my-aix-copy'>
|
|
93
|
+
<View className='k-aix__t'>{t('system.settings.ai_title')}</View>
|
|
94
|
+
<View className='k-aix__d'>{t('system.settings.ai_description')}</View>
|
|
71
95
|
</View>
|
|
96
|
+
<View className='k-aix__b'>{t('system.settings.go_ask')}</View>
|
|
72
97
|
</View>
|
|
73
|
-
|
|
74
|
-
)}
|
|
75
|
-
<View className='my-settings' onClick={() => openAuthenticatedPage('app/settings')}>
|
|
76
|
-
{t('system.settings.title')}
|
|
98
|
+
) : null}
|
|
77
99
|
</View>
|
|
78
100
|
</View>
|
|
79
|
-
|
|
80
|
-
{aiEnabled ? <View className='my-ai-entry' onClick={() => openAuthenticatedPage('app/ai')}>
|
|
81
|
-
<View className='my-ai-entry__icon'>AI</View>
|
|
82
|
-
<View className='my-ai-entry__content'>
|
|
83
|
-
<View className='my-ai-entry__title'>{t('system.settings.ai_title')}</View>
|
|
84
|
-
<View className='my-ai-entry__desc'>{t('system.settings.ai_description')}</View>
|
|
85
|
-
</View>
|
|
86
|
-
<View className='my-ai-entry__action'>{t('system.settings.go_ask')}</View>
|
|
87
|
-
</View> : null}
|
|
88
101
|
</ScrollView>
|
|
89
102
|
)
|
|
90
103
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Button, Text, Textarea, View } from '@tarojs/components'
|
|
2
2
|
import { useMemo, useState } from 'react'
|
|
3
|
+
import { t, useThemeStore } from '@liujitcn/kratos-taro-app-core'
|
|
3
4
|
import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
|
|
4
5
|
import type { AiAttachment } from '../../../../rpc/base/v1/ai_session'
|
|
5
6
|
import type { AiProviderModelOption } from '../../../../rpc/base/v1/ai_provider'
|
|
7
|
+
import type { AiKnowledgeOption } from '../../../../rpc/base/v1/ai_knowledge'
|
|
6
8
|
import { fallbackModelDisplayName } from '../modelDisplay'
|
|
9
|
+
import KnowledgePicker from './KnowledgePicker'
|
|
7
10
|
import ModelPicker from './ModelPicker'
|
|
8
11
|
import './composer.scss'
|
|
9
12
|
|
|
@@ -16,6 +19,9 @@ type ComposerProps = {
|
|
|
16
19
|
/** 当前选中的供应商编号和模型名称。 */
|
|
17
20
|
providerId: number
|
|
18
21
|
modelName: string
|
|
22
|
+
/** 当前可供选择的知识库及已选择编号。 */
|
|
23
|
+
knowledgeOptions: AiKnowledgeOption[]
|
|
24
|
+
knowledgeIds: number[]
|
|
19
25
|
/** 选择器未配置时显示的本地化占位文字。 */
|
|
20
26
|
modelPlaceholder: string
|
|
21
27
|
placeholder: string
|
|
@@ -29,11 +35,16 @@ type ComposerProps = {
|
|
|
29
35
|
onSend: () => void
|
|
30
36
|
onRemoveAttachment: (attachment: AiAttachment) => void
|
|
31
37
|
onModelChange: (providerId: number, modelName: string) => void
|
|
38
|
+
onKnowledgeChange: (knowledgeIds: number[]) => void
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
/** AI 助手消息输入器。 */
|
|
35
42
|
export default function Composer(props: ComposerProps) {
|
|
36
43
|
const [showModelPicker, setShowModelPicker] = useState(false)
|
|
44
|
+
const [showKnowledgePicker, setShowKnowledgePicker] = useState(false)
|
|
45
|
+
const themeStore = useThemeStore()
|
|
46
|
+
const iconColor = themeStore.theme === 'light' ? '#5a7083' : '#8aa0b4'
|
|
47
|
+
const primaryIconColor = themeStore.theme === 'light' ? '#039e8c' : '#28f0d4'
|
|
37
48
|
|
|
38
49
|
const selectedModelDisplayName = useMemo(() => {
|
|
39
50
|
const provider = props.modelProviders.find((item) => item.provider_id === props.providerId)
|
|
@@ -53,6 +64,20 @@ export default function Composer(props: ComposerProps) {
|
|
|
53
64
|
props.onModelChange(providerId, modelName)
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
/** 打开可用知识库选择器。 */
|
|
68
|
+
const openKnowledgePicker = () => {
|
|
69
|
+
if (props.sending || !props.knowledgeOptions.length) return
|
|
70
|
+
setShowKnowledgePicker(true)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 切换知识库选择并通知页面。 */
|
|
74
|
+
const handleKnowledgeToggle = (knowledgeId: number) => {
|
|
75
|
+
const current = new Set(props.knowledgeIds)
|
|
76
|
+
if (current.has(knowledgeId)) current.delete(knowledgeId)
|
|
77
|
+
else current.add(knowledgeId)
|
|
78
|
+
props.onKnowledgeChange([...current])
|
|
79
|
+
}
|
|
80
|
+
|
|
56
81
|
return (
|
|
57
82
|
<View className='composer' style={{ paddingBottom: props.bottom }}>
|
|
58
83
|
<View
|
|
@@ -65,9 +90,21 @@ export default function Composer(props: ComposerProps) {
|
|
|
65
90
|
</Text>
|
|
66
91
|
<Text className='composer-model-chip__chevron'>▾</Text>
|
|
67
92
|
</View>
|
|
93
|
+
<View
|
|
94
|
+
className={`composer-model-chip${props.sending || !props.knowledgeOptions.length ? ' is-disabled' : ''}${props.knowledgeIds.length ? ' is-selected' : ''}`}
|
|
95
|
+
onClick={openKnowledgePicker}
|
|
96
|
+
>
|
|
97
|
+
<Text className='composer-model-chip__spark'>📚</Text>
|
|
98
|
+
<Text className='composer-model-chip__label'>
|
|
99
|
+
{props.knowledgeIds.length
|
|
100
|
+
? t('system.ai.knowledge.selected_count', { count: props.knowledgeIds.length })
|
|
101
|
+
: t('system.ai.knowledge.title')}
|
|
102
|
+
</Text>
|
|
103
|
+
<Text className='composer-model-chip__chevron'>▾</Text>
|
|
104
|
+
</View>
|
|
68
105
|
<View className='composer-main'>
|
|
69
106
|
<Button className='attach-button' hoverClass='none' onClick={props.onAttach}>
|
|
70
|
-
<UniIcon type='plusempty' size={30} color=
|
|
107
|
+
<UniIcon type='plusempty' size={30} color={iconColor} />
|
|
71
108
|
</Button>
|
|
72
109
|
<View className='composer-card'>
|
|
73
110
|
{props.attachments.length ? (
|
|
@@ -97,7 +134,7 @@ export default function Composer(props: ComposerProps) {
|
|
|
97
134
|
hoverClass='none'
|
|
98
135
|
onClick={props.onRecord}
|
|
99
136
|
>
|
|
100
|
-
<UniIcon type='mic' size={28} color={props.recording ?
|
|
137
|
+
<UniIcon type='mic' size={28} color={props.recording ? primaryIconColor : iconColor} />
|
|
101
138
|
</Button>
|
|
102
139
|
</View>
|
|
103
140
|
<Button
|
|
@@ -106,7 +143,11 @@ export default function Composer(props: ComposerProps) {
|
|
|
106
143
|
hoverClass='none'
|
|
107
144
|
onClick={props.onSend}
|
|
108
145
|
>
|
|
109
|
-
<UniIcon
|
|
146
|
+
<UniIcon
|
|
147
|
+
type='paperplane'
|
|
148
|
+
size={28}
|
|
149
|
+
color={props.disabled ? iconColor : primaryIconColor}
|
|
150
|
+
/>
|
|
110
151
|
</Button>
|
|
111
152
|
</View>
|
|
112
153
|
|
|
@@ -119,6 +160,14 @@ export default function Composer(props: ComposerProps) {
|
|
|
119
160
|
onClose={() => setShowModelPicker(false)}
|
|
120
161
|
/>
|
|
121
162
|
) : null}
|
|
163
|
+
{showKnowledgePicker ? (
|
|
164
|
+
<KnowledgePicker
|
|
165
|
+
options={props.knowledgeOptions}
|
|
166
|
+
selectedIds={props.knowledgeIds}
|
|
167
|
+
onToggle={handleKnowledgeToggle}
|
|
168
|
+
onClose={() => setShowKnowledgePicker(false)}
|
|
169
|
+
/>
|
|
170
|
+
) : null}
|
|
122
171
|
</View>
|
|
123
172
|
)
|
|
124
173
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ScrollView, Text, View } from '@tarojs/components'
|
|
2
|
+
import { t } from '@liujitcn/kratos-taro-app-core'
|
|
3
|
+
import type { AiKnowledgeOption } from '../../../../rpc/base/v1/ai_knowledge'
|
|
4
|
+
import './knowledge-picker.scss'
|
|
5
|
+
|
|
6
|
+
type KnowledgePickerProps = {
|
|
7
|
+
options: AiKnowledgeOption[]
|
|
8
|
+
selectedIds: number[]
|
|
9
|
+
onToggle: (knowledgeId: number) => void
|
|
10
|
+
onClose: () => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** AI 知识库多选底部弹层。 */
|
|
14
|
+
export default function KnowledgePicker(props: KnowledgePickerProps) {
|
|
15
|
+
return (
|
|
16
|
+
<View className='knowledge-picker' catchMove>
|
|
17
|
+
<View className='knowledge-picker__mask' onClick={props.onClose} />
|
|
18
|
+
<View className='knowledge-picker__sheet'>
|
|
19
|
+
<View className='knowledge-picker__handle' />
|
|
20
|
+
<View className='knowledge-picker__head'>
|
|
21
|
+
<Text className='knowledge-picker__title'>{t('system.ai.knowledge.title')}</Text>
|
|
22
|
+
<Text className='knowledge-picker__count'>
|
|
23
|
+
{t('system.ai.knowledge.selected_count', { count: props.selectedIds.length })}
|
|
24
|
+
</Text>
|
|
25
|
+
<View className='knowledge-picker__close' onClick={props.onClose}>✕</View>
|
|
26
|
+
</View>
|
|
27
|
+
<Text className='knowledge-picker__hint'>{t('system.ai.knowledge.select_hint')}</Text>
|
|
28
|
+
<ScrollView className='knowledge-picker__body' scrollY>
|
|
29
|
+
{props.options.map((option) => {
|
|
30
|
+
const selected = props.selectedIds.includes(option.id)
|
|
31
|
+
return (
|
|
32
|
+
<View
|
|
33
|
+
key={option.id}
|
|
34
|
+
className={`knowledge-picker__row${selected ? ' is-selected' : ''}`}
|
|
35
|
+
onClick={() => props.onToggle(option.id)}
|
|
36
|
+
>
|
|
37
|
+
<View className='knowledge-picker__row-main'>
|
|
38
|
+
<Text className='knowledge-picker__row-name'>{option.name}</Text>
|
|
39
|
+
{option.doc_count ? (
|
|
40
|
+
<Text className='knowledge-picker__row-meta'>
|
|
41
|
+
{t('system.ai.knowledge.doc_count', { count: option.doc_count })}
|
|
42
|
+
</Text>
|
|
43
|
+
) : null}
|
|
44
|
+
</View>
|
|
45
|
+
<View className={`knowledge-picker__check${selected ? ' is-checked' : ''}`}>
|
|
46
|
+
{selected ? <Text>✓</Text> : null}
|
|
47
|
+
</View>
|
|
48
|
+
</View>
|
|
49
|
+
)
|
|
50
|
+
})}
|
|
51
|
+
{!props.options.length ? (
|
|
52
|
+
<View className='knowledge-picker__empty'>{t('system.ai.knowledge.empty')}</View>
|
|
53
|
+
) : null}
|
|
54
|
+
</ScrollView>
|
|
55
|
+
</View>
|
|
56
|
+
</View>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button, Input, ScrollView, Text, View } from '@tarojs/components'
|
|
2
|
-
import {
|
|
3
|
-
import { t, useI18n } from '@liujitcn/kratos-taro-app-core'
|
|
2
|
+
import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
|
|
3
|
+
import { t, useI18n, useThemeStore } from '@liujitcn/kratos-taro-app-core'
|
|
4
4
|
import type { AiSession } from '../../../../rpc/base/v1/ai_session'
|
|
5
5
|
import './session-drawer.scss'
|
|
6
6
|
|
|
@@ -39,6 +39,9 @@ function formatSessionTime(session: AiSession) {
|
|
|
39
39
|
/** AI 助手历史会话抽屉。 */
|
|
40
40
|
export default function SessionDrawer(props: SessionDrawerProps) {
|
|
41
41
|
useI18n()
|
|
42
|
+
const themeStore = useThemeStore()
|
|
43
|
+
const primaryIconColor = themeStore.theme === 'light' ? '#039e8c' : '#28f0d4'
|
|
44
|
+
const mutedIconColor = themeStore.theme === 'light' ? '#5a7083' : '#8aa0b4'
|
|
42
45
|
return (
|
|
43
46
|
<>
|
|
44
47
|
{props.open ? <View className='session-mask' onClick={props.onClose} /> : null}
|
|
@@ -49,12 +52,12 @@ export default function SessionDrawer(props: SessionDrawerProps) {
|
|
|
49
52
|
<View className='session-drawer__head'>
|
|
50
53
|
<View className='session-drawer__title'>{t('system.ai.history')}</View>
|
|
51
54
|
<Button className='session-create' hoverClass='none' onClick={props.onCreate}>
|
|
52
|
-
<
|
|
55
|
+
<UniIcon type='plusempty' size={16} color={primaryIconColor} />
|
|
53
56
|
<Text>{t('common.action.new')}</Text>
|
|
54
57
|
</Button>
|
|
55
58
|
</View>
|
|
56
59
|
<View className='session-search'>
|
|
57
|
-
<
|
|
60
|
+
<UniIcon type='search' size={16} color={mutedIconColor} />
|
|
58
61
|
<Input
|
|
59
62
|
className='session-search-input'
|
|
60
63
|
confirmType='search'
|
|
@@ -88,7 +91,7 @@ export default function SessionDrawer(props: SessionDrawerProps) {
|
|
|
88
91
|
props.onAction(session)
|
|
89
92
|
}}
|
|
90
93
|
>
|
|
91
|
-
<
|
|
94
|
+
<UniIcon type='more' size={22} color={mutedIconColor} />
|
|
92
95
|
</Button>
|
|
93
96
|
</View>
|
|
94
97
|
))}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button, Text, View } from '@tarojs/components'
|
|
2
|
-
import {
|
|
3
|
-
import { t, useI18n } from '@liujitcn/kratos-taro-app-core'
|
|
2
|
+
import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
|
|
3
|
+
import { t, useI18n, useThemeStore } from '@liujitcn/kratos-taro-app-core'
|
|
4
4
|
import type { AiShortcut } from '../../../../rpc/base/v1/ai_tool'
|
|
5
5
|
import './welcome-panel.scss'
|
|
6
6
|
|
|
@@ -16,6 +16,9 @@ type WelcomePanelProps = {
|
|
|
16
16
|
/** AI 助手空会话欢迎面板。 */
|
|
17
17
|
export default function WelcomePanel(props: WelcomePanelProps) {
|
|
18
18
|
useI18n()
|
|
19
|
+
const themeStore = useThemeStore()
|
|
20
|
+
const primaryIconColor = themeStore.theme === 'light' ? '#039e8c' : '#28f0d4'
|
|
21
|
+
const mutedIconColor = themeStore.theme === 'light' ? '#9aabbb' : '#8aa0b4'
|
|
19
22
|
return (
|
|
20
23
|
<View className='welcome-panel'>
|
|
21
24
|
<View className='welcome-row is-hello'>
|
|
@@ -46,7 +49,7 @@ export default function WelcomePanel(props: WelcomePanelProps) {
|
|
|
46
49
|
</View>
|
|
47
50
|
{props.canRefresh ? (
|
|
48
51
|
<Button className='prompt-refresh' hoverClass='none' onClick={props.onRefresh}>
|
|
49
|
-
<Text>{t('system.ai.refresh')}</Text><
|
|
52
|
+
<Text>{t('system.ai.refresh')}</Text><UniIcon type='refresh' size={25} color={primaryIconColor} />
|
|
50
53
|
</Button>
|
|
51
54
|
) : null}
|
|
52
55
|
</View>
|
|
@@ -67,7 +70,7 @@ export default function WelcomePanel(props: WelcomePanelProps) {
|
|
|
67
70
|
<Text className='prompt-text'>{shortcut.title}</Text>
|
|
68
71
|
<Text className='prompt-meta'>{shortcut.group || t('system.ai.general_assistant')}</Text>
|
|
69
72
|
</View>
|
|
70
|
-
<
|
|
73
|
+
<UniIcon type='right' size={20} color={mutedIconColor} />
|
|
71
74
|
</Button>
|
|
72
75
|
))
|
|
73
76
|
: null}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
.composer {
|
|
14
14
|
flex-shrink: 0;
|
|
15
15
|
width: 100%;
|
|
16
|
-
padding: 14px 24px 18px;
|
|
16
|
+
padding: 14px 24px calc(18px + env(safe-area-inset-bottom));
|
|
17
17
|
overflow: hidden;
|
|
18
|
-
background-color
|
|
18
|
+
background: var(--kratos-color-surface, #fff);
|
|
19
|
+
border-top: 1PX solid var(--kratos-color-border-accent, rgba(0, 150, 134, 0.28));
|
|
19
20
|
box-sizing: border-box;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -27,11 +28,10 @@
|
|
|
27
28
|
height: 56px;
|
|
28
29
|
padding: 0 24px;
|
|
29
30
|
margin: 0 8px 12px;
|
|
30
|
-
color: #
|
|
31
|
-
background: #fff;
|
|
32
|
-
border:
|
|
31
|
+
color: var(--kratos-color-text, #102232);
|
|
32
|
+
background: var(--kratos-color-surface, #fff);
|
|
33
|
+
border: 1PX solid var(--kratos-color-border-accent, rgba(0, 150, 134, 0.28));
|
|
33
34
|
border-radius: 28px;
|
|
34
|
-
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04);
|
|
35
35
|
box-sizing: border-box;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
.composer-model-chip__spark {
|
|
43
43
|
flex-shrink: 0;
|
|
44
|
-
color: #
|
|
44
|
+
color: var(--kratos-color-primary, #039e8c);
|
|
45
45
|
font-size: 26px;
|
|
46
46
|
line-height: 26px;
|
|
47
47
|
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
|
|
60
60
|
.composer-model-chip__chevron {
|
|
61
61
|
flex-shrink: 0;
|
|
62
|
-
color: #
|
|
62
|
+
color: var(--kratos-color-text-muted, #5a7083);
|
|
63
63
|
font-size: 22px;
|
|
64
64
|
line-height: 22px;
|
|
65
65
|
}
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
gap: 16px;
|
|
71
71
|
min-height: 124px;
|
|
72
72
|
padding: 22px 18px;
|
|
73
|
+
border: 1PX solid var(--kratos-color-border, #e3eaf2);
|
|
73
74
|
border-radius: 24px;
|
|
74
|
-
background-color
|
|
75
|
-
box-shadow: 0 12px 34px rgba(15, 23, 42, 0.08);
|
|
75
|
+
background: var(--kratos-color-surface, #fff);
|
|
76
76
|
box-sizing: border-box;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -83,9 +83,10 @@
|
|
|
83
83
|
justify-content: center;
|
|
84
84
|
width: 72px;
|
|
85
85
|
height: 72px;
|
|
86
|
-
border:
|
|
86
|
+
border: 1PX solid var(--kratos-color-border, #e3eaf2);
|
|
87
87
|
border-radius: 50%;
|
|
88
|
-
background-color
|
|
88
|
+
background: var(--kratos-color-surface, #fff);
|
|
89
|
+
color: var(--kratos-color-text-muted, #5a7083);
|
|
89
90
|
box-shadow: none;
|
|
90
91
|
box-sizing: border-box;
|
|
91
92
|
}
|
|
@@ -99,9 +100,9 @@
|
|
|
99
100
|
gap: 10px;
|
|
100
101
|
min-height: 72px;
|
|
101
102
|
padding: 0 12px 0 28px;
|
|
102
|
-
border:
|
|
103
|
+
border: 1PX solid var(--kratos-color-border, #e3eaf2);
|
|
103
104
|
border-radius: 38px;
|
|
104
|
-
background-color
|
|
105
|
+
background: var(--kratos-color-surface-2, #eef4fa);
|
|
105
106
|
box-shadow: none;
|
|
106
107
|
box-sizing: border-box;
|
|
107
108
|
}
|
|
@@ -120,11 +121,12 @@
|
|
|
120
121
|
text-overflow: ellipsis;
|
|
121
122
|
white-space: nowrap;
|
|
122
123
|
padding: 6px 12px;
|
|
124
|
+
border: 1PX solid var(--kratos-color-primary-edge, rgba(0, 150, 134, 0.3));
|
|
123
125
|
border-radius: 8px;
|
|
124
|
-
color: #
|
|
126
|
+
color: var(--kratos-color-primary, #039e8c);
|
|
125
127
|
font-size: 20px;
|
|
126
128
|
line-height: 28px;
|
|
127
|
-
background-color
|
|
129
|
+
background: var(--kratos-color-primary-soft, rgba(3, 158, 140, 0.08));
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
.composer-input {
|
|
@@ -134,14 +136,14 @@
|
|
|
134
136
|
max-height: 126px;
|
|
135
137
|
padding: 14px 0;
|
|
136
138
|
box-sizing: border-box;
|
|
137
|
-
color: #
|
|
139
|
+
color: var(--kratos-color-text, #102232);
|
|
138
140
|
font-size: 29px;
|
|
139
141
|
line-height: 42px;
|
|
140
142
|
overflow-y: auto;
|
|
141
|
-
background
|
|
143
|
+
background: transparent;
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
.composer-placeholder { color: #
|
|
146
|
+
.composer-placeholder { color: var(--kratos-color-text-dim, #9aabbb); font-size: 29px; }
|
|
145
147
|
|
|
146
148
|
.voice-button,
|
|
147
149
|
.send-button {
|
|
@@ -153,7 +155,10 @@
|
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
.voice-button { width: 60px; height: 60px; background-color: transparent; }
|
|
156
|
-
.voice-button.active { background-color
|
|
157
|
-
.send-button { width: 74px; height: 74px; background-color
|
|
158
|
-
.send-button.is-disabled { background-color
|
|
159
|
-
.send-button:not(.is-disabled) {
|
|
158
|
+
.voice-button.active { background: var(--kratos-color-primary-soft, rgba(3, 158, 140, 0.08)); }
|
|
159
|
+
.send-button { width: 74px; height: 74px; background: var(--kratos-color-track, #e8eef5); }
|
|
160
|
+
.send-button.is-disabled { background: var(--kratos-color-track, #e8eef5); }
|
|
161
|
+
.send-button:not(.is-disabled) {
|
|
162
|
+
background: var(--kratos-color-primary-soft-2, rgba(3, 158, 140, 0.1));
|
|
163
|
+
border: 1PX solid var(--kratos-color-primary-edge, rgba(0, 150, 134, 0.3));
|
|
164
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
.knowledge-picker {
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 0;
|
|
4
|
+
right: 0;
|
|
5
|
+
bottom: 0;
|
|
6
|
+
left: 0;
|
|
7
|
+
z-index: 1000;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.knowledge-picker__mask {
|
|
11
|
+
position: absolute;
|
|
12
|
+
inset: 0;
|
|
13
|
+
background: var(--kratos-color-mask, rgba(2, 6, 12, 0.55));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.knowledge-picker__sheet {
|
|
17
|
+
position: absolute;
|
|
18
|
+
right: 0;
|
|
19
|
+
bottom: 0;
|
|
20
|
+
left: 0;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
max-height: 62vh;
|
|
24
|
+
padding: 20px 40px calc(40px + env(safe-area-inset-bottom));
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
background: var(--kratos-color-surface-solid, #070d18);
|
|
27
|
+
border-radius: 40px 40px 0 0;
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.knowledge-picker__handle {
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
width: 72px;
|
|
34
|
+
height: 8px;
|
|
35
|
+
margin: 0 auto 20px;
|
|
36
|
+
background: var(--kratos-color-track, rgba(255, 255, 255, 0.06));
|
|
37
|
+
border-radius: 4px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.knowledge-picker__head {
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 20px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.knowledge-picker__title {
|
|
47
|
+
flex-shrink: 0;
|
|
48
|
+
color: var(--kratos-color-text, #e9f4f6);
|
|
49
|
+
font-size: 34px;
|
|
50
|
+
font-weight: 600;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.knowledge-picker__count {
|
|
54
|
+
flex: 1;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
color: var(--kratos-color-text-muted, #8aa0b4);
|
|
57
|
+
font-size: 24px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.knowledge-picker__close {
|
|
61
|
+
flex-shrink: 0;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
width: 44px;
|
|
66
|
+
height: 44px;
|
|
67
|
+
color: var(--kratos-color-text-muted, #8aa0b4);
|
|
68
|
+
font-size: 32px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.knowledge-picker__hint {
|
|
72
|
+
flex-shrink: 0;
|
|
73
|
+
margin-top: 12px;
|
|
74
|
+
color: var(--kratos-color-text-muted, #8aa0b4);
|
|
75
|
+
font-size: 24px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.knowledge-picker__body {
|
|
79
|
+
flex: 1;
|
|
80
|
+
min-height: 0;
|
|
81
|
+
margin-top: 20px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.knowledge-picker__row {
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: space-between;
|
|
88
|
+
gap: 24px;
|
|
89
|
+
min-height: 92px;
|
|
90
|
+
padding: 24px 8px;
|
|
91
|
+
border-bottom: 1PX solid var(--kratos-color-border, #e3eaf2);
|
|
92
|
+
box-sizing: border-box;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.knowledge-picker__row-main {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
gap: 4px;
|
|
99
|
+
min-width: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.knowledge-picker__row-name {
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
color: var(--kratos-color-text, #e9f4f6);
|
|
105
|
+
font-size: 30px;
|
|
106
|
+
text-overflow: ellipsis;
|
|
107
|
+
white-space: nowrap;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.knowledge-picker__row-meta {
|
|
111
|
+
color: var(--kratos-color-text-muted, #8aa0b4);
|
|
112
|
+
font-size: 24px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.knowledge-picker__check {
|
|
116
|
+
flex-shrink: 0;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
width: 44px;
|
|
121
|
+
height: 44px;
|
|
122
|
+
border: 1PX solid var(--kratos-color-border, #d0d2d6);
|
|
123
|
+
border-radius: 50%;
|
|
124
|
+
color: var(--kratos-color-on-primary, #031513);
|
|
125
|
+
font-size: 26px;
|
|
126
|
+
box-sizing: border-box;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.knowledge-picker__check.is-checked {
|
|
130
|
+
background: var(--kratos-color-primary, #28f0d4);
|
|
131
|
+
border-color: var(--kratos-color-primary, #28f0d4);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.knowledge-picker__empty {
|
|
135
|
+
padding: 48px 0;
|
|
136
|
+
color: var(--kratos-color-text-muted, #8aa0b4);
|
|
137
|
+
font-size: 26px;
|
|
138
|
+
text-align: center;
|
|
139
|
+
}
|