@aicupa/plugin-bg-music 1.0.3 → 1.0.4

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 (2) hide show
  1. package/inject.js +247 -146
  2. package/package.json +1 -1
package/inject.js CHANGED
@@ -1,11 +1,8 @@
1
1
  (function () {
2
- // 防止重复注入
3
2
  if (window.__AICUPA_BG_MUSIC_LOADED__) return;
4
3
  window.__AICUPA_BG_MUSIC_LOADED__ = true;
5
4
 
6
- // ==========================================
7
5
  // 1. 动态引入 Tone.js
8
- // ==========================================
9
6
  const script = document.createElement("script");
10
7
  script.src = "https://unpkg.com/tone";
11
8
  script.async = true;
@@ -16,9 +13,7 @@
16
13
  };
17
14
 
18
15
  function initPlugin() {
19
- // ==========================================
20
- // 2. 注入 CSS 霓虹深色样式
21
- // ==========================================
16
+ // 2. 注入全局样式 (包含曲目列表的抽屉动画)
22
17
  const style = document.createElement("style");
23
18
  style.textContent = `
24
19
  .music-floating-container {
@@ -28,7 +23,7 @@
28
23
  display: flex;
29
24
  align-items: center;
30
25
  justify-content: flex-end;
31
- z-index: 999999; /* 确保在 Todolist 所有人机界面最上层 */
26
+ z-index: 999999;
32
27
  touch-action: none;
33
28
  user-select: none;
34
29
  }
@@ -47,119 +42,145 @@
47
42
  position: relative;
48
43
  z-index: 2;
49
44
  }
50
- .music-icon-wrapper:active {
51
- cursor: grabbing;
52
- transform: scale(0.95);
53
- }
54
- .music-icon-wrapper svg {
55
- fill: #00e5ff;
56
- width: 20px;
57
- height: 20px;
58
- pointer-events: none;
59
- }
60
- .music-icon-wrapper.playing svg {
61
- animation: musicPulse 1.5s infinite alternate;
62
- }
45
+ .music-icon-wrapper:active { cursor: grabbing; transform: scale(0.95); }
46
+ .music-icon-wrapper svg { fill: #00e5ff; width: 20px; height: 20px; pointer-events: none; }
47
+ .music-icon-wrapper.playing svg { animation: musicPulse 1.5s infinite alternate; }
48
+
63
49
  @keyframes musicPulse {
64
50
  0% { transform: scale(1); filter: drop-shadow(0 0 2px #00e5ff); }
65
51
  100% { transform: scale(1.12); filter: drop-shadow(0 0 8px #00e5ff); }
66
52
  }
53
+
54
+ /* 控制面板基础样式 */
67
55
  .music-control-panel {
68
56
  position: absolute;
69
57
  right: 15px;
70
58
  background: #0f172a;
71
59
  border: 1px solid #1e293b;
72
- padding: 10px 45px 10px 15px;
73
- border-radius: 20px 0 0 20px;
60
+ padding: 12px 45px 12px 15px;
61
+ border-radius: 16px 0 0 16px;
74
62
  display: flex;
75
63
  flex-direction: column;
76
- gap: 6px;
77
- width: 180px;
64
+ gap: 8px;
65
+ width: 200px;
78
66
  opacity: 0;
79
67
  transform: translateX(20px);
80
- transition: opacity 0.3s, transform 0.3s;
68
+ transition: opacity 0.3s, transform 0.3s, height 0.3s;
81
69
  pointer-events: none;
82
70
  box-shadow: -5px 5px 15px rgba(0,0,0,0.5);
71
+ overflow: hidden;
83
72
  }
73
+
74
+ /* Hover 激活主面板 */
84
75
  .music-floating-container:not(.dragging):hover .music-control-panel {
85
76
  opacity: 1;
86
77
  transform: translateX(0);
87
78
  pointer-events: auto;
88
79
  }
80
+
81
+ /* 左靠齐适配 */
89
82
  .music-floating-container.align-left .music-control-panel {
90
83
  right: auto;
91
84
  left: 15px;
92
- border-radius: 0 20px 20px 0;
93
- padding: 10px 15px 10px 45px;
85
+ border-radius: 0 16px 16px 0;
86
+ padding: 12px 15px 12px 45px;
94
87
  transform: translateX(-20px);
95
88
  box-shadow: 5px 5px 15px rgba(0,0,0,0.5);
96
89
  }
97
90
  .music-floating-container.align-left:not(.dragging):hover .music-control-panel {
98
91
  transform: translateX(0);
99
92
  }
93
+
100
94
  .music-floating-container:hover .music-icon-wrapper {
101
95
  border-color: #ffffff;
102
96
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.2);
103
97
  }
98
+
104
99
  .music-title {
105
100
  color: #00e5ff;
106
101
  font-size: 11px;
107
102
  white-space: nowrap;
108
103
  overflow: hidden;
109
104
  text-overflow: ellipsis;
105
+ font-weight: bold;
110
106
  }
111
- .panel-row {
112
- display: flex;
113
- align-items: center;
114
- justify-content: space-between;
115
- gap: 8px;
116
- }
107
+
108
+ .panel-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
109
+
117
110
  .music-ctrl-btn {
118
111
  background: none;
119
112
  border: 1px solid #334155;
120
113
  color: #e2e8f0;
121
114
  font-size: 10px;
122
- padding: 2px 6px;
115
+ padding: 3px 8px;
123
116
  cursor: pointer;
124
- border-radius: 3px;
117
+ border-radius: 4px;
118
+ transition: all 0.2s;
125
119
  }
126
- .music-ctrl-btn:hover {
127
- border-color: #00e5ff;
128
- color: #00e5ff;
120
+ .music-ctrl-btn:hover { border-color: #00e5ff; color: #00e5ff; }
121
+
122
+ .music-progress-container { width: 100%; height: 4px; background: #334155; border-radius: 2px; position: relative; overflow: hidden; }
123
+ .music-progress-bar { height: 100%; width: 0%; background: #00e5ff; transition: width 0.5s linear; }
124
+
125
+ /* === 新增:曲库列表样式 === */
126
+ .music-playlist-drawer {
127
+ border-top: 1px solid #1e293b;
128
+ margin-top: 4px;
129
+ padding-top: 8px;
130
+ display: none; /* 默认折叠 */
131
+ flex-direction: column;
132
+ gap: 4px;
133
+ max-height: 150px;
134
+ overflow-y: auto;
129
135
  }
130
- .music-progress-container {
131
- width: 100%;
132
- height: 4px;
133
- background: #334155;
134
- border-radius: 2px;
135
- position: relative;
136
+ /* 自定义滚动条 */
137
+ .music-playlist-drawer::-webkit-scrollbar { width: 4px; }
138
+ .music-playlist-drawer::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 2px; }
139
+
140
+ /* 控制面板展开状态样式触发 */
141
+ .music-control-panel.list-expanded .music-playlist-drawer {
142
+ display: flex;
143
+ }
144
+
145
+ .playlist-item {
146
+ font-size: 10px;
147
+ color: #94a3b8;
148
+ padding: 5px 6px;
149
+ cursor: pointer;
150
+ border-radius: 4px;
151
+ white-space: nowrap;
136
152
  overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ transition: all 0.2s;
137
155
  }
138
- .music-progress-bar {
139
- height: 100%;
140
- width: 0%;
141
- background: #00e5ff;
142
- transition: width 0.5s linear;
156
+ .playlist-item:hover {
157
+ background: rgba(0, 229, 255, 0.1);
158
+ color: #ffffff;
159
+ }
160
+ .playlist-item.active {
161
+ color: #00e5ff;
162
+ background: rgba(0, 229, 255, 0.15);
163
+ font-weight: bold;
164
+ border-left: 2px solid #00e5ff;
143
165
  }
144
166
  `;
145
167
  document.head.appendChild(style);
146
168
 
147
- // ==========================================
148
- // 3. 动态插入 DOM 结构
149
- // ==========================================
169
+ // 3. 动态插入 DOM (加入了 LIST 列表切换按钮和列表容器)
150
170
  const container = document.createElement("div");
151
171
  container.className = "music-floating-container";
152
172
  container.id = "music-container";
153
173
  container.innerHTML = `
154
- <div class="music-control-panel">
155
- <div class="music-title" id="track-name">Crystal Piano</div>
174
+ <div class="music-control-panel" id="control-panel">
175
+ <div class="music-title" id="track-name">1. Crystal Piano</div>
156
176
  <div class="music-progress-container">
157
177
  <div class="music-progress-bar" id="track-progress"></div>
158
178
  </div>
159
179
  <div class="panel-row">
160
180
  <button class="music-ctrl-btn" id="btn-toggle">PLAY</button>
161
- <button class="music-ctrl-btn" id="btn-switch">SWITCH</button>
181
+ <button class="music-ctrl-btn" id="btn-list">LIST ☰</button>
162
182
  </div>
183
+ <div class="music-playlist-drawer" id="playlist-drawer"></div>
163
184
  </div>
164
185
  <div class="music-icon-wrapper" id="icon-trigger">
165
186
  <svg viewBox="0 0 24 24">
@@ -169,21 +190,22 @@
169
190
  `;
170
191
  document.body.appendChild(container);
171
192
 
172
- // ==========================================
173
- // 4. 音频引擎调度逻辑
174
- // ==========================================
193
+ // 4. 音频核心配置与多曲目矩阵
175
194
  let isPlaying = false;
176
195
  let currentTrackIndex = 0;
177
196
  let progressTimer = null;
178
197
  let currentStep = 0;
179
198
 
180
199
  const tracks = [
181
- { name: "Crystal Piano", stepsMax: 12 },
182
- { name: "Ambient Canon", stepsMax: 16 },
200
+ { name: "1. Crystal Piano", stepsMax: 12, bpm: 72 },
201
+ { name: "2. Ambient Canon", stepsMax: 16, bpm: 72 },
202
+ { name: "3. Always With Me", stepsMax: 16, bpm: 84 },
203
+ { name: "4. Air on G String", stepsMax: 16, bpm: 60 },
204
+ { name: "5. Gymnopédie No.1", stepsMax: 8, bpm: 54 },
183
205
  ];
184
206
 
185
207
  const reverb = new Tone.Reverb({
186
- decay: 6,
208
+ decay: 7,
187
209
  preDelay: 0.15,
188
210
  wet: 0.45,
189
211
  }).toDestination();
@@ -193,13 +215,13 @@
193
215
  wet: 0.2,
194
216
  }).connect(reverb);
195
217
  const filter = new Tone.Filter({
196
- frequency: 1800,
218
+ frequency: 2000,
197
219
  type: "lowpass",
198
220
  }).connect(delay);
199
221
 
200
222
  const padSynth = new Tone.PolySynth(Tone.Synth, {
201
223
  oscillator: { type: "triangle" },
202
- envelope: { attack: 0.5, decay: 2, sustain: 0.4, release: 4 },
224
+ envelope: { attack: 0.3, decay: 2, sustain: 0.5, release: 3.5 },
203
225
  }).connect(filter);
204
226
  padSynth.volume.value = -18;
205
227
 
@@ -209,24 +231,24 @@
209
231
  }).connect(filter);
210
232
  leadSynth.volume.value = -10;
211
233
 
212
- const pianoChords = [
213
- ["F3", "A3", "C4", "E4", "G4"],
234
+ // 乐理矩阵
235
+ const pChords = [
236
+ ["F3", "A3", "C4", "E4"],
214
237
  ["G3", "B3", "D4", "G4"],
215
- ["C3", "E3", "G3", "B3", "D4"],
238
+ ["C3", "E3", "G3", "B3"],
216
239
  ];
217
- const pianoScale = ["C5", "D5", "E5", "G5", "A5", "C6", "D6", "E6"];
218
-
219
- const canonChords = [
220
- ["C3", "E4", "G4"],
221
- ["G3", "D4", "B4"],
222
- ["A2", "C4", "E4"],
223
- ["E3", "B3", "G4"],
224
- ["F2", "A3", "C4"],
225
- ["C3", "G3", "E4"],
226
- ["F2", "A3", "C4"],
227
- ["G2", "B3", "D4"],
240
+ const pScale = ["C5", "D5", "E5", "G5", "A5", "C6", "D6", "E6"];
241
+ const cChords = [
242
+ ["C3", "E4"],
243
+ ["G3", "D4"],
244
+ ["A2", "C4"],
245
+ ["E3", "B3"],
246
+ ["F2", "A3"],
247
+ ["C3", "G3"],
248
+ ["F2", "A3"],
249
+ ["G2", "B3"],
228
250
  ];
229
- const canonMelody = [
251
+ const cMelody = [
230
252
  ["C5", "G5"],
231
253
  ["B4", "G5"],
232
254
  ["A4", "E5"],
@@ -236,138 +258,216 @@
236
258
  ["F4", "C5"],
237
259
  ["D4", "G4"],
238
260
  ];
261
+ const ghibliChords = [
262
+ ["F3", "A3", "C4"],
263
+ ["C3", "E3", "G3"],
264
+ ["G3", "B3", "D4"],
265
+ ["A2", "C3", "E3"],
266
+ ["F3", "A3", "C4"],
267
+ ["C3", "E3", "G3"],
268
+ ["G3", "B3", "D4"],
269
+ ["C3", "E4", "G4"],
270
+ ];
271
+ const ghibliMelody = [
272
+ ["A4", "C5"],
273
+ ["G4", "C5"],
274
+ ["F4", "D5"],
275
+ ["E4", "C5"],
276
+ ["F4", "A4"],
277
+ ["E4", "G4"],
278
+ ["D4", "F4"],
279
+ ["C4", "E5"],
280
+ ];
281
+ const bachBass = [
282
+ ["C3", "E4"],
283
+ ["B2", "D4"],
284
+ ["A2", "C4"],
285
+ ["G2", "B3"],
286
+ ["F2", "A3"],
287
+ ["E2", "G3"],
288
+ ["D2", "F3"],
289
+ ["G2", "F3"],
290
+ ];
291
+ const bachMelody = [
292
+ ["E5", "G5"],
293
+ ["F5", "A5"],
294
+ ["C5", "E5"],
295
+ ["B4", "D5"],
296
+ ["A4", "C5"],
297
+ ["G4", "B4"],
298
+ ["F4", "A4"],
299
+ ["B4", "G5"],
300
+ ];
301
+ const satieChords = [
302
+ ["G2", "B3", "D4", "F#4"],
303
+ ["D2", "F#3", "A3", "C#4"],
304
+ ];
305
+ const satieMelody = ["F#5", "A5", "C#6", "B5", "E5", "D5", "A4", "F#4"];
239
306
 
240
307
  function setupTransport() {
241
308
  Tone.getTransport().cancel();
242
- Tone.getTransport().bpm.value = 72;
309
+ Tone.getTransport().bpm.value = tracks[currentTrackIndex].bpm;
243
310
  currentStep = 0;
244
311
 
245
312
  Tone.getTransport().scheduleRepeat((time) => {
313
+ const step = currentStep;
246
314
  if (currentTrackIndex === 0) {
247
- const chord = pianoChords[currentStep % pianoChords.length];
248
- padSynth.triggerAttackRelease(chord, 5, time);
249
- if (Math.random() > 0.3) {
250
- const note =
251
- pianoScale[Math.floor(Math.random() * pianoScale.length)];
315
+ padSynth.triggerAttackRelease(
316
+ pChords[step % pChords.length],
317
+ "2n",
318
+ time,
319
+ );
320
+ if (Math.random() > 0.3)
252
321
  leadSynth.triggerAttackRelease(
253
- note,
322
+ pScale[Math.floor(Math.random() * pScale.length)],
254
323
  "4n",
255
- time + Math.random() * 0.15,
324
+ time + Math.random() * 0.1,
256
325
  );
257
- }
258
- } else {
259
- const idx = currentStep % 8;
260
- padSynth.triggerAttackRelease(canonChords[idx], "2n", time);
261
- const pool = canonMelody[idx];
262
- leadSynth.triggerAttackRelease(pool[0], "4n", time);
263
- if (Math.random() > 0.4) {
326
+ } else if (currentTrackIndex === 1) {
327
+ const idx = step % 8;
328
+ padSynth.triggerAttackRelease(cChords[idx], "2n", time);
329
+ leadSynth.triggerAttackRelease(cMelody[idx][step % 2], "4n", time);
330
+ if (Math.random() > 0.5)
264
331
  leadSynth.triggerAttackRelease(
265
- pool[1],
332
+ cMelody[idx][1],
266
333
  "8n",
267
334
  time + Tone.Time("4n"),
268
335
  );
269
- }
336
+ } else if (currentTrackIndex === 2) {
337
+ const idx = step % 8;
338
+ padSynth.triggerAttackRelease(ghibliChords[idx], "2n", time);
339
+ leadSynth.triggerAttackRelease(
340
+ ghibliMelody[idx][Math.floor(Math.random() * 2)],
341
+ "4n",
342
+ time,
343
+ );
344
+ } else if (currentTrackIndex === 3) {
345
+ const idx = step % 8;
346
+ padSynth.triggerAttackRelease(bachBass[idx], "2n", time);
347
+ leadSynth.triggerAttackRelease(
348
+ step % 2 === 0 ? bachMelody[idx][0] : bachMelody[idx][1],
349
+ "4n",
350
+ time,
351
+ );
352
+ } else if (currentTrackIndex === 4) {
353
+ padSynth.triggerAttackRelease(satieChords[step % 2], "1m", time);
354
+ if (step % 2 === 0)
355
+ leadSynth.triggerAttackRelease(
356
+ satieMelody[Math.floor(step / 2) % satieMelody.length],
357
+ "2n",
358
+ time + Tone.Time("4n"),
359
+ );
270
360
  }
271
361
  currentStep++;
272
362
  }, "2n");
273
363
  }
274
364
 
275
- // ==========================================
276
- // 5. 交互绑定与拖拽定位
277
- // ==========================================
365
+ // 5. DOM 节点引用与渲染逻辑
278
366
  const icon = document.getElementById("icon-trigger");
279
367
  const btnToggle = document.getElementById("btn-toggle");
280
- const btnSwitch = document.getElementById("btn-switch");
368
+ const btnList = document.getElementById("btn-list");
369
+ const panel = document.getElementById("control-panel");
370
+ const drawer = document.getElementById("playlist-drawer");
281
371
  const trackName = document.getElementById("track-name");
282
372
  const progressBar = document.getElementById("track-progress");
283
373
 
284
- let isDragging = false;
285
- let hasMoved = false;
374
+ // 动态渲染曲目列表
375
+ function renderPlaylist() {
376
+ drawer.innerHTML = "";
377
+ tracks.forEach((track, index) => {
378
+ const item = document.createElement("div");
379
+ item.className = `playlist-item ${index === currentTrackIndex ? "active" : ""}`;
380
+ item.innerText = track.name;
381
+
382
+ // 绑定点击曲目切歌事件
383
+ item.addEventListener("click", (e) => {
384
+ e.stopPropagation(); // 阻止冒泡
385
+ selectTrack(index);
386
+ });
387
+ drawer.appendChild(item);
388
+ });
389
+ }
390
+
391
+ function selectTrack(index) {
392
+ currentTrackIndex = index;
393
+ trackName.innerText = tracks[currentTrackIndex].name;
394
+ currentStep = 0;
395
+ progressBar.style.width = "0%";
396
+ renderPlaylist(); // 刷新高亮状态
397
+
398
+ if (isPlaying) {
399
+ setupTransport();
400
+ } else {
401
+ togglePlay(); // 如果之前没播放,点击列表曲目直接触发播放
402
+ }
403
+ }
404
+
405
+ // 6. 交互控制
406
+ let isDragging = false,
407
+ hasMoved = false;
286
408
  let startX = 0,
287
- startY = 0;
288
- let initialLeft = 0,
409
+ startY = 0,
410
+ initialLeft = 0,
289
411
  initialTop = 0;
290
412
 
291
413
  function onStart(e) {
292
414
  isDragging = true;
293
415
  hasMoved = false;
294
416
  container.classList.add("dragging");
295
-
296
417
  const clientX = e.touches ? e.touches[0].clientX : e.clientX;
297
418
  const clientY = e.touches ? e.touches[0].clientY : e.clientY;
298
419
  startX = clientX;
299
420
  startY = clientY;
300
-
301
421
  const rect = container.getBoundingClientRect();
302
422
  initialLeft = rect.left;
303
423
  initialTop = rect.top;
304
-
305
424
  container.style.right = "auto";
306
425
  container.style.bottom = "auto";
307
426
  container.style.left = `${initialLeft}px`;
308
427
  container.style.top = `${initialTop}px`;
309
-
310
428
  document.addEventListener("mousemove", onMove);
311
429
  document.addEventListener("mouseup", onEnd);
312
- document.addEventListener("touchmove", onMove, { passive: false });
313
- document.addEventListener("touchend", onEnd);
314
430
  }
315
431
 
316
432
  function onMove(e) {
317
433
  if (!isDragging) return;
318
434
  const clientX = e.touches ? e.touches[0].clientX : e.clientX;
319
435
  const clientY = e.touches ? e.touches[0].clientY : e.clientY;
320
-
321
- const deltaX = clientX - startX;
322
- const deltaY = clientY - startY;
323
-
324
- if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
325
- hasMoved = true;
326
- }
327
-
328
- let newLeft = initialLeft + deltaX;
329
- let newTop = initialTop + deltaY;
330
-
331
- const padding = 10;
332
- newLeft = Math.max(padding, Math.min(window.innerWidth - 55, newLeft));
333
- newTop = Math.max(padding, Math.min(window.innerHeight - 55, newTop));
334
-
335
- container.style.left = `${newLeft}px`;
336
- container.style.top = `${newTop}px`;
436
+ const deltaX = clientX - startX,
437
+ deltaY = clientY - startY;
438
+ if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) hasMoved = true;
439
+ let nl = initialLeft + deltaX,
440
+ nt = initialTop + deltaY;
441
+ nl = Math.max(10, Math.min(window.innerWidth - 55, nl));
442
+ nt = Math.max(10, Math.min(window.innerHeight - 55, nt));
443
+ container.style.left = `${nl}px`;
444
+ container.style.top = `${nt}px`;
337
445
  }
338
446
 
339
447
  function onEnd() {
340
448
  if (!isDragging) return;
341
449
  isDragging = false;
342
450
  container.classList.remove("dragging");
343
-
344
451
  document.removeEventListener("mousemove", onMove);
345
452
  document.removeEventListener("mouseup", onEnd);
346
- document.removeEventListener("touchmove", onMove);
347
- document.removeEventListener("touchend", onEnd);
348
-
349
453
  const rect = container.getBoundingClientRect();
350
- const screenWidth = window.innerWidth;
351
- const padding = 20;
352
-
454
+ const sw = window.innerWidth;
353
455
  container.style.transition =
354
456
  "left 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28)";
355
- if (rect.left + rect.width / 2 < screenWidth / 2) {
356
- container.style.left = `${padding}px`;
457
+ if (rect.left + rect.width / 2 < sw / 2) {
458
+ container.style.left = "20px";
357
459
  container.classList.add("align-left");
358
460
  } else {
359
- container.style.left = `${screenWidth - rect.width - padding}px`;
461
+ container.style.left = `${sw - rect.width - 20}px`;
360
462
  container.classList.remove("align-left");
361
463
  }
362
-
363
464
  setTimeout(() => {
364
465
  container.style.transition = "";
365
466
  }, 300);
366
467
  }
367
468
 
368
469
  async function togglePlay() {
369
- if (hasMoved) return; // 拖动时不触发播放/暂停
370
-
470
+ if (hasMoved) return; // 拖拽时不执行点击
371
471
  await Tone.start();
372
472
  await reverb.ready;
373
473
 
@@ -390,17 +490,18 @@
390
490
  }
391
491
  }
392
492
 
493
+ // 事件注册
393
494
  icon.addEventListener("mousedown", onStart);
394
- icon.addEventListener("touchstart", onStart, { passive: true });
395
495
  icon.addEventListener("click", togglePlay);
396
496
  btnToggle.addEventListener("click", togglePlay);
397
497
 
398
- btnSwitch.addEventListener("click", () => {
399
- currentTrackIndex = (currentTrackIndex + 1) % tracks.length;
400
- trackName.innerText = tracks[currentTrackIndex].name;
401
- currentStep = 0;
402
- progressBar.style.width = "0%";
403
- if (isPlaying) setupTransport();
498
+ // LIST 按钮:控制列表展开与收起
499
+ btnList.addEventListener("click", (e) => {
500
+ e.stopPropagation();
501
+ panel.classList.toggle("list-expanded");
404
502
  });
503
+
504
+ // 首次渲染列表
505
+ renderPlaylist();
405
506
  }
406
507
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicupa/plugin-bg-music",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Aicupa Todolist Background Music Inject Plugin",
5
5
  "main": "./service",
6
6
  "view": "./view",