* fix(ui): avoid tooltip trigger overlap when a side fits
* feat(ui): add navigation entry customization
* fix(test): keep tooltip geometry independent of JSX types
* fix(ui): align customization catalogs and integration fixtures
* test(ui): complete customization lifecycle and release integration coverage
* fix(ui): return entry discovery to the installed plugin list
* docs: refresh module graph for entry customization
* build: refresh workspace links for the moved entry customization package
* docs: regenerate catalogs and pairing hashes for the moved entry customization package
* test(web): cover default-loaded entry customization in the Web e2e
* test(ui): describe entry customization as a browser plugin in the host entry spec
* build: order entry customization publication files as workspace constraints require
* test(release): drop the moved entry customization package from the experimental family list
* style(ui): use the shared focus ring color on entry customization triggers
* test(web): re-record goldens for the default-loaded More navigation menus
* test(web): add the More conversation views trigger to the remaining CI-reported goldens
* docs: record region-owned pins for plugin entry customization
* refactor(ui): rebuild entry customization on region-owned pins and More menus
- Each navigation region (left sidebar, Conversation header, right sidebar
guide) persists its own unpinned list under
dsh.slot.manual-order.<list slot key>.v1, filters its own launchers, and
draws its own More button and Menu around its actions slot.
- ui-entry-customization only registers the shared menu rows (entries with
pin buttons, Browse other plugins, Create your plugin) into the three
actions slots and keeps no state.
- Remove ctx.layout.entryVisibility, the plugin store, left-sidebar drag
reordering, and agentPresetNavigation; ui-layout and ui-agent-preset match
release/dsh-0.2.1 again.
- Keep the release <nav> and Conversation tab-bar visibility conditions.
- Move the More glyph into ui-primitives as IconAppsAddOutline{Regular,Medium}.
* docs: compile the entry customization owner props example
* docs(ui): describe the wrapping Conversation tab row in the header comment
* fix(ui): return New Session to the Workspace blank and its unsent draft
- New Session (startSession without options) takes the blank-reuse path
again instead of creating a fresh Session on every call, which hid the
previous blank and its unsent draft since a5c9c2aaeef (#5775).
- A request that names an Agent preset skips blanks listing a different
preset, so Creator entries no longer take over an ordinary draft.
- Record the change in a v0.2.1-rc.2 upgrade guide; the Host refresh of a
stale loaded blank stays deferred (see the Agent Note).
* docs: regenerate the Cordis inspect catalog for New Session blank reuse
* fix(ui): keep the selected entry visible while unpinned
- Left-sidebar rows and View tabs keep the selected entry, with aria-current or aria-selected, until another entry is selected.
- Entry menu Creator action takes no region argument; the registration's region selects the prompt.
- Document the 2px panel-row and 32px View-tab gaps; drop commit hashes from the New Session Agent Note.
* fix(ui): leave a blank of another preset unstaged when starting Creator
startCreatorDraft stages cordis only when the main blank lists no preset or already lists cordis; navigation opens a separate Creator blank otherwise, so a Standard draft blank keeps its preset.
* feat(ui): choose whether a draft prompt replaces, appends to, or keeps an existing draft
- DraftInitializationOptions gains whenDraftExists ('replace' | 'append' | 'keep', default 'keep'); clearPreviousDraft stays and maps to 'replace' only when whenDraftExists is omitted.
- Append adds the prompt as new paragraphs after the existing text and references; attachments stay in every mode.
- The three Create your plugin entries append their regional prompt.
* test(web): align e2e with New Session blank reuse and the visible selected entry
* refactor(ui): share the draft paragraph writer between setDraft and appendDraft
---------
Co-authored-by: Yif <877193178@qq.com>
Co-authored-by: imccyu <276526105+imccyu@users.noreply.github.com>
5.0 KiB
Agent Note: 新会话复用空白会话,指定预设时按预设匹配
Status: implemented
English | 中文
问题
0.2.0-rc.2 中,新会话(左侧栏按钮、工作区“+”和快捷键,均调用不带选项的 uiWorkspace.startSession)复用工作区内第一个空白会话。PR #5775(首次发布于 dsh-v0.2.1-alpha.2)改为每次新建 Session 与 Agent,目的是在用户修改预设定义后,新会话按当前定义装配,而不是沿用复用空白会话的旧装配(升级指南)。
空白会话不是当前会话时不在 Session 树中显示,返回它的唯一入口是新会话。改为每次新建后,用户在空白会话中写下草稿、切到其他会话再点新会话,会进入另一个空的 Session;原草稿仍按会话 id 保存在 localStorage,但没有界面入口可以回到它。0.2.1-rc.2 因此丢失新会话的未发送草稿。
此外,创造模式入口(startSession(…, { agentPreset: 'cordis', prompt, clearPreviousDraft: false }))复用第一个空白会话并把它切换到 Creator:已有普通草稿时,草稿留在 Creator 模式下,Creator 提示语不会填入;反之新会话也可能接管 Creator 的空白会话。
决策
只修改前端(ui-workspace),Host 不变:
- 不带选项的
startSession重新走复用分支,与openWorkspace、connectWorkspace和带选项的startSession使用同一规则。 createInWorkspace在未开始、未归档、仍属于该工作区的空白会话中选第一个。请求指明预设时,跳过列出的预设(projectionValues.agentPreset)为其他值的空白会话,未列出预设的空白会话仍可用;请求未指明预设时任何空白会话都可用,由 Host 切换到默认预设。没有可用的空白会话时由 Host 新建。
结果:新会话回到原来的空白会话,未发送的草稿保留;创造模式等预设流程不会接管列出其他预设的空白会话,用户的普通草稿不会被切到 Creator 下。不新增持久化状态,前端也不读取预设名单。
考虑过的替代方案
完全回退 PR #5775 的这项改动,所有请求都复用第一个空白会话。 草稿可以回来,但创造模式等预设流程会接管用户普通草稿所在的空白会话,把它切到 Creator,草稿留在 Creator 下,提示语也填不进去。
保持每次新建,把被丢下的空白会话中的草稿迁移到新会话。 需要定义多个空白会话时迁移哪一个、附件如何处理、旧会话是否归档,改动和状态都比按预设复用多。
未指明预设的请求只复用 Host 默认预设的空白会话。 前端不知道默认预设,需要额外请求 agentPresets.list() 名单。
在前端记录“来源 → 空白会话 id”的映射并持久化。 与空白会话自身记录的预设重复,需要处理映射过期、按浏览器 origin 隔离等问题。
预设变化时在原会话上清空草稿(clearPreviousDraft: true)。 会删除用户在另一种模式下写到一半的草稿。
同时修改 Host,让复用内存中的空白会话时总是调用 presets.select。 能完整保留 #5775 的目的,但不在本次只修前端的范围内,见“推迟工作”。
验证
ui-workspace单元测试:不带选项的新会话回到已有空白会话;未指明预设时复用任何空白会话,指明预设时只复用列出相同预设的空白会话,没有时新建。navigation.ts逐文件覆盖率 100%。- 回归定位:在 PR #5775 中做出这项改动的提交及其父提交上运行同一单元探针,以及在两侧构建上用浏览器复现,确认草稿丢失由该改动引入。
后果
- 新会话不再保证使用当前预设定义:复用的空白会话若其 Agent 仍在 Host 内存中且预设 id 相同,沿用创建时的装配;Host 重启后恢复的空白会话按当前定义装配。
- 新会话可能复用创造模式等预设流程留下的空白会话,Host 把它切回默认预设,其中的草稿保留。
- 升级指南 new-session-blank-reuse 记录相对 0.2.1-rc.2 的行为变化。
推迟工作
- Host
reuseLive(packages/api/session-controller/src/agent.ts)在预设 id 相同时直接返回内存中的 Agent,不调用presets.select。select用定义的Generation对象是否相同判断定义是否重新注册过:相同则不做任何事;不同则在同一会话 id 上按当前定义重新装配并记录agent-preset/selected;会话已开始时抛出agent-preset/locked,调用方改为新建。去掉target === current的提前返回、改为总是调用select,可以在保留草稿的同时满足 #5775“新会话按当前定义装配”的目的;需要配套 Host 测试,并更新reuseLive的 JSDoc 和上述升级指南中“不再保证当前预设定义”的说明。