@appaflytech/zeyrek-ai-mcp 0.0.21 → 0.0.23

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 (51) hide show
  1. package/dist/config.js +2 -0
  2. package/dist/knowledge/componentCatalog.js +6 -4
  3. package/dist/knowledge/componentEvents.js +12 -5
  4. package/dist/knowledge/mobileComponentCatalog.js +628 -0
  5. package/dist/knowledge/mobileComponentEvents.js +110 -0
  6. package/dist/knowledge/normalize.js +52 -11
  7. package/dist/knowledge/platform/lint.js +205 -0
  8. package/dist/knowledge/platform/mobile.js +78 -0
  9. package/dist/knowledge/platform/mobileRules.js +126 -0
  10. package/dist/knowledge/platform/mobileTabs.js +108 -0
  11. package/dist/knowledge/platform/profile.js +20 -0
  12. package/dist/knowledge/platform/resolve.js +122 -0
  13. package/dist/knowledge/platform/web.js +38 -0
  14. package/dist/knowledge/rules.js +56 -12
  15. package/dist/knowledge/styleResolve.js +1 -1
  16. package/dist/knowledge/styleResolveNative.js +410 -0
  17. package/dist/knowledge/themeResolve.js +3 -3
  18. package/dist/platform/capture/actions.js +333 -0
  19. package/dist/platform/capture/browser.js +1 -0
  20. package/dist/platform/{screenshot.js → capture/capture.js} +23 -509
  21. package/dist/platform/capture/index.js +10 -0
  22. package/dist/platform/capture/readiness.js +103 -0
  23. package/dist/platform/capture/summary.js +77 -0
  24. package/dist/platform/capture/target.js +5 -0
  25. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  26. package/dist/platform/capture/targets/studio-web.js +5 -0
  27. package/dist/platform/domSummary.js +1 -1
  28. package/dist/resources/examples/mobile-app-shell-drawer.json +209 -0
  29. package/dist/resources/examples/mobile-app-shell-stack.json +123 -0
  30. package/dist/resources/examples/mobile-app-shell-tab.json +174 -0
  31. package/dist/resources/examples/mobile-list-detail-form.json +656 -0
  32. package/dist/resources/guides/component.md +6 -1
  33. package/dist/resources/guides/functions.md +1 -1
  34. package/dist/resources/guides/layout.md +11 -7
  35. package/dist/resources/guides/mobile.md +283 -0
  36. package/dist/resources/guides/page.md +69 -16
  37. package/dist/resources/guides/translation.md +1 -1
  38. package/dist/server.js +2 -0
  39. package/dist/tools/applications.js +60 -3
  40. package/dist/tools/components.js +50 -10
  41. package/dist/tools/designSchemaPatch.js +13 -4
  42. package/dist/tools/externalComponents.js +25 -4
  43. package/dist/tools/flows.js +9 -2
  44. package/dist/tools/layouts.js +41 -9
  45. package/dist/tools/mobile.js +203 -0
  46. package/dist/tools/pages.js +115 -35
  47. package/dist/tools/platformInfo.js +2 -1
  48. package/dist/tools/profileWrite.js +45 -0
  49. package/dist/tools/screenWrite.js +2 -2
  50. package/dist/tools/screens.js +9 -3
  51. package/package.json +1 -1
@@ -4,6 +4,10 @@ Sayfaların paylaştığı kabuk: yan menü, üst şerit, kullanıcı menüsü v
4
4
  gireceği delik. Şema gövdesi ve koordinat modeli için `platform_guide('page')` (Kural 1,
5
5
  1d, 1f); komponent sözleşmeleri için `platform_guide('component')`.
6
6
 
7
+ > **Mobil uygulamada** kabuk `app-bar` + tek `slot`'tur; sekmeler uygulama ayarı
8
+ > (`properties.mobile.tabs`), çekmece `drawer` widget'ıdır. Web kabuk bileşenleri (`navigations`,
9
+ > `sidebar`, `header`) `MOB-004` alır — `platform_guide('mobile')` §Gezinme.
10
+
7
11
  ## Karar kuralları
8
12
 
9
13
  **1. Layout, tek `slot` içeren SIRADAN bir komponent ağacıdır.** Ayrı bir dil yoktur:
@@ -61,7 +65,7 @@ yapılan eski numaraya artık gerek yok.
61
65
  NEREDE durduğunu değil, BİÇİMİNİ söyler (dikey kolon / yatay şerit / dolgu yüzeyi). Hem
62
66
  runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
63
67
 
64
- **7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
68
+ **7. Dar ekranda flex YIĞILMAZ.** Runtime'ın dar ekran kuralı yalnız ızgara özelliklerini ezer.
65
69
  Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
66
70
  `direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
67
71
  alt alta, flex çocukları 49/310px yan yana). Not: `wrap` varsayılanı artık açık (2026-09-01) —
@@ -140,8 +144,8 @@ bağlantı nesnesi ya da tam yol).
140
144
  "direction": { "type": "static", "data": "row" } },
141
145
  "blocks": [{ "breakpoint": 1024, "size": { "w": 12, "h": 80 }, "position": { "x": 0, "y": 0 } }] }
142
146
 
143
- // KÖTÜ — telefonda 49px'lik yan menü: row flex mobilde yığılmaz (Kural 7)
144
- // Mobilde alt alta düşmesi gerekiyorsa kökü layout:'grid' kur — bedeli, fill'in
147
+ // KÖTÜ — telefonda 49px'lik yan menü: row flex dar ekranda yığılmaz (Kural 7)
148
+ // Dar ekranda alt alta düşmesi gerekiyorsa kökü layout:'grid' kur — bedeli, fill'in
145
149
  // flex anlamının devreye girmemesi.
146
150
  ```
147
151
 
@@ -179,9 +183,9 @@ bunlardan biri olabilir:
179
183
  |---|---|
180
184
  | ~~`heightType` yalnız `auto` \| `fixed`~~ | **28 Ağustos'ta kapandı:** üçüncü değer `fill` (Kural 5b) |
181
185
  | ~~Kök kap viewport'a kilitlenmez~~ | **28 Ağustos'ta kapandı:** `fill` verilmiş kök 100vh (Kural 5b) |
182
- | Flex mobilde yığılmaz | Kural 7 — kabuk bugün bir MASAÜSTÜ kabuğudur |
183
- | Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve mobilde AYNI; yalnız `blocks` ve `appearance.hideOn` kırılım bilir |
184
- | Mobil çekmece (overlay) yok | Telefonda içeriğin üstüne binen panel kurulamaz |
186
+ | Flex dar ekranda yığılmaz | Kural 7 — kabuk bugün bir MASAÜSTÜ kabuğudur |
187
+ | Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve dar ekranda AYNI; yalnız `blocks` ve `appearance.hideOn` kırılım bilir |
188
+ | Dar ekranda çekmece (overlay) yok | Telefon genişliğinde içeriğin üstüne binen panel kurulamaz (mobil UYGULAMADA çekmece `drawer` widget'ıdır — `platform_guide('mobile')`) |
185
189
 
186
190
  ---
187
191
 
@@ -189,7 +193,7 @@ bunlardan biri olabilir:
189
193
  (`injectPageSchemaIntoSlots`, `getResolvedLayoutViews`), `apps/studio/src/features/page/api/page.mappers.ts`
190
194
  (`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
191
195
  (`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
192
- `packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
196
+ `packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (dar ekran media kuralı),
193
197
  `packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthType`
194
198
  fill/fixed/auto dalları, `normalizeCssSize` kabul kuralı; tuval ve runtime aynı fonksiyonu okur),
195
199
  `apps/studio/src/features/page/components/appearance/appearance-size.functions.ts` (panelin
@@ -0,0 +1,283 @@
1
+ # Mobil uygulama rehberi
2
+
3
+ Mobil uygulama (`application_create` → `type: 'mobile'`) web uygulamasıyla **aynı şemayı**, aynı
4
+ micro-op sözlüğünü ve aynı akış motorunu kullanır; farkı çizerdedir. Sayfalar
5
+ `@ng-apps/ui-native` ile React Native olarak çizilir: Studio'da (kanvas, `/preview`, `/dev|/view`)
6
+ react-native-web üzerinde, cihazda Expo uygulamasında. Gateway uygulamanın tipini kendisi okur
7
+ (platform profili) ve mobilde çizilmeyecek her yazımı `MOB-*` satırıyla söyler — yazım yine
8
+ platforma gider, veri düşürülmez.
9
+
10
+ **Sözlük.** Bu rehberde "mobil" yalnız **mobil uygulama** demektir. Web uygulamasının telefondaki
11
+ dar ekran kırılımı (`hideOn: 'mobile'`, `breakpoint: mobile`) ayrı bir kavramdır ve
12
+ `platform_guide('page')` / `platform_guide('layout')`'ta anlatılır; alan adları değişmedi, yalnız
13
+ anlamları ayrı.
14
+
15
+ ## Kurulum sırası
16
+
17
+ 1. **Uygulama ve kabuk.** Tek çağrıda: `mobile_app_create` (uygulama + sayfalar + kabuk layout'u +
18
+ sekme ayarı + ana sayfa bağı, `dryRun` ile önce planı görün). Tekil araçlarla aynı sıra:
19
+ `application_create({ type: 'mobile' })` → `page_create` × N → `layout_create` (kabuk) →
20
+ `page_update({ layoutId })` × N → `application_update({ homepageId, properties.mobile.tabs })`.
21
+ Sekme ayarı sayfa **id**'si ister; bu yüzden sayfalardan SONRA yazılır.
22
+ 2. **Veri.** Entity, microservice ve workflow web'dekiyle aynıdır (`platform_guide('microservice')`).
23
+ Sayfa akışı `microservice` adımıyla aynı uçları çağırır.
24
+ 3. **Ekranlar.** `page_schema_patch` ile bileşen ekle, akış bağla (`flow_create` → `set_event`).
25
+ 4. **Doğrulama.** `page_screenshot` (mobilde varsayılan `device: 'phone'`, 390x844 @2) ve
26
+ `page_runtime_get({ resolveStyles: true })` (düğümün React Native stili). Ayrıntı §Doğrulama.
27
+
28
+ `page_create` mobil uygulamada kökü kendisi kurar: `page_container`, `layout` prop'u YOK,
29
+ `direction: 'column'`, `blocks: []`, `appearance.heightType: 'fill'` — kök telefon ekranını doldurur.
30
+
31
+ ## Bileşenler
32
+
33
+ Mobil uygulamanın bileşen kataloğu ayrıdır (backend satırları `(slug, type: 'mobile')`).
34
+ `component_list` ve `component_get` mobil uygulamada **kendiliğinden** mobil kataloğu okur.
35
+ `component_get` yanıtı `platform: 'mobile'` ile üç katman taşır: `knowledge` (web kaydı — mobil
36
+ şema web sınıfını genişlettiği için prop'ların anlamı oradadır), `mobileKnowledge` (bu bileşenin
37
+ mobil farkları: düşen prop, değişen etkileşim, mobile özgü prop notları) ve `mobileNotes` (bütün
38
+ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`app-bar`, `safe-area`,
39
+ `scroll-view`, `choice-chips`, `tooltip`, `markdown`, `calendar`, `stack`, `list-item`, `fab`, `timer`,
40
+ `audio`, `lottie`, `webview`, `map`) bilgi yalnız `mobileKnowledge`'tadır. Olay yükleri
41
+ (`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
42
+ `choice-chips.change`…), yoksa web'inkinden.
43
+
44
+ - Katalogda olmayan tip `MOB-003` alır ve backend `unknown-type` (422) ile reddeder. Mobil pakette
45
+ olan ama bu uygulamanın kataloğuna yazılmamış tip için gateway katalog senkronunu gösterir; web'de
46
+ olup mobilde henüz olmayan için ne zaman geleceğini söyler (Dalga 2, Faz 3).
47
+ - **Katalog A** (FlutterFlow paritesi, 2026-09-24) on bileşen ekledi: `badge`, `avatar`, `progress`,
48
+ `radio`, `checkboxgroup`, `choice-chips`, `collapsible-container`, `slider`, `tabs`, `tooltip`;
49
+ `button` mobilde `secondary`/`ghost`/`link` varyantlarını ve `icon` boyunu alır. Tam liste
50
+ `component_list`'tir.
51
+ - **Katalog B** (FlutterFlow paritesi) önce dört prop'la başladı; hepsi YALNIZ mobil şemadadır:
52
+ `scroll-view.stickyHeader` (ilk çocuk dikey kaydırmada üstte kalır), `numberinput.variant: stepper`
53
+ (± düğmeleri, iç roller `__decrement`/`__increment`), `modal.presentation: center | sheet | fullscreen`
54
+ (alt sayfa şeritten aşağı sürükleyince `change { isOpen: false }` ile kapanır; en çok ekranın %90'ı,
55
+ uzun içerik için içine `scroll-view`). En-boy oranı yeni
56
+ prop DEĞİLDİR: boyut panelinin `appearance.aspectRatio`'su mobilde kaplara da uygulanır
57
+ (§Yerleşim).
58
+ İkinci adımda dört bileşen: `markdown` (Markdown çizer, HTML çalıştırmaz; bağlantı `linkClick { href }`)
59
+ ve `calendar` (satır içi takvim, tek gün ya da aralık; prop adları ve `change` biçimi web
60
+ `datepicker`ınki) mobile özgüdür; `otpinput` ve `qrcode` web ikizleridir, kuralları web modülünden.
61
+ Üçüncü adımda dört bileşen daha: `stack` (katmanlar: ilk çocuk taban, üsttekiler yığının 9 noktalı
62
+ `alignment`ıyla hizalanır — çocukta hiza alanı YOKTUR), `list-item` (satır: baş · başlık · alt başlık ·
63
+ son; `click`), `fab` (yüzen düğme: sayfanın neresinde olursa olsun ekranın alt köşesinde durur, içerikle
64
+ kaymaz; `click`, iç rol `__press`) mobile özgüdür; `carousel` web ikizidir (`activeIndex`,
65
+ `slideChange`) ve mobilde bırakılabilir — çocukları sayfalardır.
66
+ Dördüncü adım Katalog B'yi kapattı: `timer` (mobile özgü; başlat/duraklat `isRunning`, sıfırlama `value`
67
+ yazmak; `tick { value }` her saniye, `finish`), `rating`, `multi-select` (değer `{ label, value }`
68
+ dizisi) ve `toast` web ikizleridir. `toast`ın bildirimi sayfadan bağımsız yaşar (`redirect`ten sonra da
69
+ görünür), açık modal ya da çekmecenin de üstünde çizilir; akışın tipli `alert` uyarıları cihazda da
70
+ toast'tur.
71
+ - **Katalog C** (cihaz modüllü bileşenler) medyayla başladı: `video` web ikizidir (cihazda `expo-video`,
72
+ kanvas ve önizlemede HTML5 `<video>`; `click` yalnız `controls: false` iken), `audio` (oynat/duraklat,
73
+ ilerleme izi; iç roller `__toggle`, `__track`) ve `lottie` (Lottie JSON ya da `.lottie`) mobile özgüdür.
74
+ İkisinde çalma `isPlaying` prop'udur: akış `true`/`false` yazar, bileşen `play`/`pause`/`ended` ya da
75
+ `finish` olayında `{ isPlaying }` yayar ve prop'u gerçeğe döndürür.
76
+ İkinci adım: `signature` web ikizidir (her vuruşta `change { value }` PNG data URL; vuruş sürerken sayfa
77
+ kaymaz; iOS 26'da itilmiş sayfada sağa vuruş sayfayı kapatır — platformun geri jesti) ve `webview` mobile özgüdür (web
78
+ `iframe`inin karşılığı): `src` ya da `html`; yüklemeden sonra başka belgeye giden gezinme varsayılan olarak
79
+ engellenir ve `navigate { url }` yayar, adres yalnız kullanıcının dokunduğu bağlantıda sistem tarayıcısında
80
+ açılır (`allowNavigation` görünümde açar); sayfa akışa `window.ReactNativeWebView.postMessage(metin)` →
81
+ `message { data }` ile yazar, yüklenemeyen sayfa `error` yayar.
82
+ Üçüncü adım: `chart` web ikizidir — seçenek ve olay eşlemesi web'in modülünden gelir, iki platform aynı
83
+ ECharts seçeneğini çizer (cihazda SVG); dışa aktarma, tip değiştirici, yenile ve kırıntı araç çubuğu
84
+ düşer. `map` mobile özgüdür (cihazda Apple/Google Haritalar, kanvas ve önizlemede Leaflet +
85
+ OpenStreetMap): görünüm `latitude`/`longitude`/`zoom` sayılarıdır ve akış yazınca harita oraya gider;
86
+ kullanıcı haritayı hareket ettirince `regionChange { latitude, longitude, zoom }` düğüme geri yazılır,
87
+ işaretçiye dokunmak `markerClick { markerId }` yayar.
88
+ Dördüncü adım: `datepicker`, `timepicker` ve `datetimepicker` web ikizleridir — kutuya dokununca cihazda
89
+ işletim sisteminin seçicisi (iOS alttan açılan sayfa, seçim Tamam'la yazılır; Android iletişim kutusu),
90
+ önizlemede tarayıcının seçicisi açılır; değer ve `change` yükü web ile aynıdır. Sistem seçicisi tek tek
91
+ saati kapatamadığı için `minTime`/`maxTime`/`disabledTimes` dışındaki seçim izinli en yakın saate yaslanır;
92
+ `datetimepicker` tek kutudur (`timePlaceholder` yok). `page_screenshot` `set` kökte değil
93
+ `<elementId>__input`ta, HTML biçimiyle çalışır (`2026-08-01`, `09:30`, `2026-08-01T09:30`). `fileInput` ve `filebutton` de web ikizidir:
94
+ değer web gibi base64 veri URL'sidir (dosya yüklenmez), tür/boyut/adet kapısı ve uyarılar web kuralı.
95
+ Seçiciyi mobile özgü `source` seçer — `files` (belge seçici), `library` (fotoğraf arşivi), `camera`,
96
+ `ask` (her seferinde sorar; `filebutton`'ın varsayılanı); fotoğraflar `quality` ile sıkıştırılır ve
97
+ iOS'ta HEIC JPEG'e çevrilir. Önizlemede tarayıcının dosya penceresi açılır (kanvasta alan kilitli);
98
+ kırpma yoktur.
99
+ - Web'e özgü bileşenlerin mobil karşılığı:
100
+
101
+ | Web | Mobilde |
102
+ |---|---|
103
+ | `navigations`, `sidebar`, `header`, `layout-shell` | `app-bar` + Ayarlar › Sekmeler + `drawer` widget'ı (`MOB-004`) |
104
+ | `popover` | `modal` widget'ı |
105
+ | `pagination` | `repeater` + "daha fazla yükle" düğmesi |
106
+ | `breadcrumb` | yığın gezinmesi (app-bar geri düğmesi) |
107
+ | `iframe` | `webview` (`src` ya da `html`) |
108
+ | `theme-toggle` | cihaz teması |
109
+ | `fileUpload` | `fileInput` (alan) ya da `filebutton` (düğme) — sürükle-bırak yok |
110
+
111
+ **Harici bileşen mobilde yoktur** (`MOB-006`): protokol v1 tarayıcı `.mjs` paketidir;
112
+ `external_component_*` araçları mobil uygulamada platforma gitmez.
113
+
114
+ ## Yerleşim — yalnız flex
115
+
116
+ Mobil çizer 12 sütunlu ızgarayı **okumaz**: sıra dizi indeksidir, yerleşim flex'tir.
117
+
118
+ - `blocks[]`, `set_block`, `layout: 'grid' | 'cssgrid'` yazılır ama çizilmez (`MOB-001`).
119
+ `add_component` / `move_component`'ın `index`'i dizideki sıradır; gateway konum çevirisi yapmaz.
120
+ - Kaplar flex kabıdır: `direction`, `gap`, `horizontalAlign`, `verticalAlign`, `wrap`, `size`.
121
+ Mobil `container` şemasında `layout` prop'u yoktur.
122
+ - Ölçü `appearance` ile: `widthType: 'fill' | 'auto' | 'fixed'` (+ `widthString`),
123
+ `heightType: 'fill' | 'auto' | 'fixed'` (+ `heightString`). `fill` kalan alanı alır.
124
+ - En-boy oranı `appearance.aspectRatio` (sayı; 16:9 için `1.78`): boy genişlikten türer. Koşullar:
125
+ `heightType: 'auto'` ve genişlik belli — `widthType: 'fill'` ya da `'fixed'` (içerik genişliğinde
126
+ web ile cihaz oranı ters yönden çözer, uygulanmaz). Web'den farkı: mobilde **akış kapları** da oran
127
+ alır (`container`, `form`, `safe-area`, `scroll-view`) ve akışları oranlı boyu doldurur; öteki
128
+ bırakılabilir tipler, widget örneği, sayfa kökü ve `modal`/`drawer` almaz; `image` oranı kendi
129
+ çizimine yazar.
130
+ - Hiza (`verticalAlign` `top | middle | bottom`, `horizontalAlign` `left | center | right`) web ile
131
+ aynıdır. Dikey hizanın boşluğu kabın boyundan gelir: `heightType` `fill`, `fixed` ya da oranlı kapta
132
+ akış kabın boyunu doldurur; boyu içerikten gelen sütun kapta dikey hiza etkisizdir (satır kapta farklı
133
+ boydaki kardeşleri yine hizalar). `stretch` de web gibi: ana eksende çocukları yayar, çapraz eksende gerer.
134
+ - Birim: `px` ve `%`. `em`/`rem` tema puntosuyla çarpılır; `calc()` ve `vw/vh` React Native'de yoktur
135
+ (`MOB-002`).
136
+
137
+ ## Görünüm — tipli gruplar
138
+
139
+ React Native'de CSS yoktur; tipli stil grupları **derlenir** (web'in `composeTypedStyle` ikizi,
140
+ aynı katman sırası: tema taban → gruplar → serbest `style`).
141
+
142
+ - `surfaceStyle` → zemin, köşe, kenarlık, gölge (tek katman), opaklık, taşma.
143
+ - `typographyStyle` → renk, aile (tek ad), punto, kalınlık, hizalama, satır yüksekliği.
144
+ - Tema token'ları (`var(--ng-*)`) sayılara çözülür; proje teması değişince mobil de değişir.
145
+
146
+ Çizilmeyen alanlar (`MOB-002`) ve yerine ne:
147
+
148
+ | Alan | Yerine |
149
+ |---|---|
150
+ | `className` | `surfaceStyle` / `typographyStyle` |
151
+ | serbest `style` nesnesi (alt seçici `&:hover`, `&__body`) | tipli gruplar; düz anahtarlar (ör. `padding`) çevrilir, alt seçici düşer |
152
+ | `hoverStyle` | yok — dokunmatikte üzerine gelme durumu yoktur |
153
+ | `tooltip`, `tooltipPosition` | düğümü `tooltip` bileşenine sarın (dokunuşla açılır, yanı `side`) ya da açıklamayı metin olarak koyun |
154
+ | `zIndex` | katman için `modal` ya da `drawer` widget'ı |
155
+
156
+ Düğümün gerçek stilini görmek: `page_runtime_get({ applicationSlug, pageSlug, resolveStyles: true })`
157
+ → her düğümde `nativeStyle: { shell, text, warnings }` (`shell` kutunun, `text` içine akan metnin
158
+ stili; RN'de tipografi miras alınmaz). Yanıtta `styleResolver: 'native'`. Temanın
159
+ `componentStyles`/preset katmanı mobil çizerde henüz uygulanmıyor ve rapora da girmez.
160
+
161
+ ## Görünüm — shadcn/ui
162
+
163
+ Bileşenler shadcn/ui görünümünde çizilir (react-native-reusables ilkelleri, NativeWind). Renk ve
164
+ köşe yine **proje temasından** gelir: `--ng-*` değişkenleri shadcn token'larına köprüyle çevrilir
165
+ (`--primary` ← `accent.primary`, `--destructive` ← hata rengi, `--border`, `--input`, `--ring`…).
166
+ `theme_update` web'i ve mobili birlikte değiştirir; koyu kip aynı köprünün ikinci çözümüdür. Ölçü
167
+ (alan yüksekliği, punto, iç boşluk) temanın `--ng-form-*` değişkenlerinden gelir. Yazı **ailesi**
168
+ köprüde yoktur: temanın ailesi alanlarda, başlıklarda ve sekme çubuğunda uygulanır; `text` ve düğme
169
+ etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tipiyle çizilir.
170
+
171
+ - `button`: `kind` rengi, `variant` kullanımını seçer — `solid` dolu (`kind: error` → shadcn
172
+ `destructive`), `outline` çerçeveli, `text`/`ghost` zeminsiz; mobilde ayrıca `secondary` (nötr
173
+ dolgu) ve `link`. `info`/`success`/`warning` → temanın `--status-*` renkleri. Eşlemenin tamamı ve
174
+ bilinen sınırlar: `component_get('button')` → `mobileKnowledge.propNotes`.
175
+ - `switch` platformun kendi anahtarı DEĞİLDİR: üç barındırıcıda (kanvas, önizleme, cihaz) aynı
176
+ shadcn anahtarı çizilir; kanvasta basılamadığı için soluk görünür.
177
+ - Web sayfası kendi bileşen ailesiyle çizilir; iki platformun görünüm farkı bilinçlidir, ortak olan
178
+ temadır. Yakalama seçicileri (`role="button"`, `__tabs__item-<slug>`, `submit` köprüsü) görünümden
179
+ bağımsızdır (§Doğrulama).
180
+
181
+ ## Gezinme
182
+
183
+ - **Yığın varsayılandır; ayar yoktur.** `redirect` adımı hedef sayfayı yığına iter, geri hareketi
184
+ (cihaz geri tuşu, app-bar geri düğmesi, tarayıcı geri) önceki ekrana döner. Göreli yol uygulama
185
+ köküne eklenir; tam adres (`https://…`) dışarıda açılır.
186
+ - **Sekmeler uygulama ayarıdır, layout'ta değil.** `application_update({ properties: { mobile: { tabs } } })`:
187
+
188
+ ```json
189
+ {
190
+ "mobile": {
191
+ "tabs": {
192
+ "enabled": true,
193
+ "style": "default",
194
+ "items": [
195
+ { "page": "<page_list id>", "label": "Görevler", "icon": "list" },
196
+ { "page": "<page_list id>", "label": "Profil", "icon": "user" }
197
+ ],
198
+ "showLabels": true
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ `items` 1–5 sayfa **id**'si (slug değil), tekrar yok; `style: 'floating'` köşe `pill` + gölge `md`
205
+ çizer. Çubuğun rengi ayarda YOKTUR: zemin `surface`, seçili `accent-primary`, seçilmemiş
206
+ `text-secondary` — değiştirmek için `theme_update`; eski `appearance` alanı okunmaz; `style: 'custom'` + `customWidgetId` çubuğu kendi widget'ınla çizer. Tutarsız ayar ve
207
+ layout/sayfada eski `tab-bar` / `drawer-menu` düğümü `MOB-007` alır. Sekme dışı sayfalar yığına açılır.
208
+ - **Çekmece bir bileşendir.** `drawer` widget'ı + `app-bar`'ın `menuTarget` prop'u çekmeceyi açar.
209
+ - Kabuk örnekleri: `ngapps://examples/mobile-app-shell-stack` (yığın), `ngapps://examples/mobile-app-shell-tab`
210
+ (sekmeler), `ngapps://examples/mobile-app-shell-drawer` (çekmece); üç sayfalık liste → detay → form akışı:
211
+ `ngapps://examples/mobile-list-detail-form`.
212
+
213
+ ## Çok dil
214
+
215
+ Mobil uygulama web ile **aynı sözlüğü ve aynı motoru** kullanır (plans/mobil-coklu-dil, 2026-09-25):
216
+ `project_update(languages, defaultLanguage)` → prop'u bağla (`set_prop_type(target, prop, 'translation', ref)`)
217
+ → `translation_update`. Kurulum ve fallback zinciri `platform_guide('translation')`.
218
+
219
+ - **Dil değiştirici:** `language-selector` mobil katalogdadır (`component_get` → `mobileKnowledge`):
220
+ `dropdown`un tam ekran `Modal` listesi, tetikleyici `role="combobox"`, seçenekler dilin yerel adıyla
221
+ (`tr` → Türkçe). Seçim `machine.setLanguage` ile bütün prop'ları yerinde çözer ve cihazda uygulama
222
+ çapında yayılır: öteki sekmelerin ekranları ve sekme etiketleri de değişir, makine yeniden kurulmaz.
223
+ - **Cihazda boot sırası:** uygulama içi seçim → cihazda kayıtlı seçim (`zeyrek:<app>:lang`) → cihaz dili
224
+ → projenin varsayılanı. `?lang` CİHAZDA YOKTUR; `/preview`'da vardır, bu yüzden `page_screenshot(language:)`
225
+ mobil sayfada da çalışır — kanıt için sayfaya seçici koymak gerekmez.
226
+ - **Sekme etiketi:** `properties.mobile.tabs[].label` `t:<anahtar>` ile çevrilir (doğrulama mesajıyla aynı
227
+ önek); `mobile_app_create` `tabs[].label` de kabul eder. Sayfa adı fallback'i düz kalır.
228
+ - **Çerçeve:** cihazda `alert`/`confirm` düğmeleri (`Tamam`/`Evet`/`Hayır` ↔ `OK`/`Yes`/`No`) uygulama dilini
229
+ izler; mesaj gövdesi akışın kendi metnidir.
230
+ - **Doğrulama:** `page_screenshot({ language: 'en' })` ikinci dilin karesi;
231
+ `page_screenshot({ actions: [{ select: '<seçici elementId>', value: 'en' }] })` seçiciyi sürer
232
+ (`optionsSeen` proje dili sayısıdır); seçici sonrası sekme çubuğunun `t:` etiketleri de aynı karede
233
+ çevrilir (ölçüldü 2026-09-25: `Ana/Liste`).
234
+ - **Bilinen fark:** mobilde istemci tarafı şema doğrulaması yoktur, `t:` doğrulama mesajı akışta
235
+ (`Loc()` / `t()`) çözülür.
236
+
237
+ ## Akış adımları
238
+
239
+ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
240
+ `validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
241
+
242
+ | Adım | Mobilde |
243
+ |---|---|
244
+ | `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
245
+ | `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
246
+ | `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler |
247
+ | `confirm` | `yes` / `no` dalları |
248
+ | `download` | adres cihazın açma sayfasında açılır, gövde paylaşım sayfasına gider (`MOB-005`, bilgi) |
249
+ | `storage` | cihaz deposu |
250
+ | `setcookie` | cihaz deposuna yazar (`MOB-005`, bilgi); paletten yalnız web'de sunulur |
251
+ | `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ı |
252
+
253
+ Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar.
254
+ `Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
255
+
256
+ ## Doğrulama
257
+
258
+ - `page_screenshot({ applicationSlug, pageSlug })` mobil uygulamada `device: 'phone'` (390x844, kare
259
+ 780x1688 px) çeker; `'tablet'` 820x1180 @2 ya da `{ width, height, scale? }`. `viewport` verilirse
260
+ cihaz yok sayılır (`deviceNote`). Yanıt `applicationType: 'mobile'` ve `platformNote` taşır: kare
261
+ react-native-web çizimidir, cihazdakiyle yapısal olarak (düğüm, sıra, ölçü, renk) aynıdır.
262
+ - Eylemler aynı sözlük: `click` basılabilir öğeyi (`role="button"`) bulur; `submit` runtime
263
+ köprüsüyle gider (`submitVia: 'bridge'`); sekme öğesi `__tabs__item-<sayfa slug'ı>`, çubuk `__tabs`
264
+ özet satırıdır. `select` açılır listeyi açar ve seçeneği değerle ya da görünen metinle seçer
265
+ (liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
266
+ bileşeninin sekmesini anahtarıyla (`<elementId>__tab-<anahtar>`) bulur.
267
+ - `flowRuns: 0` + `requests: 0` bir tıklamada akışın koşmadığını söyler: olay bağını
268
+ `page_runtime_get`, akışı `flow_get` ile doğrulayın.
269
+ - Kuru doğrulama: `page_schema_validate` mobil kataloğa karşı doğrular (`MOB-003`).
270
+
271
+ ## Cihazda önizleme
272
+
273
+ Expo uygulaması (`apps/mobile-runtime`) aynı sayfayı native çizer. Derin bağlantı
274
+ `zeyrek://open/{proje}/{uygulama}/{sayfa}?env=<ortam>` **token taşımaz**: cihazdaki uygulama zaten giriş
275
+ yapmış olmalıdır. Canlı önizleme sayfa şeması değişikliğini (`/hubs/page-schema`) dinler; yazdığın
276
+ yama cihazda yenilenir.
277
+
278
+ ## Bilinen farklar
279
+
280
+ - Piksel paritesi yoktur: yazı tipi ölçüsü, gölge ve güvenli alan cihaza göre değişir. Parite yapısaldır.
281
+ - Bileşen kataloğu dalga dalga gelir; `component_list` ne döndürüyorsa odur.
282
+ - Temanın bileşen stilleri (`componentStyles`, preset'ler) mobil çizerde henüz uygulanmıyor.
283
+ - Dar ekran kırılımı (`hideOn: 'mobile'`) mobil uygulamada anlamsızdır: ekran zaten tek boydur.
@@ -4,6 +4,10 @@ Studio'nun tuval tarafı. Buradaki kurallar controller + komut kodundan doğrula
4
4
  (`zeyrek-mcp/docs/13-faz2-envanter.md` §4); veri/mantık tarafı için
5
5
  `platform_guide('entity')`, `('microservice')`, `('workflow')`.
6
6
 
7
+ > **Mobil uygulamada** (`type: 'mobile'`) şema ve micro-op'lar aynıdır ama yerleşim yalnız flex'tir
8
+ > (blocks çizilmez), görünüm tipli gruplarla yazılır ve `page_create` kökü flex sütun kurar —
9
+ > `platform_guide('mobile')`. Bu rehberdeki "dar ekran" notları WEB uygulamasının telefon kırılımıdır.
10
+
7
11
  ## Kuruluş sırası
8
12
 
9
13
  ```
@@ -386,11 +390,8 @@ göstermek error flow'un işidir.
386
390
  > `errorTasks.some(t => t.type !== 'start')` arar. Start'ın ardına en az bir adım bağla —
387
391
  > gateway `FLOW-021` ile uyarır.
388
392
  >
389
- > **Mobilde `errorSteps` diye bir şey YOK.** `ScreenRuntime.tsx` `mapFlowsToMachine` yalnız
390
- > `tasks` üretir (web `Runtime.svelte` ayrıca `errorTasks` verir), yani mobil uygulamada
391
- > `hasErrorFlow` her zaman false: hata yolu hiç koşmaz ve akış yukarıdaki 2. yoldan —
392
- > `always` + hata gövdeli storage — devam eder. Mobil sayfada hata yolunu `condition
393
- > Steps.<adımAdı>.error` ile kur.
393
+ > **Mobil uygulama:** mobil runtime (`@ng-apps/ui-native` `NativeRuntime`) `errorSteps`'i web gibi
394
+ > `errorTasks`'a eşler — hata yolu iki platformda aynı koşar.
394
395
 
395
396
  > **Hata metni ÜÇ ayrı yerde durur ve ikisi kullanıcıya gösterilecek metin DEĞİLDİR.**
396
397
  > `onError` hata akışını koşturmadan önce `machine.setStorage('error', errorInfo)` yapar
@@ -513,21 +514,21 @@ Tarif: `platform_guide('middleware')`.
513
514
 
514
515
  **Sözlükler** (`StepType.cs`, `StepParameterValueType.cs` — hepsi küçük harf):
515
516
  `start · microservice · setter · setcookie · validation · condition · redirect · alert ·
516
- confirm · flow-trigger · storage · navigate · code · download · function · end`
517
+ confirm · flow-trigger · storage · navigate · code · download · function · share · end`
517
518
  değer tipleri: `static · page · property · storage · variable · code · item · state`
518
519
 
519
520
  > **Sözlükte olmak KOŞACAĞI anlamına gelmez — koşan tipler barındırıcıdan gelir.** Motor yedi
520
521
  > tip register eder, gerisini uygulama ekler. Bir sayfa TEK bir uygulamaya aittir
521
522
  > (`Page.ApplicationId`) ve uygulamanın tipi `web` ya da `mobile`'dır (`ApplicationType.cs`), yani
522
523
  > sayfanın barındırıcısı bellidir — matris "hangi tip sende çalışır"ı söyler, iki barındırıcıda
523
- > birden koşmayı değil (web `Runtime.svelte`, mobil `ScreenRuntime.tsx`):
524
+ > birden koşmayı değil (web `Runtime.svelte`, mobil uygulama `@ng-apps/ui-native` `NativeRuntime`):
524
525
  >
525
- > | Adım tipi | Web | Mobil | Nerede register ediliyor |
526
+ > | Adım tipi | Web | Mobil uygulama | Nerede register ediliyor |
526
527
  > |---|---|---|---|
527
- > | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` · `function` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi (`function` mobilde kayıtlıdır ama mobil runtime `Functions`'ı boş kurar — K11, çağrı çalışmaz) |
528
- > | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | uygulama; mobilde `redirect` navigate, `setcookie` storage uygulamasına eşlenir |
529
- > | `confirm` · `download` | ✅ | ❌ | yalnız `Runtime.svelte` |
530
- > | `navigate` | ❌ | ✅ | yalnız `ScreenRuntime.tsx` — web'deki karşılığı `redirect` |
528
+ > | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` · `function` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi |
529
+ > | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | barındırıcı; mobilde `setcookie` cihaz deposuna yazar (`MOB-005`) |
530
+ > | `confirm` · `download` · `share` | ✅ | ✅ | `Runtime.svelte` · `runtime-tasks.ts` (`download` mobilde açma/paylaşım — `MOB-005`; `share` aşağıda) |
531
+ > | `navigate` | ❌ | ✅ (`redirect` takma adı) | web'deki karşılığı `redirect`; gateway `redirect`'e çevirir |
531
532
  > | `end` · `query` | ❌ | ❌ | hiçbir yerde — üstelik `query` backend sözlüğünde de yok (`StepType.cs` onu saymaz), yalnız frontend enum'unda duruyor |
532
533
  >
533
534
  > **Register edilmemiş bir tipe gelen akış SESSİZCE durur.** `flow-engine.ts` `process()`
@@ -563,6 +564,19 @@ değer tipleri: `static · page · property · storage · variable · code · it
563
564
  > adımında ve `{{ }}` bağlamasında `Functions.<ad>(…)` ile de çağrılır; gateway bilinmeyen adı ve
564
565
  > fazla argümanı FLOW-025 ile uyarır. Tam gövde `ngapps://examples/flow-call-function`, kurallar
565
566
  > **`platform_guide('functions')`**.
567
+ >
568
+ > **`share` adımı ("Share")** metin ve bağlantı paylaşır: `args: { title, message, path }`. `title`
569
+ > DÜZ METİNDİR (ifade yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
570
+ > İFADEDİR — düz metin tırnak ister (`"'Şuna bak'"`, `"'https://…/urun/' + Page.queryParams.id"`).
571
+ > Bağlantının adı `url` DEĞİL `path`'tir: backend `Title`/`Message`/`Path` alanlarında saklar ve
572
+ > tanımadığı anahtarı 200 ile sessizce düşürür (FLOW-027 `url`/`link`/`text`'i ve tırnaksız adresi
573
+ > bloklar). Bağlantı ŞEMALI TAM ADRES olmalı (`https://…`, `mailto:…`): şemasız değer (`www.…`,
574
+ > `urun/42`) adres sayılmaz, metnin sonuna eklenir. İfadenin sonucu yalnız dize ya da sayıysa
575
+ > paylaşılır; `false` (`koşul && 'x'`), `null` ve nesne boş sayılır. Cihazda paylaşım sayfası açılır
576
+ > (Android'de bağlantı metnin sonuna eklenir), web'de tarayıcının paylaşım penceresi, yoksa ya da
577
+ > reddederse metin + bağlantı panoya yazılır ve bildirim çıkar. Tek dal `always`: iOS'ta sayfa
578
+ > kapanınca, Android'de seçici açılır açılmaz (RN seçicinin kapanışını bildirmez) sürer; metin de
579
+ > bağlantı da boşsa hiçbir şey açılmaz, akış hemen sürer.
566
580
 
567
581
  > Bir microservice adımı kaydedildiğinde platform `args.microserviceSchema`'yı
568
582
  > (parameterScheme + returnScheme + `isReturnArray`) kendisi doldurur — `flow_get` ile geri
@@ -861,7 +875,7 @@ kullanır: `page.mappers.ts` `mapPageLayoutToPreview` — layout varsa çizilen
861
875
  > `add_component` ile yeniden kurmaktır (sonrasında Kural 2'deki flow yeniden kaydetme adımı
862
876
  > ZORUNLU olur).
863
877
 
864
- Mobil not: <768px'te ızgara tek kolona düşer (`RenderComponent` media query'si) — y sırası
878
+ Dar ekran notu: <768px'te ızgara tek kolona düşer (`RenderComponent` media query'si) — y sırası
865
879
  dikey sıraya dönüşür, x/w yok sayılır. Yerleşimi masaüstü (breakpoint 1024) bloğuyla kur.
866
880
 
867
881
  ## Kural 1f-2 — Kapsayıcı düzeni: `flex` (varsayılan) ↔ `grid`
@@ -950,10 +964,10 @@ Beş şeyi bilmeden flex kurma:
950
964
  4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
951
965
  doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
952
966
  sessizce hiçbir şey yapmaz. Gateway bunu **PAGE-015 (uyarı)** ile söyler.
953
- 5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve mobilde AYNIDIR;
954
- sıra ve span'lar Desktop (1024) bloğundandır — Studio, mobil kırılım düzenlenirken flex
967
+ 5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve dar ekranda AYNIDIR;
968
+ sıra ve span'lar Desktop (1024) bloğundandır — Studio, dar ekran kırılımı düzenlenirken flex
955
969
  bölgeyi salt-okunur kilitler. Kırılım bilen yalnız `blocks[]` ve `appearance.hideOn`'dur;
956
- mobilde farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f mobil notu).
970
+ dar ekranda farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f dar ekran notu).
957
971
 
958
972
  ```jsonc
959
973
  // Ortalanmış giriş kartı — style ile CSS hack'i YOK, kabın kendi düzeni
@@ -1097,6 +1111,45 @@ kendisi nesneye çevirir; Studio tablosunda `[object Object]` görünmesi normal
1097
1111
  (`platform_guide('entity')` → "TEXT kolonunda JSON"). Ekranın görsel tarafı için **Kural 1h**:
1098
1112
  tema `--ng-*` değişkenleriyle tek yerden çevrilir, `html` kaçış kapısına gerek kalmaz.
1099
1113
 
1114
+ ## Kural 1g-2 — Mobil davranış: dört ayar, dört ayrı sahip
1115
+
1116
+ Mobil listelerin davranışı TEK bileşende toplanmaz. Hangi ayarın kimde olduğunu bilmeden yazılan
1117
+ şema sessizce çalışmaz — prop var olmayan bileşene yazılır ve `page_schema_patch` onu düşürmez,
1118
+ çizer yok sayar.
1119
+
1120
+ | Davranış | Sahip bileşen | Prop / olay |
1121
+ |---|---|---|
1122
+ | Çekerek yenile | `scroll-view` (listeyi SARAN kap) | `refreshable: true` + `refresh` olayı |
1123
+ | Sonsuz kaydırma | `scroll-view` | `scrollEnd` olayı (prop yok) |
1124
+ | Kaydırma eylemleri | `repeater` | `swipeActions: [{ key, label, tone, icon }]` + `swipe` olayı, payload `{ key, index, row }` |
1125
+ | Satıra dokunma | `repeater` | `itemPressable: true` + `itemPress` olayı, payload `{ index, row }` |
1126
+
1127
+ Çekerek yenileyi `repeater`'a yazmak en sık hatadır: liste kendi kaydırmasını yapmaz, onu saran
1128
+ `scroll-view` yapar. `repeater` kökteyse (kap yok) yenileme hiç çizilmez.
1129
+
1130
+ Satıra dokunmanın kartın `click`'iyle yapılan eski yolu mobilde bırakıldı: `itemPress` satır
1131
+ bağlamını payload'da taşır ve seçimi (`selectedIndex`) de yazar, yani akış satırı
1132
+ `<tekrarlayıcı>.selectedItem` ile okumaya devam eder.
1133
+
1134
+ ### `redirect.presentation`
1135
+
1136
+ `redirect` adımının `args.presentation` alanı ekranın NASIL açıldığını söyler ve kapalı bir küme
1137
+ alır:
1138
+
1139
+ - `push` — yığına ekler, geri düğmesi önceki ekrana döner (**varsayılan**; alan yazılmayabilir)
1140
+ - `modal` — alttan açılan ayrı bir sunum; geri onu kapatır, altındaki ekran yerinde kalır
1141
+ - `replace` — geçerli ekranı değiştirir, geçmişe girmez (giriş → ana ekran için doğru olan)
1142
+
1143
+ Küme dışı bir değer **MOB-008** uyarısıdır ve backend doğrulayıcısı 422 döner. Alanı hiç yazmamak
1144
+ kusur değildir.
1145
+
1146
+ **Sekme hedefinde `modal` çalışmaz.** Hedef sayfa bir sekmeyse gezinme her zaman `navigate`'e
1147
+ düşer: sekme yığını kendi geçmişini tutar ve modal onu kırardı. `presentation`ı yazabilirsiniz,
1148
+ çizer sessizce yok sayar — sekme dışı bir sayfaya modal istiyorsanız o sayfayı sekme listesinden
1149
+ çıkarın.
1150
+
1151
+ Web uygulamasında alan yok sayılır; gateway web profilinde bu konuda bulgu üretmez.
1152
+
1100
1153
  ## Kural 1h — Tasarım: `style` prop'u ve tema değişkenleri
1101
1154
 
1102
1155
  Her komponentin `style` prop'u vardır ve emotion `css()` bloğudur: iç seçiciler, pseudo-class'lar
@@ -208,7 +208,7 @@ gerekmez — `schema.ts`'e dokunulmaz, katalog yeniden yüklenmez.
208
208
  `translation_update` artık uygulamanın bütün sayfalarının önbellek satırlarını düşürüyor
209
209
  (`RefreshPageCacheWhenTranslationChangedCommand`), okuma yolu cache-aside olduğu için bir
210
210
  sonraki `page_runtime_get` taze değeri kurar. Yazmadan hemen sonra okuyabilirsin.
211
- - Mobil runtime (`apps/mobile`) bu özelliği henüz beslemiyor — yalnız web.
211
+ - Mobil uygulama runtime'ı (`@ng-apps/ui-native`, Expo) web ile aynı sözlüğü ve motoru kullanır (2026-09-25, plans/mobil-coklu-dil): `language-selector` mobil katalogda, cihazda dil sırası uygulama içi seçim → kayıtlı seçim → cihaz dili → varsayılan (`?lang` yalnız `/preview`'da), seçim uygulama çapında ve kalıcı, sekme etiketi `t:<anahtar>`, `X-Data-Language` sonraki isteklerle gider. Ayrıntı `platform_guide('mobile')` §Çok dil.
212
212
 
213
213
  ## Backend doğrulama sınırları (TR-001)
214
214
 
package/dist/server.js CHANGED
@@ -39,6 +39,7 @@ import { registerMergeTools } from "./tools/merges.js";
39
39
  import { registerRevisionTools } from "./tools/revisions.js";
40
40
  import { registerActivityTools } from "./tools/activities.js";
41
41
  import { registerDtoTools } from "./tools/dtos.js";
42
+ import { registerMobileTools } from "./tools/mobile.js";
42
43
  const moduleRegistrations = {
43
44
  platform: registerPlatformTools,
44
45
  project: registerProjectTools,
@@ -80,6 +81,7 @@ const moduleRegistrations = {
80
81
  merge: registerMergeTools,
81
82
  activity: registerActivityTools,
82
83
  revision: registerRevisionTools,
84
+ mobile: registerMobileTools,
83
85
  };
84
86
  export function createGatewayServer(platform) {
85
87
  const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config));
@@ -1,6 +1,44 @@
1
1
  import { guid, pagingParams, readOnly, writes, updates, destructive } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
+ import { forgetApplication, readApplicationPlatform, resolveApplication } from '../knowledge/platform/resolve.js';
4
+ import { mobileTabsProblems, readMobileTabs } from '../knowledge/platform/mobileTabs.js';
5
+ import { findingLine } from '../knowledge/platform/profile.js';
3
6
  import { z } from 'zod';
7
+ /**
8
+ * MOB-007 (sekme ayarı) — `properties.mobile.tabs` yazan mobil uygulama çağrısının denetimi. Yazım
9
+ * platforma YİNE gider (D3; backend `properties`'i doğrulamaz): bulgu `platformWarnings`'e düşer.
10
+ * Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar.
11
+ */
12
+ export async function mobileTabsWarnings(platform, applicationId, projectSlug, homepageId, properties) {
13
+ const tabs = readMobileTabs(properties);
14
+ if (tabs === undefined)
15
+ return [];
16
+ const pages = applicationId && projectSlug
17
+ ? await platform.tenant('GET', projectSlug, `/pages/${applicationId}`, { query: { pageIndex: 0, pageLength: 200 } })
18
+ .then(response => (response?.data ?? []).map(page => ({ id: String(page.id ?? '') })))
19
+ .catch(() => undefined)
20
+ : undefined;
21
+ const widgetIds = applicationId && projectSlug
22
+ ? await platform.tenant('GET', projectSlug, `/components/${applicationId}`, { query: { pageIndex: 0, pageLength: 999 } })
23
+ .then(response => new Set((response?.data ?? []).filter(entry => entry.kind === 'widget').map(entry => String(entry.id ?? ''))))
24
+ .catch(() => undefined)
25
+ : undefined;
26
+ return mobileTabsProblems(tabs, {
27
+ ...(pages ? { pages } : {}),
28
+ ...(homepageId ? { homepageId } : {}),
29
+ ...(widgetIds ? { widgetIds } : {})
30
+ }).map(message => findingLine({
31
+ code: 'MOB-007',
32
+ severity: 'warn',
33
+ message,
34
+ guide: "platform_guide('mobile') §Gezinme"
35
+ }));
36
+ }
37
+ function withPlatformWarnings(response, warnings) {
38
+ if (warnings.length === 0 || !response || typeof response !== 'object' || Array.isArray(response))
39
+ return response;
40
+ return { ...response, platformWarnings: warnings };
41
+ }
4
42
  export function registerApplicationTools(server, platform) {
5
43
  const config = platform.config;
6
44
  server.registerTool('application_list', {
@@ -34,7 +72,14 @@ export function registerApplicationTools(server, platform) {
34
72
  notFoundPageId: guid.optional()
35
73
  },
36
74
  annotations: writes
37
- }, handle(config, ({ projectId, ...body }) => platform.org('POST', `/applications/${projectId}`, { body })));
75
+ }, handle(config, async ({ projectId, ...body }) => {
76
+ const warnings = readApplicationPlatform(body.type) === 'mobile'
77
+ ? await mobileTabsWarnings(platform, undefined, undefined, undefined, body.properties)
78
+ : [];
79
+ const response = await platform.org('POST', `/applications/${projectId}`, { body });
80
+ forgetApplication(platform);
81
+ return withPlatformWarnings(response, warnings);
82
+ }));
38
83
  server.registerTool('application_update', {
39
84
  title: 'Application güncelle',
40
85
  description: 'Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın).',
@@ -49,11 +94,23 @@ export function registerApplicationTools(server, platform) {
49
94
  notFoundPageId: guid.optional()
50
95
  },
51
96
  annotations: updates
52
- }, handle(config, ({ id, ...body }) => platform.org('PUT', `/applications/${id}`, { body })));
97
+ }, handle(config, async ({ id, ...body }) => {
98
+ const current = await resolveApplication(platform, id);
99
+ const warnings = current?.platform === 'mobile'
100
+ ? await mobileTabsWarnings(platform, id, current.projectSlug, body.homepageId, body.properties)
101
+ : [];
102
+ const response = await platform.org('PUT', `/applications/${id}`, { body });
103
+ forgetApplication(platform, id);
104
+ return withPlatformWarnings(response, warnings);
105
+ }));
53
106
  server.registerTool('application_delete', {
54
107
  title: 'Application sil',
55
108
  description: 'Application\'ı kalıcı olarak siler.',
56
109
  inputSchema: { id: guid },
57
110
  annotations: destructive
58
- }, handle(config, args => platform.org('DELETE', `/applications/${args.id}`)));
111
+ }, handle(config, async (args) => {
112
+ const response = await platform.org('DELETE', `/applications/${args.id}`);
113
+ forgetApplication(platform, args.id);
114
+ return response;
115
+ }));
59
116
  }