@appaflytech/zeyrek-ai-mcp 0.0.24 → 0.0.26

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 (81) hide show
  1. package/.env.example +0 -15
  2. package/dist/config.js +0 -11
  3. package/dist/index.js +0 -2
  4. package/dist/knowledge/componentCatalog.js +25 -13
  5. package/dist/knowledge/componentEvents.js +6 -13
  6. package/dist/knowledge/normalize.js +17 -68
  7. package/dist/knowledge/rules.js +49 -137
  8. package/dist/knowledge/styleResolve.js +4 -50
  9. package/dist/knowledge/themeResolve.js +3 -3
  10. package/dist/observability/trace.js +1 -1
  11. package/dist/platform/diagnostics.js +4 -10
  12. package/dist/platform/domSummary.js +1 -1
  13. package/dist/platform/http.js +7 -23
  14. package/dist/platform/responses.js +2 -6
  15. package/dist/platform/{capture/capture.js → screenshot.js} +514 -52
  16. package/dist/platform/secrets.js +0 -9
  17. package/dist/platform/strictArguments.js +12 -68
  18. package/dist/platform/tokenVault.js +4 -187
  19. package/dist/resources/examples/workflow-http-step.json +2 -2
  20. package/dist/resources/guides/component.md +1 -6
  21. package/dist/resources/guides/expressions.md +2 -2
  22. package/dist/resources/guides/functions.md +1 -1
  23. package/dist/resources/guides/layout.md +7 -11
  24. package/dist/resources/guides/microfunction.md +28 -0
  25. package/dist/resources/guides/microservice.md +4 -10
  26. package/dist/resources/guides/middleware.md +1 -5
  27. package/dist/resources/guides/page.md +55 -215
  28. package/dist/resources/guides/theme.md +1 -4
  29. package/dist/resources/guides/translation.md +1 -1
  30. package/dist/resources/guides/workflow.md +9 -12
  31. package/dist/resources/schema-fingerprint.json +2 -2
  32. package/dist/resources/schemas/CreateWorkflowCommand.json +3 -0
  33. package/dist/resources/schemas/PreviewWorkflowCommand.json +3 -0
  34. package/dist/resources/schemas/StepDto.json +12 -9
  35. package/dist/resources/schemas/UpdateWorkflowCommand.json +3 -0
  36. package/dist/server.js +1 -3
  37. package/dist/tools/applications.js +3 -60
  38. package/dist/tools/components.js +10 -50
  39. package/dist/tools/datasources.js +1 -1
  40. package/dist/tools/designSchemaPatch.js +6 -16
  41. package/dist/tools/externalComponents.js +4 -25
  42. package/dist/tools/flows.js +6 -79
  43. package/dist/tools/layouts.js +12 -44
  44. package/dist/tools/microservices.js +10 -26
  45. package/dist/tools/pages.js +67 -303
  46. package/dist/tools/platformInfo.js +2 -6
  47. package/dist/tools/rows.js +1 -7
  48. package/dist/tools/schemaOps.js +1 -1
  49. package/dist/tools/screenWrite.js +5 -6
  50. package/dist/tools/screens.js +3 -90
  51. package/dist/tools/workflows.js +1 -1
  52. package/package.json +1 -1
  53. package/dist/knowledge/mobileComponentCatalog.js +0 -801
  54. package/dist/knowledge/mobileComponentEvents.js +0 -225
  55. package/dist/knowledge/platform/lint.js +0 -312
  56. package/dist/knowledge/platform/mobile.js +0 -78
  57. package/dist/knowledge/platform/mobileRules.js +0 -182
  58. package/dist/knowledge/platform/mobileTabs.js +0 -108
  59. package/dist/knowledge/platform/profile.js +0 -20
  60. package/dist/knowledge/platform/resolve.js +0 -122
  61. package/dist/knowledge/platform/web.js +0 -38
  62. package/dist/knowledge/styleResolveNative.js +0 -510
  63. package/dist/platform/argumentAliases.js +0 -128
  64. package/dist/platform/bodyFile.js +0 -103
  65. package/dist/platform/capture/actions.js +0 -333
  66. package/dist/platform/capture/browser.js +0 -1
  67. package/dist/platform/capture/console-log.js +0 -80
  68. package/dist/platform/capture/index.js +0 -11
  69. package/dist/platform/capture/readiness.js +0 -103
  70. package/dist/platform/capture/summary.js +0 -77
  71. package/dist/platform/capture/target.js +0 -5
  72. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  73. package/dist/platform/capture/targets/studio-web.js +0 -5
  74. package/dist/platform/schemaReadMemory.js +0 -60
  75. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -209
  76. package/dist/resources/examples/mobile-app-shell-stack.json +0 -123
  77. package/dist/resources/examples/mobile-app-shell-tab.json +0 -174
  78. package/dist/resources/examples/mobile-list-detail-form.json +0 -656
  79. package/dist/resources/guides/mobile.md +0 -419
  80. package/dist/tools/mobile.js +0 -203
  81. package/dist/tools/profileWrite.js +0 -45
@@ -1,225 +0,0 @@
1
- /**
2
- * MOBİLE ÖZGÜ olay yükleri. Mobil komponentlerin ÇOĞU web şemasını genişletir ve
3
- * olayları web sözleşmesindedir (`componentEvents.ts`) — bu dosya yalnız web'de
4
- * karşılığı OLMAYAN komponentler içindir (`app-bar` gibi kabuk komponentleri).
5
- *
6
- * Okuyucu önce buraya, sonra web sözlüğüne bakar; parity testi
7
- * (`test/mobile-component-parity.test.ts`) her mobil olayın iki sözlükten
8
- * birinde karşılanmasını zorunlu tutar.
9
- */
10
- const NO_DATA = { payload: {} };
11
- export const mobileComponentEventContracts = {
12
- 'choice-chips': {
13
- change: {
14
- payload: {
15
- value: 'string | number | boolean | Array — seçenek ÖZGÜN tipinde (değeri `34` yazılmış seçenek sayı yayar). `multiple` açıkken seçili değerlerin DİZİSİ (`[34, "a"]`); tek kipte seçili çipe yeniden dokunmak boş metin (`""`) yayar. Devre dışı seçenek olay yaymaz.'
16
- }
17
- }
18
- },
19
- // Web ikizinin yükü metindir (`e.currentTarget.value`); mobil seçeneği ÖZGÜN tipinde yayar (f-06, `optionEmitValues`).
20
- // İki platformda aynı koşul için karşılaştırmayı metne çevirin: `String(self.value) === '34'`.
21
- radio: {
22
- change: {
23
- payload: {
24
- value: 'string | number | boolean — seçilen seçeneğin ÖZGÜN değeri (değeri `34` yazılmış seçenek sayı yayar; web aynı seçimi `"34"` metni olarak yayar)'
25
- }
26
- }
27
- },
28
- checkboxgroup: {
29
- change: {
30
- payload: {
31
- value: 'Array — seçili seçeneklerin ÖZGÜN değerleri (`["su", 9]`; web metin dizisi yayar). Seçeneklerde olmayan eski değer metin kalır'
32
- }
33
- }
34
- },
35
- tooltip: {
36
- open: NO_DATA
37
- },
38
- calendar: {
39
- mount: NO_DATA,
40
- change: {
41
- payload: {
42
- value: "string 'yyyy-MM-ddT00:00:00' | null — seçilen gün (aralıkta başlangıç); web datepicker'ın biçimi: yerel duvar saati, gece yarısı, dilim eki yok",
43
- endValue: "string 'yyyy-MM-ddT00:00:00' | null — YALNIZ mode: 'range'; ilk basışta null, ikinci basışta bitiş günü"
44
- }
45
- }
46
- },
47
- stack: {
48
- mount: NO_DATA
49
- },
50
- 'list-item': {
51
- click: NO_DATA
52
- },
53
- fab: {
54
- click: NO_DATA
55
- },
56
- timer: {
57
- mount: NO_DATA,
58
- tick: { payload: { value: 'number — gösterilen saniye (geri sayımda kalan, kronometrede geçen); makine value prop\'una yazar' } },
59
- finish: { payload: { isRunning: 'false — geri sayım 0\'a indi; makine isRunning\'i kapatır' } }
60
- },
61
- audio: {
62
- mount: NO_DATA,
63
- play: { payload: { isPlaying: "true — çalma başladı (denetim ya da akış); makine isPlaying'i yazar" } },
64
- pause: { payload: { isPlaying: "false — çalma durdu (denetim, akış, bitiş ya da tarayıcının otomatik oynatma reddi); makine isPlaying'i yazar" } },
65
- ended: { payload: { isPlaying: "false — ses sona vardı (loop kapalıyken, pause'tan sonra); makine isPlaying'i kapatır" } }
66
- },
67
- lottie: {
68
- mount: NO_DATA,
69
- finish: { payload: { isPlaying: "false — döngüsüz animasyon bitti; makine isPlaying'i kapatır, yeniden true yazmak baştan oynatır" } }
70
- },
71
- webview: {
72
- mount: NO_DATA,
73
- load: { payload: {}, reads: "her tamamlanan yüklemede yayılır (izin verilen gezinmeden sonra da); cihazda yüklenemeyen sayfada yayılmaz (yerine error), web'de tarayıcı engellenen çerçevede de yayar" },
74
- navigate: { payload: { url: "string — sayfanın gitmek istediği adres; makine url prop'una yazar (self.url). allowNavigation kapalıyken (varsayılan) gezinme engellenir; adres yalnız kullanıcının dokunduğu bağlantıda sistemde açılır" } },
75
- message: {
76
- payload: { data: "string — sayfanın window.ReactNativeWebView.postMessage(metin) ile yolladığı metin, olduğu gibi; makine data prop'una yazar (self.data)" },
77
- reads: "iki yönlü köprü: sayfa window.ngApps.emit(name, data) çağırırsa yük olayın params'ında gelir — akış event.name ve event.data okur (self.data yazılmaz)"
78
- },
79
- error: { payload: {}, reads: "cihazda sayfa yüklenemeyince (ağ hatası) yayılır, görünüm yer tutucu çizer; web'de pratikte gelmez (iframe gibi)" }
80
- },
81
- html: {
82
- mount: NO_DATA,
83
- message: {
84
- payload: {},
85
- reads: "belgenin window.ngApps.emit(name, data) çağrısı; yük olayın params'ındadır — akış event.name (ileti adı) ve event.data (veri) okur. actions satırının akışı ise fireEvent(key, data) ile koşar: event.name = key, event.data = veri"
86
- }
87
- },
88
- map: {
89
- mount: NO_DATA,
90
- markerClick: { payload: { markerId: "string — dokunulan işaretçinin `id`si (boşsa listedeki sırası); makine markerId prop'una yazar (self.markerId). Başlığı ya da açıklaması olan işaretçide balon da açılır" } },
91
- regionChange: {
92
- payload: {
93
- latitude: 'number — kullanıcının bıraktığı merkezin enlemi (6 basamak)',
94
- longitude: 'number — merkezin boylamı (6 basamak, −180…180)',
95
- zoom: 'number — web harita ölçeği (2 basamak, 1–19)'
96
- },
97
- reads: "yalnız kullanıcının sürüklemesi ya da yakınlaştırması bitince yayılır; akışın yazdığı görünüm ve kutu boyu değişimi yaymaz. Makine üç prop'u da yazar (self.latitude, self.longitude, self.zoom)"
98
- }
99
- },
100
- markdown: {
101
- mount: NO_DATA,
102
- linkClick: {
103
- payload: {
104
- href: "string — dokunulan bağlantının ham adresi; şema süzülmeden gelir (dışarıda yalnız http(s), mailto, tel açılır)"
105
- }
106
- }
107
- },
108
- 'app-bar': {
109
- clickBack: NO_DATA,
110
- clickMenu: NO_DATA,
111
- clickAction: { payload: { key: 'string (eylemin key alanı; yoksa dizideki sırası)' } }
112
- },
113
- 'safe-area': {
114
- mount: NO_DATA
115
- },
116
- // Web `container` yalnız `mount` yayar; `click` mobile-only (plans/mobil-plantcare-acigi f-04, K1).
117
- container: {
118
- click: NO_DATA
119
- },
120
- // Arama varyantının "Vazgeç"i (plans/mobil-plantcare-acigi f-05): değer temizlenir, klavye kapanır.
121
- textinput: {
122
- cancel: NO_DATA
123
- },
124
- 'scroll-view': {
125
- mount: NO_DATA,
126
- scrollEnd: NO_DATA,
127
- refresh: NO_DATA,
128
- scroll: {
129
- payload: {
130
- y: 'number — dikey kaydırma konumu (yatay kaydırıcıda x)',
131
- contentHeight: 'number — içeriğin boyu (yatayda contentWidth)',
132
- viewportHeight: 'number — görünen alanın boyu (yatayda viewportWidth)'
133
- },
134
- reads: '100 ms seyreltmeli; yalnız bu olaya akış bağlıyken yayılır. Düğüme scrollY (scrollX) de yazılır'
135
- }
136
- },
137
- 'grouped-list': {
138
- mount: NO_DATA,
139
- change: {
140
- payload: {
141
- selectedIndex: 'number — dokunulan öğenin rows içindeki DÜZ indeksi (bölüm içi değil); aynı öğeye yeniden dokununca yayılmaz. Makine selectedIndex\'i yazar, akışta tıklanan satır <liste>.selectedItem'
142
- }
143
- },
144
- itemPress: {
145
- payload: {
146
- index: 'number (öğenin rows içindeki düz indeksi, 0 tabanlı)',
147
- row: 'object (öğenin satır verisi)'
148
- },
149
- reads: 'yalnız itemPressable açıkken; önce change { selectedIndex } yayılır. Öğe şablonu basılabilir bir bileşense (list-item, button) dokunuş onun click\'ine gider, itemPress yayılmaz'
150
- },
151
- swipe: {
152
- payload: {
153
- key: 'string (kaydırma eyleminin key alanı)',
154
- index: 'number (öğenin rows içindeki düz indeksi, 0 tabanlı)',
155
- row: 'object (öğenin satır verisi)'
156
- }
157
- },
158
- sectionPress: {
159
- payload: {
160
- key: 'string — bölüm anahtarı (tarih değerinde yerel gün YYYY-AA-GG)',
161
- title: 'string — bölüm başlığı (anahtar; boş alanlı satırların bölümünde otherSectionTitle)',
162
- count: 'number — bölümdeki öğe sayısı',
163
- items: 'object[] — bölümdeki satırların verisi'
164
- },
165
- reads: 'bölüm başlığına dokununca; yük başlık şablonunun Section ifadesiyle aynı nesnedir'
166
- },
167
- selectionChange: {
168
- payload: {
169
- selectedIndices: 'number[] — seçili satırların rows içindeki DÜZ indeksleri, sıralı',
170
- count: 'number — seçili satır sayısı'
171
- },
172
- reads: 'yalnız selectionMode: multiple (ya da longPressSelects ile açılınca); akış event.data.selectedIndices / event.data.count okur, düğümün selectedIndices/selectedItems değeri de yazılır'
173
- }
174
- },
175
- eventcalendar: {
176
- mount: NO_DATA,
177
- dateClick: {
178
- payload: { date: "string 'yyyy-MM-dd'", rows: 'object[] — o güne düşen kaynak satırlar (mobil)' },
179
- reads: "self'e yazılmaz — event.data ile oku; ay ve hafta görünümünde güne dokununca"
180
- },
181
- eventClick: {
182
- payload: { id: 'string', title: 'string', row: 'kaynak satırın TAMAMI' },
183
- reads: "self'e yazılmaz — event.data ile oku; gün çizelgesinde bloğa dokununca"
184
- },
185
- monthChange: {
186
- payload: {
187
- month: 'number (1-TABANLI)',
188
- year: 'number',
189
- view: "'month' | 'week' | 'day'",
190
- start: "string 'yyyy-MM-dd' (görünen ilk gün)",
191
- end: "string 'yyyy-MM-dd' (görünen son gün)"
192
- },
193
- reads: "self'e yazılmaz — event.data ile oku; oklar, yatay kaydırma ve Bugün ile her görünümde"
194
- },
195
- viewChange: {
196
- payload: { view: "'month' | 'week' | 'day'", start: "string 'yyyy-MM-dd'", end: "string 'yyyy-MM-dd'" },
197
- reads: "self'e yazılmaz — event.data ile oku; görünüm düğmesiyle"
198
- }
199
- },
200
- 'floating-container': {
201
- mount: NO_DATA
202
- },
203
- repeater: {
204
- swipe: {
205
- payload: {
206
- key: 'string (kaydırma eyleminin key alanı)',
207
- index: 'number (satırın sırası, 0 tabanlı)',
208
- row: 'object (satırın verisi)'
209
- }
210
- },
211
- itemPress: {
212
- payload: {
213
- index: 'number (satırın sırası, 0 tabanlı)',
214
- row: 'object (satırın verisi)'
215
- }
216
- },
217
- selectionChange: {
218
- payload: {
219
- selectedIndices: 'number[] — seçili satırların rows içindeki DÜZ indeksleri, sıralı',
220
- count: 'number — seçili satır sayısı'
221
- },
222
- reads: 'yalnız selectionMode: multiple (ya da longPressSelects ile açılınca); akış event.data.selectedIndices / event.data.count okur, düğümün selectedIndices/selectedItems değeri de yazılır'
223
- }
224
- }
225
- };
@@ -1,312 +0,0 @@
1
- import { IMAGE_SOURCE_PROPS, IMAGE_SOURCE_PROP_NAMES, MOBILE_RULES, REDIRECT_PRESENTATIONS, fileIdImageExpression, missingComponentAdvice, styleReplacement } from './mobileRules.js';
2
- import { findingLine } from './profile.js';
3
- import { SURFACE_KEYS } from '../styleResolveNative.js';
4
- import { DOCUMENT_STYLE_TYPES } from '../styleResolve.js';
5
- function asRecord(value) {
6
- return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
7
- }
8
- function walk(value, visit) {
9
- if (!Array.isArray(value)) {
10
- const single = asRecord(value);
11
- if (single)
12
- walk([single], visit);
13
- return;
14
- }
15
- for (const entry of value) {
16
- const node = asRecord(entry);
17
- if (!node)
18
- continue;
19
- visit(node);
20
- walk(node.children, visit);
21
- }
22
- }
23
- /**
24
- * Koda ya da çeviriye bağlı prop: değeri çalışma anında `ref`'ten gelir, `data` yalnız tasarım yedeğidir.
25
- * Belge CSS'i alan tipte (`DOCUMENT_STYLE_TYPES`) bağlı `style` metin sayılır — şema onu uzun metin ilan eder.
26
- */
27
- function boundWrapper(value) {
28
- const wrapped = asRecord(value);
29
- return wrapped !== undefined && (wrapped.type === 'code' || wrapped.type === 'translation');
30
- }
31
- function propValue(value) {
32
- const wrapped = asRecord(value);
33
- return wrapped && 'data' in wrapped && typeof wrapped.type === 'string' ? wrapped.data : value;
34
- }
35
- /** Prop'un bağlı ifadesi: code sarmalının `ref`i (yoksa `data`sı) ya da çıplak şablon metni. */
36
- function boundExpression(value) {
37
- if (typeof value === 'string')
38
- return value;
39
- const wrapped = asRecord(value);
40
- if (!wrapped || wrapped.type !== 'code')
41
- return undefined;
42
- const expression = wrapped.ref ?? wrapped.data;
43
- return typeof expression === 'string' ? expression : undefined;
44
- }
45
- function label(node) {
46
- return String(node.elementId ?? node.name ?? node.type ?? '?');
47
- }
48
- const GRID_LAYOUTS = new Set(['grid', 'cssgrid']);
49
- const RELATIVE_UNIT = /\b\d*\.?\d+(em|rem)\b|calc\(/i;
50
- /**
51
- * Profilin taşınabilirlik bulguları. Web profilinde HER ZAMAN boş dizi döner — web davranışı
52
- * değişmez (G1 kilidi); bulgular yalnız mobil profilde üretilir.
53
- */
54
- export function profileFindings(profile, input) {
55
- if (profile.platform === 'web')
56
- return [];
57
- const findings = [];
58
- const nodes = [];
59
- const blockNodes = [];
60
- const gridNodes = [];
61
- const styleHits = new Map();
62
- const unknownTypes = new Map();
63
- const shellNodes = new Map();
64
- const legacyNodes = [];
65
- const fileIdSources = [];
66
- const nodeTypes = new Map();
67
- const floorImages = [];
68
- const namelessIconButtons = [];
69
- const hit = (map, key, target) => map.set(key, [...(map.get(key) ?? []), target]);
70
- const inspectAppearance = (target, appearance) => {
71
- if (!appearance)
72
- return;
73
- for (const key of profile.appearance.ignoredKeys) {
74
- const value = appearance[key];
75
- if (value !== undefined && value !== null && value !== '' && value !== 0)
76
- hit(styleHits, key, target);
77
- }
78
- for (const key of ['widthString', 'heightString']) {
79
- const value = appearance[key];
80
- if (typeof value === 'string' && RELATIVE_UNIT.test(value))
81
- hit(styleHits, `${key} (${value})`, target);
82
- }
83
- };
84
- // Mobil derleyici yüzey grubunun yalnız bilinen alanlarını okur; kalan alt anahtar cihazda düşer.
85
- // `data`sız sarmal (`{ type: 'code', ref }`) bütün grubu bir ifadeye bağlar: denetlenecek alt anahtar yoktur.
86
- const inspectSurface = (target, value) => {
87
- const raw = asRecord(value);
88
- if (raw && typeof raw.type === 'string' && !('data' in raw))
89
- return;
90
- for (const [key, wrapped] of Object.entries(asRecord(propValue(value)) ?? {})) {
91
- const value = propValue(wrapped);
92
- if (!SURFACE_KEYS.has(key) && value !== undefined && value !== null && value !== '')
93
- hit(styleHits, `surfaceStyle.${key}`, target);
94
- }
95
- };
96
- const inspectNode = (node) => {
97
- const target = label(node);
98
- const type = String(node.type ?? '');
99
- const props = asRecord(node.props) ?? {};
100
- nodes.push(node);
101
- nodeTypes.set(target, type);
102
- for (const prop of IMAGE_SOURCE_PROPS[type] ?? []) {
103
- const expression = fileIdImageExpression(boundExpression(props[prop]) ?? '');
104
- if (expression)
105
- fileIdSources.push({ target: `${target}.${prop}`, expression });
106
- }
107
- if (Array.isArray(node.blocks) && node.blocks.length > 0 && !profile.layout.blocksMeaningful)
108
- blockNodes.push(target);
109
- const layout = propValue(props.layout);
110
- if (typeof layout === 'string' && GRID_LAYOUTS.has(layout))
111
- gridNodes.push(`${target} (layout:${layout})`);
112
- inspectAppearance(target, asRecord(node.appearance));
113
- inspectSurface(target, props.surfaceStyle);
114
- if (type === 'image' && !imageHasDefiniteHeight(asRecord(node.appearance)))
115
- floorImages.push(target);
116
- if (type === 'button' && propValue(props.size) === 'icon' && !String(propValue(props.ariaLabel) ?? '').trim() && !String(propValue(props.text) ?? '').trim()) {
117
- namelessIconButtons.push(target);
118
- }
119
- if (!profile.style.freeStyle) {
120
- const style = propValue(props.style);
121
- const documentCss = DOCUMENT_STYLE_TYPES.has(type) && (typeof style === 'string' || boundWrapper(props.style));
122
- if (!documentCss && ((asRecord(style) && Object.keys(style).length > 0) || (typeof style === 'string' && style.trim() !== '')))
123
- hit(styleHits, 'style', target);
124
- }
125
- if (!profile.style.hoverStyle) {
126
- const hover = propValue(props.hoverStyle);
127
- if (asRecord(hover) && Object.keys(hover).length > 0)
128
- hit(styleHits, 'hoverStyle', target);
129
- }
130
- if (type && input.catalog && input.catalog.size > 0 && !input.catalog.has(type) && !profile.shell.legacyShellNodes.has(type))
131
- hit(unknownTypes, type, target);
132
- if (type && profile.shell.suggestions[type])
133
- hit(shellNodes, type, target);
134
- if (type && profile.shell.legacyShellNodes.has(type))
135
- legacyNodes.push(`${target} (${type})`);
136
- };
137
- walk(input.nodes, inspectNode);
138
- const savedTypes = new Map();
139
- walk(input.savedNodes, node => savedTypes.set(label(node), String(node.type ?? '')));
140
- for (const op of input.ops ?? []) {
141
- const target = typeof op.target === 'string' ? op.target : '?';
142
- if (op.op === 'add_component')
143
- walk(op.component, inspectNode);
144
- if (op.op === 'set_block' && !profile.layout.blocksMeaningful)
145
- blockNodes.push(`${target} (set_block)`);
146
- if (op.op === 'set_appearance')
147
- inspectAppearance(target, asRecord(op.appearance));
148
- if (op.op === 'replace_prop' || op.op === 'set_binding') {
149
- const prop = String(op.prop ?? '');
150
- const value = propValue(op.value);
151
- const targetType = nodeTypes.get(target) ?? savedTypes.get(target) ?? '';
152
- const documentCss = DOCUMENT_STYLE_TYPES.has(targetType) && (typeof value === 'string' || op.op === 'set_binding' || boundWrapper(op.value));
153
- if (prop === 'layout' && typeof value === 'string' && GRID_LAYOUTS.has(value))
154
- gridNodes.push(`${target} (layout:${value})`);
155
- if (prop === 'style' && !profile.style.freeStyle && !documentCss)
156
- hit(styleHits, 'style', target);
157
- if (prop === 'hoverStyle' && !profile.style.hoverStyle)
158
- hit(styleHits, 'hoverStyle', target);
159
- if (prop === 'surfaceStyle')
160
- inspectSurface(target, op.value);
161
- // MOB-011: var olan düğümün tipi op'ta yok — tip biliniyorsa (aynı yazımda eklendi) ona, yoksa prop adına bakılır.
162
- const knownType = nodeTypes.get(target);
163
- const imageProp = knownType === undefined ? IMAGE_SOURCE_PROP_NAMES.has(prop) : (IMAGE_SOURCE_PROPS[knownType] ?? []).includes(prop);
164
- const expression = imageProp ? fileIdImageExpression(boundExpression(typeof op.expression === 'string' ? op.expression : op.value) ?? '') : undefined;
165
- if (expression)
166
- fileIdSources.push({ target: `${target}.${prop}`, expression });
167
- }
168
- }
169
- if (blockNodes.length > 0 || gridNodes.length > 0) {
170
- findings.push({
171
- code: MOBILE_RULES['MOB-001'].id,
172
- severity: 'warn',
173
- message: [
174
- blockNodes.length > 0 ? `blok (12 sütun ızgara) mobil çizerde yok sayılır: ${[...new Set(blockNodes)].join(', ')}` : '',
175
- gridNodes.length > 0 ? `grid/cssgrid yerleşimi yok, kap flex çizilir: ${gridNodes.join(', ')}` : ''
176
- ].filter(Boolean).join(' · ') + ". Sıra dizi indeksinden, yerleşim flex'ten gelir (direction/gap/horizontalAlign). Yazım platforma gitti.",
177
- guide: "platform_guide('mobile')"
178
- });
179
- }
180
- for (const [key, targets] of styleHits) {
181
- findings.push({
182
- code: MOBILE_RULES['MOB-002'].id,
183
- severity: 'warn',
184
- target: [...new Set(targets)].join(', '),
185
- message: `'${key}' mobilde çizilmez — ${styleReplacement(key.split(' ')[0] ?? key)}. Yazım platforma gitti.`
186
- });
187
- }
188
- for (const [type, targets] of unknownTypes) {
189
- findings.push({
190
- code: MOBILE_RULES['MOB-003'].id,
191
- severity: 'warn',
192
- target: [...new Set(targets)].join(', '),
193
- message: missingComponentAdvice(type)
194
- });
195
- }
196
- for (const [type, targets] of shellNodes) {
197
- findings.push({
198
- code: MOBILE_RULES['MOB-004'].id,
199
- severity: 'warn',
200
- target: [...new Set(targets)].join(', '),
201
- message: `'${type}' web kabuğudur, mobil uygulamada karşılığı: ${profile.shell.suggestions[type]}`,
202
- guide: "examples/mobile-app-shell-{tab,stack,drawer}.json"
203
- });
204
- }
205
- if (floorImages.length > 0) {
206
- findings.push({
207
- code: MOBILE_RULES['MOB-012'].id,
208
- severity: 'info',
209
- target: floorImages.join(', '),
210
- message: "görselin boyu belirsiz, 96 dp taban boyla çizilir — boy verin (heightType: 'fixed' + heightString ya da 'fill': görsel kabuğu doldurur) ya da oran verin (appearance.aspectRatio + heightType: 'auto' + widthType 'fill'/'fixed')",
211
- guide: "platform_guide('mobile')"
212
- });
213
- }
214
- if (namelessIconButtons.length > 0) {
215
- findings.push({
216
- code: MOBILE_RULES['MOB-013'].id,
217
- severity: 'info',
218
- target: namelessIconButtons.join(', '),
219
- message: "ikon boyundaki düğmenin adı yok — size: 'icon' metni çizmez ve ekran okuyucu ikonun ligatür adını (ör. 'notifications') okur; ariaLabel verin (ör. 'Bildirimler')",
220
- guide: "platform_guide('mobile')"
221
- });
222
- }
223
- if (legacyNodes.length > 0) {
224
- findings.push({
225
- code: MOBILE_RULES['MOB-007'].id,
226
- severity: 'warn',
227
- target: legacyNodes.join(', '),
228
- message: "sekme çubuğu ve çekmece menüsü katalog bileşeni değildir, çizer bu düğümleri yok sayar — sekmeler Ayarlar › Sekmeler'den (application_update properties.mobile.tabs), çekmece 'drawer' widget'ı + app-bar.menuTarget"
229
- });
230
- }
231
- if (fileIdSources.length > 0) {
232
- const sample = fileIdSources[0]?.expression ?? '';
233
- const object = sample.replace(/\??\.value(\s*\}\})$/, '$1');
234
- findings.push({
235
- code: MOBILE_RULES['MOB-011'].id,
236
- severity: 'warn',
237
- target: fileIdSources.map(source => source.target).join(', '),
238
- message: `${[...new Set(fileIdSources.map(source => `'${source.expression}'`))].join(', ')} '.value' ile bitiyor — kayıttan okunan FILE alanı { type: 'ref', value: <dosya id>, absolute_path: <url> } döner ve '.value' dosya KİMLİĞİDİR (GUID), görsel çizilmez. Nesnenin kendisini verin ('${object}') ya da '.absolute_path' yazın. Gateway alan tipini sayfa şemasından bilemez: yol FILE alanı değilse (ör. URL taşıyan metin alanı) uyarıyı yok sayın. Yazım platforma gitti.`,
239
- guide: "platform_guide('mobile') §Dosya ve fotoğraf"
240
- });
241
- }
242
- const infoSteps = new Map();
243
- const badPresentation = new Map();
244
- walkSteps(input.steps, step => {
245
- const type = String(step.type ?? '');
246
- if (profile.steps.infoOnly.has(type))
247
- hit(infoSteps, type, String(step.stepId ?? step.name ?? '?'));
248
- // MOB-008: sunum biçimi yalnız `redirect`te anlamlı ve kümesi kapalı.
249
- // Alan YOKSA bulgu yok — varsayılan `push` ve yazımların çoğu onu yazmaz.
250
- if (type !== 'redirect')
251
- return;
252
- const args = asRecord(step.args);
253
- const presentation = args?.presentation;
254
- if (presentation === undefined || presentation === null || presentation === '')
255
- return;
256
- if (!REDIRECT_PRESENTATIONS.has(String(presentation))) {
257
- hit(badPresentation, String(presentation), String(step.stepId ?? step.name ?? '?'));
258
- }
259
- });
260
- for (const [value, stepIds] of badPresentation) {
261
- findings.push({
262
- code: MOBILE_RULES['MOB-008'].id,
263
- severity: 'warn',
264
- target: stepIds.join(', '),
265
- message: `redirect.presentation '${value}' tanınmıyor — ${[...REDIRECT_PRESENTATIONS].join(' | ')} bekleniyor. Backend doğrulayıcısı 422 döner; geçse bile çizer 'push'a düşer.`,
266
- guide: "platform_guide('mobile')"
267
- });
268
- }
269
- for (const [type, stepIds] of infoSteps) {
270
- findings.push({
271
- code: MOBILE_RULES['MOB-005'].id,
272
- severity: 'info',
273
- target: stepIds.join(', '),
274
- message: type === 'download'
275
- ? "mobil uygulamada 'download' dosyayı cihazın paylaşım/açma sayfasında açar (Linking.openURL) — tarayıcı indirmesi yok"
276
- : "mobil uygulamada 'setcookie' çerez yazmaz, cihaz deposuna (storage, expo-sqlite/kv-store) eşlenir — okuyan ifadeler aynı çalışır"
277
- });
278
- }
279
- return findings;
280
- }
281
- function walkSteps(value, visit) {
282
- if (!Array.isArray(value))
283
- return;
284
- for (const entry of value) {
285
- const step = asRecord(entry);
286
- if (!step)
287
- continue;
288
- visit(step);
289
- walkSteps(step.steps, visit);
290
- }
291
- }
292
- /** Bulguları `layoutWarnings[]` satırlarına çevirir. */
293
- export function findingLines(findings) {
294
- return findings.map(findingLine);
295
- }
296
- /**
297
- * Görselin boyu belli mi — mobil `imageFrameStyle` sırası: kabuktan (`fixed` + ölçülebilir `heightString`,
298
- * ya da `fill`) ya da orandan (`aspectRatio` + `heightType: 'auto'` + genişlik `fill`/`fixed`+ölçü).
299
- * Değilse görsel 96 dp taban boyla çizilir (MOB-012). Koşu bulgusu: `fixed`le kurulan görseller boşluk
300
- * bıraktı, doğru kalıp yalnız bileşen açıklamasında yazıyordu (plans/mobil-plantcare-acigi g-10 adım 3).
301
- */
302
- function imageHasDefiniteHeight(appearance) {
303
- if (!appearance)
304
- return false;
305
- // Çizicinin `cssLength`'i: sayı, `Npx` ya da `N%` — birimsiz dize ölçülemez.
306
- const measurable = (value) => typeof value === 'number' || (typeof value === 'string' && /^\d+(\.\d+)?(px|%)$/.test(value.trim()));
307
- const heightType = appearance.heightType;
308
- if (heightType === 'fill' || (heightType === 'fixed' && measurable(appearance.heightString)))
309
- return true;
310
- const definiteWidth = appearance.widthType === 'fill' || (appearance.widthType === 'fixed' && measurable(appearance.widthString));
311
- return Number(appearance.aspectRatio) > 0 && heightType === 'auto' && definiteWidth;
312
- }
@@ -1,78 +0,0 @@
1
- import { ENGINE_STEP_TYPES, STEP_ALIASES } from './profile.js';
2
- import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
3
- /**
4
- * Mobil uygulamanın kök kapsayıcısı. Web'in `pageContainerNode()`'undan üç farkı var:
5
- *
6
- * - `layout` prop'u YOK: mobil `container` şeması onu düşürür (`@ng-apps/ui-native`
7
- * `components/container/schema.ts` `omitProps(['layout', …])`) — mobil kanvas yalnız flex akışıdır
8
- * ve backend doğrulayıcısı mobil kataloğa bakar, yazılan `layout` bilinmeyen prop olurdu.
9
- * - `direction: 'column'`, `wrap: false`: telefon ekranı dikey akar; web kökü satır + sarma ile
10
- * 12 sütunu taklit eder.
11
- * - `blocks: []` ve `heightType: 'fill'`: çizer blokları yok sayar (D3), kök ekranı doldurur.
12
- */
13
- export function mobileRootContainer() {
14
- return {
15
- elementId: PAGE_CONTAINER_ELEMENT_ID,
16
- kind: 'component',
17
- name: PAGE_CONTAINER_ELEMENT_ID,
18
- type: 'container',
19
- props: {
20
- title: { type: 'static', data: '' },
21
- direction: { type: 'static', data: 'column' },
22
- horizontalAlign: { type: 'static', data: 'left' },
23
- verticalAlign: { type: 'static', data: 'top' },
24
- size: { type: 'static', data: 'grow' },
25
- gap: { type: 'static', data: 'medium' },
26
- wrap: { type: 'static', data: false }
27
- },
28
- blocks: [],
29
- states: { isSticky: false, isDroppable: true, isScrollable: true, isWrapper: true },
30
- appearance: { heightType: 'fill', marginValue: 0, marginType: 'none', paddingType: 'none', paddingValue: 0 },
31
- events: [],
32
- validations: [],
33
- stateBindings: [],
34
- children: []
35
- };
36
- }
37
- /**
38
- * Mobil profil = taşınabilir alt küme (`plans/mobil-uygulama-expo/01-mimari-tasarim.md` §5, §7).
39
- * Yazım platforma YİNE gider (D3: şema aynı); alt kümenin dışı `MOB-*` bulgusu olarak uyarılır.
40
- */
41
- export const mobileProfile = Object.freeze({
42
- platform: 'mobile',
43
- catalogType: 'mobile',
44
- layout: {
45
- modes: new Set(['flex']),
46
- blocksMeaningful: false,
47
- rootContainer: mobileRootContainer,
48
- orderBy: 'array-index'
49
- },
50
- appearance: {
51
- ignoredKeys: new Set(['className', 'tooltip', 'tooltipPosition', 'zIndex']),
52
- sizeUnits: new Set(['px', '%'])
53
- },
54
- style: { freeStyle: false, hoverStyle: false, resolver: 'native' },
55
- steps: {
56
- registered: new Set([...ENGINE_STEP_TYPES, 'alert', 'confirm', 'redirect', 'navigate', 'storage', 'setcookie', 'download', 'share']),
57
- aliases: STEP_ALIASES,
58
- infoOnly: new Set(['download', 'setcookie'])
59
- },
60
- shell: {
61
- components: new Set(['app-bar']),
62
- suggestions: {
63
- navigations: "sekmeler: application_update ile properties.mobile.tabs (Ayarlar › Sekmeler); üst çubuk için 'app-bar'",
64
- sidebar: "çekmece: 'drawer' widget'ı + app-bar.leading:'menu' ve menuTarget",
65
- header: "'app-bar' (başlık, leading: auto | none | back | menu, sağ eylemler)"
66
- },
67
- legacyShellNodes: new Set(['tab-bar', 'drawer-menu'])
68
- },
69
- externalComponents: false,
70
- capture: {
71
- target: 'studio-mobile',
72
- devices: {
73
- phone: { width: 390, height: 844, dpr: 2 },
74
- tablet: { width: 820, height: 1180, dpr: 2 }
75
- },
76
- defaultDevice: 'phone'
77
- }
78
- });