1. Czym jest inline i dlaczego to „domyślne”
Oficjalne wytyczne OpenAI podkreślają: wyświetlanie inline to podstawowy tryb dla ChatGPT Apps. Widżet inline jest renderowany bezpośrednio w wątku czatu, nad odpowiedzią modelu, i zawiera mały blok UI (kartę, listę, karuzelę) plus wiadomość follow‑up od GPT poniżej.
Idea jest prosta: zamiast wyprowadzać użytkownika do osobnego, rozbudowanego interfejsu, dajemy mu kompaktowy wizualny „błysk” bezpośrednio w kontekście rozmowy: model wyjaśnia, co się stało i jakie są dalsze opcje, a widżet zwięźle pokazuje strukturę i dostępne działania.
Widżet inline:
- jest lekki treściowo i nie wymaga wielu kroków;
- nie wprowadza użytkownika w złożoną nawigację z kartami i wewnętrznymi paskami przewijania;
- rozwiązuje jedno–dwa małe zadania: pokazać listę opcji, pozwolić wybrać, potwierdzić działanie, pokazać status.
Tryb fullscreen (o nim w następnej lekcji) jest potrzebny dla rozbudowanych kreatorów i złożonych treści. Teraz ważniejsze jest co innego: domyślnie myślcie inline, a fullscreen włączajcie świadomie — wtedy, gdy inline już wyraźnie nie wystarcza.
Naszym zadaniem w tej lekcji jest nauczyć się pewnie operować trzema podstawowymi inline‑wzorcami i na ich bazie pracować z CTA:
- karty
- listy
- karuzele
a także umiejętnie dodawać do nich CTA‑przyciski (Call to Action).
2. Kiedy inline jest lepszy od fullscreen
Upraszczając, tryb inline to „szybki asystent”, a fullscreen to „oddzielna aplikacja wewnątrz ChatGPT”.
Inline sprawdza się szczególnie, gdy:
- trzeba pokazać kilka wariantów i pozwolić wybrać jeden–dwa;
- wynik można ująć w kompaktowej strukturze: karta prezentu, podsumowanie zamówienia, mini‑tabelka;
- użytkownik wykonuje krótką akcję: „wybierz”, „pokaż szczegóły”, „zmień filtr”;
- dialog pozostaje najważniejszy: GPT tłumaczy, żartuje, komentuje, a widżet po prostu daje wygodną formę lub wizualizację.
W kategoriach GiftGenius inline to:
- pokazać 3–5 najlepszych prezentów dla wybranej osoby;
- dać szybki filtr: „pokaż tylko cyfrowe prezenty”;
- potwierdzić wybór: „oto podsumowanie zamówienia, wszystko ok?”
Fullscreen przyda się później dla trzyetapowego kreatora złożonego checkoutu. Teraz pozostajemy w lekkiej strefie: jedno wywołanie narzędzia → jeden widżet inline.
Dla przejrzystości — mała tabela:
| Wzorzec | Kiedy pasuje idealnie | Przykład w GiftGenius |
|---|---|---|
| Karta | 1–3 obiekty z kluczowymi parametrami i CTA | 3 najlepsze prezenty |
| Lista | 5–10 tekstowych pozycji, liczy się czytelność | lista pomysłów bez obrazków |
| Karuzela | 3–8 podobnych wariantów z wizualem, potrzebne przewijanie | długa lista prezentów |
Mając to na uwadze, zejdźmy do konkretów: jak te wzorce realizować w UI i w kodzie. Dalej przejdziemy każdy wzorzec w tym samym formacie: najpierw — co to jest z punktu widzenia UX, potem — prosta komponenta React dla GiftGenius i wreszcie, jak to wszystko osadzić w widżecie inline.
3. Karty: podstawowy klocek UI inline
Co to jest karta w kontekście Apps SDK
Zgodnie z wytycznymi OpenAI, karta inline to lekki, jednostanowiskowy widżet, który pokazuje niewielką ilość ustrukturyzowanych danych i 1–2 działania na dole. Może mieć tytuł, obrazek, kilka wierszy metadanych oraz jeden główny przycisk CTA (plus opcjonalny drugorzędny).
W GiftGenius każda karta to jeden prezent. W niej warto umieścić:
- nazwę prezentu;
- cenę;
- dla kogo jest odpowiedni (np. „kolega”, „bliski przyjaciel”);
- krótkie wyjaśnienie, dlaczego to dobry wybór;
- przycisk „Wybierz ten prezent” albo „Szczegóły”.
Karta powinna być samowystarczalna: użytkownik, zerkając na nią, rozumie, jaka to jednostka i jakie jest główne działanie.
Typ danych i prosta komponenta GiftCard
Najpierw zdefiniujmy typ danych dla prezentu. Załóżmy, że mamy już ToolOutput z tablicą takich obiektów; interesuje nas tylko część UI.
// Ogólna struktura prezentu dla UI
export type GiftSuggestion = {
id: string;
title: string;
priceLabel: string; // np. "≈ 40 $"
recipientLabel: string; // "dla kolegi"
reason?: string; // wyjaśnienie od modelu
imageUrl?: string;
};
Teraz zróbmy prostą komponentę karty w 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">
Dla: {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)}
>
Wybierz ten prezent
</button>
</div>
);
}
Kilka niuansów od razu:
- nie przeładowujemy karty tekstem — maksymalnie 2–3 linijki metadanych i krótkie wyjaśnienie;
- jedna główna CTA — „Wybierz ten prezent”; nie próbujemy upchnąć tu 5 różnych opcji;
- komponentę łatwo ponownie wykorzystać zarówno w inline‑liście, jak i w karuzeli.
Jak karty wpisują się w ogólny widżet
Załóżmy, że mamy tablicę gifts otrzymaną po wywołaniu narzędzia giftgenius.suggestGifts przez nasz MCP. Widżet w trybie inline może po prostu narysować je w siatce 1–3 kolumn.
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>
);
}
Tutaj:
- używamy siatki 1–2 kolumny, aby nie zamienić widżetu w „ceglaną ścianę”;
- łatwo ograniczamy liczbę kart, np. pokazujemy tylko pierwsze 3–6.
Sam obsługujący onSelect może wywołać narzędzie checkoutu albo po prostu zapisać wybór w stanie widżetu (Widget State) i pozwolić modelowi kontynuować dialog. Najprostszy przykład integracji z narzędziem:
async function handleSelect(gift: GiftSuggestion) {
await window.openai.actions.call("giftgenius.startCheckout", {
giftId: gift.id,
});
}
Tutaj window.openai.actions.call to most do wywołania zarejestrowanego narzędzia MCP bezpośrednio z widżetu.
Zazwyczaj po takim wywołaniu model pokaże status albo otworzy kolejny widżet (np. podsumowanie zamówienia). Najważniejsze — nie próbować robić całego checkoutu logiką wewnątrz karty; karta powinna uruchamiać jasny kolejny krok.
4. Listy: gdy wizual nie jest najważniejszy
Jeśli karta to mały „plakat”, to lista jest schludnym tekstowym spisem. Dokumentacja i rekomendacje UX pokazują, że listy są dobre, gdy ważniejsza jest treść tekstu, a nie mocny akcent wizualny.
Lista sprawdzi się, gdy:
- trzeba pokazać 5–10 wariantów, ale nie wymagają obrazka;
- użytkownik chce po prostu „przebiec wzrokiem” po nazwach i krótkich opisach;
- działania we wszystkich pozycjach są takie same i UI nie powinien rozpraszać.
Przykłady w GiftGenius:
- lista „szybkich” pomysłów na prezenty bez szczegółów;
- lista ulubionych kategorii: „dla kolegów”, „dla rodziców”, „dla dzieci”;
- lista zapisanych zestawień („Prezenty dla działu HR”, „Świąteczne drobiazgi do $20”).
Prosta komponenta listy
Zróbmy kompaktową listę z jednym przyciskiem CTA „Szczegóły” po prawej.
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)}
>
Szczegóły
</button>
</li>
))}
</ul>
);
}
Tutaj:
- dajemy tytułowi truncate, by długie nazwy nie psuły układu;
- ponownie używamy jednego CTA na element;
- całą „bogatą” informację (opis, obrazek, opinie) zostawiamy na następny krok — np. po kliknięciu otwieramy osobną kartę lub widok fullscreen.
Lista wyjątkowo dobrze łączy się z propozycjami follow‑up od GPT. Widżet pokazuje listę „kandydatów”, a poniżej GPT pisze coś w stylu:
„Mogę zawęzić do prezentów do $30 albo pokazać tylko cyfrowe. Co wybieramy?” i proponuje dwie–trzy przyciski follow‑up.
W osobnej sekcji omówimy jeszcze, jak najlepiej łączyć widżety inline i wiadomości follow‑up w różnych scenariuszach.
5. Karuzele: gdy wariantów jest dużo, ale są podobne
Karuzela to zestaw kart ułożonych poziomo i przewijanych gestem lub przyciskami nawigacji. Wytyczne sugerują używać karuzel, gdy pokazujecie niewielką listę podobnych elementów (zwykle 3–8), z których każdy zawiera obraz, tytuł i trochę metadanych.
Główny pomysł: użytkownik może szybko przeskanować zestaw wariantów, nie zasypiając przy nieskończonej pionowej liście.
W GiftGenius karuzela przyda się, jeśli:
- mamy 10–15 pasujących prezentów, ale widżet inline ma pokazać tylko „gorącą ósemkę”;
- każdy prezent jest atrakcyjny wizualnie (obrazek, oprawa);
- ważne jest, by użytkownik przekładał warianty, nie zjeżdżając daleko w dół czatu.
Reguły UX dla karuzel
Na podstawie wytycznych i researchu:
- liczba kart w karuzeli — od 3 do 8; jeśli jest ich więcej, lepiej dać osobne polecenie „Pokaż więcej”;
- każda karta:
- powinna mieć obrazek lub inny element wizualny;
- nie powinna zawierać więcej niż dwóch wierszy tekstu‑metadanych;
- ma jedno jasne CTA, np. „Wybierz” albo „Szczegóły”;
- żadnych złożonych zagnieżdżonych nawigacji (karty, podprzejścia) wewnątrz karty;
- unikamy wewnętrznych (pionowych) pasków przewijania: niech wysokość karty dostosowuje się do rozsądnego limitu, ale bez własnego scrolla.
Prosta karuzela w stylu „jedna karta naraz”
Aby nie wchodzić w złożony poziomy scroll, można zaimplementować najprostszy wariant: pokazywać jedną kartę naraz i dać przyciski „poprzedni/następny”.
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)}
>
← Poprzedni
</button>
<span>
{index + 1} / {gifts.length}
</span>
<button
disabled={index === gifts.length - 1}
onClick={() => setIndex((i) => i + 1)}
>
Następny →
</button>
</div>
</div>
);
}
To już daje wrażenie „karuzeli”, a jednocześnie:
- kod pozostaje kompaktowy;
- nie trzeba walczyć z szerokością kontenera i poziomymi scrollami wewnątrz widżetu;
- łatwo ograniczyć gifts do 8 elementów przed przekazaniem do komponentu.
Jeśli chcemy bardziej „prawdziwej” karuzeli, można użyć overflow-x-auto i stałej szerokości kart, ale to ten przypadek, gdy prościej wziąć gotowy komponent z biblioteki UI (shadcn/ui, rozwiązania kompatybilne z Radix itp.), niż wymyślać własny od zera.
6. CTA: mało, jasno, na temat
CTA (Call to Action) to serce każdego wzorca inline. To właśnie przyciski zmieniają widżet z obrazka w działające narzędzie.
Główne zasady
Dokumentacja OpenAI daje dość rygorystyczne zalecenia:
- na karcie — maksymalnie dwa przyciski główne (jeden podstawowy, drugi — drugorzędny);
- w karuzeli — po jednym CTA na element, gdy to możliwe;
- tekst CTA powinien być konkretnym czasownikiem: „Pokaż szczegóły”, „Dodaj do listy”, „Przejdź do płatności”, a nie abstrakcyjnym „OK” czy „Działanie”.
Im mniej przycisków, tym prościej dla modelu i użytkownika. Pamiętajcie, że nad i pod widżetem jest jeszcze tekstowa część odpowiedzi oraz propozycje follow‑up od GPT.
Powiązanie CTA z logiką aplikacji
W naszym GiftGenius większość CTA będzie albo:
- zmieniać filtry/kryteria doboru (nowy tool‑call giftgenius.refineSearch),
- uruchamiać checkout (giftgenius.startCheckout),
- otwierać zewnętrzną stronę (przez openExternal, znane już z wcześniejszych lekcji).
Przykład prostego handlera dla CTA „Zmień filtry”:
async function handleRefineFilters(gift: GiftSuggestion) {
await window.openai.actions.call("giftgenius.refineSearch", {
baseGiftId: gift.id,
});
}
Z perspektywy UX bardzo ważne jest, by w instrukcjach systemowych doprecyzować, kiedy i jakie CTA model ma proponować. Na przykład:
- jeśli użytkownik prosi „pokaż więcej opcji”, lepiej pokazać nową karuzelę z przyciskiem „Wybierz”;
- jeśli doszło do zakupu, CTA „Przejdź do płatności” powinna prowadzić do wywołania narzędzia uruchamiającego checkout ACP (do tego dojdziemy w module o komercji i płatnościach).
Jeszcze jedna dobra praktyka — nie dublować funkcji ChatGPT w CTA. Nie trzeba tworzyć przycisku „Zapytaj ChatGPT”, użytkownik ma już pole wprowadzania i głos. Wytyczne wyraźnie zalecają unikać „dublujących” wejść wewnątrz karty.
7. Inline + follow‑up: gra w duecie
Widżet inline nigdy nie żyje w próżni. Struktura odpowiedzi wygląda zwykle tak:
- model decyduje się użyć waszej aplikacji i wywołać narzędzie;
- wasz MCP zwraca dane;
- ChatGPT renderuje widżet inline z tymi danymi;
- pod nim model dopisuje krótką wiadomość follow‑up i gotowe opcje kontynuacji.
Dla GiftGenius może to wyglądać tak:
- widżet inline: trzy karty prezentów z CTA „Wybierz”;
- tekst poniżej:
„Oto trzy pomysły dla kolegi: lampka biurkowa, kurs wystąpień publicznych i karta podarunkowa do kawiarni. Mogę: — pokazać tylko opcje do $30; — dobrać jeszcze kilka pomysłów w podobnym stylu; — pomóc od razu przejść do zakupu jednego z nich.”
Model w follow‑up może odwoływać się do CTA waszego widżetu („kliknij „Wybierz” pod wybranym wariantem”) albo proponować komendy tekstowe, które znów doprowadzą do wywołania narzędzia i przerysowania UI inline.
Ważne: widżet nie musi umieć wszystkiego. Czasem lepiej część scenariusza zostawić rozmowie tekstowej, a widżetu użyć jako „bloku wizualnego” w trakcie konwersacji.
8. Jak to jest wpięte w ogólny flow GiftGenius
Aby było to jaśniejsze, zbierzmy wszystko w prosty diagram sekwencji:
sequenceDiagram participant U as Użytkownik participant C as ChatGPT participant A as GiftGenius Widget participant B as MCP/Backend U->>C: "Wybierz 3 prezenty do 50$ dla kolegi" C->>B: call_tool(giftgenius.suggestGifts) B-->>C: 3 najlepsze warianty C->>A: render widżetu inline (karty/karuzela) A-->>U: karty z CTA "Wybierz" U->>A: klik w CTA A->>B: call_tool(giftgenius.startCheckout) B-->>A: status / link do płatności A-->>U: podsumowanie wyboru / status C-->>U: follow-up: "Mogę dobrać więcej pomysłów albo pomóc z kartką"
Z punktu widzenia architektury:
- MCP pozostaje „mózgiem” (dobór, logika biznesowa, ACP),
- widżet — „twarzą” (karty/listy/karuzele),
- ChatGPT — „prowadzącym dialog”, który wyjaśnia, co się stało, i proponuje kolejne kroki.
Aby ten flow był wygodny:
- nie przeładowujcie widżetu działaniami;
- trzymajcie dane w kartach w kompaktowej formie;
- przemyślcie, jakie opcje follow‑up będą przydatne po każdym wyświetleniu inline.
9. Kilka słów o stronie wizualnej wzorców inline
Szczegółowo o designie wizualnym porozmawiamy w jednej z kolejnych lekcji modułu, ale kilka kwestii krytycznych dla wzorców inline warto wspomnieć już teraz.
Po pierwsze, upewnijcie się, że wasze karty i listy nie wyglądają jak obca strona wewnątrz ChatGPT. Kolory i odstępy powinny być stonowane, bez jaskrawych gradientów i czcionek typu Comic Sans. Widżet inline jest częścią ogólnego UI ChatGPT, a nie banerem z 2007 roku.
Po drugie, unikajcie wewnętrznych pasków przewijania. Jeśli wasza karta jest tak długa, że pojawia się w niej własny pasek przewijania, coś poszło nie tak: albo próbujecie wcisnąć zbyt dużo treści, albo wybrano zły wzorzec (być może potrzebny jest fullscreen).
Po trzecie, kontrolujcie gęstość:
- między kartami powinien być widoczny odstęp;
- CTA musi być łatwe do kliknięcia (odpowiedni padding);
- tekst — czytelny także na komórkach, bez mikroskopijnych fontów.
To może brzmieć jak „uwagi estetyczne”, ale praktyka pokazuje: jeśli widżet inline wygląda „jak rodzimy”, model chętniej go używa, a użytkownicy rzadziej się gubią.
10. Praktyka: jak rozwinąć GiftGenius na podstawie lekcji
Jeśli chcecie utrwalić materiał, oto prosty checklist:
Najpierw weźcie aktualny wynik narzędzia giftgenius.suggestGifts (tablica prezentów) i:
- Zaimplementujcie trzy różne warianty UI w jednym komponencie:
- GiftGrid z kartami;
- GiftList z listą tekstową;
- GiftCarousel z nawigacją „poprzedni/następny”.
- Dodajcie do nich po jednym–dwóch CTA‑przyciskach:
- dla kart — „Wybierz”;
- dla listy — „Szczegóły”;
- dla karuzeli — również „Wybierz”, plus osobny przycisk pod widżetem „Pokaż więcej wariantów”.
- W zależności od stanu (np. ile łącznie prezentów zwróciło narzędzie) wybierajcie, którego wzorca użyć:
- jeśli wariantów jest mało (≤ 3) — siatka kart;
- jeśli jest dużo pomysłów tekstowych — lista;
- jeśli wiele prezentów ma silny aspekt wizualny — karuzela.
Dzięki temu nie tylko poćwiczycie UI, ale też zaczniecie myśleć o dynamicznym wyborze wzorca w zależności od kontekstu, co docenią i użytkownicy, i recenzenci w Store.
Ogólnie inline‑wzorce to szybka, lekka warstwa UI, która żyje bezpośrednio w wątku czatu i nie próbuje zastąpić osobnej aplikacji. Karty, listy i karuzele pokrywają 80% typowych zadań: pokazać warianty, pozwolić wybrać i płynnie kontynuować dialog.
W następnej lekcji tego modułu zobaczymy, co robić, gdy inline już „nie dźwiga”: omówimy kreatory fullscreen, tryb PiP i scenariusze, w których wasza aplikacja rzeczywiście potrzebuje osobnego, dużego ekranu wewnątrz ChatGPT.
11. Typowe błędy przy pracy ze wzorcami inline
Błąd nr 1: zamienianie widżetu inline w mini‑stronę.
Czasem deweloperzy próbują wcisnąć w jedną kartę zakładki, akordeony, formularze, tabelę i masę innych elementów. W rezultacie powstaje ciężki UI, który psuje rytm czatu i jest niewygodny na urządzeniach mobilnych. Wytyczne mówią wprost: żadnych głębokich nawigacji i złożonych widoków wewnątrz kart inline; złożone scenariusze przenosimy do fullscreen.
Błąd nr 2: zbyt wiele przycisków CTA.
„A może na karcie zrobimy ‘Szczegóły’, ‘Kup’, ‘Do ulubionych’, ‘Udostępnij’, ‘Zgłoś’ i ‘Wygeneruj kartkę’?” W efekcie użytkownik się gubi, model też, a szansa, że klikną właściwy przycisk, spada. Pamiętajcie o zasadzie: jeden główny CTA i maksymalnie jeden drugorzędny. Pozostałe scenariusze lepiej przenieść do wiadomości follow‑up od GPT lub kolejnych kroków.
Błąd nr 3: mieszanie listy, kart i karuzeli w jednej odpowiedzi bez powodu.
Jeżeli ta sama treść jest pokazywana raz listą, raz kartami, raz karuzelą „bo tak umiemy”, użytkownik traci poczucie spójności. Lepiej wybrać jeden wzorzec dla konkretnego typu wyników (np. pomysły bez obrazków — lista, prezenty z obrazkami — karuzela) i się go trzymać.
Błąd nr 4: przeładowane tekstem karty.
Karta, w której są trzy akapity opisu, trzy ceny i dwa bloki „dlaczego to super”, zamienia się w ścianę tekstu. Użytkownik przestaje ją „skanować”, tylko przewija dalej. Starajcie się zostawiać w karcie tylko to, co najważniejsze: tytuł, jeden kluczowy parametr, jedną krótką przesłankę i CTA. Wszystko inne można wyjaśnić w tekstowej odpowiedzi GPT obok.
Błąd nr 5: poleganie tylko na UI i ignorowanie dialogu follow‑up.
Czasem spotyka się podejście „wszystko robimy przez przyciski, użytkownik nie musi rozmawiać”. To stoi w sprzeczności z ideą ChatGPT. Widżet inline ma uzupełniać dialog, a nie go zastępować. Nie zapominajcie przemyśleć, jakie opcje follow‑up model może zaproponować pod widżetem: zmienić filtry, poprosić o więcej wariantów, przejść do kolejnego kroku.
Błąd nr 6: ignorowanie limitów liczby elementów.
Karuzela z 25 kartami albo lista z 50 pozycjami w jednym widżecie inline to prosta droga do tego, by użytkownik przewinął wszystko obojętnie. Dokumentacja zaleca 3–8 elementów w karuzeli i 5–10 pozycji na liście. Jeśli danych jest więcej, warto dodać CTA typu „Pokaż więcej” lub „Pokaż wszystko tekstem”.
Błąd nr 7: używanie inline tam, gdzie już potrzebny jest fullscreen.
Bywa pokusa „wszystko robić inline”, nawet gdy mamy już 4 kroki, formularze z dziesięcioma polami i duże tabele. W efekcie albo powstaje potworek, albo zaczynacie wymyślać zagnieżdżone scrolle i pseudo‑steppery wewnątrz kart. Gdy tylko czujecie, że kroków i pól robi się dużo — to sygnał, by pomyśleć o przejściu do kreatora fullscreen, a inline zostawić do szybkich podglądów i podsumowań działań.
GO TO FULL VERSION