- 表单下拉框不再用 label 包裹,改为 div + aria-label,避免点击后 下拉面板瞬间关闭 - Provider 卡片新增「从上游获取模型」和「修改」入口,支持编辑 base URL、API Key 与 api_mode,凭据写入对应的环境变量 - 服务端抽出 provider-model-discovery 统一不同 api_mode 的模型发现, 旧的 custom_providers 与新的 providers 字典结构都能正确更新 - Agent Bridge 改为后台启动,HTTP 接口在 Python 运行时就绪前即可用; worker 连接被拒时按类型重试,不重放可能已产生副作用的请求 - 断线重连后恢复 profiles、models 与会话列表,健康检查改为自调度 的 setTimeout,离线时 2 秒后重试 - 批量删除确认文案改用 Popconfirm content,避免变成侧栏触发器文本
44 lines
2.0 KiB
TypeScript
44 lines
2.0 KiB
TypeScript
import { readdirSync, readFileSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
import { parse as parseSfc } from '@vue/compiler-sfc'
|
|
import { parse as parseTemplate, type ElementNode, type RootNode, type TemplateChildNode } from '@vue/compiler-dom'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
function vueFiles(directory: string): string[] {
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
|
|
const path = join(directory, entry.name)
|
|
return entry.isDirectory() ? vueFiles(path) : path.endsWith('.vue') ? [path] : []
|
|
})
|
|
}
|
|
|
|
// A native label forwards clicks on custom control chrome to the first input.
|
|
// For popup controls this can immediately toggle the menu closed again.
|
|
const popupControls = new Set([
|
|
'a-select', 'a-tree-select', 'a-cascader', 'a-auto-complete', 'a-dropdown',
|
|
'a-trigger', 'a-date-picker', 'a-range-picker', 'a-time-picker', 'a-color-picker',
|
|
'ModelSelector', 'ProfileSelector', 'LanguageSwitch', 'WorkflowModelSelector',
|
|
])
|
|
|
|
describe('dropdown label activation', () => {
|
|
it('keeps custom popup controls outside native labels throughout the client', () => {
|
|
const violations: string[] = []
|
|
for (const path of vueFiles('packages/client/src')) {
|
|
const { descriptor } = parseSfc(readFileSync(path, 'utf8'))
|
|
if (!descriptor.template) continue
|
|
const offset = descriptor.template.loc.start.line - 1
|
|
function visit(node: RootNode | TemplateChildNode, insideLabel = false) {
|
|
if (node.type !== 0 && node.type !== 1) return
|
|
if (node.type === 1) {
|
|
if (insideLabel && popupControls.has(node.tag)) {
|
|
violations.push(`${path}:${offset + node.loc.start.line} <${node.tag}> inside <label>`)
|
|
}
|
|
insideLabel ||= node.tag === 'label'
|
|
}
|
|
for (const child of (node as RootNode | ElementNode).children) visit(child, insideLabel)
|
|
}
|
|
visit(parseTemplate(descriptor.template.content))
|
|
}
|
|
expect(violations).toEqual([])
|
|
})
|
|
})
|