@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
package/dist/tools/pages.js
CHANGED
|
@@ -10,7 +10,8 @@ import { emptyWriteDiff, schemaOpDrops, unwrapProps, withWriteDiff, writeDiff }
|
|
|
10
10
|
import { PlatformApiError } from '../platform/errors.js';
|
|
11
11
|
import { existingMatch, ifExistsParam, listAll, withOutcome } from '../platform/ifExists.js';
|
|
12
12
|
import { handle, toolResult, toolError } from '../platform/responses.js';
|
|
13
|
-
import { StudioScreenshotter } from '../platform/
|
|
13
|
+
import { StudioScreenshotter } from '../platform/capture/index.js';
|
|
14
|
+
import { captureTargetFor } from '../platform/capture/target.js';
|
|
14
15
|
import { diffSummaries, summaryLines, summaryStats } from '../platform/domSummary.js';
|
|
15
16
|
import { captureKey, imageHash, sharedCaptureMemory } from '../platform/screenshotMemory.js';
|
|
16
17
|
import { summarizeLayout } from '../knowledge/layoutSummary.js';
|
|
@@ -22,6 +23,9 @@ import { dirname, resolve } from 'node:path';
|
|
|
22
23
|
import { setPropTypeErrors } from '../knowledge/propTypes.js';
|
|
23
24
|
import { designSnapshotInput } from '../knowledge/designSnapshot.js';
|
|
24
25
|
import { discoverPage } from '../knowledge/pageDiscover.js';
|
|
26
|
+
import { applicationTypeField, resolveProfile, resolveProfileBySlug } from '../knowledge/platform/resolve.js';
|
|
27
|
+
import { resolveThemeNative } from '../knowledge/styleResolveNative.js';
|
|
28
|
+
import { boundFlowSteps, screenProfileWarnings } from './profileWrite.js';
|
|
25
29
|
import { z } from 'zod';
|
|
26
30
|
/**
|
|
27
31
|
* Runtime paketindeki `appFunctions`/`pageFunctions` (UiFunctionRuntimeDto: id, name, parameters,
|
|
@@ -105,6 +109,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
105
109
|
annotations: readOnly
|
|
106
110
|
}, handle(config, async ({ project, environment, applicationId, pageId }) => {
|
|
107
111
|
const page = await fetchPage(platform, project, environment, applicationId, pageId);
|
|
112
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
108
113
|
const layout = (page.layout ?? {});
|
|
109
114
|
return {
|
|
110
115
|
id: page.id,
|
|
@@ -123,7 +128,8 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
123
128
|
flows: (Array.isArray(page.flows) ? page.flows : [])
|
|
124
129
|
.map(flow => ({ id: flow.id, name: flow.name, slug: flow.slug })),
|
|
125
130
|
schemaNodes: countSchemaNodes(page.schema),
|
|
126
|
-
updatedAt: page.audit?.updatedAt ?? page.audit?.createdAt
|
|
131
|
+
updatedAt: page.audit?.updatedAt ?? page.audit?.createdAt,
|
|
132
|
+
...applicationTypeField(resolved)
|
|
127
133
|
};
|
|
128
134
|
}));
|
|
129
135
|
server.registerTool('page_runtime_get', {
|
|
@@ -171,11 +177,17 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
171
177
|
const styleSource = resolveStyles ? (theme ?? null) : undefined;
|
|
172
178
|
const asNodes = (value) => (Array.isArray(value) ? value : []);
|
|
173
179
|
const named = (value) => (Array.isArray(value) ? value : []).map(entry => ({ id: entry.id, name: entry.name, slug: entry.slug }));
|
|
180
|
+
const runtimeApplication = (body.application ?? {});
|
|
181
|
+
// Mobil uygulama React Native stiliyle çizilir (G4): rapor CSS kaskadı değil `shell`/`text`.
|
|
182
|
+
const nativeTheme = resolveStyles && runtimeApplication.type === 'mobile' ? resolveThemeNative((theme ?? undefined)) : undefined;
|
|
183
|
+
const nodeStyleSource = nativeTheme ? undefined : styleSource;
|
|
174
184
|
return {
|
|
175
185
|
page: { id: body.id, name: body.name, slug: body.slug, browserTitle: body.browserTitle, browserTitleKey: body.browserTitleKey, isHomepage: body.isHomepage },
|
|
186
|
+
...(typeof runtimeApplication.type === 'string' ? { applicationType: runtimeApplication.type } : {}),
|
|
176
187
|
events: body.events ?? [],
|
|
177
|
-
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(node => summarize(node,
|
|
178
|
-
schema: asNodes(body.schema).map(node => summarize(node,
|
|
188
|
+
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(node => summarize(node, nodeStyleSource, nativeTheme)) } : null,
|
|
189
|
+
schema: asNodes(body.schema).map(node => summarize(node, nodeStyleSource, nativeTheme)),
|
|
190
|
+
...(nativeTheme ? { styleResolver: 'native' } : {}),
|
|
179
191
|
widgets: named(body.widgets),
|
|
180
192
|
flows: named(body.flows),
|
|
181
193
|
variableKeys: Object.keys((body.variables ?? {})),
|
|
@@ -194,9 +206,11 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
194
206
|
}
|
|
195
207
|
}
|
|
196
208
|
: {}),
|
|
197
|
-
note:
|
|
198
|
-
? "
|
|
199
|
-
:
|
|
209
|
+
note: nativeTheme
|
|
210
|
+
? "Mobil uygulama: nativeStyle, mobil çizerin düğüme verdiği React Native stilidir — shell kutunun, text içine akan metnin stili (RN'de tipografi miras alınmaz); warnings mobilde çizilmeyen değerlerdir (MOB-002). Tema componentStyles/preset katmanı mobil çizerde henüz uygulanmıyor, bu yüzden rapora girmez."
|
|
211
|
+
: resolveStyles
|
|
212
|
+
? "schema, layout'taki slot'un İÇİNE yerleşerek çizilir (Kural 1f). resolvedStyle = componentStyles → preset → düğüm stili kaskadı (aibox'ta tipli grupların derlemesi TABAN katmandır); styleLayers hangi katmanların konuştuğunu söyler — styleLayers.componentPreset ise CSS DEĞİL, düğümün hangi bileşen preseti versiyonundan kurulduğunun işaretidir."
|
|
213
|
+
: "schema, layout'taki slot'un İÇİNE yerleşerek çizilir (Kural 1f); tam gövde gerekirse view:'full'. Bir düğümün ekranda GERÇEKTEN aldığı stili görmek için resolveStyles:true."
|
|
200
214
|
};
|
|
201
215
|
}));
|
|
202
216
|
const screenshotter = injectedScreenshotter ?? new StudioScreenshotter(config);
|
|
@@ -213,7 +227,15 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
213
227
|
viewport: z.object({
|
|
214
228
|
width: z.number().int().min(320).max(3840),
|
|
215
229
|
height: z.number().int().min(240).max(2160)
|
|
216
|
-
}).optional().describe('Varsayılan 1280x800 (deviceScaleFactor 1)'),
|
|
230
|
+
}).optional().describe('Varsayılan 1280x800 (deviceScaleFactor 1); mobil uygulamada varsayılan device. Verilirse device yok sayılır'),
|
|
231
|
+
device: z.union([
|
|
232
|
+
z.enum(['phone', 'tablet']),
|
|
233
|
+
z.object({
|
|
234
|
+
width: z.number().int().min(320).max(3840),
|
|
235
|
+
height: z.number().int().min(240).max(2160),
|
|
236
|
+
scale: z.number().min(1).max(3).optional()
|
|
237
|
+
})
|
|
238
|
+
]).optional().describe("Yalnız MOBİL uygulama: cihaz ön ayarı — 'phone' 390x844 @2x (varsayılan), 'tablet' 820x1180 @2x, ya da { width, height, scale? }. Kare cihaz piksel yoğunluğunda çıkar (phone → 780x1688 px); raster küçültme için scale parametresi bununla çarpılır. Web uygulamasında reddedilir: dar ekran için viewport verin."),
|
|
217
239
|
fullPage: z.boolean().optional().describe('true: kaydırılan kısım dahil tüm sayfa; varsayılan yalnız viewport karesi'),
|
|
218
240
|
delayMs: z.number().int().min(0).max(60000).optional().describe("Hazırlık bütçesine EK süre (ms). Varsayılan bütçe 15 sn'dir ve kare sayfa hazır olur olmaz alınır (akışsız giriş ekranı ~1 sn, dashboard mount akışları bitince) — normalde vermeye gerek yok. Yalnız sayfa gerçekten uzun sürüyorsa (soğuk Vite derlemesi, ağır mount akışı) büyütün: bütçe dolduğunda sayfa hâlâ hazır değilse çağrı hatayla döner, kare alınmaz. Sayfanın AÇILMASI için beklenen 20 sn'lik navigasyon süresi de bu kadar uzar — soğuk Studio'da tek düğme budur (09-08 S-8)."),
|
|
219
241
|
captureUnready: z.boolean().optional().describe("true: TEŞHİS kipi — bütçe dolduğunda sayfa hazır olmasa da kare alınır ve yanıt pageState: 'not-ready' taşır (iskelet/yarım kare görülebilir). Varsayılan false: yüklenmemiş sayfa çekilmez, hata döner."),
|
|
@@ -229,7 +251,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
229
251
|
value: z.unknown().optional().describe("'set' için yazılacak değer. Tarih kutularına ISO yazın ('2026-08-01'); çok alanlı tarih aralığında kök DEĞİL kutu hedeflenir: '<elementId>__start' / '<elementId>__end'. 'tab' için sekmenin ANAHTARI ('iot') ya da görünen metni; 'select' için seçeneğin value'su ya da görünen metni"),
|
|
230
252
|
ms: z.number().int().min(0).max(15000).optional().describe("'wait' için bekleme süresi")
|
|
231
253
|
})).max(20).optional().describe("Kare ALINMADAN ÖNCE sayfada koşturulacak etkileşim adımları: [{action:'set', target:'flt_month', value:'2022-10'}, {action:'click', target:'btn_sign_in'}, {action:'tab', target:'tabs_settings', value:'iot'}, {action:'select', target:'dd_city', value:'34'}, {action:'submit', target:'frm_note'}, {action:'drag', target:'kb_board__card-42', to:'kb_board__col-Assigned'}]. 'tab' SEKME SEÇER: sekme grubunun köküne 'click' göndermek DOM sırasındaki İLK sekmeye gider ve ikinci sekme hiç açılamıyordu — tablist köküne click artık reddedilir ve bu fiile yönlendirir. Eşleme önce '<elementId>__tab-<sekme anahtarı>' kimliğiyle, o yoksa görünen metinle yapılır; actionsReport.matchedBy hangisi olduğunu, actionsReport.activeTab adımdan SONRA hangi sekmenin açık olduğunu söyler. Etkin olmayan sekmenin içeriği DOM'da YOKTUR: o sekmedeki bir düğüme yazmadan önce 'tab' adımı gerekir. 'select' açılır kutudan seçer — kutu native alan değildir, değeri yazmak hiçbir şey yapmaz, seçeneğe tıklanması gerekir; liste 3 sn'ye kadar YOKLANIR (sorgudan beslenen kutuda seçenekler geç basılır) ve actionsReport.optionsSeen + waitedMs neyin görüldüğünü söyler. 'submit' formu gönder butonu OLMADAN gönderir (form.requestSubmit); actionsReport.submitVia hangi yolun kullanıldığını yazar. 'drag' HTML5 sürükle-bırak olaylarını (dragstart→dragover→drop→dragend) hedefin merkezinde atar; kanban cardMove ve takvim eventMove bununla tetiklenir. 'set' native setter kullanır ve input+change olaylarını bubbles ile atar (düz DOM ataması framework'ün duyduğu olayı üretmez); ardından kök düğümün runtime prop'larını yeniden okur: actionsReport.propWritten değerin prop'a ULAŞIP ulaşmadığıdır ve false ise applied da false döner — DOM'a yazılmış ama bileşenin duymadığı değer 'uygulandı' sayılmaz (G-14). Her adımdan sonra yerleşme beklenir ve yanıttaki actionsReport hedefin bulunup bulunmadığını ve adımdan sonra KAÇ AĞ İSTEĞİ çıktığını söyler — 'bağ kuruldu' ile 'çalışıyor' arasındaki farkı ancak bu ayırır (G-24)."),
|
|
232
|
-
language: z.string().min(1).optional().describe("Kareyi BU DİLDE çeker: adrese '?lang=<kod>' eklenir ve runtime dili oradan seçer (öncelik: ?lang → tarayıcıda kayıtlı seçim → tarayıcı dilleri → projenin varsayılanı). Seçim hem çeviriye bağlı prop'ları hem çok dilli alanların satır değerlerini kapsar, yani ikinci dilin karesi için sayfaya dil değiştirici KOYMAK GEREKMEZ — referans tasarımı bozulmadan ikinci dil kanıtlanır. ISO 639-1 iki harfli kod ('tr', 'en'); bölgeli kod ('tr-TR') indirgenir ve indirgeme yanıtta bildirilir. Kod projenin languages listesinde yoksa çağrı REDDEDİLİR: runtime tanımadığı kodu yok sayıp kareyi varsayılan dilde çizerdi ve 'çevrildi' sanılan yanlış bir kare raporlanırdı."),
|
|
254
|
+
language: z.string().min(1).optional().describe("Kareyi BU DİLDE çeker: adrese '?lang=<kod>' eklenir ve runtime dili oradan seçer (öncelik: ?lang → tarayıcıda kayıtlı seçim → tarayıcı dilleri → projenin varsayılanı). Seçim hem çeviriye bağlı prop'ları hem çok dilli alanların satır değerlerini kapsar, yani ikinci dilin karesi için sayfaya dil değiştirici KOYMAK GEREKMEZ — referans tasarımı bozulmadan ikinci dil kanıtlanır. ISO 639-1 iki harfli kod ('tr', 'en'); bölgeli kod ('tr-TR') indirgenir ve indirgeme yanıtta bildirilir. Kod projenin languages listesinde yoksa çağrı REDDEDİLİR: runtime tanımadığı kodu yok sayıp kareyi varsayılan dilde çizerdi ve 'çevrildi' sanılan yanlış bir kare raporlanırdı. MOBİL uygulamada da geçerlidir (/preview aynı ?lang'i okur); cihazın kendisinde ?lang yoktur (platform_guide('mobile') §Çok dil)."),
|
|
233
255
|
colorScheme: z.enum(['light', 'dark']).optional().describe("Kareyi açık ya da koyu temada çeker. Ziyaretçi tema tercihi çerezde değil localStorage'da yaşar ve tema store'u onu sayfa açılırken bir kez okur; gateway tercihi sayfa script'lerinden ÖNCE eker, bu yüzden sayfada 'theme-toggle' komponenti OLMASA DA koyu tema karesi alınabilir. Verilmezse projenin kendi modu çizilir."),
|
|
234
256
|
format: z.enum(['png', 'jpeg']).optional().describe("Kare biçimi; varsayılan 'png'. Doğrulama karesi için 'jpeg' + quality 60 bağlamı küçültür (ölçüldü 2026-09-16, aynı sayfa: scale 1'de png 8639 B / jpeg60 8691 B — kazanç yok; scale 0.5'te png 3603 B / jpeg60 3035 B). Asıl kaldıraç ÖLÇEKTİR; renk ve kenar keskinliğinin kendisi inceleniyorsa png'de kalın."),
|
|
235
257
|
quality: z.number().int().min(1).max(100).optional().describe("Yalnız format:'jpeg' ile: 1-100, doğrulama için 60 yeter. format 'png' iken verilirse çağrı reddedilir — PNG kayıpsızdır, kalite ayarı yoktur."),
|
|
@@ -244,7 +266,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
244
266
|
outPath: z.string().min(1).optional().describe('Verilirse PNG bu yerel yola yazılır ve inline görüntü DÖNMEZ; yalnız özet (yol, boyut) döner — büyük fullPage kareleri için')
|
|
245
267
|
},
|
|
246
268
|
annotations: readOnly
|
|
247
|
-
}, async ({ project, environment, applicationSlug, pageSlug, viewport, fullPage, delayMs, captureUnready, anonymous, asUser, cookies, layoutReport, actions, outPath, expectFailure, language, colorScheme, summary, image, format, quality, scale, clip, diff }) => {
|
|
269
|
+
}, async ({ project, environment, applicationSlug, pageSlug, viewport, device, fullPage, delayMs, captureUnready, anonymous, asUser, cookies, layoutReport, actions, outPath, expectFailure, language, colorScheme, summary, image, format, quality, scale, clip, diff }) => {
|
|
248
270
|
try {
|
|
249
271
|
if (anonymous === true && (asUser !== undefined || cookies !== undefined)) {
|
|
250
272
|
throw new PlatformApiError("page_screenshot: anonymous:true ile asUser/cookies birlikte verilemez — biri kimliksiz kareyi, diğeri belirli bir kimlikle çekilen kareyi ister. Anonim kare için yalnız anonymous:true, üye kimliğiyle kare için yalnız asUser (ve gerekiyorsa cookies) gönderin.");
|
|
@@ -288,7 +310,24 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
288
310
|
}
|
|
289
311
|
}
|
|
290
312
|
const url = `${config.studioBaseUrl}/${lane}/${projectSlug}/${applicationSlug}/${pageSlug}${languageChoice ? `?lang=${languageChoice.code}` : ''}`;
|
|
291
|
-
|
|
313
|
+
// Hedef ve cihaz uygulamanın platform profilinden (G3): mobil uygulama react-native-web
|
|
314
|
+
// çizimidir, varsayılan kare telefon ön ayarıdır. Tip okunamazsa web davranışı aynen.
|
|
315
|
+
const resolved = await resolveProfileBySlug(platform, projectSlug, applicationSlug);
|
|
316
|
+
const target = captureTargetFor(resolved.profile.capture.target);
|
|
317
|
+
const mobile = resolved.profile.platform === 'mobile';
|
|
318
|
+
if (device !== undefined && !mobile) {
|
|
319
|
+
throw new PlatformApiError(`page_screenshot: device yalnız MOBİL uygulamada geçerlidir — '${applicationSlug}' ${resolved.known ? 'bir web uygulaması' : 'uygulamasının tipi okunamadı (web varsayıldı)'}. Web'in dar ekran kırılımını görmek için viewport verin (ör. { width: 390, height: 844 }).`);
|
|
320
|
+
}
|
|
321
|
+
const deviceName = typeof device === 'string' ? device : device === undefined && mobile ? resolved.profile.capture.defaultDevice : undefined;
|
|
322
|
+
const preset = typeof device === 'object'
|
|
323
|
+
? { width: device.width, height: device.height, dpr: device.scale ?? 1 }
|
|
324
|
+
: deviceName === undefined ? undefined : resolved.profile.capture.devices[deviceName];
|
|
325
|
+
const appliedDevice = viewport === undefined && preset !== undefined ? preset : undefined;
|
|
326
|
+
const deviceNote = viewport !== undefined && device !== undefined
|
|
327
|
+
? 'viewport verildiği için device yok sayıldı: kare verilen viewport ve deviceScaleFactor 1 ile çekildi.'
|
|
328
|
+
: undefined;
|
|
329
|
+
const size = viewport ?? (appliedDevice ? { width: appliedDevice.width, height: appliedDevice.height } : { width: 1280, height: 800 });
|
|
330
|
+
const dpr = appliedDevice && appliedDevice.dpr !== 1 ? appliedDevice.dpr : undefined;
|
|
292
331
|
const extraCookies = Object.entries(cookies ?? {}).map(([name, value]) => ({ name, value }));
|
|
293
332
|
const identity = anonymous === true ? 'anonymous' : asUser === undefined ? 'gateway' : 'asUser';
|
|
294
333
|
const identityToken = anonymous ? undefined : resolveIdentity(asUser) ?? await platform.accessToken();
|
|
@@ -313,7 +352,9 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
313
352
|
...(quality === undefined ? {} : { quality }),
|
|
314
353
|
...(scale === undefined ? {} : { scale }),
|
|
315
354
|
...(clip === undefined ? {} : { clip: clip }),
|
|
316
|
-
...(actions === undefined ? {} : { actions: actions })
|
|
355
|
+
...(actions === undefined ? {} : { actions: actions }),
|
|
356
|
+
...(mobile ? { target } : {}),
|
|
357
|
+
...(dpr === undefined ? {} : { dpr })
|
|
317
358
|
});
|
|
318
359
|
// Fark ÖZETTEN çıkar; `summary:false` verilse de fark için toplanmıştır (yanıta girmez).
|
|
319
360
|
// Anahtar KİMLİĞİ ayırır (K8): 'asUser' etiketi yetmez — iki farklı üyenin jetonu aynı
|
|
@@ -384,6 +425,11 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
384
425
|
...(languageChoice?.note ? { languageNote: languageChoice.note } : {}),
|
|
385
426
|
...(colorScheme === undefined ? {} : { colorScheme }),
|
|
386
427
|
viewport: size,
|
|
428
|
+
...(appliedDevice === undefined ? {} : { device: typeof device === 'object' ? { ...device } : deviceName, deviceScaleFactor: appliedDevice.dpr }),
|
|
429
|
+
...(deviceNote === undefined ? {} : { deviceNote }),
|
|
430
|
+
// Web karesinin yanıtı profil öncesiyle aynı kalır (G3 kısıtı); alan yalnız mobilde.
|
|
431
|
+
...(mobile ? applicationTypeField(resolved) : {}),
|
|
432
|
+
...(target.platformNote === undefined ? {} : { platformNote: target.platformNote }),
|
|
387
433
|
fullPage: fullPage === true,
|
|
388
434
|
identity,
|
|
389
435
|
cookieNames,
|
|
@@ -535,19 +581,20 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
535
581
|
annotations: readOnly
|
|
536
582
|
}, handle(config, async ({ project, environment, applicationId, pageId, mode, componentId }) => {
|
|
537
583
|
const page = await fetchPage(platform, project, environment, applicationId, pageId);
|
|
584
|
+
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
538
585
|
const updatedAt = page.audit?.updatedAt ?? page.audit?.createdAt;
|
|
539
586
|
const version = page.version;
|
|
540
587
|
if (mode === 'full')
|
|
541
|
-
return { version, updatedAt, schema: page.schema ?? [] };
|
|
588
|
+
return { version, updatedAt, ...typeField, schema: page.schema ?? [] };
|
|
542
589
|
if (mode === 'subtree') {
|
|
543
590
|
if (!componentId)
|
|
544
591
|
throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
|
|
545
592
|
const subtree = findNode(page.schema, componentId);
|
|
546
593
|
if (!subtree)
|
|
547
594
|
throw new PlatformApiError(`Sayfada '${componentId}' id'li bir komponent yok; page_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
|
|
548
|
-
return { version, updatedAt, schema: subtree };
|
|
595
|
+
return { version, updatedAt, ...typeField, schema: subtree };
|
|
549
596
|
}
|
|
550
|
-
return { version, updatedAt, schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
597
|
+
return { version, updatedAt, ...typeField, schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
551
598
|
}));
|
|
552
599
|
server.registerTool('page_state', {
|
|
553
600
|
title: 'Sayfa durumunu getir',
|
|
@@ -573,15 +620,20 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
573
620
|
...schemaOpValidationErrors(opRecords).map(issue => `PAGE-016 — ${issue}`),
|
|
574
621
|
...codePropTemplateErrors(opRecords).map(issue => `PAGE-032 — ${issue}`)
|
|
575
622
|
];
|
|
576
|
-
const
|
|
623
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
624
|
+
const ordering = resolved.profile.layout.orderBy === 'block-position'
|
|
625
|
+
? await orderByIndex(opRecords, () => fetchPage(platform, project, environment, applicationId, pageId))
|
|
626
|
+
: { ops: opRecords, moved: [], warnings: [] };
|
|
627
|
+
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'page', ops: opRecords });
|
|
577
628
|
const response = ((await platform.tenant('POST', project, `/pages/${applicationId}/${pageId}/schema/validate`, {
|
|
578
629
|
environment,
|
|
579
630
|
body: { ops: [...allOps.filter(entry => entry.op === 'resize_component').flatMap(resizeServerOps), ...ordering.ops] }
|
|
580
631
|
})) ?? {});
|
|
581
632
|
if (validationIssues.length > 0)
|
|
582
633
|
response.validationWarnings = validationIssues;
|
|
583
|
-
if (ordering.warnings.length > 0)
|
|
584
|
-
response.layoutWarnings = ordering.warnings;
|
|
634
|
+
if (ordering.warnings.length > 0 || profileWarnings.length > 0)
|
|
635
|
+
response.layoutWarnings = [...ordering.warnings, ...profileWarnings];
|
|
636
|
+
Object.assign(response, applicationTypeField(resolved));
|
|
585
637
|
return withNormalization(response, [
|
|
586
638
|
...componentDefaultsNote(normalized.components),
|
|
587
639
|
...validationNote(normalized.validations),
|
|
@@ -669,7 +721,12 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
669
721
|
retargeted = retargetRootAdds(opRecords, PAGE_CONTAINER_ELEMENT_ID);
|
|
670
722
|
}
|
|
671
723
|
}
|
|
672
|
-
const
|
|
724
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
725
|
+
const blocksMeaningful = resolved.profile.layout.blocksMeaningful;
|
|
726
|
+
// Mobil (dizi sırası): index platformun kendi add/move index'idir, konum çevirisi yapılmaz.
|
|
727
|
+
const ordering = resolved.profile.layout.orderBy === 'block-position'
|
|
728
|
+
? await orderByIndex(opRecords, readPage)
|
|
729
|
+
: { ops: opRecords, moved: [], warnings: [] };
|
|
673
730
|
const sentOps = ordering.ops;
|
|
674
731
|
// Akış silen op'lar yamadan ÖNCE hesaplanır: uyarı sonradan geldiğinde akış zaten kalıcı
|
|
675
732
|
// silinmiş oluyordu ve "önce yedekle" tavsiyesi uygulanamıyordu (09-05 koşusu, G-21).
|
|
@@ -729,10 +786,10 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
729
786
|
// görünür, iç içeliği ancak hedef parent zinciri belli olunca anlarsın.
|
|
730
787
|
nestedAfterOps = nestedRepeaterNodes(simulated);
|
|
731
788
|
layoutWarnings = [
|
|
732
|
-
...pageContainerPositionWarning(page.schema),
|
|
733
|
-
...verticalGapWarnings(simulated, 'PAGE-010 (uyarı)', touched),
|
|
789
|
+
...(blocksMeaningful ? pageContainerPositionWarning(page.schema) : []),
|
|
790
|
+
...(blocksMeaningful ? verticalGapWarnings(simulated, 'PAGE-010 (uyarı)', touched) : []),
|
|
734
791
|
...stringStyleWarning(stringStyleNodes(simulated)),
|
|
735
|
-
...inertFlexPropWarning(inertFlexPropNodes(simulated)),
|
|
792
|
+
...(resolved.profile.layout.modes.has('grid') ? inertFlexPropWarning(inertFlexPropNodes(simulated)) : []),
|
|
736
793
|
...flexSizeWarnings(simulated, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'),
|
|
737
794
|
...spacingLayerWarnings(simulated, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)')
|
|
738
795
|
];
|
|
@@ -747,7 +804,12 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
747
804
|
const resized = resizeOps.length > 0 ? planResizes((await readPage()).schema ?? [], resizeOps) : { serverOps: [], applied: [], warnings: [] };
|
|
748
805
|
const propTypes = propTypeOps.length > 0 ? planPropTypes((await readPage()).schema ?? [], propTypeOps) : { serverOps: [], applied: [] };
|
|
749
806
|
const serverOps = [...removeEventOps, ...resized.serverOps, ...propTypes.serverOps, ...sentOps];
|
|
750
|
-
|
|
807
|
+
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, {
|
|
808
|
+
kind: 'page',
|
|
809
|
+
ops: [...opRecords, ...propTypeOps],
|
|
810
|
+
steps: await boundFlowSteps(platform, resolved, project, environment, 'page', applicationId, pageId, opRecords)
|
|
811
|
+
});
|
|
812
|
+
layoutWarnings = [...ordering.warnings, ...layoutWarnings, ...profileWarnings];
|
|
751
813
|
// Tek istek, tek sürüm: öykünülen op'lar 2026-09-14'e kadar ayrı GET + tam PUT çiftleriydi ve
|
|
752
814
|
// yamadan ÖNCE yazılıyordu — PATCH reddedilse bile kalıcıydılar. Artık hepsi aynı atomik PATCH'te.
|
|
753
815
|
// baseVersion yalnız biliniyorsa gider (ajanın verdiği ya da bu çağrıda zaten okunan sayfanınki):
|
|
@@ -801,6 +863,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
801
863
|
opDrops = emptyWriteDiff();
|
|
802
864
|
}
|
|
803
865
|
}
|
|
866
|
+
Object.assign(response, applicationTypeField(resolved));
|
|
804
867
|
return withWriteDiff(withNormalization(response, [
|
|
805
868
|
...rootAddsNote(retargeted, PAGE_CONTAINER_ELEMENT_ID),
|
|
806
869
|
...componentDefaultsNote(normalized.components),
|
|
@@ -845,16 +908,21 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
845
908
|
},
|
|
846
909
|
annotations: writes
|
|
847
910
|
}, handle(config, async ({ project, environment, applicationId, name, slug, browserTitle, browserTitleKey, layoutId, roleIds, middlewareIds, events, schema, view, ifExists }) => {
|
|
848
|
-
const
|
|
911
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
912
|
+
const blocksMeaningful = resolved.profile.layout.blocksMeaningful;
|
|
913
|
+
const container = ensurePageContainer(schema ?? [], resolved.profile.layout.rootContainer);
|
|
849
914
|
assertScreenWriteGuards('page', { schema: container.schema, events });
|
|
850
915
|
const stringStyles = stringStyleNodes(container.schema);
|
|
851
916
|
const fixed = normalizeComponentDefaults(container.schema);
|
|
852
|
-
|
|
917
|
+
// Mobil çizer blokları okumaz (D3): kök konumu sıfırlama, blok sıralama ve dikey boşluk
|
|
918
|
+
// uyarısı yalnız ızgaranın anlamlı olduğu profilde koşar.
|
|
919
|
+
const reset = blocksMeaningful ? resetPageContainerPosition(container.schema) : [];
|
|
853
920
|
const catalogNotes = (schema ?? []).length > 0
|
|
854
921
|
? await screenCatalogNotes(platform, project, environment, applicationId, container.schema)
|
|
855
922
|
: [];
|
|
856
|
-
const moved = reorderSchemaBlocks(container.schema);
|
|
857
|
-
const gaps = (schema ?? []).length > 0 ? verticalGapWarnings(container.schema, 'PAGE-010 (uyarı)') : [];
|
|
923
|
+
const moved = blocksMeaningful ? reorderSchemaBlocks(container.schema) : [];
|
|
924
|
+
const gaps = (schema ?? []).length > 0 && blocksMeaningful ? verticalGapWarnings(container.schema, 'PAGE-010 (uyarı)') : [];
|
|
925
|
+
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'page', nodes: container.schema });
|
|
858
926
|
const existing = await existingMatch({
|
|
859
927
|
code: 'PAGE-035',
|
|
860
928
|
noun: 'sayfa',
|
|
@@ -882,12 +950,13 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
882
950
|
await platform.tenant('PUT', project, `/pages/${applicationId}/${pageId}`, { environment, body });
|
|
883
951
|
const updateWarnings = schema === undefined
|
|
884
952
|
? []
|
|
885
|
-
: [...gaps, ...stringStyleWarning(stringStyles), ...inertFlexPropWarning(inertFlexPropNodes(container.schema)), ...flexSizeWarnings(container.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(container.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)')];
|
|
953
|
+
: [...gaps, ...stringStyleWarning(stringStyles), ...(resolved.profile.layout.modes.has('grid') ? inertFlexPropWarning(inertFlexPropNodes(container.schema)) : []), ...flexSizeWarnings(container.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(container.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)'), ...profileWarnings];
|
|
886
954
|
const summary = {
|
|
887
955
|
id: pageId,
|
|
888
956
|
outcome: 'updated',
|
|
889
957
|
changed,
|
|
890
|
-
preserved: { schemaNodes: countSchemaNodes(body.schema), events: body.events.length }
|
|
958
|
+
preserved: { schemaNodes: countSchemaNodes(body.schema), events: body.events.length },
|
|
959
|
+
...applicationTypeField(resolved)
|
|
891
960
|
};
|
|
892
961
|
if (updateWarnings.length > 0)
|
|
893
962
|
summary.layoutWarnings = updateWarnings;
|
|
@@ -920,10 +989,13 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
920
989
|
saved.schema = saved.schema.map(node => summarize(node));
|
|
921
990
|
}
|
|
922
991
|
const normalized = withWriteDiff(withNormalization(withOutcome(response, undefined), [...container.fixes, ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved)]), drops);
|
|
923
|
-
const createWarnings = [...gaps, ...stringStyleWarning(stringStyles), ...inertFlexPropWarning(inertFlexPropNodes(container.schema)), ...flexSizeWarnings(container.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(container.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)')];
|
|
924
|
-
|
|
992
|
+
const createWarnings = [...gaps, ...stringStyleWarning(stringStyles), ...(resolved.profile.layout.modes.has('grid') ? inertFlexPropWarning(inertFlexPropNodes(container.schema)) : []), ...flexSizeWarnings(container.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(container.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)'), ...profileWarnings];
|
|
993
|
+
const typeField = applicationTypeField(resolved);
|
|
994
|
+
if (!normalized || typeof normalized !== 'object')
|
|
925
995
|
return normalized;
|
|
926
|
-
|
|
996
|
+
if (createWarnings.length === 0)
|
|
997
|
+
return Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField };
|
|
998
|
+
return { ...normalized, ...typeField, layoutWarnings: createWarnings };
|
|
927
999
|
}));
|
|
928
1000
|
server.registerTool('page_update', {
|
|
929
1001
|
title: 'Sayfa üstbilgisini güncelle',
|
|
@@ -948,13 +1020,21 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
948
1020
|
const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
|
|
949
1021
|
assertScreenWriteGuards('page', { schema: overrides.schema, events: overrides.events });
|
|
950
1022
|
const current = await fetchHost(platform, 'page', project, environment, applicationId, pageId);
|
|
1023
|
+
const resolved = overrides.schema === undefined ? undefined : await resolveProfile(platform, applicationId);
|
|
1024
|
+
const blocksMeaningful = resolved?.profile.layout.blocksMeaningful ?? true;
|
|
951
1025
|
const fixed = normalizeComponentDefaults(overrides.schema);
|
|
952
|
-
const reset = overrides.schema === undefined ? [] : resetPageContainerPosition(overrides.schema);
|
|
1026
|
+
const reset = overrides.schema === undefined || !blocksMeaningful ? [] : resetPageContainerPosition(overrides.schema);
|
|
953
1027
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.schema);
|
|
954
|
-
const moved = overrides.schema === undefined ? [] : reorderSchemaBlocks(overrides.schema);
|
|
1028
|
+
const moved = overrides.schema === undefined || !blocksMeaningful ? [] : reorderSchemaBlocks(overrides.schema);
|
|
955
1029
|
const gaps = overrides.schema === undefined
|
|
956
1030
|
? []
|
|
957
|
-
: [
|
|
1031
|
+
: [
|
|
1032
|
+
...(blocksMeaningful ? verticalGapWarnings(overrides.schema, 'PAGE-010 (uyarı)') : []),
|
|
1033
|
+
...(resolved?.profile.layout.modes.has('grid') ?? true ? inertFlexPropWarning(inertFlexPropNodes(overrides.schema)) : []),
|
|
1034
|
+
...flexSizeWarnings(overrides.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'),
|
|
1035
|
+
...spacingLayerWarnings(overrides.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)'),
|
|
1036
|
+
...(resolved ? await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'page', nodes: overrides.schema }) : [])
|
|
1037
|
+
];
|
|
958
1038
|
const body = pageUpdateBody(current, overrides);
|
|
959
1039
|
await platform.tenant('PUT', project, `/pages/${applicationId}/${pageId}`, { environment, body });
|
|
960
1040
|
const summary = {
|
|
@@ -64,7 +64,8 @@ const guideRouting = {
|
|
|
64
64
|
microfunction: 'kendi runtime\'ında koşan Node/Python fonksiyonu: protokol, yayın döngüsü, step kullanımı',
|
|
65
65
|
entity: "alan tipleri, ilişkiler, hazır entity'ler",
|
|
66
66
|
page: 'sayfa/flow işleri; flex düzen prop grubu ve boyut modları (Kural 1f-2)',
|
|
67
|
-
layout: 'yerleşim kabuğu: tek slot, views,
|
|
67
|
+
layout: 'yerleşim kabuğu: tek slot, views, dar ekranda yığılma, widthType fill/fixed/auto',
|
|
68
|
+
mobile: "mobil UYGULAMA (type:'mobile'): kurulum sırası (mobile_app_create), flex-only yerleşim, tipli stil, sekmeler (properties.mobile.tabs) ve çekmece, adım sözlüğü, MOB-* bulguları, page_screenshot device",
|
|
68
69
|
component: 'komponent modeli, olay sözleşmeleri, appearance boyut sözlüğü (widthString/heightString)',
|
|
69
70
|
html: 'hazır komponent yetmediğinde tek seferlik kaçış kapısı (Mustache, fireEvent, script kuralları)',
|
|
70
71
|
'external-component': 'kalıcı ihtiyaçta kendi komponentini yazıp yayınlama',
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { invalidScreenEventNames, invalidWidgetTypeNodes, nestedRepeaterNodes, nestedRepeaterMessage, blockSizeNote, heightTypeNote, sizeStringNote } from '../knowledge/normalize.js';
|
|
1
|
+
import { invalidScreenEventNames, invalidWidgetTypeNodes, nestedRepeaterNodes, nestedRepeaterMessage, blockSizeNote, heightTypeNote, sizeStringNote, droppableStatesNote } from '../knowledge/normalize.js';
|
|
2
2
|
import { FUNCTIONS_NAMESPACE } from '../knowledge/functionGuards.js';
|
|
3
3
|
import { applyCatalogLayoutRules } from './components.js';
|
|
4
4
|
import { PlatformApiError } from '../platform/errors.js';
|
|
@@ -65,5 +65,5 @@ export async function screenCatalogNotes(platform, project, environment, applica
|
|
|
65
65
|
if (!Array.isArray(nodes) || nodes.length === 0)
|
|
66
66
|
return [];
|
|
67
67
|
const catalog = await applyCatalogLayoutRules(platform, project, environment, applicationId, nodes);
|
|
68
|
-
return [...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights), ...sizeStringNote(catalog.sizes)];
|
|
68
|
+
return [...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights), ...sizeStringNote(catalog.sizes), ...droppableStatesNote(catalog.states)];
|
|
69
69
|
}
|
package/dist/tools/screens.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PlatformApiError } from '../platform/errors.js';
|
|
2
2
|
import { resolveNodeStyleReport } from '../knowledge/styleResolve.js';
|
|
3
|
+
import { nodeNativeStyle } from '../knowledge/styleResolveNative.js';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export const hostParam = z.enum(['page', 'layout', 'component'])
|
|
5
6
|
.describe("Akışın barındırıcısı: page | layout | component (widget de component'tir)");
|
|
@@ -13,21 +14,26 @@ export const modeParam = z.enum(['summary', 'subtree', 'full']).optional().descr
|
|
|
13
14
|
* `styleSource` verilirse (undefined DEĞİLSE) her düğüme efektif stil raporu da
|
|
14
15
|
* yazılır: tema componentStyles → preset → düğümün kendi stili kaskadı.
|
|
15
16
|
*/
|
|
16
|
-
export function summarize(node, styleSource) {
|
|
17
|
+
export function summarize(node, styleSource, nativeTheme) {
|
|
17
18
|
const summary = {
|
|
18
19
|
id: node.id,
|
|
19
20
|
elementId: node.elementId,
|
|
20
21
|
name: node.name,
|
|
21
22
|
type: node.type
|
|
22
23
|
};
|
|
23
|
-
|
|
24
|
+
// Mobil uygulama: stil CSS değil React Native nesnesidir (`shell` kutu, `text` metin).
|
|
25
|
+
if (nativeTheme !== undefined) {
|
|
26
|
+
const { shell, text, warnings } = nodeNativeStyle(node, nativeTheme);
|
|
27
|
+
summary.nativeStyle = { shell, text, ...(warnings.length > 0 ? { warnings } : {}) };
|
|
28
|
+
}
|
|
29
|
+
else if (styleSource !== undefined) {
|
|
24
30
|
const report = resolveNodeStyleReport(node, styleSource ?? undefined);
|
|
25
31
|
if (report.resolvedStyle !== undefined)
|
|
26
32
|
summary.resolvedStyle = report.resolvedStyle;
|
|
27
33
|
summary.styleLayers = report.styleLayers;
|
|
28
34
|
}
|
|
29
35
|
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
30
|
-
summary.children = node.children.map(child => summarize(child, styleSource));
|
|
36
|
+
summary.children = node.children.map(child => summarize(child, styleSource, nativeTheme));
|
|
31
37
|
}
|
|
32
38
|
return summary;
|
|
33
39
|
}
|
package/package.json
CHANGED