dsh-vscode-mode 0.7.0 → 0.8.0
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 +23 -6
- package/assets/icon.svg +7 -0
- package/cordis.patch.yml +12 -0
- package/lib/client.js +1167 -188
- package/lib/client.js.map +1 -1
- package/lib/index.js +845 -96
- package/lib/index.js.map +1 -1
- package/locale/en.json +4 -0
- package/locale/zh.json +4 -0
- package/package.json +3 -1
- package/src/client/compat.ts +134 -4
- package/src/client/imagePreview.ts +76 -2
- package/src/client/index.ts +65 -36
- package/src/client/monaco/lsp/lspClient.ts +46 -0
- package/src/client/monaco/lsp/providers.ts +245 -1
- package/src/client/nativeOpenStore.ts +91 -0
- package/src/client/officialSidebar.ts +11 -45
- package/src/client/outline/index.ts +1 -1
- package/src/client/pdf/pdfPanel.ts +18 -0
- package/src/client/settingsContext.ts +6 -4
- package/src/client/sidebar/panels/FileExplorer.ts +1 -1
- package/src/client/sidebar/panels/SvnPanel.ts +1 -1
- package/src/client/sidebar/panels/index.ts +1 -1
- package/src/client/styles/editor.css +5 -3
- package/src/client/svnStore.ts +6 -1
- package/src/client/ui/EditorView.ts +117 -18
- package/src/client/ui/McpSettings.ts +30 -0
- package/src/client/ui/PerfSettings.ts +4 -1
- package/src/client/ui/icons.ts +72 -0
- package/src/compat.ts +2 -2
- package/src/dshVersion.ts +2 -1
- package/src/fileOpenSettings.ts +163 -40
- package/src/index.ts +15 -1
- package/src/lsp/client.ts +24 -0
- package/src/lsp/rpc.ts +57 -0
- package/src/lsp/server.ts +272 -2
- package/src/perf.ts +90 -4
- package/src/routes.ts +13 -1
- package/src/rpc.ts +96 -34
- package/src/shared/lsp.ts +79 -0
- package/src/shared/nativeOpen.ts +91 -0
- package/src/shared/rpc.ts +146 -1
|
@@ -11,10 +11,12 @@ import React from 'react'
|
|
|
11
11
|
import { dbg, rpc } from '../rpc.js'
|
|
12
12
|
import { emitFileChanged, emitRefresh } from '../events.js'
|
|
13
13
|
import { langOf, loadMonaco, snippetLanguageOf } from '../monaco/loader.js'
|
|
14
|
-
import { dataUrlOf, isImagePath, isSvgPath } from '../imagePreview.js'
|
|
14
|
+
import { clampZoom, clearZoomMem, dataUrlOf, isImagePath, isSvgPath, recallZoom, rememberZoom, zoomKeyOf, zoomStepOf } from '../imagePreview.js'
|
|
15
15
|
import { isMarkdownPath } from '../markdownPreview.js'
|
|
16
16
|
import { MarkdownPanel } from '../md/mdPanel.js'
|
|
17
|
-
import { base64ToBytes, isPdfPath } from '../pdfPreview.js'
|
|
17
|
+
import { base64ToBytes, bytesToBase64, isPdfPath } from '../pdfPreview.js'
|
|
18
|
+
import { inNativePath, tryNativeOpen } from '../nativeOpenStore.js'
|
|
19
|
+
import { readBinaryPreview } from '../../shared/rpc.js'
|
|
18
20
|
import {
|
|
19
21
|
clearBaseline,
|
|
20
22
|
clearReadVersion,
|
|
@@ -121,6 +123,22 @@ function dirOfRel(relPath) {
|
|
|
121
123
|
/** 跳转目标高亮的保留时长(LSP/搜索跳转落地后给用户的位置提示,到期自动清除)。 */
|
|
122
124
|
const NAV_FLASH_MS = 1200
|
|
123
125
|
|
|
126
|
+
/** 二进制通道回退日志的一次性闸(整页仅报一次,避免每次读图/读 PDF 刷屏)。 */
|
|
127
|
+
let binFallbackLogged = false
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 二进制通道回退的一次性诊断日志:readBinaryPreview 本次尝试过新通道并失败时上报。
|
|
131
|
+
* 有效期:随页面生命周期仅一条;dbg 本身受诊断开关控制(关=零输出零落盘)。
|
|
132
|
+
* @author ddj 2026年09月22号
|
|
133
|
+
* @param sid 会话 id
|
|
134
|
+
* @param out readBinaryPreview 统一结果(带 probed 才说明本次尝试过新通道)
|
|
135
|
+
*/
|
|
136
|
+
const noteBinFallback = (sid, out) => {
|
|
137
|
+
if (binFallbackLogged || !out?.probed) return
|
|
138
|
+
binFallbackLogged = true
|
|
139
|
+
dbg(sid, 'edrv.readBinary 不可用(host 未接线/旧版或网络失败),已回退 base64', 'debug')
|
|
140
|
+
}
|
|
141
|
+
|
|
124
142
|
/**
|
|
125
143
|
* 从 Monaco 语言目录取可选语言 id 列表(代码片段新建时的语言下拉来源)。
|
|
126
144
|
* 旧版 Monaco / 目录不可读时返回 undefined,由调用方回落 shared 的内置常量。
|
|
@@ -197,6 +215,8 @@ export function EditorView(props) {
|
|
|
197
215
|
const [imageSrc, setImageSrc] = React.useState(null) // 图片预览 data URL(图片 tab 专用,文本 tab 恒为 null)
|
|
198
216
|
const [imgSize, setImgSize] = React.useState(null) // 图片自然尺寸 { w, h }(路径栏 meta)
|
|
199
217
|
const [imgBroken, setImgBroken] = React.useState(false) // 图片解码失败(onError),显示占位与重试
|
|
218
|
+
const [imgZoom, setImgZoom] = React.useState(null) // 图片缩放比例(null=适应宽度;会话+路径级记忆,见 imagePreview.zoomKeyOf)
|
|
219
|
+
const imgElRef = React.useRef(null) // 预览 <img> 元素(适应宽度态下步进取实测缩放基准)
|
|
200
220
|
const svgTextRef = React.useRef(new Set()) // 强制以文本打开的 SVG 路径集合(toggleSvgText 维护)
|
|
201
221
|
const mdPreviewRef = React.useRef(new Set()) // 处于预览态的 Markdown 路径集合(toggleMdPreview 维护;不持久化)
|
|
202
222
|
const [pdfBytes, setPdfBytes] = React.useState(null) // 当前 PDF tab 的原始字节(null=加载中/非 PDF)
|
|
@@ -828,26 +848,36 @@ export function EditorView(props) {
|
|
|
828
848
|
setImgSize(null)
|
|
829
849
|
setImgBroken(false)
|
|
830
850
|
setLoadStage({ progress: monaco ? 72 : 12, message: '读取图片…' })
|
|
831
|
-
rpc
|
|
851
|
+
readBinaryPreview(rpc, { sessionId: sid, path }).then((out) => {
|
|
832
852
|
if (seq !== loadSeqRef.current || path !== active) return
|
|
833
|
-
if (
|
|
834
|
-
|
|
853
|
+
if (out.ok && out.via === 'binary') {
|
|
854
|
+
// 新通道:原始字节本地组 data URL(线上省 base64,本地编码为 img src 所需不可省)
|
|
855
|
+
recordBaseline(scope, path, out.version || version)
|
|
856
|
+
clearReadVersion(scope, path) // 刚读过内容:已读版本台账失效,下一轮按新版本比对
|
|
857
|
+
setImageSrc(dataUrlOf(bytesToBase64(out.bytes), out.mime))
|
|
858
|
+
setLoadStage({ progress: 100, message: '图片已就绪' })
|
|
859
|
+
setStatus('已加载')
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
noteBinFallback(sid, out)
|
|
863
|
+
if (out.ok && out.via === 'base64') {
|
|
864
|
+
if (!out.mime) {
|
|
835
865
|
setLoadError('host 版本过旧:图片响应缺少 mime')
|
|
836
866
|
setError('host 版本过旧:图片响应缺少 mime')
|
|
837
867
|
setStatus('读取失败')
|
|
838
868
|
return
|
|
839
869
|
}
|
|
840
|
-
const imgVersion =
|
|
870
|
+
const imgVersion = out.version ?? version
|
|
841
871
|
recordBaseline(scope, path, imgVersion)
|
|
842
872
|
clearReadVersion(scope, path) // 刚读过内容:已读版本台账失效,下一轮按新版本比对
|
|
843
|
-
setImageSrc(dataUrlOf(
|
|
873
|
+
setImageSrc(dataUrlOf(out.content, out.mime))
|
|
844
874
|
setLoadStage({ progress: 100, message: '图片已就绪' })
|
|
845
875
|
setStatus('已加载')
|
|
846
876
|
return
|
|
847
877
|
}
|
|
848
|
-
const base =
|
|
878
|
+
const base = out.error ? String(out.error) : '读取失败'
|
|
849
879
|
// 带出 host 解析后的真实路径:跳转失败时一眼看出是路径解析错还是目标不存在
|
|
850
|
-
const message =
|
|
880
|
+
const message = out.resolvedPath ? base + ':' + String(out.resolvedPath) : base
|
|
851
881
|
setLoadError(message)
|
|
852
882
|
setError(message)
|
|
853
883
|
setStatus('读取失败')
|
|
@@ -884,21 +914,34 @@ export function EditorView(props) {
|
|
|
884
914
|
setStatus('已加载')
|
|
885
915
|
return
|
|
886
916
|
}
|
|
887
|
-
rpc
|
|
917
|
+
readBinaryPreview(rpc, { sessionId: sid, path }).then((out) => {
|
|
888
918
|
if (seq !== loadSeqRef.current || path !== active) return
|
|
889
|
-
if (
|
|
890
|
-
const pdfVersion =
|
|
919
|
+
if (out.ok && out.via === 'binary') {
|
|
920
|
+
const pdfVersion = out.version || version
|
|
921
|
+
recordBaseline(scope, path, pdfVersion)
|
|
922
|
+
clearReadVersion(scope, path) // 刚读过内容:已读版本台账失效,下一轮按新版本比对
|
|
923
|
+
// 缓存仍存 base64(pdfB64CacheRef 形状不变,命中路径零改动);字节直通面板省一次解码
|
|
924
|
+
cache.set(path, bytesToBase64(out.bytes))
|
|
925
|
+
while (cache.size > 6) cache.delete(cache.keys().next().value)
|
|
926
|
+
setPdfBytes(out.bytes)
|
|
927
|
+
setLoadStage({ progress: 100, message: 'PDF 已就绪' })
|
|
928
|
+
setStatus('已加载')
|
|
929
|
+
return
|
|
930
|
+
}
|
|
931
|
+
noteBinFallback(sid, out)
|
|
932
|
+
if (out.ok && out.via === 'base64') {
|
|
933
|
+
const pdfVersion = out.version ?? version
|
|
891
934
|
recordBaseline(scope, path, pdfVersion)
|
|
892
935
|
clearReadVersion(scope, path) // 刚读过内容:已读版本台账失效,下一轮按新版本比对
|
|
893
|
-
cache.set(path,
|
|
936
|
+
cache.set(path, out.content)
|
|
894
937
|
while (cache.size > 6) cache.delete(cache.keys().next().value)
|
|
895
|
-
setPdfBytes(base64ToBytes(
|
|
938
|
+
setPdfBytes(base64ToBytes(out.content))
|
|
896
939
|
setLoadStage({ progress: 100, message: 'PDF 已就绪' })
|
|
897
940
|
setStatus('已加载')
|
|
898
941
|
return
|
|
899
942
|
}
|
|
900
|
-
const base =
|
|
901
|
-
const message =
|
|
943
|
+
const base = out.error ? String(out.error) : '读取失败'
|
|
944
|
+
const message = out.resolvedPath ? base + ':' + String(out.resolvedPath) : base
|
|
902
945
|
setLoadError(message)
|
|
903
946
|
setError(message)
|
|
904
947
|
setStatus('读取失败')
|
|
@@ -1369,6 +1412,10 @@ export function EditorView(props) {
|
|
|
1369
1412
|
if (!event?.detail?.scope || event.detail.scope === scope) setSvnStatus(getSvnStatus(scope))
|
|
1370
1413
|
}
|
|
1371
1414
|
window.addEventListener('edrv:svn-status', onSvnStatus)
|
|
1415
|
+
// 缓存命中时 ensure 直接 return、不广播事件,须同步读回当前 scope 的载荷:
|
|
1416
|
+
// 否则从非 SVN 工作区切回 SVN 工作区后,状态残留上一个工作区的 managed:false,
|
|
1417
|
+
// 右键菜单/页签菜单的 SVN 组会整组消失(与下方变更清单 effect 的同步语句同理)。
|
|
1418
|
+
setSvnStatus(getSvnStatus(scope))
|
|
1372
1419
|
return () => window.removeEventListener('edrv:svn-status', onSvnStatus)
|
|
1373
1420
|
}, [sessionId, scope])
|
|
1374
1421
|
|
|
@@ -1908,6 +1955,7 @@ export function EditorView(props) {
|
|
|
1908
1955
|
// model 不在此销毁:跨挂载缓存按作用域存活(modelCache 切作用域时统一释放)
|
|
1909
1956
|
for (const ctl of pdfCtlRef.current.values()) ctl.destroy()
|
|
1910
1957
|
pdfCtlRef.current.clear()
|
|
1958
|
+
clearZoomMem() // 图片缩放记忆随 EditorView 卸载清空(会话级生命周期,不持久化)
|
|
1911
1959
|
const root = editorRef.current && editorRef.current.getDomNode ? editorRef.current.getDomNode() : null
|
|
1912
1960
|
if (root) {
|
|
1913
1961
|
const ov = root.querySelector('.edrv-minus-overlay')
|
|
@@ -2401,15 +2449,58 @@ export function EditorView(props) {
|
|
|
2401
2449
|
}
|
|
2402
2450
|
toggleMdPreviewRef.current = toggleMdPreview
|
|
2403
2451
|
|
|
2452
|
+
/** 当前图片的会话+路径级缩放记忆键(页签切换回来保留同一缩放)。 */
|
|
2453
|
+
const imgZoomKey = zoomKeyOf(sessionId, active)
|
|
2454
|
+
|
|
2455
|
+
// 页签/会话切换时恢复该图片的记忆缩放(无记忆 = 初始适应宽度,等价 PDF page-width)
|
|
2456
|
+
React.useEffect(() => { setImgZoom(recallZoom(imgZoomKey)) }, [imgZoomKey])
|
|
2457
|
+
|
|
2404
2458
|
/**
|
|
2405
|
-
*
|
|
2406
|
-
* @author ddj 2026年09月
|
|
2459
|
+
* 应用图片缩放并写入页签记忆(null = 适应宽度;数值经 clampZoom 收敛 25%–400%)。
|
|
2460
|
+
* @author ddj 2026年09月22号
|
|
2461
|
+
* @param next 目标缩放(null = 适应宽度)
|
|
2462
|
+
*/
|
|
2463
|
+
const applyImgZoom = (next) => {
|
|
2464
|
+
const value = next === null ? null : clampZoom(next)
|
|
2465
|
+
setImgZoom(value)
|
|
2466
|
+
rememberZoom(imgZoomKey, value)
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
/**
|
|
2470
|
+
* 图片缩放步进(10%/档,clamp 25%–400%);适应宽度态先按渲染尺寸实测当前缩放作基准。
|
|
2471
|
+
* @author ddj 2026年09月22号
|
|
2472
|
+
* @param dir 方向:1 放大 / -1 缩小
|
|
2473
|
+
*/
|
|
2474
|
+
const stepImgZoom = (dir) => {
|
|
2475
|
+
const el = imgElRef.current
|
|
2476
|
+
const measured = el && el.naturalWidth ? el.clientWidth / el.naturalWidth : 1
|
|
2477
|
+
applyImgZoom(zoomStepOf(imgZoom ?? measured, dir))
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
/**
|
|
2481
|
+
* 图片预览面板:工具条(尺寸 meta / 缩放 −+适应宽度 / SVG 文本切换 / 刷新)+ 棋盘底图片。
|
|
2482
|
+
* 初始适应宽度(等价 PDF page-width,0.1.7 官方统一缩放);按钮样式与 PDF 工具条同款。
|
|
2483
|
+
* @author ddj 2026年09月08号 / 2026年09月22号
|
|
2407
2484
|
*/
|
|
2408
2485
|
const imagePanel = () => React.createElement('div', { className: 'edrv-imgview' },
|
|
2409
2486
|
React.createElement('div', { className: 'edrv-imgview-bar' },
|
|
2410
2487
|
React.createElement('span', { className: 'edrv-imgview-meta' },
|
|
2411
2488
|
imgBroken ? '图片无法显示' : (imgSize ? imgSize.w + '×' + imgSize.h : '…')),
|
|
2412
2489
|
React.createElement('span', { style: { flex: 1 } }),
|
|
2490
|
+
React.createElement('span', { className: 'edrv-pdf-page' },
|
|
2491
|
+
imgZoom === null ? '适应' : Math.round(imgZoom * 100) + '%'),
|
|
2492
|
+
React.createElement('button', {
|
|
2493
|
+
className: 'edrv-pill edrv-pill-ghost edrv-pdf-zoom', title: '缩小(10%/档)',
|
|
2494
|
+
onClick: () => stepImgZoom(-1),
|
|
2495
|
+
}, '−'),
|
|
2496
|
+
React.createElement('button', {
|
|
2497
|
+
className: 'edrv-pill edrv-pill-ghost edrv-pdf-zoom', title: '放大(10%/档)',
|
|
2498
|
+
onClick: () => stepImgZoom(1),
|
|
2499
|
+
}, '+'),
|
|
2500
|
+
React.createElement('button', {
|
|
2501
|
+
className: 'edrv-pill edrv-pill-ghost', title: '恢复适应宽度',
|
|
2502
|
+
disabled: imgZoom === null, onClick: () => applyImgZoom(null),
|
|
2503
|
+
}, '适应宽度'),
|
|
2413
2504
|
(isSvgPath(active) ? React.createElement('button', {
|
|
2414
2505
|
className: 'edrv-pill edrv-pill-ghost', title: '以文本方式查看/编辑(SVG 为文本格式)',
|
|
2415
2506
|
onClick: () => toggleSvgText(active),
|
|
@@ -2422,8 +2513,12 @@ export function EditorView(props) {
|
|
|
2422
2513
|
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', onClick: () => reloadFile() }, '重试'))
|
|
2423
2514
|
: React.createElement('img', {
|
|
2424
2515
|
className: 'edrv-imgview-img',
|
|
2516
|
+
ref: imgElRef,
|
|
2425
2517
|
src: imageSrc,
|
|
2426
2518
|
alt: active || '',
|
|
2519
|
+
style: (imgZoom !== null && imgSize)
|
|
2520
|
+
? { width: Math.round(imgSize.w * imgZoom) + 'px', maxWidth: 'none', maxHeight: 'none' }
|
|
2521
|
+
: undefined,
|
|
2427
2522
|
onLoad: (e) => { setImgBroken(false); setImgSize({ w: e.target.naturalWidth, h: e.target.naturalHeight }) },
|
|
2428
2523
|
onError: () => setImgBroken(true),
|
|
2429
2524
|
}))))
|
|
@@ -2996,6 +3091,10 @@ export function EditorView(props) {
|
|
|
2996
3091
|
|
|
2997
3092
|
const openFile = (path, focusDiff) => {
|
|
2998
3093
|
if (!path) return
|
|
3094
|
+
// 原生打开范围(nativeOpenExts 设置):命中且非差异跳转 → 走官方渲染器
|
|
3095
|
+
//(focusDiff=true 跳过:差异审查对象必为本插件编辑的文本文件,审查优先);
|
|
3096
|
+
// handler 缺失/失败(旧版无 openResource / seat 未挂)→ 回退现状页签打开,不阻断
|
|
3097
|
+
if (!focusDiff && inNativePath(path) && tryNativeOpen(path)) return
|
|
2999
3098
|
// 打开文件即离开基线差异审阅态(差异视图是「当前文件」的临时视图)
|
|
3000
3099
|
setSvnDiff(null)
|
|
3001
3100
|
recordNav()
|
|
@@ -13,6 +13,7 @@ import { SettingsContext } from '../settingsContext.js'
|
|
|
13
13
|
import { normalizeSidebarMinWidth, SIDEBAR_MIN_DEFAULT } from '../sidebarMin.js'
|
|
14
14
|
import { EDITOR_LIMIT_CEIL, EDITOR_LIMIT_DEFAULT, normalizeMaxOpenEditors } from '../../shared/editorLimit.js'
|
|
15
15
|
import { TORTOISE_DIR_DEFAULT } from '../../shared/svn.js'
|
|
16
|
+
import { DEFAULT_NATIVE_CSV } from '../../shared/nativeOpen.js'
|
|
16
17
|
import { KeybindingsPanel } from './KeybindingsPanel.js'
|
|
17
18
|
import { LspSettings } from './LspSettings.js'
|
|
18
19
|
import { PerfSettings } from './PerfSettings.js'
|
|
@@ -139,6 +140,8 @@ function GeneralSettings({ registry }) {
|
|
|
139
140
|
const [minDraft, setMinDraft] = React.useState(null) // 输入草稿(null=跟随已保存值;blur/Enter 提交)
|
|
140
141
|
const [limit, setLimit] = React.useState(EDITOR_LIMIT_DEFAULT)
|
|
141
142
|
const [limitDraft, setLimitDraft] = React.useState(null) // 页签上限草稿(同上提交语义)
|
|
143
|
+
const [nativeSaved, setNativeSaved] = React.useState(DEFAULT_NATIVE_CSV) // 原生打开范围已保存值
|
|
144
|
+
const [nativeDraft, setNativeDraft] = React.useState(null) // 原生打开范围草稿(同上提交语义)
|
|
142
145
|
const settings = React.useContext(SettingsContext)
|
|
143
146
|
const snapshot = settings?.getSnapshot?.()
|
|
144
147
|
const loading = !snapshot || snapshot.status === 'loading'
|
|
@@ -151,6 +154,8 @@ function GeneralSettings({ registry }) {
|
|
|
151
154
|
if (typeof next === 'string') setTool(next)
|
|
152
155
|
setMinW(normalizeSidebarMinWidth(snap?.value?.sidebarMinWidth))
|
|
153
156
|
setLimit(normalizeMaxOpenEditors(snap?.value?.maxOpenEditors))
|
|
157
|
+
const native = snap?.value?.nativeOpenExts
|
|
158
|
+
setNativeSaved(typeof native === 'string' && native.trim() ? native : DEFAULT_NATIVE_CSV)
|
|
154
159
|
}
|
|
155
160
|
onChange()
|
|
156
161
|
return settings?.subscribe?.(onChange)
|
|
@@ -191,6 +196,18 @@ function GeneralSettings({ registry }) {
|
|
|
191
196
|
setLimitDraft(null)
|
|
192
197
|
saveLimit(limitDraft)
|
|
193
198
|
}
|
|
199
|
+
/** 提交原生打开范围(逗号分隔后缀;空串回落默认让位清单后持久化)。 */
|
|
200
|
+
const saveNative = (raw) => {
|
|
201
|
+
const next = String(raw ?? '').trim() || DEFAULT_NATIVE_CSV
|
|
202
|
+
setNativeSaved(next); setNativeDraft(null); setBusy(true); setError('')
|
|
203
|
+
if (!settings?.set) { setError('设置服务不可用'); setBusy(false); return }
|
|
204
|
+
settings.set('nativeOpenExts', next).catch((e) => setError(String(e))).finally(() => setBusy(false))
|
|
205
|
+
}
|
|
206
|
+
/** 结束输入(blur/Enter)时提交原生打开范围草稿。 */
|
|
207
|
+
const commitNative = () => {
|
|
208
|
+
if (nativeDraft === null) return
|
|
209
|
+
saveNative(nativeDraft)
|
|
210
|
+
}
|
|
194
211
|
const closeDevForm = () => {
|
|
195
212
|
if (!window.confirm('关闭开发形态:插件将切换为正式版安装(版本依赖 + 删除工作区链接),pnpm 装配后需重启 DSH 生效。确认关闭?')) return
|
|
196
213
|
setDevBusy(true)
|
|
@@ -249,6 +266,19 @@ function GeneralSettings({ registry }) {
|
|
|
249
266
|
onKeyDown: (event) => { if (event.key === 'Enter') commitLimit() },
|
|
250
267
|
}),
|
|
251
268
|
React.createElement('small', null, '0 = 不限制;超限时关闭最久未使用的页签(固定页签除外)')),
|
|
269
|
+
React.createElement('label', { className: 'vsm-general-row' },
|
|
270
|
+
React.createElement('span', null, '原生打开范围'),
|
|
271
|
+
React.createElement('input', {
|
|
272
|
+
type: 'text',
|
|
273
|
+
value: nativeDraft ?? nativeSaved,
|
|
274
|
+
disabled: loading || unavailable || notReady || busy || snapshot?.writable === false,
|
|
275
|
+
title: '逗号分隔后缀(如 doc,xlsx):文件树点击命中时用 DSH 官方预览原生打开;留空 = 默认让位清单(Office + 不可预览二进制)',
|
|
276
|
+
placeholder: 'doc,xlsx,…(留空恢复默认)',
|
|
277
|
+
onChange: (event) => setNativeDraft(event.target.value),
|
|
278
|
+
onBlur: commitNative,
|
|
279
|
+
onKeyDown: (event) => { if (event.key === 'Enter') commitNative() },
|
|
280
|
+
}),
|
|
281
|
+
React.createElement('small', null, '逗号分隔;命中的文件用 DSH 原生预览打开,留空恢复默认(Office + 二进制,csv/tsv 需显式加入)')),
|
|
252
282
|
),
|
|
253
283
|
),
|
|
254
284
|
React.createElement(SvnSettingsSection, null),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
/**
|
|
3
3
|
* dsh-vscode-mode client — PerfSettings:「性能优化」设置子页(会话卫生 + 压缩调优)。
|
|
4
|
-
* -
|
|
4
|
+
* - 会话盘点:全工作区/每会话体积、活跃标记、官方归档/置顶徽标,按体积排序;
|
|
5
5
|
* - 移出到归档(先规划→确认→执行→可恢复)与归档清除;
|
|
6
6
|
* - DSH 内置压缩调优:写 profile patch(compaction-basic / tool-result-pruner 更低阈值,
|
|
7
7
|
* 带备份与撤销,需重启生效);
|
|
@@ -67,6 +67,9 @@ function SessionRow({ row, checked, disabled, onToggle }) {
|
|
|
67
67
|
React.createElement('span', { className: 'vsm-perf-cell vsm-perf-size ' + (row.bytes >= 1024 * 1024 ? 'big' : '') }, formatBytes(row.bytes)),
|
|
68
68
|
React.createElement('span', { className: 'vsm-perf-cell vsm-perf-mtime', title: new Date(row.mtime).toLocaleString() }, when),
|
|
69
69
|
row.active ? React.createElement('span', { className: 'vsm-perf-cell vsm-perf-active' }, '活跃') : null,
|
|
70
|
+
// 官方侧栏归档/置顶状态徽标(旧 host 不回传字段 → undefined 按 false,不渲染;样式复用面板现有 vsm-perf-active 徽标)
|
|
71
|
+
row.archived ? React.createElement('span', { className: 'vsm-perf-cell vsm-perf-active', title: '官方侧栏已归档(持久标志位,未搬目录)' }, '已归档') : null,
|
|
72
|
+
row.pinned ? React.createElement('span', { className: 'vsm-perf-cell vsm-perf-active', title: '官方侧栏已置顶' }, '已置顶') : null,
|
|
70
73
|
)
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode client — UI 原语图标跨版本出口(消费方导出名保持 ≤0.1.6 旧名)。
|
|
3
|
+
* 背景:DSH 0.1.7 图标命名重构——`Icon<Name>16`(0.1.6 及更旧)→
|
|
4
|
+
* `Icon<Name>Regular/Medium`(0.1.7+,size 参数化 + 双笔画档),旧名在 0.1.7
|
|
5
|
+
* 全树已不存在(整树 grep 实证)。解析策略:namespace import 属性探测,旧名优先
|
|
6
|
+
* (现网 0.1.6)→ 新名次之(0.1.7+)→ 皆缺走通用占位(第三态不炸渲染);
|
|
7
|
+
* 打包期 external 不校验成员存在性,两代运行时均稳(能力探测,不做版本判定)。
|
|
8
|
+
* 调用点零语义变化:导出名与旧垫片一致,仅改 import 来源。
|
|
9
|
+
* @author ddj 2026年09月22号
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react'
|
|
12
|
+
import * as primitives from '@deepseek-ai/dsh-client-ui-primitives'
|
|
13
|
+
import type { EdrvIconProps } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
14
|
+
|
|
15
|
+
/** 图标组件形状(与垫片 EdrvIconProps 兼容)。 */
|
|
16
|
+
type IconComponent = (props: EdrvIconProps) => unknown
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 通用占位图标(两代皆缺的第三态兜底:空态方框,不抛错不白屏)。
|
|
20
|
+
* @author ddj 2026年09月22号
|
|
21
|
+
* @param props 图标 props(size/className 透传)
|
|
22
|
+
* @returns React 元素
|
|
23
|
+
*/
|
|
24
|
+
function fallbackIcon(props: EdrvIconProps): unknown {
|
|
25
|
+
return React.createElement('svg', {
|
|
26
|
+
width: props.size ?? 16,
|
|
27
|
+
height: props.size ?? 16,
|
|
28
|
+
viewBox: '0 0 16 16',
|
|
29
|
+
className: props.className,
|
|
30
|
+
'aria-hidden': true,
|
|
31
|
+
focusable: false,
|
|
32
|
+
}, React.createElement('rect', {
|
|
33
|
+
x: 3.5, y: 3.5, width: 9, height: 9, rx: 2,
|
|
34
|
+
fill: 'none', stroke: 'currentColor', strokeWidth: 1.2, opacity: 0.45,
|
|
35
|
+
}))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 按候选名序列解析图标组件(首个存在的函数命中;皆缺返回占位)。
|
|
40
|
+
* @author ddj 2026年09月22号
|
|
41
|
+
* @param names 候选导出名(旧名在前、新名在后)
|
|
42
|
+
* @returns 图标组件
|
|
43
|
+
*/
|
|
44
|
+
function iconOf(...names: string[]): IconComponent {
|
|
45
|
+
const table = primitives as unknown as Record<string, unknown>
|
|
46
|
+
for (const name of names) {
|
|
47
|
+
const hit = table[name]
|
|
48
|
+
if (typeof hit === 'function') return hit as IconComponent
|
|
49
|
+
}
|
|
50
|
+
return fallbackIcon
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 两代语义等价的图标对(旧名 → 0.1.7 Regular 档;Medium 档不用于本插件 16px 场景)
|
|
54
|
+
/** 目录闭合图标(官方文件树同款)。 */
|
|
55
|
+
export const IconFolderClose16 = iconOf('IconFolderClose16', 'IconFolderCloseRegular')
|
|
56
|
+
/** 目录展开图标(官方文件树同款)。 */
|
|
57
|
+
export const IconFolderOpen16 = iconOf('IconFolderOpen16', 'IconFolderOpenRegular')
|
|
58
|
+
/** 目录轮廓图标(活动栏「文件管理」)。 */
|
|
59
|
+
export const IconFolderOpenOutline16 = iconOf('IconFolderOpenOutline16', 'IconFolderOpenOutlineRegular')
|
|
60
|
+
/** 刷新图标(面板工具条)。 */
|
|
61
|
+
export const IconRefreshOutline16 = iconOf('IconRefreshOutline16', 'IconRefreshOutlineRegular')
|
|
62
|
+
/** 搜索图标(活动栏「搜索」)。 */
|
|
63
|
+
export const IconSearchOutline16 = iconOf('IconSearchOutline16', 'IconSearchOutlineRegular')
|
|
64
|
+
/** 列表+笔图标(活动栏「规则」)。 */
|
|
65
|
+
export const IconListPenOutline16 = iconOf('IconListPenOutline16', 'IconListPenOutlineRegular')
|
|
66
|
+
/** 代码图标(活动栏「大纲」)。 */
|
|
67
|
+
export const IconCodeOutline16 = iconOf('IconCodeOutline16', 'IconCodeOutlineRegular')
|
|
68
|
+
/** 分支图标(SVN 面板)。 */
|
|
69
|
+
export const IconBranchOutline16 = iconOf('IconBranchOutline16', 'IconBranchOutlineRegular')
|
|
70
|
+
|
|
71
|
+
// 两代同名存活的原语:直通再导出(0.1.6/0.1.7 实证均在)
|
|
72
|
+
export { FileTypeIcon, classifyFileType } from '@deepseek-ai/dsh-client-ui-primitives'
|
package/src/compat.ts
CHANGED
|
@@ -133,7 +133,7 @@ export function detectGuards(ctx: Ctx): CompatAdapter[] {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/** 已实测覆盖的最高 DSH 版本(适配矩阵上界,超过则提示,见 buildReport)。 */
|
|
136
|
-
const TESTED_DSH_MAX = '0.1.
|
|
136
|
+
const TESTED_DSH_MAX = '0.1.7-alpha.1'
|
|
137
137
|
|
|
138
138
|
/** 版本适配机制状态行:DSH 版本探测 + 设置 section 安装策略。 */
|
|
139
139
|
export function versionAdapters(dshVersion: string): CompatAdapter[] {
|
|
@@ -149,7 +149,7 @@ export function versionAdapters(dshVersion: string): CompatAdapter[] {
|
|
|
149
149
|
const strategy = settingsInstallStrategy()
|
|
150
150
|
adapters.push({
|
|
151
151
|
name: '设置 section 安装(版本适配)',
|
|
152
|
-
active: strategy === 'legacy' || strategy === 'service',
|
|
152
|
+
active: strategy === 'legacy' || strategy === 'service' || strategy === 'forms',
|
|
153
153
|
note: settingsInstallNote(),
|
|
154
154
|
})
|
|
155
155
|
return adapters
|
package/src/dshVersion.ts
CHANGED
|
@@ -85,13 +85,14 @@ export function inDshRange(version: DshVersion | null | undefined, range: DshRan
|
|
|
85
85
|
/**
|
|
86
86
|
* 版本线标签:报告与文档展示版本归属;不可解析返回 '未知'。
|
|
87
87
|
* 逐线自新到旧匹配,先命中先返回(区间无上界,故顺序即优先级)。
|
|
88
|
-
* @author ddj 2026年09月02号 / 2026年09月18号
|
|
88
|
+
* @author ddj 2026年09月02号 / 2026年09月18号 / 2026年09月22号
|
|
89
89
|
* @param input 版本串(如 '0.1.6-alpha.2')
|
|
90
90
|
* @returns 版本线标签
|
|
91
91
|
*/
|
|
92
92
|
export function familyLabel(input: string): string {
|
|
93
93
|
const version = parseDshVersion(input)
|
|
94
94
|
if (!version) return '未知'
|
|
95
|
+
if (inDshRange(version, { from: '0.1.7-alpha.1' })) return '0.1.7-alpha.1 及更新(设置=profile Config + configForms 客户端面 + 会话 V4)'
|
|
95
96
|
if (inDshRange(version, { from: '0.1.6-alpha.2' })) return '0.1.6-alpha.2 及更新(会话多实例共存 + 回合改动卡片 + Office 侧栏预览 + 侧栏浏览器)'
|
|
96
97
|
if (inDshRange(version, { from: '0.1.6-alpha.1' })) return '0.1.6-alpha 及更新(MCP SDK v2 + Web 侧边栏终端 + 文件链接默认侧栏预览)'
|
|
97
98
|
if (inDshRange(version, { from: '0.1.5-alpha.1' })) return '0.1.5-alpha 及更新(官方右侧 Sidebar 编辑区 + sidebar.panellist)'
|