Files
obsidian-vault/AI Coding/inbox/20260824-composed-slot-before-dom-seam.md
yuxuanhui dcd6d44960 feat: add Feishu user authorization flow documentation and update frontend guidelines
- Added new documentation for the Feishu user authorization flow, detailing the backend protocol, authorization initiation, callback handling, and token management.
- Updated frontend index to link to the new authorization flow documentation for better accessibility and guidance on UI design consistency.
2026-08-31 09:18:03 +08:00

107 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: 20260824-composed-slot-before-dom-seam
title: 先查宿主组合包 SlotMap,再退回 DOM seam
created: 2026-08-24
updated: 2026-08-24
status: candidate
scope: global
category: api-discovery
confidence: high
last_verified: 2026-08-24
promotion_target: pattern
projects:
- dsh-lark-cli
tags:
- host-extension
- slotmap
- declaration-merging
- native-tab
- api-discovery
- dsh
---
# 先查宿主组合包 SlotMap,再退回 DOM seam
## Trigger
需要判断一个模块化 Web 宿主的插件能否原生增加页面、Tab、导航项或面板,但当前插件仓库和它直接依赖的 runtime 类型里没有找到对应扩展 API。
## Context
调研 DeepSeek Harness(DSH)插件能否增加与 Chat、Trajectory 并列的 Session 顶部 Tab 时,先检索当前插件仓库和直接安装的 `@deepseek-ai/dsh-client-runtime`,只看到了 `root`、`shell.overlay` 以及仓库已有的 DOM Portal 路径,一度推断“没有原生 Tab API”。
继续检查实际 DSH Web Host 组合的 UI packages 后,`@deepseek-ai/dsh-client-ui-conversation` 的 TypeScript declaration merging 明确补充了 `conversation.view`:这是 `kind: 'list'`、`scope: 'session'` 的视图环,一个注册项就是一个顶部 Tab。第一方 `ui-trajectory` 插件正是通过同一 slot 注册 Trajectory。
这次有意义的复利点不是记住一个 slot 名,而是:扩展契约可能由“视觉表面的所有者包”声明,不在核心 runtime,也不一定出现在业务插件的直接依赖树中。否定一个扩展能力前,必须检查宿主最终组合和第一方同类贡献者。
## Evidence
- 2026-08-24,本机 `dsh --version` 返回 `0.1.0-rc.7`;Web UI packages 为 `0.1.0-rc.8`。
- 官方 `ui-conversation` SlotMap 在 [`packages/client/ui-conversation/src/client/contract/slots.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-conversation/src/client/contract/slots.ts) 声明 `conversation.view`,注释说明它是 “one list entry per view tab”。
- 官方会话组装在 [`packages/client/ui-conversation/src/client/apply.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-conversation/src/client/apply.ts) 遍历 `slots.entries('conversation.view')`,从 entry 的 `id` 和 `label` 生成 Tab;Chat 以 `id: 'chat'`、`order: 0` 注册。
- 第一方 [`packages/client/ui-trajectory/src/client/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-trajectory/src/client/index.ts) 使用 `ctx.slots.inject('conversation.view', ...)` 注册 `id: 'trajectory'`、`order: 10` 的 Trajectory Tab。
- 当前 `dsh-lark-cli` 只注册 `sidebar.footer.action` 和 `shell.overlay`,再通过 React Portal 使用 Conversation DOM seam;它证明了另一种布局扩展方式,但不能证明原生 Tab slot 不存在。
- 本次同时核对了已安装包的 `.d.ts`、编译后 `client.js`、官方中文 README、Context7 官方库 `/deepseek-ai/deepseek-harness` 和 GitHub `master` 原始源码。
- 项目内完整调研记录:`dsh-lark-cli/.scratch/research/dsh-plugin-custom-tab.md`。
## Root cause
已验证:DSH 的 `SlotMap` 使用 TypeScript declaration merging,由各 UI owner package 分散声明。`dsh-client-runtime` 只声明内建 `root` slot;`conversation.view` 由 `dsh-client-ui-conversation` 声明,因此只查 runtime 会得到不完整的扩展面。
已验证:宿主最终装配包含的包可以多于业务插件的直接依赖。当前项目没有直接安装 `dsh-client-ui-conversation`,但全局 DSH Web bundle 已组合该包及 `ui-trajectory`,所以项目局部 `node_modules` 不是宿主能力全集。
已验证:slot 类型声明只能证明 seat 存在;宿主如何把 seat 投影为 UI,需要继续核对 owner 实现。`ui-conversation` 的 header/render 代码证明 `id`、`order`、`label` 会形成 Tab,active id 会选择唯一视图。
推断:在其他可组合插件宿主中,同类误判也容易发生:核心 SDK 暴露注册机制,具体贡献点由 feature package 增补;从业务仓库向内搜索会漏掉最终装配才拥有的契约。
## Preferred action
1. 先确定目标 UI 的所有者:Tab 属于 Conversation、Settings、Sidebar 还是根 Layout;不要默认所有扩展点都由 runtime 声明。
2. 枚举实际宿主装配的 feature/client packages。优先读取 bundle manifest、profile dump、全局安装目录或已发布 package metadata,而不是只看业务插件的直接依赖。
3. 搜索所有 `SlotMap` declaration merging、slot declaration 和 `slots.register` 调用,区分:
- `single`:替换整个表面;
- `list`:可追加贡献;
- `keyed` / `chain`:按 key 或选择器扩展;
- slot 的 `root` / `session` scope。
4. 用三类一手证据闭环:
- owner package 的契约声明;
- owner 如何把注册项投影为实际 UI;
- 第一方同类插件的最小注册示例。
5. 分别核对“当前部署版本”和“上游当前版本”。前者回答现在能否工作,后者回答接口是否仍存在;不要用其中一个替代另一个。
6. 只有确认没有满足需求的 additive slot 后,才评估 DOM seam、Portal、CSS 重排或宿主 fork。若已有原生 slot,用它承担生命周期、排序、卸载和 active state。
7. 实现时把 owner package 加入插件的 client inject,并按宿主版本固定 peer/dev dependency;RC API 必须做真实 Web profile smoke test。
## Boundaries
- 该方法适用于由多个包组合、扩展契约分散声明的插件宿主。单体应用或拥有集中式完整 schema 的 SDK 不需要扫描全部 feature packages。
- 发现内部 slot 不等于它是稳定公共 API。至少需要 package 导出、类型声明、owner 文档或第一方插件用例之一;仅从 minified DOM、私有类名或未导出符号推断时仍按内部实现处理。
- `conversation.view` 适合“切换到插件拥有的整页视图”。若需求是文件树、编辑器与官方 Chat 同时可见,原生 Tab 会隐藏非 active view,仍应评估 [[20260822-extension-slot-portal-three-column-grid|生命周期 slot + 稳定布局 seam + Portal]]。
- `conversation.view`、`dsh.client.inject` 和具体 order 值是 DSH 当前契约,不应推广成其他宿主的通用命名。
- 当前只完成源码、类型、包版本和上游 master 核验,没有为 `dsh-lark-cli` 实际注册新 Tab,也没有运行浏览器集成测试。实现层面的兼容性结论仍需 smoke test。
## Failed approaches
- 只检索业务仓库和直接依赖的 runtime:会把“当前项目没有导入 owner package”误判成“宿主没有扩展点”。
- 从 `root` 或 `conversation.session` 是 `single` slot 推导不能新增 Tab:这混淆了“替换整棵表面”和其内部声明的 additive child slot。
- 看到当前项目已经使用 DOM Portal,就把它当作该视觉需求的唯一方案:既有实现只能证明一条可行路径,不能穷举宿主扩展面。
- 只搜 `tab`、`route`、`router`:slot 系统可能把 Tab 表达成抽象的 `view` contribution,应同时沿 UI owner、slot ledger 和第一方插件反向定位。
## Examples
DSH 当前原生 Session Tab 的最小形态:
```tsx
ctx.slots.inject('conversation.view', () => ctx.slots.register({
name: 'conversation.view',
id: 'plugin-view',
order: -10,
label: () => 'Plugin View',
}, PluginView))
```
验证顺序应是:`SlotMap declaration` → `host tab projection` → `first-party trajectory registration` → `target version smoke test`。
## Promotion record
- Not promoted. 当前已有一次高置信度纠错和一套可复用调查流程;待在另一个分包式宿主扩展调研中独立复用后,再考虑提升为通用 host-extension discovery pattern。