1. Qum qutusu nədir və niyə vidjetiniz «qəfəsdədir»
ChatGPT sizin vidjetinizi göstərərkən onu heç də adi <iframe src="https://sizin-saytiniz"> kimi render etmir. Vidjet idarə olunan «qum qutusunda» — ayrıca origin-ə və sərt təhlükəsizlik parametrlərinə malik izolyasiya olunmuş iframe-də işə düşür.
Texniki olaraq bu təxminən belə görünür:
flowchart TD
User["İstifadəçi ChatGPT-də"]
Chat["ChatGPT UI + model"]
Iframe["Sizin vidjetiniz
sandboxed iframe"]
MCP["Sizin MCP / backend"]
User --> Chat
Chat -->|tool çağırışı| MCP
MCP -->|structuredContent + _meta| Chat
Chat -->|window.openai.*| Iframe
Iframe -->|callTool / follow-up| Chat
Chat --> MCP
Sizin kod yalnız bu iframe daxilində icra olunur, qalan dünyaya çıxış isə hostun (ChatGPT) təqdim etdiyi dar nəzarət olunan API vasitəsilə gedir. Vidjet etməməlidir:
- ChatGPT-nin özünü sındırmaq (DOM, stillər, performans);
- istifadəçinin məxfiliyini pozmaq;
- şəbəkəyə nəzarətsiz çıxmaq.
Buradan qum qutusunun əsas məhdudiyyətləri çıxır.
DOM və origin izolyasiyası
Vidjet qum qutusu üçün xüsusi domenə (məsələn, https://sandbox-apps.oaiusercontent.com) yerləşdirilir və iframe-də sandbox atributu olur. Bu o deməkdir ki:
- window.parent və ya ChatGPT-nin document-inə girməyiniz mümkün deyil — SecurityError alacaqsınız;
- postMessage kimi kross-domen mexanizmlər host tərəfindən idarə olunur;
- «ChatGPT interfeysini CSS-lə düzəldək» cəhdləri uğursuz olacaq.
Şəbəkə və CSP məhdudiyyətləri
Brauzer və hostun CSP siyasəti vidjetiniz üçün şəbəkə çıxışını məhdudlaşdırır:
- fetch metodu yalnız review-dan keçmiş whitelist-dəki domenlərə çıxış əldə edə bilər;
- vidjetdən müraciət edə biləcəyiniz domenləri MCP cavablarında openai/widgetCSP vasitəsilə açıq şəkildə bəyan edirsiniz; əks halda sorğular keçməyəcək;
- tövsiyə edilən yol: ciddi işlər üçün ümumiyyətlə vidjetdən şəbəkəyə çıxmayın, backend-ə MCP alətləri və callTool vasitəsilə gedin (buna Modul 4-də daha ətraflı baxacağıq).
Praktik olaraq: vidjeti nazik UI qatı kimi düşünün. O, ChatGPT və serverinizlə sərt müəyyən edilmiş kanallar üzərindən danışır, internetdə sərbəst yaşayan adi SPA kimi yox.
Yaddaş və resurslar
Yerli saxlanclar (localStorage, sessionStorage) sizin üçün əlçatandır, amma cookie — yox. Tətbiqi hazırlayarkən bunu nəzərə alın. Yaddaş və CPU məhduddur: əgər vidjet daxilində bir milyarda qədər sadə ədədləri hesablamaq qərarına gəlsəniz, host iframe-i sadəcə dayandıra bilər.
Vacib nəticə: vidjetdə ağır hesablamalar və uzunömürlü «keşlər» olmaz. Mürəkkəb məntiq — server tərəfində, React komponentində deyil.
2. window.openai: vidjetlə ChatGPT arasında körpü
Vidjetin nəsə öyrənməsi (alət nəticələri, görüntü rejimi, lokal, vəziyyət) üçün ChatGPT inisializasiya zamanı iframe pəncərəsinə bir qlobal obyekt yerləşdirir — window.openai.
Bu nə sizin kodunuz, nə də npm paketidir, bu host object-dir, onu İİ platformasının özü təqdim edir. Qapalı tərəfdə host ilə iframe arasında hadisələr və mesajlaşma əsasında işləyir, amma bunun haqqında demək olar ki, düşünməyiniz lazım deyil. Vacib bir neçə məqam var.
window.openai kim və nə zaman yaradılır
window.openai yalnız:
- ChatGPT sizin vidjetiniz üçün yaratdığı həmin iframe daxilində olur;
- HTML şablonu düzgün mimeType ilə (text/html+skybridge) verilib və bütün yoxlamalardan keçib.
Bu növü HelloWorld App modulu haqqında danışanda görmüşdünüz — məhz bu, vidjet səhifəsinin adi text/html əvəzinə qaytardığı dəyərdir.
Əgər vidjet səhifəsini brauzerdə birbaşa açırsınızsa:
console.log(window.openai); // undefined
— bu normaldır. Odur ki, lokal inkişaf və ya storybook üçün «standalone» rejimə bel bağlayırsınızsa, vidjet kodunda obyektin mövcudluğunu həmişə yoxlamağa dəyər.
Sadə nümunə (final deyil, sadəcə illüstrasiya):
if (typeof window !== "undefined" && (window as any).openai) {
console.log("We are inside ChatGPT sandbox!");
}
İnsializasiya asinxronluğu
Pərdə arxasında ChatGPT yeni məlumatlar gəldikcə (toolOutput yenilənəndə, displayMode dəyişəndə və s.) window.openai-i openai:set_globals adlı daxili hadisə ilə yeniləyir.
Yəni oradakı «dəyərlər» statik deyil: İİ modeli MCP alətini çağıra bilər, backend yeni structuredContent qaytarar və window.openai.toolOutput React komponentiniz işləyən anda dəyişə bilər.
Buradan iki tövsiyə:
- Aşağıdakı kimi «kor» snapshot-lar etməyin: const toolOutput = window.openai.toolOutput bir dəfə başlanğıcda götürüb onun əbədi qalacağını düşünmək olmaz. Eyni vidjet ChatGPT tərəfindən təkrar istifadə oluna bilər.
- Dəyişikliklərə abunə ola bilən hook qatından (bir azdan) istifadə edin.
3. window.openai anatomiyası: məlumatlar, API və kontekst
Rəsmi sənədlərdə window.openai üçün sahələr və metodlar barədə kifayət qədər kompakt cədvəl var. Gəlin bunu daha «insani» formada yığaq.
Əsas sahələr və metodlar
window.openai = {
// Vəziyyət & məlumatlar
toolInput, // JSON: İİ sizin MCP-tool-a ötürdüyü parametrlər
toolOutput, // JSON: MCP-tool-un İİ-yə qaytardığı parametrlər
toolResponseMetadata, // MCP-tool cavabının bir hissəsi: _meta: {...}
widgetState, // Vidjetin yadda saxlanmış vəziyyətini oxumaq olar
setWidgetState, // Vidjetinizin vəziyyətini burada saxlamaq olar
// İcra zamanı API-lər
callTool, // MCP-tool çağırmaq olar
sendFollowUpMessage, // Çatda İİ-yə gizli mesaj yazmaq: cavab verməyə başlayacaq
requestDisplayMode, // Vidjeti başqa bir rejimə keçirməyi xahiş et: fullscreen, pip, inline
requestModal, // Vidjeti modal pəncərəyə çevirmək.
requestClose, // Vidjeti bağlayır. Modal bağlananda — yenidən vidjet olur.
requestCheckout, // Ödəniş üçün modal pəncərə açır. Server ACP-ni reallaşdırmalıdır
notifyIntrinsicHeight, // Vidjetin hündürlüyünün dəyişdiyini bildirin
openExternal, // Keçidi yeni pəncərədə açmaq
// Kontekst
theme, // Tünd və ya açıq mövzu
displayMode, // Vidjetin cari görüntü rejimi, requestDisplayMode-dan fərqli ola bilər
maxHeight, // Vidjet üçün icazə verilən maksimal hündürlük
safeArea, // "Təhlükəsiz render sahəsi" — çentiqli telefonlar üçün aktualdır
view,
userAgent, // Brauzerin userAgent-i
locale // Brauzerin locale-i
}
Eyni məlumatı cədvəldə:
| Kateqoriya | Xüsusiyyət / metod | Nəyə lazımdır |
|---|---|---|
| State & data | |
Alətin çağırıldığı arqumentlər. Yalnız oxunur. |
| State & data | |
Sizin MCP cavabından gələn structuredContent. Vidjet və modelin gördüyü şey. |
| State & data | |
Cavabdan _meta. Yalnız vidjetə görünür, model bunu oxumur. |
| State & data | |
ChatGPT-nin vidjet renderləri arasında saxladığı UI vəziyyətinin snapshot-ı. |
| State & data | |
widgetState-in yeni snapshot-ını sinxron saxlayır. |
| Function | |
Vidjetdən MCP alətini çağırmaq. |
| Function | |
ChatGPT-dən vidjet adından çata mesaj göndərməsini xahiş etmək. O, cavab verməyə başlayacaq. |
| Function | |
Hostdan inline / fullscreen / pip xahiş etmək. |
| Function | |
Modal pəncərə açmağı xahiş etmək. |
| Function | |
Məzmunun hündürlüyü dəyişdiyini bildirmək. |
| Function | |
ACP protokolu üzrə ödəniş dialoqunu açır. |
| Function | |
Xarici linki istifadəçinin brauzerində açmaq. |
| Context | |
Mühit siqnalları: mövzu, rejim, əlçatan hündürlük, lokal və s. |
Hamısını dərhal əzbərləmək lazım deyil — bunu «xəritə» kimi qəbul edin. İndi bunu «arayış» kimi yox, normal insan kimi başa düşək.
toolInput və toolOutput: məlumatlar haradan gəlir
Model alətinizi çağırmaq qərarına gələndə JSON arqumentləri formalaşdırır. Bu arqumentlər:
- MCP serverinə handler-də input kimi gəlir;
- eyni zamanda vidjetdə window.openai.toolInput-a düşür.
Alət yerinə yetirildikdən sonra server qaytarır:
- structuredContent — UI üçün strukturlaşdırılmış məlumatlar;
- _meta — yalnız vidjet üçün özəl məlumatlar;
- content — modelin özünün istifadəçiyə «nə baş verdiyini danışması» üçün mətn.
structuredContent olur window.openai.toolOutput, _meta isə olur window.openai.toolResponseMetadata.
Mini nümunə (vanilla JS, React-siz):
const root = document.getElementById("root");
// Nullish operatorundan təhlükəsiz istifadə edə bilərsiniz
const gifts = window.openai.toolOutput?.gifts ?? [];
root.textContent = `Tapılan hədiyyələrin sayı: ${gifts.length}`;
widgetState və setWidgetState: vidjetin yaddaşı
widgetState — platformanın renderlər və hətta dialoqun ayrı gedişləri arasında sizin UI barədə yadda saxlamağa hazır olduğu şeydir.
widgetState üçün uyğun nümunələr:
- seçilmiş hədiyyə;
- cari çeşidləmə (qiymətə görə / populyarlığa görə);
- listdəki səhifə nömrəsi.
Uyğunsuz nümunələr:
- kənar API-nin xam cavabı;
- base64 şəklində təsvir;
- gizli tokenlər.
İki şeyi yadda saxlayın:
- widgetState kontekstlə birlikdə modelə də ötürülür, ona görə oraya həssas heç nə qoymuruq.
- Həcm məhduddur (təxminən 4 min token), odur ki, onu mini-bazaya çevirmirik.
Ən sadə istifadə nümunəsi (hook-suz, vanilla JS):
const current = window.openai.widgetState ?? { selectedGiftId: null };
function selectGift(id) {
window.openai.setWidgetState({ ...current, selectedGiftId: id });
}
Real kodda bunu React hook-ları ilə qablaşdıracağıq.
Runtime API: callTool, sendFollowUpMessage və digərləri
Bu metodlar vidjetin təkcə «çəkilməsinə» deyil, həm də dialoq və serverlə qarşılıqlı əlaqəsinə imkan verir.
Tipik ssenarilər:
- callTool("search_gifts", { budget: 50 }) — istifadəçi «Büdcəni dəyiş» düyməsini basır, siz serveri çağırıb UI-ni yeniləyirsiniz;
- sendFollowUpMessage({ prompt: "Daha bahalı ideyalar göstər" }) — istifadəçidən mətn yazmasını xahiş etmək əvəzinə follow-up düyməsi əlavə edirsiniz, o da çatda yeni mesaj yaradır;
- requestDisplayMode({ mode: "fullscreen" }) — inline rejim dar gəlibsə, vidjet nəzakətlə ChatGPT-dən tam ekrana keçməyi xahiş edə bilər;
- openExternal({ href: "https://myshop.com/checkout?giftId=123" }) — istifadəçini təsdiqlənmiş kanalla xarici sayta (checkout, profil və s.) yönləndirmək.
Bunların hamısı birbaşa internetə yox, ChatGPT üzərindən «xət»lə gedir.
Mühit konteksti: mövzu, rejim, hündürlük, lokal
theme, displayMode, maxHeight, locale kimi sahələr vidjetin hansı mühitdə yaşadığını anlamağa kömək edir.
Nümunə:
const theme = window.openai.theme; // "light" və ya "dark"
const mode = window.openai.displayMode; // "inline" | "fullscreen" | "pip"
const maxH = window.openai.maxHeight; // əlçatan hündürlük
const locale = window.openai.locale; // "en-US", "de-DE", ...
Bu siqnallarla siz:
- rəngləri və boşluqları mövzuya görə tənzimləyə bilərsiniz;
- rejimdən asılı olaraq layout-u dəyişə bilərsiniz (inline vs fullscreen);
- UI yazılarını istifadəçinin dilinə uyğunlaşdırabilirsiniz (bu mövzuya ayrıca modulda baxacağıq).
Platforma sizə nə qədər yer olduğu, hansı mövzu və lokalın aktiv olduğu barədə siqnallar verir. Onları useOpenAIGlobal, useDisplayMode, useMaxHeight və digər hook-lar vasitəsilə istifadə etmək məntiqlidir ki, vidjet ChatGPT-də «doğma» görünsün.
4. window.openai üzərində hook-lar: qlobal obyektə əllə toxunmuruq
window.openai-ə birbaşa çıxış prototip üçün rahatdır, amma tezliklə kodu xaosa çevirir: hadisələrə abunələr, undefined yoxlamaları, təkrarlanan qablaşdırmalar. Məhz buna görə Apps SDK üçün Next.js şablonunda detalları gizlədən və hər şeyi reaktiv edən hazır React hook dəsti var.
Tipik hook indeks faylı belə görünür:
// app/hooks/openai/index.ts
export { useCallTool } from "./use-call-tool";
export { useSendMessage } from "./use-send-message";
export { useOpenExternal } from "./use-open-external";
export { useRequestDisplayMode, useRequestModal, useRequestClose } from "./use-request-display-mode";
export { useRequestCheckout } from "./use-request-checkout";
// State hooks
export { useDisplayMode } from "./use-display-mode";
export { useWidgetProps } from "./use-widget-props";
export { useWidgetState } from "./use-widget-state";
export { useOpenAIGlobal } from "./use-openai-global";
export { useMaxHeight } from "./use-max-height";
export { useIsChatGptApp } from "./use-is-chatgpt-app";
Adlar və dəqiq yol sizin şablonda bir qədər fərqlənə bilər, amma fikir eynidir: komponentdə window.openai.* yerinə hook-lardan istifadə edirsiniz. Gələn əsaslarını baxaq.
useWidgetProps: alətin girişi və çıxışı
useWidgetProps adətən vidjetə lazım olan məlumatlarla obyekt qaytarır: toolInput, toolOutput, toolResponseMetadata və bəzən isLoading kimi əlavə flag-lər.
Nümunə:
import { useWidgetProps } from "../hooks/openai";
type Gift = { id: string; title: string; price: number };
export function GiftList() {
const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
const gifts = toolOutput?.gifts ?? [];
if (!gifts.length) {
return <div>Hələ hədiyyə variantı yoxdur.</div>;
}
return (
<ul>
{gifts.map((g) => (
<li key={g.id}>{g.title} — ${g.price}</li>
))}
</ul>
);
}
Komponent kodunda heç bir window.openai yoxdur — və bu yaxşıdır.
useWidgetState: widgetState üzərində «reaktiv qablaşdırma»
useWidgetState widgetState ilə adi React state-i kimi işləməyə imkan verir: [state, setState] alırsınız, hook isə pərdə arxasında bunu window.openai.widgetState və setWidgetState ilə sinxronlaşdırır.
Nümunə:
import { useWidgetState } from "../hooks/openai";
type UiState = { selectedGiftId: string | null };
export function SelectedGiftIndicator() {
const [uiState, setUiState] = useWidgetState<UiState>(() => ({
selectedGiftId: null,
}));
if (!uiState?.selectedGiftId) {
return <div>Hələ hədiyyə seçilməyib.</div>;
}
return (
<div>
Siz id={uiState.selectedGiftId} olan hədiyyəni seçdiniz
<button onClick={() => setUiState({ selectedGiftId: null })}>
Sıfırla
</button>
</div>
);
}
Klikdən sonra setUiState təkcə React state-i yeniləməyəcək, həm də yeni vəziyyəti ChatGPT tərəfində saxlayacaq.
useOpenAIGlobal: window.openai-dən istənilən sahəyə çıxış
Əgər bir qlobal sahəyə (məsələn, mövzuya və ya rejimə) çıxış lazımdırsa, universal useOpenAIGlobal(key) hook-u var. O, openai:set_globals hadisəsinə abunə olur və həmişə aktual dəyəri qaytarır.
Nümunə:
import { useOpenAIGlobal } from "../hooks/openai";
export function ThemeAwareBlock() {
const theme = useOpenAIGlobal<"light" | "dark">("theme");
const background = theme === "dark" ? "#222" : "#fff";
const color = theme === "dark" ? "#fff" : "#000";
return <div style={{ background, color }}>Mən ChatGPT mövzusuna uyğunlaşıram</div>;
}
useCallTool, useSendMessage, useOpenExternal və digərləri
- useCallTool(name) — göstərilən adlı MCP alətini çağıran funksiya qaytarır. Bu, callTool üzərində qablaşdırmadır.
- useSendMessage() — vidjetin mesajları təşəbbüs etməsi üçün sendFollowUpMessage üzərində qablaşdırma.
- useOpenExternal() — openExternal({ href }) ətrafında rahat köməkçi.
- useRequestDisplayMode() və useRequestModal() — rejim dəyişikliyi / modal açılması üçün qablaşdırmalar.
Demək olar ki, hər şeyi birdən istifadə edən kiçik GiftGenius vidjeti üçün baza nümunəsi:
import {
useWidgetProps,
useWidgetState,
useCallTool,
useSendMessage,
useOpenExternal,
} from "../hooks/openai";
type Gift = { id: string; title: string; url: string; price: number };
export function GiftWidget() {
const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
const gifts = toolOutput?.gifts ?? [];
const [ui, setUi] = useWidgetState<{ selectedId: string | null }>(() => ({
selectedId: null,
}));
const callSearch = useCallTool("search_gifts");
const sendMessage = useSendMessage();
const openExternal = useOpenExternal();
if (!gifts.length) {
return <div>Hələ ideya yoxdur. Nəticələri yeniləməsi üçün GPT-dən xahiş edin.</div>;
}
return (
<div>
{gifts.map((g) => (
<button
key={g.id}
style={{
display: "block",
fontWeight: ui?.selectedId === g.id ? "bold" : "normal",
}}
onClick={() => setUi({ selectedId: g.id })}
>
{g.title} — ${g.price}
</button>
))}
<div style={{ marginTop: 12 }}>
<button
onClick={() =>
sendMessage({ prompt: "Mövcudlardan daha bahalı hədiyyələr göstərin." })
}
>
Daha çox ideya istə
</button>
<button
onClick={async () => {
await callSearch({ budget: 200 });
}}
>
Büdcə $200 ilə yenilə
</button>
{ui?.selectedId && (
<button
onClick={() =>
openExternal({
href: `https://giftgenius.example.com/checkout?id=${ui.selectedId}`,
})
}
>
Alışa keç
</button>
)}
</div>
</div>
);
}
Bu səhifə hələ xamdır (növbəti modullarda UX, xəta emalı və s. əlavə edəcəyik), amma yanaşmanı artıq göstərir: heç bir birbaşa window.openai müraciəti yoxdur, yalnız hook-lar var.
5. Praktika: qum qutusunu və window.openai-ni araşdırırıq
«Vidjet adi sayt deyil» hissini qavramaq üçün bir neçə məşqi etmək faydalıdır.
Tapşırıq: «Mühiti yoxla»
Vidjetinizdə cari app/page.tsx-ə ilk renderdə sadə effekt əlavə edin:
import { useEffect } from "react";
import { useIsChatGptApp } from "../hooks/openai";
export default function Root() {
const isChatGpt = useIsChatGptApp();
useEffect(() => {
if (typeof window !== "undefined") {
console.log("window.origin =", window.origin);
console.log("window.openai =", (window as any).openai);
}
}, []);
return (
<main>
<h1>GiftGenius widget</h1>
<p>ChatGPT daxilində işə salınıb: {String(isChatGpt)}</p>
</main>
);
}
DevTools-u açın: ya birbaşa ChatGPT pəncərəsində (əgər tunel viewer buna icazə verirsə), ya da səhifəni birbaşa açarkən lokal brauzerdə. Hər iki halda müqayisə edin:
- adi brauzerdə işə salınanda isChatGptApp false olacaq, window.openai isə çox güman undefined;
- ChatGPT vasitəsilə işə salınanda toolInput, toolOutput, theme və s. sahələri olan obyekt görəcəksiniz.
Bu, yaxşı bir intuitiv hissdir: eyni React kodu mühitdən asılı olaraq fərqli davranır, və məhz bunun üçün hook-lar düşünüb tapılıb.
Tapşırıq: «Platformanın verdiyi hər şeyi çıxar»
Müvəqqəti debug komponenti əlavə edin:
import { useWidgetProps, useOpenAIGlobal } from "../hooks/openai";
export function DebugPanel() {
const { toolInput, toolOutput, toolResponseMetadata } = useWidgetProps();
const theme = useOpenAIGlobal("theme");
const displayMode = useOpenAIGlobal("displayMode");
return (
<pre style={{ fontSize: 10, maxHeight: 200, overflow: "auto" }}>
{JSON.stringify(
{ toolInput, toolOutput, toolResponseMetadata, theme, displayMode },
null,
2
)}
</pre>
);
}
Və müvəqqəti olaraq əsas UI-nin altına <DebugPanel /> daxil edin. Beləliklə, açıq-aydın görəcəksiniz:
- toolOutput-a MCP-dən konkret hansı sahələrin gəldiyini;
- _meta-da nə olduğunu (məsələn, locale, userLocation və s.);
- vidjeti böyüdəndə displayMode-un necə dəyişdiyini.
Sonradan bu komponenti silə və ya, məsələn, DEBUG_WIDGET kimi flag ilə idarə oluna bilən saxlaya bilərsiniz.
6. Münasibətlər: ChatGPT ↔ vidjet ↔ MCP/server
Vidjetə «sistemin baş iştirakçısı» kimi yanaşmamaq üçün rolları bir daha dəqiq qeyd etmək faydalıdır.
- İstifadəçi mesaj yazır: «Qız üçün hədiyyə seç, büdcə 50$».
- ChatGPT modeli sizin MCP alətiniz search_gifts-i { recipient: "girlfriend", budget: 50 } arqumentləri ilə çağırmağa qərar verir.
- MCP server biznes məntiqini yerinə yetirir, qaytarır:
- model üçün qısa təsvirlə content;
- hədiyyələr massivini əhatə edən structuredContent;
- texniki detallarla _meta (məs., source və valyuta).
- ChatGPT:
- istifadəçiyə mətn mesajını göstərir («Bir neçə variant tapdım...»);
- vidjet-iframe yaradır və ora structuredContent və _meta-nı window.openai.toolOutput və toolResponseMetadata vasitəsilə ötürür.
- Sizin vidjet:
- toolOutput-a əsasən UI-ni render edir;
- interaksiyalar zamanı callTool çağırır və ya follow-up göndərir;
- Model bundan sonra bu hərəkətlərin nəticələri ilə nə edəcəyinə qərar verir.
Bütün bunlar vacib fikrə gətirir: vidjet prosesi heç vaxt tək idarə etmir. O — model və MCP serverdən ibarət ekosistemdə yaşayan UI qatıdır. Mürəkkəb işlər (autentifikasiya, məxfi məlumatlara çıxış, ciddi biznes məntiqi) server tərəfində qalmalıdır. Vidjet isə rahat interfeys və istifadəçi ilə səliqəli ünsiyyət üçün cavabdehdir.
7. Qum qutusunda qaydalar və siyasətlər
İzolyasiya olunmuş iframe və window.openai sadəcə «belə» mövcud deyil, təhlükəsizlik və məxfilik tələblərinə görə var. OpenAI-nin rəsmi qaydaları bir neçə prinsipi vurğulayır.
Birincisi, məlumatların minimallaşdırılması. Vidjet vasitəsilə istifadəçidən mümkün qədər çox PII (personally identifiable information) toplamağa çalışmamalısınız. Həqiqətən vacib olan hər şey alətlərdə dəqiq təsvir olunmalıdır və həm model, həm də təhlükəsizlik qatı belə çağırışları diqqətlə izləyəcək.
İkincisi, gizli izləmə və fingerprinting qadağandır. İstifadəçinin cihazını «gizlicə izləyən», brauzerin barmaq izlərini toplayan və məhdudiyyətləri dolanan sistemlər qurmaq olmaz. userAgent, userLocation və s. parametrlər — UX üçün ipucudur, autentifikasiya və ya identifikasiya üçün yox.
Üçüncüsü, structuredContent, _meta, widgetState-ə qoyduğunuz hər şey hansısa formada ya istifadəçiyə görünür, ya da Store-un reviwer-i tərəfindən görülə bilər. Buna görə:
- heç bir API açarı, token, parol və admin sirləri ora qoymaq olmaz;
- vidjet vəziyyətini elə dizayn edin ki, istifadəçi onu loglarda və ya debugda görəndə təəccüblənməsin.
Dördüncüsü, şəbəkə çağırışları. Vidjetdən kənar API-lərə birbaşa sorğular yalnız ciddi şəkildə məhdud siyahıdakı domenlərə və həssas olmayan ssenarilərdə mümkündür. Söhbət pul, hesablar, məxfi məlumatlardan gedirsə — hamısı MCP/backend üzərindən olmalıdır.
8. Qum qutusunda və window.openai ilə işləyərkən tipik səhvlər
Səhv №1: vidjeti «iframe-də adi sayt» hesab etmək.
Yeni başlayanlar vərdişlə window.parent-ə girməyə, ChatGPT-nin stillərini dəyişməyə və ya localStorage-dan həmişəki kimi istifadə etməyə çalışırlar. Qum qutusunda bu ya işləmir, ya da qeyri-sabit işləyir: origin fərqlidir, storage izolyasiya olunub, DOM-a çıxış bloklanır. İdarə olunan mühitdə yaşadığınızı qəbul edin və hostla yalnız window.openai və hook-lar vasitəsilə ünsiyyət qurun.
Səhv №2: window.openai-ə hər yerdə birbaşa toxunmaq.
Onlarla komponentdə window.openai.toolOutput oxumaq — çətin debug olunan tətbiq yoludur. Bu halda hadisələrə, asinxronluğa və undefined yoxlamalarına özünüz nəzarət etməlisiniz. Daha etibarlısı, artıq openai:set_globals-ı qablaşdıran və vəziyyəti sinxronlaşdıran useWidgetProps, useWidgetState, useOpenAIGlobal və digər hook-lardan dərhal istifadə etməkdir.
Səhv №3: widgetState-də hər şeyi (xüsusən də sirləri) saxlamaq.
Bəzən «birdən lazım olar» deyə oraya nəhəng API nəticələrini və ya hətta giriş tokenini qoymaq istəyirsiniz. Nəticədə kontekst böyüyür, modelin işi pisləşir və siz təhlükəsizlik tələblərini pozursunuz. widgetState kiçik olmalıdır, yalnız UI siqnallarını saxlamalıdır və heç vaxt məxfi məlumatları yox.
Səhv №4: internetə vidjetdən birbaşa çıxmağa çalışmaq.
Qum qutusundan fetch("https://api.superbank.com/...") çağırışları demək olar ki, həmişə CORS-a ilişəcək, hətta hər şeyi ideal sazlasanız belə, bu nə təhlükəsizdir, nə də idarəolunandır. Hesablar, pullar və şəxsi məlumatlarla bağlı hər şey MCP alətləri kimi reallaşdırılmalı və callTool və ya server tərəfi vasitəsilə çağırılmalıdır.
Səhv №5: ChatGPT-dən kənarda window.openai-ın sabit olmasına güvənmək.
Bəzən inkişaf etdiricilər vidjeti ayrıca SPA kimi işə salırlar və window.openai-ın undefined ola biləcəyini yoxlamırlar. Dev mühitində bu, «Cannot read properties of undefined» ilə crash-lə bitir. useIsChatGptApp, typeof window !== "undefined" yoxlamalarından və vidjet olmayan hallar üçün fallback UI-dan istifadə edin.
Səhv №6: mühit kontekstini (theme, displayMode, maxHeight, locale) nəzərə almamaq.
Əlbəttə, 2000px sabit hündürlük, həmişə tünd mövzu və yalnız desktop üçün veriliş təyin edə bilərsiniz — amma bu, istifadəçi təcrübəsini qəribə edəcək. Platforma sizə nə qədər yer olduğu, hansı mövzu və lokalın aktiv olduğu barədə siqnallar verir — useOpenAIGlobal, useDisplayMode, useMaxHeight və s. vasitəsilə onlardan istifadə edin ki, vidjet ChatGPT-də «doğma» görünsün.
Səhv №7: siyasəti üçüncü tərəf skriptləri ilə «dolamağa» çalışmaq.
Bəzən hansısa tracker, üçüncü tərəf JS bundle-ı qoşmaq və ya «səssizcə» yad domenlərdən kod icra etmək istəyi yaranır. Qum qutusu və CSP siyasətləri məhz bunun qarşısını almaq üçün var: üçüncü tərəf skriptləri bloklanır və sistemi dolamaq cəhdləri App-ınızın Store-da rədd edilməsinə aparır.
GO TO FULL VERSION