CodeGym /課程 /ChatGPT Apps /Inline 模式樣式:卡片、清單、輪播與 CTA

Inline 模式樣式:卡片、清單、輪播與 CTA

ChatGPT Apps
等級 8 , 課堂 1
開放

1. 什麼是 inline 模式,為什麼它是「預設」

OpenAI 的官方指引強調:inline 顯示是 ChatGPT Apps 的主要模式。 Inline 小工具會直接在聊天訊息流中、模型回覆上方渲染,包含一個小型 UI 區塊(卡片、清單、輪播),以及其下方由 GPT 補充的後續訊息。

理念很簡單:不把使用者帶到另一個龐大的介面,而是在對話脈絡中直接給出一個精簡的視覺「閃現」: 模型解釋剛剛做了什麼、接下來能怎麼做,而小工具則乾淨地呈現結構與可用動作。

Inline 小工具的特性:

  • 內容輕量,不需要很多步驟;
  • 不把使用者帶進帶分頁或內部滾動條的複雜導覽;
  • 一次解決一到兩個小任務:展示選項、讓使用者選擇、確認動作、顯示狀態。

Fullscreen 模式(下一講會介紹)用於大型精靈與複雜內容。此刻更重要的是: 預設先想 inline,只有在 inline 明顯不夠用時,才有意識地切換到 fullscreen

本講的目標,是熟練運用三大 inline 樣式並在其上正確使用 CTA

  • 卡片
  • 清單
  • 輪播

以及替它們合理配置 CTA 按鈕(Call to Action)

2. 什麼時候 inlinefullscreen 更好

簡化來說,inline 是「小幫手」,fullscreen 是「在 ChatGPT 內的一個獨立應用」。

Inline 特別適合以下情況:

  • 需要展示多個選項並讓使用者選 1–2 個;
  • 結果可以以緊湊的結構表達:禮物卡片、訂單摘要、迷你表格;
  • 使用者要做的是短動作:例如「選擇」、「顯示更多資訊」、「調整篩選」;
  • 對話仍是主角:GPT 負責說明、互動、補充,而小工具只是提供便利的表單或視覺化。

就 GiftGenius 而言,inline 的典型情境:

  • 為特定對象展示 3–5 個最佳禮物;
  • 提供快速篩選:「只看數位類禮物」;
  • 確認選擇:「這裡是訂單摘要,都沒問題嗎?」。

Fullscreen 之後才用在三步驟的複雜結帳精靈。現在我們留在輕量區: 一次工具呼叫 → 一個 inline 小工具。

更直觀的對照表:

樣式 最適用情境 GiftGenius 範例
卡片 1–3 個實體,含關鍵參數與 CTA 3 個頂級禮物
清單 5–10 個文字項目,重點是可讀性 不含圖片的點子清單
輪播 3–8 個相似的可視化選項,需要橫向瀏覽 較長的禮物清單

理解上述差異後,接下來看具體實作:這些樣式在 UI 與程式碼中怎麼落地。 我們會用一致的節奏介紹每一種樣式:先談 UX、再給一個適用於 GiftGenius 的簡單 React 元件、最後說如何嵌入 inline 小工具。

3. 卡片:inline UI 的基本積木

在 Apps SDK 脈絡下的卡片是什麼

依 OpenAI 指引,inline 卡片是一個輕量的單使用者小工具,顯示少量結構化資料,底部有 1–2 個動作。 它可以包含標題、圖片、幾行中繼資料與一個主要的 CTA 按鈕(外加可選的次要按鈕)。

在 GiftGenius 中,每張卡片代表一個禮物。適合放入:

  • 禮物名稱;
  • 價格;
  • 適合對象(例如「同事」、「要好朋友」);
  • 為什麼這是好選擇的簡短說明;
  • 「選擇此禮物」或「更多資訊」的按鈕。

卡片應該自我完備:使用者掃一眼就能理解這是什麼東西、下一步的主要動作是什麼。

資料型別與簡易元件 GiftCard

先定義禮物的資料型別。假設我們已有一個包含這些物件陣列的 ToolOutput;這裡只關心 UI 部分。

// UI 的禮物通用結構
export type GiftSuggestion = {
  id: string;
  title: string;
  priceLabel: string;         // 例如 "≈ 40 $"
  recipientLabel: string;     // "給同事"
  reason?: string;            // 模型的簡短說明
  imageUrl?: string;
};

接著實作一個簡單的 React 卡片元件:

type GiftCardProps = {
  gift: GiftSuggestion;
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCard({ gift, onSelect }: GiftCardProps) {
  return (
    <div className="flex flex-col gap-2 rounded-lg border p-3">
      <div className="text-sm font-medium">{gift.title}</div>
      <div className="text-xs text-muted-foreground">
        適用對象:{gift.recipientLabel} · {gift.priceLabel}
      </div>
      {gift.reason && (
        <div className="text-xs text-muted-foreground">{gift.reason}</div>
      )}
      <button
        className="mt-2 self-start rounded bg-primary px-3 py-1 text-xs text-primary-foreground"
        onClick={() => onSelect(gift)}
      >
        選擇這個禮物
      </button>
    </div>
  );
}

幾個重點:

  • 不塞滿文字,頂多 2–3 行中繼資料加上短說明;
  • 只放一個主要的 CTA—「選擇這個禮物」,不要硬塞 5 種不同動作;
  • 元件易於重複使用:可放在 inline 清單,也能放在輪播內。

卡片如何融入整體小工具

假設我們有一個在透過 MCP 呼叫 giftgenius.suggestGifts 後取得的陣列 gifts。 在 inline 模式下,小工具可以用 1–3 欄的格狀布局直接渲染。

type GiftGridProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftGrid({ gifts, onSelect }: GiftGridProps) {
  return (
    <div className="grid gap-3 sm:grid-cols-2">
      {gifts.map((gift) => (
        <GiftCard key={gift.id} gift={gift} onSelect={onSelect} />
      ))}
    </div>
  );
}

在這裡我們:

  • 使用 1–2 欄的格狀布局,避免把小工具變成「磚牆」;
  • 可以限制卡片數量,例如只顯示前 3–6 張。

事件處理器 onSelect 可以呼叫結帳工具,或只是把選擇寫入 Widget State,然後交給模型繼續對話。 以下是與工具整合的最簡例:

async function handleSelect(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.startCheckout", {
    giftId: gift.id,
  });
}

這裡的 window.openai.actions.call 是一座橋,讓你能從小工具內直接呼叫已註冊的 MCP 工具。

通常在這種呼叫之後,模型會顯示狀態或開啟下一個小工具(例如訂單摘要)。重點是— 不要把整個結帳流程的邏輯塞進卡片內;卡片只需要啟動一個明確的下一步。

4. 清單:當視覺不是重點

如果說卡片像是一張小海報,清單就是俐落的文字清單。文件與 UX 建議表明,當文字內容本身更重要,而不是強烈的視覺焦點時,清單更合適。

清單適用於:

  • 需要展示 5–10 個選項,但不需要圖片;
  • 使用者想快速掃過名稱與簡述;
  • 每個項目的動作一致,UI 不應分散注意力。

在 GiftGenius 的例子:

  • 不含細節的「快速」禮物點子清單;
  • 常用分類清單:「給同事」、「給父母」、「給小孩」;
  • 已儲存的精選清單(「給 HR 部門的禮物」、「新年小物 20 美元內」)。

簡易清單元件

做一個精簡清單,在右側放一個 CTA 按鈕「更多資訊」。

type GiftListProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftList({ gifts, onSelect }: GiftListProps) {
  return (
    <ul className="flex flex-col gap-2">
      {gifts.map((gift) => (
        <li
          key={gift.id}
          className="flex items-center justify-between rounded-md border px-3 py-2 text-sm"
        >
          <span className="truncate">{gift.title}</span>
          <button
            className="text-xs text-primary"
            onClick={() => onSelect(gift)}
          >
            更多資訊
          </button>
        </li>
      ))}
    </ul>
  );
}

這裡我們:

  • 讓標題使用 truncate,避免長標題破版;
  • 同樣每個項目只放一個 CTA
  • 把所有「豐富」資訊(描述、圖片、評價)留到下一步,例如點擊後開啟單張卡片或 fullscreen 檢視。

清單特別適合搭配 GPT 的後續建議(follow‑up)。 小工具顯示「候選」清單,在其下方 GPT 會寫類似:

「我可以縮小到 30 美元以內的禮物,或只顯示數位類。要選哪個?」並提供兩到三個 follow‑up 按鈕。

在後續章節我們還會討論,在不同情境下如何把 inline 小工具與 follow‑up 訊息組合得更恰當。

5. 輪播:當選項很多但彼此相似

輪播是一組橫向排列、可滑動或按鈕切換的卡片。 指引建議在展示少量相似元素(通常 3–8 個)時使用輪播,每個元素含圖片、標題與少量中繼資料。

核心想法是:使用者能快速掃描一組選項,而不會被無止境的縱向清單淹沒。

在 GiftGenius,輪播適合於:

  • 有 10–15 個合適禮物,但 inline 小工具只需展示「精選 8 個」;
  • 每個禮物都有不錯的視覺(圖片、樣式);
  • 希望使用者在不往下捲太多的情況下來回瀏覽。

輪播的 UX 規則

根據指引與研究:

  • 輪播卡片數量介於 3 到 8;更多的話,另給一個「顯示更多」的動作比較好;
  • 每張卡片:
    • 需要有圖片或其他視覺元素;
    • 中繼文字不宜超過兩行;
    • 一個清楚的 CTA,例如「選擇」或「更多資訊」;
  • 避免在卡片內做複雜的巢狀導覽(分頁、次級跳轉);
  • 避免內部(縱向)卷軸:讓卡片高度在合理範圍內自適應即可,不要有自己的卷軸。

「一次一張卡片」的簡易輪播

為了避免處理複雜的橫向捲動,可以實作最簡版本:一次只顯示一張卡片,提供「上一個/下一個」按鈕。

import { useState } from "react";

type GiftCarouselProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCarousel({ gifts, onSelect }: GiftCarouselProps) {
  const [index, setIndex] = useState(0);
  const gift = gifts[index];

  return (
    <div className="flex flex-col gap-2">
      <GiftCard gift={gift} onSelect={onSelect} />
      <div className="flex items-center justify-between text-xs">
        <button
          disabled={index === 0}
          onClick={() => setIndex((i) => i - 1)}
        >
          ← 上一個
        </button>
        <span>
          {index + 1} / {gifts.length}
        </span>
        <button
          disabled={index === gifts.length - 1}
          onClick={() => setIndex((i) => i + 1)}
        >
          下一個 →
        </button>
      </div>
    </div>
  );
}

這樣已經有「輪播」的感覺,同時:

  • 程式碼保持精簡;
  • 不用與容器寬度與小工具內的橫向卷軸搏鬥;
  • 很容易在傳入元件前,把 gifts 限制為 8 個以內。

若想要更「真正」的輪播,可以使用 overflow-x-auto 與固定卡片寬度。 但在這種情況下,與其從零自製,不如直接採用 UI 套件的成熟元件(shadcn/ui、Radix 生態等)。

6. CTA 按鈕:精簡、清楚、務實

CTA(Call to Action)是所有 inline 樣式的核心。按鈕能把你的小工具從「圖片」變成「可運作的工具」。

基本原則

OpenAI 文件給了相當明確的建議:

  • 卡片上至多兩個主要按鈕(一個主按鈕、另可有次要);
  • 在輪播中,盡量讓每個元素只放一個 CTA
  • CTA 文案應是明確的動詞:「顯示詳細資訊」、「加入清單」、「前往結帳」,而不是模糊的「OK」或「動作」。

按鈕越少,模型與使用者越不費力。別忘了,小工具的上方與下方還有文字回覆,以及 GPT 的 follow‑up 建議。

CTA 綁到應用邏輯

在我們的 GiftGenius,多數 CTA 會:

  • 調整篩選/條件(新的 tool‑call:giftgenius.refineSearch),
  • 啟動結帳(giftgenius.startCheckout),
  • 開啟外部網站(透過 openExternal,在前面課程已介紹)。

以下是「調整篩選」CTA 的簡單處理器:

async function handleRefineFilters(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.refineSearch", {
    baseGiftId: gift.id,
  });
}

就 UX 而言,很重要的是在 system 指令中清楚說明模型何時應該提供哪些 CTA 按鈕。 例如:

  • 若使用者說「再給我一些選項」,最好顯示新的輪播並附「選擇」按鈕;
  • 若進入購買階段,「前往結帳」CTA 應該觸發啟動 ACP 結帳的 tool 呼叫(我們會在商務與支付模組討論)。

另一個實用建議—不要在 CTA 中重複 ChatGPT 的功能。別做「詢問 ChatGPT」這種按鈕;使用者本來就有輸入框與語音。 指引也明確建議避免在卡片內放「重複」的輸入方式。

7. Inline + follow‑up:雙人配合

Inline 小工具從不獨立存在。典型回覆結構如下:

  1. 模型決定使用你的 App 並呼叫工具;
  2. 你的 MCP 回傳資料;
  3. ChatGPT 用這些資料渲染 inline 小工具;
  4. 在小工具下方,模型補上一段簡短的 follow‑up 與可繼續操作的建議。

對 GiftGenius 而言,可能是這樣:

  • inline 小工具:三張禮物卡片,附 CTA「選擇」;
  • 下方文字:
    「這裡有三個給同事的想法:檯燈、公開演講課程、咖啡店禮品卡。我可以: — 只顯示 30 美元以內的選項; — 再找幾個風格相近的; — 直接幫你購買其中一個。」

模型在 follow‑up 中可以引用你的小工具 CTA(「按下喜歡的選項下方的『選擇』」),或提出文字指令, 再度引發 tool‑call 並重繪 inline UI。

要記得:小工具不需要無所不能。有時讓部分流程留在文字對話中,反而更好; 小工具僅作為對話中的「視覺區塊」即可。

8. 這些如何嵌入 GiftGenius 的整體流程

為了更易理解,以下是一個簡單的時序圖:

sequenceDiagram
  participant U as 使用者
  participant C as ChatGPT
  participant A as GiftGenius 小工具
  participant B as MCP/後端

  U->>C: "幫我為同事找 3 個 50 美元以內的禮物"
  C->>B: call_tool(giftgenius.suggestGifts)
  B-->>C: 3 個最佳選項
  C->>A: 渲染 inline 小工具(卡片/輪播)
  A-->>U: 卡片與 CTA「選擇」
  U->>A: 點擊 CTA
  A->>B: call_tool(giftgenius.startCheckout)
  B-->>A: 狀態/付款連結
  A-->>U: 選擇摘要/狀態
  C-->>U: follow-up:「我可以再找一些點子,或幫你寫卡片」

從架構角度:

  • MCP 是「大腦」(挑選、商業邏輯、ACP);
  • 小工具是「門面」(卡片/清單/輪播);
  • ChatGPT 是「對話主持人」,說明發生了什麼、提出下一步。

要讓這個流程好用:

  • 不要讓小工具充斥太多動作;
  • 卡片內容保持精簡;
  • 思考每次 inline 顯示之後,哪些 follow‑up 選項最有幫助。

9. inline 樣式的視覺細節

我們會在本模組後續課程深入討論視覺設計,但有幾點對 inline 樣式特別重要,先提一下。

首先,確保你的卡片與清單不會看起來像是外來網站嵌在 ChatGPT。 配色與留白要乾淨,避免刺眼漸層與 Comic Sans。 Inline 小工具是 ChatGPT 整體 UI 的一部分,而不是 2007 年的橫幅廣告。

其次,避免內部卷軸。 如果卡片長到出現自己的卷軸,就表示哪裡出問題了: 不是放了太多內容,就是選錯樣式(也許該換成 fullscreen)。

第三,控制資訊密度:

  • 卡片之間要有明顯間距;
  • CTA 要容易點按(足夠的 padding);
  • 文字在手機上也要可讀,避免過小字級。

這些看似「設計細節」,但實務上非常關鍵:若 inline 小工具看起來夠「原生」, 模型會更樂於使用,使用者也更不容易困惑。

10. 實作練習:如何依本講優化 GiftGenius

若你想複習並加深理解,這裡有個簡單的實作清單:

先拿目前 giftgenius.suggestGifts 工具的輸出(禮物陣列),然後:

  1. 在同一個元件中實作三種不同 UI
    • GiftGrid(卡片格狀);
    • GiftList(文字清單);
    • GiftCarousel(上一個/下一個導覽)。
  2. 各自加上一到兩個CTA 按鈕
    • 卡片—「選擇」;
    • 清單—「更多資訊」;
    • 輪播—同樣「選擇」,並在小工具下加一顆「顯示更多選項」。
  3. 依情境(例如工具回傳的總數量)選擇使用哪種樣式:
    • 選項很少(≤ 3)—卡片格狀;
    • 很多文字型點子—清單;
    • 很多可視化的禮物—輪播。

這麼做不僅能練習 UI,也能開始思考依情境動態選擇樣式, 這會同時讓使用者與 Store 的審查者都更滿意。

總之,inline 樣式就是快速、輕量的 UI 層,活在聊天訊息流中,而不是要取代獨立的應用。 卡片、清單與輪播能滿足 80% 的典型需求:展示選項、讓使用者選擇,並順暢地延續對話。

在本模組的下一講,我們會看看當 inline「撐不住」時該怎麼辦: 討論 fullscreen 精靈、PiP 模式,以及哪些情境下你的 App 真的需要在 ChatGPT 內擁有一個更大的獨立畫面。

11. 使用 inline 樣式時的典型錯誤

錯誤 №1:把 inline 小工具做成迷你網站。
有時開發者會在一張卡片裡塞分頁、手風琴、表單、表格與一堆元素。結果是沈重的 UI,打亂聊天節奏,在行動裝置上也難用。 指引明確說了:卡片內避免深層導覽與複雜視圖;複雜情境請移到 fullscreen

錯誤 №2:CTA 太多。
「不如在卡片上放『更多資訊』、『購買』、『加入收藏』、『分享』、『檢舉』、『產生賀卡』吧。」 最後使用者與模型都迷失,按到關鍵按鈕的機率反而下降。記住原則:一個主要 CTA,最多一個次要。 其餘情境交給 GPT 的 follow‑up 訊息或後續步驟。

錯誤 №3:在同一個回覆裡混用清單、卡片、輪播,且沒有理由。
相同內容一會兒用清單、一會兒用卡片、又換輪播,只因為「我們做得到」,會破壞一致性。 更好的做法是:對同一類輸出固定一種樣式(例如無圖點子用清單、有圖禮物用輪播),並保持一致。

錯誤 №4:卡片塞太多文字。
卡片裡放三段描述、三個價格、兩段「為什麼很棒」,就變成文字牆。 使用者不再「掃描」,只會直接略過。卡片請只留最重要的:標題、一個關鍵參數、簡短理由以及 CTA。 其他內容可以由旁邊的 GPT 文字回覆說明。

錯誤 №5:只靠 UI,不理會 follow‑up 對話。
有時會看到「全部用按鈕,使用者不必說話」的做法。這與 ChatGPT 的核心理念相違。 Inline 小工具應該輔助對話,而不是取代對話。別忘了規劃模型可在小工具下提供哪些 follow‑up 選項: 調整篩選、要求更多選項、進到下一步等。

錯誤 №6:忽視項目數量的限制。
在一個 inline 小工具中放 25 張卡片的輪播,或 50 項的清單,是逼使用者整段滑過的捷徑。 文件建議輪播 3–8 個、清單 5–10 個。若資料更多,加入「顯示更多」或「改以文字顯示全部」這類 CTA 很有幫助。

錯誤 №7:在該用 fullscreen 時還硬用 inline
有時會貪心地「全部都用 inline」,即便已經有 4 個步驟、含十幾個欄位的表單、與大型表格。 最後不是做出怪物,就是在卡片內發明巢狀卷軸與假階段條。 一旦感覺步驟與欄位越堆越多,就是該考慮 fullscreen 精靈的訊號,而把 inline 留給快速預覽與動作摘要。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION