Skip to content

Commit 82e54a7

Browse files
authored
Merge pull request #1834 from Mai-with-u/dev
Dev
2 parents 092fd66 + 3920b77 commit 82e54a7

100 files changed

Lines changed: 7954 additions & 1777 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ logs
285285

286286
.ruff_cache
287287
.codex-runtime/
288+
unsloth_compiled_cache/
288289

289290
.vscode
290291

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ https://github.com/Mai-with-u/maibot-plugin-sdk/blob/main/docs/guide.md
8383
如果你要编写插件,不要改动根目录的.gitignore,而是在/plugins下创建独立仓库,然后进行编写
8484
如果你要编写插件有需求需要改动主程序代码,请你先请求许可。
8585

86+
8687
# 修改文档
8788
如果有功能性的变更或者api或者开发变更,可以对根目录下/mai-docs进行修改,不要在上层目录新建内容
8889

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ MaiSaka 不仅仅是一个机器人,不仅仅是一个可以帮你完成任务
8282
## 🔥 更新和安装
8383
<sub><sup>Updates and Installation</sup></sub>
8484

85-
> **最新版本: v1.0.6** ([📄 更新日志](changelogs/changelog.md))
85+
> **最新版本: v1.0.7** ([📄 更新日志](changelogs/changelog.md))
8686
> <sub><sup><strong>Latest Version: v1.0.0</strong> (<a href="changelogs/changelog.md">📄 Changelog</a>)</sup></sub>
8787
8888
- **下载**:前往 [Release](https://github.com/MaiM-with-u/MaiBot/releases/) 页面下载最新版本。

changelogs/changelog.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
测试版更新日志见 [changelog_dev.md](changelog_dev.md)
44

5+
# [1.0.7] - 2026-6-19
6+
7+
## Webui
8+
9+
- 优化麦麦观察的可用性,现在信息更加实时,并且可以显示头像
10+
- 新增 聊天管理 tab,管理不同聊天的配置更加方便!可以管理回复频率,表达,黑话以及删除。
11+
- 添加麦麦推理重放功能,可以更好地重放和调试!
12+
- 优化麦麦设置的部分说明和模型设置的体验
13+
- 优化插件设置编辑的体验,顶部悬浮,更高信息密度的排版
14+
- 优化黑话管理的加载速度
15+
- 优化表情包的按钮尺寸
16+
- 优化部分卡片在现代UI下的表现
17+
- 支持字号修改
18+
- 优化知识图谱的观看
19+
- 修改新手引导逻辑
20+
21+
## Maisaka
22+
23+
- 优化tg异常调用工具的问题
24+
525
# [1.0.6] - 2026-6-16
626

727
## Maisak

dashboard/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "maibot-dashboard",
33
"private": true,
4-
"version": "1.4.6",
4+
"version": "1.5.0",
55
"type": "module",
66
"main": "./out/main/index.js",
77
"scripts": {

dashboard/src/components/ListFieldEditor.tsx

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { DraftNumberInput } from '@/components/ui/draft-number-input'
3232
import { Input } from '@/components/ui/input'
3333
import { Label } from '@/components/ui/label'
3434
import { Card } from '@/components/ui/card'
35+
import { MultiSelect } from '@/components/ui/multi-select'
3536
import { Switch } from '@/components/ui/switch'
3637
import { Slider } from '@/components/ui/slider'
3738
import {
@@ -52,6 +53,10 @@ export interface ItemFieldDefinition {
5253
label?: string
5354
placeholder?: string
5455
default?: unknown
56+
/** select 是否允许多选 */
57+
multiple?: boolean
58+
/** 当字段本身仍是数组时,描述其元素类型 */
59+
item_type?: string
5560
/** select 类型的选项 */
5661
choices?: unknown[]
5762
/** slider 类型的最小值 */
@@ -60,6 +65,12 @@ export interface ItemFieldDefinition {
6065
max?: number
6166
/** slider 类型的步进 */
6267
step?: number
68+
/** 嵌套数组为 object 时的字段定义 */
69+
item_fields?: Record<string, ItemFieldDefinition>
70+
/** 嵌套数组最小项数 */
71+
min_items?: number
72+
/** 嵌套数组最大项数 */
73+
max_items?: number
6374
}
6475

6576
export interface ListFieldEditorProps {
@@ -263,27 +274,47 @@ function ObjectItemEditor({
263274

264275
// select
265276
if (fieldDef.type === 'select' && fieldDef.choices) {
277+
const selectedValues = Array.isArray(fieldValue)
278+
? fieldValue.map((item) => String(item))
279+
: Array.isArray(fieldDef.default)
280+
? fieldDef.default.map((item) => String(item))
281+
: []
282+
266283
return (
267284
<div className="space-y-1">
268285
<Label className="text-xs text-muted-foreground">
269286
{fieldDef.label ?? fieldName}
270287
</Label>
271-
<Select
272-
value={String(fieldValue ?? fieldDef.default ?? '')}
273-
onValueChange={(v) => handleFieldChange(fieldName, v)}
274-
disabled={disabled}
275-
>
276-
<SelectTrigger className="h-8 text-sm">
277-
<SelectValue placeholder={fieldDef.placeholder ?? '请选择'} />
278-
</SelectTrigger>
279-
<SelectContent>
280-
{fieldDef.choices.map((choice) => (
281-
<SelectItem key={String(choice)} value={String(choice)}>
282-
{String(choice)}
283-
</SelectItem>
284-
))}
285-
</SelectContent>
286-
</Select>
288+
{
289+
fieldDef.multiple ?
290+
<MultiSelect
291+
options={fieldDef.choices.map((choice) => ({
292+
label: String(choice),
293+
value: String(choice),
294+
}))}
295+
selected={selectedValues}
296+
onChange={(nextValue) => handleFieldChange(fieldName, nextValue)}
297+
placeholder={fieldDef.placeholder ?? '请选择'}
298+
compact
299+
disabled={disabled}
300+
/>:
301+
<Select
302+
value={String(fieldValue ?? fieldDef.default ?? '')}
303+
onValueChange={(v) => handleFieldChange(fieldName, v)}
304+
disabled={disabled}
305+
>
306+
<SelectTrigger className="h-8 text-sm">
307+
<SelectValue placeholder={fieldDef.placeholder ?? '请选择'} />
308+
</SelectTrigger>
309+
<SelectContent>
310+
{fieldDef.choices.map((choice) => (
311+
<SelectItem key={String(choice)} value={String(choice)}>
312+
{String(choice)}
313+
</SelectItem>
314+
))}
315+
</SelectContent>
316+
</Select>
317+
}
287318
</div>
288319
)
289320
}
@@ -310,6 +341,33 @@ function ObjectItemEditor({
310341
)
311342
}
312343

344+
// array
345+
if (fieldDef.type === 'array') {
346+
const selectedValues = Array.isArray(fieldValue)
347+
? fieldValue
348+
: Array.isArray(fieldDef.default)
349+
? fieldDef.default
350+
: []
351+
352+
return (
353+
<div className="space-y-1">
354+
<Label className="text-xs text-muted-foreground">
355+
{fieldDef.label ?? fieldName}
356+
</Label>
357+
<ListFieldEditor
358+
value={selectedValues}
359+
onChange={(nextValue) => handleFieldChange(fieldName, nextValue)}
360+
itemType={fieldDef.item_type ?? 'string'}
361+
itemFields={fieldDef.item_fields}
362+
minItems={fieldDef.min_items}
363+
maxItems={fieldDef.max_items}
364+
disabled={disabled}
365+
placeholder={fieldDef.placeholder}
366+
/>
367+
</div>
368+
)
369+
}
370+
313371
// string (default)
314372
return (
315373
<div className="space-y-1">
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { fireEvent, render, screen } from '@testing-library/react'
2+
import userEvent from '@testing-library/user-event'
3+
import { describe, expect, it, vi } from 'vitest'
4+
5+
import { ListFieldEditor } from '@/components/ListFieldEditor'
6+
7+
describe('ListFieldEditor', () => {
8+
it('将 multiple=true 的 select 子字段渲染为多选下拉', async () => {
9+
const user = userEvent.setup()
10+
const handleChange = vi.fn()
11+
12+
render(
13+
<ListFieldEditor
14+
value={[{ api_names: [] }]}
15+
onChange={handleChange}
16+
itemType="object"
17+
itemFields={{
18+
api_names: {
19+
type: 'select',
20+
label: '需要推送的 API 配置组',
21+
default: [],
22+
multiple: true,
23+
choices: ['daily_news', 'ai_news', 'it_news'],
24+
},
25+
}}
26+
/>
27+
)
28+
29+
await user.click(screen.getByRole('combobox'))
30+
await user.click(screen.getByText('ai_news'))
31+
32+
expect(handleChange).toHaveBeenCalledWith([{ api_names: ['ai_news'] }])
33+
})
34+
35+
it('对象数组中的多选子字段会将已选数字值规范为字符串并正确切换', async () => {
36+
const user = userEvent.setup()
37+
const handleChange = vi.fn()
38+
39+
render(
40+
<ListFieldEditor
41+
value={[{ api_ids: [1] }]}
42+
onChange={handleChange}
43+
itemType="object"
44+
itemFields={{
45+
api_ids: {
46+
type: 'select',
47+
label: 'API 编号',
48+
default: [],
49+
multiple: true,
50+
choices: [1, 2, 3],
51+
},
52+
}}
53+
/>
54+
)
55+
56+
await user.click(screen.getByRole('combobox'))
57+
await user.click(screen.getAllByText('1').at(-1)!)
58+
59+
expect(handleChange).toHaveBeenCalledWith([{ api_ids: [] }])
60+
})
61+
62+
it('父级 disabled 时禁用对象数组中的多选子字段', () => {
63+
render(
64+
<ListFieldEditor
65+
value={[{ api_names: ['daily_news'] }]}
66+
onChange={vi.fn()}
67+
itemType="object"
68+
itemFields={{
69+
api_names: {
70+
type: 'select',
71+
label: '需要推送的 API 配置组',
72+
default: [],
73+
multiple: true,
74+
choices: ['daily_news', 'ai_news', 'it_news'],
75+
},
76+
}}
77+
disabled
78+
/>
79+
)
80+
81+
expect(screen.getByRole('combobox')).toBeDisabled()
82+
})
83+
84+
it('保留对象数组子字段中的嵌套字符串数组编辑能力', async () => {
85+
const handleChange = vi.fn()
86+
87+
render(
88+
<ListFieldEditor
89+
value={[{ push_groups: ['group-a'] }]}
90+
onChange={handleChange}
91+
itemType="object"
92+
itemFields={{
93+
push_groups: {
94+
type: 'array',
95+
label: '推送群列表',
96+
default: [],
97+
item_type: 'string',
98+
},
99+
}}
100+
/>
101+
)
102+
103+
const input = screen.getByDisplayValue('group-a')
104+
fireEvent.change(input, { target: { value: 'group-b' } })
105+
106+
expect(handleChange).toHaveBeenLastCalledWith([{ push_groups: ['group-b'] }])
107+
})
108+
})

0 commit comments

Comments
 (0)