1. 统一会话列宽度轴:新增 --dsh-chat-content-width(748px),输入框、todo、goal、queue、approval、plan review、ask question 等容器的宽度与边距全部由该变量推导,消除各面板之间的像素漂移,窄视口下的边缘留白也保持一致。 2. 输入框自适应与细节:控制行改为容器查询(460px 阈值以下权限选择器只显示图标+下拉,隐藏文字);卡片圆角 20→22、行内边距调整并整体下移 2px(发送按钮除外);permission/model 触发器统一 24px 圆角;Plan 与 Read Only 间距 +8。 3. 修复浮层菜单溢出:slash 菜单与命令弹层钳制到输入卡片宽度,超长行以省略号截断;Tooltip 增加 12px 视口边缘安全距离。 4. 增加与替换图标:Add provider 改用输入框同款加号图标(IconPlusOutline16),统一图标尺寸与字号。 5. 统一 Settings → Models 组件:补齐按钮 hover 态、select 下拉箭头不再贴边、标题区与 provider 卡片间距 +12。 6. 侧边栏交互:add workspace / group by / create session / 收起侧边栏四个图标按钮增加 500ms 延迟 tooltip(前两个向下弹出);展开态的 New Session 不再重复显示 tooltip;侧边栏窄屏自适应收起逻辑微调。 7. 其他:hero 区 workspace 徽章右移对齐;附带 Agent Note(中英双语)记录共享宽度轴与容器查询的设计取舍。
@deepseek-ai/dsh-client-ui-slash
English | 中文
Input trigger pipeline plugin: / and @ detection under the caret (word-boundary + guard-tier rules), the grouped candidate menu, and pick routing to registered sources. ctx.slash owns the source roster and resolves one SlashController per session scope (sessionOf); the conversation wiring layer drives track/arbitrate/onSpace/adjudicate on the controller. The same controller exposes toggleSource for a chrome launcher to open exactly one registered source over a synthetic selection span; the resulting candidates still use the ordinary menu, keyboard arbitration, pick callback, and scoped input mutations. Sources receive a ClientSessionContext projection per call — sessions are always agent-backed, so the projection is the session identity alone. A source is warmed in every session controller it can reach: the roster present at scope birth warms during controller construction, and a source registered later is warmed into every live controller by the registration itself. Sources whose lexicon roll changes after warm implement subscribeLexicon(session, listener); the controller re-polls on each notification and publishes the aggregation through its lexicon snapshot store. The pipeline is command-agnostic: space/enter adjudication polls the optional matchSpace/matchEnter hooks in registration order and the first non-undefined answer wins.
Layering: src/core/ (T2) is the pure core — detectTrigger, menuReduce/seedGroups/MENU_CLOSED, exactMatch, zero React/DOM/cordis; src/client/service.ts is the shell wiring the core to the menu snapshot store, the per-hit candidate fetch (generation-gated, AbortSignal-superseded, failed sources drop silently with a console record), and the three pick paths. src/types.ts and the two contract.ts files are the frozen cross-package contract (design v4 §5.1); changes require main-thread arbitration.
MenuView renders the menu store into the conversation.input.overlay slot (list kind, session scope) and renders null while closed. Typed triggers seed every source registered for that trigger; a programmatic launcher seeds only its requested source and publishes the source name through the controller's launcher snapshot store until the menu closes or typed tracking resumes. Groups sort by the optional SlashSource.order (lower first, default 0, ties keep registration order) under title rows localized through the slash.menu locale namespace (an unknown source shows its raw name); the list height clamps to the space above the composer, and a pointer down outside both the menu and the surrounding composer card dismisses it. The slot is owned by ui-conversation's composer entry (anchor, children declaration, lifecycle); its SlotMap type merge lives in this package's src/client/slots.ts because the dependency direction (ui-conversation → ui-slash) admits no reverse type import. Combobox pattern: focus stays in the textarea, rows pick on mousedown, the highlight rides aria-activedescendant.
The /client export surface is the plugin body (apply/inject), SlashService, MenuViewInjected, and the contract types. MenuView itself is internal — the slot registration closes over it.
Model Experience
None, as the trigger pipeline is browser presentation only — picks produce CommandClaim/ReferenceInsert data whose model-visible consequences (host command execution; inserted reference text riding an ordinary prompt) are owned by the consuming host and input-machine packages.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
- Global source layer only — session-scope source registration (per-session shadowing, ScopedLayers-alike) is designed but not enabled; the ledger tracks the trigger condition (a real per-session source need).
SlashCandidate.iconrenders as text — MenuView drops the string into the icon slot verbatim; wiring to the design-system icon enum (iconFile five-variant family) lands when that enum ships.- Overlay SlotMap merge home is split from slot ownership — the
conversation.input.overlaymerge lives here (sole copy) while the slot's owner semantics (anchor, children declaration, lifecycle) stay with ui-conversation; the dependency direction (ui-conversation → ui-slash) forces the split, so a future dependency reshuffle should revisit it.