@onjmin/dtm 0.1.57 → 0.1.58

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/dist/index.mjs CHANGED
@@ -5020,8 +5020,6 @@ var DAW_CSS = `
5020
5020
  max-height: 180px;
5021
5021
  overflow-y: auto;
5022
5022
  padding: 8px 10px 10px;
5023
- overflow-x: auto;
5024
- white-space: nowrap;
5025
5023
  scrollbar-width: none;
5026
5024
  display: flex;
5027
5025
  flex-direction: column;
@@ -5050,6 +5048,7 @@ var DAW_CSS = `
5050
5048
  .dtm-cp-bar {
5051
5049
  display: flex;
5052
5050
  align-items: center;
5051
+ flex-wrap: wrap;
5053
5052
  gap: 2px;
5054
5053
  margin-top: 2px;
5055
5054
  }
@@ -6752,11 +6751,13 @@ var loadDrumFont = (ctx, pitch) => {
6752
6751
  _drumLoading.set(pitch, p);
6753
6752
  return p;
6754
6753
  };
6754
+ var BAR_SEP = /[|ll→]/;
6755
+ var NON_EVENT_RE = /^[=_]$|^N\.C\.$|^N$/i;
6755
6756
  var buildDisplayLines = (chords, eventsCount) => {
6756
6757
  const rawLines = chords.split("\n").map((l) => l.trim()).filter((l) => l);
6757
6758
  const displayLines = [];
6758
6759
  let eventCounter = 0;
6759
- let colorIdx = -1;
6760
+ let colorIdx = 0;
6760
6761
  for (const line of rawLines) {
6761
6762
  if (/^#/.test(line)) {
6762
6763
  const label = line.replace(/^#\s*/, "").trim();
@@ -6764,19 +6765,21 @@ var buildDisplayLines = (chords, eventsCount) => {
6764
6765
  if (/^tone\s*=/i.test(label)) continue;
6765
6766
  if (/^instrument\s*=/i.test(label)) continue;
6766
6767
  if (/^metronome/i.test(label)) continue;
6767
- colorIdx++;
6768
+ if (displayLines.length > 0 || eventCounter > 0) colorIdx++;
6768
6769
  displayLines.push({
6769
6770
  type: "section",
6770
6771
  section: label,
6771
- colorIdx: Math.max(0, colorIdx)
6772
+ colorIdx
6772
6773
  });
6773
6774
  continue;
6774
6775
  }
6775
- const segments = line.split("|");
6776
- if (segments.length <= 1) continue;
6776
+ const segments = line.split(BAR_SEP);
6777
+ if (segments.length === 0) continue;
6778
+ const hasMultiSeg = segments.length > 1;
6777
6779
  const parts = [];
6778
6780
  for (let i = 0; i < segments.length; i++) {
6779
- if (i > 0) parts.push({ text: "|", isChord: false, eventIdx: -1 });
6781
+ if (hasMultiSeg && i > 0)
6782
+ parts.push({ text: "|", isChord: false, eventIdx: -1 });
6780
6783
  const bar = segments[i].trim();
6781
6784
  if (!bar) continue;
6782
6785
  const splitAt = [];
@@ -6799,7 +6802,7 @@ var buildDisplayLines = (chords, eventsCount) => {
6799
6802
  splitAt.push(j + 1);
6800
6803
  continue;
6801
6804
  }
6802
- if (/^[A-G]$/i.test(char)) {
6805
+ if (/^[A-G]$/.test(char)) {
6803
6806
  const prev = bar[j - 1];
6804
6807
  const prev2 = bar.slice(Math.max(0, j - 2), j);
6805
6808
  if (prev === "/" || prev2.toLowerCase() === "on") continue;
@@ -6808,12 +6811,13 @@ var buildDisplayLines = (chords, eventsCount) => {
6808
6811
  }
6809
6812
  const uniqueSplitAt = Array.from(new Set(splitAt)).filter((idx) => idx >= 0 && idx < bar.length).sort((a, b) => a - b);
6810
6813
  if (uniqueSplitAt.length === 0) {
6814
+ const isEvent = !NON_EVENT_RE.test(bar);
6811
6815
  parts.push({
6812
6816
  text: bar,
6813
6817
  isChord: true,
6814
- eventIdx: eventCounter < eventsCount ? eventCounter : -1
6818
+ eventIdx: isEvent && eventCounter < eventsCount ? eventCounter : -1
6815
6819
  });
6816
- eventCounter++;
6820
+ if (isEvent) eventCounter++;
6817
6821
  continue;
6818
6822
  }
6819
6823
  for (let ci = 0; ci < uniqueSplitAt.length; ci++) {
@@ -6822,19 +6826,20 @@ var buildDisplayLines = (chords, eventsCount) => {
6822
6826
  const end = ci < uniqueSplitAt.length - 1 ? uniqueSplitAt[ci + 1] : bar.length;
6823
6827
  const symbol = bar.slice(start, end).trim();
6824
6828
  if (symbol) {
6829
+ const isEvent = !NON_EVENT_RE.test(symbol);
6825
6830
  parts.push({
6826
6831
  text: symbol,
6827
6832
  isChord: true,
6828
- eventIdx: eventCounter < eventsCount ? eventCounter : -1
6833
+ eventIdx: isEvent && eventCounter < eventsCount ? eventCounter : -1
6829
6834
  });
6830
- eventCounter++;
6835
+ if (isEvent) eventCounter++;
6831
6836
  }
6832
6837
  }
6833
6838
  }
6834
6839
  if (parts.length > 0) {
6835
6840
  displayLines.push({
6836
6841
  type: "bar",
6837
- colorIdx: Math.max(0, colorIdx),
6842
+ colorIdx,
6838
6843
  parts
6839
6844
  });
6840
6845
  }
@@ -8768,6 +8773,18 @@ var MidiSearchClient = class {
8768
8773
  if (!res.ok) throw new Error(`picotune fetch failed: ${res.status}`);
8769
8774
  return res.arrayBuffer();
8770
8775
  }
8776
+ async searchRechord(word) {
8777
+ if (!this.enabled) return [];
8778
+ const qs = new URLSearchParams();
8779
+ qs.set("word", word);
8780
+ qs.set("guest", "true");
8781
+ const url2 = `${this.baseUrl}/rechord/scores?${qs.toString()}`;
8782
+ const res = await fetch(url2, { headers: this.headers() });
8783
+ if (!res.ok) throw new Error(`rechord search failed: ${res.status}`);
8784
+ const body = await res.json();
8785
+ const list = body.result ?? body.data ?? body;
8786
+ return Array.isArray(list) ? list : [];
8787
+ }
8771
8788
  };
8772
8789
 
8773
8790
  // src/linked-list.ts
@@ -11192,6 +11209,7 @@ var mountDAW = (target, options = {}) => {
11192
11209
  <div style="display: inline-flex; align-items: center; gap: 6px;">
11193
11210
  <span class="dtm-label">\u548C\u97F3</span>
11194
11211
  <button class="dtm-infobtn" data-dtm="chord-info" title="\u30B3\u30FC\u30C9\u9032\u884C\u306E\u66F8\u304D\u65B9\u89E3\u8AAC">${icon("info", 12)}</button>
11212
+ ${showMidiSearch ? `<button class="dtm-btn dtm-btn--ghost" data-dtm="chord-search" title="\u30B3\u30FC\u30C9\u9032\u884C\u691C\u7D22" style="min-height: 24px; min-width: auto; height: 24px; padding: 0 6px; font-size: 11px; display: inline-flex; align-items: center;">\u30B3\u30FC\u30C9\u691C\u7D22</button>` : ""}
11195
11213
  </div>
11196
11214
  <select class="dtm-select" data-dtm="chord-pattern">
11197
11215
  <option value="block">\u30D6\u30ED\u30C3\u30AF</option>
@@ -11227,6 +11245,14 @@ var mountDAW = (target, options = {}) => {
11227
11245
  save();
11228
11246
  applyChord();
11229
11247
  });
11248
+ const searchChordBtn = div.querySelector(
11249
+ '[data-dtm="chord-search"]'
11250
+ );
11251
+ if (searchChordBtn) {
11252
+ searchChordBtn.addEventListener("click", () => {
11253
+ openChordSearchModal(input);
11254
+ });
11255
+ }
11230
11256
  }
11231
11257
  };
11232
11258
  const switchTrack = (id) => {
@@ -12090,6 +12116,8 @@ var mountDAW = (target, options = {}) => {
12090
12116
  searchPreviewBtn = null;
12091
12117
  }
12092
12118
  };
12119
+ let mmlSearchCache = null;
12120
+ let chordSearchCache = null;
12093
12121
  const wireMidiSearch = () => {
12094
12122
  if (!midiSearchClient) return;
12095
12123
  refs.midiSearchOpenBtn.addEventListener("click", () => {
@@ -12100,6 +12128,7 @@ var mountDAW = (target, options = {}) => {
12100
12128
  <input type="text" class="dtm-input dtm-grow" data-dtm="modal-midi-search-input" placeholder="\u66F2\u540D\u3067MML\u691C\u7D22" style="min-width:0">
12101
12129
  <button class="dtm-btn dtm-btn--primary" data-dtm="modal-midi-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
12102
12130
  </div>
12131
+ <div data-dtm="modal-midi-search-status" style="font-size:11px;color:rgba(255,255,255,0.5);min-height:1.4em"></div>
12103
12132
  <div class="dtm-row" data-dtm="modal-midi-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
12104
12133
  `
12105
12134
  );
@@ -12109,9 +12138,17 @@ var mountDAW = (target, options = {}) => {
12109
12138
  const searchBtn = refs.modalBody.querySelector(
12110
12139
  '[data-dtm="modal-midi-search-btn"]'
12111
12140
  );
12141
+ const statusEl = refs.modalBody.querySelector(
12142
+ '[data-dtm="modal-midi-search-status"]'
12143
+ );
12112
12144
  const results = refs.modalBody.querySelector(
12113
12145
  '[data-dtm="modal-midi-search-results"]'
12114
12146
  );
12147
+ if (mmlSearchCache) {
12148
+ input.value = mmlSearchCache.query;
12149
+ statusEl.textContent = `\u300C${mmlSearchCache.query}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${mmlSearchCache.count}\u4EF6\u306E\u30D2\u30C3\u30C8`;
12150
+ mmlSearchCache.renderResults(results);
12151
+ }
12115
12152
  const runSearch = async () => {
12116
12153
  const q2 = input.value.trim();
12117
12154
  if (!q2) return;
@@ -12119,37 +12156,85 @@ var mountDAW = (target, options = {}) => {
12119
12156
  searchBtn.disabled = true;
12120
12157
  searchBtn.textContent = "\u691C\u7D22\u4E2D...";
12121
12158
  results.innerHTML = "";
12159
+ statusEl.textContent = "";
12122
12160
  try {
12123
12161
  const songs = await midiSearchClient.searchSongs({ title: q2 });
12124
12162
  if (songs.length === 0) {
12163
+ statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u30000\u4EF6\u306E\u30D2\u30C3\u30C8`;
12125
12164
  results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
12165
+ mmlSearchCache = {
12166
+ query: q2,
12167
+ count: 0,
12168
+ renderResults: (c) => {
12169
+ c.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
12170
+ }
12171
+ };
12126
12172
  return;
12127
12173
  }
12128
- for (const song of songs) {
12129
- const box = document.createElement("div");
12130
- box.className = "dtm-modal-sample-box";
12131
- const row = document.createElement("div");
12132
- row.className = "dtm-row";
12133
- row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
12134
- const label = document.createElement("span");
12135
- label.className = "dtm-label";
12136
- label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
12137
- label.textContent = `${song.title} - ${song.user}`;
12138
- const previewBtn = document.createElement("button");
12139
- previewBtn.className = "dtm-btn dtm-btn--ghost";
12140
- previewBtn.style.cssText = "flex-shrink:0";
12141
- previewBtn.textContent = "\u25B6 \u8A66\u8074";
12142
- const loadBtn = document.createElement("button");
12143
- loadBtn.className = "dtm-btn dtm-btn--primary";
12144
- loadBtn.style.cssText = "flex-shrink:0";
12145
- loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
12146
- const playerContainer = document.createElement("div");
12147
- playerContainer.className = "dtm-modal-sample-player-container";
12148
- previewBtn.addEventListener("click", async () => {
12149
- if (searchPreviewBtn === previewBtn) {
12150
- if (searchPreviewPlayer?.isPlaying()) {
12151
- searchPreviewPlayer.stop();
12174
+ statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${songs.length}\u4EF6\u306E\u30D2\u30C3\u30C8`;
12175
+ const renderMmlResults = (container) => {
12176
+ container.innerHTML = "";
12177
+ for (const song of songs) {
12178
+ const box = document.createElement("div");
12179
+ box.className = "dtm-modal-sample-box";
12180
+ const row = document.createElement("div");
12181
+ row.className = "dtm-row";
12182
+ row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
12183
+ const label = document.createElement("span");
12184
+ label.className = "dtm-label";
12185
+ label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
12186
+ label.textContent = `${song.title} - ${song.user}`;
12187
+ const previewBtn = document.createElement("button");
12188
+ previewBtn.className = "dtm-btn dtm-btn--ghost";
12189
+ previewBtn.style.cssText = "flex-shrink:0";
12190
+ previewBtn.textContent = "\u25B6 \u8A66\u8074";
12191
+ const loadBtn = document.createElement("button");
12192
+ loadBtn.className = "dtm-btn dtm-btn--primary";
12193
+ loadBtn.style.cssText = "flex-shrink:0";
12194
+ loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
12195
+ const playerContainer = document.createElement("div");
12196
+ playerContainer.className = "dtm-modal-sample-player-container";
12197
+ previewBtn.addEventListener("click", async () => {
12198
+ if (searchPreviewBtn === previewBtn) {
12199
+ if (searchPreviewPlayer?.isPlaying()) {
12200
+ searchPreviewPlayer.stop();
12201
+ } else {
12202
+ stop();
12203
+ previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
12204
+ try {
12205
+ const midi = await fetchVerifiedMidi(song.file);
12206
+ const mml = buildPreviewMml(midi);
12207
+ playerContainer.innerHTML = "";
12208
+ const player = mountMmlPlayer(playerContainer, mml, {
12209
+ onPlayNote: options.onPlayNote,
12210
+ onPlayDrum: options.onPlayDrum,
12211
+ onResumeAudio: options.onResumeAudio,
12212
+ getAudioTime: options.getAudioTime,
12213
+ singingVoices: options.singingVoices,
12214
+ drumPatterns: options.drumPatterns,
12215
+ volume: masterVolume,
12216
+ _skipInfoModals: true,
12217
+ onStop: () => {
12218
+ if (searchPreviewBtn === previewBtn) {
12219
+ previewBtn.textContent = "\u25B6 \u8A66\u8074";
12220
+ previewBtn.classList.remove("dtm-btn--danger");
12221
+ previewBtn.classList.add("dtm-btn--ghost");
12222
+ }
12223
+ }
12224
+ });
12225
+ searchPreviewPlayer = player;
12226
+ searchPreviewBtn = previewBtn;
12227
+ player.play();
12228
+ previewBtn.textContent = "\u25A0 \u505C\u6B62";
12229
+ previewBtn.classList.remove("dtm-btn--ghost");
12230
+ previewBtn.classList.add("dtm-btn--danger");
12231
+ } catch (e) {
12232
+ console.error("[dtm] MIDI preview failed", e);
12233
+ previewBtn.textContent = "\u5931\u6557";
12234
+ }
12235
+ }
12152
12236
  } else {
12237
+ collapseSearchPreview();
12153
12238
  stop();
12154
12239
  previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
12155
12240
  try {
@@ -12184,31 +12269,169 @@ var mountDAW = (target, options = {}) => {
12184
12269
  previewBtn.textContent = "\u5931\u6557";
12185
12270
  }
12186
12271
  }
12272
+ });
12273
+ loadBtn.addEventListener("click", async () => {
12274
+ loadBtn.disabled = true;
12275
+ loadBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
12276
+ try {
12277
+ const pending = await fetchVerifiedMidi(song.file);
12278
+ const analysis = analyzeMidiTracks(pending);
12279
+ const sel = analysis.filter((a) => a.selected).map((a) => a.index);
12280
+ collapseSearchPreview();
12281
+ overlayDuring(() => applyMidiSelection(pending, sel));
12282
+ refs.modalOverlay.setAttribute("hidden", "");
12283
+ } catch (e) {
12284
+ console.error("[dtm] MIDI fetch failed", e);
12285
+ loadBtn.textContent = "\u5931\u6557";
12286
+ } finally {
12287
+ loadBtn.disabled = false;
12288
+ }
12289
+ });
12290
+ row.appendChild(label);
12291
+ row.appendChild(previewBtn);
12292
+ row.appendChild(loadBtn);
12293
+ box.appendChild(row);
12294
+ box.appendChild(playerContainer);
12295
+ container.appendChild(box);
12296
+ }
12297
+ };
12298
+ renderMmlResults(results);
12299
+ mmlSearchCache = {
12300
+ query: q2,
12301
+ count: songs.length,
12302
+ renderResults: renderMmlResults
12303
+ };
12304
+ } catch (e) {
12305
+ console.error("[dtm] MIDI search failed", e);
12306
+ results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
12307
+ } finally {
12308
+ searchBtn.disabled = false;
12309
+ searchBtn.textContent = "\u691C\u7D22";
12310
+ }
12311
+ };
12312
+ searchBtn.addEventListener("click", () => void runSearch());
12313
+ input.addEventListener("keydown", (e) => {
12314
+ if (e.key === "Enter") void runSearch();
12315
+ });
12316
+ input.focus();
12317
+ });
12318
+ };
12319
+ const openChordSearchModal = (chordTextArea) => {
12320
+ if (!midiSearchClient) return;
12321
+ const active = trackStates.find((t) => t.config.id === activeTrackId);
12322
+ if (!active) return;
12323
+ showModal(
12324
+ "\u30B3\u30FC\u30C9\u9032\u884C\u691C\u7D22",
12325
+ `
12326
+ <div class="dtm-row" style="flex-wrap:nowrap">
12327
+ <input type="text" class="dtm-input dtm-grow" data-dtm="modal-chord-search-input" placeholder="\u66F2\u540D\u3084\u30B3\u30FC\u30C9\u9032\u884C\u3067\u691C\u7D22" style="min-width:0">
12328
+ <button class="dtm-btn dtm-btn--primary" data-dtm="modal-chord-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
12329
+ </div>
12330
+ <div data-dtm="modal-chord-search-status" style="font-size:11px;color:rgba(255,255,255,0.5);min-height:1.4em"></div>
12331
+ <div class="dtm-row" data-dtm="modal-chord-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
12332
+ `
12333
+ );
12334
+ const input = refs.modalBody.querySelector(
12335
+ '[data-dtm="modal-chord-search-input"]'
12336
+ );
12337
+ const searchBtn = refs.modalBody.querySelector(
12338
+ '[data-dtm="modal-chord-search-btn"]'
12339
+ );
12340
+ const statusEl = refs.modalBody.querySelector(
12341
+ '[data-dtm="modal-chord-search-status"]'
12342
+ );
12343
+ const results = refs.modalBody.querySelector(
12344
+ '[data-dtm="modal-chord-search-results"]'
12345
+ );
12346
+ if (chordSearchCache) {
12347
+ input.value = chordSearchCache.query;
12348
+ statusEl.textContent = `\u300C${chordSearchCache.query}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${chordSearchCache.count}\u4EF6\u306E\u30D2\u30C3\u30C8`;
12349
+ chordSearchCache.renderResults(results);
12350
+ }
12351
+ const runSearch = async () => {
12352
+ const q2 = input.value.trim();
12353
+ if (!q2) return;
12354
+ collapseSearchPreview();
12355
+ searchBtn.disabled = true;
12356
+ searchBtn.textContent = "\u691C\u7D22\u4E2D...";
12357
+ results.innerHTML = "";
12358
+ statusEl.textContent = "";
12359
+ try {
12360
+ const scores = await midiSearchClient.searchRechord(q2);
12361
+ if (scores.length === 0) {
12362
+ statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u30000\u4EF6\u306E\u30D2\u30C3\u30C8`;
12363
+ results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
12364
+ chordSearchCache = {
12365
+ query: q2,
12366
+ count: 0,
12367
+ renderResults: (c) => {
12368
+ c.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
12369
+ }
12370
+ };
12371
+ return;
12372
+ }
12373
+ statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${scores.length}\u4EF6\u306E\u30D2\u30C3\u30C8`;
12374
+ const renderChordResults = (container) => {
12375
+ container.innerHTML = "";
12376
+ for (const score of scores) {
12377
+ const box = document.createElement("div");
12378
+ box.className = "dtm-modal-sample-box";
12379
+ const row = document.createElement("div");
12380
+ row.className = "dtm-row";
12381
+ row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
12382
+ const label = document.createElement("span");
12383
+ label.className = "dtm-label";
12384
+ label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
12385
+ label.textContent = `${score.title}${score.user?.screen_name ? ` - ${score.user.screen_name}` : ""}`;
12386
+ const previewBtn = document.createElement("button");
12387
+ previewBtn.className = "dtm-btn dtm-btn--ghost";
12388
+ previewBtn.style.cssText = "flex-shrink:0";
12389
+ previewBtn.textContent = "\u25B6 \u8A66\u8074";
12390
+ const loadBtn = document.createElement("button");
12391
+ loadBtn.className = "dtm-btn dtm-btn--primary";
12392
+ loadBtn.style.cssText = "flex-shrink:0";
12393
+ loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
12394
+ const playerContainer = document.createElement("div");
12395
+ playerContainer.className = "dtm-modal-sample-player-container";
12396
+ previewBtn.addEventListener("click", async () => {
12397
+ if (searchPreviewBtn === previewBtn) {
12398
+ if (searchPreviewPlayer?.isPlaying()) {
12399
+ searchPreviewPlayer.stop();
12400
+ } else {
12401
+ stop();
12402
+ previewBtn.textContent = "\u518D\u751F\u4E2D...";
12403
+ try {
12404
+ searchPreviewPlayer?.play();
12405
+ previewBtn.textContent = "\u25A0 \u505C\u6B62";
12406
+ previewBtn.classList.remove("dtm-btn--ghost");
12407
+ previewBtn.classList.add("dtm-btn--danger");
12408
+ } catch (e) {
12409
+ console.error("[dtm] Chord preview play failed", e);
12410
+ previewBtn.textContent = "\u5931\u6557";
12411
+ }
12412
+ }
12187
12413
  } else {
12188
12414
  collapseSearchPreview();
12189
12415
  stop();
12190
12416
  previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
12191
12417
  try {
12192
- const midi = await fetchVerifiedMidi(song.file);
12193
- const mml = buildPreviewMml(midi);
12194
12418
  playerContainer.innerHTML = "";
12195
- const player = mountMmlPlayer(playerContainer, mml, {
12196
- onPlayNote: options.onPlayNote,
12197
- onPlayDrum: options.onPlayDrum,
12198
- onResumeAudio: options.onResumeAudio,
12199
- getAudioTime: options.getAudioTime,
12200
- singingVoices: options.singingVoices,
12201
- drumPatterns: options.drumPatterns,
12202
- volume: masterVolume,
12203
- _skipInfoModals: true,
12204
- onStop: () => {
12205
- if (searchPreviewBtn === previewBtn) {
12206
- previewBtn.textContent = "\u25B6 \u8A66\u8074";
12207
- previewBtn.classList.remove("dtm-btn--danger");
12208
- previewBtn.classList.add("dtm-btn--ghost");
12419
+ const player = mountChordPlayer(
12420
+ playerContainer,
12421
+ score.content,
12422
+ {
12423
+ volume: masterVolume,
12424
+ bpm: score.bpm ?? bpm ?? 120,
12425
+ _skipInfoModals: true,
12426
+ onStop: () => {
12427
+ if (searchPreviewBtn === previewBtn) {
12428
+ previewBtn.textContent = "\u25B6 \u8A66\u8074";
12429
+ previewBtn.classList.remove("dtm-btn--danger");
12430
+ previewBtn.classList.add("dtm-btn--ghost");
12431
+ }
12209
12432
  }
12210
12433
  }
12211
- });
12434
+ );
12212
12435
  searchPreviewPlayer = player;
12213
12436
  searchPreviewBtn = previewBtn;
12214
12437
  player.play();
@@ -12216,49 +12439,48 @@ var mountDAW = (target, options = {}) => {
12216
12439
  previewBtn.classList.remove("dtm-btn--ghost");
12217
12440
  previewBtn.classList.add("dtm-btn--danger");
12218
12441
  } catch (e) {
12219
- console.error("[dtm] MIDI preview failed", e);
12442
+ console.error("[dtm] Chord preview mount failed", e);
12220
12443
  previewBtn.textContent = "\u5931\u6557";
12221
12444
  }
12222
12445
  }
12223
12446
  });
12224
- loadBtn.addEventListener("click", async () => {
12225
- loadBtn.disabled = true;
12226
- loadBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
12227
- try {
12228
- const pending = await fetchVerifiedMidi(song.file);
12229
- const analysis = analyzeMidiTracks(pending);
12230
- const sel = analysis.filter((a) => a.selected).map((a) => a.index);
12231
- collapseSearchPreview();
12232
- overlayDuring(() => applyMidiSelection(pending, sel));
12233
- refs.modalOverlay.setAttribute("hidden", "");
12234
- } catch (e) {
12235
- console.error("[dtm] MIDI fetch failed", e);
12236
- loadBtn.textContent = "\u5931\u6557";
12237
- } finally {
12238
- loadBtn.disabled = false;
12447
+ loadBtn.addEventListener("click", () => {
12448
+ collapseSearchPreview();
12449
+ chordTextArea.value = score.content;
12450
+ active.savedChordInput = score.content;
12451
+ if (score.bpm) {
12452
+ setBpm(score.bpm);
12239
12453
  }
12454
+ applyChord();
12455
+ refs.modalOverlay.setAttribute("hidden", "");
12240
12456
  });
12241
12457
  row.appendChild(label);
12242
12458
  row.appendChild(previewBtn);
12243
12459
  row.appendChild(loadBtn);
12244
12460
  box.appendChild(row);
12245
12461
  box.appendChild(playerContainer);
12246
- results.appendChild(box);
12462
+ container.appendChild(box);
12247
12463
  }
12248
- } catch (e) {
12249
- console.error("[dtm] MIDI search failed", e);
12250
- results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
12251
- } finally {
12252
- searchBtn.disabled = false;
12253
- searchBtn.textContent = "\u691C\u7D22";
12254
- }
12255
- };
12256
- searchBtn.addEventListener("click", () => void runSearch());
12257
- input.addEventListener("keydown", (e) => {
12258
- if (e.key === "Enter") void runSearch();
12259
- });
12260
- input.focus();
12464
+ };
12465
+ renderChordResults(results);
12466
+ chordSearchCache = {
12467
+ query: q2,
12468
+ count: scores.length,
12469
+ renderResults: renderChordResults
12470
+ };
12471
+ } catch (e) {
12472
+ console.error("[dtm] Chord search failed", e);
12473
+ results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
12474
+ } finally {
12475
+ searchBtn.disabled = false;
12476
+ searchBtn.textContent = "\u691C\u7D22";
12477
+ }
12478
+ };
12479
+ searchBtn.addEventListener("click", () => void runSearch());
12480
+ input.addEventListener("keydown", (e) => {
12481
+ if (e.key === "Enter") void runSearch();
12261
12482
  });
12483
+ input.focus();
12262
12484
  };
12263
12485
  const onKeyDown = (e) => {
12264
12486
  if (!(e.ctrlKey || e.metaKey)) return;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "onjmin",
3
3
  "license": "MIT",
4
4
  "name": "@onjmin/dtm",
5
- "version": "0.1.57",
5
+ "version": "0.1.58",
6
6
  "description": "MMLを中間言語に用いた、モバイルファーストなDAW / ピアノロール打ち込みコンポーネント",
7
7
  "homepage": "https://onjmin.github.io/dtm",
8
8
  "repository": {
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "2.2.3",
46
- "@discord/embedded-app-sdk": "2.5.0",
47
46
  "@hono/node-server": "1.19.5",
47
+ "@types/node": "26.1.1",
48
48
  "esbuild": "0.28.1",
49
49
  "hono": "4.10.1",
50
50
  "rimraf": "6.0.1",