dsh-music-panel 0.3.0 → 0.3.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/client.js +95 -50
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
### 变更
|
|
6
|
+
|
|
7
|
+
- 播放顺序合并为三种互斥模式:顺序播放(播完停止)、随机播放(随机循环)、单曲循环;底栏、播放页、主面板 chips 三处按钮与图标同步
|
|
8
|
+
- 播放队列标题显示当前播放模式
|
|
9
|
+
- 旧状态归一化:升级后 `loop=all + order=list` 视为顺序播放
|
|
10
|
+
|
|
11
|
+
## 0.3.1
|
|
12
|
+
|
|
13
|
+
### 修复
|
|
14
|
+
|
|
15
|
+
- 修复 0.3.0 注入样式表中 `.mu-search-history` 规则缺少结尾 `}`,导致该规则之后的大量插件样式被浏览器丢弃、界面错乱的问题。
|
|
16
|
+
- 新增样式语法测试(`test/style.test.mjs`):CI 会拦截括号/引号不平衡的 CSS,避免同类问题再次进包。
|
|
17
|
+
|
|
3
18
|
## 0.3.0
|
|
4
19
|
|
|
5
20
|
### 功能
|
package/client.js
CHANGED
|
@@ -80,6 +80,9 @@ window.__ModuleLoader__.load({
|
|
|
80
80
|
'page.done': '完成',
|
|
81
81
|
'page.clear': '清空列表',
|
|
82
82
|
'page.loopAll': '列表循环',
|
|
83
|
+
'page.modeSequence': '顺序播放',
|
|
84
|
+
'page.modeRandom': '随机播放',
|
|
85
|
+
'page.modeOne': '单曲循环',
|
|
83
86
|
'page.loopOne': '单曲循环',
|
|
84
87
|
'page.loopNone': '不循环',
|
|
85
88
|
'page.shuffle': '随机播放',
|
|
@@ -251,6 +254,9 @@ window.__ModuleLoader__.load({
|
|
|
251
254
|
'page.done': 'Done',
|
|
252
255
|
'page.clear': 'Clear list',
|
|
253
256
|
'page.loopAll': 'Repeat all',
|
|
257
|
+
'page.modeSequence': 'Play in order',
|
|
258
|
+
'page.modeRandom': 'Shuffle',
|
|
259
|
+
'page.modeOne': 'Repeat one',
|
|
254
260
|
'page.loopOne': 'Repeat one',
|
|
255
261
|
'page.loopNone': 'No repeat',
|
|
256
262
|
'page.shuffle': 'Shuffle',
|
|
@@ -996,6 +1002,50 @@ window.__ModuleLoader__.load({
|
|
|
996
1002
|
return order
|
|
997
1003
|
}
|
|
998
1004
|
|
|
1005
|
+
// Three playback modes, mapped onto the engine's loop/order pair:
|
|
1006
|
+
// sequence → in order, stop at the end
|
|
1007
|
+
// random → shuffled, looping
|
|
1008
|
+
// one → repeat the current track
|
|
1009
|
+
var PLAY_MODES = ['sequence', 'random', 'one']
|
|
1010
|
+
|
|
1011
|
+
function playModeOf(snap) {
|
|
1012
|
+
if (snap.loop === 'one') return 'one'
|
|
1013
|
+
if (snap.order === 'random') return 'random'
|
|
1014
|
+
return 'sequence'
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function playModeLabel(mode) {
|
|
1018
|
+
return mode === 'one'
|
|
1019
|
+
? t('page.modeOne')
|
|
1020
|
+
: mode === 'random'
|
|
1021
|
+
? t('page.modeRandom')
|
|
1022
|
+
: t('page.modeSequence')
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function playModeIcon(mode) {
|
|
1026
|
+
return mode === 'one' ? 'repeatOne' : mode === 'random' ? 'shuffle' : 'list'
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function applyPlayMode(mode) {
|
|
1030
|
+
if (mode === 'one') {
|
|
1031
|
+
player.setLoop('one')
|
|
1032
|
+
player.setOrder('list')
|
|
1033
|
+
return
|
|
1034
|
+
}
|
|
1035
|
+
if (mode === 'random') {
|
|
1036
|
+
player.setLoop('all')
|
|
1037
|
+
player.setOrder('random')
|
|
1038
|
+
return
|
|
1039
|
+
}
|
|
1040
|
+
player.setLoop('none')
|
|
1041
|
+
player.setOrder('list')
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function cyclePlayMode(snap) {
|
|
1045
|
+
var current = playModeOf(snap)
|
|
1046
|
+
applyPlayMode(PLAY_MODES[(PLAY_MODES.indexOf(current) + 1) % PLAY_MODES.length])
|
|
1047
|
+
}
|
|
1048
|
+
|
|
999
1049
|
function nextIndexOf(index, order, size, randomOrder) {
|
|
1000
1050
|
if (size <= 1) return 0
|
|
1001
1051
|
if (order === 'random') {
|
|
@@ -1059,8 +1109,8 @@ window.__ModuleLoader__.load({
|
|
|
1059
1109
|
queue: [],
|
|
1060
1110
|
index: -1,
|
|
1061
1111
|
playing: false,
|
|
1062
|
-
loop: prefs.loop === 'one'
|
|
1063
|
-
order: prefs.order === 'random' ? 'random' : 'list',
|
|
1112
|
+
loop: prefs.loop === 'one' ? 'one' : 'none',
|
|
1113
|
+
order: prefs.order === 'random' && prefs.loop !== 'one' ? 'random' : 'list',
|
|
1064
1114
|
// Volume follows the system mixer: the plugin no longer exposes its own.
|
|
1065
1115
|
volume: 1,
|
|
1066
1116
|
muted: false,
|
|
@@ -1221,8 +1271,8 @@ window.__ModuleLoader__.load({
|
|
|
1221
1271
|
state = Object.assign({}, state, {
|
|
1222
1272
|
queue: queue,
|
|
1223
1273
|
index: index,
|
|
1224
|
-
loop: saved.loop === 'one'
|
|
1225
|
-
order: saved.order === 'random' ? 'random' : 'list',
|
|
1274
|
+
loop: saved.loop === 'one' ? 'one' : saved.order === 'random' ? 'all' : 'none',
|
|
1275
|
+
order: saved.order === 'random' && saved.loop !== 'one' ? 'random' : 'list',
|
|
1226
1276
|
currentTime: saved.positionSec > 0 ? saved.positionSec : 0,
|
|
1227
1277
|
duration: saved.durationSec > 0 ? saved.durationSec : 0,
|
|
1228
1278
|
playing: false,
|
|
@@ -2306,11 +2356,11 @@ window.__ModuleLoader__.load({
|
|
|
2306
2356
|
)
|
|
2307
2357
|
|
|
2308
2358
|
function cycleLoop() {
|
|
2309
|
-
|
|
2359
|
+
cyclePlayMode(snap)
|
|
2310
2360
|
}
|
|
2311
2361
|
|
|
2312
2362
|
function loopLabel() {
|
|
2313
|
-
return
|
|
2363
|
+
return playModeLabel(playModeOf(snap))
|
|
2314
2364
|
}
|
|
2315
2365
|
|
|
2316
2366
|
return h(
|
|
@@ -2623,7 +2673,7 @@ window.__ModuleLoader__.load({
|
|
|
2623
2673
|
h(
|
|
2624
2674
|
'button',
|
|
2625
2675
|
{ type: 'button', className: 'mu-icon-btn', title: loopLabel(), onClick: cycleLoop },
|
|
2626
|
-
icon(snap
|
|
2676
|
+
icon(playModeIcon(playModeOf(snap)), 18),
|
|
2627
2677
|
),
|
|
2628
2678
|
h(
|
|
2629
2679
|
'button',
|
|
@@ -3324,11 +3374,11 @@ window.__ModuleLoader__.load({
|
|
|
3324
3374
|
)
|
|
3325
3375
|
|
|
3326
3376
|
function cycleLoop() {
|
|
3327
|
-
|
|
3377
|
+
cyclePlayMode(snap)
|
|
3328
3378
|
}
|
|
3329
3379
|
|
|
3330
3380
|
function loopLabel() {
|
|
3331
|
-
return
|
|
3381
|
+
return playModeLabel(playModeOf(snap))
|
|
3332
3382
|
}
|
|
3333
3383
|
|
|
3334
3384
|
function toggleSelect(track) {
|
|
@@ -4445,7 +4495,7 @@ window.__ModuleLoader__.load({
|
|
|
4445
4495
|
h(
|
|
4446
4496
|
'button',
|
|
4447
4497
|
{ type: 'button', className: 'mu-chip', title: loopLabel(), onClick: cycleLoop },
|
|
4448
|
-
icon(snap
|
|
4498
|
+
icon(playModeIcon(playModeOf(snap)), 15),
|
|
4449
4499
|
h('span', null, loopLabel()),
|
|
4450
4500
|
),
|
|
4451
4501
|
h(
|
|
@@ -4460,18 +4510,6 @@ window.__ModuleLoader__.load({
|
|
|
4460
4510
|
icon('play', 13),
|
|
4461
4511
|
h('span', null, t('page.playAll')),
|
|
4462
4512
|
),
|
|
4463
|
-
h(
|
|
4464
|
-
'button',
|
|
4465
|
-
{
|
|
4466
|
-
type: 'button',
|
|
4467
|
-
className: 'mu-chip mu-chip-round' + (snap.order === 'random' ? ' is-on' : ''),
|
|
4468
|
-
title: t('page.shuffle'),
|
|
4469
|
-
onClick: function () {
|
|
4470
|
-
player.setOrder(snap.order === 'random' ? 'list' : 'random')
|
|
4471
|
-
},
|
|
4472
|
-
},
|
|
4473
|
-
icon('shuffle', 15),
|
|
4474
|
-
),
|
|
4475
4513
|
h(
|
|
4476
4514
|
'button',
|
|
4477
4515
|
{
|
|
@@ -4663,7 +4701,7 @@ window.__ModuleLoader__.load({
|
|
|
4663
4701
|
title: loopLabel(),
|
|
4664
4702
|
onClick: cycleLoop,
|
|
4665
4703
|
},
|
|
4666
|
-
icon(snap
|
|
4704
|
+
icon(playModeIcon(playModeOf(snap)), 17),
|
|
4667
4705
|
),
|
|
4668
4706
|
h(
|
|
4669
4707
|
'button',
|
|
@@ -4759,6 +4797,7 @@ window.__ModuleLoader__.load({
|
|
|
4759
4797
|
'div',
|
|
4760
4798
|
{ className: 'mu-pop-head' },
|
|
4761
4799
|
h('span', null, t('page.queue')),
|
|
4800
|
+
h('span', { className: 'mu-queue-mode' }, loopLabel()),
|
|
4762
4801
|
snap.queue.length > 0
|
|
4763
4802
|
? h(
|
|
4764
4803
|
'button',
|
|
@@ -5204,6 +5243,7 @@ window.__ModuleLoader__.load({
|
|
|
5204
5243
|
'.mu-pop-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px 6px;font-size:12px;font-weight:600}',
|
|
5205
5244
|
'.mu-pop-body{overflow-y:auto;padding:0 6px 8px}',
|
|
5206
5245
|
'.mu-queue-row{display:grid;grid-template-columns:26px minmax(0,1fr) minmax(0,70px) auto;align-items:center;gap:8px;padding:5px 8px;border-radius:var(--dsw-radius-sm,6px);font-size:12px;cursor:pointer}',
|
|
5246
|
+
'.mu-queue-mode{font-size:11px;color:var(--dsw-alias-label-tertiary,#8a8a93);padding:1px 6px;border-radius:999px;background:var(--dsw-alias-bg-layer-3,rgba(255,255,255,.06));white-space:nowrap}',
|
|
5207
5247
|
'.mu-queue-actions{display:inline-flex;align-items:center;gap:2px;justify-self:end}',
|
|
5208
5248
|
'.mu-queue-btn{width:20px;height:20px;padding:0;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#8a8a93);font-size:12px;line-height:20px;cursor:pointer}',
|
|
5209
5249
|
'.mu-queue-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06));color:var(--dsw-alias-label-primary,#f4f4f5)}',
|
|
@@ -5211,7 +5251,7 @@ window.__ModuleLoader__.load({
|
|
|
5211
5251
|
'.mu-fav.is-on{color:var(--dsw-alias-brand-primary,#f4f4f5)}',
|
|
5212
5252
|
'.mu-fav.is-on svg{fill:currentColor}',
|
|
5213
5253
|
'.mu-chip-sm{height:24px;padding:0 8px;font-size:11px}',
|
|
5214
|
-
'.mu-search-history{position:absolute;top:34px;right:0;z-index:20;display:flex;flex-wrap:wrap;gap:4px;max-width:320px;padding:6px;border:.5px solid var(--dsw-alias-border-l2,rgba(255,255,255,.12));border-radius:8px;background:var(--dsw-alias-bg-overlay,#202024);box-shadow:var(--dsw-elevation-prominent,0 8px 24px rgba(0,0,0,.35)}',
|
|
5254
|
+
'.mu-search-history{position:absolute;top:34px;right:0;z-index:20;display:flex;flex-wrap:wrap;gap:4px;max-width:320px;padding:6px;border:.5px solid var(--dsw-alias-border-l2,rgba(255,255,255,.12));border-radius:8px;background:var(--dsw-alias-bg-overlay,#202024);box-shadow:var(--dsw-elevation-prominent,0 8px 24px rgba(0,0,0,.35))}',
|
|
5215
5255
|
'.mu-queue-row:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}',
|
|
5216
5256
|
'.mu-queue-row.is-current{color:var(--dsw-alias-brand-primary,#f4f4f5);background:var(--dsw-alias-interactive-bg-active,rgba(255,255,255,.08))}',
|
|
5217
5257
|
'.mu-queue-index{font-size:12px;color:var(--dsw-alias-label-tertiary,#a1a1aa);font-variant-numeric:tabular-nums}',
|
|
@@ -5506,34 +5546,39 @@ window.__ModuleLoader__.load({
|
|
|
5506
5546
|
// the first tab holds the lock, later tabs queue for it and take over
|
|
5507
5547
|
// when the leader closes (the same pattern web-player PWAs use).
|
|
5508
5548
|
ctx.effect(function () {
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5549
|
+
try {
|
|
5550
|
+
if (
|
|
5551
|
+
typeof navigator === 'undefined' ||
|
|
5552
|
+
navigator.locks === undefined ||
|
|
5553
|
+
typeof navigator.locks.request !== 'function'
|
|
5554
|
+
) {
|
|
5555
|
+
return
|
|
5556
|
+
}
|
|
5557
|
+
if (window.__dshMusicLeaderBound === true) return
|
|
5558
|
+
window.__dshMusicLeaderBound = true
|
|
5559
|
+
var LOCK = 'dsh-music-panel.player'
|
|
5560
|
+
function hold() {
|
|
5561
|
+
// The lock is released when the page unloads.
|
|
5562
|
+
return new Promise(function () {})
|
|
5563
|
+
}
|
|
5564
|
+
function becomeLeader() {
|
|
5565
|
+
player.setLeader(true)
|
|
5566
|
+
return hold()
|
|
5567
|
+
}
|
|
5568
|
+
navigator.locks
|
|
5569
|
+
.request(LOCK, { mode: 'exclusive', ifAvailable: true }, function (lock) {
|
|
5570
|
+
if (lock !== null && lock !== undefined) return becomeLeader()
|
|
5571
|
+
// Another tab is playing: wait in the queue, then take over.
|
|
5572
|
+
player.setLeader(false)
|
|
5573
|
+
return navigator.locks.request(LOCK, becomeLeader)
|
|
5574
|
+
})
|
|
5575
|
+
.catch(function () {
|
|
5576
|
+
player.setLeader(true)
|
|
5577
|
+
})
|
|
5578
|
+
} catch (error) {
|
|
5579
|
+
// Cross-tab coordination must never break the plugin's UI.
|
|
5524
5580
|
player.setLeader(true)
|
|
5525
|
-
return hold()
|
|
5526
5581
|
}
|
|
5527
|
-
navigator.locks
|
|
5528
|
-
.request(LOCK, { mode: 'exclusive', ifAvailable: true }, function (lock) {
|
|
5529
|
-
if (lock !== null && lock !== undefined) return becomeLeader()
|
|
5530
|
-
// Another tab is playing: wait in the queue, then take over.
|
|
5531
|
-
player.setLeader(false)
|
|
5532
|
-
return navigator.locks.request(LOCK, becomeLeader)
|
|
5533
|
-
})
|
|
5534
|
-
.catch(function () {
|
|
5535
|
-
player.setLeader(true)
|
|
5536
|
-
})
|
|
5537
5582
|
})
|
|
5538
5583
|
|
|
5539
5584
|
function mount(name, registration, component) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-music-panel",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"audio"
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
|
-
"test": "node test/config.test.mjs && node test/meta.test.mjs && node test/library.test.mjs && node test/routes.test.mjs && node test/lyrics.test.mjs && node test/lyrics-write.test.mjs && node test/playlists.test.mjs && node test/favorites.test.mjs && node test/stats.test.mjs && node test/playback.test.mjs && node test/client.smoke.test.mjs",
|
|
28
|
+
"test": "node test/config.test.mjs && node test/meta.test.mjs && node test/library.test.mjs && node test/routes.test.mjs && node test/lyrics.test.mjs && node test/lyrics-write.test.mjs && node test/playlists.test.mjs && node test/favorites.test.mjs && node test/stats.test.mjs && node test/playback.test.mjs && node test/style.test.mjs && node test/client.smoke.test.mjs",
|
|
29
29
|
"check": "node --check index.js && node --check client.js && node --check lib/config.js && node --check lib/settings.js && node --check lib/meta.js && node --check lib/library.js && node --check lib/http.js && node --check lib/lyrics.js && node --check lib/lyrics-write.js && node --check lib/playlists.js && node --check lib/favorites.js && node --check lib/stats.js && node --check lib/playback.js && node --check lib/routes.js",
|
|
30
30
|
"pack:check": "npm pack --dry-run"
|
|
31
31
|
},
|