@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
@@ -4,7 +4,7 @@
4
4
  right: 0;
5
5
  bottom: 0;
6
6
  left: 0;
7
- z-index: 100;
7
+ z-index: 1000;
8
8
  }
9
9
 
10
10
  .model-picker__mask {
@@ -13,7 +13,7 @@
13
13
  right: 0;
14
14
  bottom: 0;
15
15
  left: 0;
16
- background-color: rgba(15, 23, 42, 0.45);
16
+ background: var(--kratos-color-mask, rgba(2, 6, 12, 0.55));
17
17
  }
18
18
 
19
19
  .model-picker__sheet {
@@ -25,7 +25,9 @@
25
25
  flex-direction: column;
26
26
  max-height: 68vh;
27
27
  overflow: hidden;
28
- background-color: #fff;
28
+ background: var(--kratos-color-surface-solid, #070d18);
29
+ border: 1PX solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
30
+ border-bottom: 0;
29
31
  border-radius: 32px 32px 0 0;
30
32
  box-shadow: 0 -24px 80px rgba(15, 23, 42, 0.16);
31
33
  box-sizing: border-box;
@@ -36,7 +38,7 @@
36
38
  width: 72px;
37
39
  height: 8px;
38
40
  margin: 16px auto 8px;
39
- background-color: #e2e5ea;
41
+ background: var(--kratos-color-track, rgba(255, 255, 255, 0.06));
40
42
  border-radius: 4px;
41
43
  }
42
44
 
@@ -51,14 +53,15 @@
51
53
  }
52
54
 
53
55
  .model-picker__title {
54
- color: #111;
56
+ color: var(--kratos-color-text, #e9f4f6);
55
57
  font-size: 32px;
56
- font-weight: 600;
58
+ font-weight: 700;
57
59
  }
58
60
 
59
61
  .model-picker__count {
60
- color: #8a8f99;
62
+ color: var(--kratos-color-text-muted, #8aa0b4);
61
63
  font-size: 20px;
64
+ font-family: var(--kratos-font-mono, monospace);
62
65
  }
63
66
 
64
67
  .model-picker__close {
@@ -70,7 +73,7 @@
70
73
  justify-content: center;
71
74
  width: 56px;
72
75
  height: 56px;
73
- color: #8a8f99;
76
+ color: var(--kratos-color-text-muted, #8aa0b4);
74
77
  font-size: 28px;
75
78
  }
76
79
 
@@ -84,8 +87,9 @@
84
87
 
85
88
  .model-picker__group {
86
89
  padding: 24px 8px 12px;
87
- color: #8a8f99;
90
+ color: var(--kratos-color-text-muted, #8aa0b4);
88
91
  font-size: 22px;
92
+ font-family: var(--kratos-font-mono, monospace);
89
93
  line-height: 32px;
90
94
  }
91
95
 
@@ -95,16 +99,17 @@
95
99
  min-height: 88px;
96
100
  padding: 16px 28px;
97
101
  margin-bottom: 12px;
98
- border: 2px solid #eef0f4;
99
- border-radius: 24px;
102
+ border: 1PX solid var(--kratos-color-border, rgba(255, 255, 255, 0.08));
103
+ background: var(--kratos-color-surface, rgba(255, 255, 255, 0.035));
104
+ border-radius: 20px;
100
105
  box-sizing: border-box;
101
106
  }
102
107
 
103
108
  .model-picker__row.is-selected {
104
- color: #0f7a62;
105
- font-weight: 500;
106
- background-color: #e8f8f4;
107
- border-color: #d5efe7;
109
+ color: var(--kratos-color-primary, #28f0d4);
110
+ font-weight: 600;
111
+ background: var(--kratos-color-primary-soft, rgba(40, 240, 212, 0.08));
112
+ border-color: var(--kratos-color-primary-edge, rgba(40, 240, 212, 0.3));
108
113
  }
109
114
 
110
115
  .model-picker__row-main {
@@ -121,7 +126,8 @@
121
126
  }
122
127
 
123
128
  .model-picker__row-raw {
124
- color: #a7acb5;
129
+ color: var(--kratos-color-text-dim, #4e6378);
130
+ font-family: var(--kratos-font-mono, monospace);
125
131
  font-size: 22px;
126
132
  font-weight: 400;
127
133
  line-height: 30px;
@@ -129,13 +135,13 @@
129
135
 
130
136
  .model-picker__row-check {
131
137
  margin-left: auto;
132
- color: #00a96b;
138
+ color: var(--kratos-color-primary, #28f0d4);
133
139
  font-size: 32px;
134
140
  }
135
141
 
136
142
  .model-picker__empty {
137
143
  padding: 64px 0;
138
- color: #8a8f99;
144
+ color: var(--kratos-color-text-dim, #4e6378);
139
145
  font-size: 26px;
140
146
  text-align: center;
141
147
  }
@@ -14,8 +14,8 @@
14
14
  right: 0;
15
15
  bottom: 0;
16
16
  left: 560px;
17
- z-index: 20;
18
- background-color: rgba(0, 0, 0, 0.18);
17
+ z-index: 1000;
18
+ background: var(--kratos-color-mask, rgba(2, 6, 12, 0.55));
19
19
  }
20
20
 
21
21
  .session-drawer {
@@ -25,10 +25,11 @@
25
25
  top: 0;
26
26
  left: 0;
27
27
  bottom: 0;
28
- z-index: 21;
28
+ z-index: 1001;
29
29
  width: 560px;
30
30
  padding: 0 24px 36px;
31
- background-color: #fff;
31
+ background: var(--kratos-color-surface-solid, #070d18);
32
+ border-right: 1PX solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
32
33
  box-shadow: none;
33
34
  box-sizing: border-box;
34
35
  transform: translateX(-100%);
@@ -36,7 +37,7 @@
36
37
  }
37
38
 
38
39
  .session-drawer.is-open {
39
- box-shadow: 24px 0 60px rgba(0, 0, 0, 0.12);
40
+ box-shadow: 24px 0 60px rgba(0, 0, 0, 0.3);
40
41
  transform: translateX(0);
41
42
  }
42
43
 
@@ -49,7 +50,12 @@
49
50
  margin-bottom: 18px;
50
51
  }
51
52
 
52
- .session-drawer__title { color: #333; font-size: 32px; font-weight: 700; line-height: 40px; }
53
+ .session-drawer__title {
54
+ color: var(--kratos-color-text, #e9f4f6);
55
+ font-size: 32px;
56
+ font-weight: 700;
57
+ line-height: 40px;
58
+ }
53
59
 
54
60
  .session-create {
55
61
  display: flex;
@@ -58,11 +64,12 @@
58
64
  gap: 4px;
59
65
  width: 112px;
60
66
  height: 52px;
61
- border-radius: 8px;
62
- color: #27ba9b;
67
+ border: 1PX solid var(--kratos-color-primary-edge, rgba(40, 240, 212, 0.3));
68
+ border-radius: 12px;
69
+ color: var(--kratos-color-primary, #28f0d4);
63
70
  font-size: 24px;
64
- line-height: 52px;
65
- background-color: #e8f8f4;
71
+ line-height: 50px;
72
+ background: var(--kratos-color-primary-soft, rgba(40, 240, 212, 0.08));
66
73
  }
67
74
 
68
75
  .session-search {
@@ -73,13 +80,19 @@
73
80
  height: 64px;
74
81
  padding: 0 18px;
75
82
  margin-bottom: 18px;
76
- border-radius: 10px;
77
- background-color: #f6f7f9;
83
+ border: 1PX solid var(--kratos-color-border, rgba(255, 255, 255, 0.08));
84
+ border-radius: 12px;
85
+ background: var(--kratos-color-surface-2, rgba(255, 255, 255, 0.06));
78
86
  box-sizing: border-box;
79
87
  }
80
88
 
81
- .session-search-input { flex: 1; min-width: 0; color: #333; font-size: 24px; }
82
- .session-search-placeholder { color: #b8bcc5; }
89
+ .session-search-input {
90
+ flex: 1;
91
+ min-width: 0;
92
+ color: var(--kratos-color-text, #e9f4f6);
93
+ font-size: 24px;
94
+ }
95
+ .session-search-placeholder { color: var(--kratos-color-text-dim, #4e6378); }
83
96
  .session-list { flex: 1; min-height: 0; }
84
97
 
85
98
  .session-item {
@@ -87,13 +100,16 @@
87
100
  align-items: center;
88
101
  gap: 12px;
89
102
  padding: 22px 16px 22px 22px;
90
- border: 1px solid #eee;
91
- border-radius: 10px;
92
- background-color: #fff;
103
+ border: 1PX solid var(--kratos-color-border, rgba(255, 255, 255, 0.08));
104
+ border-radius: 14px;
105
+ background: var(--kratos-color-surface, rgba(255, 255, 255, 0.035));
93
106
  }
94
107
 
95
108
  .session-item + .session-item { margin-top: 18px; }
96
- .session-item.is-active { border-color: #c7eee4; background-color: #e8f8f4; }
109
+ .session-item.is-active {
110
+ border-color: var(--kratos-color-primary-edge, rgba(40, 240, 212, 0.3));
111
+ background: var(--kratos-color-primary-soft, rgba(40, 240, 212, 0.08));
112
+ }
97
113
  .session-content { flex: 1; min-width: 0; }
98
114
  .session-row { display: flex; align-items: center; gap: 14px; }
99
115
 
@@ -103,20 +119,26 @@
103
119
  overflow: hidden;
104
120
  text-overflow: ellipsis;
105
121
  white-space: nowrap;
106
- color: #333;
122
+ color: var(--kratos-color-text, #e9f4f6);
107
123
  font-size: 26px;
108
124
  font-weight: 600;
109
125
  line-height: 34px;
110
126
  }
111
127
 
112
- .session-time { flex-shrink: 0; color: #a6abb3; font-size: 20px; line-height: 30px; }
128
+ .session-time {
129
+ flex-shrink: 0;
130
+ color: var(--kratos-color-text-dim, #4e6378);
131
+ font-size: 20px;
132
+ font-family: var(--kratos-font-mono, monospace);
133
+ line-height: 30px;
134
+ }
113
135
 
114
136
  .session-summary {
115
137
  margin-top: 10px;
116
138
  overflow: hidden;
117
139
  text-overflow: ellipsis;
118
140
  white-space: nowrap;
119
- color: #777;
141
+ color: var(--kratos-color-text-muted, #8aa0b4);
120
142
  font-size: 22px;
121
143
  line-height: 32px;
122
144
  }
@@ -132,4 +154,4 @@
132
154
  background-color: transparent;
133
155
  }
134
156
 
135
- .session-empty { padding: 60px 0; color: #999; font-size: 24px; text-align: center; }
157
+ .session-empty { padding: 60px 0; color: var(--kratos-color-text-dim, #4e6378); font-size: 24px; text-align: center; }
@@ -175,9 +175,9 @@
175
175
  }
176
176
 
177
177
  .welcome-bubble {
178
- color: #111;
179
- background-color: #fff;
180
- box-shadow: 0 10px 32px rgba(15, 23, 42, 0.04);
178
+ color: var(--kratos-color-text, #e9f4f6);
179
+ background: var(--kratos-color-surface, #fff);
180
+ border: 1PX solid var(--kratos-color-border, rgba(255, 255, 255, 0.08));
181
181
  box-sizing: border-box;
182
182
  }
183
183
 
@@ -204,10 +204,10 @@
204
204
  .prompt-card {
205
205
  margin-top: 34px;
206
206
  padding: 30px 28px 24px;
207
- border: 1px solid #e7ecef;
208
- border-radius: 10px;
209
- background: linear-gradient(180deg, rgba(0, 169, 107, 0.05), rgba(255, 255, 255, 0) 38%), #fff;
210
- box-shadow: 0 10px 34px rgba(15, 23, 42, 0.05);
207
+ border: 1PX solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
208
+ border-radius: 16px;
209
+ background: var(--kratos-color-surface, #fff);
210
+ box-shadow: var(--kratos-glow-soft, none);
211
211
  box-sizing: border-box;
212
212
  }
213
213
 
@@ -219,8 +219,19 @@
219
219
  margin-bottom: 18px;
220
220
  }
221
221
 
222
- .prompt-card__eyebrow { color: #00a96b; font-size: 22px; font-weight: 600; line-height: 30px; }
223
- .prompt-card__title { color: #111; font-size: 31px; font-weight: 700; line-height: 42px; }
222
+ .prompt-card__eyebrow {
223
+ color: var(--kratos-color-primary, #28f0d4);
224
+ font-size: 22px;
225
+ font-family: var(--kratos-font-mono, monospace);
226
+ font-weight: 600;
227
+ line-height: 30px;
228
+ }
229
+ .prompt-card__title {
230
+ color: var(--kratos-color-text, #e9f4f6);
231
+ font-size: 31px;
232
+ font-weight: 700;
233
+ line-height: 42px;
234
+ }
224
235
 
225
236
  .prompt-refresh {
226
237
  flex-shrink: 0;
@@ -228,12 +239,12 @@
228
239
  align-items: center;
229
240
  gap: 14px;
230
241
  height: 50px;
231
- color: #00a96b;
242
+ color: var(--kratos-color-primary, #28f0d4);
232
243
  font-size: 25px;
233
244
  line-height: 50px;
234
245
  }
235
246
 
236
- .prompt-loading { padding: 32px 0; color: #8d929c; font-size: 26px; line-height: 38px; }
247
+ .prompt-loading { padding: 32px 0; color: var(--kratos-color-text-muted, #8aa0b4); font-size: 26px; line-height: 38px; }
237
248
 
238
249
  .prompt-item {
239
250
  display: flex;
@@ -241,8 +252,8 @@
241
252
  width: 100%;
242
253
  min-height: 96px;
243
254
  padding: 16px 16px 16px 0;
244
- border-top: 1px solid #edf0f2;
245
- color: #111;
255
+ border-top: 1PX solid var(--kratos-color-border, rgba(255, 255, 255, 0.08));
256
+ color: var(--kratos-color-text, #e9f4f6);
246
257
  text-align: left;
247
258
  box-sizing: border-box;
248
259
  }
@@ -255,12 +266,12 @@
255
266
  height: 36px;
256
267
  margin-right: 24px;
257
268
  border-radius: 8px;
258
- color: #00a96b;
269
+ color: var(--kratos-color-primary, #28f0d4);
259
270
  font-size: 24px;
260
271
  font-weight: 700;
261
272
  line-height: 34px;
262
273
  text-align: center;
263
- background-color: #e7f7f2;
274
+ background: var(--kratos-color-primary-soft, rgba(40, 240, 212, 0.08));
264
275
  box-sizing: border-box;
265
276
  }
266
277
 
@@ -280,5 +291,5 @@
280
291
  white-space: nowrap;
281
292
  }
282
293
 
283
- .prompt-text { color: #111; font-size: 29px; line-height: 40px; }
284
- .prompt-meta { color: #8d929c; font-size: 22px; line-height: 30px; }
294
+ .prompt-text { color: var(--kratos-color-text, #e9f4f6); font-size: 29px; line-height: 40px; }
295
+ .prompt-meta { color: var(--kratos-color-text-muted, #8aa0b4); font-size: 22px; line-height: 30px; }
@@ -1,7 +1,7 @@
1
1
  page {
2
2
  height: 100%;
3
3
  overflow: hidden;
4
- background-color: #f6f6f6;
4
+ background-color: #eef3f8;
5
5
  }
6
6
 
7
7
  .ai-page {
@@ -12,68 +12,13 @@ page {
12
12
  height: 100vh;
13
13
  height: 100dvh;
14
14
  overflow: hidden;
15
- color: #333;
16
- background-color: #f6f6f6;
15
+ color: var(--kratos-color-text);
16
+ background: var(--kratos-bg-gradient);
17
17
  }
18
18
 
19
19
  .ai-navbar {
20
20
  flex-shrink: 0;
21
- display: flex;
22
- align-items: center;
23
- justify-content: space-between;
24
- box-sizing: border-box;
25
- background-color: #fff;
26
- border-bottom: 1px solid #eceef1;
27
- height: 44PX;
28
- }
29
-
30
- .ai-navbar.is-weapp {
31
- position: relative;
32
- height: 88PX;
33
- padding-top: 44PX;
34
- }
35
-
36
- .ai-navbar.is-weapp .nav-back-button,
37
- .ai-navbar.is-weapp .nav-menu-button {
38
- position: absolute;
39
- bottom: 0;
40
- height: 44PX;
41
- }
42
-
43
- .ai-navbar.is-weapp .nav-back-button { left: 0; }
44
- .ai-navbar.is-weapp .nav-menu-button {
45
- left: 56px;
46
- right: auto;
47
- }
48
- .ai-navbar.is-weapp .ai-navbar__title {
49
- width: 100%;
50
- height: 44PX;
51
- line-height: 44PX;
52
- }
53
-
54
- .nav-back-button,
55
- .nav-menu-button {
56
- display: flex;
57
- align-items: center;
58
- justify-content: center;
59
- width: 88px;
60
- height: 44PX;
61
- padding: 0;
62
- border: 0;
63
- border-radius: 0;
64
- background: transparent;
65
- line-height: normal;
66
- }
67
-
68
- .nav-back-button::after,
69
- .nav-menu-button::after { border: 0; }
70
-
71
- .ai-navbar__title {
72
- flex: 1;
73
- color: #111;
74
- font-size: 32px;
75
- font-weight: 600;
76
- text-align: center;
21
+ padding: 0 24px;
77
22
  }
78
23
 
79
24
  .ai-body {
@@ -95,8 +40,18 @@ page {
95
40
  word-break: break-word;
96
41
  }
97
42
 
98
- .user-bubble { color: #fff; background-color: #27ba9b; border-bottom-right-radius: 6px; }
99
- .ai-bubble { color: #333; background-color: #fff; border-bottom-left-radius: 6px; box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05); }
43
+ .user-bubble {
44
+ color: var(--kratos-color-on-primary);
45
+ background: linear-gradient(135deg, var(--kratos-color-primary-deep) 0%, var(--kratos-color-primary) 100%);
46
+ border-bottom-right-radius: 6px;
47
+ box-shadow: var(--kratos-glow);
48
+ }
49
+ .ai-bubble {
50
+ color: var(--kratos-color-text);
51
+ background: var(--kratos-color-surface);
52
+ border: 1PX solid var(--kratos-color-border);
53
+ border-bottom-left-radius: 6px;
54
+ }
100
55
  .bubble.is-streaming { opacity: 0.72; }
101
56
  .bubble-content { white-space: pre-wrap; font-size: 28px; line-height: 1.65; }
102
57
 
@@ -105,11 +60,12 @@ page {
105
60
  align-items: center;
106
61
  gap: 12px;
107
62
  margin-bottom: 8px;
108
- color: #8a8f99;
63
+ color: var(--kratos-color-text-muted);
109
64
  font-size: 20px;
65
+ font-family: var(--kratos-font-mono);
110
66
  }
111
67
 
112
- .reply-tag { color: #16806d; }
68
+ .reply-tag { color: var(--kratos-color-primary); }
113
69
  .attachment-list { margin-top: 16px; }
114
70
 
115
71
  .attachment-card {
@@ -119,8 +75,9 @@ page {
119
75
  max-width: 100%;
120
76
  padding: 12px;
121
77
  margin-top: 8px;
122
- border-radius: 10px;
123
- background-color: rgba(255, 255, 255, 0.72);
78
+ border: 1PX solid var(--kratos-color-border);
79
+ border-radius: 12px;
80
+ background: var(--kratos-color-surface-2);
124
81
  box-sizing: border-box;
125
82
  }
126
83
 
@@ -128,22 +85,28 @@ page {
128
85
  flex-shrink: 0;
129
86
  width: 48px;
130
87
  height: 48px;
131
- border-radius: 8px;
132
- color: #16806d;
133
- font-size: 22px;
134
- line-height: 48px;
88
+ border: 1PX solid var(--kratos-color-primary-edge);
89
+ border-radius: 10px;
90
+ color: var(--kratos-color-primary);
91
+ font-size: 20px;
92
+ line-height: 46px;
135
93
  text-align: center;
136
- background-color: #e8f8f4;
94
+ background: var(--kratos-color-primary-soft);
137
95
  }
138
96
 
139
97
  .attachment-info { min-width: 0; }
140
98
  .attachment-name,
141
99
  .attachment-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
142
100
  .attachment-name { max-width: 320px; font-size: 22px; }
143
- .attachment-meta { margin-top: 4px; color: #8a8f99; font-size: 19px; }
101
+ .attachment-meta { margin-top: 4px; color: var(--kratos-color-text-muted); font-size: 19px; font-family: var(--kratos-font-mono); }
144
102
 
145
103
  .tool-row,
146
- .loading-session { color: #8a8f99; font-size: 22px; line-height: 34px; }
104
+ .loading-session {
105
+ color: var(--kratos-color-text-muted);
106
+ font-size: 22px;
107
+ font-family: var(--kratos-font-mono);
108
+ line-height: 34px;
109
+ }
147
110
  .tool-row { margin-top: 12px; }
148
111
  .loading-session { padding: 20px 0; text-align: center; }
149
112
  .chat-bottom { width: 2px; height: 2px; }
@@ -1,4 +1,4 @@
1
- import { Button, ScrollView, Text, View } from '@tarojs/components'
1
+ import { ScrollView, Text, View } from '@tarojs/components'
2
2
  import Taro, { getCurrentPages, useLoad } from '@tarojs/taro'
3
3
  import { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import {
@@ -8,6 +8,7 @@ import {
8
8
  t,
9
9
  uploadFile,
10
10
  useI18n,
11
+ useThemeStore,
11
12
  } from '@liujitcn/kratos-taro-app-core'
12
13
  import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
13
14
  import { isAuthExpiredError } from '@liujitcn/kratos-taro-app-core/utils/http'
@@ -15,8 +16,10 @@ import { defAiMessageService, StreamAiMessageByChunkedRequest } from '../../../a
15
16
  import { defAiSessionService } from '../../../api/base/v1/ai_session'
16
17
  import { defAiToolService } from '../../../api/base/v1/ai_tool'
17
18
  import { defAiModelService } from '../../../api/base/v1/ai_provider'
19
+ import { defAiKnowledgeService } from '../../../api/base/v1/ai_knowledge'
18
20
  import type { AiAttachment, AiMessage, AiSession } from '../../../rpc/base/v1/ai_session'
19
21
  import type { AiProviderModelOption } from '../../../rpc/base/v1/ai_provider'
22
+ import type { AiKnowledgeOption } from '../../../rpc/base/v1/ai_knowledge'
20
23
  import type { AiShortcut, AiToolCall } from '../../../rpc/base/v1/ai_tool'
21
24
  import { AiMessageStatus } from '../../../rpc/base/v1/ai_session'
22
25
  import { Terminal } from '../../../rpc/base/v1/ai_tool'
@@ -67,6 +70,9 @@ const STARTER_PROMPT_PAGE_SIZE = 4
67
70
  /** AI 助手页面。 */
68
71
  export default function AiPage() {
69
72
  const { locale } = useI18n()
73
+ const themeStore = useThemeStore()
74
+ /** 导航图标随主题取色,UniIcon 的 color 属性不接受 CSS 变量。 */
75
+ const navIconColor = themeStore.theme === 'light' ? '#102232' : '#e9f4f6'
70
76
  const windowInfo = Taro.getWindowInfo() as ReturnType<typeof Taro.getWindowInfo> & {
71
77
  safeAreaInsets?: { top?: number; bottom?: number }
72
78
  }
@@ -100,6 +106,8 @@ export default function AiPage() {
100
106
  const [modelProviders, setModelProviders] = useState<AiProviderModelOption[]>([])
101
107
  const [selectedProviderId, setSelectedProviderId] = useState(0)
102
108
  const [selectedModelName, setSelectedModelName] = useState('')
109
+ const [knowledgeOptions, setKnowledgeOptions] = useState<AiKnowledgeOption[]>([])
110
+ const [selectedKnowledgeIds, setSelectedKnowledgeIds] = useState<number[]>([])
103
111
  const [starterShortcuts, setStarterShortcuts] = useState<AiShortcut[]>(createDefaultShortcuts)
104
112
 
105
113
  const activeSessionIDRef = useRef('')
@@ -359,13 +367,24 @@ export default function AiPage() {
359
367
  if (loadingShortcutsRef.current) return
360
368
  setLoadingShortcuts(true)
361
369
  try {
362
- const [response, providerResponse] = await Promise.all([
370
+ const [shortcutResult, providerResult, knowledgeResult] = await Promise.allSettled([
363
371
  defAiToolService.ListAiShortcut({ terminal: AI_TERMINAL }),
364
372
  defAiModelService.ListAiProviderModelOptions({}),
373
+ defAiKnowledgeService.ListAiKnowledgeOptions({}),
365
374
  ])
375
+ if (shortcutResult.status === 'rejected') throw shortcutResult.reason
376
+ if (providerResult.status === 'rejected') throw providerResult.reason
377
+ if (knowledgeResult.status === 'rejected' && isAuthExpiredError(knowledgeResult.reason)) {
378
+ throw knowledgeResult.reason
379
+ }
380
+ const response = shortcutResult.value
381
+ const providerResponse = providerResult.value
366
382
  const shortcuts = normalizeStarterShortcuts(response.shortcuts).filter((item) => !item.action)
367
383
  const providers = providerResponse.providers ?? []
368
384
  setModelProviders(providers)
385
+ const options = knowledgeResult.status === 'fulfilled' ? knowledgeResult.value.options ?? [] : []
386
+ setKnowledgeOptions(options)
387
+ setSelectedKnowledgeIds((ids) => ids.filter((id) => options.some((option) => option.id === id)))
369
388
  applyModelSelection(providers)
370
389
  if (shortcuts.length) {
371
390
  setStarterShortcuts(shortcuts)
@@ -375,6 +394,8 @@ export default function AiPage() {
375
394
  setModelProviders([])
376
395
  setSelectedProviderId(0)
377
396
  setSelectedModelName('')
397
+ setKnowledgeOptions([])
398
+ setSelectedKnowledgeIds([])
378
399
  showError(error, t('system.ai.load_shortcuts_failed'))
379
400
  } finally {
380
401
  setLoadingShortcuts(false)
@@ -387,6 +408,11 @@ export default function AiPage() {
387
408
  saveModelSelection({ provider_id: providerId, model_name: modelName })
388
409
  }
389
410
 
411
+ /** 更新当前聊天检索注入的知识库选择。 */
412
+ const handleKnowledgeChange = (knowledgeIds: number[]) => {
413
+ setSelectedKnowledgeIds(knowledgeIds)
414
+ }
415
+
390
416
  /** 优先恢复本地缓存的模型选择,缓存失效时回退到首个可用模型。 */
391
417
  const applyModelSelection = (providers: AiProviderModelOption[]) => {
392
418
  const stored = loadStoredModelSelection()
@@ -417,7 +443,7 @@ export default function AiPage() {
417
443
  content: payload.text,
418
444
  provider_id: selectedProviderId,
419
445
  model_name: selectedModelName,
420
- knowledge_base_ids: [],
446
+ knowledge_base_ids: selectedKnowledgeIds,
421
447
  attachments: payload.attachments,
422
448
  action: undefined,
423
449
  }
@@ -506,7 +532,6 @@ export default function AiPage() {
506
532
  })
507
533
 
508
534
  useEffect(() => {
509
- void Taro.setNavigationBarTitle({ title: t('system.ai.chat_title') })
510
535
  setStarterShortcuts(createDefaultShortcuts())
511
536
  setStarterPromptGroupIndex(0)
512
537
  }, [locale])
@@ -558,7 +583,8 @@ export default function AiPage() {
558
583
  title: session?.title || t('system.ai.current_session'),
559
584
  }),
560
585
  confirmText: t('common.action.delete'),
561
- confirmColor: '#cf4444',
586
+ confirmColor: themeStore.theme === 'light' ? '#e0324f' : '#ff4d6d',
587
+ cancelColor: themeStore.theme === 'light' ? '#5a7083' : '#8aa0b4',
562
588
  })
563
589
  if (!result.confirm) return
564
590
  try {
@@ -695,15 +721,19 @@ export default function AiPage() {
695
721
  }
696
722
 
697
723
  return (
698
- <View className='ai-page'>
699
- <View className={`ai-navbar${process.env.TARO_ENV === 'weapp' ? ' is-weapp' : ''}`}>
700
- <Button className='nav-back-button' hoverClass='none' onClick={navigateBack}>
701
- <UniIcon type='left' size={24} color='#111' />
702
- </Button>
703
- <View className='ai-navbar__title'>{t('system.ai.chat_title')}</View>
704
- <Button className='nav-menu-button' hoverClass='none' onClick={() => setShowSessionDrawer((open) => !open)}>
705
- <UniIcon type='bars' size={24} color='#111' />
706
- </Button>
724
+ <View
725
+ className={`ai-page ${themeStore.theme === 'light' ? 'theme-light' : 'theme-dark'}`}
726
+ >
727
+ <View className={`ai-navbar k-page-nav ${process.env.TARO_ENV === 'weapp' ? 'k-page-nav--weapp' : ''}`}>
728
+ <View className='k-page-nav__back' aria-label={t('common.action.back')} onClick={navigateBack} />
729
+ <View className='k-page-nav__title'>{t('system.ai.chat_title')}</View>
730
+ <View
731
+ className='k-page-nav__side k-ic'
732
+ aria-label={t('system.ai.history')}
733
+ onClick={() => setShowSessionDrawer((open) => !open)}
734
+ >
735
+ <UniIcon type='bars' size={22} color={navIconColor} />
736
+ </View>
707
737
  </View>
708
738
  <ScrollView
709
739
  className='ai-body'
@@ -773,6 +803,8 @@ export default function AiPage() {
773
803
  modelProviders={modelProviders}
774
804
  providerId={selectedProviderId}
775
805
  modelName={selectedModelName}
806
+ knowledgeOptions={knowledgeOptions}
807
+ knowledgeIds={selectedKnowledgeIds}
776
808
  modelPlaceholder={t('system.ai.model.model_placeholder')}
777
809
  placeholder={composerPlaceholder}
778
810
  bottom={composerBottom}
@@ -792,6 +824,7 @@ export default function AiPage() {
792
824
  }}
793
825
  onSend={() => void handleSend()}
794
826
  onModelChange={handleModelChange}
827
+ onKnowledgeChange={handleKnowledgeChange}
795
828
  onRemoveAttachment={(attachment) => setSelectedAttachments((current) => current.filter((item) => item !== attachment))}
796
829
  />
797
830
  <SessionDrawer