diff --git a/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.i18n.yaml b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.i18n.yaml new file mode 100644 index 0000000000..554c1976d5 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.md +2026-08-12-hero-fish-hover-swim-morph.md: c485f94e244040d7e72838065f977e698391c00d +2026-08-12-hero-fish-hover-swim-morph.zh.md: 46fa50732bf98aa5e3afb503e84f8e23001499b2 diff --git a/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.md b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.md new file mode 100644 index 0000000000..c485f94e24 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.md @@ -0,0 +1,27 @@ +# Agent Note: Hero fish hover swim morph + +Status: implemented + +English | [中文](2026-08-12-hero-fish-hover-swim-morph.zh.md) + +## Problem + +Hovering the New Session hero fish (`EmptyHero.tsx` in `dsh-client-ui-conversation`) played a one-shot rigid CSS sway of the whole svg. The user wanted the whale to visibly swim — the tail wagging and the mouth curve lifting — which requires deforming the path geometry itself. CSS transforms cannot bend a subset of a path's curves, and the logo ships as one `FISH_LOGO_PATH` string in `dsh-client-ui-primitives`. + +## Decision + +Real curve deformation via SMIL `` cycling `rest → tail-up → rest → tail-down → rest` on the same 1.6s period as the CSS sway, which becomes continuous (`infinite`) for as long as the pointer stays. The two morph targets are generated programmatically (`/tmp`-run script, not checked in): parse `FISH_LOGO_PATH`'s absolute M/C/L/Z commands, rotate the tail region about a pivot with smoothstep falloff weights, bend the mouth/fin swoosh vertically with weight-squared falloff from its body anchor (a smile lift, not a rigid swing — rigid rotation read as detached), and emit structure-identical command strings SMIL can interpolate. The baked path constants live next to the component with the generation parameters documented. SMIL cannot ride CSS media queries, so a `hovering` state gated by `matchMedia('(prefers-reduced-motion: reduce)')` mounts the morph, while the CSS sway sits under `@media (hover: hover) and (prefers-reduced-motion: no-preference)`. + +The morphing fish reaches the hero as the fallback of the `conversation.hero.brand.mark` slot; no shipped package occupies it — `dsh-client-ui-brand-official` fills only the sidebar slots, since a feature plugin may not value-import `HeroFish` across packages ([client cross-package rule](../process/2026-08-23-client-cross-package-value-dependencies.md)) and the fallback already is the official mark. `FISH_LOGO_PATH` and `FISH_LOGO_VIEWBOX` are exported from `dsh-client-ui-primitives` for consumers that compose their own svg around the same geometry. + +## Alternatives considered + +**Vector-tool path editing for the morphs.** No interactive tool in the loop; programmatic weighted deformation was chosen because it guarantees the identical command structure SMIL `d` interpolation requires and makes amplitudes reviewable numbers. + +**Blowhole spout on hover.** Removed at the user's request; hover keeps only shape morph and sway. + +**Occupying the hero slot with the official mark.** The previous arrangement; rejected because the static occupant shadowed the animated fallback, and animating the occupant instead would need the forbidden cross-package value import. + +## Consequences + +The hover swim is decorative (`aria-hidden`) and reduced-motion-safe (static logo on hover). The sway CSS targets the stationary `.fishHitbox` wrapper, so a slot occupant would sway too; the body morph lives only in the fallback `HeroFish`. Coverage is the `skeleton.client.spec.tsx` suite asserting slot contract (name, owner props, fallback existence); the keyless snapshot harness records transcripts, not browser animation, so visual verification of the morph stays manual. Regenerating the morph targets requires re-running the (uncommitted) deformation script against `FISH_LOGO_PATH`; if the logo geometry ever changes, the baked constants must be regenerated with it. diff --git a/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.zh.md b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.zh.md new file mode 100644 index 0000000000..46fa50732b --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-12-hero-fish-hover-swim-morph.zh.md @@ -0,0 +1,27 @@ +# Agent Note:主页鲸鱼 hover 游动变形 + +Status: implemented + +[English](2026-08-12-hero-fish-hover-swim-morph.md) | 中文 + +## 问题 + +hover New Session 主页的鲸鱼(`dsh-client-ui-conversation` 的 `EmptyHero.tsx`)原本只播放一次整个 svg 的刚性 CSS 摇摆。用户希望鲸鱼有真实的游动感——尾巴摆动、嘴巴曲线上扬,这要求对路径几何本身做变形。CSS transform 无法弯曲路径中的部分曲线,且 logo 以单一 `FISH_LOGO_PATH` 字符串存放在 `dsh-client-ui-primitives`。 + +## 决定 + +通过 SMIL `` 做真实曲线变形,按与 CSS 摇摆相同的 1.6s 周期循环 `静止 → 尾上摆 → 静止 → 尾下压 → 静止`;CSS 摇摆改为持续循环(`infinite`),指针停留多久就游多久。两个变形目标由程序生成(在 `/tmp` 运行的脚本,未入库):解析 `FISH_LOGO_PATH` 的绝对 M/C/L/Z 命令,尾部区域绕支点做带 smoothstep 衰减权重的旋转,嘴巴/鳍的内侧曲线以距身体锚点的权重平方做竖直弯曲(微笑式上扬,而非刚性摆动——刚性旋转看起来与身体脱节),并输出结构完全一致、SMIL 可插值的命令串。烘焙出的路径常量与组件放在一起,并在注释中记录生成参数。SMIL 无法响应 CSS 媒体查询,因此用经 `matchMedia('(prefers-reduced-motion: reduce)')` 判定的 `hovering` 状态控制变形挂载,CSS 摇摆则在 `@media (hover: hover) and (prefers-reduced-motion: no-preference)` 之下。 + +变形鲸鱼以 `conversation.hero.brand.mark` slot 的 fallback 身份进入主页;没有任何发布包占据该 slot——`dsh-client-ui-brand-official` 只填充侧栏槽位,因为 feature 插件不得跨包 value-import `HeroFish`([client 跨包规则](../process/2026-08-23-client-cross-package-value-dependencies.zh.md)),而 fallback 本身就是官方标志。`FISH_LOGO_PATH` 与 `FISH_LOGO_VIEWBOX` 从 `dsh-client-ui-primitives` 导出,供围绕同一几何自行组装 svg 的消费方使用。 + +## 考虑过的替代方案 + +**用矢量工具编辑路径做变形。** 流程中没有可交互的工具;选择程序化加权变形,因为它保证 SMIL `d` 插值所要求的完全一致的命令结构,且振幅是可评审的数字。 + +**hover 气孔喷水。** 按用户要求移除;hover 只保留形状变形与摇摆。 + +**让官方标志占据主页 slot。** 即先前的安排;否决,因为静态 occupant 会遮住动画 fallback,而给 occupant 加动画又需要被禁止的跨包 value import。 + +## 影响 + +hover 游动是纯装饰(`aria-hidden`)且对 reduced-motion 安全(hover 保持静态 logo)。摇摆 CSS 作用于外层静止的 `.fishHitbox`,因此换成 slot occupant 也会摇摆;身体变形只存在于 fallback 的 `HeroFish` 中。覆盖由 `skeleton.client.spec.tsx` 断言 slot 合约(名称、owner props、fallback 存在性);keyless 快照体系记录对话转录而非浏览器动画,变形的视觉验证仍需人工。重新生成变形目标需要对 `FISH_LOGO_PATH` 重跑(未入库的)变形脚本;若 logo 几何将来变化,烘焙常量必须随之重新生成。 diff --git a/docs/module-graph.i18n.yaml b/docs/module-graph.i18n.yaml index 41e0d5d0e8..73269aadfb 100644 --- a/docs/module-graph.i18n.yaml +++ b/docs/module-graph.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 docs/module-graph.md -module-graph.md: f9cafb86b22012c2dc1198ff810ededd988911f9 -module-graph.zh.md: ec4ac506cfef4d8ceaf3c5f83f07dad1fee66c50 +module-graph.md: 90c0f2061d27fa6410dd867edb94089440fab006 +module-graph.zh.md: fb3d7c7f6a22b9ff2f6b41ed9745c050aa483759 diff --git a/docs/module-graph.md b/docs/module-graph.md index f9cafb86b2..90c0f2061d 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -1462,7 +1462,6 @@ flowchart TD pkg_client_ui_approval --> pkg_llm pkg_client_ui_approval --> pkg_session pkg_client_ui_approval --> pkg_typert_protocol - pkg_client_ui_brand_official --> pkg_client_ui_conversation pkg_client_ui_brand_official --> pkg_client_ui_renderer pkg_client_ui_brand_official --> pkg_client_ui_sidebar pkg_client_ui_brand_official --> pkg_invariants @@ -1955,7 +1954,7 @@ flowchart TD | [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) | | [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`agent-presets`](../packages/preset/agent-presets), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | | [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index ec4ac506cf..fb3d7c7f6a 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -1464,7 +1464,6 @@ flowchart TD pkg_client_ui_approval --> pkg_llm pkg_client_ui_approval --> pkg_session pkg_client_ui_approval --> pkg_typert_protocol - pkg_client_ui_brand_official --> pkg_client_ui_conversation pkg_client_ui_brand_official --> pkg_client_ui_renderer pkg_client_ui_brand_official --> pkg_client_ui_sidebar pkg_client_ui_brand_official --> pkg_invariants @@ -1957,7 +1956,7 @@ flowchart TD | [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) | | [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`agent-presets`](../packages/preset/agent-presets), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | | [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | diff --git a/packages/client/ui-brand-official/README.i18n.yaml b/packages/client/ui-brand-official/README.i18n.yaml index e29dcc623f..4a60183c6e 100644 --- a/packages/client/ui-brand-official/README.i18n.yaml +++ b/packages/client/ui-brand-official/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 packages/client/ui-brand-official/README.md -README.md: 03712e826fb415dd6fb3125c06865a2402342948 -README.zh.md: 94e8a30bb11abd468a59cc80b6745d5d929a1e23 +README.md: 6b613db7c302e77f4bbcd93468e712beb5cf2a61 +README.zh.md: a01f659dcd76b98b94607c764988b5295e761b76 diff --git a/packages/client/ui-brand-official/README.md b/packages/client/ui-brand-official/README.md index 03712e826f..6b613db7c3 100644 --- a/packages/client/ui-brand-official/README.md +++ b/packages/client/ui-brand-official/README.md @@ -1,5 +1,5 @@ --- -description: "Official DeepSeek Harness brand occupants for the sidebar and conversation hero, active only in official builds; for users and maintainers choosing or replacing brand presentation." +description: "Official DeepSeek Harness brand occupants for the sidebar, active only in official builds; for users and maintainers choosing or replacing brand presentation." kind: "package-reference" --- @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -This package fills the browser brand slots — `sidebar.brand.mark`, `sidebar.brand.name`, and `conversation.hero.brand.mark` — with the official DeepSeek Harness mark and name. It registers these occupants only when the client bundle builds with the `official` profile; every other build loads the plugin but registers nothing, so the shell fallbacks stay visible. Choose it when the deployed identity is DeepSeek's own; a deployment with its own brand composes a different package into the same slots instead. It retains no runtime state and contributes nothing to model requests. +This package fills the sidebar brand slots — `sidebar.brand.mark` and `sidebar.brand.name` — with the official DeepSeek Harness mark and name. It registers these occupants only when the client bundle builds with the `official` profile; every other build loads the plugin but registers nothing, so the shell fallbacks stay visible. The conversation hero slot (`conversation.hero.brand.mark`) stays unoccupied in every build: its declaring package renders the animated hero fish (hover swim morph) as the fallback, and the official brand is that fish. Choose this package when the deployed identity is DeepSeek's own; a deployment with its own brand composes a different package into the same slots instead. It retains no runtime state and contributes nothing to model requests. ## Table of Contents @@ -29,11 +29,11 @@ Mount this plugin in the browser roster of a deployment whose identity is DeepSe ### Choosing the profile -`DSH_CLIENT_BUILD_PROFILE` selects which brand renders. An `official` build shows the official mark and name in the sidebar and the mark in the conversation hero; any other value leaves the shell fallbacks — the fish mark and the local-build label — in place. The plugin still loads and validates in both cases; only the registration is profile-gated. +`DSH_CLIENT_BUILD_PROFILE` selects which brand renders. An `official` build shows the official mark and name in the sidebar; any other value leaves the shell fallbacks — the fish mark and the local-build label — in place. The conversation hero shows the animated hero fish from `dsh-client-ui-conversation` regardless of profile, because that fallback is already the official mark. The plugin still loads and validates in both cases; only the registration is profile-gated. ### Replacing the brand -A deployment with its own identity leaves this package out and composes another package that occupies the same three slots. Occupying a slot is the only composition route; there is no brand configuration surface here. +A deployment with its own identity leaves this package out and composes another package that occupies the sidebar slots — and the hero slot, which this package leaves on its fallback. Occupying a slot is the only composition route; there is no brand configuration surface here. ----- @@ -43,7 +43,7 @@ A deployment with its own identity leaves this package out and composes another
Implementation internals — click to expand -The three occupants install as one declaration-aware registration set: nested `ctx.slots.inject()` calls wait on the sidebar and conversation declarations, so the set works whether this row activates before or after the declarers, withdraws all three occupants when either declaration collapses, and leaves no partial brand mix during HMR. The browser half is [`src/client/index.ts`](src/client/index.ts); the node half is an empty Loader seat. The browser title is a build-environment concern (`DSH_CLIENT_TITLE`), outside the slot system. +The two occupants install as one declaration-aware registration set: nested `ctx.slots.inject()` calls wait on the sidebar declaration, so the set works whether this row activates before or after the declarer, withdraws both occupants when the declaration collapses, and leaves no partial brand mix during HMR. The browser half is [`src/client/index.ts`](src/client/index.ts); the node half is an empty Loader seat. The browser title is a build-environment concern (`DSH_CLIENT_TITLE`), outside the slot system.
diff --git a/packages/client/ui-brand-official/README.zh.md b/packages/client/ui-brand-official/README.zh.md index 94e8a30bb1..a01f659dcd 100644 --- a/packages/client/ui-brand-official/README.zh.md +++ b/packages/client/ui-brand-official/README.zh.md @@ -1,5 +1,5 @@ --- -description: "面向侧栏与会话首屏的官方 DeepSeek Harness 品牌填充,仅在官方构建中生效;供选择或替换品牌呈现的用户与维护者阅读。" +description: "面向侧栏的官方 DeepSeek Harness 品牌填充,仅在官方构建中生效;供选择或替换品牌呈现的用户与维护者阅读。" kind: "package-reference" --- @@ -9,7 +9,7 @@ kind: "package-reference" ## 概述 -本包向浏览器品牌槽位——`sidebar.brand.mark`、`sidebar.brand.name` 与 `conversation.hero.brand.mark`——填充官方 DeepSeek Harness 标志与名称。它只在客户端以 `official` profile 构建时注册这些填充;其余构建同样加载插件但不注册任何内容,因此外壳回退保持可见。当部署身份就是 DeepSeek 自身时选择它;自有品牌的部署改为在相同槽位中组合另一个包。它不保留任何运行时状态,也不向模型请求贡献任何内容。 +本包向侧栏品牌槽位——`sidebar.brand.mark` 与 `sidebar.brand.name`——填充官方 DeepSeek Harness 标志与名称。它只在客户端以 `official` profile 构建时注册这些填充;其余构建同样加载插件但不注册任何内容,因此外壳回退保持可见。会话首屏槽位(`conversation.hero.brand.mark`)在所有构建中都保持无填充:其声明包以动画首屏鱼(悬停游动形变)作为回退渲染,而官方品牌正是这条鱼。当部署身份就是 DeepSeek 自身时选择本包;自有品牌的部署改为在相同槽位中组合另一个包。它不保留任何运行时状态,也不向模型请求贡献任何内容。 ## 目录 @@ -29,11 +29,11 @@ kind: "package-reference" ### 选择 profile -`DSH_CLIENT_BUILD_PROFILE` 决定渲染哪个品牌。`official` 构建在侧栏显示官方标志与名称、在会话首屏显示标志;任何其他取值都让外壳回退——鱼形标志与本地构建标签——保持原样。两种情况下插件都会照常加载并通过校验;只有注册受 profile 门控。 +`DSH_CLIENT_BUILD_PROFILE` 决定渲染哪个品牌。`official` 构建在侧栏显示官方标志与名称;任何其他取值都让外壳回退——鱼形标志与本地构建标签——保持原样。会话首屏无论 profile 如何都显示来自 `dsh-client-ui-conversation` 的动画首屏鱼,因为这个回退本身就是官方标志。两种情况下插件都会照常加载并通过校验;只有注册受 profile 门控。 ### 替换品牌 -自有身份的部署不组合本包,而是组合另一个占据相同三个槽位的包。占据槽位是唯一的组合路径;这里不存在任何品牌配置面。 +自有身份的部署不组合本包,而是组合另一个占据侧栏槽位——以及本包留给回退的首屏槽位——的包。占据槽位是唯一的组合路径;这里不存在任何品牌配置面。 ----- @@ -43,7 +43,7 @@ kind: "package-reference"
实现细节——点击展开 -三个填充作为一组声明感知的注册安装:嵌套的 `ctx.slots.inject()` 调用等待侧栏与会话声明,因此无论本行在声明者之前还是之后激活,这组注册都能工作;任一声明消失时全部三个填充一并撤回,HMR 期间也不会留下残缺的品牌混合。浏览器半部是 [`src/client/index.ts`](src/client/index.ts);node 半部是一个空 Loader 座位。浏览器标题是构建环境的事(`DSH_CLIENT_TITLE`),不在槽位系统之内。 +两个填充作为一组声明感知的注册安装:嵌套的 `ctx.slots.inject()` 调用等待侧栏声明,因此无论本行在声明者之前还是之后激活,这组注册都能工作;声明消失时两个填充一并撤回,HMR 期间也不会留下残缺的品牌混合。浏览器半部是 [`src/client/index.ts`](src/client/index.ts);node 半部是一个空 Loader 座位。浏览器标题是构建环境的事(`DSH_CLIENT_TITLE`),不在槽位系统之内。
diff --git a/packages/client/ui-brand-official/package.json b/packages/client/ui-brand-official/package.json index b40b6d8664..791ce76c9c 100644 --- a/packages/client/ui-brand-official/package.json +++ b/packages/client/ui-brand-official/package.json @@ -1,6 +1,6 @@ { "name": "@deepseek-ai/dsh-client-ui-brand-official", - "description": "Official DeepSeek Harness brand occupants for the Web client's sidebar and conversation Hero slots", + "description": "Official DeepSeek Harness brand occupants for the Web client's sidebar slots", "version": "0.1.2-alpha.1", "publishConfig": { "access": "public" @@ -32,7 +32,6 @@ "dsh": { "client": { "inject": [ - "@deepseek-ai/dsh-client-ui-conversation", "@deepseek-ai/dsh-client-ui-renderer", "@deepseek-ai/dsh-client-ui-sidebar" ], @@ -45,14 +44,12 @@ }, "license": "MIT", "peerDependencies": { - "@deepseek-ai/dsh-client-ui-conversation": "workspace:^", "@deepseek-ai/dsh-client-ui-renderer": "workspace:^", "@deepseek-ai/dsh-client-ui-sidebar": "workspace:^", "@deepseek-ai/dsh-invariants": "workspace:^", "@deepseek-ai/cordis": "workspace:^" }, "devDependencies": { - "@deepseek-ai/dsh-client-ui-conversation": "workspace:^", "@deepseek-ai/dsh-client-ui-primitives": "workspace:^", "@deepseek-ai/dsh-client-ui-renderer": "workspace:^", "@deepseek-ai/dsh-client-ui-sidebar": "workspace:^", diff --git a/packages/client/ui-brand-official/src/client/Brand.tsx b/packages/client/ui-brand-official/src/client/Brand.tsx index 4e0a60fd26..9ac6dd7ce2 100644 --- a/packages/client/ui-brand-official/src/client/Brand.tsx +++ b/packages/client/ui-brand-official/src/client/Brand.tsx @@ -1,16 +1,13 @@ import { BrandWordmark, FishLogo } from '@deepseek-ai/dsh-client-ui-primitives' -import type { HeroBrandMarkOwnerProps } from '@deepseek-ai/dsh-client-ui-conversation/client' import type { SidebarBrandMarkOwnerProps } from '@deepseek-ai/dsh-client-ui-sidebar/client' -type OfficialBrandMarkProps = HeroBrandMarkOwnerProps & SidebarBrandMarkOwnerProps - /** * Render the official mark with the presentation requested by its host surface. * @param props - Host-supplied mark presentation. * @returns the official whale mark. */ -export function OfficialBrandMark({ size, className }: OfficialBrandMarkProps) { - return +export function OfficialBrandMark({ size }: SidebarBrandMarkOwnerProps) { + return } /** diff --git a/packages/client/ui-brand-official/src/client/index.ts b/packages/client/ui-brand-official/src/client/index.ts index 237272bd1d..332a5f6007 100644 --- a/packages/client/ui-brand-official/src/client/index.ts +++ b/packages/client/ui-brand-official/src/client/index.ts @@ -1,6 +1,5 @@ /** Official DeepSeek Harness occupants for the generic browser-brand slots. */ import type { Context as ClientContext } from '@deepseek-ai/cordis' -import type {} from '@deepseek-ai/dsh-client-ui-conversation/client' import type {} from '@deepseek-ai/dsh-client-ui-renderer/client' import type {} from '@deepseek-ai/dsh-client-ui-sidebar/client' import { OfficialBrandMark, OfficialBrandName } from './Brand.tsx' @@ -9,16 +8,16 @@ import { OfficialBrandMark, OfficialBrandName } from './Brand.tsx' export const inject = ['slots'] /** - * Fill every shipped brand slot as one declaration-aware registration set. + * Fill the sidebar brand slots as one declaration-aware registration set. The + * conversation hero stays on its declaring package's animated fish fallback, + * so the official build registers nothing there. * @param ctx - Client root context. */ export function apply(ctx: ClientContext): void { if (process.env.DSH_CLIENT_BUILD_PROFILE !== 'official') return ctx.slots.inject('sidebar.brand.mark', () => - ctx.slots.inject('sidebar.brand.name', () => - ctx.slots.inject('conversation.hero.brand.mark', function* () { - yield ctx.slots.register({ name: 'sidebar.brand.mark' }, OfficialBrandMark) - yield ctx.slots.register({ name: 'sidebar.brand.name' }, OfficialBrandName) - yield ctx.slots.register({ name: 'conversation.hero.brand.mark' }, OfficialBrandMark) - }))) + ctx.slots.inject('sidebar.brand.name', function* () { + yield ctx.slots.register({ name: 'sidebar.brand.mark' }, OfficialBrandMark) + yield ctx.slots.register({ name: 'sidebar.brand.name' }, OfficialBrandName) + })) } diff --git a/packages/client/ui-brand-official/tests/browser-plugin.client.spec.tsx b/packages/client/ui-brand-official/tests/browser-plugin.client.spec.tsx index 8fa5ca8056..36e8c62488 100644 --- a/packages/client/ui-brand-official/tests/browser-plugin.client.spec.tsx +++ b/packages/client/ui-brand-official/tests/browser-plugin.client.spec.tsx @@ -14,16 +14,17 @@ afterEach(() => { const HOLES = [ 'sidebar.brand.mark', 'sidebar.brand.name', - 'conversation.hero.brand.mark', ] as const +const HERO_HOLE = 'conversation.hero.brand.mark' + async function bench(declare = true) { const ctx = new Context() await ctx.plugin(SlotRegistry).await() const slots = ctx.get('slots') as SlotRegistry const declareHoles = () => slots.register({ name: 'root', - children: Object.fromEntries(HOLES.map(name => [name, { kind: 'single', scope: 'root' }])), + children: Object.fromEntries([...HOLES, HERO_HOLE].map(name => [name, { kind: 'single', scope: 'root' }])), } as never, () => null) const disposeHoles = declare ? declareHoles() : undefined return { ctx, slots, declareHoles, disposeHoles } @@ -65,14 +66,20 @@ describe('official browser-brand plugin', () => { for (const hole of HOLES) expect(after.slots.entries(hole)).toHaveLength(1) }) + it('leaves the conversation hero on its declaring fallback even in official builds', async () => { + vi.stubEnv('DSH_CLIENT_BUILD_PROFILE', 'official') + const subject = await bench() + await subject.ctx.plugin({ inject: [...inject], apply }).await() + expect(subject.slots.entries(HERO_HOLE)).toHaveLength(0) + }) + it('renders the official name independently from both requested mark sizes', () => { const name = render() expect(name.container.querySelector('svg')?.getAttribute('viewBox')).toBe('26 0 156 24') name.unmount() - const mark = render() + const mark = render() expect(mark.container.querySelector('svg')?.getAttribute('width')).toBe('34') - expect(mark.container.querySelector('svg')?.getAttribute('class')).toBe('hero-mark') mark.rerender() expect(mark.container.querySelector('svg')?.getAttribute('width')).toBe('24') }) diff --git a/packages/client/ui-brand-official/tsconfig.json b/packages/client/ui-brand-official/tsconfig.json index 0ba9d872dd..479e3ad74a 100644 --- a/packages/client/ui-brand-official/tsconfig.json +++ b/packages/client/ui-brand-official/tsconfig.json @@ -14,9 +14,6 @@ { "path": "../ui-renderer" }, - { - "path": "../ui-conversation" - }, { "path": "../ui-primitives" }, diff --git a/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx b/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx index 10dfddc2d6..6c4820ed4f 100644 --- a/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/EmptyHero.tsx @@ -1,9 +1,10 @@ // The composer remains in ConversationRoot so switching out of the blank-draft // phase does not remount its textarea. +import { useState } from 'react' import type { ReactNode, RefObject } from 'react' import { - FishLogo, IconChevronDownOutline14, IconFolderClose16, IconFolderOpen16, + FISH_LOGO_PATH, FISH_LOGO_VIEWBOX, IconChevronDownOutline14, IconFolderClose16, IconFolderOpen16, } from '@deepseek-ai/dsh-client-ui-primitives' import { workspaceTitleOf } from '@deepseek-ai/dsh-util-workspace-path' import type { ConversationSlotProps } from '../contract/slots.ts' @@ -70,22 +71,87 @@ export interface HeroShellProps { children?: ReactNode } +/* Hover swim morph targets: the resting FISH_LOGO_PATH with weighted + regional deformation baked in (generated programmatically — parse the + path's absolute M/C/L/Z commands, displace points with smoothstep falloff + weights, emit the same command structure so SMIL can interpolate `d` + between them). Tail: rotation about (15.6, 5.2) with weight growing toward + the tail tip (x>15, y<8.5) — UP -7°, DOWN +6°. Mouth/fin swoosh: a bend, + not a rotation — vertical lift with weight-squared falloff from the body + anchor (14.1, 15.0), so the near end stays seated and the mouth corner + sweeps most, a smile lift (UP -0.7 units at the tip, DOWN +0.5). The eye + subpaths carry zero weight and stay fixed. */ +const HERO_SWIM_UP_PATH = + 'M22.403 0.567C22.145 0.477 22.068 0.718 21.939 0.85C21.895 0.893 21.86 0.947 21.824 0.997C21.515 1.421 21.13 1.721 20.591 1.77C19.829 1.867 19.221 2.244 18.712 2.958C18.535 2.227 18.116 1.839 17.516 1.626C17.203 1.506 16.887 1.379 16.663 1.064C16.508 0.839 16.462 0.581 16.383 0.329C16.332 0.176 16.283 0.02 16.121 -0.002C15.944 -0.029 15.875 0.133 15.805 0.269C15.52 0.822 15.408 1.43 15.42 2.046C15.449 3.432 16.031 4.532 17.202 5.274C17.337 5.356 17.374 5.445 17.335 5.582C17.261 5.862 17.169 6.134 17.086 6.413C17.032 6.59 16.952 6.63 16.764 6.558C16.118 6.301 15.562 5.909 15.074 5.433C14.248 4.633 13.5 3.751 12.568 3.06C12.349 2.898 12.13 2.748 11.903 2.605C10.952 1.682 12.028 0.923 12.277 0.833C12.537 0.739 12.367 0.416 11.526 0.42C10.684 0.424 9.914 0.706 8.933 1.081C8.789 1.138 8.638 1.179 8.484 1.213C7.593 1.044 6.668 1.006 5.702 1.115C3.883 1.318 2.43 2.178 1.362 3.646C0.079 5.41 -0.223 7.415 0.147 9.506C0.535 11.71 1.66 13.535 3.389 14.962C5.181 16.441 7.246 17.166 9.601 17.027C11.032 16.944 12.624 16.753 14.421 15.232C14.874 15.458 15.35 15.548 16.138 15.615C16.746 15.672 17.331 15.585 17.784 15.491C18.493 15.341 18.444 14.684 18.188 14.564C16.108 13.595 16.565 13.989 16.15 13.67C17.206 12.42 18.82 10.198 19.363 7.086C19.421 6.709 19.484 6.171 19.469 5.866C19.458 5.681 19.493 5.604 19.681 5.556C20.199 5.412 20.691 5.172 21.125 4.806C22.366 3.824 22.758 2.554 22.708 1.1C22.7 0.878 22.649 0.654 22.403 0.567ZM11.175 14.451C9.159 12.726 8.182 12.088 7.778 12.067C7.401 12.047 7.469 12.505 7.552 12.807C7.639 13.103 7.752 13.313 7.91 13.581C8.02 13.758 8.095 14.01 7.801 14.16C7.152 14.487 6.023 13.806 5.97 13.772C4.657 12.85 3.559 11.766 2.785 10.369C2.037 9.025 1.603 7.583 1.532 6.044C1.513 5.672 1.622 5.541 1.992 5.473C2.479 5.383 2.981 5.364 3.468 5.436C5.525 5.736 7.276 6.675 8.744 8.323C9.582 9.299 10.216 10.425 10.869 11.496C11.563 12.592 12.31 13.603 13.262 14.414C13.598 14.696 13.866 14.91 14.123 15.068C13.349 15.154 12.058 15.167 11.175 14.452L11.175 14.451ZM12.141 8.26C12.141 8.095 12.273 7.963 12.439 7.963C12.476 7.963 12.511 7.971 12.541 7.982C12.582 7.997 12.62 8.019 12.65 8.053C12.704 8.106 12.733 8.181 12.733 8.26C12.733 8.425 12.601 8.556 12.435 8.556C12.27 8.556 12.141 8.425 12.141 8.26ZM15.142 9.799C14.949 9.878 14.757 9.945 14.572 9.953C14.284 9.968 13.972 9.851 13.802 9.709C13.537 9.487 13.348 9.363 13.27 8.977C13.236 8.812 13.255 8.556 13.284 8.41C13.352 8.094 13.277 7.892 13.055 7.708C12.873 7.558 12.643 7.516 12.39 7.516C12.296 7.516 12.209 7.475 12.145 7.441C12.039 7.389 11.952 7.257 12.035 7.096C12.062 7.043 12.19 6.916 12.22 6.893C12.563 6.698 12.96 6.762 13.326 6.908C13.665 7.047 13.922 7.302 14.292 7.663C14.669 8.098 14.738 8.218 14.953 8.545C15.123 8.801 15.277 9.063 15.383 9.364C15.447 9.551 15.364 9.705 15.142 9.799Z' +const HERO_SWIM_DOWN_PATH = + 'M23.271 2.216C23.039 2.071 22.91 2.287 22.755 2.388C22.703 2.42 22.656 2.464 22.61 2.505C22.214 2.848 21.771 3.054 21.225 2.956C20.412 2.784 19.68 2.919 19.005 3.435C18.92 2.663 18.493 2.157 17.808 1.798C17.446 1.621 17.08 1.449 16.83 1.111C16.656 0.872 16.611 0.612 16.524 0.354C16.469 0.198 16.414 0.039 16.223 0.009C16.017 -0.024 15.936 0.137 15.856 0.271C15.539 0.822 15.418 1.43 15.429 2.046C15.454 3.432 16.041 4.538 17.196 5.36C17.325 5.456 17.356 5.547 17.312 5.674C17.229 5.936 17.134 6.191 17.051 6.454C16.999 6.623 16.921 6.659 16.738 6.58C16.107 6.306 15.56 5.909 15.074 5.433C14.248 4.633 13.5 3.751 12.568 3.06C12.349 2.898 12.13 2.748 11.903 2.605C10.952 1.682 12.028 0.923 12.277 0.833C12.537 0.739 12.367 0.416 11.526 0.42C10.684 0.424 9.914 0.706 8.933 1.081C8.789 1.138 8.638 1.179 8.484 1.213C7.593 1.044 6.668 1.006 5.702 1.115C3.883 1.318 2.43 2.178 1.362 3.646C0.079 5.41 -0.223 7.415 0.147 9.506C0.535 11.71 1.66 13.535 3.389 14.962C5.181 16.441 7.246 17.166 9.601 17.027C11.032 16.944 12.624 16.753 14.421 15.232C14.874 15.458 15.35 15.548 16.138 15.615C16.746 15.672 17.331 15.585 17.784 15.491C18.493 15.341 18.444 14.684 18.188 14.564C16.108 13.595 16.565 13.989 16.15 13.67C17.206 12.42 18.82 10.198 19.278 7.246C19.318 6.948 19.375 6.534 19.371 6.293C19.371 6.145 19.411 6.092 19.597 6.098C20.113 6.109 20.619 6.051 21.096 5.891C22.503 5.375 23.169 4.232 23.448 2.804C23.49 2.586 23.491 2.356 23.271 2.216ZM11.175 14.49C9.159 13.005 8.182 12.567 7.778 12.621C7.401 12.673 7.469 13.087 7.552 13.354C7.639 13.619 7.752 13.797 7.91 14.024C8.02 14.175 8.095 14.406 7.801 14.609C7.152 15.063 6.023 14.63 5.97 14.609C4.657 13.941 3.559 12.965 2.785 11.569C2.037 10.225 1.603 8.783 1.532 7.244C1.513 6.872 1.622 6.741 1.992 6.673C2.479 6.583 2.981 6.564 3.468 6.636C5.525 6.936 7.276 7.843 8.744 9.163C9.582 9.888 10.216 10.783 10.869 11.679C11.563 12.659 12.31 13.617 13.262 14.415C13.598 14.696 13.866 14.91 14.123 15.068C13.349 15.155 12.058 15.177 11.175 14.491L11.175 14.49ZM12.141 8.26C12.141 8.095 12.273 7.963 12.439 7.963C12.476 7.963 12.511 7.971 12.541 7.982C12.582 7.997 12.62 8.019 12.65 8.053C12.704 8.106 12.733 8.181 12.733 8.26C12.733 8.425 12.601 8.556 12.435 8.556C12.27 8.556 12.141 8.425 12.141 8.26ZM15.142 9.799C14.949 9.878 14.757 9.945 14.572 9.953C14.284 9.968 13.972 9.851 13.802 9.709C13.537 9.487 13.348 9.363 13.27 8.977C13.236 8.812 13.255 8.556 13.284 8.41C13.352 8.094 13.277 7.892 13.055 7.708C12.873 7.558 12.643 7.516 12.39 7.516C12.296 7.516 12.209 7.475 12.145 7.441C12.039 7.389 11.952 7.257 12.035 7.096C12.062 7.043 12.19 6.916 12.22 6.893C12.563 6.698 12.96 6.762 13.326 6.908C13.665 7.047 13.922 7.302 14.292 7.663C14.669 8.098 14.738 8.218 14.953 8.545C15.123 8.801 15.277 9.063 15.383 9.364C15.447 9.551 15.364 9.705 15.142 9.799Z' + +/** + * The hero fish (34px wide), static at rest. Hovering swims the whale in + * place: a gentle head-up sway (CSS, on the hitbox hover) while the body + * itself morphs — SMIL interpolates `d` through the tail-up and tail-down + * targets on the same 1.6s period, so the tail wags and the fin flutters in + * real curve deformation. Decorative — hidden from the accessibility tree; + * reduced motion keeps the static filled logo on hover (sampled at + * mouseenter; a mid-hover preference change takes effect on the next enter). + * @param props.hovering - driven by the hitbox parent's pointer state. + * @returns the fish svg element. + */ +function HeroFish({ hovering }: { hovering: boolean }) { + return ( + + ) +} + /** * Render the hero chrome (headline only; no composer, no workspace row). * @param props - see {@link HeroShellProps}. * @returns the centered hero element tree. */ export function HeroShell({ t, renderSlot, children }: HeroShellProps) { + const [hovering, setHovering] = useState(false) return (
- + {/* figma 34:10412: fish 34×25 leading the headline, gap 10. */} + { + if (window.matchMedia('(hover: hover) and (prefers-reduced-motion: no-preference)').matches) { + setHovering(true) + } + }} + onMouseLeave={() => { setHovering(false) }} + > {renderSlot('conversation.hero.brand.mark', { size: 34, className: css.fish }, { - fallback: , + fallback: , })} - {t('hero.headline')} + + {t('hero.headline')} + {t('hero.preview')}
diff --git a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css index 937056122e..983c168ec1 100644 --- a/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css +++ b/packages/client/ui-conversation/src/client/skeleton/HeroShell.module.css @@ -61,7 +61,8 @@ white-space: nowrap; } -/* Keep hover detection on a stationary box while the mark moves within it. */ +/* Keep hover detection on a stationary box while the mark moves within it; + hover rules target the hitbox so a slot-occupant mark swims too. */ .fishHitbox { grid-row: 1; grid-column: 1; @@ -70,29 +71,38 @@ justify-content: center; } -/* Keep the hero mark in the same primary ink as its headline. */ +/* The swim morph's curves poke slightly past the viewBox edges, so clipping + would slice the tail mid-wag. The hero mark stays in the same primary ink + as its headline. */ .fish { - color: var(--dsw-alias-label-primary); + display: block; + overflow: visible; transform-origin: 50% 60%; + color: var(--dsw-alias-label-primary); } +/* Hover swim: a slow continuous sway about the body center — the head + (left) lifts as the whale rises, then settles back with a small + counter-tilt, like treading water. Negative rotation = head up (the head + is the left half; origin 50% 60%). */ @keyframes hero-fish-swim { - 0%, 100% { - transform: translate(0, 0) rotate(0deg); + 0%, + 100% { + transform: none; } 35% { - transform: translate(-1px, -1px) rotate(-5deg); + transform: rotate(-4deg) translate(-0.4px, -0.9px); } 70% { - transform: translate(1px, 0) rotate(3deg); + transform: rotate(1.6deg) translate(0.3px, 0.2px); } } @media (hover: hover) and (prefers-reduced-motion: no-preference) { .fishHitbox:hover .fish { - animation: hero-fish-swim var(--ds-transition-duration-slow) var(--ds-ease-in-out); + animation: hero-fish-swim 1.6s ease-in-out infinite; } } diff --git a/packages/client/ui-conversation/tests/skeleton.client.spec.tsx b/packages/client/ui-conversation/tests/skeleton.client.spec.tsx index 40f6dfda6d..ed8a222e4f 100644 --- a/packages/client/ui-conversation/tests/skeleton.client.spec.tsx +++ b/packages/client/ui-conversation/tests/skeleton.client.spec.tsx @@ -480,7 +480,7 @@ describe('ConversationRoot resident composer', () => { const b = mount(sessionSnapshotOf({ blank: true, openState: 'loading' })) const root = b.view.container.querySelector('[data-phase]') expect(root?.getAttribute('data-phase')).toBe('settling') - expect(b.view.queryByText('探索未至之境')).toBeNull() + expect(b.view.queryByTestId('hero-headline')).toBeNull() }) it('settling phase: a session the list has no row for settles conservatively', () => { @@ -523,7 +523,7 @@ describe('ConversationRoot resident composer', () => { expect(b.wiring.snapshot.draft).toBe('kept across flip') expect(b.store.store.getSnapshot().draft).toBe('kept across flip') expect(b.view.container.querySelector('[data-conversation-scroll]')?.contains(after)).toBe(true) - expect(b.view.queryByText('探索未至之境')).toBeNull() + expect(b.view.queryByTestId('hero-headline')).toBeNull() expect(b.view.getByTestId('view-chat')).toBeTruthy() }) diff --git a/packages/client/ui-primitives/src/FishLogo.tsx b/packages/client/ui-primitives/src/FishLogo.tsx index 130060c660..8621f17273 100644 --- a/packages/client/ui-primitives/src/FishLogo.tsx +++ b/packages/client/ui-primitives/src/FishLogo.tsx @@ -1,5 +1,11 @@ import type { IconProps } from './icons/props.ts' +/** Native viewBox of {@link FISH_LOGO_PATH} (width and height in user units). */ +export const FISH_LOGO_VIEWBOX = { width: 23.16, height: 17.04 } + +/** The fish silhouette path data, exported for consumers that compose their own svg (entrance effects, masks) around the same geometry. */ +export const FISH_LOGO_PATH = 'M22.9168 1.43018C22.6713 1.31018 22.5658 1.53918 22.4223 1.65519C22.3733 1.69269 22.3318 1.74169 22.2903 1.78669C21.9317 2.1697 21.5127 2.42121 20.9657 2.39121C20.1657 2.34621 19.4827 2.59771 18.8787 3.20973C18.7502 2.45521 18.3236 2.0047 17.6746 1.71569C17.3351 1.56568 16.9916 1.41518 16.7536 1.08867C16.5876 0.856163 16.5421 0.597155 16.4591 0.341647C16.4061 0.187643 16.3536 0.0301382 16.1761 0.00363739C15.9836 -0.0263635 15.9081 0.135141 15.8326 0.270145C15.5306 0.822162 15.4136 1.43018 15.4251 2.0462C15.4516 3.43174 16.0366 4.53527 17.1991 5.3203C17.3311 5.4103 17.3651 5.5003 17.3236 5.63181C17.2441 5.90231 17.1501 6.16482 17.0671 6.43533C17.0141 6.60784 16.9351 6.64584 16.7501 6.57033C16.1121 6.30383 15.5611 5.90931 15.074 5.4328C14.2475 4.63328 13.5 3.75075 12.568 3.05973C12.349 2.89822 12.13 2.74822 11.9034 2.60522C10.9524 1.68169 12.028 0.923165 12.277 0.833162C12.5375 0.739159 12.3675 0.41615 11.5259 0.42015C10.6844 0.42365 9.91439 0.705658 8.93286 1.08117C8.78935 1.13767 8.63835 1.17867 8.48384 1.21267C7.59332 1.04367 6.66829 1.00617 5.70226 1.11517C3.88321 1.31768 2.43016 2.1777 1.36213 3.64575C0.0790928 5.4103 -0.222916 7.41536 0.146595 9.50642C0.535106 11.7105 1.66014 13.535 3.38869 14.9616C5.18125 16.4406 7.24581 17.1657 9.60138 17.0266C11.0319 16.9441 12.6245 16.7526 14.421 15.2321C14.874 15.4576 15.3496 15.5476 16.1381 15.6151C16.7456 15.6716 17.3306 15.5851 17.7836 15.4911C18.4931 15.3411 18.4441 14.6841 18.1876 14.5636C16.1081 13.595 16.5646 13.9891 16.1496 13.67C17.2061 12.42 18.8202 10.1979 19.3182 7.17235C19.3672 6.83834 19.4297 6.36783 19.4222 6.09732C19.4182 5.93231 19.4562 5.86831 19.6447 5.84931C20.1657 5.78931 20.6712 5.64681 21.1357 5.3913C22.4833 4.65528 23.0268 3.44624 23.1548 1.9972C23.1738 1.77569 23.1508 1.54668 22.9168 1.43018ZM11.1749 14.4736C9.15936 12.889 8.18184 12.3675 7.77832 12.39C7.40081 12.4125 7.46881 12.8445 7.55182 13.126C7.63882 13.404 7.75182 13.5955 7.91033 13.8396C8.01983 14.0011 8.09533 14.2411 7.80083 14.4216C7.15181 14.8231 6.02327 14.2866 5.97027 14.2601C4.65673 13.4865 3.5587 12.4655 2.78467 11.069C2.03715 9.72493 1.60314 8.28289 1.53164 6.74384C1.51264 6.37233 1.62214 6.24082 1.99215 6.17332C2.47916 6.08332 2.98118 6.06432 3.46769 6.13582C5.52476 6.43633 7.27581 7.35586 8.74385 8.8129C9.58188 9.64243 10.2159 10.634 10.8689 11.6025C11.5634 12.631 12.3105 13.611 13.262 14.4146C13.598 14.6961 13.866 14.9101 14.1225 15.0681C13.349 15.1546 12.058 15.1731 11.1749 14.4746L11.1749 14.4736ZM12.141 8.25988C12.141 8.09488 12.273 7.96338 12.439 7.96338C12.4765 7.96338 12.5105 7.97088 12.541 7.98188C12.5825 7.99688 12.6205 8.01938 12.6505 8.05338C12.7035 8.10588 12.7335 8.18088 12.7335 8.25988C12.7335 8.42489 12.6015 8.55639 12.4355 8.55639C12.2695 8.55639 12.141 8.42489 12.141 8.25988ZM15.1415 9.79893C14.949 9.87793 14.7565 9.94544 14.5715 9.95294C14.2845 9.96794 13.9715 9.85143 13.8015 9.70893C13.5375 9.48742 13.3485 9.36342 13.2695 8.97691C13.2355 8.8119 13.2545 8.55639 13.2845 8.40989C13.3525 8.09438 13.277 7.89187 13.0545 7.70787C12.8735 7.55786 12.643 7.51636 12.39 7.51636C12.2955 7.51636 12.209 7.47486 12.1445 7.44136C12.039 7.38886 11.9519 7.25735 12.035 7.09585C12.0615 7.04335 12.19 6.91584 12.22 6.89334C12.5635 6.69784 12.9595 6.76184 13.326 6.90834C13.6655 7.04735 13.9225 7.30236 14.292 7.66287C14.6695 8.09838 14.7375 8.21838 14.9525 8.54539C15.1225 8.8009 15.277 9.06341 15.3831 9.36392C15.4471 9.55142 15.3641 9.70493 15.1415 9.79893Z' + /** * Render the fish logo. * @param props.size - width in px (default 24; height keeps the 23.16:17.04 ratio). @@ -10,13 +16,13 @@ export function FishLogo({ size = 24, className }: IconProps) { return ( ) } diff --git a/packages/client/ui-primitives/src/index.ts b/packages/client/ui-primitives/src/index.ts index 20371add21..2c2643112d 100644 --- a/packages/client/ui-primitives/src/index.ts +++ b/packages/client/ui-primitives/src/index.ts @@ -22,7 +22,7 @@ export { OnboardingSurface } from './OnboardingSurface.tsx' export { RiskConfirmation } from './RiskConfirmation.tsx' export type { RiskConfirmationProps } from './RiskConfirmation.tsx' export { ConnectionBanner } from './ConnectionBanner.tsx' -export { FishLogo } from './FishLogo.tsx' +export { FishLogo, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX } from './FishLogo.tsx' export { BrandWordmark } from './BrandWordmark.tsx' export type { BrandWordmarkProps } from './BrandWordmark.tsx' export { ReferenceIcon } from './ReferenceIcon.tsx' diff --git a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts index e448cc722e..b01882d442 100644 --- a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts +++ b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts @@ -588,10 +588,8 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ hookContext: '', slotInject: '', declaredBy: 'an entry in \'conversation\' (client-ui-conversation), so it exists while that entry is mounted', - occupants: [ - 'client-ui-brand-official OfficialBrandMark', - ], - replaceRisk: 'shadows-shipped-ui', + occupants: [], + replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.brand.mark\', () => ctx.slots.register(\n { name: \'conversation.hero.brand.mark\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', source: 'packages/client/ui-conversation/src/client/contract/slots.ts:123', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9049facb91..b437464058 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1962,9 +1962,6 @@ importers: '@deepseek-ai/cordis': specifier: workspace:^ version: link:../../../vendor/cordis - '@deepseek-ai/dsh-client-ui-conversation': - specifier: workspace:^ - version: link:../ui-conversation '@deepseek-ai/dsh-client-ui-primitives': specifier: workspace:^ version: link:../ui-primitives