訊息氣泡 (Message Bubble)

  • 新提案
  • 封閉測試
  • 公開測試
  • 已穩定
成熟度說明
  • 新提案:未完成開發、請勿使用。
  • 封閉測試:開發暫時性完成,可使用。可能仍有親和力或其他使用上問題待透過實測發現。
  • 公開測試:開發暫時性完成,歡迎使用。具有完整親和力報告。
  • 已穩定:開發完成、歡迎使用。應無任何親和力或使用上問題。

基本氣泡

請問如何申請自然人憑證?

客服助理

您好!請攜帶國民身分證正本與健保卡,親至任一戶政事務所申請,工本費為新臺幣 250 元。
HTML
<!-- 即時宣告元件:全站僅需一個,供訊息氣泡的複製等操作播報使用 -->
<live-announcer></live-announcer>

<message-bubble variant="user">
  <article class="message-bubble__inner message-bubble__inner--user" aria-labelledby="mb-basic-user-role">
    <p class="message-bubble__role" id="mb-basic-user-role">你</p>
    <div class="message-bubble__slot-pre" hidden></div>
    <div class="message-bubble__body">請問如何申請自然人憑證?</div>
  </article>
</message-bubble>
<message-bubble variant="ai">
  <article class="message-bubble__inner message-bubble__inner--ai" aria-labelledby="mb-basic-ai-role">
    <p class="message-bubble__role" id="mb-basic-ai-role">客服助理</p>
    <div class="message-bubble__slot-pre" hidden></div>
    <div class="message-bubble__body">您好!請攜帶國民身分證正本與健保卡,親至任一戶政事務所申請,工本費為新臺幣 250 元。</div>
    <p class="message-bubble__error" hidden></p>
    <!-- 操作列預設 hidden:JS 啟用後才顯示(無 JS 時讚/倒讚等維持隱藏) -->
    <div class="toolbar message-bubble__toolbar" role="toolbar" aria-label="訊息操作" hidden>
      <button type="button" class="toolbar__button" data-action="copy" tabindex="0">
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
          <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
        </svg>
        <span class="visually-hidden">複製</span>
      </button>
      <button type="button" class="toolbar__button" data-action="regenerate" tabindex="-1">
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <polyline points="23 4 23 10 17 10"/>
          <polyline points="1 20 1 14 7 14"/>
          <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
        </svg>
        <span class="visually-hidden">重新生成</span>
      </button>
      <button type="button" class="toolbar__button toolbar__button--toggle" data-action="like" aria-pressed="false" tabindex="-1">
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/>
        </svg>
        <span class="visually-hidden">讚</span>
      </button>
      <button type="button" class="toolbar__button toolbar__button--toggle" data-action="dislike" aria-pressed="false" tabindex="-1">
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"/>
        </svg>
        <span class="visually-hidden">倒讚</span>
      </button>
    </div>
  </article>
</message-bubble>
  • 角色標示為可見文字(「你」「客服助理」),<article>aria-labelledby 指向它。
  • AI 氣泡含操作列:複製、重新生成、讚、倒讚。

模擬串流回覆

已宣告內容

  1. 尚未有任何宣告
HTML
<!-- 即時宣告元件:全站僅需一個 -->
<live-announcer></live-announcer>

<!-- 等待回覆時顯示的輸入中提示,初始 hidden -->
<div class="typing-indicator" data-typing-indicator aria-hidden="true" hidden>
  <span class="typing-indicator__dot"></span>
  <span class="typing-indicator__dot"></span>
  <span class="typing-indicator__dot"></span>
</div>

<!-- AI 回覆的訊息氣泡以 streaming 狀態動態建立、appendToken()/complete() 逐步填入,
     見下方「使用方式」;append 到任意容器(如對話容器)皆可 -->
  • 送出訊息後先由輸入中提示元件顯示等待動畫並播報「AI 正在輸入」;等其 typing-indicator:announced 事件確認播報已送達,才開始串流。
  • 串流期間正文掛 aria-hidden="true",由本元件依句末標點分句、以至少 1100ms 間隔交由即時宣告代播(節流原因詳見即時宣告頁);完成時播「回覆完成」並顯示操作列。
  • 「模擬 AI 回覆」按鈕與「已宣告內容」面板僅供文件示範。

Custom Element

使用方式

元件對外只有四個後端無關的方法,任何串流來源都翻譯成這四個呼叫:

const bubble = document.createElement('message-bubble');
bubble.setAttribute('variant', 'ai');
bubble.setAttribute('streaming', '');
container.chatContainer.appendMessage(bubble); // 或直接 append 到任何位置

bubble.appendToken('串流文字片段'); // 追加正文
bubble.complete();                  // 回覆完成
bubble.abort();                     // 停止生成(保留已生成部分)
bubble.error('連線逾時');           // 錯誤(assertive 播報 + 可見錯誤文字)

後端事件對照範例(非規範性,僅為示意):

// Anthropic Messages API(串流)
stream.on('content_block_delta', (event) => bubble.appendToken(event.delta.text));
stream.on('message_stop', () => bubble.complete());
// OpenAI Responses API(串流)
for await (const event of stream) {
  if (event.type === 'response.output_text.delta') bubble.appendToken(event.delta);
  if (event.type === 'response.completed') bubble.complete();
}

角色標題與收尾播報文字可用 data-* 屬性覆寫(動態建立與伺服器渲染的靜態氣泡皆支援):

bubble.setAttribute('data-role-label', '王小明');           // 覆寫角色標題,預設「你」/「客服助理」
bubble.setAttribute('data-complete-message', '已回覆完畢'); // 覆寫 complete() 結語,預設「回覆完成」
bubble.setAttribute('data-abort-message', '已取消生成');    // 覆寫 abort() 結語,預設「已停止產生回覆」

CSS

  • .message-bubble__inner__inner--user__inner--ai:氣泡本體與角色 modifier。
  • .message-bubble__role:可見角色標題。
  • .message-bubble__toolbar:操作列,樣式沿用工具列元件。

親和力

  • 角色標示為可見文字,不靠顏色或對齊區分。
  • 串流正文的播報由「即時宣告」元件分句代播,正文本身不掛任何 aria-live
  • 操作列沿用工具列模式:單一 Tab 停駐點、方向鍵移動。
  • 讚/倒讚為互斥 toggle(aria-pressed),再按一次可取消。
  • 操作列按鈕標籤(複製/重新生成/讚/倒讚)、工具列的 aria-label="訊息操作",以及複製失敗時的提示文字,為刻意固定的無障礙輔助文字,目前不提供覆寫。

JavaScript

元件以 ES module 撰寫,載入必須加 type="module"。(若因架構需要跨網域載入,伺服器須提供 CORS 標頭,否則會靜默失敗。)路徑請依實際部署位置調整。

<script type="module" src="js/components/message-bubble.js"></script>
  • 本元件會自動載入同目錄的 toolbar.js,部署時需一併放置;頁面需掛載即時宣告元件。
  • 複製成功後有 polite 播報;重新生成與讚/倒讚以 message-bubble:regeneratemessage-bubble:feedback 事件通知外部,元件本體不重送請求。
  • 無 JS 時:靜態氣泡(角色標題、正文)可讀;操作列(含讚/倒讚)維持隱藏。本元件未內建表單模式;若伺服器端支援表單提交,可自行將讚/倒讚改造為表單版本。

多格式文字段落

  • 氣泡正文可放入語意化 HTML(如表格、清單):由伺服器或產品層渲染完成後置入,螢幕閱讀器以原生語意閱讀。
  • appendToken() 僅支援純文字;含結構化內容的回覆,建議串流階段以純文字呈現,complete() 後由產品層替換正文內容。
  • 即時宣告僅播純文字,表格與清單內容不會透過 live region 朗讀;建議完成時以文字摘要宣告(如「回覆包含表格」),內容由使用者於對話紀錄中瀏覽。
  • 元件不會解析 Markdown;若由呼叫端自行轉換為 HTML 後傳入,該 HTML 須先經過淨化(sanitize)以避免 XSS 風險。

參考