@appaflytech/zeyrek-ai-mcp 0.0.31 → 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 (205) 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 +233 -568
  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 -366
  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/middleware-authenticate.json +1 -1
  30. package/dist/resources/examples/page-detail-profile.json +1 -2
  31. package/dist/resources/examples/page-dynamic-form.json +2 -3
  32. package/dist/resources/examples/page-flex-sections.json +1 -1
  33. package/dist/resources/examples/page-html-embed.json +1 -1
  34. package/dist/resources/examples/workflow-basic.json +2 -8
  35. package/dist/resources/guides/component.md +5 -92
  36. package/dist/resources/guides/entity.md +8 -50
  37. package/dist/resources/guides/expressions.md +5 -14
  38. package/dist/resources/guides/functions.md +1 -1
  39. package/dist/resources/guides/getting-started.md +1 -38
  40. package/dist/resources/guides/layout.md +58 -55
  41. package/dist/resources/guides/merge.md +3 -37
  42. package/dist/resources/guides/microservice.md +2 -87
  43. package/dist/resources/guides/middleware.md +4 -68
  44. package/dist/resources/guides/page.md +75 -456
  45. package/dist/resources/guides/theme.md +2 -8
  46. package/dist/resources/guides/translation.md +1 -1
  47. package/dist/resources/guides/workflow.md +1 -58
  48. package/dist/resources/rules.js +5 -11
  49. package/dist/resources/schema-fingerprint.json +5 -7
  50. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  51. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  52. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  53. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -146
  54. package/dist/resources/schemas/DataJobDto.json +1 -6
  55. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
  56. package/dist/resources/schemas/MicroserviceDto.json +3 -15
  57. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  58. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -25
  59. package/dist/resources/schemas/StepDto.json +1 -25
  60. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  61. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  62. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  63. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  64. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -146
  65. package/dist/server.js +1 -27
  66. package/dist/tools/access.js +2 -369
  67. package/dist/tools/applications.js +3 -60
  68. package/dist/tools/common.js +0 -7
  69. package/dist/tools/components.js +14 -118
  70. package/dist/tools/datasources.js +3 -3
  71. package/dist/tools/dependencies.js +1 -1
  72. package/dist/tools/deployments.js +3 -38
  73. package/dist/tools/designSchemaPatch.js +18 -34
  74. package/dist/tools/dtos.js +5 -36
  75. package/dist/tools/entities.js +12 -79
  76. package/dist/tools/externalComponents.js +5 -26
  77. package/dist/tools/flows.js +14 -122
  78. package/dist/tools/folders.js +1 -1
  79. package/dist/tools/imports.js +2 -5
  80. package/dist/tools/layouts.js +17 -50
  81. package/dist/tools/merges.js +2 -2
  82. package/dist/tools/microservices.js +25 -77
  83. package/dist/tools/pages.js +112 -465
  84. package/dist/tools/platformInfo.js +8 -27
  85. package/dist/tools/projects.js +9 -71
  86. package/dist/tools/rows.js +13 -69
  87. package/dist/tools/schemaOps.js +1 -61
  88. package/dist/tools/screenWrite.js +8 -37
  89. package/dist/tools/screens.js +5 -95
  90. package/dist/tools/templates.js +1 -26
  91. package/dist/tools/translations.js +1 -1
  92. package/dist/tools/variables.js +5 -11
  93. package/dist/tools/workflows.js +5 -10
  94. package/package.json +1 -1
  95. package/dist/knowledge/access.js +0 -802
  96. package/dist/knowledge/case.js +0 -41
  97. package/dist/knowledge/connectDataBind.js +0 -819
  98. package/dist/knowledge/connectDataDefinition.js +0 -341
  99. package/dist/knowledge/connectDataExecute.js +0 -93
  100. package/dist/knowledge/connectDataRules.js +0 -128
  101. package/dist/knowledge/decision.js +0 -121
  102. package/dist/knowledge/expressionPrelude.js +0 -59
  103. package/dist/knowledge/microserviceParameters.js +0 -58
  104. package/dist/knowledge/mining.js +0 -94
  105. package/dist/knowledge/mobileComponentCatalog.js +0 -859
  106. package/dist/knowledge/mobileComponentEvents.js +0 -239
  107. package/dist/knowledge/platform/lint.js +0 -385
  108. package/dist/knowledge/platform/mobile.js +0 -142
  109. package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
  110. package/dist/knowledge/platform/mobileRules.js +0 -231
  111. package/dist/knowledge/platform/mobileSpacing.js +0 -82
  112. package/dist/knowledge/platform/mobileTabs.js +0 -108
  113. package/dist/knowledge/platform/nativeBoxModel.js +0 -44
  114. package/dist/knowledge/platform/profile.js +0 -25
  115. package/dist/knowledge/platform/resolve.js +0 -122
  116. package/dist/knowledge/platform/web.js +0 -39
  117. package/dist/knowledge/process.js +0 -510
  118. package/dist/knowledge/rowFilter.js +0 -43
  119. package/dist/knowledge/styleResolveNative.js +0 -527
  120. package/dist/platform/argumentAliases.js +0 -128
  121. package/dist/platform/bodyFile.js +0 -103
  122. package/dist/platform/capture/actions.js +0 -333
  123. package/dist/platform/capture/browser.js +0 -1
  124. package/dist/platform/capture/console-log.js +0 -122
  125. package/dist/platform/capture/index.js +0 -11
  126. package/dist/platform/capture/readiness.js +0 -103
  127. package/dist/platform/capture/summary.js +0 -77
  128. package/dist/platform/capture/target.js +0 -5
  129. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  130. package/dist/platform/capture/targets/studio-web.js +0 -5
  131. package/dist/platform/impersonation.js +0 -45
  132. package/dist/platform/jsonArray.js +0 -24
  133. package/dist/platform/schemaReadMemory.js +0 -60
  134. package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
  135. package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
  136. package/dist/resources/examples/bpm-satinalma.json +0 -376
  137. package/dist/resources/examples/connect-data-convert-verify.json +0 -107
  138. package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
  139. package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
  140. package/dist/resources/examples/connect-data-query-params.json +0 -50
  141. package/dist/resources/examples/connect-data-table-entity.json +0 -147
  142. package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
  143. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
  144. package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
  145. package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
  146. package/dist/resources/examples/mobile-form-screen.json +0 -365
  147. package/dist/resources/examples/mobile-list-detail-form.json +0 -672
  148. package/dist/resources/guides/access.md +0 -810
  149. package/dist/resources/guides/bpm.md +0 -691
  150. package/dist/resources/guides/case.md +0 -153
  151. package/dist/resources/guides/decision.md +0 -185
  152. package/dist/resources/guides/mobile-layout.md +0 -193
  153. package/dist/resources/guides/mobile.md +0 -495
  154. package/dist/resources/read.js +0 -41
  155. package/dist/resources/schemas/AccessExplanationDto.json +0 -144
  156. package/dist/resources/schemas/AccessMatrixDto.json +0 -170
  157. package/dist/resources/schemas/AccessPermissionDto.json +0 -57
  158. package/dist/resources/schemas/AccessPolicyDto.json +0 -223
  159. package/dist/resources/schemas/AccessResourceDto.json +0 -86
  160. package/dist/resources/schemas/AppSessionDto.json +0 -53
  161. package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
  162. package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
  163. package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
  164. package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
  165. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
  166. package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
  167. package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
  168. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
  169. package/dist/resources/schemas/DecisionDto.json +0 -302
  170. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
  171. package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
  172. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
  173. package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
  174. package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
  175. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
  176. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
  177. package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
  178. package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
  179. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
  180. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
  181. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
  182. package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
  183. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
  184. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
  185. package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
  186. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
  187. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
  188. package/dist/tools/accessOperations.js +0 -135
  189. package/dist/tools/accessPolicies.js +0 -170
  190. package/dist/tools/accessRuntime.js +0 -203
  191. package/dist/tools/bpm.js +0 -343
  192. package/dist/tools/bpmGovernance.js +0 -266
  193. package/dist/tools/bpmOperations.js +0 -230
  194. package/dist/tools/bpmReporting.js +0 -154
  195. package/dist/tools/bpmRouting.js +0 -150
  196. package/dist/tools/bpmRuntime.js +0 -396
  197. package/dist/tools/case.js +0 -178
  198. package/dist/tools/caseRuntime.js +0 -89
  199. package/dist/tools/connectData.js +0 -449
  200. package/dist/tools/connectors.js +0 -193
  201. package/dist/tools/decisions.js +0 -206
  202. package/dist/tools/mobile.js +0 -221
  203. package/dist/tools/modules.js +0 -48
  204. package/dist/tools/organizationStructure.js +0 -137
  205. package/dist/tools/profileWrite.js +0 -92
@@ -19,16 +19,7 @@ export const componentEventContracts = {
19
19
  rowChange: { payload: {}, reads: 'self.selectedRow (düzenlenen satır, YENİ değeriyle) — satır nesnesi self.rows içinde de yerinde güncellenir; değer gerçekten değişmediyse olay çalışmaz' }
20
20
  },
21
21
  datatable: {
22
- mount: { payload: {}, reads: 'self.filters · self.sortField · self.sortType · self.offset · self.limit' },
23
- save: {
24
- payload: {},
25
- reads: "self.changeset = { inserted:[{ rowId, values }], updated:[{ rowId, id, patch }], deleted:[{ rowId, id }] } (komponent olaydan ÖNCE yazar). Bağlı tabloda (rows connectData) yazmayı runtime bağlantısı yapar, akış ardından koşar; hatalar self.rowErrors [{ rowId, field?, message }]. İşlemin bağlamı da olaydan ÖNCE yazılır (2026-09-21): self.operation (rowAdd|rowEdit|cellEdit|rowDelete|bulkEdit|bulkDelete|batchSave), self.editedRows (tam satırlar), self.deletedRows (silinen tam satırlar), self.editedCell ({ rowId, field, value, previousValue }, yalnız cellEdit), self.bulkPatch (yalnız bulkEdit). Her işlem için ayrı olay yoktur; tek save akışı self.operation ile ayırır"
26
- },
27
- discard: { payload: {}, reads: 'self.discardedRowIds (vazgeçilen taslak satır kimlikleri)' },
28
- rowClick: { payload: {}, reads: 'self.selectedRow (tıklanan satırın sunucu satırı; kaydedilmemiş yeni satırda taslak değerleri). Veri hücresine tıklamada yayılır — onay kutusu, ✏/🗑 ve aksiyon düğmeleri tetiklemez; çift tık önce iki rowClick üretir' },
29
- rowDoubleClick: { payload: {}, reads: 'self.selectedRow (çift tıklanan satır). Düzenleme kipi açıksa satır/hücre ayrıca düzenlemeye açılır; editMode none tabloda detay sayfasına gitmek gibi akışlar için' },
30
- cellSelectionChange: { payload: {}, reads: "self.selectedCells = { column, rowIds[], anchorRowId } (aynı kolonda Shift aralık, Ctrl/Alt tekil; yalnız düzenlenebilen hücreler)" },
31
- rowSelectionChange: { payload: {}, reads: 'self.selectedRows (seçili satırların sunucu satırları; onay kutusu kolonu yalnız bulkActions açık ve editMode none değilken vardır)' }
22
+ mount: { payload: {}, reads: 'self.filters · self.sortField · self.sortType · self.offset · self.limit' }
32
23
  },
33
24
  button: { mount: NO_DATA, click: NO_DATA },
34
25
  card: {
@@ -114,7 +105,7 @@ export const componentEventContracts = {
114
105
  comments: {
115
106
  mount: NO_DATA,
116
107
  submit: {
117
- payload: { text: 'string', parentId: "string (yanıtsa üst yorumun id’si, kök yorumda '')", mentions: 'string[] (metinden çözülmüş @kişi listesi)', author: 'string (currentUser)', process: "{ scope: 'task' | 'instance', targetId, visibility, body (@Ad → @<id>), mentionUserIds } | null (serbest yorumda null)" },
108
+ payload: { text: 'string', parentId: "string (yanıtsa üst yorumun id’si, kök yorumda '')", mentions: 'string[] (metinden çözülmüş @kişi listesi)', author: 'string (currentUser)' },
118
109
  reads: 'Komponent yorumu ANINDA iyimser olarak listeye ekler (bekleyen katman); kalıcılık akışın işidir — rows tazelenmezse bekleyen satır bir sonraki içerik değişiminde kaybolur'
119
110
  },
120
111
  like: { payload: { id: 'string', liked: 'boolean (yeni durum)', author: 'string (currentUser — işlemi yapan)' }, reads: 'beğeni de iyimserdir; sayaç rows tazelenene kadar yerel. isDisabled/isReadonly/isViewmode açıkken yayılmaz' },
@@ -125,8 +116,9 @@ export const componentEventContracts = {
125
116
  layout: { mount: NO_DATA, logout: NO_DATA },
126
117
  'layout-shell': {
127
118
  mount: NO_DATA,
128
- // LayoutShell.svelte toggle(): dispatch('emit', { name, data, params }) — `params` VERİLDİĞİ
129
- // için bu olay istisnadır: sidebarOpen hem self.sidebarOpen'dan hem event.data'dan okunur.
119
+ // LayoutShell.svelte announce(): sidebarOpen her değiştiğinde (overlay, Escape, akış, Durum
120
+ // bağlaması — SHELL-009) dispatch('emit', { name, data, params }). `params` VERİLDİĞİ için bu olay
121
+ // istisnadır: sidebarOpen hem self.sidebarOpen'dan hem event.data'dan okunur.
130
122
  toggle: { payload: { sidebarOpen: 'boolean (panelin YENİ durumu)' }, reads: 'self.sidebarOpen; params verildiği için event.data.sidebarOpen de dolar' }
131
123
  },
132
124
  'layout-region': { mount: NO_DATA },
@@ -370,7 +362,6 @@ export const componentEventContracts = {
370
362
  sidebar: { mount: NO_DATA },
371
363
  html: { mount: NO_DATA },
372
364
  view: { mount: NO_DATA },
373
- 'datatable-cell': { mount: NO_DATA },
374
365
  'widget-area': { mount: NO_DATA },
375
366
  'document-editor': { mount: NO_DATA },
376
367
  video: {
@@ -443,8 +434,11 @@ export const componentEventContracts = {
443
434
  'breadcrumb-title': {
444
435
  mount: NO_DATA,
445
436
  clickItem: {
446
- payload: { currentItem: '{ key, text, flow, isHidden } — tıklanan iz öğesi (satır içi ya da … menüsünden, ikisi de aynı yükü yayar)' },
447
- reads: 'self.currentItem — öğe düğümün prop’una da yazılır; akış hangi adımın seçildiğini self.currentItem.key ile okur. Öğenin kendi flow’u varsa ÖNCE o çalışır, sonra clickItem yayılır.'
437
+ payload: {
438
+ currentItem: '{ key, text, flow, isHidden } — tıklanan iz öğesi (satır içi ya da … menüsünden, ikisi de aynı yükü yayar)',
439
+ index: 'number (items dizisindeki GERÇEK yer; gizli öğeler kaydırmaz)'
440
+ },
441
+ reads: "event.data.currentItem.key ve event.data.index ile oku (2026-09-30'dan önce event.data BOŞ geliyordu — QA BT-002). Öğenin kendi flow'u varsa ÖNCE o çalışır ve AYNI yükü event.data'dan alır, sonra clickItem yayılır. currentItem düğüme de yazılır ama self.currentItem ifade yüzeyine yalnız kayıtlı düğüm o anahtarı taşıyorsa çıkar (ARC-096) — güvenilir yol event.data'dır; index self'e YAZILMAZ. breadcrumb'ın aksine olay bağlı akış olmasa da yayılır (tıklama düğüme currentItem yazar)."
448
442
  },
449
443
  clickRoot: {
450
444
  payload: {},
@@ -464,11 +458,11 @@ export const componentEventContracts = {
464
458
  },
465
459
  chart: {
466
460
  mount: NO_DATA,
467
- dataPointClick: { payload: { name: 'string (kategori); scatter grafiğinde x değeri', seriesName: 'string', value: 'number | null; scatter grafiğinde y', dataIndex: 'number (çizimdeki sıra)', row: 'kaynak satırın TAMAMI | null' }, reads: 'YALNIZ seri noktasına basışta; eksen etiketi tıklaması axisLabelClick’tir (2026-09-16 öncesinde o da boş bir dataPointClick üretiyordu), hedef çizgisi (referenceLines) ve bant (plotBands) olay değildir. row kategori adıyla — scatter’da x değeriyle — bulunur (nullMode skip ya da huni sıralaması dataIndex`i kaydırır); kategorisi olmayan noktada (histogram kutusu, radar serisi, funnelflow düğümü/bağı) null. scatter noktası ECharts tarafında [x, y, size] dizisidir, yük tek değere indirgenir: name = x, value = y' },
461
+ dataPointClick: { payload: { name: 'string (kategori); scatter grafiğinde x değeri', seriesName: 'string', value: 'number | null; scatter grafiğinde y', dataIndex: 'number', row: 'kaynak satırın TAMAMI | null' }, reads: 'YALNIZ seri noktasında yayılır; eksen etiketi tıklaması axisLabelClick’tir (2026-09-16 öncesinde o da boş bir dataPointClick üretiyordu). scatter noktası ECharts tarafında [x, y, size] dizisidir, yük tek değere indirgenir: name = x, value = y' },
468
462
  breadcrumbClick: { payload: { filterValue: 'hedef seviyenin filtre değeri (kök seviyede undefined)' } },
469
- selectionChange: { payload: { selected: 'seçili veri noktaları' }, reads: "yalnız selectionMode 'single'/'multi' iken; 'none' iken ECharts'ın her tıklamada attığı boş seçim yayılmaz (2026-09-25 öncesi yayılıyordu)" },
463
+ selectionChange: { payload: { selected: 'seçili veri noktaları' } },
470
464
  legendClick: { payload: { name: 'string (seri adı)', selected: 'seri → görünürlük haritası' } },
471
- axisLabelClick: { payload: { value: 'string (tıklanan eksen etiketi)' }, reads: 'yalnız kategori ekseninin etiketine basışta; kategori ekseni hangi eksene düşerse oradan gelir — yatay yönelimde yAxis (2026-09-16 öncesinde yatayda hiç yayılmıyordu). Nokta tıklaması ve eksen başlığı (categoryAxis.title) bu olayı yaymaz — 2026-09-25 öncesinde nesne sorgusu süzmediği için her nokta tıklamasında noktanın değeriyle yayılıyordu' },
465
+ axisLabelClick: { payload: { value: 'string (tıklanan eksen etiketi)' }, reads: 'Kategori ekseni hangi eksene düşerse oradan gelir — yatay yönelimde yAxis (2026-09-16 öncesinde yatayda hiç yayılmıyordu)' },
472
466
  zoom: { payload: { start: 'number | null (görünen aralığın başlangıç yüzdesi)', end: 'number | null (bitiş yüzdesi)', startValue: 'number | null (eksenin kendi birimindeki mutlak alt sınır)', endValue: 'number | null (mutlak üst sınır)' }, reads: 'Yük normalize edilir: ECharts iki dataZoom bileşeniyle alanları batch içine koysa da akış her zaman bu dört alanı görür. Aralığı sorguya çevirecekseniz yüzdeleri değil startValue/endValue kullanın (2026-09-16)' },
473
467
  refresh: NO_DATA,
474
468
  retry: NO_DATA
@@ -476,7 +470,7 @@ export const componentEventContracts = {
476
470
  checkboxgroup: { mount: NO_DATA, change: valueChange('string[] (seçili değerler)'), focus: NO_DATA, blur: NO_DATA },
477
471
  emptystate: {
478
472
  mount: NO_DATA,
479
- actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "aksiyonun kendi flow'u zaten doğrudan çalışır; index GÖRÜNEN aksiyonların sırasıdır (isHidden olanlar elenmiş). href dolu satırda olay YİNE yayılır ama aynı sekmede gezinme sayfayı değiştirir ve olayın sayfa-içi sonucu görünmeden kaybolur — ikisini birlikte görmek için satırda isNewTabLink aç" }
473
+ actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "event.data.text / event.data.index (2026-10-01, QA ES-005; self.text / self.index de yazılır). Aksiyonun kendi flow'u varsa tıklamada YALNIZ o akış koşar, bu olaya bağlı akış kendi flow'u olmayan aksiyonlarda koşar; index GÖRÜNEN aksiyonların sırasıdır (isHidden olanlar elenmiş). href dolu satırda olay YİNE yayılır ama aynı sekmede gezinme sayfayı değiştirir ve olayın sayfa-içi sonucu görünmeden kaybolur — ikisini birlikte görmek için satırda isNewTabLink aç" }
480
474
  },
481
475
  iframe: {
482
476
  mount: NO_DATA,
@@ -548,7 +542,7 @@ export const componentEventContracts = {
548
542
  mount: NO_DATA,
549
543
  actionClick: {
550
544
  payload: { text: 'string (aksiyonun metni)', index: 'number' },
551
- reads: "aksiyonun kendi flow'u zaten doğrudan çalışır. index GÖRÜNEN sıradır: isHidden satırlar çizilmeden ELENİR, yani gizli satırı olan bir listede index actions dizisindeki sıra DEĞİLDİR — akışı sıraya değil text'e ya da aksiyonun kendi flow'una bağla. isLoading açıkken HİÇ yayılmaz (iskelet çizilirken düğme de yoktur); isDisabled satır da yaymaz"
545
+ reads: "event.data.text / event.data.index (2026-10-01, QA RS-004; self.text / self.index de yazılır). Aksiyonun kendi flow'u varsa tıklamada YALNIZ o akış koşar, bu olaya bağlı akış kendi flow'u olmayan aksiyonlarda koşar. index GÖRÜNEN sıradır: isHidden satırlar çizilmeden ELENİR, yani gizli satırı olan bir listede index actions dizisindeki sıra DEĞİLDİR — akışı sıraya değil text'e ya da aksiyonun kendi flow'una bağla. isLoading açıkken HİÇ yayılmaz (iskelet çizilirken düğme de yoktur); isDisabled satır da yaymaz"
552
546
  }
553
547
  },
554
548
  slider: {
@@ -590,10 +584,10 @@ export const componentEventContracts = {
590
584
  },
591
585
  close: {
592
586
  payload: { reason: "'timeout' | 'user' | 'programmatic'" },
593
- reads: 'Toast kapanınca yayılır; akışta self.reason ile okunur. timeout = süre doldu; user = kullanıcı kapattı (× düğmesi, closeOnClick tıklaması, kaydırma, Escape, aksiyon düğmesi, Clear all); programmatic = koddan dismiss/dismissAll. duplicates ile atılan tetikleme ve delay beklerken iptal edilen toast close YAYMAZ'
587
+ reads: 'Toast kapanınca yayılır; neden akışta event.data.reason ile okunur (2026-10-01, QA TS-006; self.reason da yazılmaya devam eder). timeout = süre doldu; user = kullanıcı kapattı (× düğmesi, closeOnClick tıklaması, kaydırma, Escape, aksiyon düğmesi, Clear all); programmatic = koddan dismiss/dismissAll. duplicates ile atılan tetikleme ve delay beklerken iptal edilen toast close YAYMAZ'
594
588
  },
595
589
  click: NO_DATA,
596
- actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "aksiyonun kendi flow'u zaten doğrudan çalışır" }
590
+ actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "event.data.text / event.data.index (2026-10-01, QA TS-006). Yalnız kendi flow'u OLMAYAN aksiyonda koşar: aksiyonun flow'u varsa tıklamada o akış çalışır, bu olaya bağlı akış çalışmaz" }
597
591
  },
598
592
  'qm-header': {
599
593
  mount: NO_DATA,
@@ -725,133 +719,19 @@ export const componentEventContracts = {
725
719
  payload: { currentStep: 'number (engellenen adım)', previous: 'number (aynı değer)' },
726
720
  reads: 'YALNIZ İleri düğmesinden yayılır; engellenen başlık tıklaması sessiz kalır'
727
721
  }
728
- },
729
- 'task-inbox': {
730
- mount: NO_DATA,
731
- tabChange: {
732
- payload: { tab: "string (mine | group | delegated | started | done | planned)", scope: 'string (app | realm)' },
733
- reads: "setter: activeTab self'e yazılır; tab ve scope event.data ile gelir"
734
- },
735
- taskClick: { payload: { taskId: 'string', row: 'tıklanan satırın TAMAMI' }, reads: "self'e yazılmaz — event.data ile oku" },
736
- claim: { payload: { taskId: 'string', version: 'string (optimistic sürüm)', row: 'satırın TAMAMI' }, reads: "self'e yazılmaz — event.data ile oku; process.task.claim { id: taskId, version }" },
737
- bulkClaim: { payload: { taskIds: 'string[]', rows: 'seçili ve üstlenilebilir satırlar' }, reads: "self'e yazılmaz — event.data ile oku" },
738
- search: { payload: { q: 'string (kırpılmış, boş gönderilmez)' }, reads: "self'e yazılmaz — event.data ile oku; rows ← process.task.list { tab, q } sonucu; appliedFilter sistem ucunda yok — rozet gerekiyorsa Page.bpm.tasks(tab, { q }).appliedFilter" },
739
- clearFilter: { payload: {}, reads: "self'e yazılmaz; appliedFilter ← null ve liste q'suz yeniden okunur" }
740
- },
741
- 'task-actions': {
742
- mount: NO_DATA,
743
- action: {
744
- payload: { action: "string (approve | reject | complete | request-info | provide-info | reassign | sign | start-assessment | submit-assessment | özel eylem id)", taskId: 'string', version: "string? (start-assessment ve submit-assessment yükünde YOKTUR: sınavı açmak görevi üstlenir ve sürümü ilerletir, panelin çizildiği sürüm bayattır — gönderilse 409 alınır)", comment: 'string?', payload: "object? (sign: payload.signature = { meaning, email?, password }; submit-assessment: payload.answers = { soruId: cevap })", targetUserId: 'string? (reassign)' },
745
- reads: "self'e yazılmaz — event.data ile oku; boş alanlar yükte hiç yoktur"
746
- },
747
- assistantSummary: { payload: { taskId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; assistant ← await Page.bpm.task(taskId).assistant()" },
748
- assistantAsk: { payload: { taskId: 'string', question: 'string (kırpılmış)' }, reads: "self'e yazılmaz — event.data ile oku; assistant ← await Page.bpm.task(taskId).assistant(question)" }
749
- },
750
- 'process-timeline': {
751
- mount: NO_DATA,
752
- clickItem: { payload: { currentItem: 'tıklanan satır ({ key, kind, nodeId, taskId, title, actor, decision, round, isSuperseded })', index: 'number' }, reads: "self'e yazılmaz — event.data ile oku" },
753
- exportLedger: { payload: {}, reads: 'yük yok — akış Page.bpm.instance(instanceId).exportLedger() ile CSV metnini alır' },
754
- verifyLedger: { payload: {}, reads: "yük yok — akış process.instance.ledger.verify { id: instanceId } sonucunu (isValid, brokenAtSequence) ledgerStatus/brokenAtSequence'e yazar" }
755
- },
756
- 'process-monitor': {
757
- mount: NO_DATA,
758
- filter: {
759
- payload: { filter: 'object (güncel süzgeç: definitionId, state, unitId, priority, slaBreached …)', query: 'object (process.instance.list için yalnız sunucu alanları)' },
760
- reads: "event.data ile oku; filter prop'u komponent içinde de güncellenir"
761
- },
762
- clickItem: { payload: { currentItem: 'tıklanan satırın TAMAMI (currentNodes, ageHours, slaStatus dahil)', index: 'number' }, reads: "self'e yazılmaz — event.data ile oku" },
763
- instanceAction: {
764
- payload: { operation: 'string (retry | skip | assign)', instanceId: 'string', nodeId: 'string? (ilk bekleyen adım)', taskIds: 'string[] (sahipsiz görevler)', reason: 'string?', targetUserId: 'string? (assign)', dueAt: 'string?', targetNodeId: 'string?' },
765
- reads: "self'e yazılmaz — event.data ile oku; retry → process.instance.retry { id, reason }, skip → process.instance.skip { id, nodeId, reason }, assign → process.batch.execute { kind: 'reassign', taskIds, targetUserId, reason }"
766
- },
767
- bulkAction: {
768
- payload: { operation: 'string (reassign | extend-due | suspend | jump | cancel)', instanceIds: 'string[] (uygun durumdaki seçili örnekler)', taskIds: 'string[] (yalnız reassign/extend-due)', fromNodeIds: 'object? (yalnız jump: { instanceId: bekleyen ilk adım })', reason: 'string?', targetUserId: 'string?', dueAt: 'string? (UTC ISO; yerel girişten çevrilir)', targetNodeId: 'string?' },
769
- reads: "self'e yazılmaz — event.data ile oku; boş alanlar yükte hiç yoktur. reassign/extend-due → process.batch.execute { kind: operation, taskIds, … }; diğerleri örnek başına process.instance.<operation> { id, … } (jump: { fromNodeId: fromNodeIds[id], targetNodeId, reason }) — akış grafiğinde condition → microservice → code döngüsüyle"
770
- },
771
- undo: { payload: { batchOperationId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.batch.undo { id: batchOperationId }" }
772
- },
773
- 'process-variants': {
774
- mount: NO_DATA,
775
- select: {
776
- payload: { value: "string (düğüm kimlikleri '>' ile birleşik)", path: 'string[]', instanceCount: 'number', sampleInstanceIds: 'string[] (en çok beş örnek)' },
777
- reads: "value self'e yazılır (setter); path ve örnekler event.data ile okunur"
778
- },
779
- clickInstance: { payload: { instanceId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.instance.get { id: instanceId }" },
780
- download: { payload: { format: 'string (xes | ocel)' }, reads: "self'e yazılmaz — event.data ile oku; Page.bpm.exportLog(slug, format, { from, to })" }
781
- },
782
- 'read-proof-matrix': {
783
- mount: NO_DATA,
784
- remind: {
785
- payload: { revision: 'string', instanceId: 'string?', userIds: 'string[] (bekleyen okuyucular)' },
786
- reads: "self'e yazılmaz — event.data ile oku; okumayanlara bildirim gönderen akışı çağır"
787
- }
788
- },
789
- 'case-view': {
790
- mount: NO_DATA,
791
- action: {
792
- payload: { action: 'string (discretionary | start | complete | disable | enable)', itemId: 'string', planItemId: "string ('' — discretionary)" },
793
- reads: "self'e yazılmaz — event.data ile oku; Page.bpm.cases.act(caseInstanceId, event.data), sonra view'i yeniden setter yaz"
794
- },
795
- clickTask: { payload: { taskId: 'string', planItemId: 'string', itemId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.task.get { id: taskId }" }
796
- },
797
- 'delegation-editor': {
798
- mount: NO_DATA,
799
- save: {
800
- payload: { fromUserId: 'string? (yalnız başkası adına)', toUserId: 'string', startsAt: 'string (ISO)', endsAt: 'string? (ISO)', processSlugs: 'string[] (boş = tüm süreçler)', reason: 'string?' },
801
- reads: "self'e yazılmaz — event.data ile oku; process.delegation.create (event.data alanları). Yalnız istemci doğrulaması geçince yayılır"
802
- },
803
- end: { payload: { delegationId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.delegation.end { id: delegationId }" }
804
- },
805
- 'handover-wizard': {
806
- mount: NO_DATA,
807
- stepChange: { payload: { step: 'string (tasks | ownership | confirm)', index: 'number' }, reads: "self'e yazılmaz — event.data ile oku" },
808
- apply: {
809
- payload: { userId: 'string', decisions: '[{ kind, key, target, targetUserId? }]', force: 'boolean', deactivate: 'boolean' },
810
- reads: "self'e yazılmaz — event.data ile oku; process.handover.apply { userId, decisions, force }"
811
- }
812
- },
813
- 'notification-center': {
814
- mount: NO_DATA,
815
- open: { payload: { notificationId: 'string', link: 'string?', taskId: 'string?', instanceId: 'string?', markRead: 'boolean (okunmamış satırda true)' }, reads: "self'e yazılmaz — event.data ile oku" },
816
- read: { payload: { notificationId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.notification.read { id: notificationId }" },
817
- ack: { payload: { notificationId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.notification.ack { id: notificationId }" },
818
- markAllRead: { payload: { notificationIds: 'string[] (okunmamışlar)' }, reads: "self'e yazılmaz — event.data ile oku; her kimlik için read()" },
819
- savePreference: {
820
- payload: { mode: 'string (immediate | digest | sla-only)', digestAt: "string ('HH:mm:00')", quietFrom: "string | null", quietTo: "string | null", timeZone: 'string' },
821
- reads: "self'e yazılmaz — event.data ile oku; process.notification-preference.update (event.data alanları)"
822
- }
823
- },
824
- 'approval-chain-editor': {
825
- mount: NO_DATA,
826
- save: {
827
- payload: { definitionId: 'string', baseVersion: 'number (aktif sürüm)', overrideRevision: 'number (okunan revizyon; uyarlama yoksa 0)', patch: 'JsonPatchOperation[] (aktif sürüme karşı tam uyarlama, seçicili yollar)' },
828
- reads: "self'e yazılmaz — event.data ile oku; process.override.save { id: definitionId, baseVersion, overrideRevision, patch }. Yalnız istemci doğrulaması geçince yayılır"
829
- },
830
- reset: { payload: { definitionId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.override.reset { id: definitionId }" },
831
- migrateOpen: { payload: { definitionId: 'string' }, reads: "self'e yazılmaz — event.data ile oku; process.override.migrate-open { id: definitionId }" },
832
- simulate: {
833
- payload: { definitionId: 'string', subjectRowId: 'string?', overridePatch: 'JsonPatchOperation[] (kaydedilmemiş uyarlama)' },
834
- reads: "self'e yazılmaz — event.data ile oku; process.override.simulate { id: definitionId, subjectRowId, overridePatch } sonucunu simulation prop'una yaz"
835
- }
836
722
  }
837
723
  };
838
724
  export const eventPayloadNote = "Olay yükü motorda komponentin KENDİ prop'una yazılır (FlowMachine.onEmit → setNode) ve akışta " +
839
725
  "self.<anahtar> ile okunur — payload anahtarları aynı zamanda o prop'ların adıdır. " +
840
726
  "event.data ESKİ kuşak komponent olaylarında BOŞ gelir: motor akışı emit'in params'ıyla başlatır ve " +
841
727
  "event.data = params.data'dır (html actions/fireEvent ile 2026-08-27 kuşağı — eventcalendar · " +
842
- "importwizard · kanban · signature · stepper — bu yoldan doldurur; ayrım tekil sözleşme notlarında). " +
728
+ "importwizard · kanban · signature · stepper — ve QA turlarında params eklenen tabs · breadcrumb-title · " +
729
+ "toast · result · emptystate bu yoldan doldurur; ayrım tekil sözleşme notlarında). " +
843
730
  "payload boşsa olay veri taşımaz; 'reads' verilmişse değer yükte değil komponentin başka bir prop'undadır. " +
844
731
  "self yerine value { type:'property', ref:{ source:'component', element:'<elementId>', property:'<prop>' } } de kullanılabilir. " +
845
732
  "payload alanı yoksa yük sözlüğe henüz işlenmemiştir; tahmin etmeyin.";
846
- /**
847
- * `platformContracts` platforma özgü sözlüktür (mobilde `mobileComponentEvents.ts`): aynı olayda
848
- * web sözleşmesinin ÖNÜNE geçer, web'de karşılığı olmayan bileşenin tek kaynağıdır.
849
- */
850
- export function eventContractsFor(slug, names, platformContracts) {
851
- const own = (source) => slug !== undefined && source !== undefined && Object.hasOwn(source, slug) ? source[slug] : undefined;
852
- const web = own(componentEventContracts);
853
- const platform = own(platformContracts);
854
- const contracts = web || platform ? { ...web, ...platform } : undefined;
733
+ export function eventContractsFor(slug, names) {
734
+ const contracts = slug ? componentEventContracts[slug] : undefined;
855
735
  return names.map(name => {
856
736
  const contract = contracts?.[name];
857
737
  if (!contract)
@@ -1,148 +1,10 @@
1
- import { MOBILE_DROPPABLE_TYPES, MOBILE_INTRINSIC_WIDTH_TYPES, MOBILE_OUT_OF_FLOW_TYPES, MOBILE_TRANSPARENT_WRAPPER_TYPES } from './mobileComponentCatalog.js';
2
- const SPACING_SCALE = [0, 4, 8, 16, 24, 32];
3
- const EDGE_TOLERANCE = 1;
4
- const WIDTH_TOLERANCE = 2;
5
- export function summarizeLayout(boxes, viewport, options = {}) {
1
+ export function summarizeLayout(boxes, viewport) {
6
2
  const outside = (box) => box.x + box.width <= 0 || box.y + box.height <= 0 || box.x >= viewport.width || box.y >= viewport.height;
7
- const alignment = options.alignment === true ? summarizeAlignment(boxes, viewport) : undefined;
8
3
  return {
9
4
  nodeCount: boxes.length,
10
5
  overflowing: boxes.filter(box => box.overflowX || box.overflowY).map(box => box.elementId),
11
6
  outsideViewport: boxes.filter(box => box.width > 0 && box.height > 0 && outside(box)).map(box => box.elementId),
12
7
  zeroSized: boxes.filter(box => box.width === 0 || box.height === 0).map(box => box.elementId),
13
- truncated: boxes.length >= 500,
14
- ...(alignment ? { alignment } : {})
8
+ truncated: boxes.length >= 500
15
9
  };
16
10
  }
17
- function summarizeAlignment(boxes, viewport) {
18
- // Tip ve ata sırası olmayan kutular (eski runtime/betik) ağaç kurmaz; hiza uydurulmaz.
19
- if (!boxes.some(box => typeof box.type === 'string' && box.parentIndex !== undefined))
20
- return undefined;
21
- // Ağaç sırayla kurulur, kimlikle değil: `repeater` aynı elementId'yi her satırda yeniden çizer.
22
- const parentOf = (box) => typeof box.parentIndex === 'number' ? boxes[box.parentIndex] : undefined;
23
- // İç roller (`__trigger`, `__press`) tip taşımaz: bileşenin parçasıdır, düğüm değildir.
24
- const typedParent = (box) => {
25
- for (let current = parentOf(box); current; current = parentOf(current))
26
- if (current.type)
27
- return current;
28
- return undefined;
29
- };
30
- const ancestors = (box) => {
31
- const chain = [];
32
- for (let current = typedParent(box); current; current = typedParent(current))
33
- chain.push(current);
34
- return chain;
35
- };
36
- // Ana ağacın kökü en büyük atasız kutudur. Öteki atasız kutular portala çizilir (floating-container,
37
- // toast, modal içeriği) ve akışta değildir; sayılsalar ana kökü "yan yana kardeş" yapar, ekran satır sayılırdı.
38
- // Sayfanın ağacını taşıyan kök seçilir (kabuklu sayfada shell_root, kabuksuzda page_container); bulunamazsa
39
- // en büyük kök, eşitlikte sonraki — portal içeriği DOM'da uygulamadan önce durur.
40
- const roots = boxes.filter(box => box.type && !typedParent(box));
41
- const pageContainer = boxes.find(box => box.elementId === 'page_container');
42
- const mainRoot = (pageContainer ? [pageContainer, ...ancestors(pageContainer)].at(-1) : undefined)
43
- ?? [...roots].reverse().sort((a, b) => b.width * b.height - a.width * a.height)[0];
44
- // Saydam sarmalayıcının (tooltip) kutusu görünmez; dikey aralık onun görünen içeriğinden ölçülür.
45
- const visibleExtent = (box) => {
46
- const children = MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(box.type ?? '') ? boxes.filter(child => child.type && typedParent(child) === box && child.height > 0) : [];
47
- return children.length === 0
48
- ? { top: box.y, bottom: box.y + box.height }
49
- : { top: Math.min(...children.map(child => child.y)), bottom: Math.max(...children.map(child => child.y + child.height)) };
50
- };
51
- const outOfFlow = (box) => {
52
- const chain = [box, ...ancestors(box)];
53
- return chain.some(item => MOBILE_OUT_OF_FLOW_TYPES.includes(item.type ?? '')) || chain[chain.length - 1] !== mainRoot;
54
- };
55
- // Yatayda ekrana sığmayan kutu (carousel ya da yatay kaydırmanın öteki sayfaları) sütun akışında değildir.
56
- const onScreen = (box) => box.x >= -EDGE_TOLERANCE && box.x + box.width <= viewport.width + EDGE_TOLERANCE;
57
- const flow = boxes.filter(box => box.type && box.width > 0 && box.height > 0 && onScreen(box) && !outOfFlow(box));
58
- const inFlow = new Set(flow);
59
- const parents = new Set(flow.map(typedParent).filter(Boolean));
60
- const siblings = (box) => flow.filter(other => other !== box && typedParent(other) === typedParent(box));
61
- const besideSibling = (box) => siblings(box).some(other => other.y < box.y + box.height - EDGE_TOLERANCE && box.y < other.y + other.height - EDGE_TOLERANCE);
62
- // Kapsam, kendi kapsamından dar bir yüzeydir. Kapsamı boydan boya kaplayan zemin (boyanmış sayfa kökü,
63
- // tam genişlik bant) yeni kapsam açmaz; içeriği ekranın kenar ölçüsüne girer.
64
- const scopeCache = new Map();
65
- const scopeOf = (box) => {
66
- if (scopeCache.has(box))
67
- return scopeCache.get(box);
68
- const scope = ancestors(box).find(item => inFlow.has(item) && opensScope(item));
69
- scopeCache.set(box, scope);
70
- return scope;
71
- };
72
- const opensScope = (box) => box.surface === true && box.width < (scopeOf(box)?.width ?? viewport.width) - WIDTH_TOLERANCE;
73
- // Yan yana dizilen öğe (satır kabının çocuğu ya da onun içindeki sütun) sütun hizasına girmez.
74
- const inRow = (box) => {
75
- const scope = scopeOf(box);
76
- const chain = [box, ...ancestors(box)];
77
- return chain.slice(0, scope ? chain.indexOf(scope) : chain.length).some(besideSibling);
78
- };
79
- const isContent = (box) => parents.has(box)
80
- ? opensScope(box)
81
- : box.surface === true || !MOBILE_DROPPABLE_TYPES.includes(box.type ?? '');
82
- const scopes = new Map([[undefined, []]]);
83
- for (const box of flow.filter(isContent)) {
84
- const scope = scopeOf(box);
85
- if (box.width >= (scope?.width ?? viewport.width) / 2 && !inRow(box))
86
- scopes.set(scope, [...(scopes.get(scope) ?? []), box]);
87
- }
88
- const reports = [...scopes].map(([scope, members]) => ({ scope, ...scopeAlignment(members) }));
89
- const screen = reports.find(report => report.scope === undefined);
90
- return {
91
- gutter: screenGutter(screen?.dominantLeft, screen?.dominantRight, viewport.width),
92
- leftEdges: screen?.leftEdges ?? [],
93
- surfaces: reports.filter(report => report.scope !== undefined && report.leftEdges.length > 1).map(report => ({ elementId: report.scope?.elementId ?? '', leftEdges: report.leftEdges })),
94
- widthOutliers: reports.flatMap(report => report.widthOutliers),
95
- offScaleGaps: offScaleGaps(flow, typedParent, visibleExtent)
96
- };
97
- }
98
- function scopeAlignment(members) {
99
- const leftEdges = clusters(members, box => box.x);
100
- const sized = members.filter(box => !MOBILE_INTRINSIC_WIDTH_TYPES.includes(box.type ?? ''));
101
- // Yalnız içerik genişlikli öğe varsa (metin listesi) sağ kenar onlardan okunur; sapma yine sayılmaz.
102
- const rightEdges = clusters(sized.length > 0 ? sized : members, box => box.x + box.width);
103
- // Eşitlikte en soldaki sol kenar ve en sağdaki sağ kenar kazanır: sözleşmenin normu `fill`dir.
104
- const dominantLeft = [...leftEdges].sort((a, b) => b.elementIds.length - a.elementIds.length || a.x - b.x)[0]?.x;
105
- const dominantRight = [...rightEdges].sort((a, b) => b.elementIds.length - a.elementIds.length || b.x - a.x)[0]?.x;
106
- const widthOutliers = dominantLeft === undefined || dominantRight === undefined
107
- ? []
108
- : unique(sized.filter(box => Math.abs(box.x + box.width - dominantRight) > WIDTH_TOLERANCE), box => box.elementId)
109
- .map(box => ({ elementId: box.elementId, insetLeft: box.x - dominantLeft, insetRight: dominantRight - (box.x + box.width) }));
110
- return { leftEdges, dominantLeft, dominantRight, widthOutliers };
111
- }
112
- const unique = (items, key) => items.filter((item, index) => items.findIndex(other => key(other) === key(item)) === index);
113
- function clusters(members, edge) {
114
- const result = [];
115
- for (const box of members) {
116
- const match = result.find(cluster => Math.abs(cluster.x - edge(box)) <= EDGE_TOLERANCE);
117
- if (match && !match.elementIds.includes(box.elementId))
118
- match.elementIds.push(box.elementId);
119
- else if (!match)
120
- result.push({ x: edge(box), elementIds: [box.elementId] });
121
- }
122
- return result.sort((a, b) => a.x - b.x);
123
- }
124
- function screenGutter(left, right, width) {
125
- if (left === undefined || right === undefined)
126
- return null;
127
- const rightGutter = width - right;
128
- return Math.abs(left - rightGutter) <= EDGE_TOLERANCE ? left : { left, right: rightGutter };
129
- }
130
- /** Aynı atanın alt alta duran ardışık çocukları arasındaki boşluk ölçekte mi (yan yana olanlar sayılmaz). */
131
- function offScaleGaps(flow, typedParent, extent) {
132
- const groups = new Map();
133
- for (const box of flow) {
134
- const key = typedParent(box);
135
- groups.set(key, [...(groups.get(key) ?? []), box]);
136
- }
137
- return [...groups.values()].flatMap(group => {
138
- const sorted = [...group].sort((a, b) => a.y - b.y);
139
- return sorted.slice(1).flatMap((next, index) => {
140
- const previous = sorted[index];
141
- const gap = extent(next).top - extent(previous).bottom;
142
- const stacked = gap >= -EDGE_TOLERANCE && previous.x < next.x + next.width && next.x < previous.x + previous.width;
143
- return stacked && !SPACING_SCALE.some(step => Math.abs(step - gap) <= EDGE_TOLERANCE)
144
- ? [{ between: [previous.elementId, next.elementId], gap }]
145
- : [];
146
- });
147
- });
148
- }