プロンプト/メンション補完システムの入力ラグを修正

basecamp/lexxy

チャット入力時のオートコンプリートで発生していた入力ラグを、デバウンス導入とレンダリング件数の上限設定という2つのアプローチで解消しました。

背景

キャンプファイアのチャット入力において、プロンプト/メンション補完システムが顕著な入力ラグを引き起こしていました。特にトリガー文字(@など)を入力した直後の最初の数文字でラグが目立ち、UX上の問題となっていました。原因は独立した2つの設計上の問題でした。

1つ目はデバウンスの欠如です。キーストロークのたびに lexxy:change イベントリスナー経由でフィルタリングとDOM描画のパイプライン全体が実行されていました。2つ目はレンダリング件数の無制限です。@ 単体のような広い検索クエリを入力すると、大規模アカウントでは数百件の候補要素がDOMに書き込まれ、レンダリングコストが高騰していました。

技術的な変更

2つの問題に対してそれぞれ独立した修正が加えられ、合わせて新しいパフォーマンステストが追加されています。

デバウンスの導入

src/helpers/timing_helpers.js に同期版の debounce 関数が追加されました。既存の debounceAsync とは別に、通常の関数をラップする実装です。

export function debounce(fn, wait) {
  let timeout

  return (...args) => {
    clearTimeout(timeout)
    timeout = setTimeout(() => fn(...args), wait)
  }
}

src/elements/prompt.js では、#filterOptions を直接イベントリスナーに渡す代わりに、50ms でデバウンスされた #debouncedFilterOptions を生成してリスナーに登録するよう変更されました。

// 変更前
registerEventListener(this.#editorElement, "lexxy:change", this.#filterOptions)

// 変更後
const FILTER_DEBOUNCE_INTERVAL = 50
#debouncedFilterOptions = debounce(() => this.#filterOptions(), FILTER_DEBOUNCE_INTERVAL)

registerEventListener(this.#editorElement, "lexxy:change", this.#debouncedFilterOptions)

なお、トリガー文字を入力した際のポップオーバーの初回表示は即時のままで、デバウンスの対象は「入力中のフィルタリング」のみです。

レンダリング件数の上限設定

LocalFilterSourceRemoteFilterSource の両方に MAX_RENDERED_SUGGESTIONS = 100 が定義され、候補要素の生成を100件でキャップするよう変更されました。LocalFilterSource では forEach ループを for...of に書き換え、上限に達した時点で break するようになっています。

// 変更前
promptItems.forEach((promptItem) => {
  const searchableText = promptItem.getAttribute("search")
  if (!filter || filterMatches(searchableText, filter)) {
    const listItem = this.buildListItemElementFor(promptItem)
    this.promptItemByListItem.set(listItem, promptItem)
    listItems.push(listItem)
  }
})

// 変更後
const MAX_RENDERED_SUGGESTIONS = 100

for (const promptItem of promptItems) {
  if (listItems.length >= MAX_RENDERED_SUGGESTIONS) break

  const searchableText = promptItem.getAttribute("search")
  if (!filter || filterMatches(searchableText, filter)) {
    const listItem = this.buildListItemElementFor(promptItem)
    this.promptItemByListItem.set(listItem, promptItem)
    listItems.push(listItem)
  }
}

パフォーマンステストの追加

test/browser/fixtures/mentions-large.html(200件以上のメンション候補を含む大規模フィクスチャ)と test/browser/tests/prompts/prompt_performance.test.js が追加されました。テストは「@ 入力時に100件以下に抑えられること」と「フィルタ入力でさらに絞り込まれること」の2ケースをPlaywrightで検証しています。

設計判断

デバウンス間隔の分離が注目に値します。RemoteFilterSource が既に持っていた DEBOUNCE_INTERVAL = 200ms とは別に、prompt.js 側には FILTER_DEBOUNCE_INTERVAL = 50ms が設定されています。リモートリクエストの抑制(200ms)とDOM更新サイクルの抑制(50ms)を目的に応じて分離することで、応答性とネットワーク効率をそれぞれ独立してチューニングできる設計になっています。

同期版 debounce の独立追加についても、既存の debounceAsync を使い回さず新関数として実装したことに設計の意図が見えます。非同期フィルタリングパイプラインとイベントリスナーの用途の違いを明示し、各関数の責務を明確に保っています。

MAX_RENDERED_SUGGESTIONS の定数化は、ローカルとリモートで同じ値(100)を各ファイル内に定義する形を取っています。共通定数として一元管理するのではなく、ソースの実装ごとに定義を持たせることで、将来的にソース種別ごとに上限値を変更する際の柔軟性を確保しています。

まとめ

本PRは、デバウンスによるイベント発火の間引きとレンダリング件数の上限設定という、相互に独立した2つの施策を組み合わせて入力ラグを解消しています。どちらの変更も既存の補完機能の正確性や動作フローには手を加えず、パフォーマンス劣化の経路のみを外科的に塞いだ設計といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
e739dbe8

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という3部構成が明確に適用されており、ガイドラインに完全に準拠しています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(例: ```javascript:src/helpers/timing_helpers.js)およびGitHubのPRリンク記法([PR #968](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

デバウンス、DOMレンダリング、イベントリスナーといった専門用語が適切に使用されており、対象読者であるエンジニアに適した技術レベルと表現です。

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

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

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。非常に読みやすい構成です。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiffの内容と完全に一致しています。ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「デバウンス」「フィクスチャ」「Playwright」などの技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

入力ラグの原因や、デバウンスとレンダリング上限設定による解決策の説明は、PR情報とDiffによって裏付けられており、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescriptionやDiff内のコードで裏付けられており、ハルシネーションは検出されませんでした。「設計判断」セクションはPRに明記されていませんが、コードから読み取れる妥当な分析であり、捏造ではありません。

数値・固有名詞の確認 ⚠ WARNING

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

記事に記載されたレンダリング上限数(100件)はDiffのコードと一致していますが、PRのDescriptionに記載された数値(50件)とは異なっています。記事は技術的に正しいですが、参照元との不一致が存在します。

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

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

記事のタイトル「プロンプト/メンション補完システムの入力ラグを修正」は、PRのタイトルと内容を正確に反映しています。

外部知識の正確性 ✓ PASS

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

PR情報に記載のないバージョンサポート状況やリリース日程などの外部知識は含まれておらず、事実に忠実です。

時間表現の正確性 ✓ PASS

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

「発生していた」「解消しました」など、過去の事象として正しく記述されており、PRの時間表現と一致しています。