From dbb87f02bf78f53a362c5a919a9dc03c4b36eb12 Mon Sep 17 00:00:00 2001 From: NI0317 Date: Fri, 31 Jul 2026 15:15:14 +0800 Subject: [PATCH] copy(tui): disclose Session Log upload in Chinese welcome --- ...-versioned-tui-first-run-welcome.i18n.yaml | 4 +- ...6-07-30-versioned-tui-first-run-welcome.md | 6 +- ...7-30-versioned-tui-first-run-welcome.zh.md | 6 +- apps/cli/README.i18n.yaml | 4 +- apps/cli/README.md | 2 +- apps/cli/README.zh.md | 2 +- apps/cli/src/tui-first-run-welcome-copy.ts | 51 +++++------- apps/cli/src/tui-first-run-welcome.ts | 4 +- .../120-columns.expected.txt | 30 +++---- .../160-columns.expected.txt | 20 ++--- .../60-columns-low-height.expected.txt | 6 +- .../60-columns.expected.txt | 81 ++++++------------ .../80-columns.expected.txt | 83 +++++++++---------- apps/cli/tests/tui-first-run-welcome.spec.ts | 40 +++++++-- apps/cli/tests/tui-keyless-smoke.e2e.ts | 29 ++++--- 15 files changed, 173 insertions(+), 195 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.i18n.yaml b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.i18n.yaml index c927167d68..cfc68dc8f1 100644 --- a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.i18n.yaml @@ -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 .agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md -2026-07-30-versioned-tui-first-run-welcome.md: 71790b53ec5816a806952a1e9fcf2079e50af8be -2026-07-30-versioned-tui-first-run-welcome.zh.md: 132899b9485cd784344e54aed0fc933bc50a57bc +2026-07-30-versioned-tui-first-run-welcome.md: d78e7a172198de2c61579dc5faa37a4c9f50fb58 +2026-07-30-versioned-tui-first-run-welcome.zh.md: 1d6a3da63938f6de3dfc9b85ef0548e360de9b3e diff --git a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md index 71790b53ec..d78e7a1721 100644 --- a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md +++ b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md @@ -6,7 +6,7 @@ English | [中文](2026-07-30-versioned-tui-first-run-welcome.zh.md) ## Problem -The shipped `dsh` terminal starts directly in the editor and gives first-time internal testers no durable orientation about the product's maturity or feedback channel. The existing one-line `welcome` banner subtitle cannot carry the supplied four-paragraph notice without crowding the normal session header, and putting onboarding in the session log would create a user turn or model-visible context that is unrelated to the user's work. +The shipped `dsh` terminal starts directly in the editor and gives first-time internal testers no durable orientation about the product's maturity or feedback channel. The existing one-line `welcome` banner subtitle cannot carry the supplied notice without crowding the normal session header, and putting onboarding in the session log would create a user turn or model-visible context that is unrelated to the user's work. The notice also needs a recognizable DeepSeek composition without copying another product's startup art or maintaining a hand-drawn approximation that drifts from the official mark. @@ -18,7 +18,7 @@ The marker is launcher state rather than session persistence because eligibility The supplied official `24x24` DeepSeek SVG is committed as the visual source. Static full, compact, and minimal terminal rasters sample that exact path at decreasing square resolutions; they do not redraw the contour. Unicode `▀`/`▄`/`█` cells preserve two vertical source pixels per terminal cell, while an explicitly ASCII-only locale uses the bit-equivalent `'`/`_`/`#` fallback. ANSI styling stays outside both the SVG and editable copy: `ctx.tui` supplies a semantic `brand` role, using the official `#4D6BFE` ink when truecolor is available, standard ANSI blue otherwise, and plain text when color is disabled. The normal startup banner retains its existing gradient. -The overlay is centered and consumes the available terminal width, while its height follows actual content and treats 90% of the viewport only as an upper bound. Wide terminals place the full icon beside the title and prose; medium and narrow terminals stack the compact or minimal icon above them; low height removes the icon before reducing prose space. The prose scrolls while the title and only action remain fixed. The quotation is promoted to its own visual paragraph without changing the centrally owned Chinese string. Closing through Enter returns modal ownership to the existing FIFO manager, which restores the editor and leaves the normal startup banner, transcript, and focus behavior intact. +The overlay is centered and consumes the available terminal width, while its height follows actual content and treats 90% of the viewport only as an upper bound. Wide terminals place the full icon beside the title and prose; medium and narrow terminals stack the compact or minimal icon above them; low height removes the icon before reducing prose space. The prose scrolls while the title and only action remain fixed. Every locale uses the same centrally owned Chinese copy, and the quotation is promoted to its own visual paragraph without changing that string. Closing through Enter returns modal ownership to the existing FIFO manager, which restores the editor and leaves the normal startup banner, transcript, and focus behavior intact. ## Verification @@ -38,6 +38,6 @@ Focused unit coverage pins the supplied SVG and Chinese copy hashes, version bum ## Consequences -Each Harness home receives the notice once per copy version, only after a successful Enter acknowledgement. Maintainers can edit all bilingual wording and the version in one small owner file, and can update the official SVG and derived static rasters in their separate visual owner without chasing snapshots for full prose copies. +Each Harness home receives the notice once per copy version, only after a successful Enter acknowledgement. Maintainers can edit the all-locale Chinese wording and version in one small owner file, and can update the official SVG and derived static rasters in their separate visual owner without chasing snapshots for full prose copies. The terminal cannot display SVG vectors directly, so its faithful representation is resolution-bounded. Smaller tiers preserve the sampled silhouette but necessarily lose fine detail; low-height terminals prefer readable prose and an always-reachable action over brand art. The marker format is intentionally one-file-per-version during the pre-release period; old markers are harmless and no compatibility reader is required. diff --git a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.zh.md b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.zh.md index 132899b948..1d6a3da639 100644 --- a/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.zh.md +++ b/.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.zh.md @@ -6,7 +6,7 @@ Status: implemented ## 问题 -已交付的 `dsh` 终端会直接进入编辑器,没有为首次使用的内部测试者提供持久的产品成熟度说明或反馈渠道指引。现有单行 `welcome` banner 副标题无法容纳指定的四段通知,否则会挤占正常的会话 header;如果把 onboarding 写入会话日志,还会创建一个与用户工作无关的用户轮次或模型可见上下文。 +已交付的 `dsh` 终端会直接进入编辑器,没有为首次使用的内部测试者提供持久的产品成熟度说明或反馈渠道指引。现有单行 `welcome` banner 副标题无法容纳指定通知,否则会挤占正常的会话 header;如果把 onboarding 写入会话日志,还会创建一个与用户工作无关的用户轮次或模型可见上下文。 该通知还需要形成具有辨识度的 DeepSeek 视觉构图,同时不能复制其他产品的启动图形,也不能维护一份会偏离官方标志的手绘近似图。 @@ -18,7 +18,7 @@ Status: implemented 指定的官方 `24x24` DeepSeek SVG 作为视觉真源提交。静态的完整、紧凑和最小终端栅格图以逐级降低的方形分辨率对该精确路径取样,不会重新绘制轮廓。Unicode `▀`/`▄`/`█` 单元格让每个终端单元格保留两个垂直方向的源像素;明确仅支持 ASCII 的 locale 则使用位级等价的 `'`/`_`/`#` 回退。ANSI 样式与 SVG 和可编辑文案完全分离:`ctx.tui` 提供语义化 `brand` 角色,在真彩色可用时使用官方 `#4D6BFE` 色值,否则使用标准 ANSI 蓝色;禁用颜色时则使用纯文本。普通启动 banner 保留现有渐变。 -浮层居中显示,并使用可用的终端宽度;高度则跟随实际内容,仅将 viewport 的 90% 作为上限。宽终端将完整图标置于标题与正文旁边;中等和窄终端把紧凑或最小图标堆叠在正文上方;高度不足时,先移除图标,再减少正文空间。正文可以滚动,而标题和唯一操作保持固定。引用内容会提升为独立的视觉段落,但不会改变集中持有的中文字符串。通过 Enter 关闭浮层后,模态所有权会交还给现有 FIFO 管理器;该管理器恢复编辑器,同时保持正常的启动 banner、transcript(文本记录)和焦点行为不变。 +浮层居中显示,并使用可用的终端宽度;高度则跟随实际内容,仅将 viewport 的 90% 作为上限。宽终端将完整图标置于标题与正文旁边;中等和窄终端把紧凑或最小图标堆叠在正文上方;高度不足时,先移除图标,再减少正文空间。正文可以滚动,而标题和唯一操作保持固定。所有 locale 共用同一份集中持有的中文文案,引用内容会提升为独立的视觉段落,但不会改变该字符串。通过 Enter 关闭浮层后,模态所有权会交还给现有 FIFO 管理器;该管理器恢复编辑器,同时保持正常的启动 banner、transcript(文本记录)和焦点行为不变。 ## 验证 @@ -38,6 +38,6 @@ Status: implemented ## 后果 -每个 Harness 主目录在每个文案版本中都会收到一次通知,且仅在用户成功按 Enter 确认后停止展示。维护者可以在一个小型 owner 文件中编辑所有双语文案和版本,也可以在独立的视觉 owner 中更新官方 SVG 与衍生静态栅格图,无需到各个快照中查找完整正文副本。 +每个 Harness 主目录在每个文案版本中都会收到一次通知,且仅在用户成功按 Enter 确认后停止展示。维护者可以在一个小型 owner 文件中编辑所有 locale 共用的中文文案和版本,也可以在独立的视觉 owner 中更新官方 SVG 与衍生静态栅格图,无需到各个快照中查找完整正文副本。 终端无法直接显示 SVG 矢量图,因此其忠实呈现受分辨率限制。较小分级会保留取样后的轮廓,但必然损失精细细节;低高度终端优先保障正文可读和操作始终可达,而非展示品牌图形。在预发布阶段,标记格式有意采用每个版本一个文件;旧标记无害,也无需兼容性读取器。 diff --git a/apps/cli/README.i18n.yaml b/apps/cli/README.i18n.yaml index c5a38937d2..fb3b8af50f 100644 --- a/apps/cli/README.i18n.yaml +++ b/apps/cli/README.i18n.yaml @@ -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 diff --git a/apps/cli/README.md b/apps/cli/README.md index e01989d18d..2f7238b3c0 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -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 ` to resume a persisted session. diff --git a/apps/cli/README.zh.md b/apps/cli/README.zh.md index 4df2b09110..af4004be22 100644 --- a/apps/cli/README.zh.md +++ b/apps/cli/README.zh.md @@ -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 `。 diff --git a/apps/cli/src/tui-first-run-welcome-copy.ts b/apps/cli/src/tui-first-run-welcome-copy.ts index ae5ebd8ce3..26ddbd17c2 100644 --- a/apps/cli/src/tui-first-run-welcome-copy.ts +++ b/apps/cli/src/tui-first-run-welcome-copy.ts @@ -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({ + 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({ - title: 'DeepSeek Harness', - paragraphs: Object.freeze([ - '感谢您愿意拨冗试用 DeepSeek Harness。', - '目前的版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。', - '“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。', - '我们尤其希望听见那些失败、困惑与不顺手的时刻——如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。', - ]), - continueLabel: '继续', - scrollHint: '↑/↓ 滚动', - saving: '正在保存确认…', - saveError: '无法保存确认,请按 Enter 重试。', - }), - en: Object.freeze({ - 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. */ diff --git a/apps/cli/src/tui-first-run-welcome.ts b/apps/cli/src/tui-first-run-welcome.ts index 8b1bf7e7ed..1b280f84d2 100644 --- a/apps/cli/src/tui-first-run-welcome.ts +++ b/apps/cli/src/tui-first-run-welcome.ts @@ -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 } diff --git a/apps/cli/tests/tui-first-run-snapshots/120-columns.expected.txt b/apps/cli/tests/tui-first-run-snapshots/120-columns.expected.txt index fee357976f..2bc2914a52 100644 --- a/apps/cli/tests/tui-first-run-snapshots/120-columns.expected.txt +++ b/apps/cli/tests/tui-first-run-snapshots/120-columns.expected.txt @@ -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 diff --git a/apps/cli/tests/tui-first-run-snapshots/160-columns.expected.txt b/apps/cli/tests/tui-first-run-snapshots/160-columns.expected.txt index d2d14a2360..91b6610600 100644 --- a/apps/cli/tests/tui-first-run-snapshots/160-columns.expected.txt +++ b/apps/cli/tests/tui-first-run-snapshots/160-columns.expected.txt @@ -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 diff --git a/apps/cli/tests/tui-first-run-snapshots/60-columns-low-height.expected.txt b/apps/cli/tests/tui-first-run-snapshots/60-columns-low-height.expected.txt index 7271f27e79..bdf8a46a6f 100644 --- a/apps/cli/tests/tui-first-run-snapshots/60-columns-low-height.expected.txt +++ b/apps/cli/tests/tui-first-run-snapshots/60-columns-low-height.expected.txt @@ -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| "├──────────────────────────────────────────────────────────┤" diff --git a/apps/cli/tests/tui-first-run-snapshots/60-columns.expected.txt b/apps/cli/tests/tui-first-run-snapshots/60-columns.expected.txt index 3737b99fd6..daa9bbfc67 100644 --- a/apps/cli/tests/tui-first-run-snapshots/60-columns.expected.txt +++ b/apps/cli/tests/tui-first-run-snapshots/60-columns.expected.txt @@ -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 diff --git a/apps/cli/tests/tui-first-run-snapshots/80-columns.expected.txt b/apps/cli/tests/tui-first-run-snapshots/80-columns.expected.txt index bfab89be3f..09fb756633 100644 --- a/apps/cli/tests/tui-first-run-snapshots/80-columns.expected.txt +++ b/apps/cli/tests/tui-first-run-snapshots/80-columns.expected.txt @@ -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 diff --git a/apps/cli/tests/tui-first-run-welcome.spec.ts b/apps/cli/tests/tui-first-run-welcome.spec.ts index 245db27bec..0f040de8d1 100644 --- a/apps/cli/tests/tui-first-run-welcome.spec.ts +++ b/apps/cli/tests/tui-first-run-welcome.spec.ts @@ -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()) }) }) diff --git a/apps/cli/tests/tui-keyless-smoke.e2e.ts b/apps/cli/tests/tui-keyless-smoke.e2e.ts index 6007ae69c4..7901a79555 100644 --- a/apps/cli/tests/tui-keyless-smoke.e2e.ts +++ b/apps/cli/tests/tui-keyless-smoke.e2e.ts @@ -172,6 +172,7 @@ function smoke(overrides: Partial & { } 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)