@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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/build.mjs +2 -2
  3. package/dist/module.mjs +54 -6
  4. package/package.json +3 -3
  5. package/src/api/base/v1/ai_knowledge.ts +25 -0
  6. package/src/locales/en-US.json +13 -1
  7. package/src/locales/ja-JP.json +13 -1
  8. package/src/locales/zh-CN.json +13 -1
  9. package/src/locales/zh-TW.json +13 -1
  10. package/src/pages.ts +2 -2
  11. package/src/rpc/base/v1/ai_knowledge.ts +33 -0
  12. package/src/rpc/base/v1/ai_message.ts +1 -1
  13. package/src/rpc/base/v1/ai_provider.ts +1 -1
  14. package/src/rpc/base/v1/ai_search.ts +1 -1
  15. package/src/rpc/base/v1/ai_session.ts +1 -1
  16. package/src/rpc/base/v1/ai_tool.ts +1 -1
  17. package/src/rpc/base/v1/notification.ts +1 -1
  18. package/src/rpc/google/protobuf/empty.ts +1 -1
  19. package/src/rpc/google/protobuf/timestamp.ts +1 -1
  20. package/src/views/pages/my/my.scss +28 -109
  21. package/src/views/pages/my/my.tsx +62 -49
  22. package/src/views/pagesMember/ai/components/Composer.tsx +52 -3
  23. package/src/views/pagesMember/ai/components/KnowledgePicker.tsx +58 -0
  24. package/src/views/pagesMember/ai/components/SessionDrawer.tsx +8 -5
  25. package/src/views/pagesMember/ai/components/WelcomePanel.tsx +7 -4
  26. package/src/views/pagesMember/ai/components/composer.scss +28 -23
  27. package/src/views/pagesMember/ai/components/knowledge-picker.scss +139 -0
  28. package/src/views/pagesMember/ai/components/model-picker.scss +24 -18
  29. package/src/views/pagesMember/ai/components/session-drawer.scss +44 -22
  30. package/src/views/pagesMember/ai/components/welcome-panel.scss +28 -17
  31. package/src/views/pagesMember/ai/index.scss +35 -72
  32. package/src/views/pagesMember/ai/index.tsx +47 -14
  33. package/src/views/pagesMember/message/detail.tsx +43 -25
  34. package/src/views/pagesMember/message/index.tsx +107 -98
  35. package/src/views/pagesMember/message/message.scss +78 -179
  36. package/src/views/pagesMember/profile/profile.scss +43 -99
  37. package/src/views/pagesMember/profile/profile.tsx +68 -48
  38. package/src/views/pagesMember/settings/components/PasswordVerifyDialog.scss +14 -13
  39. package/src/views/pagesMember/settings/settings.scss +27 -68
  40. package/src/views/pagesMember/settings/settings.tsx +161 -36
@@ -1,29 +1,39 @@
1
- import { Image, ScrollView, Text, View } from '@tarojs/components'
2
- import centerBackground from '@liujitcn/kratos-taro-app-core/static/images/center_bg.png'
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 { locale, t } = useI18n()
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
- useEffect(() => {
25
- void Taro.setNavigationBarTitle({ title: t('core.navigation.my') })
26
- }, [locale, t])
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
- style={{ backgroundImage: `url(${centerBackground})` }}
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={`my-profile${process.env.TARO_ENV === 'weapp' ? ' my-profile--weapp' : ''}`}>
44
- {isLoggedIn && profile ? (
45
- <View className='my-overview'>
46
- <View onClick={() => navigateAppRoute('app/profile')}>
47
- <Image
48
- className='my-avatar'
49
- src={profile.avatar ? formatSrc(profile.avatar) : defaultAvatar}
50
- mode='aspectFill'
51
- />
52
- </View>
53
- <View className='my-meta'>
54
- <View className='my-nickname'>{profile.nick_name}</View>
55
- <View className='my-extra' onClick={() => navigateAppRoute('app/profile')}>
56
- <Text className='my-update'>{t('system.profile.avatar_update')}</Text>
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
- <View className='my-overview'>
62
- <View onClick={() => navigateToLogin()}>
63
- <Image className='my-avatar my-avatar--gray' src={defaultAvatar} mode='aspectFill' />
64
- </View>
65
- <View className='my-meta'>
66
- <View className='my-nickname' onClick={() => navigateToLogin()}>
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
- </View>
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='#111' />
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 ? '#00a96b' : '#111'} />
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 type='paperplane' size={28} color={props.disabled ? '#111' : '#00a96b'} />
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 { More, Plus, Search } from '@liujitcn/kratos-taro-app-ui'
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
- <Plus size={16} color='#27ba9b' />
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
- <Search size={16} color='#898b94' />
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
- <More size={22} color='#9ca3af' />
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 { ArrowRight, Refresh } from '@liujitcn/kratos-taro-app-ui'
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><Refresh size={25} color='#00a96b' />
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
- <ArrowRight size={20} color='#9aa0aa' />
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: transparent;
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: #1f2937;
31
- background: #fff;
32
- border: 2px solid #d6dae2;
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: #00a96b;
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: #8a8f99;
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: #fff;
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: 2px solid #d6dae2;
86
+ border: 1PX solid var(--kratos-color-border, #e3eaf2);
87
87
  border-radius: 50%;
88
- background-color: #fff;
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: 2px solid #d6dae2;
103
+ border: 1PX solid var(--kratos-color-border, #e3eaf2);
103
104
  border-radius: 38px;
104
- background-color: #fff;
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: #16806d;
126
+ color: var(--kratos-color-primary, #039e8c);
125
127
  font-size: 20px;
126
128
  line-height: 28px;
127
- background-color: #e8f8f4;
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: #333;
139
+ color: var(--kratos-color-text, #102232);
138
140
  font-size: 29px;
139
141
  line-height: 42px;
140
142
  overflow-y: auto;
141
- background-color: transparent;
143
+ background: transparent;
142
144
  }
143
145
 
144
- .composer-placeholder { color: #8d929c; font-size: 29px; }
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: #e8f8f4; }
157
- .send-button { width: 74px; height: 74px; background-color: #f2f2f2; }
158
- .send-button.is-disabled { background-color: #f2f2f2; }
159
- .send-button:not(.is-disabled) { background-color: #e7f7f2; }
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
+ }