fix(web): align plan preview copy controls with Markdown files
This commit is contained in:
1 parent
0495ad9846
commit
29126c0a32
6 files changed
+11
-28
No files matched your search
@@ -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-plan/README.md
|
||||
README.md: e269ad270ed6a0d72e4cb7424a86328d3eb6cfb3
|
||||
README.zh.md: 202a6737986630f18ffdf7d010174c98be732ba5
|
||||
README.md: f5cec26aff9ec7fc478915c20bca86c06474f9f7
|
||||
README.zh.md: 4bd416b757b7ff6bae2947b84db9f8d50101aa00
|
||||
@@ -33,7 +33,7 @@ While the effective target is plan mode, the seat renders the warn-colored "Plan
|
||||
|
||||
### Reading submitted plans
|
||||
|
||||
When a Turn ends, each submitted plan appears in its final artifact area, using the file-delivery card treatment with a Markdown icon, title, and Open action. A pending plan opens automatically once per submission in the current browser session. Closing it stays effective across review remounts; a new submission opens its own plan. Historical cards open only when clicked. Use the card or review strip’s View full plan link to read and copy the complete Markdown. Plan tabs show a plain text file icon. Different submissions retain separate tabs; the review buttons alone decide whether implementation may begin.
|
||||
When a Turn ends, each submitted plan appears in its final artifact area, using the file-delivery card treatment with a Markdown icon, title, and Open action. A pending plan opens automatically once per submission in the current browser session. Closing it stays effective across review remounts; a new submission opens its own plan. Historical cards open only when clicked. Use the card or review strip’s View full plan link to read the complete Markdown. Plan tabs show a plain text file icon. Different submissions retain separate tabs; the review buttons alone decide whether implementation may begin.
|
||||
|
||||
A review without a logged invocation also opens automatically. Its complete text lives only in the tab’s navigation memory, and the pending review card can reopen it. Reloading the page loses that text; an expired preview directs the user back to a pending review.
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ kind: "package-reference"
|
||||
|
||||
### 查看已提交的计划
|
||||
|
||||
回合结束后,每次提交的计划都会显示在该回合末尾的产物区域,并采用文件产物卡片的样式,包含 Markdown 图标、标题和“打开”操作。在当前浏览器会话中,每份待审计划自动打开一次;关闭后,重新挂载审批组件不会再次展开,新提交会打开其对应计划。历史卡片仅在点击后打开。通过卡片或审批条带上的“查看全文”链接可阅读和复制完整 Markdown。计划标签页显示纯文本文件图标。不同提交保留独立标签页;是否开始实施仍由审批按钮决定。
|
||||
回合结束后,每次提交的计划都会显示在该回合末尾的产物区域,并采用文件产物卡片的样式,包含 Markdown 图标、标题和“打开”操作。在当前浏览器会话中,每份待审计划自动打开一次;关闭后,重新挂载审批组件不会再次展开,新提交会打开其对应计划。历史卡片仅在点击后打开。通过卡片或审批条带上的“查看全文”链接可阅读完整 Markdown。计划标签页显示纯文本文件图标。不同提交保留独立标签页;是否开始实施仍由审批按钮决定。
|
||||
|
||||
没有已记录调用标识的审批也会自动打开。完整正文只保存在标签页的导航内存中,待处理审批卡片可以重新打开它。刷新页面会丢失该正文;失效的预览会提示用户返回待处理审批。
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
:global(body[data-ds-dark-theme]) .card { --plan-card-fill: var(--dsw-static-neutral-850); --plan-card-hover: var(--dsw-static-neutral-800); }
|
||||
.card:hover { background: var(--plan-card-hover); }
|
||||
.card:focus-visible, .iconButton:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: 3px; }
|
||||
.card:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: 3px; }
|
||||
.cardIcon { box-sizing: border-box; flex: none; display: grid; place-items: center; width: 40px; height: 40px; border: 0.5px solid var(--dsw-alias-border-l1); border-radius: 10px; background: var(--plan-card-fill); }
|
||||
.cardDetails { display: flex; flex: 1; flex-direction: column; gap: 2px; min-width: 0; }
|
||||
.cardTitle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 500; line-height: 20px; }
|
||||
@@ -31,10 +31,7 @@
|
||||
.reviewLink { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; cursor: pointer; }
|
||||
.reviewLink:hover { color: var(--dsw-alias-label-primary); }
|
||||
.reviewLink:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: 3px; }
|
||||
.iconButton { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 6px; background: transparent; color: inherit; cursor: pointer; }
|
||||
.iconButton:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
||||
.preview { position: relative; height: 100%; overflow: auto; padding: 20px 24px 40px; box-sizing: border-box; }
|
||||
.toolbar { display: flex; justify-content: flex-end; margin-bottom: 8px; }
|
||||
.document { color: var(--dsw-alias-label-primary); font-size: 14px; line-height: 1.75; overflow-wrap: anywhere; }
|
||||
.message { padding: 24px; color: var(--dsw-alias-label-secondary); font-size: 14px; }
|
||||
.titleIcon { flex: none; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Read-only Markdown viewer for logged plans and temporary review documents. */
|
||||
import { useMemo } from 'react'
|
||||
import { FileTypeIcon, IconCopyOutline16, MarkdownText, writeClipboard } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import { FileTypeIcon, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
|
||||
import type {} from './plan-resource.ts'
|
||||
@@ -11,7 +11,7 @@ import css from './PlanPreview.module.css'
|
||||
type PlanPreviewProps = PropsRuntime<'sidebar.right.pane.tab'> & PropsLocale<'plan'>
|
||||
|
||||
/**
|
||||
* Render the submitted plan with its complete Markdown and a copy action.
|
||||
* Render the submitted plan with its complete Markdown.
|
||||
* @param props - Framework-bound tab identity, resource, and copy.
|
||||
* @returns the plan document or a localized loading/failure state.
|
||||
*/
|
||||
@@ -34,8 +34,6 @@ export function PlanPreview({ useTabInfo, useResource, t }: PlanPreviewProps) {
|
||||
)
|
||||
return (
|
||||
<section className={css.preview} data-plan-preview={'callId' in plan ? plan.callId : tab.tab.navigation.address} aria-label={plan.title}>
|
||||
<div className={css.toolbar}><button type="button" className={css.iconButton} aria-label={t('copy')}
|
||||
onClick={() => { void writeClipboard(plan.markdown) }}><IconCopyOutline16 /></button></div>
|
||||
<div className={css.document}><MarkdownText text={plan.markdown} labels={labels} /></div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -223,22 +223,10 @@ describe('plan entry points and document', () => {
|
||||
render(<PlanTitle {...props as unknown as Parameters<typeof PlanTitle>[0]} />)
|
||||
expect(screen.getByText(plan.title)).toBeTruthy()
|
||||
})
|
||||
it('copies the complete Markdown and keeps a tab label while history loads', async () => {
|
||||
const writeText = vi.fn().mockResolvedValue(undefined)
|
||||
const clipboard = Object.getOwnPropertyDescriptor(navigator, 'clipboard')
|
||||
Object.defineProperty(navigator, 'clipboard', { configurable: true, value: { writeText } })
|
||||
try {
|
||||
const props = { t, useTabInfo: () => ({ tab: { title: 'Plan', navigation: { address: planAddress(target) } } }), useResource: () => ({ status: 'live', value: plan }) }
|
||||
const view = render(<PlanPreview {...props as unknown as Parameters<typeof PlanPreview>[0]} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Copy' }))
|
||||
await Promise.resolve()
|
||||
expect(writeText).toHaveBeenCalledWith(markdown)
|
||||
view.rerender(<PlanTitle {...{ ...props, useResource: () => ({ status: 'loading' }) } as unknown as Parameters<typeof PlanTitle>[0]} />)
|
||||
expect(screen.getByText('Plan')).toBeTruthy()
|
||||
} finally {
|
||||
if (clipboard === undefined) Reflect.deleteProperty(navigator, 'clipboard')
|
||||
else Object.defineProperty(navigator, 'clipboard', clipboard)
|
||||
}
|
||||
it('keeps a tab label while history loads', () => {
|
||||
const props = { useTabInfo: () => ({ tab: { title: 'Plan', navigation: { address: planAddress(target) } } }), useResource: () => ({ status: 'loading' }) }
|
||||
render(<PlanTitle {...props as unknown as Parameters<typeof PlanTitle>[0]} />)
|
||||
expect(screen.getByText('Plan')).toBeTruthy()
|
||||
})
|
||||
it.each([en, zh])('localizes plan failures and unavailable providers', (dictionary) => {
|
||||
const props = { t: makeTranslate(dictionary, commonEn),
|
||||
|
||||
Reference in new issue
Block a user