App ShareSheetのURL貼り付けをリンクとして処理する

basecamp/lexxy

クリップボードに text/uri-list のみが含まれるApp ShareSheet由来のURLを、リンクとして正しく処理できるようになりました。これまで Safari の挙動(text/plain + text/uri-list の組み合わせ)のみを想定していた判定ロジックが、単独の text/uri-list にも対応します。

背景

iOSなどのApp ShareSheetからURLを共有する場合、クリップボードには text/uri-list のみが格納されます。一方、SafariでURLをコピーした場合は text/plaintext/uri-list の2つのMIMEタイプが組み合わされます。

Lexxyの #isOnlyURLPasted メソッドは「URLのみが貼り付けられた」ことを検出し、リンク要素を生成する判定に使われます。しかしこのメソッドは types.length === 2 かつ両タイプが揃っていることを必須条件としていたため、App ShareSheetから貼り付けた場合は条件を満たさず、URLがプレーンテキストとして扱われるか、リンクへの変換がスキップされていました。

技術的な変更

#isOnlyURLPasted の判定条件を緩和し、text/uri-list 単独のケースも受け入れるよう変更されました。

変更前:

#isOnlyURLPasted(clipboardData) {
  // Safari URLs are copied as a text/plain + text/uri-list object
  const types = Array.from(clipboardData.types)
  return types.length === 2 && types.includes("text/uri-list") && types.includes("text/plain")
}

変更後:

#isOnlyURLPasted(clipboardData) {
  // Safari URLs are copied as a text/plain + text/uri-list object
  // App ShareSheet URLs are copied as solo text/uri-list object
  const types = Array.from(clipboardData.types)
  return types.length
    && types.length <= 2
    && types.includes("text/uri-list")
    && (types.length < 2 || types.includes("text/plain"))
}

新しい条件は次の3つの要件を表現しています:

  • types.length: タイプが1つ以上存在する(空のクリップボードを除外)
  • types.length <= 2: タイプが最大2つ(URL以外のリッチデータを含む貼り付けを除外)
  • types.includes("text/uri-list") かつ types.length < 2 || types.includes("text/plain"): text/uri-list が必須、2つ目のタイプが存在する場合は text/plain でなければならない

テストヘルパー EditorHandle.paste も同様に拡張されました。uriList オプションが追加され、text パラメータが null の場合でも text/plain のセットをスキップしながら text/uri-list のみをクリップボードに設定できるようになっています。

async paste(text, { html, files = [], uriList } = {}) {
  // ...
  if (typeof text === "string") event.clipboardData.setData("text/plain", text)
  if (html) event.clipboardData.setData("text/html", html)
  if (uriList) event.clipboardData.setData("text/uri-list", uriList)
}

設計判断

=== 2 の厳格な等値比較を <= 2 の上限チェックへ変換するアプローチが採用されました。これにより、タイプ数を「ちょうど2つ」から「1つ以上2つ以下」へ緩和しつつ、3つ以上のMIMEタイプを持つリッチなクリップボードデータ(例:text/html を伴うコピー)をリンク変換の対象外に保つ安全ネットを維持しています。

2つ目のタイプに対する条件 (types.length < 2 || types.includes("text/plain")) は短絡評価を利用した記述で、単独 text/uri-listlength < 2)はそのまま通し、2タイプの場合のみ text/plain の存在を要求します。条件の追加を最小限に抑えながら既存のSafari対応を壊さない実装といえます。

まとめ

#isOnlyURLPasted の判定条件を数行変更するだけで、App ShareSheetというモバイル起点のURL共有シナリオへの対応が実現されました。types.length === 2 という暗黙の前提をより明示的な上限・組み合わせ条件に置き換えることで、既存のSafari挙動との互換性を保ちつつ、クリップボードの多様なMIMEタイプ組み合わせに対して堅牢なコードになっています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
6c5b8913

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文→背景→技術的な変更→設計判断→まとめ」という総論→各論→結論の構成が明確に守られています。各セクションの役割が果たされており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きのシンタックスハイライト(```javascript:filepath```)が正しく使用されています。PRへのリンクも適切に記述されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

クリップボードのMIMEタイプやJavaScriptの判定ロジックなど、専門知識を持つエンジニアを対象とした適切な技術レベルで記述されています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションの冒頭に要旨が述べられ、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。段落の長さも適切で可読性が高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiff情報を正確に反映しています。変更前後のコード比較が的確で、変更点を正しくハイライトできています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`text/uri-list`、`MIMEタイプ`、`clipboardData`などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

コード変更の理由と、新しい判定ロジック(`types.length <= 2`など)に関する説明が技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張はすべてPRのDescriptionやDiff内のコードコメント、コード変更内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#1054)が正確に記載・リンクされています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRの主題である「単独の`text/uri-list`をリンクとして扱う」という内容を、より具体的なユースケース(App ShareSheet)に落とし込んでおり、的確です。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PRに明記されていない外部知識の追加(バージョンのサポート状況、リリース日程など)は見られませんでした。「iOS」という言及は「App ShareSheet」からの妥当な文脈補足であり、許容範囲です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲は見られず、PRの内容と事実関係が一致しています。