dsh-music-panel 0.3.8 → 0.3.9

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/client.js +103 -84
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.9
4
+
5
+ ### 修复
6
+
7
+ - 播放队列拖拽改为 Pointer Events 实现(参考 DSH 侧栏行拖拽的落点语义 + SortableJS 的 pointer 方案):此前 HTML5 dragstart 在 composer 宿主浮层内不触发,导致松手后顺序不变
8
+ - 按住行左侧 `⋮⋮` 手柄拖动即可;拖拽中目标行显示插入线,松手立即重排;拖拽后不会误触发行的点击跳转
9
+
3
10
  ## 0.3.8
4
11
 
5
12
  ### 修复
package/client.js CHANGED
@@ -2124,12 +2124,13 @@ window.__ModuleLoader__.load({
2124
2124
  function QueueOverlay() {
2125
2125
  var [open, setOpen] = useState(queueOverlaySnapshot())
2126
2126
  var [snap, setSnap] = useState(player.getSnapshot())
2127
- var [dragOver, setDragOver] = useState(null)
2128
- // Keep the source index in a ref: calling setState from dragstart makes
2129
- // React re-render the dragged node synchronously, which cancels the
2130
- // browser's drag in Chromium.
2127
+ // Drag state lives entirely in refs/DOM classes so React never
2128
+ // re-renders the dragged row mid-drag (Chromium cancels that).
2131
2129
  var dragFromRef = useRef(null)
2130
+ var dragOverRef = useRef(null)
2131
+ var suppressClickRef = useRef(0)
2132
2132
  var ref = useRef(null)
2133
+ var listRef = useRef(null)
2133
2134
 
2134
2135
  useEffect(function () {
2135
2136
  return subscribeQueueOverlay(function () {
@@ -2160,6 +2161,90 @@ window.__ModuleLoader__.load({
2160
2161
 
2161
2162
  if (!open) return null
2162
2163
  var queueView = buildQueueView(snap, player.getPlayOrder())
2164
+
2165
+ function clearDragMarks() {
2166
+ var list = listRef.current
2167
+ if (list === null) return
2168
+ var rowEls = list.querySelectorAll('.mu-queue-row')
2169
+ for (var i = 0; i < rowEls.length; i += 1) {
2170
+ rowEls[i].classList.remove('is-dragging', 'is-drop-target')
2171
+ }
2172
+ }
2173
+
2174
+ function moveRow(from, to) {
2175
+ if (from === null || from === to || from < 0 || to < 0) return
2176
+ if (from >= queueView.length || to >= queueView.length) return
2177
+ var order = queueView.map(function (item) {
2178
+ return item.originalIndex
2179
+ })
2180
+ var moved = order.splice(from, 1)[0]
2181
+ order.splice(to, 0, moved)
2182
+ player.reorderQueue(order)
2183
+ }
2184
+
2185
+ // Pointer-driven reorder (SortableJS' approach). The harness hosts this
2186
+ // overlay inside the composer area, where HTML5 dragstart is unreliable;
2187
+ // DSH's sidebar rows use HTML5 DnD with a custom drag preview, so this
2188
+ // keeps the same before/after marker semantics but with pointer events.
2189
+ var dragMoved = false
2190
+ function startDrag(event, position) {
2191
+ if (event.button !== undefined && event.button !== 0) return
2192
+ event.preventDefault()
2193
+ event.stopPropagation()
2194
+ var list = listRef.current
2195
+ if (list === null) return
2196
+ dragFromRef.current = position
2197
+ dragOverRef.current = position
2198
+ dragMoved = false
2199
+ var sourceRow = event.currentTarget.closest('.mu-queue-row')
2200
+ if (sourceRow !== null) sourceRow.classList.add('is-dragging')
2201
+ document.body.style.userSelect = 'none'
2202
+
2203
+ function mark(target) {
2204
+ dragOverRef.current = target
2205
+ var rowEls = list.querySelectorAll('.mu-queue-row')
2206
+ for (var i = 0; i < rowEls.length; i += 1) {
2207
+ rowEls[i].classList.toggle('is-drop-target', i === target)
2208
+ }
2209
+ }
2210
+
2211
+ function onMove(moveEvent) {
2212
+ dragMoved = true
2213
+ var rowEls = list.querySelectorAll('.mu-queue-row')
2214
+ if (rowEls.length === 0) return
2215
+ var target = rowEls.length - 1
2216
+ for (var i = 0; i < rowEls.length; i += 1) {
2217
+ var rect = rowEls[i].getBoundingClientRect()
2218
+ if (moveEvent.clientY < rect.top + rect.height / 2) {
2219
+ target = i
2220
+ break
2221
+ }
2222
+ target = i
2223
+ }
2224
+ mark(target)
2225
+ }
2226
+
2227
+ function onUp() {
2228
+ document.removeEventListener('pointermove', onMove)
2229
+ document.removeEventListener('pointerup', onUp)
2230
+ document.removeEventListener('pointercancel', onUp)
2231
+ document.body.style.userSelect = ''
2232
+ var from = dragFromRef.current
2233
+ var to = dragOverRef.current
2234
+ dragFromRef.current = null
2235
+ dragOverRef.current = null
2236
+ clearDragMarks()
2237
+ if (dragMoved && from !== null && to !== null) {
2238
+ suppressClickRef.current = Date.now() + 250
2239
+ moveRow(from, to)
2240
+ }
2241
+ }
2242
+
2243
+ document.addEventListener('pointermove', onMove)
2244
+ document.addEventListener('pointerup', onUp)
2245
+ document.addEventListener('pointercancel', onUp)
2246
+ }
2247
+
2163
2248
  var rows = queueView.map(function (entry, position) {
2164
2249
  var track = entry.track
2165
2250
  var originalIndex = entry.originalIndex
@@ -2168,57 +2253,23 @@ window.__ModuleLoader__.load({
2168
2253
  'div',
2169
2254
  {
2170
2255
  key: position + ':' + track.path,
2171
- className:
2172
- 'mu-queue-row' +
2173
- (visibleCurrent ? ' is-current' : '') +
2174
- (dragOver === position ? ' is-drop-target' : ''),
2175
- draggable: true,
2256
+ className: 'mu-queue-row' + (visibleCurrent ? ' is-current' : ''),
2176
2257
  onClick: function () {
2258
+ if (Date.now() < suppressClickRef.current) return
2177
2259
  player.select(snap.queue, originalIndex)
2178
2260
  },
2179
- onDragStart: function (event) {
2180
- dragFromRef.current = position
2181
- setDragOver(position)
2182
- if (event.dataTransfer !== null && event.dataTransfer !== undefined) {
2183
- event.dataTransfer.effectAllowed = 'move'
2184
- try {
2185
- event.dataTransfer.setData('text/plain', String(position))
2186
- } catch (error) {
2187
- // ignore
2188
- }
2189
- }
2190
- },
2191
- onDragOver: function (event) {
2192
- event.preventDefault()
2193
- if (event.dataTransfer !== null && event.dataTransfer !== undefined) {
2194
- event.dataTransfer.dropEffect = 'move'
2195
- }
2196
- if (dragOver !== position) setDragOver(position)
2197
- },
2198
- onDragLeave: function () {
2199
- if (dragOver === position) setDragOver(null)
2200
- },
2201
- onDrop: function (event) {
2202
- event.preventDefault()
2203
- event.stopPropagation()
2204
- var from = dragFromRef.current
2205
- if (from !== null && from !== position) {
2206
- var order = queueView.map(function (item) {
2207
- return item.originalIndex
2208
- })
2209
- var moved = order.splice(from, 1)[0]
2210
- order.splice(position, 0, moved)
2211
- player.reorderQueue(order)
2212
- }
2213
- dragFromRef.current = null
2214
- setDragOver(null)
2215
- },
2216
- onDragEnd: function () {
2217
- dragFromRef.current = null
2218
- setDragOver(null)
2219
- },
2220
2261
  },
2221
- h('span', { className: 'mu-queue-grip', title: t('page.dragSort') }, '⋮⋮'),
2262
+ h(
2263
+ 'span',
2264
+ {
2265
+ className: 'mu-queue-grip',
2266
+ title: t('page.dragSort'),
2267
+ onPointerDown: function (event) {
2268
+ startDrag(event, position)
2269
+ },
2270
+ },
2271
+ '⋮⋮',
2272
+ ),
2222
2273
  h(
2223
2274
  'span',
2224
2275
  { className: 'mu-queue-index' },
@@ -2265,39 +2316,7 @@ window.__ModuleLoader__.load({
2265
2316
  'div',
2266
2317
  {
2267
2318
  className: 'mu-qoverlay-list',
2268
- onDragOver: function (event) {
2269
- event.preventDefault()
2270
- if (event.dataTransfer !== null && event.dataTransfer !== undefined) {
2271
- event.dataTransfer.dropEffect = 'move'
2272
- }
2273
- },
2274
- // Dropping in the gap between rows (no row handles it) still
2275
- // reorders: pick the row under the pointer.
2276
- onDrop: function (event) {
2277
- event.preventDefault()
2278
- var from = dragFromRef.current
2279
- if (from === null) return
2280
- var rowEls = event.currentTarget.querySelectorAll('.mu-queue-row')
2281
- var target = queueView.length - 1
2282
- for (var i = 0; i < rowEls.length; i += 1) {
2283
- var rect = rowEls[i].getBoundingClientRect()
2284
- if (event.clientY < rect.top + rect.height / 2) {
2285
- target = i
2286
- break
2287
- }
2288
- target = i
2289
- }
2290
- if (target !== from) {
2291
- var order = queueView.map(function (item) {
2292
- return item.originalIndex
2293
- })
2294
- var moved = order.splice(from, 1)[0]
2295
- order.splice(target, 0, moved)
2296
- player.reorderQueue(order)
2297
- }
2298
- dragFromRef.current = null
2299
- setDragOver(null)
2300
- },
2319
+ ref: listRef,
2301
2320
  },
2302
2321
  snap.queue.length === 0 ? h('div', { className: 'mu-state-sub' }, t('page.queueEmpty')) : rows,
2303
2322
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-music-panel",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "type": "module",
5
5
  "description": "Local music library for DeepSeek Harness: scan a folder, browse tracks with cover art and tags, and play through a sidebar-panel player with loop, shuffle and volume. · DSH 本地音乐插件:扫描音乐目录、读取封面与标签、侧栏入口 + 全屏播放器。",
6
6
  "license": "MIT",