From 047ea509873aa2b891deb701924c19d7b35ec107 Mon Sep 17 00:00:00 2001
From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com>
Date: Tue, 4 Aug 2026 01:46:52 -0700
Subject: [PATCH 1/4] feat(web): add preview badge to empty hero
---
.../lifecycle-chrome/hero.expected.md | 2 +-
.../lifecycle-chrome/plan-active.expected.md | 2 +-
.../ui-conversation/src/client/locales.ts | 2 ++
.../src/client/skeleton/EmptyHero.tsx | 3 +-
.../src/client/skeleton/HeroShell.module.css | 31 ++++++++++++++++---
.../ui-conversation/tests/skeleton.spec.tsx | 11 ++++++-
6 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/apps/web/tests/snapshots/lifecycle-chrome/hero.expected.md b/apps/web/tests/snapshots/lifecycle-chrome/hero.expected.md
index 8611ac5c0d..bdb07876a3 100644
--- a/apps/web/tests/snapshots/lifecycle-chrome/hero.expected.md
+++ b/apps/web/tests/snapshots/lifecycle-chrome/hero.expected.md
@@ -20,7 +20,7 @@
- button "Settings":
- img
- text: Settings
-- text: Let's start building
+- text: Let's start building Preview
- button "Choose workspace":
- img
- text: workspace
diff --git a/apps/web/tests/snapshots/lifecycle-chrome/plan-active.expected.md b/apps/web/tests/snapshots/lifecycle-chrome/plan-active.expected.md
index a9fb7901d7..8c5cf915dc 100644
--- a/apps/web/tests/snapshots/lifecycle-chrome/plan-active.expected.md
+++ b/apps/web/tests/snapshots/lifecycle-chrome/plan-active.expected.md
@@ -20,7 +20,7 @@
- button "Settings":
- img
- text: Settings
-- text: Let's start building
+- text: Let's start building Preview
- button "Choose workspace":
- img
- text: workspace
diff --git a/packages/client/ui-conversation/src/client/locales.ts b/packages/client/ui-conversation/src/client/locales.ts
index 7a38aaa0f8..2f05223e5d 100644
--- a/packages/client/ui-conversation/src/client/locales.ts
+++ b/packages/client/ui-conversation/src/client/locales.ts
@@ -33,6 +33,7 @@ export const zh = {
'access.confirm.cancel': '取消',
'access.confirm.enable': '启用 Full access',
'hero.headline': '开始构建吧',
+ 'hero.preview': '预览版',
'hero.chooseWorkspace': '选择工作区',
'session.hierarchy': '会话层级',
'details.title': '详情',
@@ -144,6 +145,7 @@ export const en = {
'access.confirm.cancel': 'Cancel',
'access.confirm.enable': 'Enable Full access',
'hero.headline': 'Let\'s start building',
+ 'hero.preview': 'Preview',
'hero.chooseWorkspace': 'Choose workspace',
'session.hierarchy': 'Session hierarchy',
'details.title': 'Details',
diff --git a/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx b/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx
index 0c1b31bbb7..4b491e1f7f 100644
--- a/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx
+++ b/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx
@@ -119,7 +119,8 @@ export function HeroShell({ t, children }: HeroShellProps) {
{/* figma 34:10412: fish 34×25 leading the headline, gap 10. */}
- {t('hero.headline')}
+ {t('hero.headline')}
+ {t('hero.preview')}
{/* The resident composer (ConversationRoot wrapActiveBody seat; the
diff --git a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
index 523f640c0b..a95007e381 100644
--- a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
+++ b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
@@ -23,21 +23,44 @@
overflow: visible;
}
-/* figma 34:10411: fish + title, gap 10, centered; 26/32 wt500. */
+/* Fish + title stay centered as one unit; the preview badge aligns with the
+ title's leading edge on the second row. */
.headline {
- display: flex;
+ display: grid;
+ grid-template-columns: 34px auto;
+ column-gap: 10px;
+ row-gap: 4px;
align-items: center;
justify-content: center;
- gap: 10px;
font-size: 26px;
line-height: 32px;
font-weight: 500;
color: var(--dsw-alias-label-primary);
}
+.headlineText {
+ grid-row: 1;
+ grid-column: 2;
+}
+
+.previewBadge {
+ grid-row: 2;
+ grid-column: 2;
+ justify-self: start;
+ padding: 0 4px;
+ border-radius: 4px;
+ background: var(--dsw-alias-state-business-tertiary);
+ color: var(--dsw-alias-state-business-primary);
+ font-size: 12px;
+ line-height: 18px;
+ font-weight: 500;
+ white-space: nowrap;
+}
+
/* figma fish fill rides business blue. */
.fish {
- flex: none;
+ grid-row: 1;
+ grid-column: 1;
color: var(--dsw-alias-state-business-primary);
}
diff --git a/packages/client/ui-conversation/tests/skeleton.spec.tsx b/packages/client/ui-conversation/tests/skeleton.spec.tsx
index 312ec88642..506984bca4 100644
--- a/packages/client/ui-conversation/tests/skeleton.spec.tsx
+++ b/packages/client/ui-conversation/tests/skeleton.spec.tsx
@@ -12,12 +12,14 @@ import type {
import type { ConversationRootProps } from '../src/client/skeleton/ConversationRoot.tsx'
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
+import { en as commonEn } from '@deepseek-ai/dsh-client-locale/src/locales/en.ts'
import { zh as commonZh } from '@deepseek-ai/dsh-client-locale/src/locales/zh.ts'
import { createChatStore } from '../src/client/stores.ts'
import { SessionInputShell } from '../src/client/input/facade.ts'
-import { zh } from '../src/client/locales.ts'
+import { en, zh } from '../src/client/locales.ts'
import { ConversationRoot } from '../src/client/skeleton/ConversationRoot.tsx'
import { ConversationSession } from '../src/client/skeleton/ConversationSession.tsx'
+import { HeroShell } from '../src/client/skeleton/EmptyHero.tsx'
import { InputBar } from '../src/client/skeleton/InputBar.tsx'
import type { InputBarProps } from '../src/client/skeleton/InputBar.tsx'
import type {
@@ -214,6 +216,12 @@ function mount(
}
describe('ConversationRoot resident composer', () => {
+ it('renders the English preview badge through the hero locale seat', () => {
+ const view = render()
+ expect(view.getByText('Let\'s start building')).toBeTruthy()
+ expect(view.getByText('Preview')).toBeTruthy()
+ })
+
it('keeps composer text in the machine, mirrors to the chat store, and submits through the sink', () => {
const b = mount(conversationSnapshot())
const box = b.view.getByRole('textbox')
@@ -273,6 +281,7 @@ describe('ConversationRoot resident composer', () => {
expect(host).not.toBeNull()
expect(header?.getAttribute('aria-hidden')).toBe('true')
expect(b.view.getByText('开始构建吧')).toBeTruthy()
+ expect(b.view.getByText('预览版')).toBeTruthy()
expect(b.view.queryByTestId('view-chat')).toBeNull()
// The same machine-backed textarea is live in the hero, and the
// persistence mirror stays bound (ConversationSession mounts chrome-hidden
From 7c1ae32e5574b2963e65f0101ddf0c22bcc3b8af Mon Sep 17 00:00:00 2001
From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com>
Date: Wed, 5 Aug 2026 23:26:39 -0700
Subject: [PATCH 2/4] fix(web): address preview badge review
---
...-08-05-web-preview-product-badge.i18n.yaml | 6 ++++
.../2026-08-05-web-preview-product-badge.md | 33 +++++++++++++++++++
...2026-08-05-web-preview-product-badge.zh.md | 33 +++++++++++++++++++
.../src/client/skeleton/HeroShell.module.css | 6 ++--
.../ui-conversation/tests/skeleton.spec.tsx | 4 ++-
5 files changed, 78 insertions(+), 4 deletions(-)
create mode 100644 .agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.i18n.yaml
create mode 100644 .agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.md
create mode 100644 .agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.zh.md
diff --git a/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.i18n.yaml b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.i18n.yaml
new file mode 100644
index 0000000000..90d3d8e1f5
--- /dev/null
+++ b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.i18n.yaml
@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# 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 .agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.md
+2026-08-05-web-preview-product-badge.md: c20dedf8caa497d17a577cf46261c6a24c09a1ce
+2026-08-05-web-preview-product-badge.zh.md: c428dabf2a9a2ece2f90b0c3a527835a494c7cb7
diff --git a/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.md b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.md
new file mode 100644
index 0000000000..c20dedf8ca
--- /dev/null
+++ b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.md
@@ -0,0 +1,33 @@
+# Agent Note: Web preview product badge
+
+Status: implemented
+
+English | [中文](2026-08-05-web-preview-product-badge.zh.md)
+
+## Problem
+
+The Web empty state does not identify the product as a preview. Users can enter the main session surface without seeing that the product is pre-release, while a deployment setting would misrepresent a product-wide lifecycle decision as an operator choice.
+
+## Decision
+
+The empty hero always renders a localized `Preview` / `预览版` badge beneath the headline. It has no configuration switch: preview status is one product identity shared by every deployment, not a deployment-varying tunable.
+
+The badge keeps the business-tertiary background so both themes retain the product-blue context, and uses the theme's primary label token for text. That pairing gives ordinary 12px text sufficient contrast in both light and dark themes; the business-primary foreground is reserved for larger or non-text accents because it does not reach the required contrast on this background.
+
+The badge leaves the product when the first tagged release removes the repository's pre-release stance, or when the owning product decision declares the preview phase complete. That change removes the badge and its locale key together rather than adding a runtime toggle.
+
+## Alternatives considered
+
+**Make preview status configurable.** Rejected because two deployments of the same pre-release product must not present different lifecycle identities, and a configuration field would turn product release state into an unsupported operator choice.
+
+**Use business-primary text on the business-tertiary background.** Rejected because the resulting light- and dark-theme contrast is below the 4.5:1 requirement for the badge's 12px text.
+
+**Hide the badge from the accessibility tree.** Rejected because preview status is product information rather than decoration; the accessible headline therefore includes the badge text.
+
+## Consequences
+
+Every new session exposes the same localized preview identity in visual and accessibility output. Removing preview status is an explicit product-release edit, and the badge favors readable neutral text over an all-blue treatment while retaining the business-tinted background.
+
+## Testing
+
+The conversation component test covers both localized badge values, and the Web lifecycle snapshots pin the English badge in the assembled empty hero.
diff --git a/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.zh.md b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.zh.md
new file mode 100644
index 0000000000..c428dabf2a
--- /dev/null
+++ b/.agents/notes/implemented/feature/2026-08-05-web-preview-product-badge.zh.md
@@ -0,0 +1,33 @@
+# Agent Note:Web 预览版产品徽标
+
+状态:已实现
+
+[English](2026-08-05-web-preview-product-badge.md) | 中文
+
+## 问题
+
+Web 空状态没有标明产品处于预览版阶段。用户可以在未看到产品尚未正式发布的情况下进入主会话界面;若改用部署设置,则会把面向整个产品的生命周期决策误表述为操作者的选择。
+
+## 决策
+
+空状态主视觉区始终在标题下方渲染本地化的 `Preview` / `预览版` 徽标。它没有配置开关:预览状态是所有部署共同的一项产品身份,而不是随部署变化的可调参数。
+
+徽标沿用 business-tertiary 背景,使两套主题都保留产品蓝的视觉语境;文字则使用主题的 primary label token。这一组合让普通 12px 文字在浅色与暗色主题下都有足够的对比度。business-primary 前景色仅留给较大字号文本或非文本强调元素,因为它在该背景上达不到要求的对比度。
+
+首个 tagged release 取消仓库的预发布立场时,或归属产品方明确决定预览阶段结束时,产品会移除该徽标。这一改动会同时移除徽标及其 locale key,而不是增加运行时开关。
+
+## 曾考虑的替代方案
+
+**让预览状态可配置。** 不予采纳:同一个预发布产品的两套部署不得展示不同的生命周期身份,配置字段还会把产品发布状态变成一项不受支持的操作者选择。
+
+**在 business-tertiary 背景上使用 business-primary 文字。** 不予采纳:由此产生的浅色与暗色主题对比度低于徽标 12px 文字所要求的 4.5:1。
+
+**在无障碍树中隐藏徽标。** 不予采纳:预览状态是产品信息而非装饰,因此无障碍标题会包含徽标文字。
+
+## 后果
+
+每个新会话都会在视觉与无障碍输出中呈现相同的本地化预览版身份。移除预览状态是一项显式的产品发布改动;徽标保留业务蓝色调背景,同时采用可读的中性色文字,而不是全蓝色处理方案。
+
+## 测试
+
+会话组件测试覆盖两个本地化徽标值,Web 生命周期快照则固定组装后空状态主视觉区中的英文徽标。
diff --git a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
index a95007e381..3166a81565 100644
--- a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
+++ b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css
@@ -23,8 +23,8 @@
overflow: visible;
}
-/* Fish + title stay centered as one unit; the preview badge aligns with the
- title's leading edge on the second row. */
+/* figma 34:10411: fish + title, gap 10, centered; 26/32 wt500. The preview
+ badge is a product addition outside that source and aligns to the title. */
.headline {
display: grid;
grid-template-columns: 34px auto;
@@ -50,7 +50,7 @@
padding: 0 4px;
border-radius: 4px;
background: var(--dsw-alias-state-business-tertiary);
- color: var(--dsw-alias-state-business-primary);
+ color: var(--dsw-alias-label-primary);
font-size: 12px;
line-height: 18px;
font-weight: 500;
diff --git a/packages/client/ui-conversation/tests/skeleton.spec.tsx b/packages/client/ui-conversation/tests/skeleton.spec.tsx
index 506984bca4..9f793a8c9b 100644
--- a/packages/client/ui-conversation/tests/skeleton.spec.tsx
+++ b/packages/client/ui-conversation/tests/skeleton.spec.tsx
@@ -215,13 +215,15 @@ function mount(
}
}
-describe('ConversationRoot resident composer', () => {
+describe('Hero chrome', () => {
it('renders the English preview badge through the hero locale seat', () => {
const view = render()
expect(view.getByText('Let\'s start building')).toBeTruthy()
expect(view.getByText('Preview')).toBeTruthy()
})
+})
+describe('ConversationRoot resident composer', () => {
it('keeps composer text in the machine, mirrors to the chat store, and submits through the sink', () => {
const b = mount(conversationSnapshot())
const box = b.view.getByRole('textbox')
From 3c09b86a34cc24f68905862951e55a7fc8162d9a Mon Sep 17 00:00:00 2001
From: imccyu
Date: Thu, 6 Aug 2026 14:39:13 +0800
Subject: [PATCH 3/4] fix: test
---
apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md | 2 +-
.../tests/snapshots/markdown-inline-code-links/ui.expected.md | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md b/apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md
index 68a4df5603..187ab25e8c 100644
--- a/apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md
+++ b/apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md
@@ -40,7 +40,7 @@
- img
- button "Branch into a new conversation":
- img
-- text: {{clock}}Ran for {{duration}}
+- text: {{clock}} Ran for {{duration}}
- textbox "Message the agent"
- button "Commands":
- img
diff --git a/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md b/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
index 059849223c..71851363d2 100644
--- a/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
+++ b/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
@@ -31,7 +31,7 @@
- img
- button "Branch into a new conversation":
- img
-- text: {{clock}}Ran for {{duration}}
+- text: {{clock}} Ran for {{duration}}
- textbox "Message the agent"
- button "Commands":
- img
@@ -40,4 +40,4 @@
- text: Select model
- img
- button "Send message" [disabled]
-- text: 1 turns · 1 steps Input 0 tok · Output 0 tok
+- text: 1 turns · 1 steps LLM {{duration}} Input 0 tok · Output 0 tok
From a8822494d28abca51f5e3c31f893fbd4ecbc836b Mon Sep 17 00:00:00 2001
From: imccyu
Date: Thu, 6 Aug 2026 14:50:55 +0800
Subject: [PATCH 4/4] fix: test
---
.../tests/snapshots/markdown-inline-code-links/ui.expected.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md b/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
index 71851363d2..19efa06238 100644
--- a/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
+++ b/apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md
@@ -40,4 +40,4 @@
- text: Select model
- img
- button "Send message" [disabled]
-- text: 1 turns · 1 steps LLM {{duration}} Input 0 tok · Output 0 tok
+- text: 1 turns · 1 steps Input 0 tok · Output 0 tok