@appaflytech/zeyrek-ai-mcp 0.0.41 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/knowledge/componentCatalog.js +27 -97
  2. package/dist/knowledge/componentEvents.js +1 -18
  3. package/dist/knowledge/mobileComponentCatalog.js +114 -15
  4. package/dist/knowledge/mobileComponentEvents.js +41 -0
  5. package/dist/knowledge/motionResolve.js +252 -0
  6. package/dist/knowledge/normalize.js +87 -59
  7. package/dist/knowledge/pageDiscover.js +2 -2
  8. package/dist/knowledge/platform/batchOps.js +70 -0
  9. package/dist/knowledge/platform/heroTag.js +153 -0
  10. package/dist/knowledge/platform/lint.js +128 -6
  11. package/dist/knowledge/platform/mobile.js +1 -1
  12. package/dist/knowledge/platform/mobileLayoutLint.js +8 -1
  13. package/dist/knowledge/platform/mobileRules.js +18 -0
  14. package/dist/knowledge/platform/pageTransition.js +57 -0
  15. package/dist/knowledge/platform/web.js +1 -1
  16. package/dist/knowledge/rules.js +52 -1
  17. package/dist/knowledge/styleResolveNative.js +88 -4
  18. package/dist/platform/capture/actions.js +4 -1
  19. package/dist/platform/capture/capture.js +10 -13
  20. package/dist/platform/capture/persistent-browser.js +130 -0
  21. package/dist/resources/examples/flow-list-to-form.json +8 -10
  22. package/dist/resources/examples/flow-load-table.json +1 -1
  23. package/dist/resources/examples/mobile-list-detail-form.json +4 -3
  24. package/dist/resources/examples/mobile-scan-code.json +285 -0
  25. package/dist/resources/guides/component.md +3 -3
  26. package/dist/resources/guides/expressions.md +2 -2
  27. package/dist/resources/guides/merge.md +98 -18
  28. package/dist/resources/guides/microservice.md +2 -2
  29. package/dist/resources/guides/mobile-layout.md +2 -1
  30. package/dist/resources/guides/mobile-publish.md +1 -0
  31. package/dist/resources/guides/mobile-steps.md +67 -0
  32. package/dist/resources/guides/mobile.md +27 -55
  33. package/dist/resources/guides/page.md +182 -121
  34. package/dist/resources/guides/translation.md +26 -0
  35. package/dist/resources/schema-fingerprint.json +2 -2
  36. package/dist/resources/schemas/ResolveMergeConflictsCommand.json +30 -1
  37. package/dist/tools/activities.js +3 -1
  38. package/dist/tools/applications.js +16 -2
  39. package/dist/tools/components.js +12 -6
  40. package/dist/tools/designSchemaPatch.js +18 -13
  41. package/dist/tools/layouts.js +7 -2
  42. package/dist/tools/merges.js +29 -9
  43. package/dist/tools/pages.js +129 -127
  44. package/dist/tools/platformInfo.js +3 -2
  45. package/dist/tools/profileWrite.js +2 -2
  46. package/dist/tools/schemaOps.js +132 -34
  47. package/dist/tools/screenWrite.js +1 -1
  48. package/dist/tools/screens.js +3 -3
  49. package/package.json +2 -2
@@ -17,11 +17,12 @@ onaylıdır**. Buradaki tool'ların hiçbiri production'a yazmaz.
17
17
  environment_create {projectId, name} → 202, dal "creating"
18
18
  environment_list {projectId} → state "ready" olana kadar (30-60 sn)
19
19
  … tenant tool'larını environment: "<dal>" ile çağır (page_schema_patch, entity_patch, …)
20
- merge_plan {projectId, sourceEnvironmentId} → 202, plan "planning"
20
+ merge_plan {projectId, sourceEnvironmentId, title} → 202, plan "planning" (title ZORUNLU)
21
21
  merge_get {projectId, planId} → status "planned" olana kadar
22
22
  merge_get {projectId, planId, view:"conflicts"} → çakışanları oku
23
- merge_resolve{projectId, planId, resolutions[]} → her çakışmaya source|target seç
24
- merge_apply {projectId, planId, message} → 202 (message ZORUNLU)
23
+ merge_get {projectId, planId, checks:true} → merge öncesi doğrulama (uyarılar)
24
+ merge_resolve{projectId, planId, resolutions[]} → source | target | both | custom
25
+ merge_apply {projectId, planId} → 202
25
26
  merge_get {projectId, planId} → status "applied"
26
27
  environment_list {projectId} → dal listede yok, şeması düştü
27
28
  ```
@@ -94,7 +95,9 @@ değişkenler, çözülen ref'ler) kareyi tamamlar: kare ne çizildiğini, bu ik
94
95
  - `kind`: `add` | `update` | `delete` | `conflict`
95
96
  - `dataRisk`: entity kaleminde veri riski var — **sebep `dataRiskReasons[]` içindedir**
96
97
  - `derivedFromKey`: kalem OTOMATİK ÜRETİLMİŞ bir kayıtsa (varsayılan DTO) kaynağının anahtarı
97
- - `elements[]`: `path`, `kind`, `base`, `source` (dal), `target` (development), `resolution`
98
+ - `elements[]`: `path`, `kind`, `base`, `source` (dal), `target` (development), `resolution`. Açık bir elemanda
99
+ `resolution` hiç gelmez: gateway null alanları düşürür, yani alanın yokluğu "karar verilmedi" demektir.
100
+ `ownerElementId`: yolu bileşeni adlandırmayan widget prop'unun (`widgetComponentProps[id=…]`) bileşeni
98
101
 
99
102
  `dataRisk` bir **uyarıdır, apply'ı bloklamaz**; dört sebepten biriyle doğrudur:
100
103
 
@@ -144,8 +147,83 @@ bir merge'de entity ve onun varsayılan DTO'su böylece zıt çözülemez.
144
147
  { "itemKey": "page:8f1c…", "path": "components[elementId=btn_save].props[name=label].ref", "choice": "source" }
145
148
  ```
146
149
 
147
- `path` boş bırakılırsa o kaydın **tüm** çakışmaları aynı tarafa çözülür. `source` = dal,
148
- `target` = development.
150
+ `path` boş bırakılırsa o kaydın **tüm** çakışmaları aynı karara çözülür.
151
+
152
+ | `choice` | Ne yazılır | Ne zaman |
153
+ |---|---|---|
154
+ | `source` | dalın değeri | dal haklı |
155
+ | `target` | development'ın değeri | development haklı |
156
+ | `both` | iki tarafın birleşimi: development'ın listesi + dalın ekledikleri − dalın sildikleri. Development aynı öğeyi zaten eklediyse ikinci kez eklenmez | yalnız `combinable: true` elemanda (kimliksiz ya da gruplu liste); skalerde 400 |
157
+ | `custom` | `value`'daki değer | iki taraf da tam doğru değil. `value` ZORUNLU, türü `source`/`target` ile aynı olmalı. Kök yolda kaydın gövdesidir, `order-conflict`'te kimlik listesidir. Yolsuz `custom` yalnız kaydın tek çakışması varken kabul edilir |
158
+
159
+ ```json
160
+ { "itemKey": "page:8f1c…", "path": "components[elementId=btn_save].props[name=label].ref", "choice": "custom", "value": "Siparişi kaydet" }
161
+ ```
162
+
163
+ Varsayılan DTO (`derivedFromKey`) ve alanları (`derivedFromPath: fields[id=…]`) kaynağının kararını kendiliğinden
164
+ alır. `both` kendi listesiyle yeniden birleştirilir, `custom` yalnız üç taraf aynıysa geçer. Kaynağına zıt
165
+ çözmek 400 döner.
166
+
167
+ Sayfa/layout/widget'ın **dizi prop'u** (ör. dropdown `options`) planda iki kez görünür: okunur liste `….ref` ve
168
+ runtime'ın çizdiği satırlar `….children`. **Kararı `ref`'e verin**; `children` onu kendiliğinden izler. Bir taraf
169
+ seçimi aynen geçer; `both` ve `custom` satırlara listeden yeniden kurulur. `children`'a ayrı ve farklı karar vermek
170
+ ekranda listenin değil satırların çizilmesine yol açar.
171
+
172
+ **Nesne prop'unda** (butonun `icon`'u, `surfaceStyle`) aynı kural anahtar düzeyindedir: anahtarın değeri
173
+ `….ref.<anahtar>…`, runtime'ın çizdiği satırı `….children[name=<anahtar>]…`. İki taraf aynı anahtara farklı yazdıysa
174
+ ikisi de çakışma gelir; **kararı `ref.<anahtar>`'a verin**. Satır karar istemez ve `summary.unresolved`'a girmez:
175
+ merge onu değerin birleşik hâlinden kurar (`custom` dahil). Farklı anahtarlara yapılan değişiklikler kendiliğinden birleşir. Koşu 46 S-9:
176
+ iki ayrı karar kabul ediliyor, sayfa satırınkini çiziyordu.
177
+
178
+ Bir taraf prop'un **tipini** değiştirdiyse (`valueType`: `static` → `code`) ve öbür taraf da tipine ya da değerine
179
+ dokunduysa (tipler farklı kalır) eleman prop'un kendisidir: yol `…props[id=…]` (widget'ta `widgetComponentProps[id=…]`), `base`/`source`/`target` prop gövdesi,
180
+ `combinable: false`. Tip ile değer tek karardır; ayrı karar verilseydi düz metin kod olarak çalışabilirdi.
181
+ `custom`'da `value` prop gövdesidir, kimliği sunucu yoldan yazar; statik listenin satırlarını (`children`) da `ref`'ten
182
+ sunucu kurar, gönderilen `children` kullanılmaz:
183
+
184
+ ```json
185
+ { "itemKey": "page:8f1c…", "path": "components[elementId=btn_save].props[id=01a1…]", "choice": "custom",
186
+ "value": { "name": "text", "type": "primitive", "valueType": "code", "ref": "{{ 'Kaydet ' + Page.variables.adet }}", "children": [] } }
187
+ ```
188
+
189
+ Kopyada `target`, dalın kopyasından türemiş DTO alanını da düşürür. `source` ise development'ın ikizinden türemiş
190
+ DTO alanını siler.
191
+
192
+ ### Olası kopya: `kind: "duplicate"`
193
+
194
+ İki taraf **aynı içeriği farklı kimlikle** eklediyse (iki `telefon` alanı, aynı girişi taşıyan iki bileşen) eleman
195
+ `duplicate` gelir; `twinPath` development'taki ikizdir. `combinable: true` olan kopya **engellemez**: çözümsüz
196
+ bırakılırsa ikisi de yazılır (git'in varsayılanı).
197
+
198
+ Kopyalar **`slug` taşıyorsa** (tablo alanı = kolon, sayfa = rota) ikisi birden kalamaz: eleman `combinable: false`
199
+ gelir, `summary.unresolved`'a sayılır ve birini seçene kadar apply edilmez; `both` 400 döner. Gerçek uçta ikisini
200
+ tutan apply "column already exists" ile düştü.
201
+
202
+ - `target`: development'ınki kalır, dalın eklemesi düşer.
203
+ - `source`: dalınki kalır, development'ın ikizi çıkar.
204
+ - `both`: ikisi de kalır (yalnız `combinable: true`).
205
+ - `custom`: tek öğe yazılır. `value` öğenin gövdesidir; kimliği development'ınkine çevrilir.
206
+
207
+ Development tabanda **zaten olan** bir alanı dalın eklediği slug'a yeniden adlandırdıysa dalın alanı da kopya gelir:
208
+ `twinPath` yeniden adlandırılan alandır ve **`base` doludur** (o alanın taban hâli). Bu kopyada `source` 400 döner —
209
+ development'ın alanı veri taşır, dalınki onun yerine geçemez; `target` (dalınki düşer) ya da `custom` seçin. Koşu 46
210
+ S-8: ikisi de yazılıyor, apply kolon çakışmasıyla düşüyordu.
211
+
212
+ Ayarı farklı, aynı adlı iki öğe kopya **sayılmaz**; ikisi ayrı eklenir ve doğrulamada `name-clash` uyarısı olarak
213
+ görünür. İki istisna: **tablo alanında slug** (kolon adı) ve **bileşen prop'unda ad** tekildir. Ayarı farklı olsa da
214
+ aynı slug'lı alan ya da aynı adlı prop eklemesi `combinable: false` kopyadır. Aynı adlı ama slug'ı farklı iki alan
215
+ yine ayrı eklenir.
216
+
217
+ ### Merge öncesi doğrulama: `merge_get {checks: true}`
218
+
219
+ | Kod | Seviye | Anlamı |
220
+ |---|---|---|
221
+ | `unresolved` | error | çözülmemiş çakışma ya da `slug` taşıyan çözümsüz kopya; apply 409 döner. Dizi prop'unun `children`'ı `ref` açıkken, nesne prop'unun satırı (`children[name=…]`) değeri çakışırken, türetilmiş DTO elemanı kaynağı açıkken ayrı satır almaz ve `summary.unresolved`'a girmez: kaynağı çözünce kendiliğinden çözülür |
222
+ | `duplicate-open` | warning | çözümsüz kopya (`combinable: true`), ikisi de yazılacak |
223
+ | `name-clash` | warning | birleşik gövdede aynı adla iki kardeş (örneğin iki `telefon` sütunu) |
224
+ | `dangling-reference` | warning | merge'ün sildiği ya da kopya kararında düşen bir kimliğe hâlâ başvuru var |
225
+
226
+ Uyarılar apply'ı engellemez; ama okunmadan `merge_apply` çağrılmaz.
149
227
 
150
228
  `merge_resolve` **özet döner** — `{ planId, status, summary, resolved }`. Planın kendisi yanıtta
151
229
  yoktur; kalan çakışmalar için `merge_get {planId, view:"conflicts"}` çağırın.
@@ -156,30 +234,31 @@ yoktur; kalan çakışmalar için `merge_get {planId, view:"conflicts"}` çağı
156
234
  | Durum | Kod | Ne yapmalı |
157
235
  |---|---|---|
158
236
  | Çözülmemiş çakışma var | 409 | `merge_get view:"conflicts"` → `merge_resolve` → tekrar `merge_apply` |
159
- | Development plan alındığından beri değişti | 409 | `merge_plan` ile **yeniden planla**; eski planı uygulama |
237
+ | Dal ya da development plan alındığından beri değişti (`isStale`) | 409 | `merge_refresh`; değişmeyen elemanların kararları korunur, değişenler yeniden sorulur |
160
238
  | Plan 24 saatten eski | 400 | yeniden planla |
161
239
 
162
240
  Aynı `merge_apply` çağrısını tekrarlamak sonucu değiştirmez — 409 geçici bir hata değildir.
163
241
 
164
- ## Mesaj zorunludur
242
+ ## Başlık zorunludur
165
243
 
166
- `merge_apply` mesajsız çalışmaz. Mesaj plana yazılır, plan uygulandıktan sonra da durur ve
167
- geçmişte bu mesajla görünür — git'teki merge commit mesajının karşılığıdır.
244
+ İsteğin adı **plan açılırken** `merge_plan`'ın `title` alanında verilir; `merge_apply` ve `merge_revert` ayrı bir
245
+ mesaj almaz. Başlık plana yazılır, uygulandıktan sonra da durur ve geçmişte bu adla görünür — git'teki merge
246
+ request başlığının karşılığıdır.
168
247
 
169
- - Boş ya da yalnız boşluktan oluşan mesaj **şema katmanında** reddedilir; istek platforma hiç gitmez.
170
- - En fazla 200 karakter.
248
+ - Boş ya da yalnız boşluktan oluşan başlık **şema katmanında** reddedilir; istek platforma hiç gitmez.
249
+ - En fazla 100 karakter.
171
250
  - Kalıp: `<dal> → development: <amaç>`. Ne yaptığını yaz, "merge" yazma.
172
251
 
173
- Gateway senin yerine mesaj **uydurmaz**: ne değiştirdiğini bilen sensin.
252
+ Gateway senin yerine başlık **uydurmaz**: ne değiştirdiğini bilen sensin.
174
253
 
175
254
  ## Geri almak
176
255
 
177
256
  Uygulanmış bir merge silinmez, **ters bir planla** geri alınır:
178
257
 
179
258
  ```
180
- merge_revert {projectId, planId, message} → 202, ters plan planning
259
+ merge_revert {projectId, planId} → 202, ters plan planning
181
260
  merge_get {projectId, planId: <ters plan>} → status planned olana kadar
182
- merge_apply {projectId, planId: <ters plan>, message}
261
+ merge_apply {projectId, planId: <ters plan>}
183
262
  ```
184
263
 
185
264
  Bittiğinde ters plan `applied`, kaynak plan `reverted` olur; ikisi de geçmişte durur.
@@ -220,7 +299,7 @@ merge_list {projectId, environmentId:"<env>"} → o environment'ı kaynak ya d
220
299
  ```
221
300
 
222
301
  `items[]` bu uçta **her zaman boştur** — bir planın değişiklik listesi için `merge_get`.
223
- Satırda `message`, `createdBy` (planı alan), `appliedBy` (uygulayan), `appliedAt` ve
302
+ Satırda `title`, `description`, `createdBy` (planı alan), `appliedBy` (uygulayan), `appliedAt` ve
224
303
  `summary` sayaçları gelir. Sayfalama `pageIndex` / `pageLength` ile.
225
304
 
226
305
  Bir kaydın neden değiştiğini ararken sıra: `merge_list status:"applied"` → ilgili satırın
@@ -262,5 +341,6 @@ plan `applied` kalır.
262
341
  - Merge sürerken dal ve development'a yazma kilitlidir; **diğer dallar etkilenmez**.
263
342
  - Proje başına açık dal sayısı sınırlıdır (varsayılan 5) — sınır bağlantı bütçesidir, ürün kararı
264
343
  değil.
265
- - Dal adı `^[a-z][a-z0-9_]{2,30}$` kuralına uyar ve şema adı olur; `development`, `production`,
266
- `public`, `pg_*` gibi adlar rezervedir.
344
+ - Dal adı küçük harfle başlar; küçük harf/rakam/alt çizgi/tire ve `/` ile ayrılmış parçalardan oluşur
345
+ (ör. `feature/odeme`), 3-31 karakterdir ve şema adı olur. Adres parçası (slug) `/` yerine `-` taşır;
346
+ `development`, `production`, `public`, `pg_*` gibi adlar rezervedir.
@@ -534,8 +534,8 @@ hem `joins` ile getirin hem `selectFields`'a koyun; ondan sonra `filters[0][fiel
534
534
  > 2026-09-06 koşusunda dokuz uç kuruldu ve dokuz patch çağrısının dokuzu da yalnız bu yolu yazdı
535
535
  > (~3 dk). Alan create gövdesinde zaten kabul ediliyor.
536
536
 
537
- > Sayfa tarafındaki karşılığı: kullanıcı ekrandan süzüyorsa (`table` + `filterMode` açık, ya da
538
- > `datatable` + `isFilterable` kolonlar) uç `enableDynamicFilters: true` olmalı; komponent seçilen
537
+ > Sayfa tarafındaki karşılığı: kullanıcı ekrandan süzüyorsa (`datatable` + `isFilterable`
538
+ > kolonlar) uç `enableDynamicFilters: true` olmalı; komponent seçilen
539
539
  > koşulları kendi `filters` prop'una yazar ve akış onu uca `filters` parametresi olarak geçirir.
540
540
  > Süzme yoksa statik query-string filtreleri yeterlidir — bkz. `platform_guide('page')` Kural 1e.
541
541
 
@@ -7,7 +7,8 @@ Bir mobil ekran kurmadan önce bunu okuyun. Buradaki kurallar zevk meselesi değ
7
7
  - **Yazımda uyarı.** Yazım yanıtında sözleşme dışı durum `MOB-015…021` olarak gelir (`layoutWarnings`).
8
8
  - **Çizimde ölçüm.** `page_screenshot({ layoutReport: true })` sonucu sayıyla ölçer (`layoutSummary.alignment`).
9
9
 
10
- Temel mobil bilgi (flex-only yerleşim, tipli stil, gezinme, adımlar) için `platform_guide('mobile')`.
10
+ Temel mobil bilgi (flex-only yerleşim, tipli stil, gezinme) için `platform_guide('mobile')`, cihaz adımları için
11
+ `platform_guide('mobile-steps')`.
11
12
  Altın örnek: `ngapps://examples/mobile-form-screen`.
12
13
 
13
14
  ## Sözleşme
@@ -71,6 +71,7 @@ metni yeni numarayla yeniden dener.
71
71
  | P-11 | push (yalnız `testflight` / `internal`): `warn push_incomplete` (kimlik eksik — derleme push'suz çıkar); `push_unreadable` (kimliğin şifresi çözülemiyor), `push_profile_stale` (profil APNs anahtarından önce üretilmiş), `push_firebase_mismatch` (`google-services.json` başka Firebase projesinin), `push_package_mismatch` (dosya yüklendikten sonra Android paket adı değişmiş), `push_shell_outdated` (kabuk paketi push taşımıyor) | Studio › Yayın › Hesaplar ve kimlik bilgileri (insan yükler); `push_profile_stale`: profili yeniden üret; `push_shell_outdated`: platform ayarı (destek). Push kapalı ya da hazırsa satır `ok`'tur ve dryRun'da görünmez — hazır olduğunu `mobile_store_get { manifest: true }` → `manifest.push` söyler |
72
72
  | P-12 | izinler: `warn permissions_shell_outdated` (yayındaki kabuk izin kümesini taşımıyor: sabit izinler bildirilir, kamera çalışmaz, kipler uygulanmaz — başka P-12 satırı çıkmaz); kabuk taşıyorsa `fail permission_off_in_use` (kip `off` ama production o izni yazılı olarak kullanıyor) ya da `warn launch_permissions_shell_outdated` (açılışta sorulan izin var, kabuk açılışta sormuyor; kabuk kümeyi de taşımıyorsa bu cümle ilk uyarının iletisine eklenir) | `mobile_store_update { settings: { permissionModes } }` ya da kullanımı kaldırıp dağıtım; açılış uyarısında daha yeni kabuk ya da izni akış adımından isteme |
73
73
  | P-13 | reklam (yalnız `ads.enabled`): `warn ads_app_id_missing` (bu platformun AdMob App ID'si yok — derleme reklamsız çıkar), `warn ads_shell_outdated` (yayındaki kabuk reklam taşımıyor), yükleme hedefinde `manual ads_test_mode` (test kipi açık: mağazada test reklamı görünür), değilse `ok ads_ready` (yerleşim sayısıyla) | `mobile_store_update { settings: { ads } }`; test kipini yayından önce `testMode: false` |
74
+ | P-14 | kod tarama (yalnız production akışlarında `scan-code` adımı varsa): `warn scan_shell_outdated` (yayındaki kabuk tarayıcıyı taşımıyor — derleme çıkar, adım `error` dalıyla biter); taşıyorsa `ok scan_ready`. Kamerayı yalnız dosya alanı kullanıyorsa satır hiç gelmez | yeni kabuk paketi (destek); ajan düzeltemez |
74
75
 
75
76
  Satırın `message` ve `fixHint`'i backend'den, istek dilinde gelir.
76
77
 
@@ -0,0 +1,67 @@
1
+ # Mobil akış adımları rehberi
2
+
3
+ Mobil uygulamada (`type:'mobile'`) cihaza ve barındırıcıya dokunan akış adımları: mobildeki davranış, argüman
4
+ sözleşmesi, `Steps.<adım>` sonucu ve tarifler. Uygulamanın kendisi (kurulum, bileşenler, gezinme, kalıcı state,
5
+ uzak push, sayfanın `mount` / `focus` olayları) `platform_guide('mobile')`'dedir.
6
+
7
+ ## Adım sözlüğü
8
+
9
+ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
10
+ `validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
11
+
12
+ | Adım | Mobilde |
13
+ |---|---|
14
+ | `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
15
+ | `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
16
+ | `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. `message`/`description` metin şablonudur (`"{{ t('kayit.tamam') }}"`; `platform_guide('page')` §alert). Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
17
+ | `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
18
+ | `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
19
+ | `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (`platform_guide('mobile')` §Kalıcı state), değilse cihaz deposuna |
20
+ | `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
21
+ | `share` | RN paylaşım sayfası (metin + bağlantı; Android'de bağlantı metne eklenir, `title` iOS'ta e-posta konusu, Android'de seçici başlığı); akış `always` ile sürer — iOS'ta sayfa kapanınca, Android'de seçici açılır açılmaz (RN kapanışı bildirmez). Argümanlar `platform_guide('page')` §`share` adımı |
22
+ | `haptic` | dokunsal geri bildirim (`expo-haptics`, izin istemez). `args.value` stil, **düz metin**: `success` · `warning` · `error` · `light` · `medium` · `heavy` · `selection`; boş → `medium`. Değer yazmaz, hep `always`. Web'de titreşim varsa kısa titreşim, yoksa hiçbir şey (paletten yalnız mobilde sunulur) |
23
+ | `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur; derleme konumu taşımıyorsa sorulmaz, `error` dalı `status: 'unavailable'`). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
24
+ | `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
25
+ | `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik ve `args.title` başlık **metin şablonudur** — yalnız `{{ }}` içi çözülür (`'ilac-{{ item.id }}-0800'` kayıt başına ayrı kimlik, `"{{ t('ilac.vakti') }}"` çeviri; `{{ }}` dışındaki ifade çözülmez, FLOW-022); aynı kimlik eskisinin yerini alır. `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı, düz metin — şablon çözmez (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
26
+ | `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
27
+ | `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. `tracking` (iOS App Tracking Transparency) sonucu ayrıca `advertisingId` taşır: izin `granted` iken IDFA (iOS) / AAID (Android), değilse `null`; oynatıcı `tracking`'i hiç taşımaz (`unavailable`). Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
28
+ | `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
29
+ | `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
30
+ | `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
31
+ | `scan-code` | tam ekran kod tarayıcı (kök ADR 0017): tek kod okur ve kapanır. `args.value` izinli türler, **virgüllü düz metin** (`FLOW-042`): `aztec` · `codabar` · `code39` · `code93` · `code128` · `datamatrix` · `ean8` · `ean13` · `itf14` · `pdf417` · `qr` · `upc_e`; boş → hepsi. `upc_a` yok: UPC-A etiketi `ean13` ve on üç haneyle (başında `0`) döner. `args.title` ekrandaki yönerge, metin şablonu (`{{ t('anahtar') }}`). Sonuç `Steps.<adım>.data`: `{ value, type, source }` — `source` `camera` ya da `manual` (ekrandaki "elle gir"; tür bilinmiyorsa `type: null`). `error` dalı: `status` `cancelled` (kullanıcı kapattı — **vazgeçme bir hatadır, `error` dalını bağla**) · `denied` (kamera izni yok) · `unavailable` (derleme kamerayı taşımıyor ya da tarayıcı zaten açık) · `unsupported` (web uygulaması) · `invalid`. Kamera izni kendiliğinden derlemeye girer (adım production'daysa). Önizleme (`/dev`, `page_screenshot`) kamera açmaz, "kod gir" penceresi çizer: `__scan-value`'ya yaz, `__scan-confirm` ya da `__scan-cancel` (çok türde `__scan-type-<tür>`). `page_screenshot` pencereyi yalnız `captureUnready: true` ile sürer (akış pencereyi beklerken "koşuyor" sayılır; her eylem 15 sn bekler). Tam gövde: `ngapps://examples/mobile-scan-code` |
32
+
33
+ ## Bildirim tarifi
34
+
35
+ Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
36
+ `notification-permission` (S03 düğmesi), sonra `timepicker`'ın değeriyle `notification-schedule`
37
+ (`value: "<saat>.value"`, değer `HH:mm`) ve `error` dalında kullanıcıya mesaj. Çevrimdışı bandı adım değil
38
+ ifadedir: `isHidden: {{ Device.isOnline }}` (`platform_guide('expressions')`).
39
+
40
+ ## İzin tarifi
41
+
42
+ **İzin tarifi** (izin, ihtiyaç anında — düğmeye basılınca — istenir; sayfa açılışında değil): `permission`
43
+ `check` → `condition`, **bu sırayla**: `status === 'granted'` ise işe devam; `'unavailable'` ise özelliği sunma
44
+ (Ayarlar'da açılacak izin yoktur — `canAskAgain` burada da `false` gelir); `canAskAgain` ise `permission` `request`
45
+ ve sonucuna göre devam; değilse nedenini söyleyip `open-settings`. `location` adımı, `map.showUser` ve dosya alanının
46
+ kamera/galeri kaynağı izni kendileri de ister — `permission` adımı *ne zaman* sorulacağını seçmek ve sonucu
47
+ okumak içindir. `unavailable` bir hata değil, derlemenin o izni taşımadığının haberidir
48
+ (`platform_guide('mobile-publish')` §İzinler).
49
+
50
+ ## Reklam tarifi
51
+
52
+ **Reklam tarifi** (kök ADR 0012): (1) `mobile_store_update { settings: { ads: { enabled: true, iosAppId, androidAppId,
53
+ testMode: true, units: [{ name, format, ios, android }] } } }` — reklam, App ID'si olan platformun derlemesine `tracking`'i de sokar (ATT metni
54
+ gider, kip `off` olamaz); (2) banner için sayfaya `adBanner` `unit: '<banner yerleşimi>'`; (3) geçiş/ödüllü reklam için
55
+ düğme akışına `ad-show` `value: '<yerleşim>'`, `error` dalında reklamsız devam; (4) ödül `condition`:
56
+ `Steps.<adım>.data?.reward` doluysa ödülü ver. Açılış reklamı adım değil ayardır (`ads.appOpenUnit`). Sıra cihazda:
57
+ rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa reklam kişiselleştirilmeden istenir. Yerleşim adının
58
+ ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
59
+ (`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
60
+
61
+ ## Konum ve harita
62
+
63
+ `style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
64
+ (`FLOW-029`). "Haritada aç" için adım yoktur: `map`'in `press` olayı düğüme `mapsUrl` yazar, akış
65
+ `redirect` `isRaw: true`, `path: '<harita>.mapsUrl'` ile açar. Tarif — L05 "mevcut konumumu kullan":
66
+ düğmenin `click`'i → `location` (`always` → setter `il.value = Steps.konum.data.address?.region`, `error` →
67
+ setter hata metni). Uzak push (sunucudan gelen bildirim) ayrı rehberde: `platform_guide('mobile')` §Uzak push.
@@ -37,7 +37,8 @@ Mobil uygulamanın bileşen kataloğu ayrıdır (backend satırları `(slug, typ
37
37
  mobil farkları: düşen prop, değişen etkileşim, mobile özgü prop notları) ve `mobileNotes` (bütün
38
38
  mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`app-bar`, `safe-area`,
39
39
  `scroll-view`, `choice-chips`, `tooltip`, `markdown`, `calendar`, `stack`, `list-item`, `fab`, `timer`,
40
- `audio`, `lottie`, `webview`, `map`, `adBanner`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`) bilgi yalnız
40
+ `audio`, `lottie`, `webview`, `map`, `adBanner`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`,
41
+ `flip-card`, `swipeable-stack`) bilgi yalnız
41
42
  `mobileKnowledge`'tadır. Olay yükleri
42
43
  (`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
43
44
  `choice-chips.change`…), yoksa web'inkinden.
@@ -115,6 +116,9 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
115
116
  listesi) ve `floating-container` (ekranın 9 noktasından birine sabit kap — §Ekrana sabit içerik)
116
117
  mobile özgüdür. `scroll-view`'de ilk çocuk dışındaki doğrudan çocuklar `states.isSticky: true` ile
117
118
  kaydırırken üstte kalır (Studio panelinde alanı yok; şemaya MCP yazar).
119
+ - **Dönen kart ve kart destesi** (2026-10-08): `flip-card` (children = [ön `view`, arka `view`]; `isFlipped`,
120
+ `flip`) ve `swipeable-stack` (`repeater` şekli: [kart `view`, boş `view`], `rows`, `currentIndex`;
121
+ `swipeLeft` / `swipeRight` / `finish` yükü `event.data`) mobile özgüdür.
118
122
  - **Faz 10** (FlutterFlow D listesi): `datatable` web ikizidir — kolon biçimi, sıralama ve sayfa hesabı web'in
119
123
  modülünden, akış sözleşmesi aynı (sıralama/sayfa değişince `sortField`/`sortType`/`offset` düğüme yazılır ve
120
124
  `mount` yeniden koşar; satır eylemi `selectedRow`u yazıp eylemin akışını koşturur). Mobile özgü `variant`:
@@ -303,6 +307,18 @@ aynı katman sırası: tema taban → gruplar → serbest `style`).
303
307
  (`'15deg'` yazılmaz); aralık dışı (`rotate` ±360, `scale` 10–500, kaydırma ±1000) yazılmaz ve
304
308
  `nativeStyle.warnings`'e düşer. Katman ve kabukta (`modal`, `drawer`, `app-bar`) bu dört alan yoktur.
305
309
  - `typographyStyle` → renk, aile (tek ad), punto, kalınlık, hizalama, satır yüksekliği.
310
+ - `motionStyle` (panelde Motion; kabuğun dışında çizen `modal`, `drawer`, `photo-viewer`, `toast`, `fab`,
311
+ `floating-container` dışında her mobil bileşende) → düğümün hareketi: `enter` / `exit` (`fade`, `fadeUp`, `fadeDown`, `slideLeft`,
312
+ `slideRight`, girişte `zoomIn`, çıkışta `zoomOut`), `loop` (`pulse`, `spin`, `bounce`), `transition` (`colors`,
313
+ `opacity`, `transform`, `all`), `press` (`scale`, `opacity`), `layout` (aç/kapa), `duration` 100–2000 ms, `delay`
314
+ 0–5000 ms, `easing`. Yazılmayan alan yok sayılır (300 ms, `easeOut`; döngü kendi süresini kullanır) — varsayılanı
315
+ YAZMAYIN. Yön adı BAŞLANGIÇ tarafıdır: `fadeUp` yukarıdan, `fadeDown` aşağıdan, `slideLeft` soldan, `slideRight`
316
+ sağdan gelir. Tanınmayan alan ve küme/aralık dışı değer atlanır (`MOB-028`). `/dev`'de ve cihazda oynar: `enter` düğüm
317
+ çizilince, `exit` gizlenince (`isHidden`), `layout` bir kardeş gizlenip görününce, `transition` kabuğun stili
318
+ değişince; giriş biter, döngü sonra başlar; döngü ve `press` `surfaceStyle`'ın döndürme/ölçek/opaklığının üstüne
319
+ biner. Kanvasta donuktur (araç çubuğunda Oynat); "hareketi azalt"ta ve `page_screenshot`'ın donuk karesinde durur.
320
+ Koşullu hareket: alan da (`{ type: 'code', ref }`) grup da (`set_binding`) ifadeye bağlanır; alandaki düz metin
321
+ `{{ … }}` ifade değildir (`MOB-028`). Web şemasında yoktur.
306
322
  - Tema token'ları (`var(--ng-*)`) sayılara çözülür; proje teması değişince mobil de değişir.
307
323
 
308
324
  Çizilmeyen alanlar (`MOB-002`) ve yerine ne:
@@ -317,8 +333,9 @@ aynı katman sırası: tema taban → gruplar → serbest `style`).
317
333
  | `surfaceStyle`'ın tanımadığı alt anahtar (ör. CSS `transform` metni, `padding`) | grubun tipli alanları; döndürme/ölçek/kaydırma `rotate`, `scale`, `translateX`/`translateY` |
318
334
 
319
335
  Düğümün gerçek stilini görmek: `page_runtime_get({ applicationSlug, pageSlug, resolveStyles: true })`
320
- → her düğümde `nativeStyle: { shell, text, warnings }` (`shell` kutunun, `text` içine akan metnin
321
- stili; RN'de tipografi miras alınmaz). Yanıtta `styleResolver: 'native'`. Temanın katmanı
336
+ → her düğümde `nativeStyle: { shell, text, motion, bound, warnings }` (`shell` kutunun, `text` içine akan metnin
337
+ stili; RN'de tipografi miras alınmaz; `motion` yalnız hareketli düğümde, `motionStyle`'ın `/dev` ve cihazdaki
338
+ çözümü; `bound` ifadeye bağlı stil alanları — değerleri çalışma anında oluşur). Yanıtta `styleResolver: 'native'`. Temanın katmanı
322
339
  (`componentStyles[tip]` ← `$preset`) TABANDIR: panel grupları üstüne biner, düğümün serbest `style`'ı son söz — rapor
323
340
  da onu içerir. Mobilde tasarım sisteminin yolu `componentStyles`'tır (`theme_update`; tipin bütün düğümlerine iner).
324
341
  Mobil katalogda `style` prop'u olmadığı için düğüme yeni `$preset` başvurusu yazılamaz; kayıtlı (ör. web'den taşınmış)
@@ -376,7 +393,8 @@ etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tip
376
393
  Çekmece layout'un kök dizisindedir (`views: [shell_root, nav_drawer]`), kabuk kabının içinde değil.
377
394
  - Kabuk örnekleri: `ngapps://examples/mobile-app-shell-stack` (yığın), `ngapps://examples/mobile-app-shell-tab`
378
395
  (sekmeler), `ngapps://examples/mobile-app-shell-drawer` (çekmece); üç sayfalık liste → detay → form akışı:
379
- `ngapps://examples/mobile-list-detail-form`.
396
+ `ngapps://examples/mobile-list-detail-form`; kod tarama (düğme → `scan-code` → alana yaz):
397
+ `ngapps://examples/mobile-scan-code`.
380
398
 
381
399
  ## Çok dil
382
400
 
@@ -405,56 +423,11 @@ Mobil uygulama web ile **aynı sözlüğü ve aynı motoru** kullanır (plans/mo
405
423
  ## Akış adımları
406
424
 
407
425
  Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
408
- `validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
409
-
410
- | Adım | Mobilde |
411
- |---|---|
412
- | `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
413
- | `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
414
- | `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. `message`/`description` metin şablonudur (`"{{ t('kayit.tamam') }}"`; `platform_guide('page')` §alert). Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
415
- | `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
416
- | `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
417
- | `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (§Kalıcı state), değilse cihaz deposuna |
418
- | `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
419
- | `share` | RN paylaşım sayfası (metin + bağlantı; Android'de bağlantı metne eklenir, `title` iOS'ta e-posta konusu, Android'de seçici başlığı); akış `always` ile sürer — iOS'ta sayfa kapanınca, Android'de seçici açılır açılmaz (RN kapanışı bildirmez). Argümanlar `platform_guide('page')` §`share` adımı |
420
- | `haptic` | dokunsal geri bildirim (`expo-haptics`, izin istemez). `args.value` stil, **düz metin**: `success` · `warning` · `error` · `light` · `medium` · `heavy` · `selection`; boş → `medium`. Değer yazmaz, hep `always`. Web'de titreşim varsa kısa titreşim, yoksa hiçbir şey (paletten yalnız mobilde sunulur) |
421
- | `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur; derleme konumu taşımıyorsa sorulmaz, `error` dalı `status: 'unavailable'`). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
422
-
423
- | `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
424
- | `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik ve `args.title` başlık **metin şablonudur** — yalnız `{{ }}` içi çözülür (`'ilac-{{ item.id }}-0800'` kayıt başına ayrı kimlik, `"{{ t('ilac.vakti') }}"` çeviri; `{{ }}` dışındaki ifade çözülmez, FLOW-022); aynı kimlik eskisinin yerini alır. `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı, düz metin — şablon çözmez (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
425
- | `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
426
- | `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. `tracking` (iOS App Tracking Transparency) sonucu ayrıca `advertisingId` taşır: izin `granted` iken IDFA (iOS) / AAID (Android), değilse `null`; oynatıcı `tracking`'i hiç taşımaz (`unavailable`). Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
427
- | `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
428
- | `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
429
- | `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
430
-
431
- Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
432
- `notification-permission` (S03 düğmesi), sonra `timepicker`'ın değeriyle `notification-schedule`
433
- (`value: "<saat>.value"`, değer `HH:mm`) ve `error` dalında kullanıcıya mesaj. Çevrimdışı bandı adım değil
434
- ifadedir: `isHidden: {{ Device.isOnline }}` (`platform_guide('expressions')`).
435
-
436
- **İzin tarifi** (izin, ihtiyaç anında — düğmeye basılınca — istenir; sayfa açılışında değil): `permission`
437
- `check` → `condition`, **bu sırayla**: `status === 'granted'` ise işe devam; `'unavailable'` ise özelliği sunma
438
- (Ayarlar'da açılacak izin yoktur — `canAskAgain` burada da `false` gelir); `canAskAgain` ise `permission` `request`
439
- ve sonucuna göre devam; değilse nedenini söyleyip `open-settings`. `location` adımı, `map.showUser` ve dosya alanının
440
- kamera/galeri kaynağı izni kendileri de ister — `permission` adımı *ne zaman* sorulacağını seçmek ve sonucu
441
- okumak içindir. `unavailable` bir hata değil, derlemenin o izni taşımadığının haberidir
442
- (`platform_guide('mobile-publish')` §İzinler).
443
-
444
- **Reklam tarifi** (kök ADR 0012): (1) `mobile_store_update { settings: { ads: { enabled: true, iosAppId, androidAppId,
445
- testMode: true, units: [{ name, format, ios, android }] } } }` — reklam, App ID'si olan platformun derlemesine `tracking`'i de sokar (ATT metni
446
- gider, kip `off` olamaz); (2) banner için sayfaya `adBanner` `unit: '<banner yerleşimi>'`; (3) geçiş/ödüllü reklam için
447
- düğme akışına `ad-show` `value: '<yerleşim>'`, `error` dalında reklamsız devam; (4) ödül `condition`:
448
- `Steps.<adım>.data?.reward` doluysa ödülü ver. Açılış reklamı adım değil ayardır (`ads.appOpenUnit`). Sıra cihazda:
449
- rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa reklam kişiselleştirilmeden istenir. Yerleşim adının
450
- ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
451
- (`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
452
-
453
- `style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
454
- (`FLOW-029`). "Haritada aç" için adım yoktur: `map`'in `press` olayı düğüme `mapsUrl` yazar, akış
455
- `redirect` `isRaw: true`, `path: '<harita>.mapsUrl'` ile açar. Tarif — L05 "mevcut konumumu kullan":
456
- düğmenin `click`'i → `location` (`always` → setter `il.value = Steps.konum.data.address?.region`, `error` →
457
- setter hata metni). Uzak push (sunucudan gelen bildirim) ayrı bölümde: §Uzak push.
426
+ `validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar ayrı rehberdedir:
427
+ `platform_guide('mobile-steps')` — `redirect`, `alert`, `confirm`, `download`, `storage`, `setcookie`, `share`,
428
+ `haptic`, `location`, `notification-permission` / `-schedule` / `-cancel`, `permission`, `open-settings`, `back`
429
+ ve `ad-show`'un mobildeki davranışı, argümanları, `Steps.<adım>` sonucu; bildirim, izin, reklam ve konum
430
+ tarifleri. Bu adımlardan birini yazmadan önce onu okuyun.
458
431
 
459
432
  Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar. Yığındaki
460
433
  ve sekmedeki ekran bağlı kalır: geri dönüşte `mount` KOŞMAZ. Listeyi yeniden okumak için sayfanın
@@ -463,7 +436,6 @@ değişiminde koşar. Bağ: `page_update` `events: [{ name: 'focus', value: <flo
463
436
  `mount`'ta, liste okuması `focus`'ta — aynı okuma ikisine bağlanırsa ilk açılışta çift istek gider.
464
437
  `Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
465
438
 
466
-
467
439
  ## Uzak push
468
440
 
469
441
  Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kanal ikiliye bağlıdır (NTF-004):