feat(web): display a temporary 1017 badge for DeepSeek Flash

This commit is contained in:
Yichen Jiang committed 2026-10-17 13:33:35 +08:00
1 parent 32efe36a4e
commit 3c937cd0ba
34 files changed
+657 -4

No files matched your search

@@ -0,0 +1,107 @@
/** The shipped browser plugin decorates both DeepSeek routes without changing catalog identity. */
import { fileURLToPath } from 'node:url'
import { chromium, type Browser, type Page } from 'playwright'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import {
captureStableAria, compareOrRefreshGolden, launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspaceZh, ZH_BROWSER_LOCALE } from './support.ts'
const NAME = 'DeepSeek-V4.1-Flash'
const FIXTURE = fileURLToPath(new URL('./fixtures/deepseek-model-label-1017/session.v4.jsonl', import.meta.url))
const EXPECTED = fileURLToPath(new URL('./expected/deepseek-model-label-1017/models.expected.md', import.meta.url))
const MODE = webSnapshotMode()
describe.skipIf(MODE === 'record')('web e2e: temporary DeepSeek release badge', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
let tripwire: ReturnType<typeof watchConsole>
beforeAll(async () => {
scaffold = await launchWebScaffold({
extraOverlayPath: fileURLToPath(new URL('./fixtures/deepseek-model-label-1017/account.overlay.yml', import.meta.url)),
replayFixture: FIXTURE,
replayProvidersOnly: true,
replayProviders: [
{ id: 'deepseek-account', name: 'DeepSeek Account', models: [{ id: 'deepseek-flash', name: NAME }] },
{ id: 'deepseek-official', name: 'DeepSeek', models: [
{ id: 'deepseek-flash', name: NAME }, { id: 'deepseek-v4-pro', name: 'DeepSeek-V4-Pro' },
] },
{ id: 'external', name: 'External', models: [{ id: 'deepseek-flash', name: NAME }] },
],
})
browser = await chromium.launch()
page = await browser.newPage({ viewport: { width: 1440, height: 900 }, locale: ZH_BROWSER_LOCALE })
tripwire = watchConsole(page)
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
await connectFreshWorkspaceZh(page, scaffold.workspaceCwd)
})
afterAll(async () => {
try { await browser?.close() }
finally { await scaffold?.close() }
})
it('shows the same badge in all placements while preserving the chosen provider and model id', async () => {
const trigger = page.getByRole('button', { name: /^选择模型/ })
await trigger.getByText('1017', { exact: true }).waitFor()
expect(await trigger.textContent()).toContain(`${NAME} 1017`)
expect(await trigger.getAttribute('aria-label')).toBe(`选择模型,当前 ${NAME}`)
try {
await trigger.click()
const current = page.getByRole('menuitem', { name: /^模型/ })
expect(await current.getByText('1017', { exact: true }).count()).toBe(1)
await current.click()
const menu = page.getByRole('menu', { name: '模型', exact: true })
await compareOrRefreshGolden(EXPECTED, await captureStableAria(page, '[role="menu"][aria-label="模型"]', scaffold.workspaceCwd), MODE)
expect(await menu.getByText('1017', { exact: true }).count()).toBe(2)
expect(await menu.getByRole('group', { name: 'External', exact: true }).getByText('1017', { exact: true }).count()).toBe(0)
const account = menu.getByRole('group', { name: 'DeepSeek 账号', exact: true })
await account.getByRole('menuitemradio', { name: `${NAME} 1017`, exact: true }).click()
await expect.poll(() => scaffold.ctx.agentDefaultModel.currentSelection()).toEqual({ provider: 'deepseek-account', model: 'deepseek-flash' })
await expect.poll(() => trigger.textContent()).toContain(`${NAME} 1017`)
const catalog = await scaffold.ctx.llm.listModels('deepseek-account')
expect(catalog.map(model => ({ id: model.id, name: model.name }))).toEqual([{ id: 'deepseek-flash', name: NAME }])
} finally {
await page.keyboard.press('Escape')
await page.keyboard.press('Escape')
}
})
it('keeps the release badge inside a 390px model menu and uses both theme palettes', async () => {
try {
await page.setViewportSize({ width: 390, height: 844 })
const trigger = page.getByRole('button', { name: /^选择模型/ })
await trigger.click()
await page.getByRole('menuitem', { name: /^模型/ }).click()
const menu = page.getByRole('menu', { name: '模型', exact: true })
const badge = menu.getByRole('group', { name: 'DeepSeek 账号', exact: true }).getByText('1017', { exact: true })
const box = await badge.boundingBox()
expect(box).not.toBeNull()
expect(box!.x).toBeGreaterThanOrEqual(12)
expect(box!.x + box!.width).toBeLessThanOrEqual(378)
const colors = await badge.evaluate((node) => {
const body = node.ownerDocument.body
const previous = body.getAttribute('data-ds-dark-theme')
try {
return [false, true].map((dark) => {
body.toggleAttribute('data-ds-dark-theme', dark)
const style = getComputedStyle(node)
return { color: style.color, background: style.backgroundColor }
})
} finally {
if (previous === null) body.removeAttribute('data-ds-dark-theme')
else body.setAttribute('data-ds-dark-theme', previous)
}
})
expect(colors[0]).not.toEqual(colors[1])
for (const color of colors) expect(color.color).not.toBe(color.background)
} finally {
await page.keyboard.press('Escape')
await page.keyboard.press('Escape')
await page.setViewportSize({ width: 1440, height: 900 })
}
expect(tripwire.pageErrors).toEqual([])
})
})
@@ -0,0 +1,11 @@
- menu "模型":
- group "DeepSeek 账号":
- text: DeepSeek 账号
- menuitemradio "DeepSeek-V4.1-Flash 1017"
- group "DeepSeek":
- text: DeepSeek
- menuitemradio "DeepSeek-V4.1-Flash 1017" [checked]
- menuitemradio "DeepSeek-V4-Pro"
- group "External":
- text: External
- menuitemradio "DeepSeek-V4.1-Flash"
@@ -0,0 +1,7 @@
# The replay catalog supplies the account route without account authentication or network I/O.
- id: llm-deepseek-account
disabled: true
- id: agent-default-model
config:
provider: deepseek-official
model: deepseek-flash
@@ -0,0 +1 @@
{"type":"session","version":4,"id":"{{session:1}}","createdAt":1785015039278,"cwd":"{{cwd}}","isSeeded":false,"delegationDepth":0,"agentPreset":"standard"}
+1
View File
@@ -107,6 +107,7 @@
"tests/developer-tools-settings.e2e.ts",
"tests/models-settings.e2e.ts",
"tests/deepseek-messages-settings.e2e.ts",
"tests/deepseek-model-label-1017.e2e.ts",
"tests/deepseek-messages-chat.e2e.ts",
"tests/models-settings-recovery.e2e.ts",
"tests/default-model.e2e.ts",
+1
View File
@@ -4580,6 +4580,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-client-ui-commands` | — | [`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-conversation` | — | [`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-cordis` | — | [`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deepseek-model-label-1017` | — | [`packages/client/ui-deepseek-model-label-1017/src/index.ts`](../packages/client/ui-deepseek-model-label-1017/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deliverables` | `systemPrompt` · `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy` · `workspaceChanges` | [`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | — | [`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-native` | — | [`packages/client/ui-directory-picker-native/src/index.ts`](../packages/client/ui-directory-picker-native/src/index.ts) |
+1
View File
@@ -4582,6 +4582,7 @@ export interface Config {
| `@deepseek-ai/dsh-client-ui-commands` | — | [`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-conversation` | — | [`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-cordis` | — | [`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deepseek-model-label-1017` | — | [`packages/client/ui-deepseek-model-label-1017/src/index.ts`](../packages/client/ui-deepseek-model-label-1017/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deliverables` | `systemPrompt` · `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy` · `workspaceChanges` | [`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | — | [`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-native` | — | [`packages/client/ui-directory-picker-native/src/index.ts`](../packages/client/ui-directory-picker-native/src/index.ts) |
+2
View File
@@ -160,6 +160,7 @@ flowchart TD
pkg_client_ui_chat["client-ui-chat"]
pkg_client_ui_commands["client-ui-commands"]
pkg_client_ui_conversation["client-ui-conversation"]
pkg_client_ui_deepseek_model_label_1017["client-ui-deepseek-model-label-1017"]
pkg_client_ui_deliverables["client-ui-deliverables"]
pkg_client_ui_directory_picker_browse["client-ui-directory-picker-browse"]
pkg_client_ui_directory_picker_native["client-ui-directory-picker-native"]
@@ -1468,6 +1469,7 @@ flowchart TD
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | — |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | — |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | — |
| [`client-ui-deepseek-model-label-1017`](../packages/client/ui-deepseek-model-label-1017) | `client` | — |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | — |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | — |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | — |
+2
View File
@@ -162,6 +162,7 @@ flowchart TD
pkg_client_ui_chat["client-ui-chat"]
pkg_client_ui_commands["client-ui-commands"]
pkg_client_ui_conversation["client-ui-conversation"]
pkg_client_ui_deepseek_model_label_1017["client-ui-deepseek-model-label-1017"]
pkg_client_ui_deliverables["client-ui-deliverables"]
pkg_client_ui_directory_picker_browse["client-ui-directory-picker-browse"]
pkg_client_ui_directory_picker_native["client-ui-directory-picker-native"]
@@ -1470,6 +1471,7 @@ flowchart TD
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | — |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | — |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | — |
| [`client-ui-deepseek-model-label-1017`](../packages/client/ui-deepseek-model-label-1017) | `client` | — |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | — |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | — |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | — |
+3
View File
@@ -412,6 +412,9 @@
- id: ui-model-selection
name: '@deepseek-ai/dsh-client-ui-model-selection'
- id: ui-deepseek-model-label-1017
name: '@deepseek-ai/dsh-client-ui-deepseek-model-label-1017'
- id: ui-permission
name: '@deepseek-ai/dsh-client-ui-permission-presets'
+1
View File
@@ -90,6 +90,7 @@
"@deepseek-ai/dsh-client-ui-layout": "workspace:*",
"@deepseek-ai/dsh-client-ui-message-feedback": "workspace:*",
"@deepseek-ai/dsh-client-ui-model-selection": "workspace:*",
"@deepseek-ai/dsh-client-ui-deepseek-model-label-1017": "workspace:*",
"@deepseek-ai/dsh-client-ui-open-in-app": "workspace:*",
"@deepseek-ai/dsh-client-ui-permission-presets": "workspace:*",
"@deepseek-ai/dsh-client-ui-plan": "workspace:*",
+2 -2
View File
@@ -15,8 +15,8 @@
en: 1eb35b015a6c423f
zh: a8a3dceee2c9e943
/client-web-gui-browser-half/packages:
en: 3026f68770598c24
zh: e479959e605faea0
en: 4d88ec16156593e5
zh: 745b3aab722ca17d
/client-web-gui-browser-half/related-documentation:
en: d1d506e68fab7333
zh: 1a90fe6f3f8f24a9
+1
View File
@@ -68,6 +68,7 @@ The kernel packages boot and serve the page; the UI feature packages present it.
| [`ui-jobs/`](ui-jobs/README.md) | Lists this session's background jobs with on-demand streaming record panels | — |
| [`ui-schedule/`](ui-schedule/README.md) | Lists the current Session's active reminders in a read-only header catalog | — |
| [`ui-model-selection/`](ui-model-selection/README.md) | Provides model selection in conversation surfaces | — |
| [`ui-deepseek-model-label-1017/`](ui-deepseek-model-label-1017/README.md) | Adds the temporary 1017 badge to official DeepSeek Flash model labels | — |
| [`ui-permission-presets/`](ui-permission-presets/README.md) | Configures default permissions and switches the current session's access | — |
| [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control | — |
| [`ui-settings-plugins/`](ui-settings-plugins/README.md) | Owns the Built-in plugins settings section shell and its tab extension point | — |
+1
View File
@@ -68,6 +68,7 @@ kind: "package-group"
| [`ui-jobs/`](ui-jobs/README.zh.md) | 列出本会话的后台任务,并提供按需展开的流式输出面板 | — |
| [`ui-schedule/`](ui-schedule/README.zh.md) | 在只读标题栏目录中列出当前会话中生效的提醒 | — |
| [`ui-model-selection/`](ui-model-selection/README.zh.md) | 在对话界面中提供模型选择 | — |
| [`ui-deepseek-model-label-1017/`](ui-deepseek-model-label-1017/README.zh.md) | 为官方 DeepSeek Flash 模型名称添加临时的 1017 角标 | — |
| [`ui-permission-presets/`](ui-permission-presets/README.zh.md) | 配置默认权限并切换当前会话的访问模式 | — |
| [`ui-plan/`](ui-plan/README.zh.md) | 展示生效中的 plan mode 状态及其退出控件 | — |
| [`ui-settings-plugins/`](ui-settings-plugins/README.zh.md) | 负责「内置插件」设置分区壳及其标签页扩展点 | — |
@@ -0,0 +1,37 @@
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-deepseek-model-label-1017/README.md
/:
en: 21f43d0c704c3668
zh: 0497e58bb871e2c4
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017:
en: aa0cd60e16297d9f
zh: 882cee782592c56b
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/summary:
en: 708be526d5758c04
zh: 136b968ae71c9657
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/table-of-contents:
en: d152484eb41ac6b4
zh: 09388d293f9be9cb
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/use-this-package:
en: a6d5fcecfa93935b
zh: 8ae2809a06910c52
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/understand-the-implementation:
en: ca1d0c5db1b819a0
zh: 960e8db56fa5987e
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/further-exploration:
en: 2885099078047d38
zh: d7de1db3f1ba0a50
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/model-experience:
en: 16ab1406c87419d7
zh: 01bce720d29ab2e8
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/model-experience/kv-cache-effect:
en: b7599ec8e5b66e58
zh: a21537863b9b6e3a
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/known-limitations-and-deferred-work:
en: bce774c6d8ae9428
zh: b9d7019acc7bbc19
/deepseek-ai-dsh-client-ui-deepseek-model-label-1017/known-limitations-and-deferred-work/dev-note:
en: e67f633dc6fe7773
zh: 8ec3e73528ddb1a6
@@ -0,0 +1,91 @@
---
description: "Show a temporary 1017 badge beside DeepSeek Flash in the composer model selector."
kind: "package-reference"
---
# @deepseek-ai/dsh-client-ui-deepseek-model-label-1017
English | [中文](README.zh.md)
## Summary
This package shows `1017` beside `DeepSeek-V4.1-Flash` in the composer model selector. It covers the official API-key and account routes while preserving their catalog names and request IDs. Disable it to restore plain model names.
## Table of Contents
- [Use this package](#use-this-package)
- [Understand the implementation](#understand-the-implementation)
- [Further Exploration](#further-exploration)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
-----
<a id="use-this-package"></a>
## Use this package
The Web bundle includes this plugin, including when Desktop uses that bundle. It has no configuration fields.
The badge appears only when the provider is `deepseek-official` or `deepseek-account`, the model ID is `deepseek-flash`, and the catalog name is exactly `DeepSeek-V4.1-Flash`. Other names and providers keep their catalog text. The input button, current-model menu row, and model options share the badge.
Disable the following row through a profile patch to restore the catalog name:
```yaml
- id: ui-deepseek-model-label-1017
disabled: true
```
-----
<a id="understand-the-implementation"></a>
## Understand the implementation
<details>
<summary>Implementation internals — click to expand</summary>
The browser entry contributes to the existing `conversation.input.model.label` slot through `ctx.slots.inject()`. Its component derives the label from owner props and uses the shared `Tag` primitive; it owns no cache, subscription, network request, or model-selection state. Registration and dictionaries leave with the plugin. The Host entry only makes the browser package available to the Loader.
</details>
-----
<a id="further-exploration"></a>
## Further Exploration
These packages own the selector and its two directory sources.
- [Model selection](../ui-model-selection/README.md) — label placement and selection behavior.
- [DeepSeek transport](../../llm/llm-deepseek/README.md) — built-in model catalog.
- [DeepSeek account](../../llm/llm-deepseek-account/README.md) — account-provided model directory.
-----
<a id="model-experience"></a>
## Model Experience
None, as the package only decorates browser labels without changing model requests or Session events.
#### KV Cache effect
None; provider requests and their cached prefixes are unchanged.
## Known Limitations and Deferred Work
<a id="known-limitations-and-deferred-work"></a>
The fixed release marker has these presentation limits:
- The settings editor, `/model` command, trigger accessibility text, and existing tooltip keep the original catalog name.
- This package occupies a `single` slot. Another model-label plugin at the same priority conflicts; the two presentations do not compose automatically.
- The `1017` marker is fixed client presentation, not a server capability assertion. Remove this package and its bundle row when structured release labels replace it.
<a id="dev-note"></a>
### Dev Note
<details>
<summary>Working context for maintainers — click to expand</summary>
None.
</details>
@@ -0,0 +1,91 @@
---
description: "在输入框的模型选择器中为 DeepSeek Flash 显示临时的 1017 角标。"
kind: "package-reference"
---
# @deepseek-ai/dsh-client-ui-deepseek-model-label-1017
[English](README.md) | 中文
## 概述
本包在输入框模型选择器的 `DeepSeek-V4.1-Flash` 旁显示 `1017`。它覆盖官方 API Key 和账号通道,保留目录名称和请求 ID。停用后恢复纯模型名称。
## 目录
- [使用本包](#use-this-package)
- [理解实现](#understand-the-implementation)
- [进一步探索](#further-exploration)
- [模型体验](#model-experience)
- [已知限制与延期工作](#known-limitations-and-deferred-work)
- [开发备注](#dev-note)
-----
<a id="use-this-package"></a>
## 使用本包
Web bundle 包含本插件,桌面端使用该 bundle 时也会加载。本包没有配置字段。
只有提供方为 `deepseek-official` 或 `deepseek-account`、模型 ID 为 `deepseek-flash`,且目录名称恰好为 `DeepSeek-V4.1-Flash` 时才显示角标。其他名称和提供方保留目录文字。输入框按钮、当前模型菜单行和模型选项共用这个角标。
通过 profile patch 停用以下条目即可恢复目录名称:
```yaml
- id: ui-deepseek-model-label-1017
disabled: true
```
-----
<a id="understand-the-implementation"></a>
## 理解实现
<details>
<summary>内部实现 — 点击展开</summary>
浏览器入口通过 `ctx.slots.inject()` 向已有的 `conversation.input.model.label` slot 注册组件。组件根据 owner props 派生标签,使用共享 `Tag` 基元,不拥有缓存、订阅、网络请求或模型选择状态。注册和字典随插件卸载。Host 入口仅让 Loader 能加载浏览器包。
</details>
-----
<a id="further-exploration"></a>
## 进一步探索
以下包分别拥有选择器和两个目录来源。
- [模型选择](../ui-model-selection/README.zh.md) — 名称显示位置与选择行为。
- [DeepSeek 传输](../../llm/llm-deepseek/README.zh.md) — 内置模型目录。
- [DeepSeek 账号](../../llm/llm-deepseek-account/README.zh.md) — 账号提供的模型目录。
-----
<a id="model-experience"></a>
## 模型体验
无,本包只装饰浏览器标签,不改变模型请求或 Session 事件。
#### KV Cache effect
无;提供方请求及其缓存前缀保持不变。
## 已知限制与延期工作
<a id="known-limitations-and-deferred-work"></a>
固定的版本标记有以下显示限制:
- 设置编辑器、`/model` 命令、触发按钮的无障碍文字及已有提示保留原始目录名称。
- 本包占用 `single` slot。其他模型名称插件若使用相同优先级会冲突,两种展示不会自动组合。
- `1017` 是固定的客户端展示,不是服务端能力声明。结构化版本标签替代它时,移除本包及其 bundle 条目。
<a id="dev-note"></a>
### 开发备注
<details>
<summary>维护者工作上下文 — 点击展开</summary>
无。
</details>
@@ -0,0 +1,66 @@
{
"name": "@deepseek-ai/dsh-client-ui-deepseek-model-label-1017",
"description": "Temporary 1017 release badge for official DeepSeek Flash model labels",
"version": "0.2.1-rc.1",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/client/ui-deepseek-model-label-1017"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./client": {
"types": "./lib/types/client/index.d.ts",
"default": "./lib/client.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json",
"./locale/*.json": "./locale/*.json"
},
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-client-ui-model-selection",
"@deepseek-ai/dsh-client-ui-renderer",
"@deepseek-ai/dsh-client-locale"
],
"platform": "web"
}
},
"scripts": {
"bundle": "tsdown",
"watch": "tsdown --watch"
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:~"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:~",
"@deepseek-ai/dsh-client-locale": "workspace:*",
"@deepseek-ai/dsh-client-ui-model-selection": "workspace:*",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:*",
"@deepseek-ai/dsh-client-ui-primitives": "workspace:*",
"@deepseek-ai/dsh-client-ui-slots": "workspace:*",
"@deepseek-ai/dsh-client-test-runtime": "workspace:*",
"@deepseek-ai/dsh-session": "workspace:*",
"@testing-library/react": "^16.3.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"files": [
"locale/*.json",
"lib/index.js",
"lib/client.js",
"lib/types/**/*.d.ts"
]
}
@@ -0,0 +1,7 @@
.label {
display: inline-flex;
align-items: center;
gap: 4px;
vertical-align: baseline;
white-space: nowrap;
}
@@ -0,0 +1,22 @@
/** Display-only release marking; the catalog and selected route remain unchanged. */
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
import type {} from '@deepseek-ai/dsh-client-ui-model-selection/client'
import { Tag } from '@deepseek-ai/dsh-client-ui-primitives'
import css from './DeepSeekModelLabel.module.css'
const MODEL_NAME = 'DeepSeek-V4.1-Flash'
/**
* Render the fixed 1017 release label for recognized official Flash entries.
* @param props - Provider and model identity plus the badge dictionary.
* @returns The base name with a badge, or the untouched catalog name.
*/
export function DeepSeekModelLabel({ provider, model, t }:
PropsRuntime<'conversation.input.model.label'> & PropsLocale<'deepseek-model-label-1017'>,
) {
const official = provider.id === 'deepseek-account' || provider.id === 'deepseek-official'
if (!official || model.id !== 'deepseek-flash' || model.name !== MODEL_NAME) return model.name
return <span className={css.label}>
<span>{model.name}</span>{' '}<Tag tone="info">{t('badge.label')}</Tag>
</span>
}
@@ -0,0 +1,29 @@
/** Temporary 1017 badge for the two official DeepSeek model routes. */
import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-client-locale/client'
import type {} from '@deepseek-ai/dsh-client-ui-model-selection/client'
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
import { DeepSeekModelLabel } from './DeepSeekModelLabel.tsx'
import { en, zh, type LabelKey } from './locales.ts'
declare module '@deepseek-ai/dsh-client-ui-slots' {
interface LocaleNamespaceMap {
/** The temporary model release badge. */
'deepseek-model-label-1017': LabelKey
}
}
/** Required services: model-label registration and localized badge text. */
export const inject = ['slots', 'locale']
/**
* Decorate existing model labels while their declaring slot is mounted.
* @param ctx - Client plugin context owning the dictionaries and registration.
*/
export function apply(ctx: Context): void {
const namespace = 'deepseek-model-label-1017'
ctx.effect(() => ctx.locale.register(namespace, { zh, en }), 'deepseek-model-label-1017: dictionaries')
ctx.slots.inject('conversation.input.model.label', () => ctx.slots.register({
name: 'conversation.input.model.label', locale: namespace,
}, DeepSeekModelLabel))
}
@@ -0,0 +1,10 @@
/** Localized label of the fixed release marker. */
/** Simplified Chinese badge copy. */
export const zh = { 'badge.label': '1017' }
/** Keys owned by the release-label namespace. */
export type LabelKey = keyof typeof zh
/** English badge copy. */
export const en: Record<LabelKey, string> = { 'badge.label': '1017' }
@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: Record<string, string>
export default classes
}
@@ -0,0 +1,4 @@
/** Loader entry for the temporary DeepSeek model-label presentation. */
/** Mount the browser entry through the package's client declaration. */
export function apply(): void {}
@@ -0,0 +1,74 @@
// @vitest-environment jsdom
import { expect, it, onTestFinished } from 'vitest'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import { SlotTestRuntime } from '@deepseek-ai/dsh-client-test-runtime'
import { SessionId } from '@deepseek-ai/dsh-session/types'
import type { ModelLabelOwner } from '@deepseek-ai/dsh-client-ui-model-selection/client'
import * as HostPlugin from '../src/index.ts'
import * as LabelPlugin from '../src/client/index.ts'
const SLOT = 'conversation.input.model.label'
const NAME = 'DeepSeek-V4.1-Flash'
function owner(provider: string, name = NAME, id = 'deepseek-flash', placement: ModelLabelOwner['placement'] = 'option'): ModelLabelOwner {
return { provider: { id: provider, name: provider }, model: { id, name }, placement, selected: true }
}
async function runtime() {
const host = await SlotTestRuntime.create()
onTestFinished(() => host.dispose())
const locale = new LocaleRuntime(host.ctx)
host.ctx.provide('locale', locale)
host.slots.installLocale(locale)
await host.mount(HostPlugin)
await host.sessions.add({ id: SessionId('release-badge') })
const session = host.sessions.retainFor(host.ctx, SessionId('release-badge'))
return { host, session }
}
const placements = ['trigger', 'menu', 'option'] as const
const targets = ['deepseek-official', 'deepseek-account'].flatMap(provider => placements.map(placement => ({ provider, placement })))
it.each(targets)('shows the badge for $provider at $placement without rewriting its catalog entry', async ({ provider, placement }) => {
const { host, session } = await runtime()
await host.declare({ [SLOT]: { kind: 'single', scope: 'session', inline: true } })
await host.mount(LabelPlugin)
const input = owner(provider, NAME, 'deepseek-flash', placement)
const view = host.renderSlot(SLOT, input, { session, fallback: input.model.name })
expect(view.container.textContent).toBe(`${NAME} 1017`)
expect(view.view.getByText('1017').tagName).toBe('SPAN')
expect(input.model).toEqual({ id: 'deepseek-flash', name: NAME })
})
it.each([
{ label: 'another provider', value: owner('external') },
{ label: 'another model id', value: owner('deepseek-official', NAME, 'other-model') },
{ label: 'a custom name', value: owner('deepseek-account', 'My Flash') },
{ label: 'a dated name', value: owner('deepseek-account', `${NAME}-1017`) },
{ label: 'a later release name', value: owner('deepseek-official', `${NAME}-1024`) },
])('preserves $label', async ({ value }) => {
const { host, session } = await runtime()
await host.declare({ [SLOT]: { kind: 'single', scope: 'session', inline: true } })
await host.mount(LabelPlugin)
const view = host.renderSlot(SLOT, value, { session, fallback: value.model.name })
expect(view.container.textContent).toBe(value.model.name)
expect(view.view.queryByText('1017')).toBeNull()
})
it('waits for the label declaration, follows changed model props, and restores the fallback on unload', async () => {
const { host, session } = await runtime()
const plugin = await host.mount(LabelPlugin)
expect(host.slots.entries(SLOT)).toHaveLength(0)
await host.declare({ [SLOT]: { kind: 'single', scope: 'session', inline: true } })
const input = owner('deepseek-official')
const view = host.renderSlot(SLOT, input, { session, fallback: NAME })
expect(view.container.textContent).toBe(`${NAME} 1017`)
view.update(owner('deepseek-official', 'My Flash'))
expect(view.container.textContent).toBe('My Flash')
view.update(input)
expect(view.container.textContent).toBe(`${NAME} 1017`)
await plugin.dispose()
expect(host.slots.entries(SLOT)).toHaveLength(0)
expect(view.container.textContent).toBe(NAME)
await host.mount(LabelPlugin)
expect(view.container.textContent).toBe(`${NAME} 1017`)
})
@@ -0,0 +1,30 @@
{
"extends": "../../../tsconfig.base.client.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"include": [
"src"
],
"references": [
{
"path": "../../../vendor/cordis"
},
{
"path": "../locale/tsconfig.client.json"
},
{
"path": "../ui-model-selection"
},
{
"path": "../ui-renderer"
},
{
"path": "../ui-primitives"
},
{
"path": "../ui-slots"
}
]
}
@@ -0,0 +1,3 @@
import { clientBundle } from '../tsdown.client.ts'
export default clientBundle(import.meta.dirname, ['lib/types/index.js'])
@@ -1269,8 +1269,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
hookContext: '',
slotInject: '',
declaredBy: 'an entry in \'conversation.input.model\' (client-ui-model-selection), so it exists while that entry is mounted',
occupants: [],
replaceRisk: 'none',
occupants: [
'client-ui-deepseek-model-label-1017 DeepSeekModelLabel',
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.model.label\', () => ctx.slots.register(\n { name: \'conversation.input.model.label\' },\n ({ provider, model }) => React.createElement(\'span\', { title: provider.name }, model.name),\n ))\n },\n}',
source: 'packages/client/ui-model-selection/src/client/slots.ts:36',
},
@@ -74,6 +74,7 @@ Every package below exports a Cordis plugin that a bundle patch can name in a Lo
| `@deepseek-ai/dsh-client-ui-chat` | no | Chat Conversation target, node definitions, renderers, and details surface |
| `@deepseek-ai/dsh-client-ui-commands` | no | Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry |
| `@deepseek-ai/dsh-client-ui-conversation` | no | Target-neutral Conversation assembly, shell, composer, queue, and view navigation |
| `@deepseek-ai/dsh-client-ui-deepseek-model-label-1017` | no | Temporary 1017 release badge for official DeepSeek Flash model labels |
| `@deepseek-ai/dsh-client-ui-deliverables` | no | Changed-files card with per-file comparison tabs, delivery cards, and clickable final-response file references for Web |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | no | In-app directory browsing surface: the workspace directory-flow owner rendering the host's listing and creation primitives |
| `@deepseek-ai/dsh-client-ui-directory-picker-native` | no | Native directory-picker surface: the renderless workspace directory-flow occupant driving the local Desktop or Host OS chooser |
+39
View File
@@ -2772,6 +2772,9 @@ importers:
'@deepseek-ai/dsh-client-ui-cordis':
specifier: workspace:*
version: link:../../extensions/ui-cordis
'@deepseek-ai/dsh-client-ui-deepseek-model-label-1017':
specifier: workspace:*
version: link:../../client/ui-deepseek-model-label-1017
'@deepseek-ai/dsh-client-ui-deliverables':
specifier: workspace:*
version: link:../../client/ui-deliverables
@@ -3892,6 +3895,42 @@ importers:
specifier: 4.4.3
version: 4.4.3
packages/client/ui-deepseek-model-label-1017:
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:~
version: link:../../../vendor/cordis
'@deepseek-ai/dsh-client-locale':
specifier: workspace:*
version: link:../locale
'@deepseek-ai/dsh-client-test-runtime':
specifier: workspace:*
version: link:../../test-support/client-runtime
'@deepseek-ai/dsh-client-ui-model-selection':
specifier: workspace:*
version: link:../ui-model-selection
'@deepseek-ai/dsh-client-ui-primitives':
specifier: workspace:*
version: link:../ui-primitives
'@deepseek-ai/dsh-client-ui-renderer':
specifier: workspace:*
version: link:../ui-renderer
'@deepseek-ai/dsh-client-ui-slots':
specifier: workspace:*
version: link:../ui-slots
'@deepseek-ai/dsh-session':
specifier: workspace:*
version: link:../../core/session
'@testing-library/react':
specifier: ^16.3.3
version: 16.3.3(@testing-library/[email protected])(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])
react:
specifier: ^18.2.0
version: 18.3.1
react-dom:
specifier: ^18.2.0
version: 18.3.1([email protected])
packages/client/ui-deliverables:
dependencies:
'@deepseek-ai/dsh-typert-protocol':
@@ -138,6 +138,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
'packages/client/ui-reference': { kind: 'indirect', reason: 'Browser-side reference selection delegates file guidance and session snapshot preparation to Host-owned providers.' },
'packages/client/ui-commands': { kind: 'indirect', reason: 'The dispatch paths trigger the host command.execute RPC; each command handler\'s host package owns any model-visible effect.' },
'packages/client/ui-model-selection': { kind: 'indirect', reason: 'Selection routes session.selectModel; the Host snapshots the selection at the next prompt-assembly boundary and owns the model-visible effect.' },
'packages/client/ui-deepseek-model-label-1017': { kind: 'none', reason: 'Temporary browser release badge; leaves model catalogs and requests unchanged.' },
'packages/client/ui-goal': { kind: 'indirect', reason: 'The strip verbs route goal.* mutations; the host GoalService owns the model-visible goal/change context message.' },
'packages/extensions/ui-cordis': { kind: 'indirect', reason: 'The definition card drives the host dynamic run/stop verbs that the model\'s cordis_run/cordis_stop tools also reach; the runner owns any model-visible effect.' },
'packages/client/ui-permission-presets': { kind: 'indirect', reason: 'The picker submits the host /permission command; the knob events it appends own the model-visible effect through the sandbox/approval consumers.' },
+1
View File
@@ -290,6 +290,7 @@
"@deepseek-ai/dsh-client-ui-reference": ["./packages/client/ui-reference/src"],
"@deepseek-ai/dsh-client-ui-commands": ["./packages/client/ui-commands/src"],
"@deepseek-ai/dsh-client-ui-model-selection": ["./packages/client/ui-model-selection/src"],
"@deepseek-ai/dsh-client-ui-deepseek-model-label-1017": ["./packages/client/ui-deepseek-model-label-1017/src"],
"@deepseek-ai/dsh-client-ui-goal": ["./packages/client/ui-goal/src"],
"@deepseek-ai/dsh-client-ui-message-feedback": ["./packages/client/ui-message-feedback/src"],
"@deepseek-ai/dsh-client-ui-agent-preset": ["./packages/client/ui-agent-preset/src"],
+1
View File
@@ -124,6 +124,7 @@
{ "path": "./packages/client/ui-goal" },
{ "path": "./packages/client/ui-message-feedback" },
{ "path": "./packages/client/ui-model-selection" },
{ "path": "./packages/client/ui-deepseek-model-label-1017" },
{ "path": "./packages/client/ui-agent-preset" },
{ "path": "./packages/client/ui-permission-presets" },
{ "path": "./packages/client/ui-plan" },
+1
View File
@@ -99,6 +99,7 @@
"apps/web/tests/settings-import.e2e.ts",
"apps/web/tests/models-settings.e2e.ts",
"apps/web/tests/deepseek-messages-settings.e2e.ts",
"apps/web/tests/deepseek-model-label-1017.e2e.ts",
"apps/web/tests/deepseek-messages-chat.e2e.ts",
"apps/web/tests/models-settings-recovery.e2e.ts",
"apps/web/tests/onboarding-deepseek-config.e2e.ts",