copy(tui): disclose Session Log upload in Chinese welcome

This commit is contained in:
NI0317
2026-07-31 15:15:14 +08:00
parent e73fa2ec9f
commit dbb87f02bf
15 changed files with 173 additions and 195 deletions

View File

@@ -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 apps/cli/README.md
README.md: e01989d18d7a1567ea0d62628d207bdebd8e27e1
README.zh.md: 4df2b0911059c24baa5f6bec315e7fdf523213f7
README.md: 2f7238b3c05f6b76288c5e26b14efd2e64485677
README.zh.md: af4004be225307fc3863bf6f227ef95f7ca0c14f

View File

@@ -12,7 +12,7 @@ The TUI surface:
- treats the **invoking directory** as the workspace — sessions, relative paths, and workspace instructions resolve from the cwd (`dsh meta` is the sole exception, below);
- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it;
- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`.
- presents the [versioned first-run welcome](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, bilingual copy, and notice version are static local owners; the overlay never writes a session event or model context.
- presents the [versioned first-run welcome](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context.
`dsh meta` is that same TUI with this harness checkout as the workspace, so working on dsh itself needs no `cd`. It chdirs to the checkout root — resolved from the launcher's real path, the same root the source-path prompt section names — after the environment is settled, so precedence is unchanged while the session cwd and HMR watch root move together. Meta always starts a fresh session and accepts no default-surface options; use ordinary `dsh --resume <id>` to resume a persisted session.

View File

@@ -12,7 +12,7 @@ TUI 界面:
-**调用目录** 视为 workspace会话、相对路径和 workspace 指令都从 cwd 解析(`dsh meta` 是唯一例外,见下文);
- 告知 agent 自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它;
- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../packages/ui/app-boot/README.md#personal-config)`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据 provider 自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`
-`DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、双语文案和通知版本均由静态本地文件持有overlay 不会写入会话事件或模型上下文。
-`DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有overlay 不会写入会话事件或模型上下文。
`dsh meta` 是以本 harness checkout 为 workspace 的同一个 TUI因此开发 dsh 自身无需 `cd`。它在环境确定之后才 chdir 到 checkout 根目录(从启动器的真实路径解析,与源码路径提示词段所指的根目录相同),因此环境优先级不变,而会话 cwd 与 HMR 监视根目录会一并移动。Meta 始终创建新会话,不接受默认界面的任何选项;恢复已持久化会话应使用普通的 `dsh --resume <id>`

View File

@@ -1,5 +1,5 @@
/**
* Centrally owned version and bilingual copy for the shipped TUI first-run notice.
* Centrally owned version and all-locale Chinese copy for the shipped TUI first-run notice.
*
* A material wording change increments {@link TUI_FIRST_RUN_WELCOME_NOTICE_VERSION}
* so every Harness home presents the revised notice once.
@@ -7,7 +7,7 @@
*/
/** Copy version persisted after the user explicitly continues. */
export const TUI_FIRST_RUN_WELCOME_NOTICE_VERSION = 2
export const TUI_FIRST_RUN_WELCOME_NOTICE_VERSION = 3
/** Locale-shaped text rendered by the first-run welcome overlay. */
export interface TuiFirstRunWelcomeNoticeCopy {
@@ -25,37 +25,24 @@ export interface TuiFirstRunWelcomeNoticeCopy {
readonly saveError: string
}
/**
* Complete notice copy. The shipped TUI currently presents the supplied
* Simplified Chinese locale; English remains its reviewed locale counterpart.
*/
/** Complete Chinese notice used for every locale. */
const TUI_FIRST_RUN_WELCOME_CHINESE_COPY = Object.freeze<TuiFirstRunWelcomeNoticeCopy>({
title: 'DeepSeek Harness',
paragraphs: Object.freeze([
'感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段功能仍待完善体验难免有些粗糙。',
'“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。',
'为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log如需关闭可以【关闭方式待补充】。另外如果您有任何反馈与建议请在企业微信群中留言告诉我们。每一条反馈都会帮助我们把它打磨得更好。',
]),
continueLabel: '继续',
scrollHint: '↑/↓ 滚动',
saving: '正在保存确认…',
saveError: '无法保存确认,请按 Enter 重试。',
})
/** Locale map whose entries deliberately share the single Chinese owner copy. */
export const TUI_FIRST_RUN_WELCOME_NOTICE_COPY = Object.freeze({
'zh-CN': Object.freeze<TuiFirstRunWelcomeNoticeCopy>({
title: 'DeepSeek Harness',
paragraphs: Object.freeze([
'感谢您愿意拨冗试用 DeepSeek Harness。',
'目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。',
'“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。',
'我们尤其希望听见那些失败、困惑与不顺手的时刻——如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。',
]),
continueLabel: '继续',
scrollHint: '↑/↓ 滚动',
saving: '正在保存确认…',
saveError: '无法保存确认,请按 Enter 重试。',
}),
en: Object.freeze<TuiFirstRunWelcomeNoticeCopy>({
title: 'DeepSeek Harness',
paragraphs: Object.freeze([
'Thank you for taking the time to try DeepSeek Harness.',
'This release is still in internal testing. Features remain unfinished, and the experience will inevitably feel somewhat rough.',
'“As one cuts and files, as one carves and polishes.” A product grows through real encounters and candid feedback. Problems you discover in real use may lead us to re-examine, or even overturn, existing designs.',
'We especially want to hear about moments of failure, confusion, and friction. If you have any feedback or suggestions, please leave a message in the company WeChat group and let us know. Every piece of feedback helps us refine it.',
]),
continueLabel: 'Continue',
scrollHint: '↑/↓ Scroll',
saving: 'Saving acknowledgement…',
saveError: 'Could not save the acknowledgement. Press Enter to retry.',
}),
'zh-CN': TUI_FIRST_RUN_WELCOME_CHINESE_COPY,
en: TUI_FIRST_RUN_WELCOME_CHINESE_COPY,
})
/** Locale presented by the shipped first-run notice. */

View File

@@ -169,8 +169,8 @@ export function tuiFirstRunWelcomeArtTier(
viewportRows: number,
): TuiFirstRunWelcomeArtTier | undefined {
if (innerWidth >= 96 && viewportRows >= 23) return 'full'
if (innerWidth >= 64 && viewportRows >= 18) return 'compact'
if (innerWidth >= 48 && viewportRows >= 14) return 'minimal'
if (innerWidth >= 80 && viewportRows >= 34) return 'compact'
if (innerWidth >= 64 && viewportRows >= 14) return 'minimal'
return undefined
}

View File

@@ -10,52 +10,52 @@ overlay 120x30 rows=20
style 0-0 dim
style 9-38 fg=blue
style 119-119 dim
3| "│ ▄███████████████▄ ████▄ ▄▄▄▄██ 感谢您愿意拨冗试用 DeepSeek Harness。 │"
3| "│ ▄███████████████▄ ████▄ ▄▄▄▄██ 感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能 │"
style 0-0 dim
style 4-43 fg=blue
style 119-119 dim
4| "│ ▄███████████████████▄ ████████████▀ │"
4| "│ ▄███████████████████▄ ████████████▀ 仍待完善,体验难免有些粗糙。 │"
style 0-0 dim
style 4-43 fg=blue
style 119-119 dim
5| "│ ▄██████████████████████▄ ▀█████████▀ 目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 │"
5| "│ ▄██████████████████████▄ ▀█████████▀ │"
style 0-0 dim
style 4-42 fg=blue
style 119-119 dim
6| "│ ▄███▀█████████████████████▄ ████▀▀ │"
6| "│ ▄███▀█████████████████████▄ ████▀▀ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 6-41 fg=blue
style 119-119 dim
7| "│ ███ ▀▀█████████▀▀▀█████████▀ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 7-40 fg=blue
style 49-70 bold
style 119-119 dim
8| "│ ███ ▀███████▀▀███████ │"
7| "│ ███ ▀█████████▀▀▀█████████▀ │"
style 0-0 dim
style 7-40 fg=blue
style 119-119 dim
8| "│ ███ ▀███████▀█ ▀███████ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的 │"
style 0-0 dim
style 7-39 fg=blue
style 119-119 dim
9| "│ ███▄ ▀███████▄ ▀█████▀ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的 │"
9| "│ ███▄ ▀███████▄ ▀█████▀ 问题,也可能促使我们重新审视,甚至推翻已有的设计。 │"
style 0-0 dim
style 7-39 fg=blue
style 119-119 dim
10| "│ ▀███ ▀██████████████ 问题,也可能促使我们重新审视,甚至推翻已有的设计。 │"
10| "│ ▀███ ▀██████████████ │"
style 0-0 dim
style 8-39 fg=blue
style 119-119 dim
11| "│ ▀███▄ ▀███████████▀ │"
11| "│ ▀███▄ ▀███████████▀ 为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 │"
style 0-0 dim
style 8-38 fg=blue
style 119-119 dim
12| "│ ▀███▄ ▄▄▄ ▀████████▀ 我们尤其希望听见那些失败、困惑与不顺手的时刻——如果您有任何反馈与建议 │"
12| "│ ▀███▄ ▄▄▄ ▀████████▀ Session Log如需关闭可以【关闭方式待补充】。另外如果您有任何反馈 │"
style 0-0 dim
style 9-38 fg=blue
style 119-119 dim
13| "│ █████▄ ███▄▄ ▀█████▄▄ ,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更 │"
13| "│ █████▄ ███▄▄ ▀█████▄▄ 与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打 │"
style 0-0 dim
style 9-38 fg=blue
style 119-119 dim
14| "│ ▀█████████████▄▄▄▄█▀█████▀ 好。 │"
14| "│ ▀█████████████▄▄▄▄█▀█████▀ 磨得更好。 │"
style 0-0 dim
style 8-39 fg=blue
style 119-119 dim

View File

@@ -10,7 +10,7 @@ overlay 160x30 rows=20
style 0-0 dim
style 9-38 fg=blue
style 159-159 dim
3| "│ ▄███████████████▄ ████▄ ▄▄▄▄██ 感谢您愿意拨冗试用 DeepSeek Harness。 │"
3| "│ ▄███████████████▄ ████▄ ▄▄▄▄██ 感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 │"
style 0-0 dim
style 4-43 fg=blue
style 159-159 dim
@@ -18,40 +18,40 @@ overlay 160x30 rows=20
style 0-0 dim
style 4-43 fg=blue
style 159-159 dim
5| "│ ▄██████████████████████▄ ▀█████████▀ 目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 │"
5| "│ ▄██████████████████████▄ ▀█████████▀ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 4-42 fg=blue
style 49-70 bold
style 159-159 dim
6| "│ ▄███▀█████████████████████▄ ████▀▀ │"
style 0-0 dim
style 6-41 fg=blue
style 159-159 dim
7| "│ ███ ▀▀█████████▀▀▀█████████▀ “如切如磋,如琢如磨。” │"
7| "│ ███ ▀▀█████████▀▀▀█████████▀ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已 │"
style 0-0 dim
style 7-40 fg=blue
style 49-70 bold
style 159-159 dim
8| "│ ███ ▀███████▀█ ▀███████ │"
8| "│ ███ ▀███████▀█ ▀███████ 有的设计。 │"
style 0-0 dim
style 7-39 fg=blue
style 159-159 dim
9| "│ ███▄ ▀███████▄ ▀█████▀ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已 │"
9| "│ ███▄ ▀███████▄ ▀█████▀ │"
style 0-0 dim
style 7-39 fg=blue
style 159-159 dim
10| "│ ▀███ ▀██████████████ 有的设计。 │"
10| "│ ▀███ ▀██████████████ 为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log如需关闭可以【关闭方式待补 │"
style 0-0 dim
style 8-39 fg=blue
style 159-159 dim
11| "│ ▀███▄ ▀███████████▀ │"
11| "│ ▀███▄ ▀███████████▀ 充】。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 │"
style 0-0 dim
style 8-38 fg=blue
style 159-159 dim
12| "│ ▀███▄ ▄▄▄ ▀████████▀ 我们尤其希望听见那些失败、困惑与不顺手的时刻——如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反 │"
12| "│ ▀███▄ ▄▄▄ ▀████████▀ │"
style 0-0 dim
style 9-38 fg=blue
style 159-159 dim
13| "│ █████▄ ███▄▄ ▀█████▄▄ 馈,都会帮助我们把它打磨得更好。 │"
13| "│ █████▄ ███▄▄ ▀█████▄▄ │"
style 0-0 dim
style 9-38 fg=blue
style 159-159 dim

View File

@@ -8,13 +8,13 @@ overlay 60x12 rows=10
2| "│ │"
style 0-0 dim
style 59-59 dim
3| "│ 感谢您愿意拨冗试用 DeepSeek Harness。 │"
3| "│ 感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部 │"
style 0-0 dim
style 59-59 dim
4| "│ │"
4| "│ 测试阶段,功能仍待完善,体验难免有些粗糙。 │"
style 0-0 dim
style 59-59 dim
5| "│ 目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些 │"
5| "│ │"
style 0-0 dim
style 59-59 dim
6| "├──────────────────────────────────────────────────────────┤"

View File

@@ -1,90 +1,61 @@
overlay 60x30 rows=27
overlay 60x30 rows=20
0| "╭──────────────────────────────────────────────────────────╮"
style 0-59 dim
1| "│ ▄▄▄▄▄▄ ▄▄ │"
style 0-0 dim
style 23-36 fg=blue
style 59-59 dim
2| "│ ▄████████▄ ▀████▀ │"
style 0-0 dim
style 21-38 fg=blue
style 59-59 dim
3| "│ █▀▀▀▀███████▄██▀ │"
style 0-0 dim
style 22-37 fg=blue
style 59-59 dim
4| "│ █▄ ▀███ ▀███ │"
style 0-0 dim
style 22-36 fg=blue
style 59-59 dim
5| "│ ▀█▄ ▀█████ │"
style 0-0 dim
style 23-36 fg=blue
style 59-59 dim
6| "│ ▀█▄▄ █▄▄▀███▄ │"
style 0-0 dim
style 23-36 fg=blue
style 59-59 dim
7| "│ ▀▀▀▀▀▀ │"
style 0-0 dim
style 25-34 fg=blue
style 59-59 dim
8| "│ │"
style 0-0 dim
style 59-59 dim
9| "│ DeepSeek Harness │"
1| "│ DeepSeek Harness │"
style 0-0 dim
style 22-37 fg=blue bold
style 59-59 dim
10| "│ │"
2| "│ │"
style 0-0 dim
style 59-59 dim
11| "│ 感谢您愿意拨冗试用 DeepSeek Harness。 │"
3| "│ 感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部 │"
style 0-0 dim
style 59-59 dim
12| "│ │"
4| "│ 测试阶段,功能仍待完善,体验难免有些粗糙。 │"
style 0-0 dim
style 59-59 dim
13| "│ 目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些 │"
5| "│ │"
style 0-0 dim
style 59-59 dim
14| "│ 粗糙。 │"
style 0-0 dim
style 59-59 dim
15| "│ │"
style 0-0 dim
style 59-59 dim
16| "│ “如切如磋,如琢如磨。” │"
6| "│ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 2-23 bold
style 59-59 dim
17| "│ │"
7| "│ │"
style 0-0 dim
style 59-59 dim
18| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实 │"
8| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实 │"
style 0-0 dim
style 59-59 dim
19| "│ 使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的 │"
9| "│ 使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的 │"
style 0-0 dim
style 59-59 dim
20| "│ 设计。 │"
10| "│ 设计。 │"
style 0-0 dim
style 59-59 dim
21| "│ │"
11| "│ │"
style 0-0 dim
style 59-59 dim
22| "│ 我们尤其希望听见那些失败、困惑与不顺手的时刻——如果您有任 │"
12| "│ 为了帮助我们更准确地还原您真实使用中的问题,内测版本默认 │"
style 0-0 dim
style 59-59 dim
23| "├──────────────────────────────────────────────────────────┤"
13| "│ 会上传所有 Session Log如需关闭可以【关闭方式待补充】 │"
style 0-0 dim
style 59-59 dim
14| "│ 。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉 │"
style 0-0 dim
style 59-59 dim
15| "│ 我们。每一条反馈,都会帮助我们把它打磨得更好。 │"
style 0-0 dim
style 59-59 dim
16| "├──────────────────────────────────────────────────────────┤"
style 0-59 dim
24| "│ Enter 继续 │"
17| "│ Enter 继续 │"
style 0-0 dim
style 24-34 fg=bright-magenta bold
style 59-59 dim
25| "│ ↑/↓ 滚动 ↓ │"
18| "│ │"
style 0-0 dim
style 24-35 dim
style 59-59 dim
26| "╰──────────────────────────────────────────────────────────╯"
19| "╰──────────────────────────────────────────────────────────╯"
style 0-59 dim

View File

@@ -1,93 +1,86 @@
overlay 80x30 rows=27
overlay 80x30 rows=26
0| "╭──────────────────────────────────────────────────────────────────────────────╮"
style 0-79 dim
1| "│ ▄▄▄▄▄▄▄██▀ █▄ │"
1| "│ ▄▄▄▄▄▄ ▄▄ │"
style 0-0 dim
style 26-53 fg=blue
style 33-46 fg=blue
style 79-79 dim
2| "│ ▄███████████▄▄ ███▄▄████ │"
2| "│ ▄████████▄ ▀████▀ │"
style 0-0 dim
style 26-53 fg=blue
style 31-48 fg=blue
style 79-79 dim
3| "│ ████████████████▄ ▀██████▀ │"
3| "│ █▀▀▀▀███████▄██▀ │"
style 0-0 dim
style 26-52 fg=blue
style 32-47 fg=blue
style 79-79 dim
4| "│ ██▀▀▀▀▀████████████▄▄██▀ │"
4| "│ █▄ ▀███ ▀███ │"
style 0-0 dim
style 28-51 fg=blue
style 32-46 fg=blue
style 79-79 dim
5| "│ ██ ▀█████▄ ▀█████ │"
5| "│ ▀█▄ ▀█████ │"
style 0-0 dim
style 28-50 fg=blue
style 33-46 fg=blue
style 79-79 dim
6| "│ ██▄ ▀████▄ ▄████ │"
6| "│ ▀█▄▄ █▄▄▀███▄ │"
style 0-0 dim
style 28-50 fg=blue
style 33-46 fg=blue
style 79-79 dim
7| "│ ██▄ ████████▀ │"
7| "│ ▀▀▀▀▀▀ │"
style 0-0 dim
style 29-50 fg=blue
style 35-44 fg=blue
style 79-79 dim
8| "│ ██▄ ▄▄ ▀█████▀ │"
style 0-0 dim
style 29-49 fg=blue
style 79-79 dim
9| "│ ▀███▄▄▄███▄ ████▄▄ │"
style 0-0 dim
style 29-50 fg=blue
style 79-79 dim
10| "│ ▀▀▀███████▀▀ │"
style 0-0 dim
style 31-47 fg=blue
style 79-79 dim
11| "│ │"
8| "│ │"
style 0-0 dim
style 79-79 dim
12| "│ DeepSeek Harness │"
9| "│ DeepSeek Harness │"
style 0-0 dim
style 32-47 fg=blue bold
style 79-79 dim
10| "│ │"
style 0-0 dim
style 79-79 dim
11| "│ 感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段功能仍待完 │"
style 0-0 dim
style 79-79 dim
12| "│ 善,体验难免有些粗糙。 │"
style 0-0 dim
style 79-79 dim
13| "│ │"
style 0-0 dim
style 79-79 dim
14| "│ 感谢您愿意拨冗试用 DeepSeek Harness。 │"
14| "│ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 2-23 bold
style 79-79 dim
15| "│ │"
style 0-0 dim
style 79-79 dim
16| "│ 目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 │"
16| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也 │"
style 0-0 dim
style 79-79 dim
17| "│ │"
17| "│ 可能促使我们重新审视,甚至推翻已有的设计。 │"
style 0-0 dim
style 79-79 dim
18| "│ “如切如磋,如琢如磨。” │"
style 0-0 dim
style 2-23 bold
style 79-79 dim
19| "│ │"
18| "│ │"
style 0-0 dim
style 79-79 dim
20| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题, │"
19| "│ 为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session │"
style 0-0 dim
style 79-79 dim
21| "│ 可能促使我们重新审视,甚至推翻已有的设计。 │"
20| "│ Log如需关闭可以【关闭方式待补充】。另外如果您有任何反馈与建议请在企 │"
style 0-0 dim
style 79-79 dim
22| "│ │"
21| "│ 业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 │"
style 0-0 dim
style 79-79 dim
23| "├──────────────────────────────────────────────────────────────────────────────┤"
22| "├──────────────────────────────────────────────────────────────────────────────┤"
style 0-79 dim
24| "│ Enter 继续 │"
23| "│ Enter 继续 │"
style 0-0 dim
style 34-44 fg=bright-magenta bold
style 79-79 dim
25| "│ ↑/↓ 滚动 ↓ │"
24| "│ │"
style 0-0 dim
style 34-45 dim
style 79-79 dim
26| "╰──────────────────────────────────────────────────────────────────────────────╯"
25| "╰──────────────────────────────────────────────────────────────────────────────╯"
style 0-79 dim

View File

@@ -60,6 +60,7 @@ function hostFixture(rows: number): {
}
const copy = TUI_FIRST_RUN_WELCOME_NOTICE_COPY[TUI_FIRST_RUN_WELCOME_NOTICE_LOCALE]
const openingSentence = `${copy.paragraphs[0]!.split('。', 1)[0]}`
const temporaryHomes: string[] = []
function withoutWhitespace(value: string): string {
@@ -124,15 +125,17 @@ describe('TUI first-run welcome composition', () => {
expect(createHash('sha256').update(icon).digest('hex'))
.toBe('deba5f98a5c1796e20fcac3149bcd7eb8a32f0bdd04d048819400b1f28bd1439')
expect(createHash('sha256').update(copy.paragraphs.join('\n')).digest('hex'))
.toBe('e1b8ea95d9f8af276c4b2b76fff23fa068bdad61b5d4a10300600ed47572900d')
.toBe('54389347f93109c7cb17baa4312ae55eaefe77cbbf2ffe3e7579a4538e9f5738')
expect(TUI_FIRST_RUN_WELCOME_NOTICE_COPY.en).toBe(copy)
})
it.each([
{ columns: 60, inner: 50, rows: 30, tier: 'minimal' },
{ columns: 80, inner: 68, rows: 30, tier: 'compact' },
{ columns: 60, inner: 50, rows: 30, tier: undefined },
{ columns: 80, inner: 68, rows: 30, tier: 'minimal' },
{ columns: 100, inner: 84, rows: 34, tier: 'compact' },
{ columns: 120, inner: 104, rows: 30, tier: 'full' },
{ columns: 160, inner: 140, rows: 30, tier: 'full' },
] as const)('renders the $tier composition at $columns columns without overdraw', ({ inner, rows, tier }) => {
] as const)('renders the responsive composition at $columns columns without overdraw', ({ inner, rows, tier }) => {
const fixture = hostFixture(rows)
const component = new TuiFirstRunWelcomeComponent(fixture.host, copy, async () => {})
const renderWidth = inner + 4
@@ -140,7 +143,17 @@ describe('TUI first-run welcome composition', () => {
expect(tuiFirstRunWelcomeArtTier(inner, rows)).toBe(tier)
expect(lines.every(line => tuiVisibleWidth(line) <= renderWidth)).toBe(true)
expect(lines.join('\n')).toContain(TUI_FIRST_RUN_WELCOME_WHALE[tier].unicode[0]!.trim())
if (tier === undefined) {
expect(lines.join('\n')).not.toMatch(/[]/u)
} else {
expect(lines.join('\n')).toContain(TUI_FIRST_RUN_WELCOME_WHALE[tier].unicode[0]!.trim())
}
const rendered = lines.join('\n')
const placeholder = copy.paragraphs.at(-1)!.match(/[^]+/u)![0]
expect(rendered).not.toContain(copy.scrollHint)
expect(rendered).toContain(copy.paragraphs.at(-1)!.match(/[A-Za-z]+ [A-Za-z]+/u)![0])
expect(rendered).toContain(placeholder.slice(0, 3))
expect(rendered).toContain(placeholder.slice(-3))
expect(lines.join('\n')).toContain(`Enter ${copy.continueLabel}`)
expect(lines.length).toBeLessThanOrEqual(Math.floor(rows * 0.9))
expect(lines.length).toBeGreaterThan(5)
@@ -151,12 +164,12 @@ describe('TUI first-run welcome composition', () => {
const component = new TuiFirstRunWelcomeComponent(fixture.host, copy, async () => {})
const initial = component.render(54).join('\n')
expect(tuiFirstRunWelcomeArtTier(50, 10)).toBeUndefined()
expect(initial).toContain(copy.paragraphs[0])
expect(initial).toContain(openingSentence)
expect(initial).toContain(`Enter ${copy.continueLabel}`)
component.handleInput('\x1b[F')
const end = component.render(54).join('\n')
expect(withoutWhitespace(end)).toContain(withoutWhitespace(copy.paragraphs.at(-1)!.slice(-10)))
expect(withoutWhitespace(end)).toContain(withoutWhitespace(copy.paragraphs.at(-1)!.slice(-7)))
expect(end).toContain(`Enter ${copy.continueLabel}`)
for (const key of ['\x1b[A', '\x1b[B', '\x1b[5~', '\x1b[6~', '\x1b[H', 'x']) {
@@ -173,11 +186,20 @@ describe('TUI first-run welcome composition', () => {
expect(lines.every(line => tuiVisibleWidth(line) <= 6)).toBe(true)
})
it('keeps the side-by-side composition aligned when prose outgrows the full raster', () => {
const fixture = hostFixture(40)
const longCopy = { ...copy, paragraphs: [copy.paragraphs.join(' ').repeat(4)] }
const component = new TuiFirstRunWelcomeComponent(fixture.host, longCopy, async () => {})
const lines = component.render(100)
expect(lines.length).toBeGreaterThan(TUI_FIRST_RUN_WELCOME_WHALE.full.unicode.length)
expect(lines.every(line => tuiVisibleWidth(line) <= 100)).toBe(true)
})
it('renders the bit-equivalent ASCII icon fallback for an explicitly non-Unicode terminal', () => {
const fixture = hostFixture(30)
const component = new TuiFirstRunWelcomeComponent(fixture.host, copy, async () => {}, true)
const rendered = component.render(72).join('\n')
expect(rendered).toContain(TUI_FIRST_RUN_WELCOME_WHALE.compact.ascii[0]!.trim())
expect(rendered).toContain(TUI_FIRST_RUN_WELCOME_WHALE.minimal.ascii[0]!.trim())
expect(rendered).not.toMatch(/[]/u)
})
@@ -264,6 +286,6 @@ describe('TUI first-run welcome composition', () => {
apply(ctx, { dshHome: home, asciiArt: true })
expect(request?.create(fixture.host).render(72).join('\n'))
.toContain(TUI_FIRST_RUN_WELCOME_WHALE.compact.ascii[0]!.trim())
.toContain(TUI_FIRST_RUN_WELCOME_WHALE.minimal.ascii[0]!.trim())
})
})

View File

@@ -172,6 +172,7 @@ function smoke(overrides: Partial<TuiPtySmokeOptions> & {
}
const firstRunCopy = TUI_FIRST_RUN_WELCOME_NOTICE_COPY[TUI_FIRST_RUN_WELCOME_NOTICE_LOCALE]
const firstRunOpeningSentence = `${firstRunCopy.paragraphs[0]!.split('。', 1)[0]}`
/** Keep only the overlay rows, excluding platform-specific scrollback and the underlying TUI. */
function overlaySnapshot(snapshot: string, columns: number, rows: number): string {
@@ -220,11 +221,11 @@ const SELECT_PRO_MODEL = [
describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
it.each([
{ columns: 60, tier: 'minimal' },
{ columns: 80, tier: 'compact' },
{ columns: 60, tier: undefined },
{ columns: 80, tier: 'minimal' },
{ columns: 120, tier: 'full' },
{ columns: 160, tier: 'full' },
] as const)('renders and acknowledges the $tier first-run composition at $columns columns', async ({ columns, tier }) => {
] as const)('renders and acknowledges the responsive first-run composition at $columns columns', async ({ columns, tier }) => {
const output = await smoke({
label: `dsh first-run welcome ${String(columns)} columns`,
tempDirPrefix: `dsh-tui-welcome-${String(columns)}-`,
@@ -251,9 +252,13 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
}
},
})
await expect(await firstRunFrameSnapshot(output, firstRunCopy.paragraphs[0]!, columns, 30))
await expect(await firstRunFrameSnapshot(output, firstRunOpeningSentence, columns, 30))
.toMatchFileSnapshot(join(firstRunSnapshots, `${String(columns)}-columns.expected.txt`))
expect(output).toContain(TUI_FIRST_RUN_WELCOME_WHALE[tier].unicode[0]!.trim())
if (tier === undefined) {
expect(output).not.toContain(TUI_FIRST_RUN_WELCOME_WHALE.minimal.unicode[0]!.trim())
} else {
expect(output).toContain(TUI_FIRST_RUN_WELCOME_WHALE[tier].unicode[0]!.trim())
}
expect(output).toContain(`Enter ${firstRunCopy.continueLabel}`)
}, PTY_SMOKE_TEST_TIMEOUT_MS)
@@ -267,7 +272,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
columns: 60,
rows: 12,
actions: [
{ waitFor: firstRunCopy.paragraphs[0]!, send: '\x1b[F' },
{ waitFor: firstRunOpeningSentence, send: '\x1b[F' },
{
waitFor: `Enter ${firstRunCopy.continueLabel}`,
occurrence: 2,
@@ -276,10 +281,10 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
},
],
})
await expect(await firstRunFrameSnapshot(output, firstRunCopy.paragraphs[0]!, 60, 12))
await expect(await firstRunFrameSnapshot(output, firstRunOpeningSentence, 60, 12))
.toMatchFileSnapshot(join(firstRunSnapshots, '60-columns-low-height.expected.txt'))
expect(output).toContain(firstRunCopy.title)
expect(output).toContain(firstRunCopy.paragraphs[0])
expect(output).toContain(firstRunOpeningSentence)
expect(output).toContain('企业微信群')
expect(output).toContain(`Enter ${firstRunCopy.continueLabel}`)
expect(output).not.toContain(TUI_FIRST_RUN_WELCOME_WHALE.minimal.unicode[0]!.trim())
@@ -310,7 +315,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
expectedExitCode: process.platform === 'win32' ? 0 : -15,
actions: [{ waitFor: 'main-session-', signal: 'SIGTERM' }],
})
expect(second).not.toContain(firstRunCopy.paragraphs[0])
expect(second).not.toContain(firstRunOpeningSentence)
expect(second).not.toContain(`Enter ${firstRunCopy.continueLabel}`)
} finally {
await rm(cwd, { recursive: true, force: true })
@@ -327,7 +332,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
configPath: scriptedConfigPath,
showFirstRunWelcome: true,
expectedExitCode: -15,
actions: [{ waitFor: firstRunCopy.paragraphs[0]!, signal: 'SIGTERM' }],
actions: [{ waitFor: firstRunOpeningSentence, signal: 'SIGTERM' }],
inspect: async (workspace) => {
expect(await hasTuiFirstRunWelcomeAcknowledgement(join(workspace, '.dsh'))).toBe(false)
},
@@ -344,7 +349,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
{ waitFor: `Enter ${firstRunCopy.continueLabel}`, send: '\r', signalAfterMs: 2_000 },
],
})
expect(next).toContain(firstRunCopy.paragraphs[0])
expect(next).toContain(firstRunOpeningSentence)
} finally {
await rm(cwd, { recursive: true, force: true })
}
@@ -557,7 +562,7 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
expect(appended).not.toContainEqual(expect.objectContaining({ type: 'turn/start' }))
},
})
expect(output).toContain(firstRunCopy.paragraphs[0])
expect(output).toContain(firstRunOpeningSentence)
expect(output).toContain('Resume selector design — DeepSeek Harness')
}, PTY_SMOKE_TEST_TIMEOUT_MS)