Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: コピペでシーケンサでのペーストの場合はノート情報・一般ペーストの場合は歌詞のみにする #2615

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

romot-co
Copy link
Contributor

内容

ref: #2591

ソングにおいて以下の形で
ノートをコピーしてからシーケンサ以外にペーストするとノートのJSONが貼り付けられてしまう問題を修正します。

  • ノートコピー時にカスタムMIMEタイプweb application/vnd.voicevox.song-notes`でノート構造(JSON)をコピーします
  • ノートコピー時にtext/plain形式で歌詞のみをコピーします
  • シーケンサへのノートペースト時はweb application/vnd.voicevox.song-notesを利用します
  • ノート歌詞入力など一般のペースト時にはtext/plain側が利用されます

関連 Issue

ref #2591
close #2591

スクリーンショット・動画など

その他

クリップボードAPIの制限
https://developer.mozilla.org/ja/docs/Web/API/Clipboard_API#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81%AE%E8%80%83%E6%85%AE

Chrome 104以降でカスタムMIMEタイプが使えるAsync Clipboard API
https://developer.chrome.com/blog/web-custom-formats-for-the-async-clipboard-api?hl=ja

@romot-co romot-co requested a review from a team as a code owner March 15, 2025 11:10
@romot-co romot-co requested review from Hiroshiba and removed request for a team March 15, 2025 11:10
@voicevox-preview-pages
Copy link

voicevox-preview-pages bot commented Mar 15, 2025

🚀 プレビュー用ページを作成しました 🚀

更新時点でのコミットハッシュ:5c39b4a

@romot-co romot-co changed the title [WIP] fix: コピペでシーケンサでのペーストの場合はノート情報・一般ペーストの場合は歌詞のみにする fix: コピペでシーケンサでのペーストの場合はノート情報・一般ペーストの場合は歌詞のみにする Mar 15, 2025
@Hiroshiba
Copy link
Member

PRありがとうございます!!

もしよければ @terapotan さんもコメントいただけると!!
(プルリクエストをcheckoutするか、こちらのブラウザ版で動作確認できると思います)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an issue where copying notes from a song resulted in pasting the entire note JSON when pasted outside of the sequencer, by separating out the data formats for sequencer paste (note JSON) and general paste (lyrics text only). Key changes include:

  • Serializing notes as JSON and combining lyric text for the clipboard.
  • Using a custom MIME type "web application/vnd.voicevox.song-notes" for sequencer paste while providing a plain text fallback.
  • Implementing a fallback mechanism for browsers that do not support custom MIME types.
Comments suppressed due to low confidence (1)

src/store/singing.ts:3289

  • Consider adding a feature detection or fallback for navigator.clipboard.read to improve compatibility with browsers that do not support this asynchronous Clipboard API method.
const clipboardItems = await navigator.clipboard.read();
Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ははーーーーーー なるほど、webのクリップボードはこういう感じになってるんですねぇ!!!!
たまに変な挙動するな〜と感じることがあるのですが、こりゃしょうがないわって気持ちになりました!!! ややこしすぎる!!!

主にコード整理周りでコメントしました!

@@ -3228,12 +3228,47 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
const { id, ...noteWithoutId } = note;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(この行に関係ないコメントです、議論を分けたかったので)

ちょっと提案なのですが、クリップボード周りの処理を関数に切り出してファイルをまとめるのはどうでしょうか 👀
コンテキストが特殊なのと、定数などがいっぱいあるので少なくとも外に出したほうがメンテしやすそうだな〜と。

storeは関数抜き出しするの面倒だったのですが、同名のディレクトリ以下に配置すれば綺麗に移行できることに気付きました!
やることは単純で、

  • 新しくstore/singing/ディレクトリを作って、
  • store/singing.tsstore/singing/index.tsに移動して
  • 切り出した関数をstore/singing/clipboard.tsなどに配置する

感じです!実例だとこんな感じ
切り出した関数は第1引数でcontext: ActionContextを受け取って、こんな感じで渡すとか・・・!
(もちろんstateとかactionsとかだけ渡すのもOKだと思います)

説明テキストとか、MIME Type定数とか、移してまとめられると思うのでキレイになるんじゃないかな〜みたいな!

型とかだいぶパズルになっててややこしいと思うのでなんでも聞いて下さい!
なんなら残しといていただければこっちでちゃちゃっとやっちゃいます 🙏
もしよかったら!!

Comment on lines 3243 to 3244
// ノートコピー&ペースト用のカスタムMIMEタイプ
const customMimeType = "web application/vnd.voicevox.song-notes";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これはペースト時も共通だから外に出したいかも

Comment on lines 3262 to 3265
try {
await navigator.clipboard.writeText(serializedNotes);
logger.info("Fallback to plain text.", e);
logger.info("Copied to clipboard as plain text", serializedNotes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ最近気づいたのですが、エラーになる処理の前にロガー書かないと表示されないんですよね 😇
なのでこうかも?

Suggested change
try {
await navigator.clipboard.writeText(serializedNotes);
logger.info("Fallback to plain text.", e);
logger.info("Copied to clipboard as plain text", serializedNotes);
try {
logger.info("Fallback to plain text.", e);
await navigator.clipboard.writeText(serializedNotes);
logger.info("Copied to clipboard as plain text", serializedNotes);

Comment on lines 3293 to 3310
for (const item of clipboardItems) {
if (item.types.includes(customMimeType)) {
const blob = await item.getType(customMimeType);
clipboardText = await blob.text();
break;
}
}

// カスタムMIMEタイプが見つからない場合は通常のテキストを試す
if (!clipboardText) {
for (const item of clipboardItems) {
if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");
clipboardText = await blob.text();
break;
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(ただのコメントです)

処理がにてるから関数切り出しできそう!!

@terapotan
Copy link
Contributor

@romot-co
PRありがとうございます! 動作を見た感じ良さそうでした!!
一点軽くコメントしたので、そちらだけご確認いただければ!

// カスタムMIMEタイプのデータを読み込む
const customMimeType = "web application/vnd.voicevox.song-notes";
// クリップボードアイテムからカスタムMIMEタイプのデータを探す
for (const item of clipboardItems) {
Copy link
Contributor

@terapotan terapotan Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下のような感じで、メソッドチェインにしてあげると分かりやすいかも?
(好みの問題な気もしますが)

const clipboardText = await Promise.any(
  clipboardItems
    .filter(item => item.types.includes("text/plain"))
    .map(item => item.getType("text/plain").then(blob => blob.text()))
) || null;

@romot-co romot-co requested a review from Copilot March 26, 2025 15:15
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an issue where, when pasting notes into non‐sequencer contexts, the wrong clipboard data would be pasted. Key changes include adding new clipboard helper functions to copy and read notes with custom MIME types, refactoring the store actions to use these helper functions, and adjusting import paths for consistency.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/store/singing/clipboardHelper.ts New helper functions to handle note copying and pasting with custom MIME types
src/store/singing/index.ts Refactored clipboard actions to delegate to the new helper functions and updated import paths

} catch {
// カスタムMIMEタイプを利用してのコピーが失敗した場合、クリップボードに通常のテキスト形式で書き込みを試みる
try {
await navigator.clipboard.writeText(jsonVoicevoxNotes);
Copy link
Preview

Copilot AI Mar 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When custom MIME type write fails, the fallback writes the JSON note structure instead of plain text lyrics. This may cause unexpected behavior for general paste actions; consider using plainTextLyric as the fallback value.

Suggested change
await navigator.clipboard.writeText(jsonVoicevoxNotes);
await navigator.clipboard.writeText(plainTextLyric);

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants