新增: obscura 安装引导(prompt AI 看 hint 帮装 + 前端工具卡片 AI 帮装按钮)

This commit is contained in:
lxy
2026-08-01 18:07:56 +08:00
parent d664bdc309
commit 3afaff9efe
11 changed files with 332 additions and 4 deletions
+65
View File
@@ -99,6 +99,17 @@ export interface ToolResult {
files?: string[]
counts?: Array<{ file?: string; count?: number }>
total_files?: number
// fetch_url result fields(commands/ai/fetch_url.rs execute_fetch_url):
// - markdown: 已清洗 + 截断的 markdown 主体
// - title: HTML <title> 或 obscura markdown 首个 H1/首行(可能为 null)
// - length: markdown 字符数(后端 result.length)
// - render_mode: 'static' | 'static_fallback' | 'obscura'(obscura=成功用 JS 渲染)
// static_fallback = render=true 但 obscura 未装/失败/超时 → 回退静态 + 附 hint(引导安装 obscura)
// - hint: 仅 static_fallback 时存在,obscura 安装引导提示
markdown?: string
length?: number | null
render_mode?: string
hint?: string
}
/** 内联 SVG 图标字符串(经 v-html 注入,常量安全;图标库改造见决策记录) */
@@ -496,6 +507,7 @@ const TOOL_FORMATTERS: Record<string, (r: ToolResult) => string> = {
bind_directory: formatBindDir,
run_workflow: formatRunWorkflow,
http_request: formatHttpResult,
fetch_url: formatFetchUrl,
grep: formatGrep,
}
@@ -552,6 +564,43 @@ export function formatHttpResult(r: ToolResult): string {
return parts.join('\n')
}
/**
* fetch_url 结果摘要(commands/ai/fetch_url.rs):URL + render_mode 标识 + markdown(截断)。
*
* 通用 fallback 文案(供 formatToolResult 在 ToolResultBody 无专用模板前/或解析失败时回退)。
* 折叠态 header 文案由 summaryFetchUrl 单独提供(更短)。markdown 主体由 ToolResultBody
* fetch_url 专用模板渲染(可折叠 + obscura 引导按钮),此 formatter 仅做兜底文本。
*
* - render_mode='obscura':成功用 obscura JS 渲染,显已渲染标识。
* - render_mode='static_fallback':render=true 但 obscura 未装/失败 → 回退静态,显 hint。
* - render_mode='static':普通静态渲染(默认)。
*/
export function formatFetchUrl(r: ToolResult): string {
const url = typeof r.url === 'string' ? r.url : ''
const title = typeof r.title === 'string' && r.title ? r.title : ''
const mode = typeof r.render_mode === 'string' ? r.render_mode : 'static'
const parts: string[] = []
if (title) parts.push(t('aiTool.fetchUrlTitle', { title }))
parts.push(url)
if (mode === 'obscura') {
parts.push(t('aiTool.fetchUrlRendered'))
} else if (mode === 'static_fallback') {
parts.push(t('aiTool.fetchUrlFallbackHint'))
}
const md = typeof r.markdown === 'string' ? r.markdown : ''
if (md) {
const MAX_LINES = 20
const lines = md.split('\n')
if (lines.length > MAX_LINES) {
parts.push(lines.slice(0, MAX_LINES).join('\n'))
parts.push(t('aiTool.commandOutputLines', { n: lines.length }))
} else {
parts.push(md)
}
}
return parts.join('\n')
}
/**
* completed 工具结果的一句话摘要(折叠态 header 也能看到,避免光秃秃)。
* UX-260616-04:覆盖范围与 formatToolResult 对齐(header+body 一致);
@@ -628,6 +677,21 @@ function summaryHttpRequest(r: ToolResult): string {
: t('aiTool.httpFailed', { method, status })
}
/**
* fetch_url summary 版(折叠态 header 副标题):
* - obscura 已装渲染成功 → "已 JS 渲染"
* - static_fallback(obscura 未装/失败) → "obscura 未装,已静态回退"(红色提示,引导安装)
* - static(默认静态) → 显 markdown 截断字符数或空(无 markdown 静默)
* 返空串 → header 不显副标题(对齐其他 summary 缺数据返空)。
*/
function summaryFetchUrl(r: ToolResult): string {
const mode = typeof r.render_mode === 'string' ? r.render_mode : 'static'
if (mode === 'obscura') return t('aiTool.fetchUrlRendered')
if (mode === 'static_fallback') return t('aiTool.fetchUrlFallbackHint')
const len = typeof r.length === 'number' ? r.length : 0
return len > 0 ? t('aiTool.fetchUrlChars', { n: len }) : ''
}
/** 工具结果摘要策略表(name → 纯函数)。仅 summary 独有的工具(create/update_project/restore/purge)在此登记。 */
const TOOL_SUMMARIES: Record<string, (r: ToolResult) => string> = {
list_projects: r => summaryList(r, 'list_projects'),
@@ -654,6 +718,7 @@ const TOOL_SUMMARIES: Record<string, (r: ToolResult) => string> = {
bind_directory: summaryBindDir,
run_workflow: summaryRunWorkflow,
http_request: summaryHttpRequest,
fetch_url: summaryFetchUrl,
grep: formatGrep,
}
+6
View File
@@ -130,6 +130,12 @@ export function useToolCardHeader(getTc: () => AiToolCallInfo) {
const host = httpHost(url)
return host ? `${method} ${host}` : t('aiTool.httpRequestFallback')
}
case 'fetch_url': {
// fetch_url 头部:获取 host(对齐 http_request 精简模式,折叠态一眼知抓哪个站点)
const url = argString(tc.args, 'url')
const host = httpHost(url)
return host ? `${t('aiTool.fetchUrlPrefix')} ${host}` : t('aiTool.fetchUrlFallback')
}
default: return formatToolName(tc.name)
}
}