dsh-plugin-workbench 0.0.12 → 0.0.13
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/CHANGELOG.md +22 -0
- package/lib/client.js +84 -6
- package/lib/client.js.map +1 -1
- package/lib/index.js +31 -1
- package/package.json +1 -1
- package/src/client/FilePreview.tsx +106 -6
- package/src/index.ts +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.13] - 2026-08-26
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **预览分栏宽度拖拽:一拖就骤然缩小到最窄,之后无法拖动、无法复原**。根因:
|
|
8
|
+
拖拽计算"预览最大宽度"时用 `handle.parentElement` 测量中间列宽度,但 slot 系统把
|
|
9
|
+
slot 内容包在一层 `display: contents` 的包装元素里——该包装层不参与布局,
|
|
10
|
+
`getBoundingClientRect().width` 恒为 **0**,于是
|
|
11
|
+
`max = max(240, 0 - 240) = 240`,第一次 `pointermove` 就把宽度钳到最小宽度 240,
|
|
12
|
+
之后每次移动都被钳在 240,表现为骤然缩小且拖不回来(100% 复现,与窗口/鼠标操作
|
|
13
|
+
无关)。修复:`laidOutParent()` 跳过所有 `display: contents` 层,取真正参与布局的
|
|
14
|
+
flex 容器(中间列)再测量;持久化宽度恢复时同样用它校准。
|
|
15
|
+
- 拖拽健壮性(防同类"卡死"):`setPointerCapture` 保证 `pointerup` 在指针移出窗口后
|
|
16
|
+
也能派发并清理监听器;监听 `pointercancel`;每次 `pointerdown` 防御性移除上一次
|
|
17
|
+
残留的监听器;组件卸载时清理窗口监听器;拖拽中每步实时重测中间列宽度。
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **预览宽度持久化**:拖拽后的分栏宽度写入 localStorage,刷新/重开会话后恢复
|
|
22
|
+
(自动按当前窗宽 clamp 在有效区间内)。
|
|
23
|
+
|
|
24
|
+
|
|
3
25
|
## [0.0.12] - 2026-08-25
|
|
4
26
|
|
|
5
27
|
- 相关插件段新增 dsh-plugin-windows-guard(Windows 环境防坑守则 skill 插件,互相引流)。
|
package/lib/client.js
CHANGED
|
@@ -15324,6 +15324,20 @@ window.__ModuleLoader__.load({
|
|
|
15324
15324
|
const PREVIEW_MIN = 240;
|
|
15325
15325
|
const CHAT_MIN = 240;
|
|
15326
15326
|
const PREVIEW_TOO_LARGE_LABEL = "512KB";
|
|
15327
|
+
/** Persisted split width (px) — survives reloads so a drag is never lost. */
|
|
15328
|
+
const PREVIEW_WIDTH_KEY = "dsh-plugin-workbench:preview-width";
|
|
15329
|
+
/** Read the persisted preview width; `null` means "use the default 55%". */
|
|
15330
|
+
function storedPreviewWidth() {
|
|
15331
|
+
try {
|
|
15332
|
+
if (typeof window === "undefined") return null;
|
|
15333
|
+
const raw = window.localStorage.getItem(PREVIEW_WIDTH_KEY);
|
|
15334
|
+
if (raw === null) return null;
|
|
15335
|
+
const px = Number(raw);
|
|
15336
|
+
return Number.isFinite(px) && px > 0 ? px : null;
|
|
15337
|
+
} catch {
|
|
15338
|
+
return null;
|
|
15339
|
+
}
|
|
15340
|
+
}
|
|
15327
15341
|
/** Same-origin raw-bytes route registered by the host half (see src/index.ts). */
|
|
15328
15342
|
const RAW_PREFIX = "/dsh-plugin-files/raw";
|
|
15329
15343
|
/** Same-origin SSE endpoint pushed by the host half (see src/index.ts). */
|
|
@@ -15385,6 +15399,22 @@ window.__ModuleLoader__.load({
|
|
|
15385
15399
|
function clamp(value, min, max) {
|
|
15386
15400
|
return Math.min(max, Math.max(min, value));
|
|
15387
15401
|
}
|
|
15402
|
+
/**
|
|
15403
|
+
* Nearest ancestor that actually takes part in layout. The slot system wraps
|
|
15404
|
+
* each slot's content in a `display: contents` element: children still join
|
|
15405
|
+
* the OUTER flex row, but the wrapper itself reports a 0×0 bounding rect.
|
|
15406
|
+
* Measuring that (the old `handle.parentElement`) made `max` collapse to
|
|
15407
|
+
* `PREVIEW_MIN` on the first move — the pane jumped to its minimum width and
|
|
15408
|
+
* could never be dragged back out. Skip every `display: contents` layer.
|
|
15409
|
+
*/
|
|
15410
|
+
function laidOutParent(el) {
|
|
15411
|
+
let node = el?.parentElement ?? null;
|
|
15412
|
+
while (node !== null) {
|
|
15413
|
+
if (getComputedStyle(node).display !== "contents") return node;
|
|
15414
|
+
node = node.parentElement;
|
|
15415
|
+
}
|
|
15416
|
+
return null;
|
|
15417
|
+
}
|
|
15388
15418
|
/** Parent directory of a path ('C:/a/b.md' → 'C:/a'; '' when there is none). */
|
|
15389
15419
|
function dirnameOf(path) {
|
|
15390
15420
|
const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
@@ -15436,6 +15466,8 @@ window.__ModuleLoader__.load({
|
|
|
15436
15466
|
const highlightRef = (0, react.useRef)(null);
|
|
15437
15467
|
const textareaRef = (0, react.useRef)(null);
|
|
15438
15468
|
const gutterRef = (0, react.useRef)(null);
|
|
15469
|
+
const dragMoveRef = (0, react.useRef)(() => void 0);
|
|
15470
|
+
const dragUpRef = (0, react.useRef)(() => void 0);
|
|
15439
15471
|
const refresh = (0, react.useCallback)(() => bump((v) => v + 1), []);
|
|
15440
15472
|
const tabsRef = (0, react.useRef)(tabs);
|
|
15441
15473
|
(0, react.useEffect)(() => {
|
|
@@ -15625,26 +15657,72 @@ window.__ModuleLoader__.load({
|
|
|
15625
15657
|
document.body.style.removeProperty("--dsh-preview-width");
|
|
15626
15658
|
};
|
|
15627
15659
|
}, [isOpen]);
|
|
15660
|
+
(0, react.useEffect)(() => {
|
|
15661
|
+
const saved = storedPreviewWidth();
|
|
15662
|
+
if (saved === null) return;
|
|
15663
|
+
const preview = previewRef.current;
|
|
15664
|
+
const center = laidOutParent(preview);
|
|
15665
|
+
if (preview === null || preview === void 0 || center === null || center === void 0) return;
|
|
15666
|
+
const centerWidth = center.getBoundingClientRect().width;
|
|
15667
|
+
const max = Math.max(PREVIEW_MIN, centerWidth - CHAT_MIN);
|
|
15668
|
+
setPreviewWidth(clamp(saved, PREVIEW_MIN, max));
|
|
15669
|
+
}, [isOpen]);
|
|
15670
|
+
(0, react.useEffect)(() => () => {
|
|
15671
|
+
window.removeEventListener("pointermove", dragMoveRef.current);
|
|
15672
|
+
window.removeEventListener("pointerup", dragUpRef.current);
|
|
15673
|
+
window.removeEventListener("pointercancel", dragUpRef.current);
|
|
15674
|
+
}, []);
|
|
15675
|
+
/**
|
|
15676
|
+
* Start a split-width drag. Robustness notes (the "pane suddenly shrinks
|
|
15677
|
+
* and freezes" bug class):
|
|
15678
|
+
*
|
|
15679
|
+
* - Pointer capture reroutes every later pointer event to the handle, so
|
|
15680
|
+
* `pointerup` fires EVEN when the mouse is released outside the window.
|
|
15681
|
+
* Without it the up event is lost, `onUp` never runs, and the leftover
|
|
15682
|
+
* `onMove` keeps rewriting the width from its stale baseline on every
|
|
15683
|
+
* mouse move anywhere on the page — that is the "cannot drag / cannot
|
|
15684
|
+
* restore" state.
|
|
15685
|
+
* - `pointercancel` is cleaned up too (browser steals the pointer, e.g. a
|
|
15686
|
+
* tablet palm or an OS gesture).
|
|
15687
|
+
* - Pointerdown defensively removes any previous listeners first, so even a
|
|
15688
|
+
* capture-less leftover cannot survive into a second drag.
|
|
15689
|
+
* - The max is re-measured every move: the chat minimum is relative to the
|
|
15690
|
+
* CURRENT center column, which can change while the drag is in flight.
|
|
15691
|
+
*/
|
|
15628
15692
|
const onHandleDown = (0, react.useCallback)((e) => {
|
|
15629
15693
|
e.preventDefault();
|
|
15630
15694
|
const handle = handleRef.current;
|
|
15631
15695
|
const preview = previewRef.current;
|
|
15632
15696
|
if (handle === null || preview === null) return;
|
|
15633
|
-
const center = handle
|
|
15697
|
+
const center = laidOutParent(handle);
|
|
15634
15698
|
if (center === null) return;
|
|
15635
15699
|
const startX = e.clientX;
|
|
15636
15700
|
const startWidth = preview.getBoundingClientRect().width;
|
|
15637
|
-
const centerWidth = center.getBoundingClientRect().width;
|
|
15638
|
-
const max = Math.max(PREVIEW_MIN, centerWidth - CHAT_MIN);
|
|
15639
15701
|
const onMove = (ev) => {
|
|
15640
|
-
|
|
15702
|
+
const centerWidth = center.getBoundingClientRect().width;
|
|
15703
|
+
const max = Math.max(PREVIEW_MIN, centerWidth - CHAT_MIN);
|
|
15704
|
+
const width = clamp(startWidth + ev.clientX - startX, PREVIEW_MIN, max);
|
|
15705
|
+
setPreviewWidth(width);
|
|
15706
|
+
try {
|
|
15707
|
+
window.localStorage.setItem(PREVIEW_WIDTH_KEY, String(width));
|
|
15708
|
+
} catch {}
|
|
15641
15709
|
};
|
|
15642
15710
|
const onUp = () => {
|
|
15643
|
-
window.removeEventListener("pointermove",
|
|
15644
|
-
window.removeEventListener("pointerup",
|
|
15711
|
+
window.removeEventListener("pointermove", dragMoveRef.current);
|
|
15712
|
+
window.removeEventListener("pointerup", dragUpRef.current);
|
|
15713
|
+
window.removeEventListener("pointercancel", dragUpRef.current);
|
|
15645
15714
|
};
|
|
15715
|
+
window.removeEventListener("pointermove", dragMoveRef.current);
|
|
15716
|
+
window.removeEventListener("pointerup", dragUpRef.current);
|
|
15717
|
+
window.removeEventListener("pointercancel", dragUpRef.current);
|
|
15718
|
+
dragMoveRef.current = onMove;
|
|
15719
|
+
dragUpRef.current = onUp;
|
|
15646
15720
|
window.addEventListener("pointermove", onMove);
|
|
15647
15721
|
window.addEventListener("pointerup", onUp);
|
|
15722
|
+
window.addEventListener("pointercancel", onUp);
|
|
15723
|
+
try {
|
|
15724
|
+
handle.setPointerCapture(e.pointerId);
|
|
15725
|
+
} catch {}
|
|
15648
15726
|
}, []);
|
|
15649
15727
|
const onSave = (0, react.useCallback)(async () => {
|
|
15650
15728
|
if (active === void 0) return;
|