@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
package/dist/tools/pages.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, microOps, isBranchName } from './common.js';
|
|
2
|
-
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, staleSetterWarnings, lostSetterTargets, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
|
|
2
|
+
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, lostSetterTargets, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
|
|
3
3
|
import { functionSignature } from '../knowledge/functionGuards.js';
|
|
4
4
|
import { fetchHost, pageUpdateBody, schemaParam, eventsParam, unwrap, modeParam, summarize, findNode, flowPath } from './screens.js';
|
|
5
5
|
import { fetchComponentEventNames } from './components.js';
|
|
6
6
|
import { assertScreenWriteGuards, screenCatalogNotes } from './screenWrite.js';
|
|
7
7
|
import { opsParam, writeViewParam, outlineWriteResponse, unknownEventOps, collectTypes, simulateLayoutOps, planEventRemovals, resizeServerOps, planResizes, planPropTypes, rejectedOpsMessage } from './schemaOps.js';
|
|
8
8
|
import { formBindingWarnings, formSubmitButtonWarnings } from '../knowledge/coverage.js';
|
|
9
|
-
import {
|
|
9
|
+
import { emptyWriteDiff, schemaOpDrops, unwrapProps, withWriteDiff, writeDiff } from '../platform/writeDiff.js';
|
|
10
10
|
import { PlatformApiError } from '../platform/errors.js';
|
|
11
|
+
import { existingMatch, ifExistsParam, listAll, withOutcome } from '../platform/ifExists.js';
|
|
11
12
|
import { handle, toolResult, toolError } from '../platform/responses.js';
|
|
12
|
-
import { StudioScreenshotter } from '../platform/
|
|
13
|
-
import { captureTargetFor } from '../platform/capture/target.js';
|
|
13
|
+
import { StudioScreenshotter } from '../platform/screenshot.js';
|
|
14
14
|
import { diffSummaries, summaryLines, summaryStats } from '../platform/domSummary.js';
|
|
15
15
|
import { captureKey, imageHash, sharedCaptureMemory } from '../platform/screenshotMemory.js';
|
|
16
16
|
import { summarizeLayout } from '../knowledge/layoutSummary.js';
|
|
@@ -22,9 +22,6 @@ import { dirname, resolve } from 'node:path';
|
|
|
22
22
|
import { setPropTypeErrors } from '../knowledge/propTypes.js';
|
|
23
23
|
import { designSnapshotInput } from '../knowledge/designSnapshot.js';
|
|
24
24
|
import { discoverPage } from '../knowledge/pageDiscover.js';
|
|
25
|
-
import { applicationTypeField, resolveProfile, resolveProfileBySlug } from '../knowledge/platform/resolve.js';
|
|
26
|
-
import { resolveThemeNative } from '../knowledge/styleResolveNative.js';
|
|
27
|
-
import { boundFlowSteps, screenProfileWarnings } from './profileWrite.js';
|
|
28
25
|
import { z } from 'zod';
|
|
29
26
|
/**
|
|
30
27
|
* Runtime paketindeki `appFunctions`/`pageFunctions` (UiFunctionRuntimeDto: id, name, parameters,
|
|
@@ -59,6 +56,10 @@ async function orderByIndex(ops, readPage) {
|
|
|
59
56
|
return { ops, moved: [], warnings: [] };
|
|
60
57
|
}
|
|
61
58
|
}
|
|
59
|
+
/** Şema ağacındaki TÜM düğümler (kök + iç içe çocuklar); page_get ve page_create özeti aynı sayımı kullanır. */
|
|
60
|
+
function countSchemaNodes(nodes) {
|
|
61
|
+
return (nodes ?? []).reduce((total, node) => total + 1 + countSchemaNodes(node.children), 0);
|
|
62
|
+
}
|
|
62
63
|
async function fetchPage(platform, project, environment, applicationId, pageId) {
|
|
63
64
|
const response = await platform.tenant('GET', project, `/pages/${applicationId}/${pageId}`, { environment });
|
|
64
65
|
return response?.data ?? {};
|
|
@@ -71,7 +72,7 @@ async function fetchPage(platform, project, environment, applicationId, pageId)
|
|
|
71
72
|
*/
|
|
72
73
|
export const SERVER_PAGE_OPS = [
|
|
73
74
|
'replace_prop', 'set_binding', 'set_prop_type', 'add_component', 'remove_component', 'move_component', 'reorder', 'set_block',
|
|
74
|
-
'set_appearance', 'set_states', 'set_event', 'remove_event', 'set_validations', 'set_state_binding', 'remove_state_binding', 'batch'
|
|
75
|
+
'set_appearance', 'set_states', 'set_event', 'add_event', 'remove_event', 'set_validations', 'set_state_binding', 'remove_state_binding', 'batch'
|
|
75
76
|
];
|
|
76
77
|
export function registerPageTools(server, platform, injectedScreenshotter, injectedMemory) {
|
|
77
78
|
const config = platform.config;
|
|
@@ -104,9 +105,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
104
105
|
annotations: readOnly
|
|
105
106
|
}, handle(config, async ({ project, environment, applicationId, pageId }) => {
|
|
106
107
|
const page = await fetchPage(platform, project, environment, applicationId, pageId);
|
|
107
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
108
108
|
const layout = (page.layout ?? {});
|
|
109
|
-
const countNodes = (nodes) => (nodes ?? []).reduce((total, node) => total + 1 + countNodes(node.children), 0);
|
|
110
109
|
return {
|
|
111
110
|
id: page.id,
|
|
112
111
|
name: page.name,
|
|
@@ -123,9 +122,8 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
123
122
|
events: page.events ?? [],
|
|
124
123
|
flows: (Array.isArray(page.flows) ? page.flows : [])
|
|
125
124
|
.map(flow => ({ id: flow.id, name: flow.name, slug: flow.slug })),
|
|
126
|
-
schemaNodes:
|
|
127
|
-
updatedAt: page.audit?.updatedAt ?? page.audit?.createdAt
|
|
128
|
-
...applicationTypeField(resolved)
|
|
125
|
+
schemaNodes: countSchemaNodes(page.schema),
|
|
126
|
+
updatedAt: page.audit?.updatedAt ?? page.audit?.createdAt
|
|
129
127
|
};
|
|
130
128
|
}));
|
|
131
129
|
server.registerTool('page_runtime_get', {
|
|
@@ -173,17 +171,11 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
173
171
|
const styleSource = resolveStyles ? (theme ?? null) : undefined;
|
|
174
172
|
const asNodes = (value) => (Array.isArray(value) ? value : []);
|
|
175
173
|
const named = (value) => (Array.isArray(value) ? value : []).map(entry => ({ id: entry.id, name: entry.name, slug: entry.slug }));
|
|
176
|
-
const runtimeApplication = (body.application ?? {});
|
|
177
|
-
// Mobil uygulama React Native stiliyle çizilir (G4): rapor CSS kaskadı değil `shell`/`text`.
|
|
178
|
-
const nativeTheme = resolveStyles && runtimeApplication.type === 'mobile' ? resolveThemeNative((theme ?? undefined)) : undefined;
|
|
179
|
-
const nodeStyleSource = nativeTheme ? undefined : styleSource;
|
|
180
174
|
return {
|
|
181
175
|
page: { id: body.id, name: body.name, slug: body.slug, browserTitle: body.browserTitle, browserTitleKey: body.browserTitleKey, isHomepage: body.isHomepage },
|
|
182
|
-
...(typeof runtimeApplication.type === 'string' ? { applicationType: runtimeApplication.type } : {}),
|
|
183
176
|
events: body.events ?? [],
|
|
184
|
-
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(node => summarize(node,
|
|
185
|
-
schema: asNodes(body.schema).map(node => summarize(node,
|
|
186
|
-
...(nativeTheme ? { styleResolver: 'native' } : {}),
|
|
177
|
+
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(node => summarize(node, styleSource)) } : null,
|
|
178
|
+
schema: asNodes(body.schema).map(node => summarize(node, styleSource)),
|
|
187
179
|
widgets: named(body.widgets),
|
|
188
180
|
flows: named(body.flows),
|
|
189
181
|
variableKeys: Object.keys((body.variables ?? {})),
|
|
@@ -202,11 +194,9 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
202
194
|
}
|
|
203
195
|
}
|
|
204
196
|
: {}),
|
|
205
|
-
note:
|
|
206
|
-
? "
|
|
207
|
-
: resolveStyles
|
|
208
|
-
? "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."
|
|
209
|
-
: "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."
|
|
197
|
+
note: resolveStyles
|
|
198
|
+
? "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."
|
|
199
|
+
: "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."
|
|
210
200
|
};
|
|
211
201
|
}));
|
|
212
202
|
const screenshotter = injectedScreenshotter ?? new StudioScreenshotter(config);
|
|
@@ -214,7 +204,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
214
204
|
const maxInlinePngBytes = 5 * 1024 * 1024;
|
|
215
205
|
server.registerTool('page_screenshot', {
|
|
216
206
|
title: 'Sayfanın ekran görüntüsünü al',
|
|
217
|
-
description: "Studio arayüzünün runtime render rotasını (/dev ya da /view) headless Chromium'da açar ve sayfanın PNG ekran görüntüsünü inline görüntü olarak döner — kurduğun sayfayı GÖZLE doğrulamanın yolu budur (page_runtime_get paketi verir, bu tool ekranda ne ÇİZİLDİĞİNİ gösterir). Sayfaya bağlı middleware'ler gerçekten koşar; reddedilirse yanıtta middlewareBlocked işaretlenir ve Studio'nun çizdiği hata durumu karesi döner — bu da bir doğrulamadır. YÜKLENMEMİŞ SAYFA ASLA ÇEKİLMEZ: kare, runtime çizilip mount akışları bitince alınır (yanıtta pageState + readyAfterMs); bütçe dolduğunda sayfa hâlâ iskelette ya da akış koşuyorsa çağrı ne görüldüğünü söyleyen bir hatayla döner, boş/yarım kare dönmez. Sayfa akışı bir adımda düşerse (patlayan ifade, hata dönen uç) yanıt runtimeErrors taşır ve hangi komponentin hangi prop'unun YAZILMADIĞINI söyler — kare 'çalışıyor' görünürken sessizce boş kalan komponenti ancak bu yakalar. Kare DİLİ ve TEMASI seçilebilir: 'language' adrese ?lang ekler (çeviriye bağlı prop'lar da çok dilli alanların satır değerleri de o dilde çözülür), 'colorScheme' ziyaretçinin tema tercihini sayfa açılmadan eker — ikisi de sayfaya dil değiştirici ya da theme-toggle koymayı GEREKTİRMEZ, yani referansa sadık bir tasarımın ikinci dili ve koyu teması bozulmadan kanıtlanır. STUDIO_BASE_URL tanımlı olmalı; ilk kullanım için `npx playwright-core install chromium` gerekir. DAL ORTAMI DA GÖRÜNTÜLENİR (2026-09-09'dan beri): 'development' /dev, 'production' /view, dal ise /branch/{dal} şeridinden açılır ve sayfanın BÜTÜN istekleri (şema da akışlar da) o dala gider — dalda kurulan sayfa artık merge'i beklemeden gözle doğrulanır (platform_guide('merge') §Dalda görsel doğrulama).",
|
|
207
|
+
description: "ÖNCE KARESİZ DENE: 'kart göründü mü, tablo doldu mu, hata var mı' gibi olgu sorularında image:'none' verin — kare çekilmez, yanıttaki summaryText her kök düğümün görünürlüğünü, metnini, satır sayısını, açık sekmesini ve hata mesajlarını söyler (bir tam sayfa karesinin yüzlerce katı küçük). Kareyi yalnız görsel soru (renk, hizalama, referansa sadakat) için alın ve küçültün: clip ile bölge, scale:0.5 ile yarı çözünürlük; aynı sayfayı yeniden çekerken image:'ifChanged'. Studio arayüzünün runtime render rotasını (/dev ya da /view) headless Chromium'da açar ve sayfanın PNG ekran görüntüsünü inline görüntü olarak döner — kurduğun sayfayı GÖZLE doğrulamanın yolu budur (page_runtime_get paketi verir, bu tool ekranda ne ÇİZİLDİĞİNİ gösterir). Sayfaya bağlı middleware'ler gerçekten koşar; reddedilirse yanıtta middlewareBlocked işaretlenir ve Studio'nun çizdiği hata durumu karesi döner — bu da bir doğrulamadır. YÜKLENMEMİŞ SAYFA ASLA ÇEKİLMEZ: kare, runtime çizilip mount akışları bitince alınır (yanıtta pageState + readyAfterMs); bütçe dolduğunda sayfa hâlâ iskelette ya da akış koşuyorsa çağrı ne görüldüğünü söyleyen bir hatayla döner, boş/yarım kare dönmez. Sayfa akışı bir adımda düşerse (patlayan ifade, hata dönen uç) yanıt runtimeErrors taşır ve hangi komponentin hangi prop'unun YAZILMADIĞINI söyler — kare 'çalışıyor' görünürken sessizce boş kalan komponenti ancak bu yakalar. Kare DİLİ ve TEMASI seçilebilir: 'language' adrese ?lang ekler (çeviriye bağlı prop'lar da çok dilli alanların satır değerleri de o dilde çözülür), 'colorScheme' ziyaretçinin tema tercihini sayfa açılmadan eker — ikisi de sayfaya dil değiştirici ya da theme-toggle koymayı GEREKTİRMEZ, yani referansa sadık bir tasarımın ikinci dili ve koyu teması bozulmadan kanıtlanır. STUDIO_BASE_URL tanımlı olmalı; ilk kullanım için `npx playwright-core install chromium` gerekir. DAL ORTAMI DA GÖRÜNTÜLENİR (2026-09-09'dan beri): 'development' /dev, 'production' /view, dal ise /branch/{dal} şeridinden açılır ve sayfanın BÜTÜN istekleri (şema da akışlar da) o dala gider — dalda kurulan sayfa artık merge'i beklemeden gözle doğrulanır (platform_guide('merge') §Dalda görsel doğrulama).",
|
|
218
208
|
inputSchema: {
|
|
219
209
|
project: projectParam,
|
|
220
210
|
environment: environmentParam,
|
|
@@ -223,15 +213,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
223
213
|
viewport: z.object({
|
|
224
214
|
width: z.number().int().min(320).max(3840),
|
|
225
215
|
height: z.number().int().min(240).max(2160)
|
|
226
|
-
}).optional().describe('Varsayılan 1280x800 (deviceScaleFactor 1)
|
|
227
|
-
device: z.union([
|
|
228
|
-
z.enum(['phone', 'tablet']),
|
|
229
|
-
z.object({
|
|
230
|
-
width: z.number().int().min(320).max(3840),
|
|
231
|
-
height: z.number().int().min(240).max(2160),
|
|
232
|
-
scale: z.number().min(1).max(3).optional()
|
|
233
|
-
})
|
|
234
|
-
]).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."),
|
|
216
|
+
}).optional().describe('Varsayılan 1280x800 (deviceScaleFactor 1)'),
|
|
235
217
|
fullPage: z.boolean().optional().describe('true: kaydırılan kısım dahil tüm sayfa; varsayılan yalnız viewport karesi'),
|
|
236
218
|
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)."),
|
|
237
219
|
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."),
|
|
@@ -262,7 +244,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
262
244
|
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')
|
|
263
245
|
},
|
|
264
246
|
annotations: readOnly
|
|
265
|
-
}, async ({ project, environment, applicationSlug, pageSlug, viewport,
|
|
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 }) => {
|
|
266
248
|
try {
|
|
267
249
|
if (anonymous === true && (asUser !== undefined || cookies !== undefined)) {
|
|
268
250
|
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.");
|
|
@@ -306,24 +288,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
306
288
|
}
|
|
307
289
|
}
|
|
308
290
|
const url = `${config.studioBaseUrl}/${lane}/${projectSlug}/${applicationSlug}/${pageSlug}${languageChoice ? `?lang=${languageChoice.code}` : ''}`;
|
|
309
|
-
|
|
310
|
-
// çizimidir, varsayılan kare telefon ön ayarıdır. Tip okunamazsa web davranışı aynen.
|
|
311
|
-
const resolved = await resolveProfileBySlug(platform, projectSlug, applicationSlug);
|
|
312
|
-
const target = captureTargetFor(resolved.profile.capture.target);
|
|
313
|
-
const mobile = resolved.profile.platform === 'mobile';
|
|
314
|
-
if (device !== undefined && !mobile) {
|
|
315
|
-
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 }).`);
|
|
316
|
-
}
|
|
317
|
-
const deviceName = typeof device === 'string' ? device : device === undefined && mobile ? resolved.profile.capture.defaultDevice : undefined;
|
|
318
|
-
const preset = typeof device === 'object'
|
|
319
|
-
? { width: device.width, height: device.height, dpr: device.scale ?? 1 }
|
|
320
|
-
: deviceName === undefined ? undefined : resolved.profile.capture.devices[deviceName];
|
|
321
|
-
const appliedDevice = viewport === undefined && preset !== undefined ? preset : undefined;
|
|
322
|
-
const deviceNote = viewport !== undefined && device !== undefined
|
|
323
|
-
? 'viewport verildiği için device yok sayıldı: kare verilen viewport ve deviceScaleFactor 1 ile çekildi.'
|
|
324
|
-
: undefined;
|
|
325
|
-
const size = viewport ?? (appliedDevice ? { width: appliedDevice.width, height: appliedDevice.height } : { width: 1280, height: 800 });
|
|
326
|
-
const dpr = appliedDevice && appliedDevice.dpr !== 1 ? appliedDevice.dpr : undefined;
|
|
291
|
+
const size = viewport ?? { width: 1280, height: 800 };
|
|
327
292
|
const extraCookies = Object.entries(cookies ?? {}).map(([name, value]) => ({ name, value }));
|
|
328
293
|
const identity = anonymous === true ? 'anonymous' : asUser === undefined ? 'gateway' : 'asUser';
|
|
329
294
|
const identityToken = anonymous ? undefined : resolveIdentity(asUser) ?? await platform.accessToken();
|
|
@@ -348,9 +313,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
348
313
|
...(quality === undefined ? {} : { quality }),
|
|
349
314
|
...(scale === undefined ? {} : { scale }),
|
|
350
315
|
...(clip === undefined ? {} : { clip: clip }),
|
|
351
|
-
...(actions === undefined ? {} : { actions: actions })
|
|
352
|
-
...(mobile ? { target } : {}),
|
|
353
|
-
...(dpr === undefined ? {} : { dpr })
|
|
316
|
+
...(actions === undefined ? {} : { actions: actions })
|
|
354
317
|
});
|
|
355
318
|
// Fark ÖZETTEN çıkar; `summary:false` verilse de fark için toplanmıştır (yanıta girmez).
|
|
356
319
|
// Anahtar KİMLİĞİ ayırır (K8): 'asUser' etiketi yetmez — iki farklı üyenin jetonu aynı
|
|
@@ -421,11 +384,6 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
421
384
|
...(languageChoice?.note ? { languageNote: languageChoice.note } : {}),
|
|
422
385
|
...(colorScheme === undefined ? {} : { colorScheme }),
|
|
423
386
|
viewport: size,
|
|
424
|
-
...(appliedDevice === undefined ? {} : { device: typeof device === 'object' ? { ...device } : deviceName, deviceScaleFactor: appliedDevice.dpr }),
|
|
425
|
-
...(deviceNote === undefined ? {} : { deviceNote }),
|
|
426
|
-
// Web karesinin yanıtı profil öncesiyle aynı kalır (G3 kısıtı); alan yalnız mobilde.
|
|
427
|
-
...(mobile ? applicationTypeField(resolved) : {}),
|
|
428
|
-
...(target.platformNote === undefined ? {} : { platformNote: target.platformNote }),
|
|
429
387
|
fullPage: fullPage === true,
|
|
430
388
|
identity,
|
|
431
389
|
cookieNames,
|
|
@@ -577,20 +535,19 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
577
535
|
annotations: readOnly
|
|
578
536
|
}, handle(config, async ({ project, environment, applicationId, pageId, mode, componentId }) => {
|
|
579
537
|
const page = await fetchPage(platform, project, environment, applicationId, pageId);
|
|
580
|
-
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
581
538
|
const updatedAt = page.audit?.updatedAt ?? page.audit?.createdAt;
|
|
582
539
|
const version = page.version;
|
|
583
540
|
if (mode === 'full')
|
|
584
|
-
return { version, updatedAt,
|
|
541
|
+
return { version, updatedAt, schema: page.schema ?? [] };
|
|
585
542
|
if (mode === 'subtree') {
|
|
586
543
|
if (!componentId)
|
|
587
544
|
throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
|
|
588
545
|
const subtree = findNode(page.schema, componentId);
|
|
589
546
|
if (!subtree)
|
|
590
547
|
throw new PlatformApiError(`Sayfada '${componentId}' id'li bir komponent yok; page_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
|
|
591
|
-
return { version, updatedAt,
|
|
548
|
+
return { version, updatedAt, schema: subtree };
|
|
592
549
|
}
|
|
593
|
-
return { version, updatedAt,
|
|
550
|
+
return { version, updatedAt, schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
594
551
|
}));
|
|
595
552
|
server.registerTool('page_state', {
|
|
596
553
|
title: 'Sayfa durumunu getir',
|
|
@@ -616,20 +573,15 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
616
573
|
...schemaOpValidationErrors(opRecords).map(issue => `PAGE-016 — ${issue}`),
|
|
617
574
|
...codePropTemplateErrors(opRecords).map(issue => `PAGE-032 — ${issue}`)
|
|
618
575
|
];
|
|
619
|
-
const
|
|
620
|
-
const ordering = resolved.profile.layout.orderBy === 'block-position'
|
|
621
|
-
? await orderByIndex(opRecords, () => fetchPage(platform, project, environment, applicationId, pageId))
|
|
622
|
-
: { ops: opRecords, moved: [], warnings: [] };
|
|
623
|
-
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'page', ops: opRecords });
|
|
576
|
+
const ordering = await orderByIndex(opRecords, () => fetchPage(platform, project, environment, applicationId, pageId));
|
|
624
577
|
const response = ((await platform.tenant('POST', project, `/pages/${applicationId}/${pageId}/schema/validate`, {
|
|
625
578
|
environment,
|
|
626
579
|
body: { ops: [...allOps.filter(entry => entry.op === 'resize_component').flatMap(resizeServerOps), ...ordering.ops] }
|
|
627
580
|
})) ?? {});
|
|
628
581
|
if (validationIssues.length > 0)
|
|
629
582
|
response.validationWarnings = validationIssues;
|
|
630
|
-
if (ordering.warnings.length > 0
|
|
631
|
-
response.layoutWarnings =
|
|
632
|
-
Object.assign(response, applicationTypeField(resolved));
|
|
583
|
+
if (ordering.warnings.length > 0)
|
|
584
|
+
response.layoutWarnings = ordering.warnings;
|
|
633
585
|
return withNormalization(response, [
|
|
634
586
|
...componentDefaultsNote(normalized.components),
|
|
635
587
|
...validationNote(normalized.validations),
|
|
@@ -690,10 +642,13 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
690
642
|
reject('PAGE-016', entry, schemaOpValidationErrors([entry]));
|
|
691
643
|
for (const entry of opRecords)
|
|
692
644
|
reject('PAGE-032', entry, codePropTemplateErrors([entry]));
|
|
693
|
-
if (opRecords.some(entry => entry.op === 'set_event')) {
|
|
645
|
+
if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
|
|
694
646
|
const typesByElementId = new Map();
|
|
695
647
|
try {
|
|
696
|
-
|
|
648
|
+
const page = await readPage();
|
|
649
|
+
collectTypes(page.schema, typesByElementId);
|
|
650
|
+
for (const entry of opRecords)
|
|
651
|
+
reject('PAGE-034', entry, boundEventOps(page.schema, [entry]));
|
|
697
652
|
}
|
|
698
653
|
catch {
|
|
699
654
|
typesByElementId.clear();
|
|
@@ -705,6 +660,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
705
660
|
}
|
|
706
661
|
if (rejected.length > 0)
|
|
707
662
|
throw new PlatformApiError(rejectedOpsMessage(rejected), 400);
|
|
663
|
+
stripGatewayEventFields(opRecords);
|
|
708
664
|
const wantsRootAdd = opRecords.some(entry => entry.op === 'add_component' && (entry.parent === undefined || entry.parent === null || entry.parent === 'root'));
|
|
709
665
|
let retargeted = [];
|
|
710
666
|
if (wantsRootAdd) {
|
|
@@ -713,12 +669,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
713
669
|
retargeted = retargetRootAdds(opRecords, PAGE_CONTAINER_ELEMENT_ID);
|
|
714
670
|
}
|
|
715
671
|
}
|
|
716
|
-
const
|
|
717
|
-
const blocksMeaningful = resolved.profile.layout.blocksMeaningful;
|
|
718
|
-
// Mobil (dizi sırası): index platformun kendi add/move index'idir, konum çevirisi yapılmaz.
|
|
719
|
-
const ordering = resolved.profile.layout.orderBy === 'block-position'
|
|
720
|
-
? await orderByIndex(opRecords, readPage)
|
|
721
|
-
: { ops: opRecords, moved: [], warnings: [] };
|
|
672
|
+
const ordering = await orderByIndex(opRecords, readPage);
|
|
722
673
|
const sentOps = ordering.ops;
|
|
723
674
|
// Akış silen op'lar yamadan ÖNCE hesaplanır: uyarı sonradan geldiğinde akış zaten kalıcı
|
|
724
675
|
// silinmiş oluyordu ve "önce yedekle" tavsiyesi uygulanamıyordu (09-05 koşusu, G-21).
|
|
@@ -778,10 +729,10 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
778
729
|
// görünür, iç içeliği ancak hedef parent zinciri belli olunca anlarsın.
|
|
779
730
|
nestedAfterOps = nestedRepeaterNodes(simulated);
|
|
780
731
|
layoutWarnings = [
|
|
781
|
-
...
|
|
782
|
-
...
|
|
732
|
+
...pageContainerPositionWarning(page.schema),
|
|
733
|
+
...verticalGapWarnings(simulated, 'PAGE-010 (uyarı)', touched),
|
|
783
734
|
...stringStyleWarning(stringStyleNodes(simulated)),
|
|
784
|
-
...
|
|
735
|
+
...inertFlexPropWarning(inertFlexPropNodes(simulated)),
|
|
785
736
|
...flexSizeWarnings(simulated, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'),
|
|
786
737
|
...spacingLayerWarnings(simulated, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)')
|
|
787
738
|
];
|
|
@@ -796,12 +747,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
796
747
|
const resized = resizeOps.length > 0 ? planResizes((await readPage()).schema ?? [], resizeOps) : { serverOps: [], applied: [], warnings: [] };
|
|
797
748
|
const propTypes = propTypeOps.length > 0 ? planPropTypes((await readPage()).schema ?? [], propTypeOps) : { serverOps: [], applied: [] };
|
|
798
749
|
const serverOps = [...removeEventOps, ...resized.serverOps, ...propTypes.serverOps, ...sentOps];
|
|
799
|
-
|
|
800
|
-
kind: 'page',
|
|
801
|
-
ops: [...opRecords, ...propTypeOps],
|
|
802
|
-
steps: await boundFlowSteps(platform, resolved, project, environment, 'page', applicationId, pageId, opRecords)
|
|
803
|
-
});
|
|
804
|
-
layoutWarnings = [...ordering.warnings, ...layoutWarnings, ...profileWarnings];
|
|
750
|
+
layoutWarnings = [...ordering.warnings, ...layoutWarnings];
|
|
805
751
|
// Tek istek, tek sürüm: öykünülen op'lar 2026-09-14'e kadar ayrı GET + tam PUT çiftleriydi ve
|
|
806
752
|
// yamadan ÖNCE yazılıyordu — PATCH reddedilse bile kalıcıydılar. Artık hepsi aynı atomik PATCH'te.
|
|
807
753
|
// baseVersion yalnız biliniyorsa gider (ajanın verdiği ya da bu çağrıda zaten okunan sayfanınki):
|
|
@@ -845,17 +791,16 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
845
791
|
// alanın gerçekten KAYDEDİLDİĞİNİ söylemez. Yamadan sonra şemayı bir kez okuyup op'un yazmak
|
|
846
792
|
// istediğiyle kayıtlı olanı karşılaştırıyoruz. Okuma başarısız olursa yama yine geçerlidir.
|
|
847
793
|
const contentOps = sentOps.filter(entry => entry.op === 'add_component' || entry.op === 'replace_prop');
|
|
848
|
-
let opDrops =
|
|
794
|
+
let opDrops = emptyWriteDiff();
|
|
849
795
|
if (contentOps.length > 0) {
|
|
850
796
|
try {
|
|
851
797
|
const saved = await fetchPage(platform, project, environment, applicationId, pageId);
|
|
852
798
|
opDrops = schemaOpDrops(saved.schema, contentOps);
|
|
853
799
|
}
|
|
854
800
|
catch {
|
|
855
|
-
opDrops =
|
|
801
|
+
opDrops = emptyWriteDiff();
|
|
856
802
|
}
|
|
857
803
|
}
|
|
858
|
-
Object.assign(response, applicationTypeField(resolved));
|
|
859
804
|
return withWriteDiff(withNormalization(response, [
|
|
860
805
|
...rootAddsNote(retargeted, PAGE_CONTAINER_ELEMENT_ID),
|
|
861
806
|
...componentDefaultsNote(normalized.components),
|
|
@@ -881,7 +826,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
881
826
|
})));
|
|
882
827
|
server.registerTool('page_create', {
|
|
883
828
|
title: 'Sayfa oluştur',
|
|
884
|
-
description: "Yeni sayfa açar. Şemayı burada göndermek yerine boş bırakıp page_schema_patch micro-op'larıyla kurmak daha doğrudur (canlı canvas ve hedefli düzeltme orada). Şemayı burada gönderirseniz 2026-09-08'den beri AYNI doğrulama duvarından geçer: tanınmayan prop adı ve geçersiz enum değeri 422 ile reddedilir ve sayfa HİÇ açılmaz. roleIds için role_list, layoutId için layout_list kullanın. Yanıttaki schema VARSAYILAN OLARAK iskelettir (çözülmüş layout düğümleri dâhil id/type/children'a iner) — tam gövde için view:'full'; gönderdiğiniz bir alan kaydedilmediyse silentlyDropped söyler.",
|
|
829
|
+
description: "Yeni sayfa açar. Uygulamada aynı ad ya da slug'da sayfa varsa varsayılan olarak YAZMAZ ve mevcut sayfanın id'sini döner (PAGE-035 — gönderilseydi platform '<ad> (1)' adıyla ikinci bir sayfa açardı); tekrar çalıştırılabilir yazma için ifExists:'update' verin, yanıttaki outcome 'created' | 'updated' olur. Mevcut sayfalar okunamazsa da yazmaz. Şemayı burada göndermek yerine boş bırakıp page_schema_patch micro-op'larıyla kurmak daha doğrudur (canlı canvas ve hedefli düzeltme orada). Şemayı burada gönderirseniz 2026-09-08'den beri AYNI doğrulama duvarından geçer: tanınmayan prop adı ve geçersiz enum değeri 422 ile reddedilir ve sayfa HİÇ açılmaz. roleIds için role_list, layoutId için layout_list kullanın. Yanıttaki schema VARSAYILAN OLARAK iskelettir (çözülmüş layout düğümleri dâhil id/type/children'a iner) — tam gövde için view:'full'; gönderdiğiniz bir alan kaydedilmediyse silentlyDropped söyler.",
|
|
885
830
|
inputSchema: {
|
|
886
831
|
project: projectParam,
|
|
887
832
|
environment: environmentParam,
|
|
@@ -895,25 +840,59 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
895
840
|
middlewareIds: z.array(guid).optional(),
|
|
896
841
|
events: eventsParam.optional(),
|
|
897
842
|
schema: schemaParam.optional(),
|
|
898
|
-
view: writeViewParam
|
|
843
|
+
view: writeViewParam,
|
|
844
|
+
ifExists: ifExistsParam('sayfa', "mevcut sayfayı page_update gibi günceller — yalnız bu çağrıda VERİLEN alanlar yazılır, verilmeyen şema/olaylar/roller korunur (page_schema_patch ile kurulmuş şema betik tekrar koşunca silinmez); id'si ve flow'ları korunur")
|
|
899
845
|
},
|
|
900
846
|
annotations: writes
|
|
901
|
-
}, handle(config, async ({ project, environment, applicationId, name, slug, browserTitle, browserTitleKey, layoutId, roleIds, middlewareIds, events, schema, view }) => {
|
|
902
|
-
const
|
|
903
|
-
const blocksMeaningful = resolved.profile.layout.blocksMeaningful;
|
|
904
|
-
const container = ensurePageContainer(schema ?? [], resolved.profile.layout.rootContainer);
|
|
847
|
+
}, handle(config, async ({ project, environment, applicationId, name, slug, browserTitle, browserTitleKey, layoutId, roleIds, middlewareIds, events, schema, view, ifExists }) => {
|
|
848
|
+
const container = ensurePageContainer(schema ?? []);
|
|
905
849
|
assertScreenWriteGuards('page', { schema: container.schema, events });
|
|
906
850
|
const stringStyles = stringStyleNodes(container.schema);
|
|
907
851
|
const fixed = normalizeComponentDefaults(container.schema);
|
|
908
|
-
|
|
909
|
-
// uyarısı yalnız ızgaranın anlamlı olduğu profilde koşar.
|
|
910
|
-
const reset = blocksMeaningful ? resetPageContainerPosition(container.schema) : [];
|
|
852
|
+
const reset = resetPageContainerPosition(container.schema);
|
|
911
853
|
const catalogNotes = (schema ?? []).length > 0
|
|
912
854
|
? await screenCatalogNotes(platform, project, environment, applicationId, container.schema)
|
|
913
855
|
: [];
|
|
914
|
-
const moved =
|
|
915
|
-
const gaps = (schema ?? []).length > 0
|
|
916
|
-
const
|
|
856
|
+
const moved = reorderSchemaBlocks(container.schema);
|
|
857
|
+
const gaps = (schema ?? []).length > 0 ? verticalGapWarnings(container.schema, 'PAGE-010 (uyarı)') : [];
|
|
858
|
+
const existing = await existingMatch({
|
|
859
|
+
code: 'PAGE-035',
|
|
860
|
+
noun: 'sayfa',
|
|
861
|
+
where: 'bu uygulamada',
|
|
862
|
+
name,
|
|
863
|
+
slug,
|
|
864
|
+
idParam: 'pageId',
|
|
865
|
+
ifExists,
|
|
866
|
+
list: () => listAll(platform, project, environment, `/pages/${applicationId}`),
|
|
867
|
+
listTool: 'page_list',
|
|
868
|
+
copyCheck: "kullanılıp kullanılmadığını dependency_graph module:'page' ile kontrol edip gerekirse silin",
|
|
869
|
+
updateTool: 'pageId ile page_update'
|
|
870
|
+
});
|
|
871
|
+
if (existing !== undefined) {
|
|
872
|
+
const pageId = String(existing.id);
|
|
873
|
+
const current = await fetchHost(platform, 'page', project, environment, applicationId, pageId);
|
|
874
|
+
const overrides = Object.fromEntries(Object.entries({
|
|
875
|
+
name, slug, browserTitle, browserTitleKey, layoutId, roleIds, middlewareIds, events,
|
|
876
|
+
schema: schema === undefined ? undefined : container.schema
|
|
877
|
+
}).filter(([, value]) => value !== undefined));
|
|
878
|
+
const body = pageUpdateBody(current, overrides);
|
|
879
|
+
const before = pageUpdateBody(current);
|
|
880
|
+
// Aynı ad/slug ile tekrar koşan betik o alanları "değişti" diye görmemeli (Gözlem 2).
|
|
881
|
+
const changed = Object.keys(overrides).filter(key => JSON.stringify(before[key]) !== JSON.stringify(overrides[key]));
|
|
882
|
+
await platform.tenant('PUT', project, `/pages/${applicationId}/${pageId}`, { environment, body });
|
|
883
|
+
const updateWarnings = schema === undefined
|
|
884
|
+
? []
|
|
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ı)')];
|
|
886
|
+
const summary = {
|
|
887
|
+
id: pageId,
|
|
888
|
+
outcome: 'updated',
|
|
889
|
+
changed,
|
|
890
|
+
preserved: { schemaNodes: countSchemaNodes(body.schema), events: body.events.length }
|
|
891
|
+
};
|
|
892
|
+
if (updateWarnings.length > 0)
|
|
893
|
+
summary.layoutWarnings = updateWarnings;
|
|
894
|
+
return withNormalization(summary, schema === undefined ? [] : [...container.fixes, ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved)]);
|
|
895
|
+
}
|
|
917
896
|
const response = await platform.tenant('POST', project, `/pages/${applicationId}`, {
|
|
918
897
|
environment,
|
|
919
898
|
body: {
|
|
@@ -935,19 +914,16 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
935
914
|
// kaydedildi mi" sorusunu gövde değil bu diff cevaplar. `layout_create`'in yaptığının sayfa
|
|
936
915
|
// ikizi; Prop sarmalı açılmazsa doğru kaydedilmiş her prop düşmüş görünür (G-28).
|
|
937
916
|
const drops = (schema ?? []).length > 0
|
|
938
|
-
?
|
|
939
|
-
:
|
|
917
|
+
? writeDiff({ schema: unwrapProps(container.schema) }, { schema: unwrapProps(saved.schema) })
|
|
918
|
+
: emptyWriteDiff();
|
|
940
919
|
if (view !== 'full' && Array.isArray(saved.schema)) {
|
|
941
920
|
saved.schema = saved.schema.map(node => summarize(node));
|
|
942
921
|
}
|
|
943
|
-
const normalized = withWriteDiff(withNormalization(response, [...container.fixes, ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved)]), drops);
|
|
944
|
-
const createWarnings = [...gaps, ...stringStyleWarning(stringStyles), ...
|
|
945
|
-
|
|
946
|
-
if (!normalized || typeof normalized !== 'object')
|
|
922
|
+
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
|
+
if (createWarnings.length === 0 || !normalized || typeof normalized !== 'object')
|
|
947
925
|
return normalized;
|
|
948
|
-
|
|
949
|
-
return Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField };
|
|
950
|
-
return { ...normalized, ...typeField, layoutWarnings: createWarnings };
|
|
926
|
+
return { ...normalized, layoutWarnings: createWarnings };
|
|
951
927
|
}));
|
|
952
928
|
server.registerTool('page_update', {
|
|
953
929
|
title: 'Sayfa üstbilgisini güncelle',
|
|
@@ -972,21 +948,13 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
972
948
|
const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
|
|
973
949
|
assertScreenWriteGuards('page', { schema: overrides.schema, events: overrides.events });
|
|
974
950
|
const current = await fetchHost(platform, 'page', project, environment, applicationId, pageId);
|
|
975
|
-
const resolved = overrides.schema === undefined ? undefined : await resolveProfile(platform, applicationId);
|
|
976
|
-
const blocksMeaningful = resolved?.profile.layout.blocksMeaningful ?? true;
|
|
977
951
|
const fixed = normalizeComponentDefaults(overrides.schema);
|
|
978
|
-
const reset = overrides.schema === undefined
|
|
952
|
+
const reset = overrides.schema === undefined ? [] : resetPageContainerPosition(overrides.schema);
|
|
979
953
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.schema);
|
|
980
|
-
const moved = overrides.schema === undefined
|
|
954
|
+
const moved = overrides.schema === undefined ? [] : reorderSchemaBlocks(overrides.schema);
|
|
981
955
|
const gaps = overrides.schema === undefined
|
|
982
956
|
? []
|
|
983
|
-
: [
|
|
984
|
-
...(blocksMeaningful ? verticalGapWarnings(overrides.schema, 'PAGE-010 (uyarı)') : []),
|
|
985
|
-
...(resolved?.profile.layout.modes.has('grid') ?? true ? inertFlexPropWarning(inertFlexPropNodes(overrides.schema)) : []),
|
|
986
|
-
...flexSizeWarnings(overrides.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'),
|
|
987
|
-
...spacingLayerWarnings(overrides.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)'),
|
|
988
|
-
...(resolved ? await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'page', nodes: overrides.schema }) : [])
|
|
989
|
-
];
|
|
957
|
+
: [...verticalGapWarnings(overrides.schema, 'PAGE-010 (uyarı)'), ...inertFlexPropWarning(inertFlexPropNodes(overrides.schema)), ...flexSizeWarnings(overrides.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(overrides.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)')];
|
|
990
958
|
const body = pageUpdateBody(current, overrides);
|
|
991
959
|
await platform.tenant('PUT', project, `/pages/${applicationId}/${pageId}`, { environment, body });
|
|
992
960
|
const summary = {
|
|
@@ -64,8 +64,7 @@ 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,
|
|
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",
|
|
67
|
+
layout: 'yerleşim kabuğu: tek slot, views, mobil yığılma, widthType fill/fixed/auto',
|
|
69
68
|
component: 'komponent modeli, olay sözleşmeleri, appearance boyut sözlüğü (widthString/heightString)',
|
|
70
69
|
html: 'hazır komponent yetmediğinde tek seferlik kaçış kapısı (Mustache, fireEvent, script kuralları)',
|
|
71
70
|
'external-component': 'kalıcı ihtiyaçta kendi komponentini yazıp yayınlama',
|
package/dist/tools/schemaOps.js
CHANGED
|
@@ -24,7 +24,7 @@ export function outlineWriteResponse(response) {
|
|
|
24
24
|
if (Array.isArray(container.appliedPatch))
|
|
25
25
|
container.appliedPatch = outlineAppliedPatch(container.appliedPatch);
|
|
26
26
|
}
|
|
27
|
-
export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue} | remove_event {target, event} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
|
|
27
|
+
export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue, replaceBoundFlows?} (olayın BÜTÜN bağlarını bu tek akışa indirir; olayda başka akış bağlıysa gateway PAGE-034 ile reddeder — bilerek değiştirmek için replaceBoundFlows: true) | add_event {target, event, eventValue} (olaya bir akış DAHA bağlar; bağlı akışların hepsi paralel ve birbirinden bağımsız koşar, sıra garantisi yoktur — sıra gerekiyorsa flow-trigger. Aynı akış iki kez 422 duplicate-event. Mobil runtime yalnız İLK akışı koşar) | remove_event {target, event, eventValue?} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. Olaya birden fazla akış bağlıysa eventValue ile hangisi olduğunu verin, yoksa 422 ambiguous-event. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
|
|
28
28
|
/**
|
|
29
29
|
* PAGE-008 — `set_event` uydurma bir olay adıyla 200 döner ve bağ SESSİZCE ölü kalır: motor olayı
|
|
30
30
|
* komponentin yaydığı adla eşleştirir, eşleşme yoksa akış hiç çalışmaz. Kontrol yalnız hedefin
|
|
@@ -35,7 +35,7 @@ export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listes
|
|
|
35
35
|
export function unknownEventOps(ops, typesByElementId, eventsBySlug) {
|
|
36
36
|
return ops
|
|
37
37
|
.map(op => op)
|
|
38
|
-
.filter(op => op.op === 'set_event')
|
|
38
|
+
.filter(op => op.op === 'set_event' || op.op === 'add_event')
|
|
39
39
|
.flatMap(op => {
|
|
40
40
|
const target = String(op.target ?? '');
|
|
41
41
|
const event = String(op.event ?? '');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { invalidScreenEventNames, invalidWidgetTypeNodes, nestedRepeaterNodes, nestedRepeaterMessage, blockSizeNote, heightTypeNote, sizeStringNote
|
|
1
|
+
import { invalidScreenEventNames, invalidWidgetTypeNodes, nestedRepeaterNodes, nestedRepeaterMessage, blockSizeNote, heightTypeNote, sizeStringNote } 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)];
|
|
69
69
|
}
|
package/dist/tools/screens.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PlatformApiError } from '../platform/errors.js';
|
|
2
2
|
import { resolveNodeStyleReport } from '../knowledge/styleResolve.js';
|
|
3
|
-
import { nodeNativeStyle } from '../knowledge/styleResolveNative.js';
|
|
4
3
|
import { z } from 'zod';
|
|
5
4
|
export const hostParam = z.enum(['page', 'layout', 'component'])
|
|
6
5
|
.describe("Akışın barındırıcısı: page | layout | component (widget de component'tir)");
|
|
@@ -14,26 +13,21 @@ export const modeParam = z.enum(['summary', 'subtree', 'full']).optional().descr
|
|
|
14
13
|
* `styleSource` verilirse (undefined DEĞİLSE) her düğüme efektif stil raporu da
|
|
15
14
|
* yazılır: tema componentStyles → preset → düğümün kendi stili kaskadı.
|
|
16
15
|
*/
|
|
17
|
-
export function summarize(node, styleSource
|
|
16
|
+
export function summarize(node, styleSource) {
|
|
18
17
|
const summary = {
|
|
19
18
|
id: node.id,
|
|
20
19
|
elementId: node.elementId,
|
|
21
20
|
name: node.name,
|
|
22
21
|
type: node.type
|
|
23
22
|
};
|
|
24
|
-
|
|
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) {
|
|
23
|
+
if (styleSource !== undefined) {
|
|
30
24
|
const report = resolveNodeStyleReport(node, styleSource ?? undefined);
|
|
31
25
|
if (report.resolvedStyle !== undefined)
|
|
32
26
|
summary.resolvedStyle = report.resolvedStyle;
|
|
33
27
|
summary.styleLayers = report.styleLayers;
|
|
34
28
|
}
|
|
35
29
|
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
36
|
-
summary.children = node.children.map(child => summarize(child, styleSource
|
|
30
|
+
summary.children = node.children.map(child => summarize(child, styleSource));
|
|
37
31
|
}
|
|
38
32
|
return summary;
|
|
39
33
|
}
|