dsh-workspace-explorer 1.1.0 → 1.1.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/CHANGELOG.md +15 -0
- package/README.md +11 -9
- package/docs/screenshots/grid-favorites.png +0 -0
- package/docs/screenshots/stl-preview.png +0 -0
- package/docs/screenshots/workspace-search.png +0 -0
- package/lib/client.js +820 -162
- package/package.json +53 -52
- package/scripts/build-client.mjs +1 -0
- package/src/client/conversation-drop.js +135 -0
- package/src/client/main.js +361 -91
- package/src/client/sidebar-motion.js +69 -0
- package/src/document-types.js +2 -2
- package/src/host/ExplorerShell.cs +183 -183
- package/src/host/files.js +25 -6
- package/src/host/pdf-assets.js +30 -30
- package/src/host/shell-files.js +81 -81
- package/src/thumbnail-types.js +18 -18
- package/src/ui/composer-drop.js +119 -119
- package/src/ui/explorer.js +42 -42
- package/src/ui/favorites.js +176 -124
- package/src/ui/file-menu.js +125 -125
- package/src/ui/horizontal-wheel.js +11 -11
- package/src/ui/navigation.js +9 -0
- package/src/ui/pdf-preview.js +135 -135
- package/src/ui/preview-unavailable.js +6 -6
- package/src/ui/preview-zoom.js +47 -47
- package/src/ui/preview.js +58 -12
- package/src/ui/styles.js +74 -15
- package/src/ui/thumbnail.js +105 -105
- package/src/vendor/ui/extra-views.js +1 -1
package/package.json
CHANGED
|
@@ -1,52 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-workspace-explorer",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Windows disk browsing, file operations and persistent preview in DeepSeek Harness Desktop.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./lib/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./lib/index.js",
|
|
9
|
-
"./client": "./lib/client.js",
|
|
10
|
-
"./package.json": "./package.json"
|
|
11
|
-
},
|
|
12
|
-
"dsh": {
|
|
13
|
-
"bundle": {
|
|
14
|
-
"patch": "./cordis.patch.yml"
|
|
15
|
-
},
|
|
16
|
-
"client": {
|
|
17
|
-
"platform": "web",
|
|
18
|
-
"inject": [
|
|
19
|
-
"@deepseek-ai/dsh-api-remotes",
|
|
20
|
-
"@deepseek-ai/dsh-api-session-controller",
|
|
21
|
-
"@deepseek-ai/dsh-
|
|
22
|
-
"@deepseek-ai/dsh-client-ui-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
}
|
|
52
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-workspace-explorer",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Windows disk browsing, file operations and persistent preview in DeepSeek Harness Desktop.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"dsh": {
|
|
13
|
+
"bundle": {
|
|
14
|
+
"patch": "./cordis.patch.yml"
|
|
15
|
+
},
|
|
16
|
+
"client": {
|
|
17
|
+
"platform": "web",
|
|
18
|
+
"inject": [
|
|
19
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
20
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
21
|
+
"@deepseek-ai/dsh-api-workspace-files",
|
|
22
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right",
|
|
23
|
+
"@deepseek-ai/dsh-client-ui-session"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "node scripts/build-client.mjs"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"pdfjs-dist": "5.6.205"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"lib",
|
|
35
|
+
"cordis.patch.yml",
|
|
36
|
+
"src",
|
|
37
|
+
"scripts",
|
|
38
|
+
"README.md",
|
|
39
|
+
"CHANGELOG.md",
|
|
40
|
+
"docs",
|
|
41
|
+
"LICENSE-preview-extra"
|
|
42
|
+
],
|
|
43
|
+
"keywords": [
|
|
44
|
+
"dsh-plugin",
|
|
45
|
+
"deepseek-harness",
|
|
46
|
+
"file-explorer"
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/tm5452/dsh-workspace-explorer.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/tm5452/dsh-workspace-explorer#readme"
|
|
53
|
+
}
|
package/scripts/build-client.mjs
CHANGED
|
@@ -206,6 +206,7 @@ window.__ModuleLoader__.load({
|
|
|
206
206
|
// Frozen platform table entries (see the shell's static module table).
|
|
207
207
|
var react = require("react");
|
|
208
208
|
var reactDom = require("react-dom");
|
|
209
|
+
var reactDomClient = require("react-dom/client");
|
|
209
210
|
var jsxRuntime = require("react/jsx-runtime");
|
|
210
211
|
var primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
211
212
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export const changedFileButton = '[data-changed-files] button[aria-describedby]';
|
|
2
|
+
const sourceSelector = '[data-presented-file] > button[title], [data-chat-flow] button[class*="_fileMention_"], ' + changedFileButton;
|
|
3
|
+
const dragThreshold = 5;
|
|
4
|
+
|
|
5
|
+
export function installConversationDrop(mounted, readFile, openPreview) {
|
|
6
|
+
const lifetime = new AbortController();
|
|
7
|
+
const overlay = document.createElement('div');
|
|
8
|
+
overlay.className = 'dwe-conversation-drop';
|
|
9
|
+
overlay.setAttribute('aria-hidden', 'true');
|
|
10
|
+
const area = document.createElement('div');
|
|
11
|
+
area.className = 'dwe-conversation-drop-area';
|
|
12
|
+
const icon = document.createElement('div');
|
|
13
|
+
icon.className = 'dwe-conversation-drag-icon';
|
|
14
|
+
const iconRoot = reactDomClient.createRoot(icon);
|
|
15
|
+
overlay.append(area, icon);
|
|
16
|
+
let gesture;
|
|
17
|
+
let suppressClick = false;
|
|
18
|
+
|
|
19
|
+
function reset() {
|
|
20
|
+
const previous = gesture;
|
|
21
|
+
gesture = undefined;
|
|
22
|
+
if (previous && document.body.hasPointerCapture(previous.pointerId)) {
|
|
23
|
+
document.body.releasePointerCapture(previous.pointerId);
|
|
24
|
+
}
|
|
25
|
+
overlay.remove();
|
|
26
|
+
overlay.removeAttribute('data-dwe-drop-active');
|
|
27
|
+
if (previous?.file) iconRoot.render(null);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function arm(event) {
|
|
31
|
+
if (event.button !== 0 || event.pointerType !== 'mouse' || !(event.target instanceof Element)) return;
|
|
32
|
+
suppressClick = false;
|
|
33
|
+
reset();
|
|
34
|
+
const button = event.target.closest(sourceSelector);
|
|
35
|
+
const conversation = button?.closest('[data-conversation-content]');
|
|
36
|
+
const sessionId = conversation?.dataset.conversationSession;
|
|
37
|
+
if (!sessionId || sessionId !== mounted.getSnapshot()) return;
|
|
38
|
+
gesture = { button, conversation, pointerId: event.pointerId, x: event.clientX, y: event.clientY };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function layout() {
|
|
42
|
+
if (!gesture?.file) return;
|
|
43
|
+
const rightbar = document.querySelector('[data-rightbar-col]');
|
|
44
|
+
const center = rightbar?.previousElementSibling ?? gesture.conversation;
|
|
45
|
+
const centerBounds = center.getBoundingClientRect();
|
|
46
|
+
const left = Math.max(0, centerBounds.left);
|
|
47
|
+
const right = rightbar?.getBoundingClientRect();
|
|
48
|
+
overlay.style.top = Math.max(0, centerBounds.top) + 'px';
|
|
49
|
+
overlay.style.left = (right?.width && right.left > left ? right.left : (left + innerWidth) / 2) + 'px';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function inDropArea(x, y) {
|
|
53
|
+
const { left, right, top, bottom } = overlay.getBoundingClientRect();
|
|
54
|
+
return x >= left && x < right && y >= top && y < bottom;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function move(event) {
|
|
58
|
+
if (gesture?.pointerId !== event.pointerId) return;
|
|
59
|
+
if (!(event.buttons & 1)) {
|
|
60
|
+
reset();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!gesture.file) {
|
|
64
|
+
if (Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) < dragThreshold) return;
|
|
65
|
+
const current = gesture;
|
|
66
|
+
const file = readFile(current.button);
|
|
67
|
+
if (gesture !== current || !file) {
|
|
68
|
+
reset();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
gesture.file = file;
|
|
72
|
+
suppressClick = true;
|
|
73
|
+
// Keep tracking even when the chat rerenders the file card during the drag.
|
|
74
|
+
document.body.setPointerCapture(event.pointerId);
|
|
75
|
+
layout();
|
|
76
|
+
area.textContent = file.review ? '拖到此处在DSH差异审阅中打开' : '拖到此处在DSH文档预览中打开';
|
|
77
|
+
document.body.append(overlay);
|
|
78
|
+
iconRoot.render(react.createElement(primitives.FileTypeIcon, { path: file.file, size: 28 }));
|
|
79
|
+
}
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
icon.style.setProperty('--dwe-drag-x', event.clientX + 12 + 'px');
|
|
82
|
+
icon.style.setProperty('--dwe-drag-y', event.clientY + 12 + 'px');
|
|
83
|
+
overlay.toggleAttribute('data-dwe-drop-active', inDropArea(event.clientX, event.clientY));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function release(event) {
|
|
87
|
+
if (gesture?.pointerId !== event.pointerId || event.button !== 0) return;
|
|
88
|
+
const { file } = gesture;
|
|
89
|
+
const shouldOpen = file && inDropArea(event.clientX, event.clientY);
|
|
90
|
+
reset();
|
|
91
|
+
if (!shouldOpen) return;
|
|
92
|
+
const hit = document.elementFromPoint(event.clientX, event.clientY);
|
|
93
|
+
const paneId = hit?.closest('[data-sidebar-right-panel] [data-dockkit-pane]')?.dataset.dockkitPane;
|
|
94
|
+
openPreview(file, paneId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cancelPointer(event) {
|
|
98
|
+
if (gesture?.pointerId === event.pointerId) reset();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function cancelEscape(event) {
|
|
102
|
+
if (event.key !== 'Escape' || !gesture?.file) return;
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
event.stopPropagation();
|
|
105
|
+
reset();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function preventDragClick(event) {
|
|
109
|
+
if (!suppressClick || !event.isTrusted || event.detail === 0) return;
|
|
110
|
+
suppressClick = false;
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
event.stopImmediatePropagation();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const options = { capture: true, signal: lifetime.signal };
|
|
116
|
+
const stop = mounted.subscribe(reset);
|
|
117
|
+
document.addEventListener('pointerdown', arm, options);
|
|
118
|
+
document.addEventListener('pointermove', move, options);
|
|
119
|
+
document.addEventListener('pointerup', release, options);
|
|
120
|
+
document.addEventListener('pointercancel', cancelPointer, options);
|
|
121
|
+
document.addEventListener('lostpointercapture', cancelPointer, options);
|
|
122
|
+
document.addEventListener('dragstart', event => {
|
|
123
|
+
if (gesture?.button.contains(event.target)) event.preventDefault();
|
|
124
|
+
}, options);
|
|
125
|
+
document.addEventListener('keydown', cancelEscape, options);
|
|
126
|
+
document.addEventListener('click', preventDragClick, options);
|
|
127
|
+
window.addEventListener('resize', layout, options);
|
|
128
|
+
window.addEventListener('blur', reset, { signal: lifetime.signal });
|
|
129
|
+
return () => {
|
|
130
|
+
reset();
|
|
131
|
+
iconRoot.unmount();
|
|
132
|
+
stop();
|
|
133
|
+
lifetime.abort();
|
|
134
|
+
};
|
|
135
|
+
}
|