UI
Hogyan épül fel és fut egy natív worker. Célpontok, életciklus, használható komponensek és hookok, az egyes hívások korlátai, valamint blokkbeállítások és fordítások.
Ezen az oldalon
A worker egy olyan React-kód, amely HTML helyett a Poltura komponenseivel rajzol. Különálló, biztonságos környezetben fut, nem az adminfelület vagy a webáruház oldalán, és leírást küld a platformnak arról, mit kell megjeleníteni; az adminfelület vagy a téma pedig a saját komponenseivel rajzolja ki azt. A worker ezért nem éri el az oldal DOM-ját, a cookie-kat vagy a hálózatot, és éppen ezért néz ki natívnak bárhol, ahol megjelenik.
Minden worker-fájl ugyanúgy kezdődik: először importáld a @poltura/app-ui/worker csomagot, majd jelenítsd meg a root komponenst a @poltura/app-ui/worker-runtime csomag startUiWorker függvényével.
Célpontok
Adminfelület: admin.page, admin.home, admin.product, admin.collection, admin.order, admin.customer.
Webáruház-sablonok: storefront.home, storefront.products, storefront.product, storefront.cart, storefront.checkout, storefront.login, storefront.register, storefront.account, storefront.contact, storefront.page.
Layout: storefront.layout.header, storefront.layout.footer.
A header zónák önálló, teljes szélességű sort alkotnak a navigáció alatt, a rögzített headeren kívül, mobilon is: a magasabb worker lefelé tolja az oldalt ahelyett, hogy kitakarná. A lábléczónák szintén a dokumentumfolyamban helyezkednek el.
App-oldal: storefront.app-page.
Egy release legfeljebb 16 workert tartalmazhat, amelyek mérete egyenként legfeljebb 1048576 bájt. Egy renderelt snapshot legfeljebb 32768 bájt és 8 szint mély lehet, valamint legfeljebb 128 node-ot és node-onként 24 gyermekelemet tartalmazhat.
Életciklus
Amikor egy worker csatolódik, a platform elküldi neki a munkamenetet és a kontextusát (nyelvi beállítás, beállítások, téma). A startUiWorker rendereli a komponenst, és minden renderelés után snapshotot küld. Ettől kezdve a worker eseményeket fogad (egy press, egy change), kontextusfrissítéseket kap a beállítások változásakor, valamint megkapja a tool calljaira adott válaszokat. Amikor a kereskedő vagy a vásárló távozik, a worker megsemmisül. A platform minden tool callt újra ellenőriz a telepítés alapján, mielőtt végrehajtaná azt.
A teljes példa adminfelületi oldala:
export function AdminPage(): ReactNode {
const copy = copyFor(useLocale());
const settings = useSettings();
const navigate = useNavigate();
const toast = useToast();
const pick = useResourcePicker();
const saveFeatured = useAction('save_featured');
const heading = String(settings.heading ?? copy.heading);
const [picked, setPicked] = useState('');
return (
<Card className='offer-panel rounded-theme bg-surface text-foreground p-4'>
<Stack>
<Heading>{heading}</Heading>
<Text>{copy.adminIntro}</Text>
<TextField label={copy.featuredLabel} value={picked} onChange={setPicked} />
<Inline>
<Button
label={copy.pickProducts}
onPress={async () => {
const ids = await pick('product', true);
if (ids === null) return;
setPicked(ids.join(','));
await toast('success', copy.pickedToast);
}}
/>
<Button
label={copy.save}
onPress={async () => {
const productIds = picked
.split(',')
.map((value) => value.trim())
.filter((value) => value.length > 0);
await saveFeatured({
productIds,
expectedVersion: 0,
idempotencyKey: crypto.randomUUID(),
});
await toast('success', copy.savedToast);
}}
/>
<Button
label={copy.openPage}
onPress={() => navigate({ kind: 'app_page', pageId: RECOMMENDATIONS_PAGE })}
/>
</Inline>
</Stack>
</Card>
);
}export function AdminProduct(): ReactNode {
const copy = copyFor(useLocale());
const selected = useSelectedEntity();
const navigate = useNavigate();
if (selected.status !== 'ready' || selected.entity.kind !== 'product') {
return <Text>{copy.unavailable}</Text>;
}
return (
<Card className='rounded-theme bg-surface text-foreground p-4'>
<Stack>
<Heading>{selected.entity.product.title}</Heading>
<Button
label={copy.openSettings}
onPress={() => navigate({ kind: 'app_page', pageId: RECOMMENDATIONS_PAGE })}
/>
</Stack>
</Card>
);
}export function StorefrontProduct(): ReactNode {
const copy = copyFor(useLocale());
const settings = useSettings();
const theme = useTheme();
const cart = useCart();
const toast = useToast();
const host = useStorefrontContext();
const recommend = useAction('recommend');
const heading = String(settings.heading ?? copy.alsoLike);
const storeName = theme?.settings?.store_name;
const [contextLabel, setContextLabel] = useState(copy.alsoLike);
useEffect(() => {
void host.read().then(async (context) => {
if (context.kind === 'product') setContextLabel(context.product.title);
const suggestion = await recommend({
productId: context.kind === 'product' ? context.product.id : null,
});
const title = z.object({ title: z.string().min(1) }).safeParse(suggestion);
if (title.success) setContextLabel(title.data.title);
});
}, [host, recommend]);
return (
<Card className='offer-panel rounded-theme bg-surface text-foreground p-4'>
<Stack>
<Heading className='font-display'>{heading}</Heading>
<Text>{z.string().min(1).safeParse(storeName).data ?? contextLabel}</Text>
<Button
label={copy.addToCart}
className='bg-primary text-on-primary'
onPress={async () => {
const parsed = VariantId.safeParse(settings.variant_id);
if (!parsed.success) {
await toast.show({ tone: 'warning', message: copy.cartRejected });
return;
}
const reply = await cart.add({ variantId: parsed.data, quantity: 1 });
if (reply.status === 'ok') {
await toast.show({ tone: 'success', message: copy.added });
return;
}
await toast.show({ tone: 'warning', message: copy.cartRejected });
}}
/>
</Stack>
</Card>
);
}Primitive-ek
Node type-ok: Stack, Inline, Card, Text, Heading, Button, TextField, Select, Checkbox, Alert, Divider. A className legfeljebb 2048 karakter, control character-ek nélkül. A Text/Heading/Alert gyermekelemei content tartalommá válnak (legfeljebb 2000 karakter). A Heading level értéke 2 | 3 | 4 lehet. A Button label legfeljebb 160 karakter. A Select options 1 és 24 között lehet. Az onChange értéket kap, nem DOM eventet. A HTML és a DOM libraryk nem támogatottak.
Hookok
| Hook | Belépési pont | Visszatérési érték |
|---|---|---|
useSettings | adminfelület, webáruház | Feloldott elhelyezési beállítások (Record<string, string | number | boolean | null>, max. 64 kulcs) |
useLocale | adminfelület, webáruház | LocaleTag string |
useTheme | adminfelület, webáruház | Opcionális UiAppearance (mód, tokenek, téma, deklarált nyilvános beállítások) |
useExtensionContext | adminfelület, webáruház | Teljes UiExtensionContext |
useSelectedEntity | adminfelület | { status: 'loading' | 'unavailable' } vagy { status: 'ready', entity } product | collection | order | customer esetén |
useHostContext | adminfelület, webáruház | { status, context?: UiTargetContext } |
useNavigate | adminfelület | (destination: UiNavigateDestination) => Promise<void> (app_page, host egy host célponttal, storefront_path) |
useNavigate | webáruház | { to(path): Promise<void> } (StorefrontNavigatePath) |
useToast | adminfelület | (tone: UiToastTone, message) => Promise<void> (üzenet max. 200 karakter) |
useToast | webáruház | { show({ tone, message }) } |
useResourcePicker | adminfelület | (kind: 'product' | 'collection', multiple) => Promise<string[] | null> (azonosítók max. 50) |
useCart | webáruház | { read, add, update, remove } |
useStorefrontContext | webáruház | { read(): Promise<UiTargetContext> } |
Képességek és szabályzat
Minden képesség rendelkezik timeouttal, a folyamatban lévő hívások korlátjával, egy request window-val, valamint az elérhető felületek listájával:
| Művelet | timeoutMs | maxPending | windowMs | windowRequests | felületek |
|---|---|---|---|---|---|
read_selected_entity | 5000 | 2 | 10000 | 8 | adminfelület |
read_context | 5000 | 2 | 10000 | 8 | adminfelület, webáruház |
navigate | 3000 | 1 | 10000 | 8 | adminfelület, webáruház |
toast | 2000 | 2 | 10000 | 16 | adminfelület, webáruház |
pick_resource | 15000 | 1 | 10000 | 4 | adminfelület |
cart.add | 5000 | 2 | 10000 | 8 | webáruház |
cart.update | 5000 | 2 | 10000 | 8 | webáruház |
cart.remove | 5000 | 2 | 10000 | 8 | webáruház |
cart.read | 3000 | 2 | 10000 | 16 | webáruház |
Egy képesség eredménye legfeljebb 16384 bájt lehet. A kosár mennyisége 1–99. UiCartRejectionReason: out_of_stock, invalid_variant, limit, unavailable. Válaszok: ok, unavailable, cancelled, rejected.
Beállítások és fordítások
A worker beállításai az element definition settings mezőjében találhatók, amelyeket a @poltura/app-sdk/elements csomag defineBlock és field.* függvényeivel írhatsz meg (ugyanabban a formátumban, amelyet a theme blockok használnak). A poltura app build statikusan olvassa be a definíciót, soha nem futtatja, és beírja a worker element mezőjébe (a nyilvános element definition a @poltura/app-contracts/ui-element csomagban) a type kulcs nélkül. A témaszerkesztő a label, description, icon és category értékeket jeleníti meg a választóban, a beállításokat az ellenőrzőpanelen, a style értékeket a Stílus lapon, valamint a presets elemeket beszúráskor: a sima csempe csak a default azonosítójú presetet alkalmazza, és minden más preset saját külön csempeként jelenik meg utána.
| Szabály | Szerződés |
|---|---|
| Field type-ok | text, textarea, number, range, checkbox, select, radio, color, url, header, paragraph |
| Építők | field.text, textarea, number, range, checkbox, select, radio, color, url, group (header), note (paragraph) |
| Setting ID-k | snake_case vagy kebab-case, max. 64 karakter; a hitelesítő adatra emlékeztető és a fenntartott nevek hibát adnak |
| Mezők | max. 32; a teljes definíció legfeljebb 16384 bájt |
| Értékek | text, textarea, url, color max. 2000 karakter; a select és a radio egy opció value értékét veszi fel; a number és range a min/max között marad; a checkbox logikai |
| Opciók | 1–20 egyedi érték |
| Stílus lap | layout, spacing, size, typography, color, background, border, effects, visibility |
| Ikonok és kategóriák | A platform ikon- és kategórialistájából választva |
| Presetek | max. 8; a preset value-knak a deklarált mezők érvényes értékeinek kell lenniük |
Média, rich text, resource pickerek, darkDefault, gyermekelemek (blocks, slots) és templates nem érhetők el: a worker célpontja dönti el a sablont. A header és a paragraph nem hordoz értéket. A default nélküli mezők hiányoznak, amíg a kereskedő be nem állítja őket. Az element definition nélküli worker nem kap beállításokat.
A címkék, leírások, option labelek és preset labelek képezik az alapnyelvi szövegedet; a platform compile-olja őket a bolt minden nyelvére. A kereskedők a settings_i18n mezőben fordíthatják le a szöveges értékeket nyelvenként, amit a rendszer a téma mentésekor ugyanazon definíció alapján ellenőriz.
A manifest translations[] tömbje a displayName / description mezőket fedi le. Az oldalak translations[] mezője a címeket fedi le. A worker szövegezése a saját JSON-fájlod; a useLocale() a host environment nyelvi beállítását adja vissza. Ez a példa a src/locales/en.json és a src/locales/hu.json fájlokat tölti be:
{
"heading": "Recommendations",
"adminIntro": "Pick catalog products, then save. Native workers cannot call your origin; useAction save_featured relays to your backend.",
"featuredLabel": "Featured product ids",
"pickProducts": "Pick products",
"save": "Save",
"savedToast": "Featured products saved.",
"pickedToast": "Products selected.",
"openPage": "Open app page",
"unavailable": "No product is selected.",
"openSettings": "Open recommendations",
"alsoLike": "You may also like",
"addToCart": "Add to cart",
"added": "Added to cart.",
"cartRejected": "The cart could not add that variant.",
"emailLabel": "Email",
"subscribe": "Subscribe",
"invalidEmail": "Enter a valid email address.",
"subscribed": "Subscription captured.",
"pageIntro": "This storefront app page is /apps/recommendations from storefrontPages slug recommendations.",
"shopProducts": "Browse products",
"footerHelp": "Footer capture stays on this mount. It does not call /apps-api/v1."
}export function copyFor(locale: string): CopyTable {
const table = locale.toLowerCase().startsWith('hu') ? hu : en;
return CopyTable.parse(table);
}Hostolt keretek
Az adminFrame.actions az alábbi bridge actionök részhalmaza: toast, set_loading, navigate_host, pick_resource, resize, get_session_token, get_appearance. Azok a hostolt oldalak, amelyek ugyanezeket a primitive-eket szeretnék használni worker nélkül, a @poltura/app-ui/dom csomagot és a get_appearance műveletet használják. Lásd: Adminfelület.
Következő lépések
- Adminfelület és Webáruház: hol jelennek meg az egyes célpontok.
- Stílusok: CSS a workerekhez.