fix(web): unify inline file preview labels
This commit is contained in:
1 parent
4115e8867b
commit
71046d8306
7 files changed
+17
-28
No files matched your search
@@ -1,11 +1,4 @@
|
||||
// Web e2e scenario: inline-code file mentions in the closing prose. Cold-seeds
|
||||
// a built write turn (zero model calls) whose closing message names the written
|
||||
// file three ways: by unique basename (links), ambiguously (stays inert), and
|
||||
// as a file the turn never touched (stays inert). Package tests cover the
|
||||
// resolver in isolation; only the assembled application shows a real write's
|
||||
// locations reaching the prose as an opener. The click itself is not driven
|
||||
// here: it hands the path to the Host's opener, which would launch a real
|
||||
// application on the machine running the suite (the produced-files restraint).
|
||||
/** Web e2e coverage for unique, ambiguous, and unknown inline file references. */
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
@@ -156,7 +149,7 @@ describe('web e2e: inline-code mentions of produced files', () => {
|
||||
const mentions = page.locator('[class*="markdown"] code button')
|
||||
await expect.poll(() => mentions.count(), { timeout: 10_000 }).toBe(1)
|
||||
expect(await mentions.first().innerText()).toBe('report.html')
|
||||
expect(await mentions.first().getAttribute('aria-label')).toBe('Open site/report.html')
|
||||
expect(await mentions.first().getAttribute('aria-label')).toBe('Open site/report.html in sidebar')
|
||||
expect(await mentions.first().getAttribute('title')).toBe('site/report.html')
|
||||
// The turn still ends with its produced-files row (all three writes).
|
||||
expect(await page.getByText('Files changed', { exact: true }).count()).toBe(1)
|
||||
|
||||
@@ -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-deliverables/README.md
|
||||
README.md: 4f1b853483464415f2e9df2cdf1c982f82eed16e
|
||||
README.zh.md: d2f38a74a60d322d0ea8410beaf4127436983623
|
||||
README.md: ac0e7a4508bdf2b3705491d85837e84f5911d727
|
||||
README.zh.md: f0ce8691051aae4fbf664048c2f6e322db47684a
|
||||
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
This package renders the deliverables row a finished turn ends with — the files the mutation tools created or modified — and links matching inline-code references in the closing prose, so a mentioned file opens in the right Sidebar. The vocabulary comes from the mutation tools' own `locations`, never from the closing prose — a produced file is listed whether or not the model remembered to name it. The shipped Web patch is the only composition that loads this package; removing its cordis.yml entry removes the guidance, row, and prose links together.
|
||||
This package renders the deliverables row a finished turn ends with — the files the mutation tools created or modified — and links matching inline-code references in the closing prose, so a mentioned file opens in the right Sidebar. The linked paths come from successful mutations and explicit deliveries, never from the closing prose — a produced file is listed whether or not the model remembered to name it. The shipped Web patch is the only composition that loads this package; removing its cordis.yml entry removes the guidance, row, and prose links together.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -40,7 +40,7 @@ The “Files changed” row lists successful file-tool mutations; final file del
|
||||
|
||||
### Inline-code links
|
||||
|
||||
The closing prose carries the same vocabulary: an inline-code token resolves by exact path, or by being exactly the basename of exactly one produced path — a basename two paths share stays inert rather than guessing, so a mention can never open the wrong file. A resolved mention keeps its code chip and takes the markdown sheet's link language, with the full path as its title.
|
||||
The closing prose links produced or delivered paths: an inline-code token resolves by exact path, or by being exactly the basename of exactly one such path — a basename two paths share stays inert rather than guessing, so a mention can never open the wrong file. A resolved mention keeps its code chip and takes the markdown sheet's link language, with the full path as its title.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ kind: "package-reference"
|
||||
|
||||
## 概述
|
||||
|
||||
本包渲染已完成轮次末尾的产出文件行——列出修改工具创建或修改的文件——并把收尾正文中匹配的行内代码引用转为链接,让被点名的文件在右侧 Sidebar 中打开。词表来自修改工具自身的 `locations`,而非收尾正文——无论模型是否记得点名,产出文件都会被列出。正式提供的组合中只有 Web patch 加载本包;删除其 cordis.yml 条目会同时移除指引、文件行与正文链接。
|
||||
本包渲染已完成轮次末尾的产出文件行——列出修改工具创建或修改的文件——并把收尾正文中匹配的行内代码引用转为链接,让被点名的文件在右侧 Sidebar 中打开。链接路径来自成功的文件修改与显式交付,而非收尾正文——无论模型是否记得点名,产出文件都会被列出。正式提供的组合中只有 Web patch 加载本包;删除其 cordis.yml 条目会同时移除指引、文件行与正文链接。
|
||||
|
||||
## 目录
|
||||
|
||||
@@ -40,7 +40,7 @@ Web 的 `standard`、`ptc` 与 `cordis` preset 提供 `present` 用于声明交
|
||||
|
||||
### 行内代码链接
|
||||
|
||||
收尾正文承载同一份词表:行内代码 token 按精确路径解析,或当它恰好等于某条产出路径的 basename 且该路径唯一时解析——两条路径共享同一 basename 时保持不可点击而不作猜测,因此提及绝不打开错误的文件。解析成功的提及保留代码标签,并采用 Markdown 样式表的链接样式,完整路径作为其 `title`。
|
||||
收尾正文链接产出或已交付的路径:行内代码 token 按精确路径解析,或当它恰好等于其中某条路径的 basename 且该路径唯一时解析——两条路径共享同一 basename 时保持不可点击而不作猜测,因此提及绝不打开错误的文件。解析成功的提及保留代码标签,并采用 Markdown 样式表的链接样式,完整路径作为其 `title`。
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Deliverables plugin, browser half: registers the produced-files row into
|
||||
* the chat view's turn-tail chain, and provides the `chatFileMentions`
|
||||
* service that links inline-code mentions of produced files in the closing
|
||||
* service that links inline-code mentions of produced or delivered files in the closing
|
||||
* prose. All policy lives here — the supported mutation calls, mention
|
||||
* matching, chip cap, and copy — so
|
||||
* composing this plugin out of cordis.yml removes both surfaces entirely;
|
||||
@@ -66,14 +66,11 @@ export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
const mentions: ChatFileMentions = {
|
||||
forClosing(owner) {
|
||||
// Same claim test the turn-tail chain entry runs: no produced files,
|
||||
// no vocabulary — the two surfaces agree by construction.
|
||||
const paths = selectProducedFiles(owner)
|
||||
const presented = presentedForClosing(owner)
|
||||
if (paths === null && presented.length === 0) return undefined
|
||||
const deliveries = new Set(presented.map(file => file.path))
|
||||
return producedFileMentions([...new Set([...paths ?? [], ...deliveries])], owner.openFile,
|
||||
path => t(deliveries.has(path) ? 'presented.previewButton' : 'produced.open', { name: path }))
|
||||
return producedFileMentions([...new Set([...paths ?? [], ...presented.map(file => file.path)])], owner.openFile,
|
||||
path => t('presented.previewButton', { name: path }))
|
||||
},
|
||||
}
|
||||
ctx.provide('chatFileMentions', mentions)
|
||||
|
||||
@@ -230,12 +230,10 @@ export function presentedForClosing(owner: TurnTailOwnerProps): PresentedPath[]
|
||||
export { basename } from '../presented.ts'
|
||||
|
||||
/**
|
||||
* File-mention vocabulary over one turn's produced paths, for the closing
|
||||
* message's prose: an inline-code token opens the file it names. A token
|
||||
* resolves by exact path, or by being exactly the basename of exactly one
|
||||
* produced path — a basename two paths share stays inert rather than
|
||||
* guessing, so a mention link can never open the wrong file or 404.
|
||||
* @param paths - The turn's produced paths (tool order, already deduped).
|
||||
* Resolves inline-code references against one turn's produced or delivered
|
||||
* paths. Exact paths resolve directly; a basename resolves only when exactly
|
||||
* one supplied path has that basename. Ambiguous and unknown tokens stay inert.
|
||||
* @param paths - The turn's produced or delivered paths, already deduplicated.
|
||||
* @param openFile - The chat view's file opener.
|
||||
* @param label - Localizes the accessible open-label for a resolved path.
|
||||
* @returns The resolver MarkdownText consumes; the full path rides `title`,
|
||||
@@ -255,7 +253,7 @@ export function producedFileMentions(
|
||||
}
|
||||
}
|
||||
|
||||
/** The single produced path whose basename is exactly `value`, else undefined. */
|
||||
/** The single supplied path whose basename is exactly `value`, else undefined. */
|
||||
function onlyPathWithBasename(paths: readonly string[], value: string): string | undefined {
|
||||
const matches = paths.filter(path => basename(path) === value)
|
||||
return matches.length === 1 ? matches[0] : undefined
|
||||
|
||||
@@ -547,6 +547,7 @@ describe('plugin registration', () => {
|
||||
)
|
||||
const service = (ctx as unknown as { get(name: string): ChatFileMentions | undefined }).get('chatFileMentions')
|
||||
const mentions = service?.forClosing(owner, SessionId('viewed-session'))
|
||||
expect(mentions?.resolve('report.html')?.label).toBe('Open site/report.html in sidebar')
|
||||
mentions?.resolve('report.html')?.open()
|
||||
expect(opened).toEqual(['site/report.html'])
|
||||
const fetcher = vi.fn().mockResolvedValue(new Response(null, { status: 204 }))
|
||||
|
||||
Reference in new issue
Block a user