dsh-code-ui 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/lib/client.js +35 -17
- package/lib/index.js +23 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
**两种切换按钮(插件卡片「切换按钮」可选)**——点击在 code UI 与 dsh UI(DSH 原生界面)之间切换,与布局任意组合:
|
|
18
18
|
|
|
19
|
-
|
|
|
19
|
+
| 会话头按钮 | 悬浮模式条(默认,可拖动) |
|
|
20
20
|
| :---: | :---: |
|
|
21
21
|
|  |  |
|
|
22
22
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
## 功能
|
|
26
26
|
|
|
27
27
|
- **双布局可选**:右侧停靠(默认布局,与聊天并排) / 悬浮窗口(可拖拽缩放),插件卡片切换、即时生效
|
|
28
|
-
-
|
|
28
|
+
- **切换按钮可选**:悬浮模式条(默认,可拖动) / 会话头 `⌨ code UI` 按钮,与布局任意组合
|
|
29
29
|
- **界面模式可持久化**:code UI / dsh UI(**默认隐藏**,不自动占屏,入口按钮随时唤回),所有入口切换均同步记忆
|
|
30
30
|
- **Cursor 风格工作区**:活动栏 + 文件树侧边栏 + 多标签页编辑器 + 状态栏
|
|
31
31
|
- **侧栏收起与调宽**:« 按钮 / 📁 三态 / 拖到最窄自动收起,» 把手展开,右缘拖拽调宽
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
dsh plugin --profile web add dsh-code-ui
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
安装完成后重启 DSH,编辑器**默认隐藏**(dsh UI
|
|
55
|
+
安装完成后重启 DSH,编辑器**默认隐藏**(dsh UI,不自动占屏);页面顶部出现可拖动的**悬浮模式条**(默认切换按钮),点 `code UI` 即可打开编辑器——默认布局为右侧停靠、默认根目录为**当前会话的工作区**,想换会话头 ⌨ 按钮或悬浮窗口,到 **设置 → 插件 → 插件列表 → dsh-code-ui** 的配置卡片里切换。
|
|
56
56
|
|
|
57
57
|
或手动安装:
|
|
58
58
|
|
|
@@ -108,6 +108,7 @@ dsh-code-ui/
|
|
|
108
108
|
|
|
109
109
|
| 时间 | 版本 | 修复 / 新增 |
|
|
110
110
|
| --- | --- | --- |
|
|
111
|
+
| 2026-09-02 | v1.3.2 | 调整:**切换按钮默认改为悬浮模式条**(原会话头 ⌨ 按钮,已保存选择的用户不受影响);**文件树默认根跟随当前会话的工作区目录**——打开编辑器即定位到当前对话的项目根,会话切换自动换根(有未保存文件时暂不跟随并提示,保存后点 ⟳ 即切换);手动「以此文件夹为根」后不再自动跟随 |
|
|
111
112
|
| 2026-09-02 | v1.3.1 | 调整:安装后编辑器**默认隐藏**(dsh UI,新装不自动占屏,⌨ / 模式条随时唤回),布局默认仍为右侧停靠;配置卡片「编辑器界面」三态说明与 README 措辞同步。新增:一键更新 / 本地包更新成功后按钮文案临时变为「需重启 DSH 生效」(置灰防误点);README 增加「界面一览」(双布局与两种切换按钮截图)并明确配置入口路径(设置 → 插件 → 插件列表 → dsh-code-ui) |
|
|
112
113
|
| 2026-09-02 | v1.3.0 | 新增:文件树右键操作(真实磁盘)。右键文件:打开 / 引用给 AI(整个文件)/ 复制路径 / 在资源管理器中显示 / 重命名 / 删除;右键文件夹:新建文件 / 新建文件夹 / 以此文件夹为根 / 刷新 / 复制路径 / 定位 / 重命名 / 删除(递归强确认);空白区右键:根目录新建 / 刷新 / 更换根;新建重命名输入弹窗(Enter 确认)、危险操作确认弹窗、工作区根禁删;重命名自动同步标签页/缓冲/watcher,删除自动清理该路径下全部标签。引用体系升级:整文件引用只传绝对路径由 AI 自行读取;停靠布局下 DSH 原生输入条发送也自动附加引用指针(挂钩 submit,两布局统一)。配置卡片:「布局模式 × 界面模式」合并为三态「编辑器界面」(右侧停靠/悬浮窗口/隐藏 dsh UI,隐藏时记住布局)。打开文件弹窗:输入路径回车直接认定(文件夹=设为根 / 文件=打开)。提示条移至顶部居中、可拖拽自定位置(⠿ 把手)。右键菜单视口边界自适应、点击菜单项后自动关闭;⟳ 刷新不再折叠整棵树 |
|
|
113
114
|
| 2026-09-02 | v1.2.0 | 里程碑:双布局与全面可配置。新增右侧停靠布局(code UI 固定占右侧、DSH 界面推到左侧并排,Cursor 式;左缘拖拽实时调宽,壳层帧用 [data-slot=root] 语义属性定位 + 内联 margin-right 推挤);插件卡片新增「布局模式」(右侧停靠=默认 / 悬浮窗口)、「界面模式」(code UI / dsh UI,状态持久化、所有入口同步记忆)、「切换按钮」(会话头 ⌨ 按钮=默认 / 悬浮模式条,与布局任意组合),全部即时生效 + 持久化;文件树侧栏可收起(« / 📁 三态 / 拖窄收起)+ » 展开把手 + 右缘拖宽;侧栏标题不折行;停靠面板去投影;界面文案更名(Code UI→code UI、原始风格-ui→dsh UI);悬浮布局也获得侧栏收起/调宽能力 |
|
package/lib/client.js
CHANGED
|
@@ -265,6 +265,9 @@ window.__ModuleLoader__.load({
|
|
|
265
265
|
toast: null,
|
|
266
266
|
toastKeep: false,
|
|
267
267
|
toastPos: null,
|
|
268
|
+
sessionId: null, // 当前会话 id(InputBridge 从 slot kit 捕获):文件树默认根跟随当前会话
|
|
269
|
+
rootIsAuto: true, // 根是否为自动跟随(用户「以此文件夹为根」后置 false,不再跟随)
|
|
270
|
+
rootSessionId: null, // 当前自动根来自哪个会话(落后于 sessionId 时触发重新解析)
|
|
268
271
|
error: null,
|
|
269
272
|
}
|
|
270
273
|
const listeners = new Set()
|
|
@@ -613,7 +616,7 @@ window.__ModuleLoader__.load({
|
|
|
613
616
|
emit()
|
|
614
617
|
const start = store.browsePath || store.rootPath || null
|
|
615
618
|
if (start) { browseDir(start); return }
|
|
616
|
-
callHost('get-root').then((r) => {
|
|
619
|
+
callHost('get-root', store.sessionId ? { sessionId: store.sessionId } : {}).then((r) => {
|
|
617
620
|
if (r && r.root) { browseDir(r.root) }
|
|
618
621
|
else { store.error = '未定位到工作区根,请在输入框粘贴目录或文件路径后回车'; emit() }
|
|
619
622
|
})
|
|
@@ -627,7 +630,13 @@ window.__ModuleLoader__.load({
|
|
|
627
630
|
if (!path) return
|
|
628
631
|
const res = await callHost('set-root', { path })
|
|
629
632
|
if (res && res.error) { showToast('设置失败:' + res.error); return }
|
|
630
|
-
|
|
633
|
+
store.rootIsAuto = false // 用户显式选择:此后不再跟随会话切换
|
|
634
|
+
store.rootSessionId = null
|
|
635
|
+
await applyRoot((res && res.root) ? res.root : path)
|
|
636
|
+
showToast('工作区根目录:' + store.rootPath)
|
|
637
|
+
}
|
|
638
|
+
// 换根并整体重置文件树/标签页/缓冲(自动跟随与手动设置共用)
|
|
639
|
+
async function applyRoot(newRoot) {
|
|
631
640
|
store.rootPath = newRoot
|
|
632
641
|
store.nodes = {}
|
|
633
642
|
store.tabs = []
|
|
@@ -642,19 +651,18 @@ window.__ModuleLoader__.load({
|
|
|
642
651
|
store.filePickerOpen = false
|
|
643
652
|
emit()
|
|
644
653
|
await toggleExpand(newRoot)
|
|
645
|
-
showToast('工作区根目录:' + newRoot)
|
|
646
654
|
}
|
|
647
655
|
// —— 文件树 ——
|
|
648
656
|
async function refresh() {
|
|
649
657
|
store.error = null
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
+
const sid = store.sessionId
|
|
659
|
+
// 自动根模式:首次无根、或会话已切换(rootSessionId 落后)都重新解析当前会话的工作区
|
|
660
|
+
if (!store.rootPath || (store.rootIsAuto && sid && sid !== store.rootSessionId)) {
|
|
661
|
+
const r = await callHost('get-root', sid ? { sessionId: sid } : {})
|
|
662
|
+
const root = (r && r.root) || null
|
|
663
|
+
if (!root) { store.error = '未找到工作区根目录'; emit(); return }
|
|
664
|
+
await applyRoot(root)
|
|
665
|
+
store.rootSessionId = sid
|
|
658
666
|
} else {
|
|
659
667
|
// 修复:刷新不再折叠整棵树,只重载根目录(子目录展开状态保留在 store.nodes)
|
|
660
668
|
await reloadDir(store.rootPath)
|
|
@@ -1035,6 +1043,15 @@ window.__ModuleLoader__.load({
|
|
|
1035
1043
|
store.inputDraft = inputState.draft
|
|
1036
1044
|
store.inputPhase = inputState.phase
|
|
1037
1045
|
}
|
|
1046
|
+
// 捕获当前会话 id:文件树默认根跟随当前会话的工作区目录
|
|
1047
|
+
if (props.sessionId && props.sessionId !== store.sessionId) {
|
|
1048
|
+
store.sessionId = props.sessionId
|
|
1049
|
+
if (store.rootIsAuto) {
|
|
1050
|
+
const hasDirty = Object.keys(store.dirty || {}).some((k) => store.dirty[k])
|
|
1051
|
+
if (hasDirty) showToast('会话已切换:有未保存文件,工作区暂不跟随(保存后点 ⟳ 刷新即可)')
|
|
1052
|
+
else refresh()
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1038
1055
|
emit()
|
|
1039
1056
|
})
|
|
1040
1057
|
// 挂钩 DSH 原生输入条的发送:存在引用时把指针附进草稿再发(整文件=绝对路径)。
|
|
@@ -1644,7 +1661,7 @@ window.__ModuleLoader__.load({
|
|
|
1644
1661
|
// get-self 失败 / 字段缺失(旧 host)时绝不隐藏编辑器,只有明确 enabled===false 才不挂载。
|
|
1645
1662
|
let unmountEditorFns = null
|
|
1646
1663
|
let layoutMode = 'dock' // dock(右侧停靠,默认) / float(悬浮窗口):编辑器以哪种布局呈现
|
|
1647
|
-
let toggleStyle = '
|
|
1664
|
+
let toggleStyle = 'bar' // bar(悬浮模式条,默认) / header(会话头 ⌨ 按钮):用哪种按钮切换界面
|
|
1648
1665
|
function mountEditor() {
|
|
1649
1666
|
if (unmountEditorFns) return
|
|
1650
1667
|
const disposers = []
|
|
@@ -1718,13 +1735,14 @@ window.__ModuleLoader__.load({
|
|
|
1718
1735
|
if (v) mountEditor()
|
|
1719
1736
|
else unmountEditor()
|
|
1720
1737
|
}
|
|
1721
|
-
//
|
|
1738
|
+
// 启动时先查开关/主题/布局/界面模式/切换按钮再挂载(新装默认:启用+跟随DSH+停靠+隐藏+悬浮模式条;
|
|
1739
|
+
// RPC 失败回退到「可见+会话头按钮」保证可发现性)
|
|
1722
1740
|
callHost('get-self').then((r) => {
|
|
1723
1741
|
if (r && !r.error) {
|
|
1724
1742
|
setThemeMode(r.theme)
|
|
1725
1743
|
layoutMode = (r.layout === 'float') ? 'float' : 'dock'
|
|
1726
|
-
toggleStyle = (r.toggle === '
|
|
1727
|
-
applyUiMode(r.uiMode === '
|
|
1744
|
+
toggleStyle = (r.toggle === 'header') ? 'header' : 'bar'
|
|
1745
|
+
applyUiMode(r.uiMode === 'codeui' ? 'codeui' : 'native')
|
|
1728
1746
|
applyEnabled(!(r.enabled === false))
|
|
1729
1747
|
} else {
|
|
1730
1748
|
setThemeMode('dsh')
|
|
@@ -1971,13 +1989,13 @@ window.__ModuleLoader__.load({
|
|
|
1971
1989
|
['header', 'bar'].map((m) => h('button', {
|
|
1972
1990
|
key: m,
|
|
1973
1991
|
type: 'button',
|
|
1974
|
-
className: 'cdx-pc-btn ' + (((self && self.toggle) || '
|
|
1992
|
+
className: 'cdx-pc-btn ' + (((self && self.toggle) || 'bar') === m ? 'cdx-pc-btn-primary' : 'cdx-pc-btn-outline'),
|
|
1975
1993
|
disabled: !self || toggling,
|
|
1976
1994
|
onClick: () => setToggle(m),
|
|
1977
1995
|
}, m === 'header' ? '会话头按钮' : '悬浮模式条')),
|
|
1978
1996
|
),
|
|
1979
1997
|
),
|
|
1980
|
-
h('p', { className: 'cdx-pc-note' }, '选择用哪种按钮在 code UI 与dsh UI
|
|
1998
|
+
h('p', { className: 'cdx-pc-note' }, '选择用哪种按钮在 code UI 与 dsh UI 之间切换:「悬浮模式条」为屏幕上方可拖动的横条(code UI / dsh UI 两键,默认);「会话头按钮」在会话标题栏显示 ⌨ code UI。与布局模式无关,可任意组合,选择持久化保存。'),
|
|
1981
1999
|
),
|
|
1982
2000
|
// 字段一:在线检查更新
|
|
1983
2001
|
h('div', { className: 'cdx-pc-field' },
|
package/lib/index.js
CHANGED
|
@@ -102,11 +102,11 @@ const CodeUiSettingsSchema = z.object({
|
|
|
102
102
|
layout: z.string().default("dock"),
|
|
103
103
|
// 界面模式:native(隐藏编辑器,默认——新装不自动占屏,由 ⌨ 按钮唤出) / codeui(显示编辑器)
|
|
104
104
|
uiMode: z.string().default("native"),
|
|
105
|
-
// 切换按钮样式:header(会话头 ⌨
|
|
106
|
-
toggle: z.string().default("
|
|
105
|
+
// 切换按钮样式:bar(悬浮模式条,默认) / header(会话头 ⌨ 按钮)
|
|
106
|
+
toggle: z.string().default("bar"),
|
|
107
107
|
});
|
|
108
108
|
// 解析后的当前值:settings 服务未挂载/未写入时保持默认
|
|
109
|
-
const selfSettings = { updateRegistry: REGISTRY_DEFAULT, enabled: true, theme: "dsh", layout: "dock", uiMode: "native", toggle: "
|
|
109
|
+
const selfSettings = { updateRegistry: REGISTRY_DEFAULT, enabled: true, theme: "dsh", layout: "dock", uiMode: "native", toggle: "bar" };
|
|
110
110
|
// settings 服务引用(「启用/禁用」开关写入用;由 installSelfSettings 捕获)
|
|
111
111
|
let settingsServiceRef = null;
|
|
112
112
|
function installSelfSettings(ctx) {
|
|
@@ -349,13 +349,9 @@ function unwatchAllFiles() {
|
|
|
349
349
|
|
|
350
350
|
function rpcHandler(ctx) {
|
|
351
351
|
const fs = ctx.get("fs");
|
|
352
|
-
let currentRootRef = detectWorkspaceRoot(ctx);
|
|
352
|
+
let currentRootRef = detectWorkspaceRoot(ctx); // 最近一次解析的根:list-dir 的兜底
|
|
353
|
+
let manualRootRef = null; // 用户「以此文件夹为根」的显式选择:一旦设置不再跟随会话
|
|
353
354
|
|
|
354
|
-
// 每次 get-root 动态回读会话工作区根,避免一次缓存导致拿不到当前项目目录
|
|
355
|
-
function baseRoot() {
|
|
356
|
-
if (currentRootRef) return currentRootRef;
|
|
357
|
-
return detectWorkspaceRoot(ctx);
|
|
358
|
-
}
|
|
359
355
|
function detectWorkspaceRoot(_ctx) {
|
|
360
356
|
// 1) 当前会话的真实工作区根(项目根):优先取正在发起调用的 agent 的 session cwd。
|
|
361
357
|
// sandboxPolicy.workspaceRoot 只是「部署回退根」,常是 DSH 进程 cwd 而非项目目录。
|
|
@@ -388,13 +384,29 @@ function rpcHandler(ctx) {
|
|
|
388
384
|
const args = payload && typeof payload === "object" ? payload : {};
|
|
389
385
|
|
|
390
386
|
if (endpoint === "get-root") {
|
|
391
|
-
|
|
387
|
+
// 优先级:手动设置的根 > 浏览器上报的当前会话 cwd(agent/session id 共享,agents.get 可查)
|
|
388
|
+
// > initiator 会话 cwd(agent 驱动中)> 部署回退根 > 进程 cwd
|
|
389
|
+
let root = manualRootRef;
|
|
390
|
+
if (!root && args.sessionId) {
|
|
391
|
+
const agentsSvc = ctx.get("agents");
|
|
392
|
+
if (agentsSvc) {
|
|
393
|
+
try {
|
|
394
|
+
const a = agentsSvc.get(String(args.sessionId));
|
|
395
|
+
if (a && a.session && a.session.header && a.session.header.cwd) {
|
|
396
|
+
root = a.session.header.cwd;
|
|
397
|
+
}
|
|
398
|
+
} catch (_) {}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!root) root = detectWorkspaceRoot(ctx);
|
|
402
|
+
if (root) currentRootRef = root;
|
|
392
403
|
return ok({ root });
|
|
393
404
|
}
|
|
394
405
|
|
|
395
406
|
if (endpoint === "set-root") {
|
|
396
407
|
if (!args.path) return ok({ error: "no-path" });
|
|
397
|
-
|
|
408
|
+
manualRootRef = String(args.path);
|
|
409
|
+
currentRootRef = manualRootRef;
|
|
398
410
|
return ok({ root: currentRootRef });
|
|
399
411
|
}
|
|
400
412
|
|