İçeriğe geç

Runtime Protocol

Bu sayfa İngilizceden çevrilmiştir. İngilizce sayfayı okuyun

Durum: taslak, zeyn::server ve assets/zeyn/*.js tarafından uygulanır (/_zeyn/zeyn.js olarak sunulur).

Bu, Zeyn sunucusu ile tarayıcı çalışma zamanı (runtime) arasındaki sözleşmedir. Ekranlar sunucuda oluşturulur. Tarayıcı runtime'ı, kullanıcı eylemlerini sunucuya gönderen ve dönen yanıtı uygulayan küçük bir betiktir. Uygulama kodu hiçbir zaman JavaScript yazmaz.

1. Page load

GET {route}?params={json} tam bir HTML belgesi döndürür. params, ekran parametrelerinin URL kodlu JSON halidir; ekranın parametresi yoksa atlanır.

/orders/{id} gibi yol segmentleri olan bir route, bu params alanlarını yoldan (/orders/42) okur, diğer alanları params içinden alır. Ayrıştırılamayan bir segment değeri 404 döndürür. Değerler yüzde kodludur, noktalar dahil (%2E); böylece bir ekran URL'si runtime'a hiçbir zaman dosya gibi görünmez. Screen::moved_to yanıtı veren bir ekran, yeni URL'yi Location içinde taşıyan 301 Moved Permanently alır.

Başarısız bir sayfa kendi durum koduyla (404, 403, 500, ...) ve uygulamanın hata ekranıyla (App::not_found, App::server_error) ya da kabuğun (shell) içinde kısa, yerleşik bir iletiyle yanıtlanır. x-zeyn-partial: 1 başlığı olan bir istek, yalnızca o ekranı x-zeyn-partial: 1 ve x-zeyn-title ile alır; böylece runtime ekranı sayfa yüklemeden yerine koyar. Hata ayrıntıları tarayıcıya asla ulaşmaz.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="zeyn-csrf" content="TOKEN">
  <title>Products · Inventory</title>
  <link rel="stylesheet" href="/_zeyn/zeyn.css?v=BUILD">
  <script type="module" src="/_zeyn/zeyn.js?v=BUILD"></script>
</head>
<body>
  <a class="z-skip" href="#z-main">Skip to content</a>
  <div class="z-app">
    <nav class="z-nav" aria-label="Main">...</nav>
    <main class="z-main" id="z-main">SCREEN</main>
  </div>
  <div class="z-toasts" role="status" aria-live="polite"></div>
</body>
</html>

Menü yalnızca geçerli kullanıcının açabileceği ekranları listeler. Geçerli öğede aria-current="page" bulunur.

Head içindeki her framework script'i ve stil dosyası ?v= ile build kimliğini taşır (meta[name=zeyn-build]; ZEYN_BUILD_ID'den ya da çalıştırılabilir dosyanın hash'inden gelir); theme.css tema metninin hash'ini taşır. Güncel değerli bir istek Cache-Control: public, max-age=31536000, immutable alır, bu yüzden sıcak bir sayfa yüklemesi bunların hiçbirini istemez; başka her istek (bir dağıtımdan sonra eski bir sekme, v yok) no-cache ve ETag alır. Çalışma zamanının sonradan import ettiği modüller, onları import eden script'in URL'sinden aynı v değerini alır.

Her sayfa zeyn.js (istekler, efektler, pencereler, formlar, menüler, kısayollar ve olay delegasyonu), nav.js, components.js ve uygulama kabuğunda pro.js ile enterprise.js dosyalarını yükler. Geri kalanı yalnızca onu kullanan sayfalarda yüklenir; nav.js bir ekranı yerine koyduğunda da böyledir: zeyn.js, içerik ilk kez bir [data-zeyn-grid] taşıdığında grid çalışma zamanı grid.js dosyasını import eder (grid içeren tam sayfalar onu <link rel="modulepreload"> içinde belirtir), components.js ise catalog.js (kart gridleri, arama kutuları), media.js (oynatıcılar, gömülü içerik), files.js (yüklemeler), charts.js ve assistant.js (sohbet panelleri ya da başlatıcının ilk basışı) dosyalarını import eder. Stil dosyaları head içinde kalır, bu yüzden script geldiğinde görünüm değişmez. Showcase panosunda bu, 9 script ve 403 KB (116 KB) yerine 5 script ve 258 KB (ağda 74 KB) demektir.

2. Screen markup

Her ekran kökü kendi adını ve parametrelerini taşır:

<section class="z-screen" data-zeyn-screen="product_list" data-zeyn-params="null" aria-labelledby="product_list-title">

Pencereler (Window) class="z-window" role="dialog" aria-modal="true" ve bir --z-width stili kullanır.

Her gövde öğesi bir düğüm (node) elemanına sarılır. Düğümün yolu (path), aşağıdaki endpoint'lerde kullanılan adrestir. Kapsayıcılar (Row, Stack, Panel, Card, Tabs, Split) her çocuğu aynı şekilde sarar ve çocuğun yolu kendi indeksini ekler: 1 numaralı gövde öğesinin ilk çocuğunun üçüncü çocuğu "1.0.2" olur.

<div class="z-node" data-zeyn-node="1">
  <div class="z-node" data-zeyn-node="1.0">...</div>
</div>

Endpoint'ler node değerini noktalı bir dize olarak ya da gövde öğeleri için düz bir sayı (0) olarak alır. Yollar en fazla 16 basamak seviyesindedir; başka her şey 422 ile reddedilir.

3. Attributes the runtime reads

AttributeOnMeaning
data-zeyn-effectbutton, trTıklamada çalıştırılacak JSON effect (satırlarda: çift tıklamada veya Enter ile)
aria-keyshortcutsbuttonKlavye kısayolu, örneğin Control+N
data-zeyn-exportbuttonAynı ekrandaki grid'in dışa aktarma biçimi (csv, xlsx)
data-zeyn-gridgrid rootBir grid'i işaretler; ayrıca data-zeyn-mode, data-zeyn-paging, data-zeyn-page-size, data-zeyn-total, data-zeyn-next, data-zeyn-offset taşır
data-field, aria-sort, aria-colindexthSütun alan adı, geçerli sıralama, konum
data-filter, data-optionsthFiltre girişi türü (text, number, money, date, datetime, time, choice, bool) ve seçim seçenekleri [{value,label}]
data-pinthSabitlenmiş sütunlar için start veya end (seçim ve satır eylemi sütunları da dahil)
data-edit, data-optionalthKaydetme yetkisi olan kullanıcılar için düzenlenebilir bir sütunun giriş türü
data-zeyn-texts, data-zeyn-columns, data-zeyn-viewgrid rootYerelleştirilmiş denetim metinleri, tüm sütunlar [{field,label,hidden,pin,width}], kaydedilmiş görünüm
data-zeyn-viewsgrid rootGörünümler sunucuda kaydediliyorsa server
data-zeyn-version-field, data-zeyn-exportablegrid rootSatır içi düzenlemeler için satır sürüm alanı adı; grid dışa aktarılabiliyorsa csv
data-zeyn-key, data-zeyn-version, data-zeyn-menutrSatır anahtarı, satır sürümü, bağlam menüsü öğeleri
data-vtdDüzenlenebilir bir hücrenin giriş değeri
data-zeyn-detail, aria-expandedtrSatırın {screen, params} biçimindeki ayrıntı ekranı; açık olup olmadığı
data-zeyn-expandbutton in a rowSatırın ayrıntısını, grubunu veya çocuklarını açar ya da kapatır
data-zeyn-level, aria-level, data-zeyn-pathtr in a treegridGruplanmış veya ağaç grid'inde derinlik (en üstte 0); satır açılırken gönderilecek grup değerlerinin veya üst anahtarların yolu
z-grouptrBir grup başlığı satırı; z-branch-more bir dalın "Daha fazla yükle" satırıdır
data-zeyn-runbutton in a rowÇalıştırılacak satır öğesi (a0 ilk satır eylemidir)
data-aggtfoot tdSütunun gösterdiği toplamlar (sum avg min max count)
data-zeyn-bulkdiv before the griddata-zeyn-bulk-run düğmeleri olan seçim çubuğu
data-zeyn-menuscreen rootEkranın bağlam menüsü
data-zeyn-native-menu="off"htmlMetin girişleri dışında tarayıcının bağlam menüsü kapalıdır
data-zeyn-rowstbodySatırların gittiği yer
data-zeyn-pagepager buttonprev veya next
data-zeyn-page-infospan1-50 of 230 gibi metin; grid kökündeki yerelleştirilmiş şablonlardan oluşturulur
data-zeyn-text-range, data-zeyn-text-range-total, data-zeyn-text-loadedgrid rootYerelleştirilmiş şablonlar: {from}-{to}, {from}-{to} of {total}, {count} rows loaded
data-zeyn-moredivSonsuz kaydırma nöbetçisi, "Daha fazla yükle" düğmesini içerir
data-zeyn-submitformGönderme eyleminin adı
data-zeyn-editorinputÖzel bir alan türünün istemci tarafı düzenleyicisi

Effect'ler:

EffectJSONRuntime does
Open window{"effect":"open_window","screen":"product_form","params":{...}}Ekranı (bölüm 4.1) modal bir <dialog> içine yükler
Navigate{"effect":"navigate","screen":"...","params":{...}}Sayfaya yönlendiren /_zeyn/route/{screen}?params= adresini yükler; route'unun segmentleri varsa yol biçiminde yönlendirir
Toast{"effect":"toast","message":"Saved"}4 saniye boyunca bir toast gösterir
Close and refresh{"effect":"close_and_refresh"}En üstteki diyaloğu kapatır ve altındaki ekrandaki her grid'i yeniden yükler

4. Endpoints

Tüm POST istekleri JSON gönderir, zeyn-csrf meta etiketindeki belirteçle birlikte x-zeyn-csrf başlığını taşımak zorundadır; aksi halde 403 ile reddedilir.

Oturumsuz açılan herkese açık bir ekran, önbelleğe alınabilmesi için boş bir zeyn-csrf meta etiketi ile ve Set-Cookie olmadan gelir. İlk POST'tan önce runtime GET /_zeyn/csrf çağrısını yapar; bu çağrı Cache-Control: no-store ile { "token": "..." } yanıtını verir, tarayıcıda yoksa zeyn_csrf çerezini ayarlar ve Sec-Fetch-Site değeri same-origin olmayan istekleri reddeder. Belirteç meta etiketine yazılır; çağrı başarısız olursa POST belirteçsiz gider ve diğerleri gibi reddedilir. Hiçbir şey sessizce yeniden denenmez.

Uygulama içi gezinme (nav.js) bir ekranı x-zeyn-partial: 1 ile ister ve yalnızca HTML'ini alır; sayfa başlığı x-zeyn-title içindedir. Ekran head etiketleri ayarlıyorsa (açıklama, canonical URL, önizleme görseli, robots, JSON-LD), gövde bunları tutan <template data-zeyn-head> ile başlar; nav.js belge head'indeki her [data-zeyn-head] elemanını kaldırır, şablonun etiketlerini oraya taşır ve şablonu siler. Head etiketi olmayan bir ekran şablon göndermez, bu yüzden eski etiketler yalnızca kaldırılır.

4.1 Screen fragment

GET /_zeyn/screen/{screen}?params={json} yalnızca ekranın HTML'ini, sayfa kabuğu olmadan döndürür. Pencereler ve satır ayrıntıları için kullanılır: data-zeyn-detail olan bir satırı açmak, ekranını hemen altındaki yeni bir tr.z-detail içine yükler (satırı kaplayan tek bir hücre, içinde bir role="region"), kapatmak bu satırı kaldırır. Ekranın izni (permission) diğer ekranlar gibi denetlenir; ayrıntı içindeki grid'ler sunucuyla ayrıntı ekranının kendi adı ve parametreleriyle konuşur.

4.2 Grid rows

POST /_zeyn/grid

{
  "screen": "product_list",
  "params": null,
  "node": 0,
  "query": {
    "page": { "type": "offset", "offset": 0, "limit": 50 },
    "sort": [{ "field": "code", "desc": false }],
    "filters": [{ "field": "name", "contains": "tea" }, { "field": "price", "from": "10", "to": "99.90" }],
    "with_total": true,
    "aggregates": [{ "field": "price", "op": "sum" }]
  },
  "view": { "columns": [{ "field": "code", "width": 140, "pin": "start" }, { "field": "name" }] },
  "head": false
}

Filtreler contains, starts_with, equals, from, to, any_of ve is alanlarını ayarlayabilir (bkz. docs/grid.md). view.columns, satırların oluşturulacağı sütun düzenidir; head: true ile yanıtta ayrıca başlık satırı olan head bulunur.

Sonsuz kaydırma için page değeri { "type": "after", "cursor": "...", "limit": 100 } olur ve ilk grup için cursor null olur.

200 yanıtı:

{ "rows": "<tr>...</tr>", "foot": "<tr>...</tr>", "head": null, "count": 50, "total": 230, "next": "50" }

foot, toplamlar istenmişse toplam satırıdır, aksi halde null.

total ve next null olabilir. Runtime, sayfalı grid'lerde satırları değiştirir, sonsuz kaydırmada ise sona ekler.

Gruplanmış ve ağaç grid'leri (<table role="treegrid">) bir dalı yüklemek için POST /_zeyn/grid ile path gönderir: açılan satırın grup değerleri (veya üst anahtarları), satırın data-zeyn-path değerinde bulunduğu gibi. Yanıt aynı biçimdedir; rows grup satırlarını veya bir seviye daha derindeki satırları tutar ve total bunları sayar. Runtime bunları açılan satırın arkasına koyar ve satır kapanırken onu izleyen her daha derin satırı kaldırır. Grid'in grup seviyelerinden (ağaçta 32'den) uzun bir yol 404 döndürür.

4.2.1 More grid endpoints

EndpointBodyDoes
POST /_zeyn/grid/view{screen, params, node, view}Kullanıcının görünümünü kaydeder (null siler). 204; görünüm deposu yoksa 404
POST /_zeyn/grid/run{screen, params, node, key, item}Bir satır öğesini çalıştırır (a0 satır eylemi, m2.1 menü yolu). Sunucu satırı anahtarıyla yükler ve girdiyi oluşturur. {effects}
POST /_zeyn/grid/bulk{screen, params, node, action, keys, all, query}Seçilen anahtarlarla toplu bir eylem çalıştırır; all ise query.filters ile eşleşen her satırla çalıştırır. {effects, count}
POST /_zeyn/grid/edit{screen, params, node, key, values, view}Satır içi bir düzenlemeyi kaydeder; values, düzenlenen sütunları ve satır sürümünü bir formun gönderdiği gibi tutar. {row}; düzenlenemeyen sütunlar için 403, 409, 422
GET /_zeyn/grid/export?screen&params&node&q={sort,filters,columns}Eşleşen her satırı CSV olarak akıtır
POST /_zeyn/menu{screen, params, item}Ekranın bağlam menüsündeki bir eylem öğesini çalıştırır

4.2.2 Virtual scrolling

data-zeyn-paging="virtual" olan grid'ler, kaç satır olursa olsun tüm satırlar üzerinde tek bir kaydırılabilir liste gösterir. Sunucu, data-zeyn-total ile ilk bloğu (data-zeyn-page-size satır, ofset 0) oluşturur: ilk 64 satır tabloda, kalanı ondan sonra gelen <template data-zeyn-rows-later> içinde durur; grid 40 veya daha fazla sütun gösteriyorsa tüm satırlar template içindedir. Grid kökünde ayrıca data-zeyn-row-height (piksel) ve --z-row-height` CSS değişkeni bulunur.

Runtime:

  • Tablo gövdesini, iç ara parçası total * rowHeight piksel yüksekliğinde olan bir kaydırma kapsayıcısına koyar; yükseklik 8.000.000 pikselle sınırlıdır. Sınırın üstünde kaydırma konumu satır indeksine oranla eşlenir (row = scrollTop / (spacerHeight - viewport) * (total - visibleRows)), böylece 1.000.000 satırda da sona ulaşılır.
  • DOM'da yalnızca görünen satırları ve alt ile üstteki 20'şer satırı tutar; bunları transform: translateY(...) ile konumlandırır. Satırların yüksekliği sabittir; uzun metin üç noktayla kesilir.
  • data-zeyn-page-size satırlık blokları POST /_zeyn/grid ile ve page: { "type": "offset", "offset": blockStart, "limit": blockSize } kullanarak yükler; ilk çağrıdan sonra with_total: false olur. Önbellekte en fazla 20 blok ve en çok 100.000 hücre tutar (en az son kullanılan çıkar; 63 sütunlu bir grid için 7 blok, hiçbir zaman 3'ten az değil), reload için saklanan blokları aynı sınıra sayar ve onları ilk önce atar, kaydırma yönündeki sonraki bloğu önceden getirir ve görünümden çıkan bloklar için istekleri iptal eder (AbortController).
  • Bir blok yüklenirken görünen satırları asla temizlemez. Eksik satırlar, blokları gelene kadar aria-busy="true" olan bir yer tutucu satır gösterir; böylece ekran yanıp sönmez.
  • Sıralama ve filtre değişiklikleri önbelleği düşürür ve en üste kaydırır.
  • Klavye: Ok tuşları, Page Up, Page Down, Home ve End etkin satırı taşır ve görünüme kaydırır. Tabloda aria-rowcount = total, her satırda aria-rowindex bulunur.
  • Konum metni (data-zeyn-page-info) data-zeyn-text-range-total değerini kullanır.

4.3 Form submit

POST /_zeyn/form

{ "screen": "product_form", "params": { "id": 7 }, "node": 0, "values": { "code": "P-001", "price": "1.234,56", "active": true } }

Değerler kullanıcının yazdığı gibi gönderilir: girişler ve seçimler için dizeler, onay kutuları için boolean'lar. Sunucu bunları alan türüne göre ayrıştırır.

Sunucu, ekranın yüklediği modelden başlar ve yalnızca formda bulunan ve salt okunur olmayan alanların üzerine yazar. İstemciden gelen diğer değerler yok sayılır. Anahtarlar hiçbir zaman istemciden gelmez.

Yanıtlar:

  • 200 { "effects": [ ... ] }: kaydedildi; effect'leri sırayla çalıştırın.
  • 422 { "errors": [ { "field": "code", "rule": { "rule": "required" }, "message": "Code is required." } ] }: her iletiyi kendi alanının altında gösterin (#{scope}-{field}-error), girişte aria-invalid="true" ayarlayın ve ilk geçersiz girişe odaklanın. field değeri boş olan hatalar formun en üstünde gösterilir.

4.4 Errors

Herhangi bir endpoint şu yanıtları verebilir:

StatusBodyMeaning
403{ "error": "forbidden", "message": "..." }İzin eksik, guard reddetti veya CSRF belirteci hatalı
404{ "error": "not_found", "message": "..." }Bilinmeyen ekran veya düğüm
409{ "error": "conflict", "message": "..." }Kaydı başka biri değiştirdi
422bkz. 4.3Doğrulama
504{ "error": "timeout", "message": "..." }Çağrı sunucu sınırından uzun sürdü
500{ "error": "internal", "message": "An unexpected error occurred." }Ayrıntılar yalnızca sunucu günlüğündedir

Runtime, message değerini bir hata toast'ı olarak gösterir.

4.5 Language

{ "locale": "tr" } ile POST /_zeyn/locale, seçimi zeyn_locale çerezinde saklar ve 204 yanıtı verir. Runtime sayfayı yeniden yükler. Çerez yoksa sunucu önce Accept-Language başlığına, sonra uygulamanın varsayılanına göre seçer. <html lang> her zaman seçilen yerel ayarla eşleşir.

Uygulamanın birden fazla yerel ayarı varsa kabuk bir dil seçici (select[data-zeyn-locale]) oluşturur.

4.6 Theme

Uygulama gizlemediği veya tek bir tema sunmadığı sürece kabuk bir tema seçici (select[data-zeyn-theme-picker]) oluşturur. Her seçenek data-scheme (light, dark, Sistem için boş) ve uygulama temaları data-custom taşır. Değişiklikte runtime <html> üzerinde data-theme ve data-zeyn-theme ile color-scheme meta etiketini hemen ayarlar, ardından { "theme": "dark" } ile POST /_zeyn/theme gönderir. Sunucu zeyn_theme çerezi ile 204 yanıtı verir; uygulamanın sunmadığı bir tema için 404 verir. Sayfalar bu çerezdeki temayla oluşturulur, bu yüzden yeniden yükleme ve yanıp sönme olmaz. Bkz. docs/theming.md.

4.7 Enterprise endpoints

enterprise.js ve enterprise.css, runtime ile birlikte yüklenir. Ayrıntılar docs/enterprise.md içindedir.

EndpointPurpose
POST /_zeyn/reveal {screen, params, node, field, key?}Maskelenmiş bir alanın ham değeri, {value}; maske kaldırma izni gerekir, her zaman denetim kaydına yazılır
POST /_zeyn/reason {screen, reason}reason olarak işaretlenmiş bir ekran için gerekçe; HttpOnly çerezle 204
POST /_zeyn/import {screen, params, node, csv, commit}CSV önizlemesi (commit: false) veya içe aktarma
POST /_zeyn/keepaliveBoşta uyarısından sonra "Oturumu açık tut"; 204
GET /_zeyn/health, GET /_zeyn/readyCanlılık ve hazırlık; health istek sınırına tabi değildir, readiness yalnızca probe_ips adreslerinden erişilir
GET /_zeyn/jobsJobs iznine sahip kullanıcılar için iş durumu

Her yanıt x-zeyn-build taşır. Runtime'ın üzerinde işlem yaptığı retler: 428 reason_required (bir gerekçe ister ve isteği yineler) ve 401 reauth_required (kullanıcıdan yeniden oturum açmasını ister ve isteği yineler).

5. Security

  • Her yanıt şunları ayarlar: Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; style-src-attr 'unsafe-inline'; img-src 'self' data: blob:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none', X-Content-Type-Options: nosniff ve Referrer-Policy: same-origin. App::csp, adı verilen https sunucularını img-src, media-src (ve blob:), frame-src, connect-src ve font-src yönergelerine ekler; diğer yönergeler hiçbir zaman değişmez. Başlık metni açılışta bir kez oluşturulur.
  • Satır içi betik veya satır içi olay işleyicisi yoktur. Satır içi style öznitelikleri yalnızca CSS özel özellikleri (--z-width, --z-flex) ve sabit sütun genişlikleri için kullanılır; bu yüzden politika style-src-attr 'unsafe-inline' ekler. Betikler sıkı kalır.
  • CSRF belirteci HttpOnly, SameSite=Strict bir çerezde saklanır ve x-zeyn-csrf başlığıyla sabit zamanlı karşılaştırılır.
  • Sayfalar, totp formu ve GET /_zeyn/csrf belirteci çerezdeki haliyle asla taşımaz: her yanıt yeni bir rastgele pad ve ardından belirtecin bu pad ile XOR'lanmış halini (128 onaltılık hane) alır, böylece gzip onu sızdıramaz (BREACH). Sunucu karşılaştırmadan önce maskeyi açar ve düz 64 haneli belirteci hâlâ kabul eder.
  • 1 KB veya daha büyük dinamik yanıtlar (sayfalar, kısmi ekranlar, grid blokları, dışa aktarmalar) tarayıcı kabul ediyorsa 2. seviyede gzip ile sıkıştırılır ve Vary: Accept-Encoding taşır. Asla sıkıştırılmayanlar: zaten bir Content-Encoding taşıyan yanıtlar (bir kez sıkıştırılıp önbelleğe alınan framework varlıkları), aralıklar (Content-Range ve Range içeren her istek), video, ses, SVG dışındaki görseller, application/octet-stream, arşivler ve PDF'ler, text/event-stream, application/x-ndjson (asistanın akışı) ve WebSocket yükseltmeleri.
  • Her çağrı sırasıyla şunları denetler: kimlik doğrulama, ekranın erişimi, Feature'ın guard'ları, düğüm eyleminin erişimi; ardından interceptor'ları ve eylemi çalıştırır.

6. Runtime behavior

  • JavaScript olmadan sayfalar yine de her grid'in ilk sayfasıyla oluşturulur. Formlar ve sayfalama runtime'a ihtiyaç duyar.
  • Klavye: ok tuşları grid satırları arasında gezinir, Enter bir satırı açar, Escape en üstteki diyaloğu kapatır, aria-keyshortcuts kısayolları metin girişleri dışında her yerde çalışır. Grid'lerde: Boşluk satırı seçer, F2 düzenler, Ctrl+Z son düzenlemeyi geri alır, Shift+F10 veya bağlam menüsü tuşu satırın ya da başlığın menüsünü açar, Shift+tıklama birden çok sütuna göre sıralar, Sağ ve Sol (sağdan sola sayfalarda yansıtılmış olarak) bir satırın ayrıntısını, grubunu veya çocuklarını açar ve kapatır, bir çocuk satırdaki Sol ise üst satıra gider. Bir satırda Sağ, hücrelerine geçer: oklar, Home, End, Ctrl+Home, Ctrl+End, Tab ve Shift+Tab hücre imlecini taşır, Shift bir aralığı büyütür (hücrelerde aria-selected), Ctrl+C aralığı sekmeyle ayrılmış metin olarak kopyalar ve Ctrl+V düzenlenebilir hücrelere yapıştırır; değişen her satır için bir POST /_zeyn/grid/edit gönderilir. 40 veya daha fazla sütunu olan grid'ler sayfada yalnızca görünümdeki sütunları tutar (sabit genişlikli bir colgroup, colspan taşıyan td.z-col-gap ara parçaları, hücrelerde aria-colindex).
  • Sağ tık, tanımlı olan yerde Zeyn'in menüsünü açar (başlıklar, data-zeyn-menu olan satırlar, data-zeyn-menu olan ekranlar); başka her yerde tarayıcının kendi menüsü kalır.
  • Odak, diyalog açıldığında diyalogun içine taşınır ve kapandığında açana geri döner.
  • İstemci tarafı denetimler (required, minlength, maxlength, min, max, type="email") göndermeden önce tarayıcının kısıt doğrulama API'siyle çalışır. Sunucu her zaman yeniden doğrular.

7. Component library

zeyn::ui bileşenleri, sayfa head'inde runtime'dan sonra bağlanan iki varlık daha ile sunulur: /_zeyn/components.js ve /_zeyn/components.css. Runtime gibi bunlar da oturum açmayı gerektirmez.

7.1 Endpoints

Hepsi JSON ve CSRF başlığıyla POST'tur ve önce ekranın erişimini ve Feature'ın guard'larını denetler.

EndpointBodyAnswer
/_zeyn/node{ screen, params, node }Bir düğümün, grid'lerinin ilk sayfasıyla birlikte HTML'i. Tembel (lazy) sekmeler tarafından kullanılır.
/_zeyn/data{ screen, params, node, key, input }Düğümün key veri kaynağından JSON: arama seçenekleri (key alan adıdır), ağaç çocukları (key boştur), adım denetimleri (key _step olur). Kaynağın eylem izni denetlenir ve interceptor'lar, grid'lerde olduğu gibi çalışır. Eyleme uymayan girdi 404 ile reddedilir.
/_zeyn/parse{ kind, text, label }Sunucunun yazılmış bir tarihi veya saati kullanıcının dilinde, saat diliminde ve saat biçiminde nasıl okuduğu: { ok, text, iso, utc, zone } veya { ok: false, message }. kind değeri date, time, datetime, local veya presets (bugün ve hızlı aralıklar) olur.

7.2 Values the forms submit

  • Lookup'lar kimlikleri metin olarak gönderir: "7", birden çok için "3,7,9".
  • Lines, "{field}": "rows" gönderir ve her hücre için bir değeri "{field}.{line}.{column}" olarak, ayrıca satırın geldiği yüklü satırın indeksi olan "{field}.{line}._i" değerini gönderir (yeni satırlar için boş). Sunucu her satırı yüklü satırından yeniden oluşturur; böylece yalnızca gösterilen, düzenlenebilir sütunlar değişir.
  • Bir satırın doğrulama hataları { "field": "lines", "rule": { "rule": "line", "value": { "row": 0, "field": "quantity" } } } biçimini kullanır.

7.3 Events

EventOnDetail
zeyn:contentdocumentYeni içeriği olan eleman (gezinme, tembel paneller). Her iki betik de içerdiklerini kurar.
zeyn:errorsthe formRuntime onları gösterdikten sonra 422 hataları. Satır düzenleyici ve adımlı formlar bunu kullanır.
zeyn:effectdocumentRuntime'ın tanımadığı bir effect: { effect, source, run }. run(next) başka bir effect çalıştırır. confirm tarafından kullanılır.

{"effect":"confirm","message":"...","title":"...","ok":"...","cancel":"...","then":{...}} effect'i modal bir uyarı diyaloğunda sorar ve then değerini yalnızca kullanıcı onayladıktan sonra çalıştırır.

7.4 Media

Video ve Audio, bileşen kitaplığından sonra bağlanan /_zeyn/media.js ve /_zeyn/media.css ile sunulur. Bkz. docs/media.md.

EndpointDoes
GET /_zeyn/media/{source}/{id}Kayıtlı bir medya kaynağının dosyası, aralık (range) isteklerini destekler: 200, 206, 304, 416.
GET /_zeyn/media/position?source=&id=Oturum açmış kullanıcının kaldığı yer olan { at, kept }; kaynak konum tutmuyorsa kept false olur (oynatıcı bu durumda yerel depolamayı kullanır).
POST /_zeyn/media/positionCSRF başlığıyla { source, id, at } konumu kaydeder: 204, hatalı konum için 422.

7.5 Islands

islands özelliğiyle, div[data-zeyn-island] içeren bir ekran <meta name="zeyn-islands"> (island adı ile { js, wasm } URL'leri), /_zeyn/islands.js ve 'wasm-unsafe-eval' içeren bir politika ile sunulur; diğer sayfalar değişmez. Bir island içeren kısmi yanıt x-zeyn-islands: 1 taşır ve belgede island politikası yoksa nav.js onu tam olarak yükler.

Attribute or endpointMeaning
data-zeyn-island, data-zeyn-load, data-zeyn-propsIsland adı, visible, interaction veya idle, props JSON'u
data-zeyn-island-fallbackYedek içerik, island bağlanınca gizlenir
data-zeyn-island-stateYükleyici tarafından ayarlanır: loading, ready, failed
GET /_zeyn/islands/{file}Manifestte listelenen derlenmiş dosya, bir yıl önbelleğe alınır
POST /_zeyn/data with key = remote nameIsland'dan tipli bir çağrı, 7.1'deki tüm denetimlerle

Bkz. docs/islands.md.

7.6 Realtime

realtime özelliğiyle, div[data-zeyn-live="topic"] sunucu konuyu bildirdiğinde yenilenen düğümleri işaretler. Birini içeren sayfalar /_zeyn/live.js dosyasını bağlar; birini içeren kısmi yanıt x-zeyn-live: 1 taşır ve nav.js betiği içe aktarır. Bir eleman üzerindeki zeyn:reload, içindeki grid'leri yerinde yeniden yükler.

EndpointDoes
GET /_zeyn/live/topics?t=a,b&csrf=WebSocket; metin çerçeveleri { "topic": "a" }
GET /_zeyn/live/doc/{kind}/{doc}?csrf=WebSocket; ikili çerçeveler 0x01 + Y güncellemesi (v1), iki yönde

El sıkışmalar, yükseltmeden önce origin'i, CSRF belirtecini çerezle karşılaştırarak, oturum açmış kullanıcıyı ve izinleri denetler. Bkz. docs/realtime.md.

Oynatıcı, aşamalı (progressive) dosyalara dönmeden önce kökünde iptal edilebilir zeyn:media-adaptive olayını ({ media, kind, url, hls, dash }) gönderir; bir akış kitaplığı devralmak için preventDefault() çağırır.

8. Desktop class shell

/_zeyn/pro.js ve /_zeyn/pro.css bileşen kitaplığından sonra yüklenir. Runtime gibi bunlar da oturum açmayı gerektirmez.

8.1 Commands and the palette

Komutlar Rust'ta tanımlanır (Command::open, Command::navigate, Command::effect, Command::action) ve Feature::command ile kaydedilir. Kabuk, kullanıcının çalıştırabileceği her komut için (Feature guard'ları geçer ve komutun erişimi izin verir) div[data-zeyn-commands] içinde gizli bir düğme oluşturur:

AttributeMeaning
data-zeyn-commandKomut kimliği
data-zeyn-effectBir effect komutunun effect'i, tıklamada runtime tarafından çalıştırılır
data-zeyn-command-runBir eylem komutunun kimliği, POST /_zeyn/command ile çalıştırılır
aria-keyshortcutsNormalleştirilmiş kısayol (Control+Alt+N); runtime'ın kısayol işleme mantığı düğmeye tıklar
data-hint, data-iconKullanıcılara gösterildiği haliyle kısayol, simge adı
data-text-* on the containerYerelleştirilmiş palet metinleri

App::check, okunamayan kısayolları, tek karakterli kısayolları (WCAG 2.1.4), paletin kendi kısayolunu, iki komutun kullandığı kısayolları veya kimlikleri, tarayıcının sayfaya hiç iletmediği kısayolları (BROWSER_RESERVED: Ctrl+N, Ctrl+T, Ctrl+W, Ctrl+Tab, Alt+F4 ve macOS biçimleri) ve komut .override_browser() çağırmıyorsa tarayıcının kullandığı kısayolları (BROWSER_DEFAULTS: yazdırma, kaydetme, bulma, adres çubuğu, yeniden yükleme, yakınlaştırma, geliştirici araçları, kopyala ve yapıştır, F1 ile F12 arası) reddeder. Araç çubuğu düğmeleri çalışma zamanında aynı kurallara uyar: böyle bir kısayol bağlanmaz ve nedeni günlüğe yazılır. Serbest kombinasyonlar Alt+Shift+<letter> ve Ctrl+Alt+<letter> biçimindedir.

Alt+Shift+P (macOS'ta Option+Shift+P) paleti varsayılan olarak açar; App::palette_shortcut("Ctrl+Shift+P") bunu, bilerek tarayıcının kullandığı bir tuşa bile olsa, değiştirir. Kısayol data-palette-keys içindedir ve harfler fiziksel tuşa göre eşleşir; böylece Option ve AltGr bozmaz. Palet ayrıca menüdeki button[data-zeyn-palette-open] ile de açılır. ARIA combobox ve listbox içeren modal bir <dialog>'dur. Uygulamanın komutlarını, geçerli ekranın (bir pencere açıksa en üstteki pencerenin) araç çubuğu düğmelerini, menü çubuğu komutlarını ve bağlam menüsü öğelerini ve menüdeki ekranları listeler. Eşleştirme bulanıktır (fuzzy), büyük/küçük harfi ve aksanları yok sayar; en son çalıştırılan sekiz komut başta gelir (localStorage anahtarı zeyn.palette.recent). Escape paleti kapatır ve odak önceki yerine döner.

EndpointBodyAnswer
POST /_zeyn/command{ command }Girdisi sunucuda sabitlenmiş bir eylem komutunu çalıştırır: CSRF, Feature guard'ları, eylemin izni ve interceptor'lar. { effects }; bilinmeyen kimlikler ve effect komutları için 404
POST /_zeyn/search{ query }Kullanıcının çalıştırabileceği her Feature::search değerini (CSRF, Feature guard'ları, eylemin izni, interceptor'lar) sorgu 100 karakterde kesilerek çalıştırır. { hits: [{ label, group, url }] }, en fazla 20, yalnızca uygulama yolları

Kapsayıcıda data-search varsa palet sorguyu son tuştan 150 ms sonra gönderir (iki karakter veya daha fazla) ve sonuçları komutların altında listeler. Yalnızca en son sorgunun yanıtı çizilir. Bir sonucu seçmek, url önüne temel yolu ekleyerek zeyn:navigate gönderir; içindeki #anchor, ekran değiştirildikten sonra o elemana kaydırır.

Runtime, diğer betiklerin işi onun üzerinden çalıştırabilmesi için document üzerinde zeyn:run olayını dinler: { effects, source } effect'leri çalıştırır, { screen, item } bir ekran bağlam menüsü öğesini çalıştırır, { error } bir hata toast'ı gösterir.

8.2 Menu bar, toolbox, property inspector, status bar

MarkupBehavior
ul[role=menubar][data-zeyn-menubar]W3C APG menü çubuğu. Üst öğeler, dolaşan sekme durağı (roving tab stop) olan button[role=menuitem][aria-haspopup=menu] düğmeleridir; her biri, kapalıyken hidden olan bir ul[role=menu] denetler. Öğeler 8.1'deki komut özniteliklerini taşır. Sol ve Sağ menüler arasında gezinir, Aşağı, Enter ve Boşluk açar, Yukarı son öğede açar, Home ve End atlar, bir harf o harfle başlayan sonraki öğeye gider, Escape kapatır ve çubuğa döner, Tab ve dışarı tıklama kapatır. Kullanıcının çalıştıramayacağı menüler ve öğeler oluşturulmaz.
div[role=toolbar][aria-orientation=vertical][data-zeyn-toolbox]button.z-tool[aria-pressed] düğmelerinden oluşan geçiş grubu. Oklar, Home ve End dolaşan sekme durağını taşır; bir tıklama, Enter veya Boşluk bir aracı açar, zeyn:tool (detail.tool) gönderir ve data-text-selected değerini durum çubuğuna yazar.
section.z-inspectorForm::section grupları olan bir form (role=group gövdeli details.z-form-section), inspector'ın düğüm yolunda POST /_zeyn/form ile kaydedilir. Hatalı alanlar içeren bölümler, tarayıcının denetimlerinden önce ve bir 422 sonrasında açılır.
div[data-zeyn-statusbar]Başta ve sonda öğeler ile [data-zeyn-status-live] (role=status, aria-live=polite). Ekranın herhangi bir elemanından gelen zeyn:status (detail.text), canlı metnin yerine geçer.

8.3 Docking panels

div.z-dock[data-zeyn-dock], dört section.z-dock-area[data-area] (left, center, bottom, right) ve üç div.z-dock-bar[role=separator][data-edge] ayırıcıdan oluşan bir grid'dir. Boyutlar --z-dock-left, --z-dock-right (genişliğin yüzdesi) ve --z-dock-bottom (yüksekliğin yüzdesi) özel özellikleridir, 10 ile 60 arası. Her alanda sürüklenebilir button.z-dock-tab[data-panel] düğmelerinden oluşan bir role=tablist, bir seçenekler düğmesi ve çocuk düğümleri tutan role=tabpanel panelleri bulunur (yollar herhangi bir kapsayıcıdaki gibidir; bir panel taşındığında asla değişmezler). Açık paneli olmayan yan alanlar gizlenir.

Klavye: Sol ve Sağ (sağdan sola sayfalarda yansıtılmış olarak), Home ve End bir alanın açık sekmeleri arasında gezinir; Delete odaktaki paneli kapatır; Shift+F10 veya bağlam menüsü tuşu alanın seçeneklerini açar (başka alana taşı, kapat, kapalı panelleri göster); bir ayırıcı üzerindeki oklar yüzde 5 yeniden boyutlandırır, Home ve End sınırlara gider. MenuList::panels ile yapılan bir menü çubuğu girdisi her paneli menuitemcheckbox olarak listeler. Değişiklikler [data-zeyn-dock-live] içinde duyurulur.

EndpointBodyAnswer
POST /_zeyn/dock{ screen, params, node, save: false }{ layout }: oturum açmış kullanıcının kaydedilmiş düzeni veya null
POST /_zeyn/dock{ screen, params, node, save: true, layout }204; layout: null siler

layout, sekme sırasıyla { sizes: { left, right, bottom }, panels: [{ id, area, open, active }] } biçimindedir. Sunucu CSRF'yi, ekranın erişimini ve Feature'ın guard'larını denetler, yalnızca dock'un kendi panellerini tutar (her biri bir kez) ve boyutları sınırlar, ardından tenant, ekran ve düğüm altında uygulamanın görünüm deposunda (App::datagrid_views) saklar. Depo yoksa endpoint 404, kullanıcı yoksa 403 yanıtı verir; bu durumda tarayıcı düzeni localStorage içinde tutar.

9. AI agents: MCP and WebMCP

Dışarıdaki AI ajanları uygulamaya, ikisi de varsayılan olarak kapalı olan ve App::mcp ile açılan iki kapıdan ulaşır:

SettingEndpoint MCPWebMCP toolsWrites
Mcp::off() (default)404404no
Mcp::read_only()ononno
Mcp::on()ononform saves and API actions that write

.server(false) ve .webmcp(false) bir kapıyı kapatır. zeyn new ile oluşturulan yeni projeler Mcp::read_only() ile başlar.

Neden kapalı: endpoint, geçerli bir Authorization başlığına sahip herkesin uygulamayı bir model üzerinden yönetmesine izin verir ve bir model okuduğu verilerle yönlendirilebilir. Framework her uygulamayı ajanlara hazır hale getirir (araçlar koddan üretilir, yazılacak bir şey yoktur), ancak bir uygulamanın bu kapıyı açıp açmayacağı ve ajanların yazıp yazamayacağı, uygulamayı işletenlerin kararıdır. Salt okunur mod tek satır uzaklıktadır.

9.1 Tools

Araçlar, oturum açmış kullanıcı için uygulamadan oluşturulur:

  • her grid için bir arama aracı ({screen}_{node}_search, girdi: bir DataQuery) ve yazma açıkken, parametresiz açılan her route'lu ekranın her formu için bir kaydetme aracı ({screen}_{node}_save, girdi: yazıldığı gibi form değerleri);
  • yayımlanmış her API eylemi için bir araç (api_{feature}_{action}). Girdisi DataQuery olan bir eylem okur; diğer her API eylemi yazma sayılır.

Açıklama; ekran başlığından, ekranın ve eylemin /// doc yorumunun ilk paragrafından ve ai = "..." yönlendirmelerinden oluşturulur. Kullanıcının kullanamayacağı grid'ler ve formlar ile ai = false olarak işaretlenmiş ekranlar ve eylemler hiçbir zaman listelenmez ve çağrıldıklarında reddedilir.

#[action(confirm)], bir modelin ancak kullanıcı bunu uygulamada onayladıktan sonra çalıştırabileceği bir eylemi işaretler. /_zeyn/mcp bu tür araçları ne listeler ne de çalıştırır (o kanalda kimse onaylayamaz). WebMCP araçları modal bir diyalogda sorar (zeyn:confirm) ve ardından form ile birlikte confirmed: true gönderir; yerleşik asistan ise kendi sohbetinde sorar (bkz. docs/assistant.md).

9.2 Endpoints

EndpointDoes
GET /_zeyn/tools?screen&paramsGörünümdeki ekran için { tools: [{ name, description, inputSchema, kind, screen, node, writes, confirm }], texts }. zeyn.js bunları document.modelContext ile kaydeder
POST /_zeyn/mcpJSON-RPC: initialize, tools/list, tools/call. Bir Authorization başlığı ve oturum açmış bir kullanıcı gerekir
POST /_zeyn/form with via_agent: trueBir WebMCP form kaydı; WebMCP ile Mcp::on() gerekir. confirmed: true yalnızca kullanıcı onayladıktan sonra

Her ajan çağrısı kullanıcı olarak çalışır: ekranın erişimi, guard'lar, eylemin izni, interceptor'lar, istek sınırları ve zaman sınırı. Ajan çağrıları her zaman denetim kaydına yazılır (via_agent: true) ve maskelenmiş alanlar, kullanıcı gösterebilse bile ajanlar için her zaman maskeli kalır. OpenAPI her işlemi x-zeyn-ai ile işaretler: ai = false için false, aksi halde { hint, confirm, writes }.