dsh-scroll-piano 0.3.0 → 0.3.1

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/README.md +2 -2
  2. package/lib/client.js +22 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,12 +6,12 @@
6
6
 
7
7
  - 聊天区右边缘一条细彩色竖条(开启时淡淡发光),鼠标进入该区间即触发。
8
8
  - 鼠标纵向移动 = 连续刮奏;竖条上有个白点跟随鼠标位置。
9
- - 气泡恒显当前音名(如 `♪ C4 · 60`);**当且只当鼠标正落在「你发的某条消息」上**时,气泡展开显示那条消息的正文(最多 3 行)+「点击跳转」。
9
+ - **气泡只在鼠标正落在「你发的某条消息」上时出现**,内容就是那条消息的正文(最多 3 行)+「点击跳转」提示;**其余位置整个不显示**(不显示音名、不显示 MIDI 数字)。
10
10
  - **点击气泡** → 平滑滚动,把那条消息滚到视口中央。
11
11
  - 左下角一枚 `🎵 滚动钢琴 ON/OFF` 胶囊:点开关切换、右侧滑块调音量(设置存 `localStorage`)。
12
12
  - 默认音域 C3–C6(3 个八度),只按半音触发(同音不重复触发)。
13
13
 
14
- > 预览**只显示用户自己发的消息**,且**不做「取最近一条」兜底**:滑到 AI 回复 / 工具调用 / 过程块上时气泡只留音名、正文收起,避免同一条消息在整片区域里被反复显示。
14
+ > 预览**只显示用户自己发的消息**,且**不做「取最近一条」兜底**:滑到 AI 回复 / 工具调用 / 过程块上时气泡整个收起,避免同一条消息在整片区域里被反复显示。声音与预览**彼此独立**——任何位置滑动都照常出声,只有预览挑消息。
15
15
 
16
16
  ## 消息预览怎么定位
17
17
 
package/lib/client.js CHANGED
@@ -37,7 +37,7 @@ window.__ModuleLoader__.load({
37
37
  ".dshscp_prevTop{display:flex;align-items:center;gap:8px;margin-bottom:4px}",
38
38
  ".dshscp_prevRole{flex:none;padding:1px 5px;border-radius:4px;background:rgba(91,141,239,.3);color:#bcd4ff;font-size:10px;line-height:14px}",
39
39
  ".dshscp_prevRoleAI{background:rgba(160,120,240,.28);color:#d6c4ff}",
40
- ".dshscp_prevNote{flex:none;color:#9aa2b1;font-size:10px;line-height:14px;font-variant-numeric:tabular-nums}",
40
+ ".dshscp_prevHint{margin-left:auto;flex:none;color:#9aa2b1;font-size:10px;line-height:14px}",
41
41
  ".dshscp_prevText{font-size:12px;line-height:17px;color:#e6e8ee;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-break:break-word}"
42
42
  ].join("");
43
43
  const tagId = "dsh-scroll-piano/scroll-piano.css";
@@ -150,12 +150,7 @@ window.__ModuleLoader__.load({
150
150
  noise.start(now);
151
151
  }
152
152
 
153
- //#region note naming
154
- const NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
155
- function noteLabel(midi) {
156
- return NOTE_NAMES[((midi % 12) + 12) % 12] + (Math.floor(midi / 12) - 1);
157
- }
158
-
153
+ //#region chat anchors
159
154
  /** 聊天区的滚动容器(从 `[data-chat-flow]` 往上找第一个可纵向滚动的祖先)。 */
160
155
  function findChatScroller() {
161
156
  const flow = document.querySelector("[data-chat-flow]");
@@ -223,7 +218,6 @@ window.__ModuleLoader__.load({
223
218
  //#region component
224
219
  function ScrollPiano() {
225
220
  const [settings, setSettings] = useState(loadSettings);
226
- const [label, setLabel] = useState("");
227
221
  const settingsRef = useRef(settings);
228
222
  settingsRef.current = settings;
229
223
  const lastMidiRef = useRef(null);
@@ -232,7 +226,6 @@ window.__ModuleLoader__.load({
232
226
  const markerRef = useRef(null);
233
227
  const prevRef = useRef(null);
234
228
  const roleRef = useRef(null);
235
- const noteRef = useRef(null);
236
229
  const textRef = useRef(null);
237
230
  const bubbleHoverRef = useRef(false);
238
231
  const activeMsgRef = useRef(null);
@@ -288,20 +281,28 @@ window.__ModuleLoader__.load({
288
281
  const m = markerRef.current;
289
282
  if (m) m.style.top = Math.round(y - 3.5) + "px";
290
283
  };
291
- const showPreview = (y, noteText, msg) => {
284
+ /**
285
+ * 气泡**只在鼠标压在某条「用户消息」上时出现**,内容只有那条消息的正文。
286
+ * 其余位置(AI 回复 / 工具块 / 空白)整个收起——不放音名、不放 MIDI 数字。
287
+ */
288
+ const showPreview = (y, msg) => {
292
289
  const el = prevRef.current;
293
290
  if (!el) return;
294
- activeMsgRef.current = msg || null;
295
- if (noteRef.current) noteRef.current.textContent = "♪ " + noteText + (msg ? " · 点击跳转" : "");
296
- if (roleRef.current) roleRef.current.style.display = msg ? "" : "none";
297
- if (textRef.current) textRef.current.textContent = msg ? msg.text : "";
298
- // 只有预览到用户消息时才可点(点了跳过去);否则纯视觉不拦事件
299
- el.style.pointerEvents = msg ? "auto" : "none";
300
- el.style.cursor = msg ? "pointer" : "default";
291
+ if (!msg) {
292
+ activeMsgRef.current = null;
293
+ el.classList.remove("dshscp_prevOn");
294
+ el.style.pointerEvents = "none";
295
+ return;
296
+ }
297
+ activeMsgRef.current = msg;
298
+ if (textRef.current) textRef.current.textContent = msg.text;
299
+ el.style.pointerEvents = "auto";
300
+ el.style.cursor = "pointer";
301
301
  // 竖直居中于鼠标,并夹在视口内
302
302
  const h = el.offsetHeight || 40;
303
303
  const top = Math.max(8, Math.min(window.innerHeight - h - 8, y - h / 2));
304
304
  el.style.top = Math.round(top) + "px";
305
+ el.classList.add("dshscp_prevOn");
305
306
  };
306
307
  const hidePreview = () => {
307
308
  activeMsgRef.current = null;
@@ -329,9 +330,9 @@ window.__ModuleLoader__.load({
329
330
  // t=0(顶部)→ highNote;t=1(底部)→ lowNote
330
331
  const midi = Math.round(s.highNote - t * (s.highNote - s.lowNote));
331
332
  showMarker(e.clientY);
332
- showPreview(e.clientY, noteLabel(midi) + " · " + midi, userMessageAtY(e.clientY));
333
+ // 预览只看用户消息;没有就整个收起(不显示音名/MIDI)
334
+ showPreview(e.clientY, userMessageAtY(e.clientY));
333
335
  if (markerRef.current) markerRef.current.classList.add("dshscp_markerOn");
334
- if (prevRef.current) prevRef.current.classList.add("dshscp_prevOn");
335
336
  if (midi !== lastMidiRef.current) {
336
337
  lastMidiRef.current = midi;
337
338
  // 限速:滑太快时不要瞬间堆几十个音(会糊成一片杂音)
@@ -340,7 +341,6 @@ window.__ModuleLoader__.load({
340
341
  lastPlayRef.current = nowMs;
341
342
  playNote(midi, s.volume);
342
343
  }
343
- setLabel(noteLabel(midi));
344
344
  }
345
345
  };
346
346
  const onLeave = () => {
@@ -390,7 +390,7 @@ window.__ModuleLoader__.load({
390
390
  },
391
391
  h("div", { className: "dshscp_prevTop" },
392
392
  h("span", { ref: roleRef, className: "dshscp_prevRole" }, "你"),
393
- h("span", { ref: noteRef, className: "dshscp_prevNote" }, "♪")
393
+ h("span", { className: "dshscp_prevHint" }, "点击跳转")
394
394
  ),
395
395
  h("div", { ref: textRef, className: "dshscp_prevText" })
396
396
  ),
@@ -411,8 +411,7 @@ window.__ModuleLoader__.load({
411
411
  value: settings.volume,
412
412
  onChange: (e) => setVolume(parseFloat(e.target.value)),
413
413
  title: "音量"
414
- }),
415
- label ? h("span", { style: { opacity: 0.75, minWidth: "26px" } }, label) : null
414
+ })
416
415
  )
417
416
  );
418
417
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-scroll-piano",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "滚动钢琴:鼠标滑过 DSH 右侧边缘的滚动条竖条时,按纵向位置发出钢琴音(越高音越高,特雷门琴式)。",
5
5
  "type": "module",
6
6
  "license": "MIT",