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/src/ui/styles.js CHANGED
@@ -1,4 +1,36 @@
1
1
  export const explorerCss = `
2
+ [data-dwe-sidebar-handoff] {
3
+ grid-template-columns: var(--dwe-handoff-columns) !important;
4
+ transition: none !important;
5
+ }
6
+ [data-dwe-sidebar-track-instant],
7
+ [data-sidebar-right-panel] :is([data-dockkit-host='dock'], [data-dockkit-empty], [data-dockkit-divider]):not([data-dwe-sidebar-toggle-motion]),
8
+ [data-dwe-sidebar-handoff] [data-sidebar-right-panel] :is([data-dockkit-host='dock'], [data-dockkit-empty], [data-dockkit-divider]) {
9
+ transition: none !important;
10
+ }
11
+ .dwe-conversation-drop {
12
+ position: fixed; inset: 0; z-index: 2147483647;
13
+ display: grid; box-sizing: border-box; padding: 0 6px 6px 3px;
14
+ --dwe-drop-blue: #4176E6;
15
+ pointer-events: none; user-select: none;
16
+ }
17
+ .dwe-conversation-drop-area {
18
+ position: relative; display: grid; place-items: center; padding: 24px;
19
+ border-radius: 10px; overflow: hidden;
20
+ background: color-mix(in srgb, var(--dsw-alias-bg-base, Canvas) 38%, transparent);
21
+ backdrop-filter: blur(7.5px); color: var(--dwe-drop-blue);
22
+ text-align: center; font-size: 24px;
23
+ }
24
+ .dwe-conversation-drop-area::before {
25
+ content: ''; position: absolute; inset: 0; border: 4px solid var(--dwe-drop-blue); border-radius: inherit;
26
+ }
27
+ .dwe-conversation-drop[data-dwe-drop-active] .dwe-conversation-drop-area::before { border-width: 8px; }
28
+ .dwe-conversation-drag-icon {
29
+ position: fixed; top: 0; left: 0; width: 40px; height: 40px;
30
+ display: grid; place-items: center; border-radius: 8px;
31
+ background: var(--dsw-alias-bg-base, Canvas); box-shadow: 0 2px 8px #0004;
32
+ transform: translate3d(clamp(0px, var(--dwe-drag-x), 100vw - 44px), clamp(0px, var(--dwe-drag-y), 100vh - 44px), 0);
33
+ }
2
34
  .dwe-host { height: 100%; min-height: 0; min-width: 0; }
3
35
  [data-dockkit-tab]:has([data-dwe-tab-title]) {
4
36
  flex-shrink: 0; min-inline-size: calc(5em + 59px); max-inline-size: max(170px, calc(5em + 59px));
@@ -12,6 +44,7 @@ export const explorerCss = `
12
44
  [data-dwe-tab-title] > span { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
13
45
  .dwe {
14
46
  --dwe-line: var(--dsw-alias-border-l3, color-mix(in srgb, currentColor 14%, transparent));
47
+ --dwe-rule-inset: 8px;
15
48
  --dwe-tint: color-mix(in srgb, currentColor 5%, transparent);
16
49
  --dwe-hover: color-mix(in srgb, currentColor 8%, transparent);
17
50
  --dwe-blue: #458bf5;
@@ -48,11 +81,17 @@ export const explorerCss = `
48
81
  .dwe-divider:focus-visible { outline: 2px solid var(--dwe-blue); outline-offset: -2px; }
49
82
  .dwe-toolbar, .dwe-preview-bar {
50
83
  display: flex; flex: none; align-items: center; gap: 6px;
51
- padding: 3px 8px; min-width: 0; border-bottom: 1px solid var(--dwe-line);
84
+ padding: 3px 8px; min-width: 0; border-bottom: 1px solid transparent;
85
+ }
86
+ .dwe-toolbar, .dwe-head, .dwe-status-bar, .dwe-preview-bar {
87
+ background-image: linear-gradient(#F5F5F5, #F5F5F5);
88
+ background-repeat: no-repeat; background-origin: border-box;
89
+ background-size: calc(100% - 2 * var(--dwe-rule-inset)) 1px; background-position: center bottom;
52
90
  }
53
91
  .dwe-address-bar { display: flex; min-width: 0; flex: none; align-items: center; gap: 3px; padding: 5px 8px 0; }
54
92
  .dwe-toolbar { justify-content: space-between; gap: 4px; overflow-x: auto; scrollbar-width: none; }
55
93
  .dwe-nav { display: flex; align-items: center; gap: 2px; flex: none; }
94
+ .dwe-v-divider { flex: 0 0 1px; height: 15px; margin-inline: 3px; background: var(--dwe-line); }
56
95
  .dwe button {
57
96
  font: inherit; color: inherit; border: 0; background: transparent; cursor: default;
58
97
  }
@@ -66,8 +105,9 @@ export const explorerCss = `
66
105
  background: color-mix(in srgb, var(--dwe-blue) 20%, transparent);
67
106
  }
68
107
  .dwe-tool[data-dwe-tool="star"][aria-pressed="true"] svg { color: #a36a00; fill: #ffca58; }
69
- .dwe-tool[data-dwe-tool="favorites"][aria-expanded="true"] {
70
- background: color-mix(in srgb, var(--dwe-blue) 20%, transparent);
108
+ .dwe-tool:is(.dwe-scope-tool, .dwe-favorites-tool, [data-dwe-tool="preview"], [data-dwe-tool="list"], [data-dwe-tool="grid"]) {
109
+ &:is([aria-pressed="true"], [aria-expanded="true"]) { background: var(--dwe-hover); }
110
+ &:hover:not(:disabled) { background: color-mix(in srgb, var(--dwe-blue) 20%, transparent); }
71
111
  }
72
112
  .dwe-favorites {
73
113
  position: absolute; z-index: 4; display: flex; flex-direction: column; box-sizing: border-box;
@@ -80,7 +120,12 @@ export const explorerCss = `
80
120
  padding-inline-start: 6px; min-height: 25px;
81
121
  }
82
122
  .dwe-favorites ul { list-style: none; margin: 0; padding: 0; overflow: auto; }
83
- .dwe-favorites li { display: flex; align-items: center; gap: 4px; }
123
+ .dwe-favorites li { position: relative; display: flex; align-items: center; gap: 4px; }
124
+ .dwe-favorites li[data-dwe-favorite-drop]::after {
125
+ content: ''; position: absolute; inset-inline: 4px; height: 2px; background: var(--dwe-blue); pointer-events: none;
126
+ }
127
+ .dwe-favorites li[data-dwe-favorite-drop='before']::after { top: 0; }
128
+ .dwe-favorites li[data-dwe-favorite-drop='after']::after { bottom: 0; }
84
129
  .dwe-favorite-location {
85
130
  display: flex; flex: 1; min-width: 0; align-items: center; gap: 7px; text-align: start;
86
131
  min-height: 28px; padding: 2px 4px; border-radius: 5px;
@@ -132,7 +177,7 @@ export const explorerCss = `
132
177
  .dwe-columns > span { padding-inline: 8px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
133
178
  .dwe-head {
134
179
  position: sticky; top: 0; z-index: 1; padding: 5px 6px; font-size: 12px;
135
- background: var(--dsw-alias-bg-base, Canvas); border-bottom: 1px solid var(--dwe-line);
180
+ background-color: var(--dsw-alias-bg-base, Canvas); border-bottom: 1px solid transparent;
136
181
  }
137
182
  .dwe-head span + span { border-inline-start: 1px solid var(--dwe-line); }
138
183
  .dwe-columns > span:last-child { text-align: end; }
@@ -146,7 +191,7 @@ export const explorerCss = `
146
191
  .dwe-tile .dwe-name { flex-direction: column; gap: 5px; }
147
192
  .dwe-thumbnail {
148
193
  position: relative; display: grid; place-items: center; inline-size: 100%; max-inline-size: 110px;
149
- block-size: 60px; pointer-events: none;
194
+ grid-template: minmax(0, 1fr) / minmax(0, 1fr); block-size: 60px; pointer-events: none;
150
195
  }
151
196
  .dwe-thumbnail img {
152
197
  display: block; max-inline-size: 100%; max-block-size: 100%; object-fit: contain; border-radius: 3px;
@@ -171,13 +216,14 @@ export const explorerCss = `
171
216
  .dwe-meta, .dwe-status { opacity: .72; font-size: 12px; }
172
217
  .dwe-status { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
173
218
  .dwe-status-bar {
174
- flex: none; display: flex; align-items: center; justify-content: space-between; gap: 4px;
175
- padding: 2px 6px 2px 12px; border-top: 1px solid var(--dwe-line);
219
+ flex: none; display: flex; align-items: center; gap: 4px;
220
+ padding: 2px 8px; border-top: 1px solid transparent; background-position: center top;
176
221
  }
222
+ .dwe-status-bar > .dwe-tool { margin-inline-start: auto; }
177
223
  .dwe-preview {
178
224
  min-height: 0; display: flex; flex-direction: column;
179
225
  }
180
- .dwe-preview-bar { background: var(--dsw-alias-bg-base, Canvas); }
226
+ .dwe-preview-bar { background-color: var(--dsw-alias-bg-base, Canvas); }
181
227
  .dwe-preview-label { flex: none; margin-inline: 4px; line-height: 28px; }
182
228
  .dwe-preview-name {
183
229
  min-width: 0; height: 28px; box-sizing: border-box; padding-inline: 7px 3px;
@@ -188,13 +234,17 @@ export const explorerCss = `
188
234
  .dwe-preview-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
189
235
  .dwe-preview-cover {
190
236
  position: relative; display: grid; place-items: center; isolation: isolate;
191
- height: 100%; min-height: 100px; overflow: hidden; background: var(--dwe-tint);
237
+ height: 100%; min-height: 112px; box-sizing: border-box; padding: 6px 12px;
238
+ overflow: hidden; background: var(--dwe-tint);
192
239
  }
240
+ .dwe-preview-cover[data-video] { min-height: 128px; }
193
241
  .dwe-preview-cover[data-poster] { background: #15171a; }
194
242
  .dwe-preview-cover > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: -1; }
243
+ .dwe-cover-content { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 8px; width: 100%; }
195
244
  .dwe-cover-action { display: flex; align-items: center; gap: 10px; }
196
245
  .dwe .dwe-cover-play {
197
- display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%;
246
+ display: grid; place-items: center; width: 52px; height: 52px; padding: 0; box-sizing: border-box;
247
+ border-radius: 50%; corner-shape: round;
198
248
  background: #243953; color: #fff; box-shadow: 0 2px 8px #0003; cursor: pointer;
199
249
  }
200
250
  .dwe .dwe-cover-play:hover:not(:disabled) { background: #34577f; }
@@ -202,11 +252,17 @@ export const explorerCss = `
202
252
  padding: 7px 12px; border: 1px solid var(--dwe-line); border-radius: 6px;
203
253
  background: var(--dsw-alias-bg-base, Canvas); white-space: nowrap; cursor: pointer;
204
254
  }
205
- .dwe-cover-file {
206
- position: absolute; inset-block-end: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
207
- max-width: calc(100% - 24px); padding: 3px 8px; border-radius: 5px;
255
+ .dwe-cover-details {
256
+ display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 3px;
257
+ max-width: 100%; min-width: 0; box-sizing: border-box; padding: 2px 8px; border-radius: 5px;
258
+ }
259
+ .dwe-cover-file { max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
260
+ .dwe-cover-size { font-size: 12px; opacity: .72; }
261
+ .dwe-preview-cover[data-video] .dwe-cover-details {
262
+ position: absolute; inset: 0 0 auto; display: flex; align-items: center; gap: 6px;
263
+ padding: 8px; border-radius: 0; background: #0009; color: #fff;
208
264
  }
209
- .dwe-preview-cover[data-poster] .dwe-cover-file { background: #15171abb; color: #fff; }
265
+ .dwe-preview-cover[data-video] .dwe-cover-size { flex: none; white-space: nowrap; }
210
266
  .dwe-pdf-scroll {
211
267
  height: 100%; overflow: auto; scrollbar-gutter: stable both-edges; background: var(--dwe-tint);
212
268
  }
@@ -274,6 +330,9 @@ export const explorerCss = `
274
330
  .dwe-dialog-actions button[type="submit"] { background: color-mix(in srgb, var(--dwe-blue) 23%, transparent); }
275
331
  .dwe-dialog-actions button[data-dwe-action="delete"] { background: #ad303d; color: #fff; }
276
332
  .dwe-dialog-actions button[data-dwe-action="delete"]:hover:not(:disabled) { background: #942634; }
333
+ @container (max-width: 440px) {
334
+ .dwe-toolbar .dwe-tool:not(.dwe-scope-tool) { width: 26px; height: 26px; flex-basis: 26px; }
335
+ }
277
336
  @container (max-width: 380px) {
278
337
  .dwe-columns { grid-template-columns: minmax(0, 1fr) 62px; }
279
338
  .dwe-columns > span:nth-child(2) { display: none; }
@@ -1,106 +1,106 @@
1
- import { thumbnailType } from '../thumbnail-types.js';
2
- import { disks } from './workspace.js';
3
-
4
- const { createElement: h, useEffect, useRef, useState } = react;
5
- const pending = new Set();
6
- let running = 0;
7
-
8
- function startNext() {
9
- while (running < 3 && pending.size) {
10
- const next = pending.values().next().value;
11
- pending.delete(next);
12
- running++;
13
- void next().finally(() => { running--; startNext(); });
14
- }
15
- }
16
-
17
- function waitForFrame(media, event, signal) {
18
- return new Promise((resolve, reject) => {
19
- const handlers = new AbortController();
20
- function settle(error) {
21
- handlers.abort();
22
- signal.removeEventListener('abort', cancel);
23
- if (error) reject(error);
24
- else resolve();
25
- }
26
- function cancel() { settle(signal.reason); }
27
- if (signal.aborted) { cancel(); return; }
28
- const options = { once: true, signal: handlers.signal };
29
- signal.addEventListener('abort', cancel, { once: true });
30
- media.addEventListener(event, () => settle(), options);
31
- media.addEventListener('error', () => settle(new Error('无法解码缩略图')), options);
32
- });
33
- }
34
-
35
- export async function makeThumbnail(path, type, signal, root, poster = false) {
36
- const { url } = await disks.thumbnail(path, signal, root);
37
- signal.throwIfAborted();
38
- const video = type.startsWith('video/');
39
- const media = document.createElement(video ? 'video' : 'img');
40
- if (video) { media.preload = 'auto'; media.muted = true; }
41
- try {
42
- const firstFrame = waitForFrame(media, video ? 'loadeddata' : 'load', signal);
43
- media.src = url;
44
- await firstFrame;
45
- if (video && !poster && Number.isFinite(media.duration) && media.duration > 0.2) {
46
- const seeked = waitForFrame(media, 'seeked', signal);
47
- media.currentTime = Math.min(0.7, media.duration * 0.12);
48
- await seeked;
49
- }
50
- const width = video ? media.videoWidth : media.naturalWidth;
51
- const height = video ? media.videoHeight : media.naturalHeight;
52
- if (!width || !height) throw new Error('缩略图为空');
53
- const [maxWidth, maxHeight] = poster ? [640, 360] : [192, 136];
54
- const scale = Math.min(maxWidth / width, maxHeight / height, poster ? 1 : Infinity);
55
- const canvas = document.createElement('canvas');
56
- canvas.width = Math.max(1, Math.round(width * scale));
57
- canvas.height = Math.max(1, Math.round(height * scale));
58
- canvas.getContext('2d').drawImage(media, 0, 0, canvas.width, canvas.height);
59
- return canvas.toDataURL('image/webp', 0.85);
60
- } finally {
61
- media.removeAttribute('src');
62
- if (video) media.load();
63
- }
64
- }
65
-
66
- export function Thumbnail({ path, active, fallback, root }) {
67
- const slot = useRef(null);
68
- const [result, setResult] = useState(null);
69
- const type = thumbnailType(path);
70
- const image = result?.path === path && result?.root === root ? result.image : null;
71
- useEffect(() => {
72
- const element = slot.current;
73
- if (!element || !active || !type || image) return;
74
- let job;
75
- let failed = false;
76
- function cancel() {
77
- if (!job) return;
78
- pending.delete(job.run);
79
- job.controller.abort();
80
- job = undefined;
81
- }
82
- const observer = new IntersectionObserver(([entry]) => {
83
- if (!entry.isIntersecting) { cancel(); return; }
84
- if (job || failed) return;
85
- const controller = new AbortController();
86
- const run = async () => {
87
- try {
88
- const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(12000)]);
89
- const image = await makeThumbnail(path, type, signal, root);
90
- if (!signal.aborted) setResult({ path, root, image });
91
- } catch {
92
- if (!controller.signal.aborted) failed = true;
93
- }
94
- };
95
- job = { controller, run };
96
- pending.add(run);
97
- startNext();
98
- }, { root: element.closest('.dwe-list'), rootMargin: '72px 0px' });
99
- observer.observe(element);
100
- return () => { observer.disconnect(); cancel(); };
101
- }, [active, path, type, image, root]);
102
-
103
- return h('span', { ref: slot, className: 'dwe-thumbnail', 'aria-hidden': true },
104
- image ? h('img', { src: image, alt: '', draggable: false }) : fallback,
105
- image && type?.startsWith('video/') && h('span', { className: 'dwe-video-mark' }, '▶'));
1
+ import { thumbnailType } from '../thumbnail-types.js';
2
+ import { disks } from './workspace.js';
3
+
4
+ const { createElement: h, useEffect, useRef, useState } = react;
5
+ const pending = new Set();
6
+ let running = 0;
7
+
8
+ function startNext() {
9
+ while (running < 3 && pending.size) {
10
+ const next = pending.values().next().value;
11
+ pending.delete(next);
12
+ running++;
13
+ void next().finally(() => { running--; startNext(); });
14
+ }
15
+ }
16
+
17
+ function waitForFrame(media, event, signal) {
18
+ return new Promise((resolve, reject) => {
19
+ const handlers = new AbortController();
20
+ function settle(error) {
21
+ handlers.abort();
22
+ signal.removeEventListener('abort', cancel);
23
+ if (error) reject(error);
24
+ else resolve();
25
+ }
26
+ function cancel() { settle(signal.reason); }
27
+ if (signal.aborted) { cancel(); return; }
28
+ const options = { once: true, signal: handlers.signal };
29
+ signal.addEventListener('abort', cancel, { once: true });
30
+ media.addEventListener(event, () => settle(), options);
31
+ media.addEventListener('error', () => settle(new Error('无法解码缩略图')), options);
32
+ });
33
+ }
34
+
35
+ export async function makeThumbnail(path, type, signal, root, poster = false) {
36
+ const { url } = await disks.thumbnail(path, signal, root);
37
+ signal.throwIfAborted();
38
+ const video = type.startsWith('video/');
39
+ const media = document.createElement(video ? 'video' : 'img');
40
+ if (video) { media.preload = 'auto'; media.muted = true; }
41
+ try {
42
+ const firstFrame = waitForFrame(media, video ? 'loadeddata' : 'load', signal);
43
+ media.src = url;
44
+ await firstFrame;
45
+ if (video && !poster && Number.isFinite(media.duration) && media.duration > 0.2) {
46
+ const seeked = waitForFrame(media, 'seeked', signal);
47
+ media.currentTime = Math.min(0.7, media.duration * 0.12);
48
+ await seeked;
49
+ }
50
+ const width = video ? media.videoWidth : media.naturalWidth;
51
+ const height = video ? media.videoHeight : media.naturalHeight;
52
+ if (!width || !height) throw new Error('缩略图为空');
53
+ const [maxWidth, maxHeight] = poster ? [640, 360] : [192, 136];
54
+ const scale = Math.min(maxWidth / width, maxHeight / height, poster ? 1 : Infinity);
55
+ const canvas = document.createElement('canvas');
56
+ canvas.width = Math.max(1, Math.round(width * scale));
57
+ canvas.height = Math.max(1, Math.round(height * scale));
58
+ canvas.getContext('2d').drawImage(media, 0, 0, canvas.width, canvas.height);
59
+ return canvas.toDataURL('image/webp', 0.85);
60
+ } finally {
61
+ media.removeAttribute('src');
62
+ if (video) media.load();
63
+ }
64
+ }
65
+
66
+ export function Thumbnail({ path, active, fallback, root }) {
67
+ const slot = useRef(null);
68
+ const [result, setResult] = useState(null);
69
+ const type = thumbnailType(path);
70
+ const image = result?.path === path && result?.root === root ? result.image : null;
71
+ useEffect(() => {
72
+ const element = slot.current;
73
+ if (!element || !active || !type || image) return;
74
+ let job;
75
+ let failed = false;
76
+ function cancel() {
77
+ if (!job) return;
78
+ pending.delete(job.run);
79
+ job.controller.abort();
80
+ job = undefined;
81
+ }
82
+ const observer = new IntersectionObserver(([entry]) => {
83
+ if (!entry.isIntersecting) { cancel(); return; }
84
+ if (job || failed) return;
85
+ const controller = new AbortController();
86
+ const run = async () => {
87
+ try {
88
+ const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(12000)]);
89
+ const image = await makeThumbnail(path, type, signal, root);
90
+ if (!signal.aborted) setResult({ path, root, image });
91
+ } catch {
92
+ if (!controller.signal.aborted) failed = true;
93
+ }
94
+ };
95
+ job = { controller, run };
96
+ pending.add(run);
97
+ startNext();
98
+ }, { root: element.closest('.dwe-list'), rootMargin: '72px 0px' });
99
+ observer.observe(element);
100
+ return () => { observer.disconnect(); cancel(); };
101
+ }, [active, path, type, image, root]);
102
+
103
+ return h('span', { ref: slot, className: 'dwe-thumbnail', 'aria-hidden': true },
104
+ image ? h('img', { src: image, alt: '', draggable: false }) : fallback,
105
+ image && type?.startsWith('video/') && h('span', { className: 'dwe-video-mark' }, '▶'));
106
106
  }
@@ -909,4 +909,4 @@ function paintKicad(canvas, doc, view) {
909
909
  if ((primitive.size || 1) * fit < 7) continue;
910
910
  ctx.fillText(String(primitive.text), mapX(primitive.x) + 2, mapY(primitive.y) - 2);
911
911
  }
912
- }
912
+ }