@appaflytech/zeyrek-ai-mcp 0.0.4 → 0.0.6

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 (101) hide show
  1. package/.env.example +8 -3
  2. package/.env.production.example +2 -0
  3. package/README.md +229 -193
  4. package/dist/config.js +20 -2
  5. package/dist/knowledge/backendConstraints.js +897 -0
  6. package/dist/knowledge/componentCatalog.js +196 -42
  7. package/dist/knowledge/coverage.js +43 -0
  8. package/dist/knowledge/flowGuards.js +30 -0
  9. package/dist/knowledge/guideSections.js +49 -0
  10. package/dist/knowledge/hydrate.js +97 -0
  11. package/dist/knowledge/languageCodes.js +36 -0
  12. package/dist/knowledge/languages.js +105 -0
  13. package/dist/knowledge/layoutSummary.js +10 -0
  14. package/dist/knowledge/localization.js +51 -0
  15. package/dist/knowledge/normalize.js +449 -16
  16. package/dist/knowledge/outlines.js +136 -12
  17. package/dist/knowledge/propTypes.js +80 -0
  18. package/dist/knowledge/rules.js +618 -43
  19. package/dist/knowledge/schemaGuard.js +199 -0
  20. package/dist/knowledge/styleResolve.js +63 -4
  21. package/dist/observability/trace.js +27 -3
  22. package/dist/platform/errors.js +46 -4
  23. package/dist/platform/http.js +33 -4
  24. package/dist/platform/patch.js +27 -0
  25. package/dist/platform/refs.js +104 -0
  26. package/dist/platform/responses.js +9 -2
  27. package/dist/platform/screenshot.js +527 -16
  28. package/dist/platform/secrets.js +40 -0
  29. package/dist/platform/tokenVault.js +89 -0
  30. package/dist/platform/writeDiff.js +281 -0
  31. package/dist/resources/examples/layout-app-shell.json +1 -1
  32. package/dist/resources/examples/microservice-table-list.json +2 -4
  33. package/dist/resources/examples/page-dynamic-form.json +2 -2
  34. package/dist/resources/examples/page-flex-sections.json +99 -0
  35. package/dist/resources/examples/page-sign-in.json +3 -2
  36. package/dist/resources/guides/component.md +69 -8
  37. package/dist/resources/guides/entity.md +404 -16
  38. package/dist/resources/guides/expressions.md +44 -2
  39. package/dist/resources/guides/external-component.md +9 -0
  40. package/dist/resources/guides/getting-started.md +77 -7
  41. package/dist/resources/guides/layout.md +53 -10
  42. package/dist/resources/guides/microservice.md +226 -13
  43. package/dist/resources/guides/middleware.md +89 -1
  44. package/dist/resources/guides/page.md +383 -46
  45. package/dist/resources/guides/translation.md +126 -8
  46. package/dist/resources/guides/view.md +16 -1
  47. package/dist/resources/guides/workflow.md +198 -27
  48. package/dist/resources/knowledge.js +5 -0
  49. package/dist/resources/rules.js +27 -0
  50. package/dist/resources/schema-fingerprint.json +8 -0
  51. package/dist/resources/schemas/CreateBulkRowCommand.json +33 -0
  52. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  53. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  54. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  55. package/dist/resources/schemas/CreateMicroserviceCommand.json +26 -2
  56. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  57. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  58. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  59. package/dist/resources/schemas/CreateWorkflowCommand.json +95 -14
  60. package/dist/resources/schemas/GetRowsQuery.json +3 -0
  61. package/dist/resources/schemas/PreviewMicroserviceCommand.json +16 -0
  62. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  63. package/dist/resources/schemas/PreviewWorkflowCommand.json +34 -9
  64. package/dist/resources/schemas/StepDto.json +23 -3
  65. package/dist/resources/schemas/UpdateBulkRowCommand.json +33 -0
  66. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  67. package/dist/resources/schemas/UpdateEntityCommand.json +49 -10
  68. package/dist/resources/schemas/UpdateMicroserviceCommand.json +28 -3
  69. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  70. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  71. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  72. package/dist/resources/schemas/UpdateWorkflowCommand.json +95 -14
  73. package/dist/server.js +4 -0
  74. package/dist/tools/access.js +3 -3
  75. package/dist/tools/applications.js +1 -1
  76. package/dist/tools/automations.js +63 -30
  77. package/dist/tools/common.js +39 -0
  78. package/dist/tools/components.js +16 -7
  79. package/dist/tools/datasources.js +83 -21
  80. package/dist/tools/deployments.js +34 -2
  81. package/dist/tools/dtos.js +25 -12
  82. package/dist/tools/entities.js +113 -28
  83. package/dist/tools/externalComponents.js +9 -3
  84. package/dist/tools/files.js +20 -6
  85. package/dist/tools/flows.js +110 -11
  86. package/dist/tools/folders.js +7 -2
  87. package/dist/tools/globalFunctions.js +63 -0
  88. package/dist/tools/layouts.js +103 -29
  89. package/dist/tools/microservices.js +229 -38
  90. package/dist/tools/organizations.js +5 -6
  91. package/dist/tools/pages.js +365 -100
  92. package/dist/tools/platformInfo.js +152 -11
  93. package/dist/tools/projects.js +183 -33
  94. package/dist/tools/queries.js +32 -10
  95. package/dist/tools/rows.js +159 -45
  96. package/dist/tools/screenWrite.js +59 -0
  97. package/dist/tools/screens.js +28 -0
  98. package/dist/tools/translations.js +24 -8
  99. package/dist/tools/views.js +20 -10
  100. package/dist/tools/workflows.js +33 -16
  101. package/package.json +6 -3
@@ -8,13 +8,46 @@
8
8
  * çıplak sayı px sayılır. `radius` TEK alandır: preset anahtarı (none|sm|md|
9
9
  * lg|pill) YA DA doğrudan CSS uzunluğu ('18px') yazılabilir.
10
10
  */
11
+ /**
12
+ * KOLON TİPİ SÖZLÜĞÜ — her tip için "neyi bekler / neyi çizer" (G-29).
13
+ * Katalog tipleri yalnız ADIYLA listeliyordu; 09-05 koşusunda altı avatar yüklenip satırlara
14
+ * bağlandığı hâlde `image` kolonu eklenmedi, çünkü kolonun dosya alanı mı yoksa URL mi beklediği
15
+ * hiçbir yerde yazmıyordu. Kaynak: `packages/ng-apps-ui/src/components/table/components/renderers/*`
16
+ * ve `column-format.functions.ts`.
17
+ */
18
+ export const TABLE_COLUMN_TYPE_NOTE = "KOLON TİPİ SÖZLÜĞÜ (bekler → çizer). " +
19
+ "text/long-text: metin → düz metin (long-text hücreye sığdırıp kırpar). " +
20
+ "number/time: sayı ya da metin → olduğu gibi. " +
21
+ "money: sayı → TRY para biçimi, ayıraçlar dile göre (tr '₺1.250,00', en '₺1,250.00'). " +
22
+ "pertance: SAYI → yüzde metni; 0..1 arası değer ORAN sayılıp 100'le çarpılır, 1'den büyük değer zaten yüzdedir (tam 1 = %100). Çubuk ÇİZMEZ — çubuk için progress. " +
23
+ "progress: ham sayı → hücrede oransal çubuk (value/max, max varsayılanı 100); progressVariant/progressLabel/progressColor/thresholds ile ayarlanır. " +
24
+ "date/datetime: ISO metni ya da Date → dile göre biçimli tarih (locale prop'undan). " +
25
+ "switch: boolean → yeşil onay / kırmızı çarpı ikonu (metin değil). " +
26
+ "image: entity'nin FILE alanının nesnesi (uçtan geldiği gibi) YA DA mutlak URL metni → satır yüksekliğine sığdırılmış görsel (object-fit contain, köşe yuvarlatma). Kolon url'yi kendisi çözer: metin olduğu gibi kullanılır, nesnede `type:'file'` ise `data`/`value`, değilse `absolute_path` okunur — table-source bir uç FILE alanını `{ id, absolute_path, value, type:'ref', … }` olarak döndürür ve mapping'de alanı OLDUĞU GİBİ taşımak yeter (ölçüldü 2026-09-05: altı avatar `image` kolonunda çizildi). Yüklenemezse hücrede hata metni çıkar; datatable'ın image kolonu daha katıdır (düz URL kabul etmez). " +
27
+ "file: dosya alanı ya da url metni → dosya adı + ikon, yeni sekmede açılan bağlantı. " +
28
+ "url: metin → tıklanabilir bağlantı (ikonlu, http ise yeni sekme). link: metin → tıklanmayan düz bağlantı görünümü. " +
29
+ "email: metin → düz metin. " +
30
+ "dropdown: option value'su → kolonun options listesinden LABEL, appearance renginde rozet. multiselect: value dizisi → eşleşen label'lar rozet olarak. " +
31
+ "tag: metin → appearance renginde TEK rozet; renk kolonun appearance prop'undan gelir (satır bazında rows[i]._columns[<indeks>].appearance ile ezilir), serbest hex ALMAZ. " +
32
+ "tags: [{ value, color }] dizisi ya da metin dizisi → çok rozet, renk SATIR VERİSİNDEN (serbest hex burada geçer), sığmayanlar +N ile toplanır. " +
33
+ "action: veri kolonu DEĞİL — aksiyon sütunu ayrı çizilir; kolon tanımında seçilirse ham değer metin olarak görünür.";
11
34
  export const STYLE_GROUP_NOTES = {
12
- surfaceStyle: "{ background, radius, borderTop, borderRight, borderBottom, borderLeft, shadow, contentOverflow(visible|hidden|auto) }. radius: tema kademesi ('var(--ng-radius-md)'; sm/lg 'Border radius'un oranı, md temanın kendisi), tek uzunluk ('18px') YA DA dört köşe ('90px 14px 14px 14px' — sol-üst, sağ-üst, sağ-alt, sol-alt). KENARLIK YÖN BAŞINADIR: her yön kendi CSS kısayolunu taşır ('1px solid var(--ng-border-primary)'), 'none' o kenarı kapatır; eski borderWidth/borderStyle/borderColor üçlüsü EMEKLİ (yalnız hiçbir yön yazılmamışsa okunur). shadow: çok katmanlı `box-shadow` metni ('0px 2px 5px 0px rgba(0,0,0,.2), inset 8px 5px 7px 0px #000'), tema token'ı ('var(--ng-shadow-md)') ya da eski preset anahtarı (none|sm|md|lg).",
35
+ surfaceStyle: "{ background, radius, borderTop, borderRight, borderBottom, borderLeft, shadow, contentOverflow(visible|hidden|auto), opacity(0–100 yüzde; 100/boş yazılmaz) }. radius: tema kademesi ('var(--ng-radius-md)'; sm/lg 'Border radius'un oranı, md temanın kendisi), tek uzunluk ('18px') YA DA dört köşe ('90px 14px 14px 14px' — sol-üst, sağ-üst, sağ-alt, sol-alt). KENARLIK YÖN BAŞINADIR: her yön kendi CSS kısayolunu taşır ('1px solid var(--ng-border-primary)'), 'none' o kenarı kapatır; eski borderWidth/borderStyle/borderColor üçlüsü EMEKLİ (yalnız hiçbir yön yazılmamışsa okunur). shadow: çok katmanlı `box-shadow` metni ('0px 2px 5px 0px rgba(0,0,0,.2), inset 8px 5px 7px 0px #000'), tema token'ı ('var(--ng-shadow-md)') ya da eski preset anahtarı (none|sm|md|lg).",
13
36
  typographyStyle: "{ textColor, fontFamily('var(--ng-font)' tema gövde | 'var(--ng-font-heading)' tema başlık | 'monospace' — token tema fontunu izler; serbest Google fontu yazma, tema yüklemez ve sessizce düşer), fontSize(CSS uzunluğu: '15px'|'1.1em'; çıplak sayı px sayılır), fontWeight(400|500|600|700), fontStyle(normal|italic), textDecoration(none|underline|line-through), lineHeight(TEK alan, üç yazım: oran '1.4' | yüzde '140%' | uzunluk '24px'; eski kayıtlardaki çıplak 140 yüzde sayılır), letterSpacing(CSS uzunluğu, negatif meşru: '-0.4px'), textAlign(left|center|right|justify) }. Komponentin içeriğine miras kalır.",
14
37
  hoverStyle: "{ background, borderColor, shadow(none|sm|md|lg), lift(CSS uzunluğu: '2px'), transitionMs }. Hiçbir alan dolu değilse hover bloğu ve geçiş HİÇ yazılmaz.",
15
38
  colorTokens: "RENK ALANLARI (background, textColor, borderColor, hover.background/borderColor) hex kadar tema token'ı da alır: 'var(--ng-accent-primary)', 'var(--ng-status-error)', 'var(--ng-background-inverse)'. Token yazılan alan tema değişince izler, hex yazılan kopar; tasarım sistemi renklerinde ÖNCE token'ı tercih et (studio panelinde aynı seçim tema chip'iyle yapılır).",
16
39
  styleEscape: "Kaçış kapısı ve SON SÖZ: tipli grupların/builder'ın karşılamadığı her CSS kararı buraya yazılır (custom properties alanının veri kanalı budur). Önce tipli grupları dene; grup alanı yoksa style'a yaz. Efektif sonucu görmek için page_runtime_get(resolveStyles:true)."
17
40
  };
41
+ /**
42
+ * Kendi derleyicisi olmayan KAP bileşenlerinin yüzey notu (2026-09-04): grup
43
+ * bileşen kodunda okunmaz, render katmanı onu düğüm kutusuna yazar; taşma da
44
+ * kutunun kendisine gider. Eski appearance Surface bloğunun (background:'warning',
45
+ * borderWidth…) yerini alır; kayıtlı appearance değerleri çizilmeye devam eder
46
+ * ama Studio'da artık düzenlenmez — yeni karar surfaceStyle'a yazılır.
47
+ */
48
+ export const LOCALE_FOLLOWS_APP_NOTE = "Boş bırak — komponent o zaman UYGULAMA dilini izler ve language-selector ile dil değişince gün/ay adları ve saat biçimi anında güncellenir. Doldurulursa o dile SABİTLENİR (bilinçli sabitleme için). Tasarımcı listesi tr/en gösterir ama uygulama dili yolu her ISO 639-1 kodunu kabul eder; sözlüğü olmayan dilde METİN İngilizceye düşer, sayı/tarih BİÇİMİ o dilin kendi kuralını kullanır.";
49
+ const NODE_SURFACE_NOTE = STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens +
50
+ " Bu bileşen grubu KENDİ OKUMAZ: render katmanı yüzeyi düğüm kutusuna (.ng-app__node-item) yazar, contentOverflow kutunun kendisine iner. Eski appearance Surface alanları (background/borderWidth/radius/shadow/opacity/overflow) EMEKLİ — yeni karar buraya yazılır.";
18
51
  export const componentKnowledge = {
19
52
  html: {
20
53
  summary: 'Kaçış kapısı: hazır komponentler yetmediğinde ham HTML + Mustache şablonu + scoped CSS + satır içi script + flow köprüsüyle her gereksinimi kapatır.',
@@ -29,6 +62,7 @@ export const componentKnowledge = {
29
62
  "Şema olayı yalnız mount'tur; aksiyonlar set_event ile DEĞİL kendi flow prop'uyla bağlanır (type:'flow', ref:<flowId>)."
30
63
  ],
31
64
  propNotes: {
65
+ surfaceStyle: NODE_SURFACE_NOTE,
32
66
  content: "Ham HTML ya da Mustache şablonu. type:'code' + {{ }} ifadesiyle dinamik de beslenebilir.",
33
67
  context: 'Mustache değişken sözlüğü (nesne). Setter ile yazmak içeriği yeniden render eder.',
34
68
  style: "emotion css() bloğu: iç seçiciler ve &:hover çalışır, üretilen sınıf komponente scope'ludur.",
@@ -44,10 +78,12 @@ export const componentKnowledge = {
44
78
  "Koşullu/satır bazlı renk table'da _columns ile kurulur ve style prop'unu KAYBETTİRMEZ — datatable'ın colorMap'i statiktir. Hem CSS hem satıra göre renk isteyen tablo table'dır; renk için datatable'a mecbur değilsin."
45
79
  ],
46
80
  propNotes: {
81
+ surfaceStyle: NODE_SURFACE_NOTE,
47
82
  rows: 'Satır dizisi; mount/filter/sort/pagination akışlarının setter adımıyla doldurulur.',
83
+ appearance: "YÜKSEKLİK/GENİŞLİK ELLE HESAPLANMAZ (ölçüldü 2026-09-05, f11-repro). heightType:'auto' tablo içeriği kadar uzar — satır sayısı × satır yüksekliği hesabı gerekmez. heightType:'fixed' gövdeyi KAYDIRIR (grid-container overflow:auto), satır kaybolmaz; kare'de görünmeyen satır kırpılmış değil kaydırılmıştır. KOLONLAR KARTI TAŞIRMAZ: genişlik toplamı kabı aşarsa tablo kendi içinde YATAY kaydırır ve sağ kenara taşma göstergesi koyar; kabın scrollWidth'i değişmez. Toplam kaptan KÜÇÜKSE kolonlar orantılı büyütülüp kap tam doldurulur (stretchColumns) — bu yüzden 'kolonu daraltayım da sığsın' yamalarına gerek yok. Tek gerçek tuzak yerleşim tarafındadır: FLEX ebeveynde blocks[].size yok sayılır, ölçü appearance'tan gelir; IZGARA ebeveynde tersi (gateway PAGE-019 ile uyarır).",
48
84
  totalRows: "Toplam kayıt: Number(Steps.<adım>?.headers?.[\"total-count\"] ?? 0) guard'lı ifadesiyle yazılır.",
49
85
  rowSelection: "none | single | multi — seçim self.selectedRow / self.selectedRows prop'larından okunur.",
50
- columns: "Kolon tanımları; mapping anahtarları kolonların name'iyle birebir eşleşmeli. RENK: tag kolonu appearance ile 5 token alır (primary · info · success · warning · error) ve satır bazında _columns ezmesiyle değişir; serbest hex gerekiyorsa tags kolonunu kullan ve satır verisine [{ value, color:'#da1e28' }] yaz. Sayı/tarih biçimlendirmesi (prefix/suffix/precision/thousands/dateFormat/imageShape) table'da YOK — o datatable'ın kolon şemasındadır.",
86
+ columns: TABLE_COLUMN_TYPE_NOTE + " Kolon tanımları; mapping anahtarları kolonların name'iyle birebir eşleşmeli. RENK: tag kolonu appearance ile 5 token alır (primary · info · success · warning · error) ve satır bazında _columns ezmesiyle değişir; serbest hex gerekiyorsa tags kolonunu kullan ve satır verisine [{ value, color:'#da1e28' }] yaz. Sayı/tarih biçimlendirmesi (prefix/suffix/precision/thousands/dateFormat/imageShape) table'da YOK — o datatable'ın kolon şemasındadır. ORANSAL ÇUBUK: type:'progress' hücrede çubuk çizer ve HAM SAYI bekler (yüzdeyi kendisi hesaplar): value/max. max varsayılanı 100, yani 0..100 arası yüzde alanını doğrudan bağlayabilirsin; sayı 25/40 gibi bir orandan geliyorsa max'ı paydaya ver. Ayarlar: progressVariant(bar|circle) · progressLabel(none|percent|fraction|value) · progressColor(primary|info|success|warning|error|custom) + customProgressColor · thresholds[{from(%), color}] — eşik listesi doluysa çubuğun DOLULUK YÜZDESİNE göre son eşiğin rengi kazanır, yani tek kolon 80'in altında yeşil üstünde kırmızı olur. Sayıya çevrilemeyen değer 0'dır, hücre boş çubuk çizer.",
51
87
  style: "İKİ AYRI YAZIM KURALI: style.root emotion css() bloğudur (camelCase çalışır, iç seçici yazılabilir); style.thead|th|tr|td ise satır içi style attribute'una basılır (column-view.ts toStyleString anahtarı olduğu gibi yazar) — bu dördünde KEBAB-CASE zorunludur, fontSize sessizce düşer, font-size çalışır.",
52
88
  isEditable: 'Hücre içi düzenleme; değişen satır rowChange olayında self.rows üzerinde güncellenir.'
53
89
  }
@@ -63,7 +99,9 @@ export const componentKnowledge = {
63
99
  "Satır bazlı görünüm ezmesi (_columns / _actions) da YOKTUR — o table'a özgüdür. Renklendirme burada yalnız kolonun colorMap/autoColor sözlüğüyle, yani satırdan bağımsız kurulur."
64
100
  ],
65
101
  propNotes: {
66
- columns: "Kolon tipleri: text · number · date · datetime · time · switch · tag · email · url · link · image · file · progress. PROGRESS (2026-08-27) hücrenin içine çubuk ya da halka çizer: progressVariant bar|circle · max (varsayılan 100) · progressLabel none|percent|fraction|value · progressColor + customProgressColor · thresholds [{from,color}] (yüzdeyi geçmeyen en yüksek eşik kazanır) · captionField BAŞKA bir satır alanının metnini çubuğun ÜSTÜNE yazar, yani ad ve çubuk tek hücrede durur. Sıralama/dışa aktarım ham sayıyı görür, çizimi değil.",
102
+ surfaceStyle: NODE_SURFACE_NOTE,
103
+ columns: "Kolon tipleri: text · number · date · datetime · time · switch · tag · email · url · link · image · file · progress. Her tipin ne beklediği/ne çizdiği table'ın kolon sözlüğüyle AYNIDIR (component_get('table') → knowledge.propNotes.columns), İKİ İSTİSNA dışında: burada image/file düz URL kabul etmez (aşağıdaki image notu) ve renk satır bazında değil colorMap ile kurulur. PROGRESS (2026-08-27) hücrenin içine çubuk ya da halka çizer: progressVariant bar|circle · max (varsayılan 100) · progressLabel none|percent|fraction|value · progressColor + customProgressColor · thresholds [{from,color}] (yüzdeyi geçmeyen en yüksek eşik kazanır) · captionField BAŞKA bir satır alanının metnini çubuğun ÜSTÜNE yazar, yani ad ve çubuk tek hücrede durur. Sıralama/dışa aktarım ham sayıyı görür, çizimi değil.",
104
+ image: "IMAGE/FILE kolonu düz URL string'i KABUL ETMEZ: hücre `{ absolute_path, display_name }` (ya da `fileName`) alanlarını taşıyan bir DOSYA NESNESİ (veya nesne dizisi) bekler — `DisplayCellView.svelte:10` `file.absolute_path ?? ''` okur, düz string verilince kaynak boş kalır ve hücre SESSİZCE boş çizilir (09-04 Y-21). Satır verisi bir metin kolonunda URL taşıyorsa kolonu 'url'/'link' yap ya da veriyi { absolute_path: '<url>', display_name: '<ad>' } biçimine çevir; entity'nin FILE alanı zaten bu şekli döner.",
67
105
  actions: "Satır aksiyonları. kind sözlüğü BUTON'unkinden farklıdır: error · information · solid · transparent · dark · danger · warning (butonda success/error/info/primary/warning). Yanlış sözlükten değer invalid-enum-value verir.",
68
106
  selectedRow: "Salt okunur: satır aksiyonu tıklanınca komponentin kendisi doldurur; akış ifadesinde '<elementId>.selectedRow' ile okunur.",
69
107
  colorMap: "Kolon içi değer→renk sözlüğü: [{ value:'Active', color:'#16a34a' }]. STATİKTİR — hücrenin kendi değerine bakar, satırın başka alanına göre hesaplanamaz. Satıra göre koşullu renk gerekiyorsa ya table + _columns ezmesi kullan ya da satır verisinde rengi taşı ({ label, color }) — render sırası: veri objesindeki color → colorMap[etiket] → autoColor.",
@@ -74,27 +112,32 @@ export const componentKnowledge = {
74
112
  form: {
75
113
  summary: "Alanları saran kapsayıcı; submit olayı yayar ve doğrulama hatalarını errorElementId'deki form-validation-errors komponentinde toplar.",
76
114
  behavior: [
77
- "Düzen prop'ları (layout/direction/gap/horizontalAlign…) YOKTUR — onlar container'a aittir. Form çocukları ızgara koordinatıyla (blocks[].position + size.w) dizilir; 'layout' yazmak unknown-prop ile reddedilir.",
78
- "Alan doğrulaması komponentin kendi validations dizisindedir: [{ type: 'required'|'email'|'max-length'…, message, value, isActive }] — dördü de zorunlu, eski 'maxLength' yazımı kaydedilir ama runtime doğrulayıcı onu TANIMAZ ve sessizce hiçbir şey doğrulamaz."
115
+ "Düzen prop grubunun TAMAMINI taşır (2026-09-01): layout ('flex' | 'grid') + direction/horizontalAlign/verticalAlign/size/gap/wrap, varsayılanları container ile aynı (flex · row · left · top · shrink · medium · wrap açık). 'Form'a layout yazılmaz' eski bilgidir. layout prop'u hiç yazılmamış eski form kayıtları ızgara koordinatıyla (blocks[].position + size.w) dizilmeye devam eder. Platform kataloğu component_get'te hâlâ eski şemayı gösteriyorsa kayıt yenilenmemiştir (ARC-024).",
116
+ "Alan doğrulaması komponentin kendi validations dizisindedir: [{ type: 'required'|'email'|'max-length'…, message, value, isActive }] — dördü de zorunlu, eski 'maxLength' yazımı kaydedilir ama runtime doğrulayıcı onu TANIMAZ ve sessizce hiçbir şey doğrulamaz.",
117
+ "message bir ÇEVİRİ ANAHTARI olabilir: 't:' önekiyle yazılan mesaj (ör. 't:form.required') uygulamanın çeviri sözlüğünden çözülür — hem alanın kendi hata metninde hem form-validation-errors listesinde. Öneksiz mesaj yazarın kendi cümlesidir ve iki nokta içerse bile aynen gösterilir. Çözüm form MOUNT olurken bir kez yapılır: ekranda hata dururken dil değiştirilirse metin sayfa yenilenene kadar eski dilde kalır."
79
118
  ],
80
119
  propNotes: {
120
+ surfaceStyle: NODE_SURFACE_NOTE,
81
121
  errorElementId: "Sayfadaki form-validation-errors komponentinin elementId'si — bağlanmazsa alan doğrulama mesajları görünmez."
82
122
  }
83
123
  },
84
124
  box: {
85
- summary: "Evrensel bırakma kutusu: container'dan farkı MUTLAK 12 kolon canvas modunun OLMAMASI (yalnız akış), kutu modelinin border-box olması ve isteğe bağlı başlık/altlık bölgeleridir.",
125
+ summary: "Evrensel bırakma kutusu: container'dan farkı kutu modelinin border-box olması ve isteğe bağlı başlık/altlık bölgeleridir; yerleşim değer kümesi 2026-09-02'den beri container'ınkiyle aynıdır.",
86
126
  behavior: [
87
- "layout iki değer alır: 'flex' (akış) ve 'cssgrid' (GERÇEK display:grid). 'cssgrid', container'ın 'grid'inden AYRI bir değerdir — container'ınki mutlak koordinatlı canvas'tır, buradaki gerçek CSS grid akışıdır.",
127
+ "layout iki değer alır: 'flex' (akış) ve 'grid' — ve 'grid' container'ınkiyle AYNI ŞEYDİR: mutlak koordinatlı 12 kolon canvas (kullanıcı kararı 2026-09-02, tek grid kavramı). Şemadaki eski 'cssgrid' değeri (akan GERÇEK display:grid) seçenek olmaktan çıktı: yeni yazımda geçmez (invalid-enum-value), yalnız o değerle KAYITLI eski düğümler çizilmeye devam eder (screen-builder/grid-layout.ts isCssGridLayout yalnız 'cssgrid'e bakar).",
88
128
  "Yerleşim prop'ları bileşende OKUNMAZ: çocuk sarmalayıcısının stilini runtime'da RenderComponent, canvas'ta Draggable uygular (container ile aynı sözleşme).",
89
129
  "GÖVDE İÇİN DÜĞÜM YOKTUR: kutunun kendi bırakma alanına inen her şey gövdedir. hasHeader/hasFooter ise ayrı 'box-section' DÜĞÜMLERİ açar; bölge kapatılınca içindeki çocuklar SİLİNMEZ, gövdeye taşınır.",
90
- "cssgrid modunda çocuğun blocks[].size.w'si 'grid-column: span N'e çevrilir; gridTemplate doluysa gridColumns yok sayılır (şablon geçersizse kolon sayısına düşülür).",
130
+ "gridColumns/gridTemplate/gridRowGap ARTIK YALNIZ eski 'cssgrid' düğümlerinde okunur: o modda çocuğun blocks[].size.w'si 'grid-column: span N'e çevrilir ve gridTemplate doluysa gridColumns yok sayılır (şablon geçersizse kolon sayısına düşülür). layout:'grid' seçilen yeni kutuda bu üç prop ÖLÜDÜR — kolon payı yine blocks[].size.w'den gelir.",
91
131
  "Yüzeyi TİPLİ GRUPLARDAN yönetilir: surfaceStyle (zemin, köşe, yön yön kenarlık, çok katmanlı gölge, gövde taşması), typographyStyle (içeriğe miras), hoverStyle ve box'a özgü sectionStyle (başlık/altlık bölgelerinin ayracı, ayraç rengi, bölge dolgusu). Bölgeler kutunun içinde bir eleman DEĞİL, gövdeye yerleşen ayrı 'box-section' düğümleridir; bu yüzden sectionStyle onları gerçek sınıflarından ('.ng-box-section--header/--footer') hedefler. style'ın tek parçası '&__body'dir — eski '&__header'/'&__footer' parçaları hiçbir elemana denk gelmediği için kaldırıldı."
92
132
  ],
93
133
  propNotes: {
94
- layout: "'flex' | 'cssgrid'. Diğer yerleşim prop'ları flex'te, gridColumns/gridTemplate/gridRowGap ise cssgrid'de okunur.",
95
- gridTemplate: 'Serbest grid-template-columns metni; beyaz listeden geçmeyen değer sessizce yok sayılır ve gridColumns geçerli olur.',
96
- isBorder: 'Çerçeveyi açar; kutu border-box olduğu için ölçü kaymaz.',
97
- style: "Parçalar: '&__header' · '&__body' · '&__footer' — ancak yalnız '&__body' gerçek bir elemana denk gelir (CQ-157)."
134
+ layout: "'flex' | 'grid' (2026-09-02'ye kadar ikinci değer 'cssgrid' idi). Diğer altı yerleşim prop'u yalnız 'flex' iken okunur; 'grid' container'ın mutlak 12 kolon canvas'ıdır ve çocuk ölçüsü blocks[].size'dan gelir.",
135
+ gridTemplate: "Serbest grid-template-columns metni; beyaz listeden geçmeyen değer sessizce yok sayılır ve gridColumns geçerli olur. YALNIZ eski 'cssgrid' düğümlerinde etkilidir.",
136
+ surfaceStyle: STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens + " EMEKLİ isBorder prop'unun yerini bu grup aldı (2026-08-31 tipli stil göçü): çerçeve artık yön başına borderTop/Right/Bottom/Left ile açılır, isBorder yazmak unknown-prop verir.",
137
+ typographyStyle: STYLE_GROUP_NOTES.typographyStyle + ' ' + STYLE_GROUP_NOTES.colorTokens,
138
+ hoverStyle: STYLE_GROUP_NOTES.hoverStyle,
139
+ sectionStyle: "{ headerDivider, footerDivider, dividerColor, sectionPadding(CSS uzunluğu: '12px') }. Ayraç anahtarı false ise bileşenin kendi ayracı KALDIRILIR (yok sayılmaz).",
140
+ style: STYLE_GROUP_NOTES.styleEscape + " Parçalar: '&__header' · '&__body' · '&__footer' — ancak yalnız '&__body' gerçek bir elemana denk gelir (CQ-157)."
98
141
  }
99
142
  },
100
143
  'box-section': {
@@ -135,6 +178,7 @@ export const componentKnowledge = {
135
178
  'closeOnOutside kapatılırsa panel yalnız tetikleyiciyle ya da akışla kapanır.'
136
179
  ],
137
180
  propNotes: {
181
+ surfaceStyle: NODE_SURFACE_NOTE,
138
182
  position: 'Panelin tetikleyiciye göre yönü; yer yoksa kaydırılır.',
139
183
  isOpen: "Açık/kapalı durumu; open/close olaylarıyla geri yazılır."
140
184
  }
@@ -177,7 +221,7 @@ export const componentKnowledge = {
177
221
  aibox: {
178
222
  summary: "box'ın akış/bölge işlevini taşıyan kutu; farkı STİL YÖNETİMİNDE: yüzey, tipografi, hover ve bölge görünümü tipli prop gruplarıdır (surfaceStyle · typographyStyle · hoverStyle · sectionStyle), ham CSS değil.",
179
223
  behavior: [
180
- "Yerleşim prop'ları (layout/direction/gap/gridColumns...) box ile BİREBİR aynıdır ve bileşende okunmaz — çocuk sarmalayıcısını RenderComponent/Draggable giydirir. layout:'cssgrid' gerçek display:grid akışıdır (container'ın mutlak 12 kolon 'grid'i DEĞİL).",
224
+ "Yerleşim prop'ları (layout/direction/gap/gridColumns...) box ile BİREBİR aynıdır ve bileşende okunmaz — çocuk sarmalayıcısını RenderComponent/Draggable giydirir. layout değerleri de box ile aynı: 'flex' | 'grid' (2026-09-02'de 'cssgrid' seçenek olmaktan çıktı; o değerle kayıtlı düğümler gerçek display:grid akışıyla çizilmeye devam eder).",
181
225
  "KATMAN SIRASI (K4): tema componentStyles → $preset TABANDIR (varsayılan verir); tipli grup alanları temayı alan alan EZER (tasarımcının/AI'ın açık seçimi kazanır); düğümün serbest style'ı her şeyin üstünde SON SÖZDÜR.",
182
226
  "TEMA PRESET'İ: bir tema preset'ini düğüme bağlamak için style.$preset: '<ad>' yaz (adlar project_get → theme.presets; componentStyles[type] referanssız, tipin her örneğine uygulanır). Bir alanı 'temaya bırakmak' = o alanı HİÇ yazmamak (null/boş) — studio'daki 'Temaya dön' tam olarak bunu yapar; tema değerini gruba kopyalamak bağı koparır.",
183
227
  "STİL YAZMA SIRASI: önce tipli gruplar (alan varsa oraya), grubun karşılamadığı karar style kanalına (custom properties). Tipli alan dururken aynı özelliği style'a yazmak paneli yanıltır — style kazanır ama panel alanı 'etkisiz' görünür.",
@@ -189,6 +233,7 @@ export const componentKnowledge = {
189
233
  surfaceStyle: STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens,
190
234
  typographyStyle: STYLE_GROUP_NOTES.typographyStyle + ' ' + STYLE_GROUP_NOTES.colorTokens,
191
235
  hoverStyle: STYLE_GROUP_NOTES.hoverStyle,
236
+ gapCustom: 'Sayısal px boşluğu (0-200); doluysa gap preset\'ini EZER. Eski kanal — yeni şemada gap alanının kendisi ham CSS değeri kabul eder (allowCustom).',
192
237
  sectionStyle: "{ headerDivider, footerDivider, dividerColor, sectionPadding(CSS uzunluğu: '12px') }. Ayraç anahtarı false ise bileşenin kendi ayracı KALDIRILIR (yok sayılmaz).",
193
238
  style: STYLE_GROUP_NOTES.styleEscape + " Parça olarak yalnız '&__body' bildirilmiştir çünkü markup'ta gerçekten o eleman vardır."
194
239
  }
@@ -200,13 +245,13 @@ export const componentKnowledge = {
200
245
  "KATMAN SIRASI (K4): tema componentStyles → $preset TABANDIR; tipli grup alanları temayı alan alan EZER; düğümün serbest style'ı SON SÖZDÜR. Yüzey/tipografi/hover/başlık kararlarını style'a ham CSS olarak yazma — alanı varsa gruba yaz.",
201
246
  "page_container'ın isLoading prop'u sayfa açılış akışının yükleniyor bayrağıdır.",
202
247
  "layout:'flex' çocukları akışa alır (RenderComponent düzeni .ng-app__node-children sarmalına yazar): sıra blocks[].position'dan gelir (önce y, sonra x) ama y BOŞLUKLARI yok sayılır — flex'te dikey konum yalnız sıradır, ızgara koordinatı değil.",
203
- "Flex çocuğun GENİŞLİĞİ yine 12 kolonluk blocks[].size.w'dur (row'da flex-basis w/12, column'da width w/12) — içerik genişliği DEĞİL. Yani ızgara↔flex geçişi kayıpsızdır ve genişlik şemadan kurulabilir.",
204
- "Flex kapsayıcının yüksekliği içeriğinden gelir; verticalAlign'ın dağıtacak boşluğu olması için appearance.heightType:'fixed' + size.h vermek gerekir. İstisna: states.isWrapper:true olan page_container zaten min-height:80vh ile sayfa boyu durur, onda dikey hizalama boşluksuz da çalışır.",
248
+ "Flex çocuğun GENİŞLİĞİ appearance.widthType'tan gelir (2026-09-01): 'fill' kabın tüm genişliği (satırda flex:1 1 100%), 'fixed' + widthString sabit CSS ölçüsü ('240px'/'50%'/calc), 'auto' içerik kadar. widthType HİÇ yazılmamışsa eski 12 kolonluk blocks[].size.w payı geçerlidir (row'da flex-basis w/12, column'da width w/12) — ızgara↔flex geçişi bu fallback sayesinde kayıpsızdır. Şemayı sen kuruyorsan widthType'ı AÇIKÇA yaz; string yalnız 'fixed' modda okunur (PAGE-017/018 uyarıları).",
249
+ "Flex kapsayıcının yüksekliği içeriğinden gelir; verticalAlign'ın dağıtacak boşluğu olması için appearance.heightType:'fixed' vermek gerekir — ölçü heightString'den (serbest CSS uzunluğu, ör. '30vh'), o yoksa size.h×10px'ten. heightString yalnız heightType:'fixed' iken okunur. İstisna: states.isWrapper:true olan page_container zaten min-height:80vh ile sayfa boyu durur, onda dikey hizalama boşluksuz da çalışır.",
205
250
  "ÜÇÜNCÜ YÜKSEKLİK: appearance.heightType:'fill' (2026-08-28) — yükseklik ne içerikten ne satır sayısından gelir, EBEVEYNİN BIRAKTIĞI yerden gelir: ızgara çocuğunda height:100% + align-self:stretch, flex sütununda flex:1 1 auto + min-height:0, flex satırında align-self:stretch + height:100%. Kaydırılabilir bir fill düğümü büyümek yerine içinde kaydırır. KÖK düğümde ekranı doldurur (100vh) — yerleşim kabuğu bununla kurulur.",
206
251
  "layout:'grid' iken diğer altı prop okunmaz — Studio panelinde de gizlenir. Katalogda tanımlı oldukları için doğrulama duvarı susar: ızgara kapsayıcıya direction yazmak sessizce hiçbir şey yapmaz (gateway PAGE-015 ile uyarır).",
207
252
  "Katalog varsayılanı 2026-08-25'te grid → FLEX oldu ama varsayılan düğüm KURULURKEN props'a yazılır (Studio bırakma yolu definition.defaults'u kopyalar), renderer'da geriye dönük bir etkisi YOKTUR: layout prop'u hiç olmayan düğüm — bütün eski kayıtlar — ızgara çizilmeye devam eder. Yani şemayı sen kuruyorsan varsayılana güvenme, layout'u açıkça yaz.",
208
253
  "YERLEŞİM (layout) KURARKEN DE TERCİH EDİLEN KAPTIR: layout views'ü sıradan bir komponent ağacıdır ve kökü container'dır — Studio'nun yeni layout iskeleti (getDefaultLayout) tam olarak bunu kurar: states.isWrapper:true + appearance.heightType:'fill' + block x0/w12 kök (sayfanın page_container'ının yerleşim ikizi: çerçevesiz, başlıksız, ekranı dolduran), içinde yan menü kabı (w2, fill) + içerik sütunu (w10, fill, direction:'column') → üst şerit + slot (fill). layout-shell/layout-region ikilisi emekliye ayrıldı (platform_guide('layout')).",
209
- "MOBİL TUZAĞI — flex çocukları telefonda ALT ALTA DÜŞMEZ: runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer (RenderComponent.svelte, @media max-width:767px → grid-row/grid-column/height), flex çocuğu genişliğini getFlexChildStyle'ın flex-basis'inden alır ve flex-wrap:nowrap altında satır bozulmaz. 375px'te ölçüldü: aynı span'lerle ızgara kabın çocukları 375/375px alt alta, flex kabınkiler 49/310px yan yana. Telefonda yığılması gereken satır için layout:'grid' kullan, ya da wrap:true ile taşmayı aç (zeyrek-frontend docs/issues.md ARC-031)."
254
+ "MOBİL TUZAĞI — flex çocukları telefonda ALT ALTA DÜŞMEZ: runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer (RenderComponent.svelte, @media max-width:767px → grid-row/grid-column/height), flex çocuğu genişliğini getFlexChildStyle'ın flex-basis'inden alır ve flex-wrap:nowrap altında satır bozulmaz. 375px'te ölçüldü: aynı span'lerle ızgara kabın çocukları 375/375px alt alta, flex kabınkiler 49/310px yan yana. Telefonda yığılması gereken satır için layout:'grid' kullan; wrap artık varsayılan açık (2026-09-01) ama sarma yalnız sığmayan çocuğu alta atar, ızgara yığılmasının yerini tutmaz (zeyrek-frontend docs/issues.md ARC-031)."
210
255
  ],
211
256
  propNotes: {
212
257
  surfaceStyle: STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens + " Kapsayıcının TABANI tema token'larıdır (--ng-background-surface zemin, 1px --ng-border-primary çerçeve, --ng-rounded köşe); alan boş bırakılırsa tema izlenir. contentOverflow gövdeye ('&__body') yazılır.",
@@ -218,25 +263,28 @@ export const componentKnowledge = {
218
263
  horizontalAlign: "left | center | right | stretch. row'da ANA eksen (justify-content), column'da ÇAPRAZ eksen (align-items). stretch: ana eksende space-between, çapraz eksende tam doldurma.",
219
264
  verticalAlign: "top | middle | bottom | stretch. row'da ÇAPRAZ eksen, column'da ANA eksen — horizontalAlign ile rollerini değiştirirler.",
220
265
  size: "'shrink' (çocuklar flex: 0 1 auto, varsayılan) | 'grow' (flex: 1 1 auto — çocuklar boşluğu paylaşır).",
221
- gap: "none | small | medium (varsayılan) | large → 0 / 8 / 16 / 32px. Çocuk genişlik formülünden payı düşülür, böylece w toplamı 12 olan satır tam kapanır.",
222
- wrap: "flex-wrap: wrap. Kapalıyken (varsayılan) satıra sığmayan çocuklar sıkışır, alt satıra inmez.",
223
- title: "isWrapper:false kapta başlık çizer; page_container'da ise tarayıcı sekmesinin adını yazar (browserTitle).",
266
+ gap: "none | small | medium (varsayılan) | large → tema değişkenleri: 0 / var(--ng-spacing-sm) / --ng-spacing-md / --ng-spacing-lg (varsayılan temada 8/16/32px). Alan allowCustom'dır: '18px' ya da 'var(--ng-spacing-xl)' gibi ham CSS değeri de geçer. Çocuk genişlik formülünden payı düşülür, böylece w toplamı 12 olan satır tam kapanır.",
267
+ wrap: "flex-wrap: wrap. Varsayılan AÇIK (2026-09-01'de flipledi): w:12 çocuklar alt satıra sararak dikey akar. Kapatırsan satıra sığmayan çocuklar sıkışır, alta inmez.",
268
+ title: "Varsayılanı boştur. isWrapper:false kapta yalnız açıkça doluysa başlık çizer; page_container'da ise dolu değer tarayıcı sekmesinin adını yazar (browserTitle).",
224
269
  isLoading: 'Gövdeyi 100px’e kısar ve spinner gösterir; sayfa açılış akışında bayrak olarak kullanılır.'
225
270
  }
226
271
  },
227
272
  repeater: {
228
273
  summary: 'Kart listesi: rows dizisindeki her satır için children şablonunu yineler; satır bağlamı item/Row olarak çocuklara iner.',
229
274
  propNotes: {
275
+ surfaceStyle: NODE_SURFACE_NOTE,
230
276
  rows: 'Yinelenen veri; çocuk komponent ifadelerinde satıra item / Row ile erişilir.',
231
- selectedIndex: 'change olayında seçilen kartın sırası.'
277
+ selectedIndex: "change olayının YÜKÜDÜR, şema prop'u DEĞİL (component_get prop listesinde görünmez; şemadaki gizli alan selectedItem'dır). RenderComponent tıklanan kartın sırasını bu adla yayar, akışta self.selectedIndex ile okunur."
232
278
  }
233
279
  },
234
280
  modal: {
235
281
  summary: 'Ortalanmış katman; şemanın KÖKÜNDE page_container’ın kardeşi olarak durur, içeriği (kendi formu ve submit akışı dahil) children’ına eklenir.',
236
- behavior: ["open/close olayları akışa bağlanabilir; açma/kapama isHidden ile değil kendi open state'iyle yürür (setter ile isOpen yaz)."]
282
+ behavior: ["open/close olayları akışa bağlanabilir; açma/kapama isHidden ile değil kendi open state'iyle yürür (setter ile isOpen yaz)."],
283
+ propNotes: { surfaceStyle: NODE_SURFACE_NOTE }
237
284
  },
238
285
  drawer: {
239
- summary: 'Yandan açılan katman; modal ile aynı kurallar: şema kökünde yaşar, kendi children ve akışlarını taşır.'
286
+ summary: 'Yandan açılan katman; modal ile aynı kurallar: şema kökünde yaşar, kendi children ve akışlarını taşır.',
287
+ propNotes: { surfaceStyle: NODE_SURFACE_NOTE }
240
288
  },
241
289
  dropdown: {
242
290
  summary: 'Tek seçim listesi; options statik verilebilir ya da mount/search akışıyla uzaktan doldurulur.',
@@ -265,18 +313,20 @@ export const componentKnowledge = {
265
313
  checkbox: { summary: 'İki durumlu kutu; change/checked/unchecked olayları boolean taşır.' },
266
314
  switch: { summary: 'Anahtar; checkbox ile aynı sözleşme, görsel fark.' },
267
315
  radio: { summary: 'Tek seçim düğme grubu; change seçilen değeri taşır.' },
268
- datepicker: { summary: 'Tarih seçici; change yükü { value, HH, mm }.' },
269
- datetimepicker: { summary: 'Tarih+saat seçici; değer event.data’da değil self.value’dadır.' },
270
- daterangepicker: { summary: 'Tarih aralığı; change yükü { valueFrom, valueTo, HH, mm }.' },
271
- datetimerangepicker: { summary: 'Tarih+saat aralığı; daterangepicker ile aynı yük.' },
316
+ datepicker: { summary: 'Tarih seçici; change yükü { value, HH, mm }.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
317
+ datetimepicker: { summary: 'Tarih+saat seçici; değer event.data’da değil self.value’dadır.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
318
+ daterangepicker: { summary: 'Tarih aralığı; change yükü { valueFrom, valueTo, HH, mm }.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
319
+ datetimerangepicker: { summary: 'Tarih+saat aralığı; daterangepicker ile aynı yük.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
272
320
  fileUpload: { summary: 'Sürükle-bırak dosya yükleme (dropzone); seçim self.value’dan okunur.' },
273
321
  fileInput: { summary: 'Dosya seç girişi; change yükü { value: [{ type, value }], fileName }.' },
274
322
  filebutton: { summary: 'Buton görünümlü dosya seçici; fileInput ile aynı yük.' },
275
- 'file-viewer': { summary: 'Dosya önizleyici (pdf/görsel); file prop’una drive dosyası bağlanır.' },
323
+ 'file-viewer': { summary: 'Dosya önizleyici (pdf/görsel); file prop’una drive dosyası bağlanır.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
276
324
  button: {
277
325
  summary: 'Tıklama kaynağı; click olayı veri taşımaz, bağlam gerekiyorsa ifadede komponent adlarıyla okunur.',
278
326
  propNotes: {
279
- kind: "success | error | info | primary | warning. `datatable.actions[].kind` FARKLI bir sözlük kullanır (error · information · solid · transparent · dark · danger · warning) — buradan bir değer (örn. 'transparent') butona yazılırsa invalid-enum-value verir."
327
+ kind: "primary | info | success | warning | error | custom — 'custom' seçilirse renk customColor'dan gelir. `datatable.actions[].kind` FARKLI bir sözlük kullanır (error · information · solid · transparent · dark · danger · warning) — buradan bir değer (örn. 'transparent') butona yazılırsa invalid-enum-value verir.",
328
+ customColor: "Yalnız kind:'custom' iken okunur; hex ya da tema token'ı ('var(--ng-accent-primary)').",
329
+ badgeValue: "Butonun köşe rozetinin metni ('3', '99+'); boşsa rozet çizilmez. Rengi badgeColor'dan gelir (primary|info|success|warning|error, varsayılan 'error')."
280
330
  }
281
331
  },
282
332
  'dropdown-button': { summary: 'Menülü buton; clickItem olayı tıklanan menü öğesini taşır.' },
@@ -290,8 +340,8 @@ export const componentKnowledge = {
290
340
  ]
291
341
  },
292
342
  'qm-card': { summary: 'QM gösterge kartı; click yayar.' },
293
- tabs: { summary: 'Sekmeler; tab olayı seçilen sekme anahtarını taşır, her view children sırasıyla eşleşir.' },
294
- 'collapsible-container': { summary: 'Aç/kapa kapsayıcı; collapsed/expanded olayları yayar.' },
343
+ tabs: { summary: 'Sekmeler; tab olayı seçilen sekme anahtarını taşır, her view children sırasıyla eşleşir.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
344
+ 'collapsible-container': { summary: 'Aç/kapa kapsayıcı; collapsed/expanded olayları yayar.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
295
345
  'tree-view': {
296
346
  summary: 'Hiyerarşik liste: data ağacını çizer, satır tıklamasını click olayıyla akışa verir, satır başına aksiyon menüsü ve isteğe bağlı istemci-tarafı arama taşır.',
297
347
  behavior: [
@@ -336,12 +386,14 @@ export const componentKnowledge = {
336
386
  notification: {
337
387
  summary: 'Sayfa içi geri bildirim kutusu; akışlarda alert adımı yerine bu komponent kullanılır.',
338
388
  behavior: [
339
- "isHidden bu komponentin KATALOĞUNDA YOKTUR: şemaya props.isHidden yazmak unknown-prop ile reddedilir (altın örnekte bir dönem yanlış duruyordu). Runtime'da gizleme çalışır — RenderComponent her komponentte props.isHidden'a bakar — ama prop'u oraya SETTER yazar, şema değil.",
389
+ "isHidden 2026-08-26'dan beri bu komponentin ŞEMASINDA VARDIR (daha önce yoktu ve bu katalog bir dönem 'unknown-prop verir' diyordu): props.isHidden'ı doğrudan şemaya yazabilirsin, başlangıçta gizli bildirim için container'a sarmak GEREKMEZ.",
340
390
  "appearance.isHidden bir çözüm DEĞİLDİR: kaydedilir ama runtime hiçbir yerde okumaz (renderer yalnız props.isHidden'a bakar).",
341
- 'Başlangıçta gizli bir bildirim istiyorsanız komponenti şemada bırakın ve içeriğini/görünürlüğünü akıştaki setter ile yönetin; timeout verilirse kendini kapatır.'
391
+ "Kendini KAPATIRKEN props.isHidden'a yazar: hem timeout dolduğunda hem kapatma düğmesinde 'toggled' emit'i { isHidden: true } taşır (Notification.svelte handleEvent). Şema events sözlüğünde bu ad YOKTUR — akışa bağlanacak bir olay değil, prop'a geri yazma yoludur.",
392
+ 'Başlangıçta gizli bir bildirim: şemaya props.isHidden:true yaz, açılışı akıştaki setter ile yap; timeout verilirse kendini kapatır.'
342
393
  ],
343
394
  propNotes: {
344
- kind: 'information | error | success | warning (buton kind sözlüğünden farklıdır: orada info/primary vardır)'
395
+ kind: 'information | error | success | warning (buton kind sözlüğünden farklıdır: orada info/primary vardır)',
396
+ isHidden: 'Şemada tanımlıdır; statik true ile gizli başlatılır, akıştaki setter ile açılıp kapanır. Komponent kendi kapanışında da bu prop\'a yazar.'
345
397
  }
346
398
  },
347
399
  'form-validation-errors': { summary: 'Formun doğrulama hatalarını listeler; form.errorElementId buraya işaret eder.' },
@@ -352,7 +404,7 @@ export const componentKnowledge = {
352
404
  pageTitle: "Bant başlığı; page_container.title ile karıştırılmamalı. `description` diye bir prop YOKTUR (unknown-prop verir).",
353
405
  links: 'Sağ tarafta çizilen aksiyon/bağlantı listesi.',
354
406
  breadcrumbStyle: 'Kırıntı izinin görünüm biçimi.',
355
- isHidden: "Bu komponentin kataloğunda VAR (bazı komponentlerde yok — bkz. 'notification'); doğrudan şemaya yazılabilir."
407
+ isHidden: "Doğrudan şemaya yazılabilir. 2026-09-08 ölçümü: 101 kayıttan 99'u isHidden tanımlıyor, tanımlamayan yalnız 'slot' ve 'widget-area'."
356
408
  }
357
409
  },
358
410
  header: {
@@ -386,16 +438,19 @@ export const componentKnowledge = {
386
438
  style: "Yalnız &__header, &__header-logo, &__header-title, &__header-subtle ve &__content gerçekten uygulanır; &__navigations* parçaları ölüdür (bkz. davranış)."
387
439
  }
388
440
  },
389
- 'auth-layout': { summary: 'Giriş ekranı kabuğu: logo kolonu + form slotu; primaryClick/secondaryClick olayları yayar.' },
441
+ 'auth-layout': { summary: 'Giriş ekranı kabuğu: logo kolonu + form slotu; primaryClick/secondaryClick olayları yayar.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
390
442
  layout: {
391
443
  summary: 'Layout kök komponenti: logo/başlık, kullanıcı menüsü, kırıntı yolu ve menü ağacı bir arada; sayfalar slot’una yerleşir, logout olayı yayar.',
392
444
  behavior: [
393
445
  "Menü ağacını sidebar ile AYNI renderer çizer (Navigations/NavigationsItem): activeHref prop'u, adresten otomatik eşleşme, isExpandable/isExpanded ve grup başlığı kuralları birebir aynıdır — ayrıntı sidebar kaydında.",
394
446
  "Öğe bazlı isHidden BU şemada tanımlıdır (sidebar'ınkinde yoktur): true olan öğe canlıda elenir, Studio'nun düzenleyici kipinde görünmeye devam eder.",
395
- "TEK PARÇA (monolit) kabuktur: logo, başlık, menü, kırıntı yolu ve kullanıcı menüsü sabittir — yalnız DOLDURULUR, yeniden düzenlenemez. Serbest kompozisyon için container + slot kullan (platform_guide('layout')); mevcut monolitler çalışmaya devam eder ve Studio'da 'Break apart' ile parçalarına ayrılabilir."
447
+ "TEK PARÇA (monolit) kabuktur: logo, başlık, menü, kırıntı yolu ve kullanıcı menüsü sabittir — yalnız DOLDURULUR, yeniden düzenlenemez. Serbest kompozisyon için container + slot kullan (platform_guide('layout')); mevcut monolitler çalışmaya devam eder ve Studio'da 'Break apart' ile parçalarına ayrılabilir.",
448
+ "İÇERİK ALANI BİR KAPTIR (2026-09-03): container ile aynı yedi yerleşim prop'unu taşır (layout/direction/horizontalAlign/verticalAlign/size/gap/wrap, katalog varsayılanı layout:'flex'); slot ve içine bırakılan başka çocuklar bu akışla dizilir. Studio'nun yeni kabuk iskeleti (getDefaultLayout) düğüme layout:'flex' + verticalAlign:'stretch' + gap:'none' yazar. Prop hiç yazılmamış eski kayıtlar ızgara çizilmeye devam eder — container'daki kuralın aynısı."
396
449
  ],
397
450
  propNotes: {
398
- activeHref: "Vurgulanacak menü satırının adresi; boşken adres çubuğu karar verir (sidebar ile aynı davranış). YENİ prop — platform kataloğuna CI senkronuyla iner."
451
+ surfaceStyle: NODE_SURFACE_NOTE,
452
+ activeHref: "Vurgulanacak menü satırının adresi; boşken adres çubuğu karar verir (sidebar ile aynı davranış). YENİ prop — platform kataloğuna CI senkronuyla iner.",
453
+ layout: "'flex' (katalog varsayılanı; akış) | 'grid' (12 kolonluk ızgara). Prop hiç yazılmamışsa renderer IZGARA çizer. Diğer altı yerleşim prop'u yalnız 'flex' iken etkilidir; anlamları container kaydındakiyle birebir aynı."
399
454
  }
400
455
  },
401
456
  navigations: {
@@ -423,6 +478,7 @@ export const componentKnowledge = {
423
478
  "isViewportFilling / isShellRoot yetenekleri ve states.isShell bayrağı BACKEND'DE SAKLANMAZ (components tablosunda sütun yok, States kaydında isShell yok — zeyrek-frontend docs/issues.md BE-002). Davranışı tip adına düşen yedek listeler taşır; şema yazarken bu bayraklara güvenme."
424
479
  ],
425
480
  propNotes: {
481
+ surfaceStyle: NODE_SURFACE_NOTE,
426
482
  sidebarOpen: 'Yan panelin açık olup olmadığı; canlı durumdur, bağlanabilir.',
427
483
  mobileBehavior: "'drawer' (varsayılan; mobilde kapalı başlar) | 'stack' (mobilde açık başlar).",
428
484
  style: "&__body ve &__overlay parçaları uygulanır."
@@ -437,6 +493,7 @@ export const componentKnowledge = {
437
493
  "appearance.hideOn, dockMobile'ı HER ZAMAN yener; dockMobile:'hidden' yalnız bölge-yerel kısayoldur."
438
494
  ],
439
495
  propNotes: {
496
+ surfaceStyle: NODE_SURFACE_NOTE,
440
497
  dock: "left | right | top | bottom | fill — biçim/yön, konum değil.",
441
498
  dockMobile: "inherit (varsayılan; masaüstünü izler) | left | right | top | bottom | drawer (içeriğin üstüne biner) | hidden.",
442
499
  isCollapsible: 'Bölgenin kapanabilir olup olmadığı.',
@@ -452,10 +509,17 @@ export const componentKnowledge = {
452
509
  ]
453
510
  },
454
511
  view: { summary: 'Tabs/switcher altındaki görünüm kapsayıcısı; children gruplamaya yarar.' },
455
- 'widget-area': { summary: 'Widget’ların yerleştirildiği alan.' },
512
+ 'widget-area': {
513
+ summary: 'Widget’ların yerleştirildiği alan: widget şemasının KÖK düğümü (widgetType: container).',
514
+ behavior: [
515
+ 'Kök bir KAPTIR ve container ile aynı yerleşim proplarını taşır (layout, direction, horizontalAlign, verticalAlign, size, gap, wrap); varsayılan layout FLEX’tir — yeni widget’ın alanı 12 kolonlu grid’e değil flex akışına düşer.',
516
+ 'layout: grid seçildiğinde flex alanları (direction/hizalama/size/gap/wrap) panelde gizlenir; kanvas mutlak 12 kolon ızgarasına döner.',
517
+ 'isLoading davranış prop’udur (stil değil): açıkken alan içeriği yerine Loader çizilir.'
518
+ ]
519
+ },
456
520
  'audit-basic': { summary: 'Kayıt meta kutusu (oluşturan/güncelleyen); meta prop’u tek setter ile beslenir.' },
457
521
  'audit-history': { summary: 'Kayıt geçmişi listesi.' },
458
- 'document-editor': { summary: 'Office şablon doldurma/görüntüleme editörü; file prop’una drive dosyası bağlanır.' },
522
+ 'document-editor': { summary: 'Office şablon doldurma/görüntüleme editörü; file prop’una drive dosyası bağlanır.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
459
523
  'qm-header': {
460
524
  summary: 'QM uygulama başlığı: global arama, bildirimler, hesap menüsü, çok seviyeli mega menü.',
461
525
  behavior: [
@@ -465,8 +529,8 @@ export const componentKnowledge = {
465
529
  'Sonuç: menü href’i sayfanın gerçek yoluna çözülüyorsa vurgu ek ayar istemeden çalışır; çözülmüyorsa vurguyu elle açmanın yolu yoktur.'
466
530
  ]
467
531
  },
468
- 'qm-sidebar': { summary: 'QM yan menüsü.' },
469
- 'qm-content': { summary: 'QM içerik kapsayıcısı.' },
532
+ 'qm-sidebar': { summary: 'QM yan menüsü.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
533
+ 'qm-content': { summary: 'QM içerik kapsayıcısı.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
470
534
  // — 2026-08-25 eşleme turu: ng-apps-ui kataloğunda olup burada kaydı olmayan 22 komponent.
471
535
  // Kaynak: packages/ng-apps-ui/src/components/<ad>/{schema.ts,*.svelte,*-logic.ts} +
472
536
  // zeyrek-frontend/docs/components/*.md davranış sözleşmeleri.
@@ -633,7 +697,9 @@ export const componentKnowledge = {
633
697
  summary: 'Son kullanıcının sayfa dilini değiştirdiği açılır liste; projenin aktif dillerini kendi ana dil adlarıyla listeler.',
634
698
  behavior: [
635
699
  "change olayında runtime machine.setLanguage(value) çağırır: seçim localStorage'a (zeyrek:{appSlug}:lang) yazılır ve çeviri bağlı bütün prop'lar SAYFA YENİLENMEDEN yeniden çözülür.",
636
- 'Projede dil tanımlı değilse liste boş çizilir — önce proje dillerini (Translations paneli) kur.'
700
+ 'Projede dil tanımlı değilse liste boş çizilir — önce proje dillerini (Translations paneli) kur.',
701
+ "Dil kümesi KAPALI DEĞİLDİR: projeye eklenen her ISO 639-1 kodu listede çıkar (project_update languages). Komponent kütüphanesinin kendi arayüz metni (sayfalama, filtre operatörleri, boş durumlar) yalnız tr/en sözlüğü taşır ve sözlüğü olmayan dilde İNGİLİZCEYE düşer — sayfa içeriği ve sayı/tarih biçimi ise seçilen dilin kendisini kullanır. Yani 'de' seçili bir uygulamada tablo sayfalaması İngilizce, sayılar Almanca ayraçla çıkar; bu bilinçlidir.",
702
+ "Sekme başlığı (page.browserTitleKey) bu değişimi İZLEMEZ: açılıştaki dilde kalır, sayfa yeniden yüklenince düzelir (platform_guide('translation'))."
637
703
  ],
638
704
  guide: 'translation'
639
705
  },
@@ -870,12 +936,100 @@ export const componentKnowledge = {
870
936
  "Form validations sistemiyle entegrasyonu YOKTUR (kapı yalnız çözülen ifade değeridir); Studio kanvasında adımlar arası geçiş Element Tree üzerindendir."
871
937
  ],
872
938
  propNotes: {
939
+ surfaceStyle: NODE_SURFACE_NOTE,
873
940
  steps: "[{ title, description, icon, optional, canProceed }] — tabs'ın views deseni; her adım children'da kendi kapsayıcısını taşır.",
874
941
  canProceed: "Aktif adımın kapısı; ifadeye bağlanır. Yalnız false/'false' engeller.",
875
942
  currentStep: '0 tabanlı aktif adım; setter hedefi, aralık dışı değer kırpılır.'
876
943
  }
877
944
  }
878
945
  };
946
+ /**
947
+ * APPEARANCE JETON KÜMELERİ — tek kaynak (S-2 · P1-2).
948
+ *
949
+ * Ajan `gap` jetonlarına (`none`/`small`/`medium`) benzeterek `paddingType:'large'` yazdı;
950
+ * platform 200 döndü ve değeri sessizce `none`a ÇEVİRDİ — kart sıfır iç boşlukla çizildi.
951
+ * Bu, 2026-09-06 koşusunun 32 sessiz düşme satırından tek gerçek olanıydı.
952
+ *
953
+ * `component_get` yanıtında alan adı `appearanceValues`'tur, `appearanceTokens` DEĞİL: gizli-veri
954
+ * maskeleyicisinin `secretKeyPattern`'i "token" geçen her anahtarı `***` yapıyor ve küme sessizce
955
+ * kayboluyordu (ölçüldü 2026-09-06, gerçek uçta).
956
+ *
957
+ * Kümeler tahmin değil: `zeyrek-backend` `DesignComponent.cs` enum'larının `[Description]`
958
+ * değerleridir (`ComponentPaddingType` · `ComponentMarginType` · `ComponentWidthType` ·
959
+ * `ComponentHeightType` · `ComponentAlignType`) — platformun kabul ettiği kümenin tek tanımı
960
+ * orasıdır ve backend artık tanımadığı jetonu 400 ile reddeder.
961
+ */
962
+ export const APPEARANCE_TOKEN_SETS = {
963
+ paddingType: {
964
+ values: ['none', 'normal', 'custom'],
965
+ note: "'custom' verildiğinde ölçü paddingValue (tek sayı) ya da paddingValues ({top,right,bottom,left}) alanından gelir. 'large'/'small' gibi jeton YOKTUR."
966
+ },
967
+ marginType: {
968
+ values: ['none', 'normal', 'custom'],
969
+ note: "'custom' verildiğinde ölçü marginValue ya da marginValues alanından gelir."
970
+ },
971
+ widthType: {
972
+ values: ['cover', 'contain', 'auto', 'fixed', 'fill'],
973
+ note: "'fixed' widthString'i (serbest CSS uzunluğu) okur; başka modda widthString yok sayılır (PAGE-017/018)."
974
+ },
975
+ heightType: {
976
+ values: ['fixed', 'auto', 'fill'],
977
+ note: "'fixed' heightString'i okur, yoksa size.h×10px kullanır; 'fill' yüksekliği ebeveynin bıraktığı yerden alır."
978
+ },
979
+ alignType: {
980
+ values: ['left', 'center', 'right'],
981
+ note: "widthType 'auto'/'contain' iken yatay hizalamayı belirler; 'fill' modda etkisizdir."
982
+ }
983
+ };
984
+ /**
985
+ * STATES BAYRAKLARI — "true iken ne olur" (S-9 · P1-5).
986
+ *
987
+ * Katalogda `isWrapper` saf bir yerleşim bayrağı gibi duruyordu; 2026-09-06 koşusunda giriş
988
+ * kartı bütün ekranı kapladı ve sebebi ancak deneyerek bulundu. Notlar runtime çizim
989
+ * kaynağından okundu: `zeyrek-frontend` `packages/ng-apps-ui/src/shared/render/render.functions.ts`
990
+ * (kutu stili) ve `packages/ng-apps-screen-builder/src/layering.functions.ts` (katmanlama).
991
+ *
992
+ * `states` bir PROP DEĞİL düğüm alanıdır: `replace_prop` yazamaz, kurulu bir düğümde
993
+ * `resize_component` micro-op'unun `states` alanıyla değişir.
994
+ */
995
+ export const STATE_FLAG_NOTES = {
996
+ isWrapper: "Kap salt düzen elemanı olur: height:100% + MIN-HEIGHT:80vh + overflow-y:inherit yazılır ve iç dolgu sıfırlanır — sayfanın altına yüzlerce piksel ölü boşluk ekleyebilir (ölçüldü 2026-09-05: dokuz düğüm ~1600px; 2026-09-06: giriş kartı bütün ekranı kapladı). style ile ezilemez. Ayrıca title ve __header bloğu ÇİZİLMEZ, zemin/çerçeve taşımaz. Sayfa kökü (page_container) ve yerleşim sarmalayıcıları dışında gerekmiyorsa false bırakın.",
997
+ isScrollable: 'Kutu büyümek yerine İÇİNDE kaydırır: overflow-y:auto + yükseklik (fill ise 100%, değilse heightString ya da size.h×10px). heightType:auto olan sıradan düğümde yükseklik auto kalır ve kaydırma etkisizdir. Yerleşim kökünde padding:0 + height:100% verir.',
998
+ isSticky: 'Düğüm position:sticky + left:0 olur ve kaydırırken yerinde kalır; z-index başlık tiplerinde 1000, diğerlerinde 1000-y — sticky, float eden komşuyu KASTEN yener. İç dolgu sıfırlanır ve piksel ince ayarı transform ile korunur.',
999
+ isDroppable: 'Düğüm çocuk kabul eder ve iç dolgusunu padding kutusundan alır (aksi hâlde dolgu hiç yazılmaz). Çocuk sahibi olduğu için appearance.aspectRatio bu düğümde YOK SAYILIR — oran yalnız kendi içeriğini taşıyan yaprakta çizilir.',
1000
+ isFloating: 'Izgara çocuğuna z-index verir (appearance.zIndex tabanlı) ve düğüm kardeşlerinin ÜSTÜNE biner; grid izi korunduğu için kardeşlerin yerleşimi değişmez. Sıradan düğümlerin z-index\'i yoktur, DOM sırasıyla yığılırlar.',
1001
+ isQuickStack: "Yalnız Studio ekran kurucusunun hızlı-yığın aracını işaretler (quick-stack.functions.ts); RUNTIME ÇİZİMİ BU BAYRAĞI HİÇ OKUMAZ — yayınlanan sayfada görsel etkisi yoktur."
1002
+ };
1003
+ /**
1004
+ * ADRESLENEBİLİR İÇ ROLLER (S-6 · G-1 · P1-3).
1005
+ *
1006
+ * `page_screenshot actions` hedefi `data-element-id` ile çözer ve o öznitelik şimdiye kadar
1007
+ * yalnız düğümün KÖKÜNDE yazılıyordu: 2026-09-06 koşusunda `target:'tbl_vacancy'` tablonun
1008
+ * köküne tıkladı, sayfalayıcıya değil. Sayfalama uçta doğrulandı ama TIKLAMA hiç sınanmadı ve
1009
+ * boşluk `platform_gap_report` ile `G-1` olarak bildirildi.
1010
+ *
1011
+ * Runtime artık iç kontrollere `<elementId>__<rol>` kimliği yazıyor; kaynak
1012
+ * `zeyrek-frontend` `packages/ng-apps-ui/src/shared/internal-element-id.ts` (`internalRolesFor`)
1013
+ * ve liste burada birebir aynadır. Kolon rolleri kolonun SLUG'ından türer, başlığından değil —
1014
+ * başlık çeviriye bağlıdır.
1015
+ */
1016
+ export const INTERNAL_ROLES_BY_TYPE = {
1017
+ table: [
1018
+ 'pager-prev',
1019
+ 'pager-next',
1020
+ 'pager-page (sayfa numarası kutusu)',
1021
+ 'pager-limit (sayfa boyu seçimi)',
1022
+ 'pager-refresh',
1023
+ "sort-<kolon slug'ı>",
1024
+ 'colmenu',
1025
+ 'row-<0 tabanlı sıra; sayfa değişince yeniden numaralanır>'
1026
+ ]
1027
+ };
1028
+ /** Hedefteki `__` ayracından sonrası bir iç roldür; gateway bulunamayan hedefte listeyi basar. */
1029
+ export const INTERNAL_ROLE_SEPARATOR = '__';
1030
+ export function knownInternalRoles() {
1031
+ return [...new Set(Object.values(INTERNAL_ROLES_BY_TYPE).flat())];
1032
+ }
879
1033
  export const knowledgeNote = 'knowledge alanı elle doğrulanmış davranış bilgisidir; kayıt yoksa davranış sözlüğe henüz işlenmemiştir — prop/olay semantiği tahmin etmeyin, gerekirse NgApps.UI kaynağına bakın.';
880
1034
  export function knowledgeFor(slug) {
881
1035
  return slug ? componentKnowledge[slug] : undefined;
@@ -20,6 +20,49 @@ export function formBindingWarnings(ops) {
20
20
  .filter(elementId => !boundForms.has(elementId))
21
21
  .map(elementId => `FORM-001: '${elementId}' formuna 'submit' olayı bağlanmadı — form kendi başına kayıt YAPMAZ, gönderimi bir flow yürütür. flow_create ile submit akışını kurup set_event { target: '${elementId}', event: 'submit', eventValue: <flow id> } ile bağlayın (örnek: ngapps://examples/page-form-upsert). Bağlamayı sonraki patch'e bıraktıysanız bu uyarıyı yok sayın.`);
22
22
  }
23
+ /**
24
+ * FORM-003 — formun `submit` olayı bağlı ama form içindeki butonun `click` olayı bağlı değil.
25
+ * ÖLÇÜLDÜ 2026-09-05 (Corecruiter koşusu, `S-18`): `ngapps://examples/page-sign-in` örneği akışı
26
+ * forma bağlıyor ve içine düz bir `button` koyuyor; runtime o butonu `<button type="button">`
27
+ * çiziyor, yani HTML form submit'i HİÇ doğmuyor. Butona tıklamak da alanda Enter da uca istek
28
+ * göndermedi, hata da çıkmadı — teşhis yalnız gerçek tıklamayla mümkün oldu. Örneğin kendi notu
29
+ * "kimlik middleware'i kuran her projede bu sayfa ZORUNLUDUR" dediği için tuzak, örneği izleyerek
30
+ * kurulan her uygulamanın giriş kapısında duruyor.
31
+ */
32
+ export function formSubmitButtonWarnings(ops) {
33
+ const operations = asObjects(ops);
34
+ const boundClicks = new Set(operations
35
+ .filter(op => op.op === 'set_event' && String(op.event ?? '') === 'click')
36
+ .map(op => String(op.target ?? '')));
37
+ const submitForms = new Set(operations
38
+ .filter(op => op.op === 'set_event' && String(op.event ?? '') === 'submit')
39
+ .map(op => String(op.target ?? '')));
40
+ const submitsNatively = (node) => {
41
+ const prop = (node.props ?? {}).type;
42
+ return String(prop?.data ?? prop ?? '') === 'submit';
43
+ };
44
+ const buttonsIn = (node) => {
45
+ const found = [];
46
+ for (const child of asObjects(node.children)) {
47
+ if (String(child.type ?? '') === 'button' && !submitsNatively(child))
48
+ found.push(String(child.elementId ?? '?'));
49
+ found.push(...buttonsIn(child));
50
+ }
51
+ return found;
52
+ };
53
+ return operations
54
+ .filter(op => op.op === 'add_component' && FORM_TYPES.has(String(op.component?.type ?? '')))
55
+ .flatMap(op => {
56
+ const component = (op.component ?? {});
57
+ const elementId = String(component.elementId ?? '?');
58
+ if (!submitForms.has(elementId))
59
+ return [];
60
+ const loose = buttonsIn(component).filter(buttonId => !boundClicks.has(buttonId));
61
+ if (loose.length === 0)
62
+ return [];
63
+ return [`FORM-003 (uyarı): '${elementId}' formunun 'submit' olayı bağlı ama içindeki buton(lar) ne props.type:'submit' taşıyor ne de 'click' olayı bağlı: ${loose.join(', ')}. Buton varsayılan olarak type="button" çizilir ve HTML form submit'i HİÇ doğmaz — tıklamak da Enter da uca istek göndermez, hata da çıkmaz (ölçüldü 2026-09-05). İki çözümden biri: butona props.type = 'submit' verin, ya da aynı akışı butonun click olayına da bağlayın (set_event { target: '${loose[0]}', event: 'click', eventValue: <aynı flow id> }).`];
64
+ });
65
+ }
23
66
  /**
24
67
  * FORM-002 — akıştaki yazma ucu (post/put/patch) hiç `source:'body'` parametresi taşımıyor:
25
68
  * istek gider, gövde boş olduğu için kayıt ya boş yazılır ya doğrulamadan döner. Form alanları uca
@@ -0,0 +1,30 @@
1
+ /**
2
+ * FLOW-011'in guard tanıması: lint `Steps.x.headers && Steps.x.headers["total-count"]` formunu
3
+ * guard'sız sanıp uyarıyordu ve aynı ifade için İKİ bulgu üretiyordu — regex her geçişi bağımsız
4
+ * saydığı, guard formu da kalıbı iki kez içerdiği için (İK koşusu E-6).
5
+ */
6
+ function stepRef(name) {
7
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
8
+ return `Steps\\s*(?:\\.\\s*${escaped}|\\[\\s*['"]${escaped}['"]\\s*\\])`;
9
+ }
10
+ const anyHeadersAccess = /Steps\s*(?:\.\s*([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])\s*\??\.\s*headers\b/g;
11
+ export function headersAccessNames(expression) {
12
+ const names = new Set();
13
+ let match;
14
+ anyHeadersAccess.lastIndex = 0;
15
+ while ((match = anyHeadersAccess.exec(expression)) !== null) {
16
+ names.add(match[1] ?? match[2] ?? '');
17
+ }
18
+ return [...names].filter(name => name !== '');
19
+ }
20
+ export function isHeadersAccessGuarded(expression, name) {
21
+ const ref = stepRef(name);
22
+ const unguardedRead = new RegExp(`${ref}\\s*\\.\\s*headers\\s*(?:\\.|\\[)`, 'g');
23
+ const guardedRead = new RegExp(`${ref}\\s*(?:\\?\\.\\s*headers|\\.\\s*headers\\s*\\?\\.)`, 'g');
24
+ const precondition = new RegExp(`${ref}\\s*(?:\\.\\s*headers\\s*)?&&`);
25
+ if (precondition.test(expression))
26
+ return true;
27
+ const unguarded = expression.match(unguardedRead)?.length ?? 0;
28
+ const guarded = expression.match(guardedRead)?.length ?? 0;
29
+ return unguarded === 0 && guarded > 0;
30
+ }