cleanup(client): extract Tool presentation into ui-tool
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/ui-skill/README.md
|
||||
README.md: bdd772662acda1f8cf1b7d8a7c5532f9b37123dd
|
||||
README.zh.md: 959ff0ede6d545150fb22710c8af75859966caa9
|
||||
README.md: 44953fe36ad337d0dd70e4d8c0cc2372b8924c9b
|
||||
README.zh.md: 8c21ef35eded61324d139dd32b7c1e38f8709d55
|
||||
|
||||
@@ -12,7 +12,7 @@ The `/client` export surface is the plugin body (`apply`/`inject`) only; the sou
|
||||
|
||||
## Skill tool row
|
||||
|
||||
The browser plugin also registers a keyed `skill` toolview in `conversation.chat.toolview`. A collapsed row renders the 14-pixel skill document-and-sparkle glyph, `Skill` title, separator, and requested skill name with the same neutral hierarchy as the Bash row; running calls carry the transcript shimmer, failures replace the name with the first error line, and interrupted calls use the warning state. A settled row expands as one whole-row disclosure into a bounded `Instructions` card containing the exact durable tool output, with the standard trajectory `Inspect` affordance when available. The row derives its name, lifecycle, and body only from a paired call/result slice in the current runtime window, never from the current catalog, so replay remains stable when installed skills or their descriptions change.
|
||||
The browser plugin also registers the `skill` wire name in `ui-tool`'s keyed `tool.call.toolview` slot. A collapsed row renders the 14-pixel skill document-and-sparkle glyph, `Skill` title, separator, and requested skill name with the same neutral hierarchy as the Bash row; running calls carry the transcript shimmer, failures replace the name with the first error line, and interrupted calls use the warning state. A settled row expands as one whole-row disclosure into a bounded `Instructions` card containing the exact durable tool output, with the standard trajectory `Inspect` affordance when available. The row derives its name, lifecycle, and body only from the frozen call/result slice supplied by `ui-tool`, never from the current catalog, so replay remains stable when installed skills or their descriptions change.
|
||||
|
||||
## Model Experience
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ pick 会落下字面文本 `/name `,提示词发出的就是同一段字面文
|
||||
|
||||
## skill 工具行
|
||||
|
||||
浏览器插件还会把一个 key 为 `skill` 的 toolview 注册进 `conversation.chat.toolview`。收起的行以与 Bash 行相同的中性色层级显示 14 像素的 skill 文档与闪光组合图标、`Skill` 标题、分隔符和请求加载的 skill 名称;运行中的调用带有 transcript(文本记录)的扫光效果,失败时用错误首行替换名称,中断调用则使用警告状态。已结算的行以整行作为展开入口,展开后显示一个尺寸受限的 `Instructions` 卡片,其中原样呈现持久化的工具输出;可用时还会提供标准执行轨迹的 `Inspect` 入口。该行的名称、生命周期和正文只派生自当前 runtime 窗口中已配对的调用/结果片段,绝不读取当前 skill 目录,因此即使已安装的 skill 或其描述发生变化,回放仍保持稳定。
|
||||
浏览器插件还会把 `skill` wire 名称注册进 `ui-tool` 的 keyed `tool.call.toolview` slot。收起的行以与 Bash 行相同的中性色层级显示 14 像素的 skill 文档与闪光组合图标、`Skill` 标题、分隔符和请求加载的 skill 名称;运行中的调用带有 transcript(文本记录)的扫光效果,失败时用错误首行替换名称,中断调用则使用警告状态。已结算的行以整行作为展开入口,展开后显示一个尺寸受限的 `Instructions` 卡片,其中原样呈现持久化的工具输出;可用时还会提供标准执行轨迹的 `Inspect` 入口。该行的名称、生命周期和正文只派生自 `ui-tool` 提供的冻结 call/result slice,绝不读取当前 skill 目录,因此即使已安装的 skill 或其描述发生变化,回放仍保持稳定。
|
||||
|
||||
## 模型体验
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-ui-conversation",
|
||||
"@deepseek-ai/dsh-client-ui-tool",
|
||||
"@deepseek-ai/dsh-client-ui-slash"
|
||||
],
|
||||
"platform": "web"
|
||||
@@ -40,7 +40,7 @@
|
||||
"@deepseek-ai/dsh-client-connection": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-locale": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-runtime": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-tool": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-slash": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1",
|
||||
@@ -53,7 +53,7 @@
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-test-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slash": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useState, type KeyboardEvent, type ReactNode } from 'react'
|
||||
import {
|
||||
IconChevronDownOutline14, IconInspectOutline12, IconSkillOutline16, StateDot,
|
||||
} from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ToolRowProps } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'
|
||||
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import css from './SkillRow.module.css'
|
||||
|
||||
@@ -14,7 +14,7 @@ import css from './SkillRow.module.css'
|
||||
type SkillRowState = 'running' | 'ok' | 'error' | 'stopped'
|
||||
|
||||
/** Full row props: the toolview runtime share plus this package's locale seat. */
|
||||
type SkillRowProps = ToolRowProps & PropsLocale<'skill'>
|
||||
type SkillRowProps = ToolCallViewProps & PropsLocale<'skill'>
|
||||
|
||||
/** Compact, replay-stable view model for the dedicated row. */
|
||||
interface SkillRowModel {
|
||||
@@ -45,9 +45,9 @@ function skillName(argsRaw: string, callId: string): string {
|
||||
return argsRaw === '' ? callId : firstLine(argsRaw)
|
||||
}
|
||||
|
||||
/** Flatten durable result blocks under the generic tool-row text contract.
|
||||
* Keep aligned with ui-conversation's contract/tool-call-model.ts `resultText`. */
|
||||
function resultText(block: ToolRowProps['block']): string | null {
|
||||
/** Flatten durable result blocks under the generic Tool-row text contract.
|
||||
* Keep aligned with ui-tool's models/tool-call-model.ts `resultText`. */
|
||||
function resultText(block: ToolCallViewProps['block']): string | null {
|
||||
if (!('kind' in block)) return null
|
||||
const parts: string[] = []
|
||||
for (const item of block.content) {
|
||||
@@ -60,7 +60,7 @@ function resultText(block: ToolRowProps['block']): string | null {
|
||||
}
|
||||
|
||||
/** Derive display state without consulting the live skill catalog. */
|
||||
function skillRowModel(block: ToolRowProps['block']): SkillRowModel {
|
||||
function skillRowModel(block: ToolCallViewProps['block']): SkillRowModel {
|
||||
const settled = 'kind' in block
|
||||
const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? ''
|
||||
const state: SkillRowState = !settled
|
||||
|
||||
@@ -58,8 +58,8 @@ export const inject = ['slash', 'connection', 'sessions', 'slots', 'locale']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-skill: dictionaries')
|
||||
ctx.slots.inject('conversation.chat.toolview', () => ctx.slots.register(
|
||||
{ name: 'conversation.chat.toolview', key: 'skill', locale: NS },
|
||||
ctx.slots.inject('tool.call.toolview', () => ctx.slots.register(
|
||||
{ name: 'tool.call.toolview', key: 'skill', locale: NS },
|
||||
SkillRow,
|
||||
))
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ function providePresentation(ctx: Context): PresentationCapture {
|
||||
const slots = new SlotsService(ctx)
|
||||
slots.register({
|
||||
name: 'root',
|
||||
children: { 'conversation.chat.toolview': { kind: 'keyed', scope: 'session' } },
|
||||
children: { 'tool.call.toolview': { kind: 'keyed', scope: 'session' } },
|
||||
} as never, () => null)
|
||||
const capture: PresentationCapture = {
|
||||
slots,
|
||||
@@ -113,7 +113,7 @@ describe('apply', () => {
|
||||
ctx.provide('sessions', { subagentAddress: () => undefined })
|
||||
const presentation = providePresentation(ctx)
|
||||
await ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const entry = presentation.slots.entries('conversation.chat.toolview')[0]
|
||||
const entry = presentation.slots.entries('tool.call.toolview')[0]
|
||||
expect(entry?.options).toMatchObject({ key: 'skill' })
|
||||
expect(entry?.locale).toBe('skill')
|
||||
expect(entry?.component).toBe(SkillToolRow)
|
||||
@@ -158,7 +158,7 @@ describe('apply', () => {
|
||||
// …and fiber teardown releases it.
|
||||
await fiber.dispose()
|
||||
expect(() => slash.registerSource(rival)).not.toThrow()
|
||||
expect(presentation.slots.entries('conversation.chat.toolview')).toHaveLength(0)
|
||||
expect(presentation.slots.entries('tool.call.toolview')).toHaveLength(0)
|
||||
expect(presentation.localeDisposed).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"path": "../runtime"
|
||||
},
|
||||
{
|
||||
"path": "../ui-conversation"
|
||||
"path": "../ui-tool"
|
||||
},
|
||||
{
|
||||
"path": "../ui-primitives"
|
||||
|
||||
Reference in New Issue
Block a user