@appaflytech/zeyrek-ai-mcp 0.0.21 → 0.0.22
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.
- package/dist/config.js +2 -0
- package/dist/knowledge/componentCatalog.js +3 -3
- package/dist/knowledge/componentEvents.js +9 -2
- package/dist/knowledge/mobileComponentCatalog.js +543 -0
- package/dist/knowledge/mobileComponentEvents.js +98 -0
- package/dist/knowledge/normalize.js +52 -11
- package/dist/knowledge/platform/lint.js +205 -0
- package/dist/knowledge/platform/mobile.js +78 -0
- package/dist/knowledge/platform/mobileRules.js +123 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/profile.js +20 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +38 -0
- package/dist/knowledge/rules.js +6 -8
- package/dist/knowledge/styleResolve.js +1 -1
- package/dist/knowledge/styleResolveNative.js +410 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/platform/capture/actions.js +333 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/{screenshot.js → capture/capture.js} +23 -509
- package/dist/platform/capture/index.js +10 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/domSummary.js +1 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +209 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +123 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +174 -0
- package/dist/resources/examples/mobile-list-detail-form.json +656 -0
- package/dist/resources/guides/component.md +6 -1
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/mobile.md +264 -0
- package/dist/resources/guides/page.md +55 -15
- package/dist/resources/guides/translation.md +1 -1
- package/dist/server.js +2 -0
- package/dist/tools/applications.js +60 -3
- package/dist/tools/components.js +50 -10
- package/dist/tools/designSchemaPatch.js +13 -4
- package/dist/tools/externalComponents.js +25 -4
- package/dist/tools/flows.js +9 -2
- package/dist/tools/layouts.js +41 -9
- package/dist/tools/mobile.js +203 -0
- package/dist/tools/pages.js +115 -35
- package/dist/tools/platformInfo.js +2 -1
- package/dist/tools/profileWrite.js +45 -0
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +9 -3
- 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.
|
|
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
|
|
144
|
-
//
|
|
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
|
|
183
|
-
| Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve
|
|
184
|
-
|
|
|
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` (
|
|
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,264 @@
|
|
|
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`) 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'ta itilmiş sayfada sağa vuruş sayfayı kapatır, ARC-116) 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
|
+
- Web'e özgü bileşenlerin mobil karşılığı:
|
|
83
|
+
|
|
84
|
+
| Web | Mobilde |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `navigations`, `sidebar`, `header`, `layout-shell` | `app-bar` + Ayarlar › Sekmeler + `drawer` widget'ı (`MOB-004`) |
|
|
87
|
+
| `popover` | `modal` widget'ı |
|
|
88
|
+
| `pagination` | `repeater` + "daha fazla yükle" düğmesi |
|
|
89
|
+
| `breadcrumb` | yığın gezinmesi (app-bar geri düğmesi) |
|
|
90
|
+
| `iframe` | `webview` (`src` ya da `html`) |
|
|
91
|
+
| `theme-toggle` | cihaz teması |
|
|
92
|
+
|
|
93
|
+
**Harici bileşen mobilde yoktur** (`MOB-006`): protokol v1 tarayıcı `.mjs` paketidir;
|
|
94
|
+
`external_component_*` araçları mobil uygulamada platforma gitmez.
|
|
95
|
+
|
|
96
|
+
## Yerleşim — yalnız flex
|
|
97
|
+
|
|
98
|
+
Mobil çizer 12 sütunlu ızgarayı **okumaz**: sıra dizi indeksidir, yerleşim flex'tir.
|
|
99
|
+
|
|
100
|
+
- `blocks[]`, `set_block`, `layout: 'grid' | 'cssgrid'` yazılır ama çizilmez (`MOB-001`).
|
|
101
|
+
`add_component` / `move_component`'ın `index`'i dizideki sıradır; gateway konum çevirisi yapmaz.
|
|
102
|
+
- Kaplar flex kabıdır: `direction`, `gap`, `horizontalAlign`, `verticalAlign`, `wrap`, `size`.
|
|
103
|
+
Mobil `container` şemasında `layout` prop'u yoktur.
|
|
104
|
+
- Ölçü `appearance` ile: `widthType: 'fill' | 'auto' | 'fixed'` (+ `widthString`),
|
|
105
|
+
`heightType: 'fill' | 'auto' | 'fixed'` (+ `heightString`). `fill` kalan alanı alır.
|
|
106
|
+
- En-boy oranı `appearance.aspectRatio` (sayı; 16:9 için `1.78`): boy genişlikten türer. Koşullar:
|
|
107
|
+
`heightType: 'auto'` ve genişlik belli — `widthType: 'fill'` ya da `'fixed'` (içerik genişliğinde
|
|
108
|
+
web ile cihaz oranı ters yönden çözer, uygulanmaz). Web'den farkı: mobilde **akış kapları** da oran
|
|
109
|
+
alır (`container`, `form`, `safe-area`, `scroll-view`) ve akışları oranlı boyu doldurur; öteki
|
|
110
|
+
bırakılabilir tipler, widget örneği, sayfa kökü ve `modal`/`drawer` almaz; `image` oranı kendi
|
|
111
|
+
çizimine yazar.
|
|
112
|
+
- Hiza (`verticalAlign` `top | middle | bottom`, `horizontalAlign` `left | center | right`) web ile
|
|
113
|
+
aynıdır. Dikey hizanın boşluğu kabın boyundan gelir: `heightType` `fill`, `fixed` ya da oranlı kapta
|
|
114
|
+
akış kabın boyunu doldurur; boyu içerikten gelen sütun kapta dikey hiza etkisizdir (satır kapta farklı
|
|
115
|
+
boydaki kardeşleri yine hizalar). `stretch` de web gibi: ana eksende çocukları yayar, çapraz eksende gerer.
|
|
116
|
+
- Birim: `px` ve `%`. `em`/`rem` tema puntosuyla çarpılır; `calc()` ve `vw/vh` React Native'de yoktur
|
|
117
|
+
(`MOB-002`).
|
|
118
|
+
|
|
119
|
+
## Görünüm — tipli gruplar
|
|
120
|
+
|
|
121
|
+
React Native'de CSS yoktur; tipli stil grupları **derlenir** (web'in `composeTypedStyle` ikizi,
|
|
122
|
+
aynı katman sırası: tema taban → gruplar → serbest `style`).
|
|
123
|
+
|
|
124
|
+
- `surfaceStyle` → zemin, köşe, kenarlık, gölge (tek katman), opaklık, taşma.
|
|
125
|
+
- `typographyStyle` → renk, aile (tek ad), punto, kalınlık, hizalama, satır yüksekliği.
|
|
126
|
+
- Tema token'ları (`var(--ng-*)`) sayılara çözülür; proje teması değişince mobil de değişir.
|
|
127
|
+
|
|
128
|
+
Çizilmeyen alanlar (`MOB-002`) ve yerine ne:
|
|
129
|
+
|
|
130
|
+
| Alan | Yerine |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `className` | `surfaceStyle` / `typographyStyle` |
|
|
133
|
+
| serbest `style` nesnesi (alt seçici `&:hover`, `&__body`) | tipli gruplar; düz anahtarlar (ör. `padding`) çevrilir, alt seçici düşer |
|
|
134
|
+
| `hoverStyle` | yok — dokunmatikte üzerine gelme durumu yoktur |
|
|
135
|
+
| `tooltip`, `tooltipPosition` | düğümü `tooltip` bileşenine sarın (dokunuşla açılır, yanı `side`) ya da açıklamayı metin olarak koyun |
|
|
136
|
+
| `zIndex` | katman için `modal` ya da `drawer` widget'ı |
|
|
137
|
+
|
|
138
|
+
Düğümün gerçek stilini görmek: `page_runtime_get({ applicationSlug, pageSlug, resolveStyles: true })`
|
|
139
|
+
→ her düğümde `nativeStyle: { shell, text, warnings }` (`shell` kutunun, `text` içine akan metnin
|
|
140
|
+
stili; RN'de tipografi miras alınmaz). Yanıtta `styleResolver: 'native'`. Temanın
|
|
141
|
+
`componentStyles`/preset katmanı mobil çizerde henüz uygulanmıyor ve rapora da girmez.
|
|
142
|
+
|
|
143
|
+
## Görünüm — shadcn/ui
|
|
144
|
+
|
|
145
|
+
Bileşenler shadcn/ui görünümünde çizilir (react-native-reusables ilkelleri, NativeWind). Renk ve
|
|
146
|
+
köşe yine **proje temasından** gelir: `--ng-*` değişkenleri shadcn token'larına köprüyle çevrilir
|
|
147
|
+
(`--primary` ← `accent.primary`, `--destructive` ← hata rengi, `--border`, `--input`, `--ring`…).
|
|
148
|
+
`theme_update` web'i ve mobili birlikte değiştirir; koyu kip aynı köprünün ikinci çözümüdür. Ölçü
|
|
149
|
+
(alan yüksekliği, punto, iç boşluk) temanın `--ng-form-*` değişkenlerinden gelir. Yazı **ailesi**
|
|
150
|
+
köprüde yoktur: temanın ailesi alanlarda, başlıklarda ve sekme çubuğunda uygulanır; `text` ve düğme
|
|
151
|
+
etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tipiyle çizilir.
|
|
152
|
+
|
|
153
|
+
- `button`: `kind` rengi, `variant` kullanımını seçer — `solid` dolu (`kind: error` → shadcn
|
|
154
|
+
`destructive`), `outline` çerçeveli, `text`/`ghost` zeminsiz; mobilde ayrıca `secondary` (nötr
|
|
155
|
+
dolgu) ve `link`. `info`/`success`/`warning` → temanın `--status-*` renkleri. Eşlemenin tamamı ve
|
|
156
|
+
bilinen sınırlar: `component_get('button')` → `mobileKnowledge.propNotes`.
|
|
157
|
+
- `switch` platformun kendi anahtarı DEĞİLDİR: üç barındırıcıda (kanvas, önizleme, cihaz) aynı
|
|
158
|
+
shadcn anahtarı çizilir; kanvasta basılamadığı için soluk görünür.
|
|
159
|
+
- Web sayfası kendi bileşen ailesiyle çizilir; iki platformun görünüm farkı bilinçlidir, ortak olan
|
|
160
|
+
temadır. Yakalama seçicileri (`role="button"`, `__tabs__item-<slug>`, `submit` köprüsü) görünümden
|
|
161
|
+
bağımsızdır (§Doğrulama).
|
|
162
|
+
|
|
163
|
+
## Gezinme
|
|
164
|
+
|
|
165
|
+
- **Yığın varsayılandır; ayar yoktur.** `redirect` adımı hedef sayfayı yığına iter, geri hareketi
|
|
166
|
+
(cihaz geri tuşu, app-bar geri düğmesi, tarayıcı geri) önceki ekrana döner. Göreli yol uygulama
|
|
167
|
+
köküne eklenir; tam adres (`https://…`) dışarıda açılır.
|
|
168
|
+
- **Sekmeler uygulama ayarıdır, layout'ta değil.** `application_update({ properties: { mobile: { tabs } } })`:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"mobile": {
|
|
173
|
+
"tabs": {
|
|
174
|
+
"enabled": true,
|
|
175
|
+
"style": "default",
|
|
176
|
+
"items": [
|
|
177
|
+
{ "page": "<page_list id>", "label": "Görevler", "icon": "list" },
|
|
178
|
+
{ "page": "<page_list id>", "label": "Profil", "icon": "user" }
|
|
179
|
+
],
|
|
180
|
+
"showLabels": true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`items` 1–5 sayfa **id**'si (slug değil), tekrar yok; `style: 'floating'` köşe `pill` + gölge `md`
|
|
187
|
+
çizer. Çubuğun rengi ayarda YOKTUR: zemin `surface`, seçili `accent-primary`, seçilmemiş
|
|
188
|
+
`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
|
|
189
|
+
layout/sayfada eski `tab-bar` / `drawer-menu` düğümü `MOB-007` alır. Sekme dışı sayfalar yığına açılır.
|
|
190
|
+
- **Çekmece bir bileşendir.** `drawer` widget'ı + `app-bar`'ın `menuTarget` prop'u çekmeceyi açar.
|
|
191
|
+
- Kabuk örnekleri: `ngapps://examples/mobile-app-shell-stack` (yığın), `ngapps://examples/mobile-app-shell-tab`
|
|
192
|
+
(sekmeler), `ngapps://examples/mobile-app-shell-drawer` (çekmece); üç sayfalık liste → detay → form akışı:
|
|
193
|
+
`ngapps://examples/mobile-list-detail-form`.
|
|
194
|
+
|
|
195
|
+
## Çok dil
|
|
196
|
+
|
|
197
|
+
Mobil uygulama web ile **aynı sözlüğü ve aynı motoru** kullanır (plans/mobil-coklu-dil, 2026-09-25):
|
|
198
|
+
`project_update(languages, defaultLanguage)` → prop'u bağla (`set_prop_type(target, prop, 'translation', ref)`)
|
|
199
|
+
→ `translation_update`. Kurulum ve fallback zinciri `platform_guide('translation')`.
|
|
200
|
+
|
|
201
|
+
- **Dil değiştirici:** `language-selector` mobil katalogdadır (`component_get` → `mobileKnowledge`):
|
|
202
|
+
`dropdown`un tam ekran `Modal` listesi, tetikleyici `role="combobox"`, seçenekler dilin yerel adıyla
|
|
203
|
+
(`tr` → Türkçe). Seçim `machine.setLanguage` ile bütün prop'ları yerinde çözer ve cihazda uygulama
|
|
204
|
+
çapında yayılır: öteki sekmelerin ekranları ve sekme etiketleri de değişir, makine yeniden kurulmaz.
|
|
205
|
+
- **Cihazda boot sırası:** uygulama içi seçim → cihazda kayıtlı seçim (`zeyrek:<app>:lang`) → cihaz dili
|
|
206
|
+
→ projenin varsayılanı. `?lang` CİHAZDA YOKTUR; `/preview`'da vardır, bu yüzden `page_screenshot(language:)`
|
|
207
|
+
mobil sayfada da çalışır — kanıt için sayfaya seçici koymak gerekmez.
|
|
208
|
+
- **Sekme etiketi:** `properties.mobile.tabs[].label` `t:<anahtar>` ile çevrilir (doğrulama mesajıyla aynı
|
|
209
|
+
önek); `mobile_app_create` `tabs[].label` de kabul eder. Sayfa adı fallback'i düz kalır.
|
|
210
|
+
- **Çerçeve:** cihazda `alert`/`confirm` düğmeleri (`Tamam`/`Evet`/`Hayır` ↔ `OK`/`Yes`/`No`) uygulama dilini
|
|
211
|
+
izler; mesaj gövdesi akışın kendi metnidir.
|
|
212
|
+
- **Doğrulama:** `page_screenshot({ language: 'en' })` ikinci dilin karesi;
|
|
213
|
+
`page_screenshot({ actions: [{ select: '<seçici elementId>', value: 'en' }] })` seçiciyi sürer
|
|
214
|
+
(`optionsSeen` proje dili sayısıdır); seçici sonrası sekme çubuğunun `t:` etiketleri de aynı karede
|
|
215
|
+
çevrilir (ölçüldü 2026-09-25: `Ana/Liste`).
|
|
216
|
+
- **Bilinen fark:** mobilde istemci tarafı şema doğrulaması yoktur, `t:` doğrulama mesajı akışta
|
|
217
|
+
(`Loc()` / `t()`) çözülür.
|
|
218
|
+
|
|
219
|
+
## Akış adımları
|
|
220
|
+
|
|
221
|
+
Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
|
|
222
|
+
`validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
|
|
223
|
+
|
|
224
|
+
| Adım | Mobilde |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
227
|
+
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
228
|
+
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler |
|
|
229
|
+
| `confirm` | `yes` / `no` dalları |
|
|
230
|
+
| `download` | adres cihazın açma sayfasında açılır, gövde paylaşım sayfasına gider (`MOB-005`, bilgi) |
|
|
231
|
+
| `storage` | cihaz deposu |
|
|
232
|
+
| `setcookie` | cihaz deposuna yazar (`MOB-005`, bilgi); paletten yalnız web'de sunulur |
|
|
233
|
+
|
|
234
|
+
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar.
|
|
235
|
+
`Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
|
|
236
|
+
|
|
237
|
+
## Doğrulama
|
|
238
|
+
|
|
239
|
+
- `page_screenshot({ applicationSlug, pageSlug })` mobil uygulamada `device: 'phone'` (390x844, kare
|
|
240
|
+
780x1688 px) çeker; `'tablet'` 820x1180 @2 ya da `{ width, height, scale? }`. `viewport` verilirse
|
|
241
|
+
cihaz yok sayılır (`deviceNote`). Yanıt `applicationType: 'mobile'` ve `platformNote` taşır: kare
|
|
242
|
+
react-native-web çizimidir, cihazdakiyle yapısal olarak (düğüm, sıra, ölçü, renk) aynıdır.
|
|
243
|
+
- Eylemler aynı sözlük: `click` basılabilir öğeyi (`role="button"`) bulur; `submit` runtime
|
|
244
|
+
köprüsüyle gider (`submitVia: 'bridge'`); sekme öğesi `__tabs__item-<sayfa slug'ı>`, çubuk `__tabs`
|
|
245
|
+
ö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
|
|
246
|
+
(liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
|
|
247
|
+
bileşeninin sekmesini anahtarıyla (`<elementId>__tab-<anahtar>`) bulur.
|
|
248
|
+
- `flowRuns: 0` + `requests: 0` bir tıklamada akışın koşmadığını söyler: olay bağını
|
|
249
|
+
`page_runtime_get`, akışı `flow_get` ile doğrulayın.
|
|
250
|
+
- Kuru doğrulama: `page_schema_validate` mobil kataloğa karşı doğrular (`MOB-003`).
|
|
251
|
+
|
|
252
|
+
## Cihazda önizleme
|
|
253
|
+
|
|
254
|
+
Expo uygulaması (`apps/mobile-runtime`) aynı sayfayı native çizer. Derin bağlantı
|
|
255
|
+
`zeyrek://open/{proje}/{uygulama}/{sayfa}?env=<ortam>` **token taşımaz**: cihazdaki uygulama zaten giriş
|
|
256
|
+
yapmış olmalıdır. Canlı önizleme sayfa şeması değişikliğini (`/hubs/page-schema`) dinler; yazdığın
|
|
257
|
+
yama cihazda yenilenir.
|
|
258
|
+
|
|
259
|
+
## Bilinen farklar
|
|
260
|
+
|
|
261
|
+
- Piksel paritesi yoktur: yazı tipi ölçüsü, gölge ve güvenli alan cihaza göre değişir. Parite yapısaldır.
|
|
262
|
+
- Bileşen kataloğu dalga dalga gelir; `component_list` ne döndürüyorsa odur.
|
|
263
|
+
- Temanın bileşen stilleri (`componentStyles`, preset'ler) mobil çizerde henüz uygulanmıyor.
|
|
264
|
+
- 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
|
-
> **
|
|
390
|
-
> `
|
|
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
|
|
@@ -520,14 +521,14 @@ değer tipleri: `static · page · property · storage · variable · code · it
|
|
|
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 `
|
|
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
|
|
528
|
-
> | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ |
|
|
529
|
-
> | `confirm` · `download` | ✅ |
|
|
530
|
-
> | `navigate` | ❌ | ✅
|
|
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` | ✅ | ✅ | `Runtime.svelte` · `runtime-tasks.ts` (`download` mobilde açma/paylaşım — `MOB-005`) |
|
|
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()`
|
|
@@ -861,7 +862,7 @@ kullanır: `page.mappers.ts` `mapPageLayoutToPreview` — layout varsa çizilen
|
|
|
861
862
|
> `add_component` ile yeniden kurmaktır (sonrasında Kural 2'deki flow yeniden kaydetme adımı
|
|
862
863
|
> ZORUNLU olur).
|
|
863
864
|
|
|
864
|
-
|
|
865
|
+
Dar ekran notu: <768px'te ızgara tek kolona düşer (`RenderComponent` media query'si) — y sırası
|
|
865
866
|
dikey sıraya dönüşür, x/w yok sayılır. Yerleşimi masaüstü (breakpoint 1024) bloğuyla kur.
|
|
866
867
|
|
|
867
868
|
## Kural 1f-2 — Kapsayıcı düzeni: `flex` (varsayılan) ↔ `grid`
|
|
@@ -950,10 +951,10 @@ Beş şeyi bilmeden flex kurma:
|
|
|
950
951
|
4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
|
|
951
952
|
doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
|
|
952
953
|
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
|
|
954
|
-
sıra ve span'lar Desktop (1024) bloğundandır — Studio,
|
|
954
|
+
5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve dar ekranda AYNIDIR;
|
|
955
|
+
sıra ve span'lar Desktop (1024) bloğundandır — Studio, dar ekran kırılımı düzenlenirken flex
|
|
955
956
|
bölgeyi salt-okunur kilitler. Kırılım bilen yalnız `blocks[]` ve `appearance.hideOn`'dur;
|
|
956
|
-
|
|
957
|
+
dar ekranda farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f dar ekran notu).
|
|
957
958
|
|
|
958
959
|
```jsonc
|
|
959
960
|
// Ortalanmış giriş kartı — style ile CSS hack'i YOK, kabın kendi düzeni
|
|
@@ -1097,6 +1098,45 @@ kendisi nesneye çevirir; Studio tablosunda `[object Object]` görünmesi normal
|
|
|
1097
1098
|
(`platform_guide('entity')` → "TEXT kolonunda JSON"). Ekranın görsel tarafı için **Kural 1h**:
|
|
1098
1099
|
tema `--ng-*` değişkenleriyle tek yerden çevrilir, `html` kaçış kapısına gerek kalmaz.
|
|
1099
1100
|
|
|
1101
|
+
## Kural 1g-2 — Mobil davranış: dört ayar, dört ayrı sahip
|
|
1102
|
+
|
|
1103
|
+
Mobil listelerin davranışı TEK bileşende toplanmaz. Hangi ayarın kimde olduğunu bilmeden yazılan
|
|
1104
|
+
şema sessizce çalışmaz — prop var olmayan bileşene yazılır ve `page_schema_patch` onu düşürmez,
|
|
1105
|
+
çizer yok sayar.
|
|
1106
|
+
|
|
1107
|
+
| Davranış | Sahip bileşen | Prop / olay |
|
|
1108
|
+
|---|---|---|
|
|
1109
|
+
| Çekerek yenile | `scroll-view` (listeyi SARAN kap) | `refreshable: true` + `refresh` olayı |
|
|
1110
|
+
| Sonsuz kaydırma | `scroll-view` | `scrollEnd` olayı (prop yok) |
|
|
1111
|
+
| Kaydırma eylemleri | `repeater` | `swipeActions: [{ key, label, tone, icon }]` + `swipe` olayı, payload `{ key, index, row }` |
|
|
1112
|
+
| Satıra dokunma | `repeater` | `itemPressable: true` + `itemPress` olayı, payload `{ index, row }` |
|
|
1113
|
+
|
|
1114
|
+
Çekerek yenileyi `repeater`'a yazmak en sık hatadır: liste kendi kaydırmasını yapmaz, onu saran
|
|
1115
|
+
`scroll-view` yapar. `repeater` kökteyse (kap yok) yenileme hiç çizilmez.
|
|
1116
|
+
|
|
1117
|
+
Satıra dokunmanın kartın `click`'iyle yapılan eski yolu mobilde bırakıldı: `itemPress` satır
|
|
1118
|
+
bağlamını payload'da taşır ve seçimi (`selectedIndex`) de yazar, yani akış satırı
|
|
1119
|
+
`<tekrarlayıcı>.selectedItem` ile okumaya devam eder.
|
|
1120
|
+
|
|
1121
|
+
### `redirect.presentation`
|
|
1122
|
+
|
|
1123
|
+
`redirect` adımının `args.presentation` alanı ekranın NASIL açıldığını söyler ve kapalı bir küme
|
|
1124
|
+
alır:
|
|
1125
|
+
|
|
1126
|
+
- `push` — yığına ekler, geri düğmesi önceki ekrana döner (**varsayılan**; alan yazılmayabilir)
|
|
1127
|
+
- `modal` — alttan açılan ayrı bir sunum; geri onu kapatır, altındaki ekran yerinde kalır
|
|
1128
|
+
- `replace` — geçerli ekranı değiştirir, geçmişe girmez (giriş → ana ekran için doğru olan)
|
|
1129
|
+
|
|
1130
|
+
Küme dışı bir değer **MOB-008** uyarısıdır ve backend doğrulayıcısı 422 döner. Alanı hiç yazmamak
|
|
1131
|
+
kusur değildir.
|
|
1132
|
+
|
|
1133
|
+
**Sekme hedefinde `modal` çalışmaz.** Hedef sayfa bir sekmeyse gezinme her zaman `navigate`'e
|
|
1134
|
+
düşer: sekme yığını kendi geçmişini tutar ve modal onu kırardı. `presentation`ı yazabilirsiniz,
|
|
1135
|
+
çizer sessizce yok sayar — sekme dışı bir sayfaya modal istiyorsanız o sayfayı sekme listesinden
|
|
1136
|
+
çıkarın.
|
|
1137
|
+
|
|
1138
|
+
Web uygulamasında alan yok sayılır; gateway web profilinde bu konuda bulgu üretmez.
|
|
1139
|
+
|
|
1100
1140
|
## Kural 1h — Tasarım: `style` prop'u ve tema değişkenleri
|
|
1101
1141
|
|
|
1102
1142
|
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 (
|
|
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 }) =>
|
|
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 }) =>
|
|
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 =>
|
|
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
|
}
|