@appaflytech/zeyrek-ai-mcp 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.env.example +0 -15
  2. package/README.md +5 -5
  3. package/dist/config.js +0 -37
  4. package/dist/index.js +0 -2
  5. package/dist/knowledge/backendConstraints.js +1 -18
  6. package/dist/knowledge/boxSections.js +184 -0
  7. package/dist/knowledge/componentCatalog.js +232 -562
  8. package/dist/knowledge/componentEvents.js +21 -141
  9. package/dist/knowledge/layoutSummary.js +2 -140
  10. package/dist/knowledge/normalize.js +67 -236
  11. package/dist/knowledge/outlines.js +0 -36
  12. package/dist/knowledge/policy.js +4 -18
  13. package/dist/knowledge/reservedTables.js +0 -79
  14. package/dist/knowledge/rules.js +21 -319
  15. package/dist/knowledge/schemaGuard.js +0 -11
  16. package/dist/knowledge/styleResolve.js +38 -55
  17. package/dist/knowledge/themeResolve.js +3 -3
  18. package/dist/observability/trace.js +3 -6
  19. package/dist/platform/diagnostics.js +4 -10
  20. package/dist/platform/domSummary.js +1 -1
  21. package/dist/platform/errors.js +10 -162
  22. package/dist/platform/http.js +8 -29
  23. package/dist/platform/responses.js +5 -39
  24. package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
  25. package/dist/platform/secrets.js +1 -17
  26. package/dist/platform/strictArguments.js +12 -68
  27. package/dist/platform/tokenVault.js +4 -187
  28. package/dist/resources/examples/layout-app-shell.json +707 -229
  29. package/dist/resources/examples/page-detail-profile.json +1 -2
  30. package/dist/resources/examples/page-dynamic-form.json +2 -3
  31. package/dist/resources/examples/page-flex-sections.json +1 -1
  32. package/dist/resources/examples/page-html-embed.json +1 -1
  33. package/dist/resources/examples/workflow-basic.json +2 -8
  34. package/dist/resources/guides/component.md +4 -91
  35. package/dist/resources/guides/entity.md +8 -50
  36. package/dist/resources/guides/expressions.md +5 -14
  37. package/dist/resources/guides/functions.md +1 -1
  38. package/dist/resources/guides/getting-started.md +1 -38
  39. package/dist/resources/guides/layout.md +58 -55
  40. package/dist/resources/guides/microservice.md +2 -81
  41. package/dist/resources/guides/middleware.md +2 -44
  42. package/dist/resources/guides/page.md +75 -454
  43. package/dist/resources/guides/theme.md +2 -8
  44. package/dist/resources/guides/translation.md +1 -1
  45. package/dist/resources/guides/workflow.md +1 -58
  46. package/dist/resources/rules.js +5 -11
  47. package/dist/resources/schema-fingerprint.json +5 -7
  48. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  49. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  50. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  51. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -143
  52. package/dist/resources/schemas/DataJobDto.json +1 -6
  53. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
  54. package/dist/resources/schemas/MicroserviceDto.json +3 -15
  55. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  56. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -22
  57. package/dist/resources/schemas/StepDto.json +1 -22
  58. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  59. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  60. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  61. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  62. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -143
  63. package/dist/server.js +1 -27
  64. package/dist/tools/access.js +2 -369
  65. package/dist/tools/applications.js +3 -60
  66. package/dist/tools/common.js +0 -7
  67. package/dist/tools/components.js +14 -118
  68. package/dist/tools/datasources.js +3 -3
  69. package/dist/tools/dependencies.js +1 -1
  70. package/dist/tools/deployments.js +2 -2
  71. package/dist/tools/designSchemaPatch.js +18 -34
  72. package/dist/tools/dtos.js +5 -36
  73. package/dist/tools/entities.js +12 -79
  74. package/dist/tools/externalComponents.js +5 -26
  75. package/dist/tools/flows.js +14 -122
  76. package/dist/tools/folders.js +1 -1
  77. package/dist/tools/imports.js +2 -5
  78. package/dist/tools/layouts.js +17 -50
  79. package/dist/tools/merges.js +1 -1
  80. package/dist/tools/microservices.js +25 -77
  81. package/dist/tools/pages.js +112 -465
  82. package/dist/tools/platformInfo.js +8 -27
  83. package/dist/tools/projects.js +9 -71
  84. package/dist/tools/rows.js +13 -69
  85. package/dist/tools/schemaOps.js +1 -61
  86. package/dist/tools/screenWrite.js +8 -37
  87. package/dist/tools/screens.js +5 -95
  88. package/dist/tools/templates.js +1 -26
  89. package/dist/tools/translations.js +1 -1
  90. package/dist/tools/variables.js +5 -11
  91. package/dist/tools/workflows.js +5 -10
  92. package/package.json +2 -2
  93. package/dist/knowledge/access.js +0 -802
  94. package/dist/knowledge/case.js +0 -41
  95. package/dist/knowledge/connectDataBind.js +0 -819
  96. package/dist/knowledge/connectDataDefinition.js +0 -341
  97. package/dist/knowledge/connectDataExecute.js +0 -93
  98. package/dist/knowledge/connectDataRules.js +0 -128
  99. package/dist/knowledge/decision.js +0 -121
  100. package/dist/knowledge/expressionPrelude.js +0 -59
  101. package/dist/knowledge/microserviceParameters.js +0 -58
  102. package/dist/knowledge/mining.js +0 -94
  103. package/dist/knowledge/mobileComponentCatalog.js +0 -859
  104. package/dist/knowledge/mobileComponentEvents.js +0 -239
  105. package/dist/knowledge/platform/lint.js +0 -385
  106. package/dist/knowledge/platform/mobile.js +0 -142
  107. package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
  108. package/dist/knowledge/platform/mobileRules.js +0 -231
  109. package/dist/knowledge/platform/mobileSpacing.js +0 -82
  110. package/dist/knowledge/platform/mobileTabs.js +0 -108
  111. package/dist/knowledge/platform/nativeBoxModel.js +0 -44
  112. package/dist/knowledge/platform/profile.js +0 -25
  113. package/dist/knowledge/platform/resolve.js +0 -122
  114. package/dist/knowledge/platform/web.js +0 -39
  115. package/dist/knowledge/process.js +0 -510
  116. package/dist/knowledge/rowFilter.js +0 -43
  117. package/dist/knowledge/styleResolveNative.js +0 -527
  118. package/dist/platform/argumentAliases.js +0 -128
  119. package/dist/platform/bodyFile.js +0 -103
  120. package/dist/platform/capture/actions.js +0 -333
  121. package/dist/platform/capture/browser.js +0 -1
  122. package/dist/platform/capture/console-log.js +0 -122
  123. package/dist/platform/capture/index.js +0 -11
  124. package/dist/platform/capture/readiness.js +0 -103
  125. package/dist/platform/capture/summary.js +0 -77
  126. package/dist/platform/capture/target.js +0 -5
  127. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  128. package/dist/platform/capture/targets/studio-web.js +0 -5
  129. package/dist/platform/impersonation.js +0 -45
  130. package/dist/platform/jsonArray.js +0 -24
  131. package/dist/platform/schemaReadMemory.js +0 -60
  132. package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
  133. package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
  134. package/dist/resources/examples/bpm-satinalma.json +0 -376
  135. package/dist/resources/examples/connect-data-convert-verify.json +0 -107
  136. package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
  137. package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
  138. package/dist/resources/examples/connect-data-query-params.json +0 -50
  139. package/dist/resources/examples/connect-data-table-entity.json +0 -147
  140. package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
  141. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
  142. package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
  143. package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
  144. package/dist/resources/examples/mobile-form-screen.json +0 -365
  145. package/dist/resources/examples/mobile-list-detail-form.json +0 -672
  146. package/dist/resources/guides/access.md +0 -796
  147. package/dist/resources/guides/bpm.md +0 -686
  148. package/dist/resources/guides/case.md +0 -153
  149. package/dist/resources/guides/decision.md +0 -185
  150. package/dist/resources/guides/mobile-layout.md +0 -193
  151. package/dist/resources/guides/mobile.md +0 -495
  152. package/dist/resources/read.js +0 -41
  153. package/dist/resources/schemas/AccessExplanationDto.json +0 -144
  154. package/dist/resources/schemas/AccessMatrixDto.json +0 -170
  155. package/dist/resources/schemas/AccessPermissionDto.json +0 -57
  156. package/dist/resources/schemas/AccessPolicyDto.json +0 -223
  157. package/dist/resources/schemas/AccessResourceDto.json +0 -86
  158. package/dist/resources/schemas/AppSessionDto.json +0 -53
  159. package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
  160. package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
  161. package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
  162. package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
  163. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
  164. package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
  165. package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
  166. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
  167. package/dist/resources/schemas/DecisionDto.json +0 -302
  168. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
  169. package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
  170. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
  171. package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
  172. package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
  173. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
  174. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
  175. package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
  176. package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
  177. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
  178. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
  179. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
  180. package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
  181. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
  182. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
  183. package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
  184. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
  185. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
  186. package/dist/tools/accessOperations.js +0 -135
  187. package/dist/tools/accessPolicies.js +0 -170
  188. package/dist/tools/accessRuntime.js +0 -203
  189. package/dist/tools/bpm.js +0 -343
  190. package/dist/tools/bpmGovernance.js +0 -266
  191. package/dist/tools/bpmOperations.js +0 -230
  192. package/dist/tools/bpmReporting.js +0 -154
  193. package/dist/tools/bpmRouting.js +0 -150
  194. package/dist/tools/bpmRuntime.js +0 -396
  195. package/dist/tools/case.js +0 -178
  196. package/dist/tools/caseRuntime.js +0 -89
  197. package/dist/tools/connectData.js +0 -449
  198. package/dist/tools/connectors.js +0 -193
  199. package/dist/tools/decisions.js +0 -206
  200. package/dist/tools/mobile.js +0 -221
  201. package/dist/tools/modules.js +0 -48
  202. package/dist/tools/organizationStructure.js +0 -137
  203. package/dist/tools/profileWrite.js +0 -92
@@ -1,142 +0,0 @@
1
- import { ENGINE_STEP_TYPES, STATE_STORAGE_KINDS, STEP_ALIASES } from './profile.js';
2
- import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
3
- /**
4
- * Mobil uygulamanın kök kapsayıcısı. Web'in `pageContainerNode()`'undan üç farkı var:
5
- *
6
- * - `layout` prop'u YOK: mobil `container` şeması onu düşürür (`@ng-apps/ui-native`
7
- * `components/container/schema.ts` `omitProps(['layout', …])`) — mobil kanvas yalnız flex akışıdır
8
- * ve backend doğrulayıcısı mobil kataloğa bakar, yazılan `layout` bilinmeyen prop olurdu.
9
- * - `direction: 'column'`, `wrap: false`: telefon ekranı dikey akar; web kökü satır + sarma ile
10
- * 12 sütunu taklit eder.
11
- * - `blocks: []` ve `heightType: 'fill'`: çizer blokları yok sayar (D3), kök ekranı doldurur.
12
- *
13
- * Mizanpaj sözleşmesi (plans/mobil-mizanpaj-dili 02): kök `size: shrink` (S-16: `grow` çocukları ekran
14
- * boyunca eşit paya böler) ve `horizontalAlign: stretch` (Studio şablonu). Şablon kabuklu sayfanındır:
15
- * üst güvenli alan ve 16 dp yatay kenar boşluğu kabuğun işidir (`safe-area top` + slot margin'i). Kabuksuz
16
- * sayfayı `shellLessPageSkeleton` tamamlar — sayfanın layout'u ancak yazımda kesinleşir.
17
- */
18
- export function mobileRootContainer() {
19
- return {
20
- elementId: PAGE_CONTAINER_ELEMENT_ID,
21
- kind: 'component',
22
- name: PAGE_CONTAINER_ELEMENT_ID,
23
- type: 'container',
24
- props: {
25
- title: { type: 'static', data: '' },
26
- direction: { type: 'static', data: 'column' },
27
- horizontalAlign: { type: 'static', data: 'stretch' },
28
- verticalAlign: { type: 'static', data: 'top' },
29
- size: { type: 'static', data: 'shrink' },
30
- gap: { type: 'static', data: 'medium' },
31
- wrap: { type: 'static', data: false }
32
- },
33
- blocks: [],
34
- states: { isSticky: false, isDroppable: true, isScrollable: true, isWrapper: true },
35
- appearance: { heightType: 'fill', marginValue: 0, marginType: 'none', paddingType: 'none', paddingValue: 0 },
36
- events: [],
37
- validations: [],
38
- stateBindings: [],
39
- children: []
40
- };
41
- }
42
- /**
43
- * Kabuksuz sayfanın iskeleti (S1/S4): kök yatay 16 padding taşır — `isWrapper` yalnız margin'i sıfırlar,
44
- * kökün padding'i çizilir (01'de ölçüldü) — ve ilk çizilen öğe üst güvenli alan değilse tepeye
45
- * `safe_area_top` konur. Gateway'in kurduğu köke uygulanır; notu döner.
46
- */
47
- export function shellLessPageSkeleton(root) {
48
- const children = Array.isArray(root.children) ? root.children : [];
49
- const addsSafeArea = topSafeAreaMissing(children);
50
- root.appearance = { ...root.appearance, paddingType: 'custom', paddingValues: { top: 0, right: 16, bottom: 0, left: 16 } };
51
- root.children = addsSafeArea ? [topSafeArea(), ...children] : children;
52
- return [`mobil kabuksuz sayfa: köke yatay 16 padding yazıldı${addsSafeArea ? " ve tepeye üst güvenli alan (safe_area_top, edges:'top') eklendi" : ''} — ekran kenarı boşluğu ve durum çubuğu payı (mizanpaj S1/S4). Kabuklu sayfada ikisi kabuğun işidir: layoutId verin`];
53
- }
54
- /** Ekrana sabit katmanlar akışta değildir; üst payı onlar vermez (`topSafeAreaMissing`, Studio kanvası). */
55
- export const OVERLAY_TYPES = ['drawer', 'fab', 'floating-container', 'modal', 'toast'];
56
- /** `safe-area`'nın `edges`'i; yazılmamışsa `all` (Studio `KNOWN_SAFE_EDGES`). */
57
- export function safeAreaEdges(node) {
58
- const edges = node.props?.edges;
59
- const value = edges && typeof edges === 'object' ? edges.data : edges;
60
- return typeof value === 'string' && value !== '' ? value : 'all';
61
- }
62
- /** Akışta çizilen ilk öğe (kaplama katmanları ve yüzen düğümler atlanır). */
63
- export function firstDrawn(nodes) {
64
- return nodes.find(node => !OVERLAY_TYPES.includes(String(node.type ?? '')) && node.states?.isFloating !== true);
65
- }
66
- /**
67
- * İlk çizilen öğe üst payı veriyor mu — Studio'nun `topSafeAreaMissing`'iyle aynı anlam
68
- * (`apps/studio/src/features/mobile/mobile-native-canvas.functions.ts`): `edges` yazılmamışsa `all`dır,
69
- * yalnız `bottom` üst payı vermez; ilk öğe çocuklu bir kapsa onun ilk öğesine bakılır. Boş sayfa da pay alır.
70
- */
71
- export function topSafeAreaMissing(nodes, mode = 'skeleton') {
72
- const first = firstDrawn(nodes);
73
- if (!first)
74
- return true;
75
- if (first.type === 'safe-area')
76
- return safeAreaEdges(first) === 'bottom';
77
- const children = Array.isArray(first.children) ? first.children : [];
78
- if (children.length > 0)
79
- return topSafeAreaMissing(children, mode);
80
- // Studio: boş kap hiçbir şey çizmez, korunacak içerik de yoktur — uyarı yok. İskelet ise payı yine kurar.
81
- return mode === 'studio' ? first.states?.isDroppable !== true : true;
82
- }
83
- /** Studio'nun yeni sayfa şablonundaki payla aynı (`new-page-schema.functions.ts`). */
84
- function topSafeArea() {
85
- return {
86
- elementId: 'safe_area_top',
87
- kind: 'component',
88
- name: 'safe_area_top',
89
- type: 'safe-area',
90
- props: { edges: { type: 'static', data: 'top' } },
91
- blocks: [],
92
- states: { isSticky: false, isDroppable: true, isScrollable: false, isWrapper: false },
93
- appearance: { heightType: 'auto', widthType: 'fill' },
94
- events: [],
95
- validations: [],
96
- stateBindings: [],
97
- children: []
98
- };
99
- }
100
- /**
101
- * Mobil profil = taşınabilir alt küme (`plans/mobil-uygulama-expo/01-mimari-tasarim.md` §5, §7).
102
- * Yazım platforma YİNE gider (D3: şema aynı); alt kümenin dışı `MOB-*` bulgusu olarak uyarılır.
103
- */
104
- export const mobileProfile = Object.freeze({
105
- platform: 'mobile',
106
- catalogType: 'mobile',
107
- layout: {
108
- modes: new Set(['flex']),
109
- blocksMeaningful: false,
110
- rootContainer: mobileRootContainer,
111
- orderBy: 'array-index'
112
- },
113
- appearance: {
114
- ignoredKeys: new Set(['className', 'tooltip', 'tooltipPosition', 'zIndex']),
115
- sizeUnits: new Set(['px', '%'])
116
- },
117
- style: { freeStyle: false, hoverStyle: false, resolver: 'native' },
118
- steps: {
119
- registered: new Set([...ENGINE_STEP_TYPES, 'alert', 'confirm', 'redirect', 'navigate', 'storage', 'setcookie', 'download', 'share', 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel']),
120
- aliases: STEP_ALIASES,
121
- infoOnly: new Set(['download', 'setcookie'])
122
- },
123
- shell: {
124
- components: new Set(['app-bar']),
125
- suggestions: {
126
- navigations: "sekmeler: application_update ile properties.mobile.tabs (Ayarlar › Sekmeler); üst çubuk için 'app-bar'",
127
- sidebar: "çekmece: 'drawer' widget'ı + app-bar.leading:'menu' ve menuTarget",
128
- header: "'app-bar' (başlık, leading: auto | none | back | menu, sağ eylemler)"
129
- },
130
- legacyShellNodes: new Set(['tab-bar', 'drawer-menu'])
131
- },
132
- externalComponents: false,
133
- state: { storageKinds: new Set(STATE_STORAGE_KINDS) },
134
- capture: {
135
- target: 'studio-mobile',
136
- devices: {
137
- phone: { width: 390, height: 844, dpr: 2 },
138
- tablet: { width: 820, height: 1180, dpr: 2 }
139
- },
140
- defaultDevice: 'phone'
141
- }
142
- });
@@ -1,324 +0,0 @@
1
- import { MOBILE_DROPPABLE_TYPES, MOBILE_OUT_OF_FLOW_TYPES, MOBILE_TRANSPARENT_WRAPPER_TYPES } from '../mobileComponentCatalog.js';
2
- import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
3
- import { OVERLAY_TYPES, firstDrawn, safeAreaEdges, topSafeAreaMissing } from './mobile.js';
4
- import { MOBILE_RULES } from './mobileRules.js';
5
- import { FULL_WIDTH_TYPES, nativeMarginLeft, nativePaddingLeft } from './nativeBoxModel.js';
6
- /**
7
- * Boş görünmesi hata olmayan bırakılabilir tipler: içeriği çalışma anında gelir (`widget-area`, `slot`), öğesini
8
- * prop'tan çizer (`carousel` items, `tabs` başlığı, `grouped-list` satırları) ya da akış dışı bir katmandır.
9
- */
10
- const EMPTY_ALLOWED_TYPES = ['widget-area', 'slot', 'carousel', 'tabs', 'grouped-list', ...OVERLAY_TYPES];
11
- export function describeShell(views) {
12
- const root = asNodes(views)[0];
13
- const children = asNodes(root?.children);
14
- return { kind: 'shell', topInset: children.length > 0 && !topSafeAreaMissing(children, 'studio'), appBar: someNode(asNodes(views), node => node.type === 'app-bar') };
15
- }
16
- /**
17
- * MOB-015…021 — mizanpaj sözleşmesinin iskelet (S1/S3/S12/S13) ve hiza/ölçek (S5/S6/S8/S9/S11) maddeleri (plans/mobil-mizanpaj-dili 03, S1/S3/S12/S13).
18
- * Ağacın tamamına bakar; yama yolunda yalnız yazımın getirdiği bulgular raporlanır (`newFindings`).
19
- */
20
- export function mobileLayoutFindings(input) {
21
- const roots = asNodes(input.tree);
22
- const screenRoot = input.kind === 'page' ? roots.find(node => node.elementId === PAGE_CONTAINER_ELEMENT_ID) : roots[0];
23
- const screen = asNodes(screenRoot?.children);
24
- const shell = input.kind === 'page' ? input.shell ?? { kind: 'none' } : { kind: 'none' };
25
- const findings = [];
26
- if (shell.kind === 'unknown') {
27
- findings.push({ code: MOBILE_RULES['MOB-015'].id, severity: 'info', message: 'sayfanın kabuğu (layout) okunamadı — üst pay ve app-bar kuralları (MOB-015, MOB-017) bu yazımda denetlenmedi' });
28
- }
29
- // MOB-015 — üst güvenli alan tek yerde.
30
- if (input.kind === 'layout' && screen.length > 0 && topSafeAreaMissing(screen, 'studio')) {
31
- findings.push(rule('MOB-015', label(screenRoot), "kabuğun ilk çizilen öğesi üst güvenli alan değil: app-bar ve sayfalar durum çubuğunun/çentiğin altına girer. app-bar'ı `safe-area` (edges: 'top') içine alın — sayfalar ikinci pay vermez"));
32
- }
33
- if (input.kind === 'page' && shell.kind === 'none' && screen.length > 0 && topSafeAreaMissing(screen, 'studio')) {
34
- findings.push(rule('MOB-015', label(screenRoot), "kabuksuz sayfanın ilk çizilen öğesi üst güvenli alan değil: içerik durum çubuğunun/çentiğin altına girer. Tepeye `safe-area` (edges: 'top') koyun ya da sayfayı üst payı veren bir kabuğa bağlayın (layoutId)"));
35
- }
36
- if (shell.kind === 'shell' && !shell.topInset) {
37
- findings.push(rule('MOB-015', 'layout', "sayfanın kabuğu üst payı vermiyor: kabukta app-bar'ı `safe-area` (edges: 'top') içine alın (layout_schema_patch); payı sayfaya koymayın, her sayfada tekrarlanır"));
38
- }
39
- if (shell.kind === 'shell' && shell.topInset) {
40
- // Katman (modal, drawer) kendi panelinde payı ayrıca çözer; ekranın akışındaki güvenli alanlar sayılır.
41
- for (const node of collect(roots, node => node.type === 'safe-area' && safeAreaEdges(node) !== 'bottom', OVERLAY_TYPES)) {
42
- findings.push(rule('MOB-015', label(node), `kabuk üst payı zaten veriyor; sayfadaki '${safeAreaEdges(node)}' güvenli alanı ikinci pay bırakır (içerik app-bar'dan kopar). Kaldırın; alt pay gerekiyorsa edges: 'bottom'`));
43
- }
44
- }
45
- // MOB-016 — tek kaydırıcı: kök kaydırır ve içerik boyundadır.
46
- if (screenRoot && propData(screenRoot, 'size') === 'grow') {
47
- findings.push(rule('MOB-016', label(screenRoot), "kök kap `size: 'grow'`: çocuklar ekran boyunu eşit paylaşır, kısa içerik gerilir ya da üst üste biner (koşu 37 S-6/S-16). replace_prop size: 'shrink'"));
48
- }
49
- walkWithAncestors(roots, (node, ancestors) => {
50
- if (node.type !== 'scroll-view' || setOrBound(node, 'horizontal'))
51
- return;
52
- const fixed = appearanceOf(node).heightType === 'fixed';
53
- if (ancestors.some(item => item.type === 'scroll-view' && !setOrBound(item, 'horizontal')) && !fixed) {
54
- findings.push(rule('MOB-016', label(node), "dikey kaydırıcı başka bir dikey kaydırıcının içinde: iç kaydırma jesti dışarıdakiyle çakışır. İçtekine sabit boy (heightType: 'fixed') verin ya da kaldırın"));
55
- }
56
- else if (!fixed && !setOrBound(node, 'refreshable') && !setOrBound(node, 'stickyHeader') && !asNodes(node.children).some(child => child.states?.isSticky === true)) {
57
- findings.push(rule('MOB-016', label(node), "gerekçesiz dikey `scroll-view`: sayfa kökü zaten kaydırır. Kaldırın ya da gerekçesini verin — refreshable (çekerek yenile), stickyHeader / states.isSticky (yapışkan başlık), heightType: 'fixed' (sabit boylu iç liste)"));
58
- }
59
- });
60
- // MOB-017 — ekranda tek app-bar, ilk çizilen öğe.
61
- const appBars = collect(roots, node => node.type === 'app-bar', OVERLAY_TYPES);
62
- if (shell.kind === 'shell' && shell.appBar) {
63
- for (const node of appBars)
64
- findings.push(rule('MOB-017', label(node), 'kabuk app-bar veriyor; sayfadaki ikinci app-bar ekranda çift başlık çubuğu çizer. Kaldırın; sayfa başlığını kabuğun app-bar\'ına bağlayın (title)'));
65
- }
66
- else if (shell.kind !== 'unknown' && appBars.length > 0) {
67
- // Tepedeki app-bar: ilk çizilen öğenin kendisi ya da onun içindeki ilk app-bar (safe-area içi, saydam
68
- // çubuk tarifi `stack` → görsel + kap → app-bar).
69
- // Boş `safe-area` (kabuksuz sayfanın pay boşluğu) çizim yapmaz; tepedeki ilk içerikli öğeye bakılır.
70
- const first = firstDrawn(screen.filter(item => !(item.type === 'safe-area' && asNodes(item.children).length === 0)));
71
- const leading = first ? collect([first], item => item.type === 'app-bar', OVERLAY_TYPES)[0] : undefined;
72
- for (const node of appBars.filter(item => item !== leading || leading?.type !== 'app-bar')) {
73
- findings.push(rule('MOB-017', label(node), appBars.length > 1 && node !== appBars[0] ? 'ekranda ikinci app-bar: tek başlık çubuğu olmalı, kaldırın' : 'app-bar ilk çizilen öğe değil: başlık çubuğu ekranın tepesinde durur. Onu ilk sıraya (ya da tepedeki safe-area\'nın içine) taşıyın'));
74
- }
75
- }
76
- // MOB-018 — boş kap bırakılmaz (tepedeki/dipteki güvenli alan bilinçli bir paydır).
77
- walkWithAncestors(screen, (node, ancestors) => {
78
- const type = String(node.type ?? '');
79
- if (!MOBILE_DROPPABLE_TYPES.includes(type) || EMPTY_ALLOWED_TYPES.includes(type) || node.kind === 'widget')
80
- return;
81
- if (asNodes(node.children).length > 0 || boundVisibility(node))
82
- return;
83
- if (type === 'safe-area' && ancestors.length === 0)
84
- return;
85
- findings.push({ ...rule('MOB-018', label(node), `boş ${type}: ekranda boş şerit bırakır${type === 'safe-area' ? ` (edges: '${safeAreaEdges(node)}' payı kadar)` : ''}. İçeriğini ekleyin ya da silin`), severity: 'info' });
86
- });
87
- if (screenRoot)
88
- findings.push(...alignmentFindings(screenRoot, input.kind), ...widthFindings(screenRoot), ...scaleFindings(screenRoot));
89
- return findings;
90
- }
91
- /** Sütunda dizilen içerik (form alanları, düğmeler); satırda yan yana olmaları hata değildir. */
92
- const FIELD_TYPES = ['textinput', 'textarea', 'password', 'numberinput', 'dropdown', 'multi-select', 'datepicker', 'daterangepicker', 'datetimepicker', 'timepicker', 'fileInput', 'slider'];
93
- const BUTTON_TYPES = ['button', 'filebutton'];
94
- /** Metin gibi görünen düğmeler içerik genişliğinde durur (S8'in istisnası). */
95
- const INLINE_BUTTON_VARIANTS = ['text', 'link', 'ghost'];
96
- /** Sütun akışını taşıyan kap tipleri ve yazılmamış yönleri (`containerDirection`: safe-area/scroll-view sütun doğar). */
97
- const FLOW_TYPES = { container: 'row', form: 'row', 'safe-area': 'column', 'scroll-view': 'column' };
98
- const SPACING_SCALE = [0, 4, 8, 16, 24, 32];
99
- const RADIUS_SCALE = ['none', 'sm', 'md', 'lg', 'pill', '0', 0];
100
- /**
101
- * MOB-019 — kenar değişmezi (S6/S8): aynı kapsamda (ekran ya da yüzey kabı) sütun akışındaki öğelerin sol
102
- * kenarı eşittir. Zeminsiz kap ve saydam sarmalayıcı (tooltip) içeriğini kendi margin + padding'i kadar
103
- * içeri iter; yüzey kabı yeni kapsam açar, içeriği bilerek içeride durur. Hesap runtime'ın kutu modeliyle
104
- * (`nativeBoxModel.ts`), 01'in çizimde ölçtüğü sapmanın şemadan öngörüsüdür.
105
- */
106
- function alignmentFindings(root, kind) {
107
- const findings = [];
108
- const scopes = [root];
109
- for (let index = 0; index < scopes.length; index++) {
110
- const owner = scopes[index];
111
- const items = [];
112
- collectFlowItems(asNodes(owner.children), 0, [], [], items, kind);
113
- for (const item of items)
114
- if (isSurface(item.node) && flowDirection(item.node) === 'column')
115
- scopes.push(item.node);
116
- // Ortalanmış ya da sağa hizalı kapsamda öğeler kendi genişliğince yerleşir; sol kenar eşitliği aranmaz.
117
- if (!['center', 'right'].includes(String(propData(owner, 'horizontalAlign') ?? 'left')))
118
- findings.push(...insetFindings(items));
119
- }
120
- return findings;
121
- }
122
- function collectFlowItems(nodes, inset, chain, shifts, items, kind) {
123
- for (const node of nodes) {
124
- const type = String(node.type ?? '');
125
- if (MOBILE_OUT_OF_FLOW_TYPES.includes(type) || type === 'slot' || propData(node, 'isHidden') === true)
126
- continue;
127
- const margin = nativeMarginLeft(node);
128
- if (transparent(node)) {
129
- const shift = margin + nativePaddingLeft(node);
130
- collectFlowItems(asNodes(node.children), inset + shift, [...chain, node], [...shifts, shift], items, kind);
131
- }
132
- else {
133
- items.push({ node, inset: inset + margin, chain, shifts });
134
- }
135
- }
136
- }
137
- /** İçeriğini sütunda, sola ya da gerilerek dizen zeminsiz kap — kendi kutusu görünmez, içeriği görünür. */
138
- function transparent(node) {
139
- const type = String(node.type ?? '');
140
- if (MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(type))
141
- return true;
142
- if (!FLOW_TYPES[type] || isSurface(node))
143
- return false;
144
- return flowDirection(node) === 'column' && !['center', 'right'].includes(String(propData(node, 'horizontalAlign') ?? 'left'));
145
- }
146
- /**
147
- * Taban, kapsamın kendi sütunundaki (en sığ) öğelerdir — çoğunluk değil. Böylece "başlık + başlıklı grup"
148
- * düzeninde suçlu grubu içeri iten kaptır, başlık değil; gruba alan eklemek tabanı el değiştirmez.
149
- */
150
- function insetFindings(items) {
151
- if (items.length === 0)
152
- return [];
153
- const depth = Math.min(...items.map(item => item.chain.length));
154
- const counts = new Map();
155
- for (const item of items.filter(entry => entry.chain.length === depth))
156
- counts.set(item.inset, (counts.get(item.inset) ?? 0) + 1);
157
- const baseline = [...counts].sort((a, b) => b[1] - a[1] || a[0] - b[0])[0]?.[0] ?? 0;
158
- const reference = items.find(item => item.chain.length === depth && item.inset === baseline);
159
- const groups = new Map();
160
- for (const item of items.filter(entry => entry.inset !== baseline)) {
161
- // Suçlu: ortak zincirden sonra içeriği gerçekten iten ilk kap; hiçbiri itmiyorsa fark öğenin kendi margin'idir.
162
- const cause = item.chain.find((ancestor, index) => reference?.chain[index] !== ancestor && (item.shifts[index] ?? 0) !== 0) ?? item.node;
163
- groups.set(cause, [...(groups.get(cause) ?? []), item]);
164
- }
165
- return [...groups].map(([cause, members]) => {
166
- // Kabın kendi payı en az kayan üyededir; daha derindeki ek kayma iç kabın ayrı bulgusudur (kap düzelince görünür).
167
- const delta = members.map(member => member.inset - baseline).sort((a, b) => Math.abs(a) - Math.abs(b))[0] ?? 0;
168
- const where = `${Math.abs(delta)} dp ${delta > 0 ? 'içeride' : 'dışarıda'}`;
169
- const ids = members.slice(0, 4).map(member => label(member.node)).join(', ') + (members.length > 4 ? ', …' : '');
170
- if (members.length === 1 && cause === members[0]?.node) {
171
- return rule('MOB-019', label(cause), `kardeşlerinden ${where} başlıyor: kendi margin'i farklı. Aralık ebeveynin gap'inden gelsin — marginType'ı kardeşleriyle aynı yapın`);
172
- }
173
- const recipe = MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(String(cause.type ?? ''))
174
- ? "paddingType:'none' ve marginType:'none' yazın"
175
- : "grup görünsün isteniyorsa yüzey verin (surfaceStyle zemin ya da kenarlık — içerik bilerek içeride durur), istenmiyorsa paddingType:'none' ve marginType:'none' yazın";
176
- return rule('MOB-019', label(cause), `${String(cause.type)} içeriği (${ids}) kardeşlerinden ${where} başlıyor — margin ve padding'i içeriği iter. ${recipe}`);
177
- });
178
- }
179
- /**
180
- * MOB-020 — genişlik (S8/S9): (1) sola hizalı sütunda tam genişlik sınıfındaki öğelerin bir kısmı `fill`, bir
181
- * kısmı değilse fill olmayanlar; (2) form alanı taşıyan satır kabı (yönü yazılmamış container/form satırdır).
182
- */
183
- function widthFindings(root) {
184
- const findings = [];
185
- walkWithAncestors([root], node => {
186
- const children = asNodes(node.children).filter(child => propData(child, 'isHidden') !== true);
187
- if (!FLOW_TYPES[String(node.type ?? '')])
188
- return;
189
- if (flowDirection(node) === 'row') {
190
- // Yazılmış `row` bilinçlidir (arama satırı: alan + düğme); yazılmamış yön unutulmuş sütundur.
191
- if (node !== root && propData(node, 'direction') === undefined && children.some(child => FIELD_TYPES.includes(String(child.type ?? '')))) {
192
- findings.push(rule('MOB-020', label(node), "yönü yazılmamış kap form alanı taşıyor — yazılmamış yön satırdır, alanlar yan yana sıkışır. direction:'column' ve horizontalAlign:'stretch' yazın"));
193
- }
194
- return;
195
- }
196
- if (propData(node, 'horizontalAlign') === 'stretch')
197
- return;
198
- const wide = children.filter(fullWidthClass);
199
- if (!wide.some(fills))
200
- return;
201
- for (const child of wide.filter(child => !fills(child))) {
202
- findings.push(rule('MOB-020', label(child), `kardeşleri tam genişlik (widthType: 'fill'), bu öğe değil: sağ kenarı hizadan kopar. widthType:'fill' yazın`));
203
- }
204
- });
205
- return findings;
206
- }
207
- /**
208
- * MOB-021 — ölçek (S5/S7/S11): özel boşluk ve sayısal gap ölçekte, yarıçap ölçek adıyla, yazı boyu seviyeyle.
209
- * Gap jetonları (`small`/`medium`/`large`) zaten ölçektir ve sözleşmenin ritmi üçünü de kullanır; sayılmaz.
210
- */
211
- function scaleFindings(root) {
212
- const findings = [];
213
- walkWithAncestors([root], node => {
214
- const appearance = appearanceOf(node);
215
- const offScale = ['margin', 'padding'].flatMap(layer => {
216
- if (appearance[`${layer}Type`] !== 'custom')
217
- return [];
218
- const values = appearance[`${layer}Values`];
219
- const sides = ['top', 'right', 'bottom', 'left'].map(side => values?.[side]);
220
- // `resolveSpacing`: tek değer yalnız yazılmamış kenarın yedeğidir.
221
- const written = [...sides, ...(sides.some(side => side === undefined || side === null) ? [appearance[`${layer}Value`]] : [])];
222
- const numbers = written.filter(value => value !== undefined && value !== null && value !== '').map(value => Number(String(value).replace(/px$/, '')));
223
- return [...new Set(numbers.filter(value => !SPACING_SCALE.includes(value)))].map(value => `${layer} ${value}`);
224
- });
225
- const radius = nested(node, 'surfaceStyle', 'radius');
226
- const fontSize = nested(node, 'typographyStyle', 'fontSize');
227
- const gap = propData(node, 'gap');
228
- const notes = [
229
- ...offScale,
230
- ...(radius !== undefined && !RADIUS_SCALE.includes(radius) && !/^var\(--ng-radius-/.test(String(radius)) ? [`radius ${String(radius)} (sm | md | lg | pill)`] : []),
231
- ...(typeof fontSize === 'number' || (typeof fontSize === 'string' && /^\d+(\.\d+)?(px)?$/.test(fontSize.trim())) ? [`fontSize ${String(fontSize)} (text seviyesi)`] : []),
232
- ...(typeof gap === 'number' && !SPACING_SCALE.includes(gap) ? [`gap ${gap}`] : [])
233
- ];
234
- if (notes.length > 0)
235
- findings.push({ ...rule('MOB-021', label(node), `ölçek dışı değer: ${notes.join(', ')} — boşluk 0/4/8/16/24/32, yarıçap ve yazı temadan gelir`), severity: 'info' });
236
- });
237
- return findings;
238
- }
239
- function fullWidthClass(node) {
240
- const type = String(node.type ?? '');
241
- if (FIELD_TYPES.includes(type))
242
- return true;
243
- if (!BUTTON_TYPES.includes(type))
244
- return false;
245
- return propData(node, 'size') !== 'icon' && !INLINE_BUTTON_VARIANTS.includes(String(propData(node, 'variant') ?? ''));
246
- }
247
- function fills(node) {
248
- return appearanceOf(node).widthType === 'fill' || FULL_WIDTH_TYPES.includes(String(node.type ?? ''));
249
- }
250
- function flowDirection(node) {
251
- if (node.type === 'scroll-view' && setOrBound(node, 'horizontal'))
252
- return 'row';
253
- const written = propData(node, 'direction');
254
- return written === 'row' || written === 'column' ? written : FLOW_TYPES[String(node.type ?? '')] ?? 'column';
255
- }
256
- /** Görünür yüzey: zemin, kenarlık ya da gölge taşıyan kap (`surfaceStyle`, `SURFACE_KEYS`). */
257
- function isSurface(node) {
258
- const style = propData(node, 'surfaceStyle');
259
- if (!style || typeof style !== 'object')
260
- return false;
261
- return Object.entries(style).some(([key, value]) => /^(background|border|shadow)/.test(key) && unwrap(value) !== undefined && unwrap(value) !== null && unwrap(value) !== '');
262
- }
263
- function nested(node, group, key) {
264
- const style = propData(node, group);
265
- return style && typeof style === 'object' ? unwrap(style[key]) : undefined;
266
- }
267
- function unwrap(value) {
268
- return value && typeof value === 'object' && 'data' in value ? value.data : value;
269
- }
270
- /**
271
- * Yazımdan önce de var olan bulgular tekrar edilmez: yama yalnız getirdiğini söyler. Aynı düğümün aynı kuralı
272
- * mesajı değişse de (ör. app-bar sayısı) yeni sayılmaz. Hedefsiz bulgu bir durum bilgisidir (kabuk okunamadı)
273
- * ve her yazımda söylenir — yoksa denetim sessizce atlanırdı.
274
- */
275
- export function newFindings(before, after) {
276
- const known = new Set(before.map(key));
277
- return after.filter(finding => finding.target === undefined || !known.has(key(finding)));
278
- }
279
- function key(finding) {
280
- return `${finding.code}|${finding.target ?? ''}`;
281
- }
282
- function rule(code, target, message) {
283
- return { code: MOBILE_RULES[code].id, severity: MOBILE_RULES[code].severity, target, message, guide: "platform_guide('mobile-layout')" };
284
- }
285
- function asNodes(value) {
286
- return Array.isArray(value) ? value.filter((item) => !!item && typeof item === 'object') : [];
287
- }
288
- function label(node) {
289
- return String(node?.elementId ?? node?.name ?? node?.type ?? '?');
290
- }
291
- function appearanceOf(node) {
292
- return node.appearance && typeof node.appearance === 'object' ? node.appearance : {};
293
- }
294
- function propData(node, prop) {
295
- const value = node.props?.[prop];
296
- return value && typeof value === 'object' && 'data' in value ? value.data : value;
297
- }
298
- /** Prop açık mı — ifadeye bağlıysa değeri çalışma anında belli olur, açık sayılır (yanlış uyarı vermemek için). */
299
- function setOrBound(node, prop) {
300
- const raw = node.props?.[prop];
301
- if (raw && typeof raw === 'object' && (raw.type === 'code' || raw.type === 'translation'))
302
- return true;
303
- const value = propData(node, prop);
304
- return value === true || value === 'true';
305
- }
306
- function boundVisibility(node) {
307
- const hidden = node.props?.isHidden;
308
- return !!hidden && typeof hidden === 'object' && (hidden.type === 'code' || hidden.type === 'translation');
309
- }
310
- function someNode(nodes, test) {
311
- return nodes.some(node => test(node) || someNode(asNodes(node.children), test));
312
- }
313
- function collect(nodes, test, skipInside = []) {
314
- return nodes.flatMap(node => [
315
- ...(test(node) ? [node] : []),
316
- ...(skipInside.includes(String(node.type ?? '')) ? [] : collect(asNodes(node.children), test, skipInside))
317
- ]);
318
- }
319
- function walkWithAncestors(nodes, visit, ancestors = []) {
320
- for (const node of nodes) {
321
- visit(node, ancestors);
322
- walkWithAncestors(asNodes(node.children), visit, [...ancestors, node]);
323
- }
324
- }