@appaflytech/zeyrek-ai-mcp 0.0.20 → 0.0.21
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/README.md +14 -13
- package/dist/config.js +0 -2
- package/dist/knowledge/componentCatalog.js +940 -215
- package/dist/knowledge/componentEvents.js +368 -75
- package/dist/knowledge/normalize.js +66 -63
- package/dist/knowledge/rules.js +29 -6
- package/dist/knowledge/styleResolve.js +1 -1
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/platform/diagnostics.js +85 -0
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/ifExists.js +129 -0
- package/dist/platform/responses.js +2 -1
- package/dist/platform/{capture/capture.js → screenshot.js} +509 -23
- package/dist/platform/strictArguments.js +22 -2
- package/dist/platform/writeDiff.js +58 -31
- package/dist/platform/writeView.js +17 -0
- package/dist/resources/guides/component.md +9 -6
- package/dist/resources/guides/functions.md +32 -3
- package/dist/resources/guides/getting-started.md +35 -0
- package/dist/resources/guides/layout.md +7 -11
- package/dist/resources/guides/page.md +49 -71
- package/dist/resources/guides/translation.md +5 -6
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +18 -0
- package/dist/server.js +0 -2
- package/dist/tools/applications.js +3 -60
- package/dist/tools/components.js +10 -50
- package/dist/tools/designSchemaPatch.js +13 -18
- package/dist/tools/externalComponents.js +4 -25
- package/dist/tools/flows.js +38 -51
- package/dist/tools/functions.js +20 -10
- package/dist/tools/layouts.js +9 -41
- package/dist/tools/microservices.js +39 -11
- package/dist/tools/pages.js +95 -127
- package/dist/tools/platformInfo.js +1 -2
- package/dist/tools/schemaOps.js +2 -2
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +3 -9
- package/dist/tools/workflows.js +39 -12
- package/package.json +7 -2
- package/dist/knowledge/mobileComponentCatalog.js +0 -331
- package/dist/knowledge/mobileComponentEvents.js +0 -50
- package/dist/knowledge/platform/lint.js +0 -205
- package/dist/knowledge/platform/mobile.js +0 -78
- package/dist/knowledge/platform/mobileRules.js +0 -121
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/profile.js +0 -20
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -38
- package/dist/knowledge/styleResolveNative.js +0 -410
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/index.js +0 -10
- package/dist/platform/capture/readiness.js +0 -103
- package/dist/platform/capture/summary.js +0 -77
- package/dist/platform/capture/target.js +0 -5
- package/dist/platform/capture/targets/studio-mobile.js +0 -15
- package/dist/platform/capture/targets/studio-web.js +0 -5
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -209
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -123
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -174
- package/dist/resources/examples/mobile-list-detail-form.json +0 -656
- package/dist/resources/guides/mobile.md +0 -215
- package/dist/tools/mobile.js +0 -203
- package/dist/tools/profileWrite.js +0 -45
|
@@ -1,215 +0,0 @@
|
|
|
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`) bilgi yalnız `mobileKnowledge`'tadır. Olay yükleri
|
|
40
|
-
(`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
|
|
41
|
-
`choice-chips.change`…), yoksa web'inkinden.
|
|
42
|
-
|
|
43
|
-
- Katalogda olmayan tip `MOB-003` alır ve backend `unknown-type` (422) ile reddeder. Mobil pakette
|
|
44
|
-
olan ama bu uygulamanın kataloğuna yazılmamış tip için gateway katalog senkronunu gösterir; web'de
|
|
45
|
-
olup mobilde henüz olmayan için ne zaman geleceğini söyler (Dalga 2, Faz 3).
|
|
46
|
-
- **Katalog A** (FlutterFlow paritesi, 2026-09-24) on bileşen ekledi: `badge`, `avatar`, `progress`,
|
|
47
|
-
`radio`, `checkboxgroup`, `choice-chips`, `collapsible-container`, `slider`, `tabs`, `tooltip`;
|
|
48
|
-
`button` mobilde `secondary`/`ghost`/`link` varyantlarını ve `icon` boyunu alır. Tam liste
|
|
49
|
-
`component_list`'tir.
|
|
50
|
-
- **Katalog B** (FlutterFlow paritesi) önce dört prop'la başladı; hepsi YALNIZ mobil şemadadır:
|
|
51
|
-
`scroll-view.stickyHeader` (ilk çocuk dikey kaydırmada üstte kalır), `numberinput.variant: stepper`
|
|
52
|
-
(± düğmeleri, iç roller `__decrement`/`__increment`), `modal.presentation: center | sheet | fullscreen`
|
|
53
|
-
(alt sayfa şeritten aşağı sürükleyince `change { isOpen: false }` ile kapanır; en çok ekranın %90'ı,
|
|
54
|
-
uzun içerik için içine `scroll-view`). En-boy oranı yeni
|
|
55
|
-
prop DEĞİLDİR: boyut panelinin `appearance.aspectRatio`'su mobilde kaplara da uygulanır
|
|
56
|
-
(§Yerleşim).
|
|
57
|
-
- Web'e özgü bileşenlerin mobil karşılığı:
|
|
58
|
-
|
|
59
|
-
| Web | Mobilde |
|
|
60
|
-
|---|---|
|
|
61
|
-
| `navigations`, `sidebar`, `header`, `layout-shell` | `app-bar` + Ayarlar › Sekmeler + `drawer` widget'ı (`MOB-004`) |
|
|
62
|
-
| `popover` | `modal` widget'ı |
|
|
63
|
-
| `pagination` | `repeater` + "daha fazla yükle" düğmesi |
|
|
64
|
-
| `breadcrumb` | yığın gezinmesi (app-bar geri düğmesi) |
|
|
65
|
-
| `iframe` | `button` + `redirect` (`targetBlank`) ile tarayıcıda aç |
|
|
66
|
-
| `theme-toggle` | cihaz teması |
|
|
67
|
-
|
|
68
|
-
**Harici bileşen mobilde yoktur** (`MOB-006`): protokol v1 tarayıcı `.mjs` paketidir;
|
|
69
|
-
`external_component_*` araçları mobil uygulamada platforma gitmez.
|
|
70
|
-
|
|
71
|
-
## Yerleşim — yalnız flex
|
|
72
|
-
|
|
73
|
-
Mobil çizer 12 sütunlu ızgarayı **okumaz**: sıra dizi indeksidir, yerleşim flex'tir.
|
|
74
|
-
|
|
75
|
-
- `blocks[]`, `set_block`, `layout: 'grid' | 'cssgrid'` yazılır ama çizilmez (`MOB-001`).
|
|
76
|
-
`add_component` / `move_component`'ın `index`'i dizideki sıradır; gateway konum çevirisi yapmaz.
|
|
77
|
-
- Kaplar flex kabıdır: `direction`, `gap`, `horizontalAlign`, `verticalAlign`, `wrap`, `size`.
|
|
78
|
-
Mobil `container` şemasında `layout` prop'u yoktur.
|
|
79
|
-
- Ölçü `appearance` ile: `widthType: 'fill' | 'auto' | 'fixed'` (+ `widthString`),
|
|
80
|
-
`heightType: 'fill' | 'auto' | 'fixed'` (+ `heightString`). `fill` kalan alanı alır.
|
|
81
|
-
- En-boy oranı `appearance.aspectRatio` (sayı; 16:9 için `1.78`): boy genişlikten türer. Koşullar:
|
|
82
|
-
`heightType: 'auto'` ve genişlik belli — `widthType: 'fill'` ya da `'fixed'` (içerik genişliğinde
|
|
83
|
-
web ile cihaz oranı ters yönden çözer, uygulanmaz). Web'den farkı: mobilde **akış kapları** da oran
|
|
84
|
-
alır (`container`, `form`, `safe-area`, `scroll-view`) ve akışları oranlı boyu doldurur; öteki
|
|
85
|
-
bırakılabilir tipler, widget örneği, sayfa kökü ve `modal`/`drawer` almaz; `image` oranı kendi
|
|
86
|
-
çizimine yazar.
|
|
87
|
-
- Hiza (`verticalAlign` `top | middle | bottom`, `horizontalAlign` `left | center | right`) web ile
|
|
88
|
-
aynıdır. Dikey hizanın boşluğu kabın boyundan gelir: `heightType` `fill`, `fixed` ya da oranlı kapta
|
|
89
|
-
akış kabın boyunu doldurur; boyu içerikten gelen sütun kapta dikey hiza etkisizdir (satır kapta farklı
|
|
90
|
-
boydaki kardeşleri yine hizalar). `stretch` de web gibi: ana eksende çocukları yayar, çapraz eksende gerer.
|
|
91
|
-
- Birim: `px` ve `%`. `em`/`rem` tema puntosuyla çarpılır; `calc()` ve `vw/vh` React Native'de yoktur
|
|
92
|
-
(`MOB-002`).
|
|
93
|
-
|
|
94
|
-
## Görünüm — tipli gruplar
|
|
95
|
-
|
|
96
|
-
React Native'de CSS yoktur; tipli stil grupları **derlenir** (web'in `composeTypedStyle` ikizi,
|
|
97
|
-
aynı katman sırası: tema taban → gruplar → serbest `style`).
|
|
98
|
-
|
|
99
|
-
- `surfaceStyle` → zemin, köşe, kenarlık, gölge (tek katman), opaklık, taşma.
|
|
100
|
-
- `typographyStyle` → renk, aile (tek ad), punto, kalınlık, hizalama, satır yüksekliği.
|
|
101
|
-
- Tema token'ları (`var(--ng-*)`) sayılara çözülür; proje teması değişince mobil de değişir.
|
|
102
|
-
|
|
103
|
-
Çizilmeyen alanlar (`MOB-002`) ve yerine ne:
|
|
104
|
-
|
|
105
|
-
| Alan | Yerine |
|
|
106
|
-
|---|---|
|
|
107
|
-
| `className` | `surfaceStyle` / `typographyStyle` |
|
|
108
|
-
| serbest `style` nesnesi (alt seçici `&:hover`, `&__body`) | tipli gruplar; düz anahtarlar (ör. `padding`) çevrilir, alt seçici düşer |
|
|
109
|
-
| `hoverStyle` | yok — dokunmatikte üzerine gelme durumu yoktur |
|
|
110
|
-
| `tooltip`, `tooltipPosition` | düğümü `tooltip` bileşenine sarın (dokunuşla açılır, yanı `side`) ya da açıklamayı metin olarak koyun |
|
|
111
|
-
| `zIndex` | katman için `modal` ya da `drawer` widget'ı |
|
|
112
|
-
|
|
113
|
-
Düğümün gerçek stilini görmek: `page_runtime_get({ applicationSlug, pageSlug, resolveStyles: true })`
|
|
114
|
-
→ her düğümde `nativeStyle: { shell, text, warnings }` (`shell` kutunun, `text` içine akan metnin
|
|
115
|
-
stili; RN'de tipografi miras alınmaz). Yanıtta `styleResolver: 'native'`. Temanın
|
|
116
|
-
`componentStyles`/preset katmanı mobil çizerde henüz uygulanmıyor ve rapora da girmez.
|
|
117
|
-
|
|
118
|
-
## Görünüm — shadcn/ui
|
|
119
|
-
|
|
120
|
-
Bileşenler shadcn/ui görünümünde çizilir (react-native-reusables ilkelleri, NativeWind). Renk ve
|
|
121
|
-
köşe yine **proje temasından** gelir: `--ng-*` değişkenleri shadcn token'larına köprüyle çevrilir
|
|
122
|
-
(`--primary` ← `accent.primary`, `--destructive` ← hata rengi, `--border`, `--input`, `--ring`…).
|
|
123
|
-
`theme_update` web'i ve mobili birlikte değiştirir; koyu kip aynı köprünün ikinci çözümüdür. Ölçü
|
|
124
|
-
(alan yüksekliği, punto, iç boşluk) temanın `--ng-form-*` değişkenlerinden gelir. Yazı **ailesi**
|
|
125
|
-
köprüde yoktur: temanın ailesi alanlarda, başlıklarda ve sekme çubuğunda uygulanır; `text` ve düğme
|
|
126
|
-
etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tipiyle çizilir.
|
|
127
|
-
|
|
128
|
-
- `button`: `kind` rengi, `variant` kullanımını seçer — `solid` dolu (`kind: error` → shadcn
|
|
129
|
-
`destructive`), `outline` çerçeveli, `text`/`ghost` zeminsiz; mobilde ayrıca `secondary` (nötr
|
|
130
|
-
dolgu) ve `link`. `info`/`success`/`warning` → temanın `--status-*` renkleri. Eşlemenin tamamı ve
|
|
131
|
-
bilinen sınırlar: `component_get('button')` → `mobileKnowledge.propNotes`.
|
|
132
|
-
- `switch` platformun kendi anahtarı DEĞİLDİR: üç barındırıcıda (kanvas, önizleme, cihaz) aynı
|
|
133
|
-
shadcn anahtarı çizilir; kanvasta basılamadığı için soluk görünür.
|
|
134
|
-
- Web sayfası kendi bileşen ailesiyle çizilir; iki platformun görünüm farkı bilinçlidir, ortak olan
|
|
135
|
-
temadır. Yakalama seçicileri (`role="button"`, `__tabs__item-<slug>`, `submit` köprüsü) görünümden
|
|
136
|
-
bağımsızdır (§Doğrulama).
|
|
137
|
-
|
|
138
|
-
## Gezinme
|
|
139
|
-
|
|
140
|
-
- **Yığın varsayılandır; ayar yoktur.** `redirect` adımı hedef sayfayı yığına iter, geri hareketi
|
|
141
|
-
(cihaz geri tuşu, app-bar geri düğmesi, tarayıcı geri) önceki ekrana döner. Göreli yol uygulama
|
|
142
|
-
köküne eklenir; tam adres (`https://…`) dışarıda açılır.
|
|
143
|
-
- **Sekmeler uygulama ayarıdır, layout'ta değil.** `application_update({ properties: { mobile: { tabs } } })`:
|
|
144
|
-
|
|
145
|
-
```json
|
|
146
|
-
{
|
|
147
|
-
"mobile": {
|
|
148
|
-
"tabs": {
|
|
149
|
-
"enabled": true,
|
|
150
|
-
"style": "default",
|
|
151
|
-
"items": [
|
|
152
|
-
{ "page": "<page_list id>", "label": "Görevler", "icon": "list" },
|
|
153
|
-
{ "page": "<page_list id>", "label": "Profil", "icon": "user" }
|
|
154
|
-
],
|
|
155
|
-
"showLabels": true
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
`items` 1–5 sayfa **id**'si (slug değil), tekrar yok; `style: 'floating'` köşe `pill` + gölge `md`
|
|
162
|
-
çizer. Çubuğun rengi ayarda YOKTUR: zemin `surface`, seçili `accent-primary`, seçilmemiş
|
|
163
|
-
`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
|
|
164
|
-
layout/sayfada eski `tab-bar` / `drawer-menu` düğümü `MOB-007` alır. Sekme dışı sayfalar yığına açılır.
|
|
165
|
-
- **Çekmece bir bileşendir.** `drawer` widget'ı + `app-bar`'ın `menuTarget` prop'u çekmeceyi açar.
|
|
166
|
-
- Kabuk örnekleri: `ngapps://examples/mobile-app-shell-stack` (yığın), `ngapps://examples/mobile-app-shell-tab`
|
|
167
|
-
(sekmeler), `ngapps://examples/mobile-app-shell-drawer` (çekmece); üç sayfalık liste → detay → form akışı:
|
|
168
|
-
`ngapps://examples/mobile-list-detail-form`.
|
|
169
|
-
|
|
170
|
-
## Akış adımları
|
|
171
|
-
|
|
172
|
-
Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
|
|
173
|
-
`validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
|
|
174
|
-
|
|
175
|
-
| Adım | Mobilde |
|
|
176
|
-
|---|---|
|
|
177
|
-
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
178
|
-
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
179
|
-
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler |
|
|
180
|
-
| `confirm` | `yes` / `no` dalları |
|
|
181
|
-
| `download` | adres cihazın açma sayfasında açılır, gövde paylaşım sayfasına gider (`MOB-005`, bilgi) |
|
|
182
|
-
| `storage` | cihaz deposu |
|
|
183
|
-
| `setcookie` | cihaz deposuna yazar (`MOB-005`, bilgi); paletten yalnız web'de sunulur |
|
|
184
|
-
|
|
185
|
-
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar.
|
|
186
|
-
`Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
|
|
187
|
-
|
|
188
|
-
## Doğrulama
|
|
189
|
-
|
|
190
|
-
- `page_screenshot({ applicationSlug, pageSlug })` mobil uygulamada `device: 'phone'` (390x844, kare
|
|
191
|
-
780x1688 px) çeker; `'tablet'` 820x1180 @2 ya da `{ width, height, scale? }`. `viewport` verilirse
|
|
192
|
-
cihaz yok sayılır (`deviceNote`). Yanıt `applicationType: 'mobile'` ve `platformNote` taşır: kare
|
|
193
|
-
react-native-web çizimidir, cihazdakiyle yapısal olarak (düğüm, sıra, ölçü, renk) aynıdır.
|
|
194
|
-
- Eylemler aynı sözlük: `click` basılabilir öğeyi (`role="button"`) bulur; `submit` runtime
|
|
195
|
-
köprüsüyle gider (`submitVia: 'bridge'`); sekme öğesi `__tabs__item-<sayfa slug'ı>`, çubuk `__tabs`
|
|
196
|
-
ö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
|
|
197
|
-
(liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
|
|
198
|
-
bileşeninin sekmesini anahtarıyla (`<elementId>__tab-<anahtar>`) bulur.
|
|
199
|
-
- `flowRuns: 0` + `requests: 0` bir tıklamada akışın koşmadığını söyler: olay bağını
|
|
200
|
-
`page_runtime_get`, akışı `flow_get` ile doğrulayın.
|
|
201
|
-
- Kuru doğrulama: `page_schema_validate` mobil kataloğa karşı doğrular (`MOB-003`).
|
|
202
|
-
|
|
203
|
-
## Cihazda önizleme
|
|
204
|
-
|
|
205
|
-
Expo uygulaması (`apps/mobile-runtime`) aynı sayfayı native çizer. Derin bağlantı
|
|
206
|
-
`zeyrek://open/{proje}/{uygulama}/{sayfa}?env=<ortam>` **token taşımaz**: cihazdaki uygulama zaten giriş
|
|
207
|
-
yapmış olmalıdır. Canlı önizleme sayfa şeması değişikliğini (`/hubs/page-schema`) dinler; yazdığın
|
|
208
|
-
yama cihazda yenilenir.
|
|
209
|
-
|
|
210
|
-
## Bilinen farklar
|
|
211
|
-
|
|
212
|
-
- Piksel paritesi yoktur: yazı tipi ölçüsü, gölge ve güvenli alan cihaza göre değişir. Parite yapısaldır.
|
|
213
|
-
- Bileşen kataloğu dalga dalga gelir; `component_list` ne döndürüyorsa odur.
|
|
214
|
-
- Temanın bileşen stilleri (`componentStyles`, preset'ler) mobil çizerde henüz uygulanmıyor.
|
|
215
|
-
- Dar ekran kırılımı (`hideOn: 'mobile'`) mobil uygulamada anlamsızdır: ekran zaten tek boydur.
|
package/dist/tools/mobile.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { environmentParam, guid, projectParam, writes } from './common.js';
|
|
3
|
-
import { fetchHost, pageUpdateBody, unwrap } from './screens.js';
|
|
4
|
-
import { fetchCatalogSlugs } from './components.js';
|
|
5
|
-
import { mobileTabsWarnings } from './applications.js';
|
|
6
|
-
import { PlatformApiError } from '../platform/errors.js';
|
|
7
|
-
import { handle } from '../platform/responses.js';
|
|
8
|
-
import { ensurePageContainer } from '../knowledge/normalize.js';
|
|
9
|
-
import { mobileProfile } from '../knowledge/platform/mobile.js';
|
|
10
|
-
import { MAX_MOBILE_TABS } from '../knowledge/platform/mobileTabs.js';
|
|
11
|
-
import { forgetApplication } from '../knowledge/platform/resolve.js';
|
|
12
|
-
import { readKnowledgeFile } from '../resources/knowledge.js';
|
|
13
|
-
/** Ana sayfa: açıkça verilen, yoksa ilk sekme, yoksa ilk sayfa. */
|
|
14
|
-
function homepageSlug(input) {
|
|
15
|
-
return input.homepage ?? input.tabs?.[0]?.pageSlug ?? input.pages[0].pageSlug;
|
|
16
|
-
}
|
|
17
|
-
export function mobileAppInputErrors(input) {
|
|
18
|
-
const errors = [];
|
|
19
|
-
const slugs = input.pages.map(page => page.pageSlug);
|
|
20
|
-
const repeated = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);
|
|
21
|
-
if (input.pages.length === 0)
|
|
22
|
-
errors.push('pages en az bir sayfa ister');
|
|
23
|
-
if (repeated.length > 0)
|
|
24
|
-
errors.push(`pages içinde tekrar eden slug var: ${[...new Set(repeated)].join(', ')}`);
|
|
25
|
-
if (input.tabs !== undefined) {
|
|
26
|
-
const tabSlugs = input.tabs.map(tab => tab.pageSlug);
|
|
27
|
-
if (input.tabs.length === 0)
|
|
28
|
-
errors.push("tabs verildiyse en az bir öğe taşır; sekme istemiyorsan alanı hiç gönderme (yığın)");
|
|
29
|
-
if (input.tabs.length > MAX_MOBILE_TABS)
|
|
30
|
-
errors.push(`tabs en fazla ${MAX_MOBILE_TABS} öğe taşır (${input.tabs.length} verildi) — fazlası yığına açılan sayfa olarak kalmalı`);
|
|
31
|
-
for (const slug of tabSlugs.filter(slug => !slugs.includes(slug)))
|
|
32
|
-
errors.push(`tabs '${slug}' pages içinde yok — sekme yalnız kurulan bir sayfayı gösterebilir`);
|
|
33
|
-
if (tabSlugs.some((slug, index) => tabSlugs.indexOf(slug) !== index))
|
|
34
|
-
errors.push('tabs içinde tekrar eden sayfa var');
|
|
35
|
-
}
|
|
36
|
-
if (input.homepage !== undefined && !slugs.includes(input.homepage))
|
|
37
|
-
errors.push(`homepage '${input.homepage}' pages içinde yok`);
|
|
38
|
-
return errors;
|
|
39
|
-
}
|
|
40
|
-
export function buildMobileAppPlan(input) {
|
|
41
|
-
const home = homepageSlug(input);
|
|
42
|
-
const others = input.pages.filter(page => page.pageSlug !== home);
|
|
43
|
-
return [
|
|
44
|
-
{ step: 'application_create', tool: 'application_create', summary: "type:'mobile' uygulama; backend ana sayfayı kendisi açar" },
|
|
45
|
-
{ step: 'layout_create', tool: 'layout_create', summary: `kabuk: app-bar + slot${input.drawer ? ' + drawer widget (app-bar.menuTarget)' : ''}` },
|
|
46
|
-
...others.map((page, index) => ({ step: `page_create#${index + 1}`, tool: 'page_create', summary: `'${page.pageSlug}' (${page.pageName}), layoutId ile` })),
|
|
47
|
-
{ step: 'page_update#homepage', tool: 'page_update', summary: `backend'in ana sayfası → '${home}' (ad, slug, layoutId)` },
|
|
48
|
-
...(input.tabs ? [{ step: 'application_update', tool: 'application_update', summary: `properties.mobile.tabs: ${input.tabs.map(tab => tab.pageSlug).join(' · ')}` }] : [])
|
|
49
|
-
];
|
|
50
|
-
}
|
|
51
|
-
/** Kabuk ağacı örnek dosyasından okunur — örnek ile aracın kurduğu layout tek kaynaktır. */
|
|
52
|
-
function shellViews(input) {
|
|
53
|
-
const example = JSON.parse(readKnowledgeFile('examples', input.drawer ? 'mobile-app-shell-drawer.json' : 'mobile-app-shell-stack.json'));
|
|
54
|
-
const views = structuredClone(example.layoutCreate.views);
|
|
55
|
-
const appBar = views[0]?.children?.find(node => node.type === 'app-bar');
|
|
56
|
-
if (appBar && input.title)
|
|
57
|
-
appBar.props.title = { type: 'static', ref: null, data: input.title };
|
|
58
|
-
return views;
|
|
59
|
-
}
|
|
60
|
-
const SHELL_TYPES = ['app-bar', 'slot'];
|
|
61
|
-
export function registerMobileTools(server, platform) {
|
|
62
|
-
const config = platform.config;
|
|
63
|
-
server.registerTool('mobile_app_create', {
|
|
64
|
-
title: 'Mobil uygulamayı kabuğuyla kur',
|
|
65
|
-
description: "Mobil uygulamayı (type:'mobile'), kabuk layout'unu (app-bar + slot; drawer:true ise çekmece widget'ı), sayfalarını ve isteğe bağlı sekme ayarını (properties.mobile.tabs) TEK çağrıda kurar. Sıra: application_create → layout_create → page_create (sayfalar layoutId ile doğar) → backend'in açtığı ana sayfa seçilen ana sayfaya dönüştürülür (yetim sayfa kalmaz) → application_update (yalnız tabs verildiyse). tabs yoksa gezinme yığındır. dryRun:true planı döner, hiçbir şey yazmaz. Platformda işlem yoktur: hata olursa yanıt completed (kurulanlar), failed (düşen adım) ve remaining (kalanlar) taşır — kalanları tekil araçlarla tamamlayın. Kabuk bileşenleri (app-bar, slot) mobil katalogda yoksa layout atlanır ve uyarı döner. Rehber: platform_guide('mobile').",
|
|
66
|
-
inputSchema: {
|
|
67
|
-
project: projectParam,
|
|
68
|
-
environment: environmentParam,
|
|
69
|
-
projectId: guid,
|
|
70
|
-
slug: z.string().min(1),
|
|
71
|
-
name: z.string().min(1),
|
|
72
|
-
color: z.string().optional(),
|
|
73
|
-
pages: z.array(z.object({ pageSlug: z.string().min(1), pageName: z.string().min(1) })).min(1),
|
|
74
|
-
tabs: z.array(z.object({ pageSlug: z.string().min(1), label: z.string().optional(), icon: z.string().optional() })).optional()
|
|
75
|
-
.describe(`1-${MAX_MOBILE_TABS} sekme; her pageSlug pages içinden. Verilmezse sekme çubuğu yok (yığın).`),
|
|
76
|
-
drawer: z.boolean().optional().describe("true: layout'a drawer widget'ı + app-bar.menuTarget (çekmece)"),
|
|
77
|
-
title: z.string().optional().describe('app-bar başlığı; boşsa her sayfanın adı yazılır'),
|
|
78
|
-
homepage: z.string().optional().describe('Ana sayfanın pageSlug\'ı; varsayılan ilk sekme, sekme yoksa ilk sayfa'),
|
|
79
|
-
dryRun: z.boolean().optional()
|
|
80
|
-
},
|
|
81
|
-
annotations: writes
|
|
82
|
-
}, handle(config, async ({ project, environment, projectId, slug, name, color, pages, tabs, drawer, title, homepage, dryRun }) => {
|
|
83
|
-
const input = { pages, ...(tabs ? { tabs } : {}), ...(drawer ? { drawer } : {}), ...(homepage ? { homepage } : {}), ...(title ? { title } : {}) };
|
|
84
|
-
const errors = mobileAppInputErrors(input);
|
|
85
|
-
if (errors.length > 0)
|
|
86
|
-
throw new PlatformApiError(`mobile_app_create: ${errors.join(' · ')}. Hiçbir şey yazılmadı.`, 400);
|
|
87
|
-
const plan = buildMobileAppPlan(input);
|
|
88
|
-
if (dryRun)
|
|
89
|
-
return { dryRun: true, plan };
|
|
90
|
-
const completed = [];
|
|
91
|
-
const skipped = new Set();
|
|
92
|
-
const warnings = [];
|
|
93
|
-
const home = homepageSlug(input);
|
|
94
|
-
const pageIds = new Map();
|
|
95
|
-
let applicationId;
|
|
96
|
-
let homepageId;
|
|
97
|
-
let layoutId;
|
|
98
|
-
let current = 'application_create';
|
|
99
|
-
try {
|
|
100
|
-
const created = unwrap(await platform.org('POST', `/applications/${projectId}`, {
|
|
101
|
-
environment,
|
|
102
|
-
body: { slug, name, type: 'mobile', ...(color ? { color } : {}) }
|
|
103
|
-
}));
|
|
104
|
-
applicationId = String(created.id ?? '');
|
|
105
|
-
homepageId = typeof created.homepageId === 'string'
|
|
106
|
-
? created.homepageId
|
|
107
|
-
: String(created.homepage?.id ?? '');
|
|
108
|
-
forgetApplication(platform);
|
|
109
|
-
if (!applicationId || !homepageId)
|
|
110
|
-
throw new PlatformApiError('application_create yanıtı uygulama ya da ana sayfa kimliği taşımıyor — application_get ile kontrol edin.');
|
|
111
|
-
completed.push({ step: 'application_create', id: applicationId });
|
|
112
|
-
current = 'layout_create';
|
|
113
|
-
const catalog = await fetchCatalogSlugs(platform, project, environment, applicationId);
|
|
114
|
-
const missing = SHELL_TYPES.filter(type => !catalog.has(type));
|
|
115
|
-
if (missing.length > 0) {
|
|
116
|
-
skipped.add('layout_create');
|
|
117
|
-
warnings.push(`MOB-003 (uyarı) — kabuk bileşenleri mobil katalogda yok (${missing.join(', ')}): layout KURULMADI, sayfalar layout'suz açıldı. Katalog yazıldıktan sonra kabuğu ngapps://examples/mobile-app-shell-${input.drawer ? 'drawer' : input.tabs ? 'tab' : 'stack'} ile layout_create + page_update(layoutId) olarak kurun.`);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
const layout = unwrap(await platform.tenant('POST', project, `/layouts/${applicationId}`, {
|
|
121
|
-
environment,
|
|
122
|
-
body: { name: `${name} kabuğu`, views: shellViews(input), events: [], parameters: null }
|
|
123
|
-
}));
|
|
124
|
-
layoutId = String(layout.id ?? '');
|
|
125
|
-
completed.push({ step: 'layout_create', id: layoutId });
|
|
126
|
-
}
|
|
127
|
-
const others = pages.filter(page => page.pageSlug !== home);
|
|
128
|
-
for (const [index, page] of others.entries()) {
|
|
129
|
-
current = `page_create#${index + 1}`;
|
|
130
|
-
const saved = unwrap(await platform.tenant('POST', project, `/pages/${applicationId}`, {
|
|
131
|
-
environment,
|
|
132
|
-
body: {
|
|
133
|
-
name: page.pageName,
|
|
134
|
-
slug: page.pageSlug,
|
|
135
|
-
browserTitle: '',
|
|
136
|
-
browserTitleKey: '',
|
|
137
|
-
layoutId: layoutId ?? null,
|
|
138
|
-
roleIds: [],
|
|
139
|
-
middlewareIds: [],
|
|
140
|
-
events: [],
|
|
141
|
-
schema: ensurePageContainer([], mobileProfile.layout.rootContainer).schema
|
|
142
|
-
}
|
|
143
|
-
}));
|
|
144
|
-
pageIds.set(page.pageSlug, String(saved.id ?? ''));
|
|
145
|
-
completed.push({ step: current, id: String(saved.id ?? '') });
|
|
146
|
-
}
|
|
147
|
-
current = 'page_update#homepage';
|
|
148
|
-
const homePage = pages.find(page => page.pageSlug === home);
|
|
149
|
-
const existing = await fetchHost(platform, 'page', project, environment, applicationId, homepageId);
|
|
150
|
-
await platform.tenant('PUT', project, `/pages/${applicationId}/${homepageId}`, {
|
|
151
|
-
environment,
|
|
152
|
-
body: pageUpdateBody(existing, { name: homePage.pageName, slug: homePage.pageSlug, layoutId: layoutId ?? null })
|
|
153
|
-
});
|
|
154
|
-
pageIds.set(home, homepageId);
|
|
155
|
-
completed.push({ step: current, id: homepageId });
|
|
156
|
-
if (tabs) {
|
|
157
|
-
current = 'application_update';
|
|
158
|
-
const properties = {
|
|
159
|
-
mobile: {
|
|
160
|
-
tabs: {
|
|
161
|
-
enabled: true,
|
|
162
|
-
style: 'default',
|
|
163
|
-
showLabels: true,
|
|
164
|
-
items: tabs.map(tab => ({ page: pageIds.get(tab.pageSlug), ...(tab.label ? { label: tab.label } : {}), ...(tab.icon ? { icon: tab.icon } : {}) }))
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
warnings.push(...await mobileTabsWarnings(platform, applicationId, platform.resolveProject(project), homepageId, properties));
|
|
169
|
-
await platform.org('PUT', `/applications/${applicationId}`, {
|
|
170
|
-
environment,
|
|
171
|
-
body: { slug, name, homepageId, ...(color ? { color } : {}), properties }
|
|
172
|
-
});
|
|
173
|
-
forgetApplication(platform, applicationId);
|
|
174
|
-
completed.push({ step: current });
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
if (completed.length === 0)
|
|
179
|
-
throw error;
|
|
180
|
-
const done = new Set(completed.map(entry => entry.step));
|
|
181
|
-
return {
|
|
182
|
-
applicationId,
|
|
183
|
-
...(layoutId ? { layoutId } : {}),
|
|
184
|
-
completed,
|
|
185
|
-
failed: { step: current, error: error instanceof Error ? error.message : String(error) },
|
|
186
|
-
remaining: plan.map(step => step.step).filter(step => !done.has(step) && !skipped.has(step) && step !== current),
|
|
187
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
188
|
-
note: 'Kurulum yarıda kaldı: completed kurulanları, remaining eksik adımları söyler. Kalanları tekil araçlarla tamamlayın (page_create, page_update, application_update) — mobile_app_create aynı slug ile tekrar çağrılırsa uygulamayı ikinci kez açmaya çalışır.'
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
return {
|
|
192
|
-
applicationId,
|
|
193
|
-
homepageId,
|
|
194
|
-
...(layoutId ? { layoutId } : {}),
|
|
195
|
-
applicationType: 'mobile',
|
|
196
|
-
pages: pages.map(page => ({ slug: page.pageSlug, name: page.pageName, id: pageIds.get(page.pageSlug), ...(page.pageSlug === home ? { homepage: true } : {}) })),
|
|
197
|
-
...(tabs ? { tabs: tabs.map(tab => tab.pageSlug) } : {}),
|
|
198
|
-
completed,
|
|
199
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
200
|
-
next: "Ekranları page_schema_patch ile doldurun; doğrulama page_screenshot (mobilde varsayılan device: 'phone'). platform_guide('mobile')."
|
|
201
|
-
};
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { findingLines, profileFindings } from '../knowledge/platform/lint.js';
|
|
2
|
-
import { resolveProfile } from '../knowledge/platform/resolve.js';
|
|
3
|
-
import { fetchCatalogSlugs } from './components.js';
|
|
4
|
-
import { flowPath } from './screens.js';
|
|
5
|
-
/**
|
|
6
|
-
* Sayfa/layout/widget yazma yollarının profil kapısı: mobil profilde `MOB-*` bulgularını
|
|
7
|
-
* `layoutWarnings[]` satırı olarak döner. Web profilinde HİÇBİR istek atmaz ve boş dizi döner —
|
|
8
|
-
* web yanıtı profil öncesiyle aynı kalır (G1 golden).
|
|
9
|
-
*/
|
|
10
|
-
export async function screenProfileWarnings(platform, resolved, project, environment, applicationId, input) {
|
|
11
|
-
if (resolved.profile.platform === 'web')
|
|
12
|
-
return [];
|
|
13
|
-
const catalog = await fetchCatalogSlugs(platform, project, environment, applicationId).catch(() => undefined);
|
|
14
|
-
return findingLines(profileFindings(resolved.profile, { ...input, ...(catalog ? { catalog } : {}) }));
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* `set_event` ile bağlanan akışların adımları (MOB-005). Akış gövdeleri şemada değil ayrı uçtadır;
|
|
18
|
-
* okunamazsa bulgu üretilmez, yazım yine geçer.
|
|
19
|
-
*/
|
|
20
|
-
export async function boundFlowSteps(platform, resolved, project, environment, host, applicationId, hostId, ops) {
|
|
21
|
-
if (resolved.profile.platform === 'web')
|
|
22
|
-
return [];
|
|
23
|
-
const flowIds = new Set(ops.filter(entry => entry.op === 'set_event').map(entry => String(entry.eventValue ?? '')).filter(id => id.length > 0));
|
|
24
|
-
if (flowIds.size === 0)
|
|
25
|
-
return [];
|
|
26
|
-
try {
|
|
27
|
-
const hosted = await platform.tenant('GET', project, flowPath(host, applicationId, hostId), { environment });
|
|
28
|
-
return (hosted?.data ?? [])
|
|
29
|
-
.filter(flow => flowIds.has(String(flow.id ?? '')))
|
|
30
|
-
.flatMap(flow => [...(Array.isArray(flow.steps) ? flow.steps : []), ...(Array.isArray(flow.errorSteps) ? flow.errorSteps : [])]);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Akış yazma yollarının profil kapısı (MOB-005): mobil uygulamada `download`/`setcookie` adımının
|
|
38
|
-
* davranış farkını `platformWarnings`'e yazar. Web profilinde istek atmaz, boş döner.
|
|
39
|
-
*/
|
|
40
|
-
export async function flowProfileWarnings(platform, applicationId, steps) {
|
|
41
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
42
|
-
if (resolved.profile.platform === 'web')
|
|
43
|
-
return [];
|
|
44
|
-
return findingLines(profileFindings(resolved.profile, { kind: 'page', steps }));
|
|
45
|
-
}
|