@appaflytech/zeyrek-ai-mcp 0.0.7 → 0.0.9

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 (54) hide show
  1. package/README.md +27 -21
  2. package/dist/config.js +2 -0
  3. package/dist/knowledge/backendConstraints.js +3 -0
  4. package/dist/knowledge/componentCatalog.js +172 -33
  5. package/dist/knowledge/componentEvents.js +35 -4
  6. package/dist/knowledge/errorPath.js +1 -1
  7. package/dist/knowledge/normalize.js +474 -23
  8. package/dist/knowledge/propTypes.js +5 -4
  9. package/dist/knowledge/rules.js +113 -6
  10. package/dist/knowledge/styleResolve.js +52 -5
  11. package/dist/knowledge/themeResolve.js +936 -0
  12. package/dist/knowledge/themeRules.js +221 -0
  13. package/dist/knowledge/themeSchema.js +335 -0
  14. package/dist/platform/domSummary.js +144 -0
  15. package/dist/platform/http.js +10 -2
  16. package/dist/platform/screenshot.js +492 -29
  17. package/dist/platform/screenshotMemory.js +55 -0
  18. package/dist/platform/strictArguments.js +32 -0
  19. package/dist/platform/writeDiff.js +6 -3
  20. package/dist/resources/examples/layout-app-shell.json +1 -1
  21. package/dist/resources/examples/page-sign-in.json +1 -1
  22. package/dist/resources/examples/theme-brand.json +162 -0
  23. package/dist/resources/guides/component.md +8 -2
  24. package/dist/resources/guides/expressions.md +6 -0
  25. package/dist/resources/guides/getting-started.md +6 -0
  26. package/dist/resources/guides/layout.md +36 -26
  27. package/dist/resources/guides/microservice.md +160 -6
  28. package/dist/resources/guides/page.md +165 -59
  29. package/dist/resources/guides/theme.md +210 -0
  30. package/dist/resources/guides/translation.md +13 -10
  31. package/dist/resources/guides/workflow.md +141 -7
  32. package/dist/resources/schema-fingerprint.json +2 -2
  33. package/dist/resources/schemas/CreateMicroserviceCommand.json +4 -1
  34. package/dist/resources/schemas/CreateWorkflowCommand.json +4 -1
  35. package/dist/resources/schemas/PreviewMicroserviceCommand.json +4 -1
  36. package/dist/resources/schemas/PreviewWorkflowCommand.json +4 -1
  37. package/dist/resources/schemas/StepDto.json +4 -1
  38. package/dist/resources/schemas/UpdateMicroserviceCommand.json +4 -1
  39. package/dist/resources/schemas/UpdateWorkflowCommand.json +4 -1
  40. package/dist/server.js +52 -42
  41. package/dist/tools/components.js +8 -0
  42. package/dist/tools/designSchemaPatch.js +152 -0
  43. package/dist/tools/flows.js +24 -21
  44. package/dist/tools/layouts.js +8 -8
  45. package/dist/tools/microservices.js +74 -10
  46. package/dist/tools/pages.js +275 -332
  47. package/dist/tools/projects.js +66 -9
  48. package/dist/tools/queries.js +72 -6
  49. package/dist/tools/schemaOps.js +280 -0
  50. package/dist/tools/screenWrite.js +2 -1
  51. package/dist/tools/screens.js +0 -7
  52. package/dist/tools/themes.js +163 -0
  53. package/dist/tools/workflows.js +10 -6
  54. package/package.json +1 -1
package/README.md CHANGED
@@ -4,10 +4,10 @@ Zeyrek (NgApps) platformunu **MCP tool'ları** olarak sunan bağımsız gateway.
4
4
  oturumu Studio arayüzüne hiç girmeden proje kurabilir, veri modeli çıkarabilir, mikroservis ve
5
5
  workflow yazıp çalıştırabilir, sayfa tasarlayabilir.
6
6
 
7
- - **191 tool · 32 modül** — veri/mantık tarafı (project, application, datasource, entity, row,
7
+ - **208 tool · 34 modül** — veri/mantık tarafı (project, application, datasource, entity, row,
8
8
  import, microservice, query, workflow, automation, function, dto, view, dependency), Studio
9
- tasarım tarafı (page, component, external component, flow, layout, variable, function, folder,
10
- file, access, translation), yönetim (template, organization, deployment) ve proje dallanması
9
+ tasarım tarafı (page, **theme**, component, external component, flow, layout, variable, function,
10
+ folder, file, access, translation), yönetim (template, organization, deployment) ve proje dallanması
11
11
  (environment, merge).
12
12
  - Platform koduna referans vermez: Organization.API ile Tenant.API'yi **HTTP üzerinden** sarar —
13
13
  istekler MVC binding'den geçtiği için FluentValidation aynen çalışır.
@@ -29,7 +29,7 @@ ekleyin:
29
29
  "zeyrek-ai": {
30
30
  "type": "stdio",
31
31
  "command": "npx",
32
- "args": ["-y", "@appaflytech/zeyrek-ai-mcp@0.0.5", "--stdio"],
32
+ "args": ["-y", "@appaflytech/zeyrek-ai-mcp@latest", "--stdio"],
33
33
  "env": {
34
34
  "ORG_API_BASE_URL": "https://new-zeyrek.glomil.com/api/org",
35
35
  "TENANT_API_BASE_URL": "https://new-zeyrek.glomil.com/api",
@@ -56,7 +56,7 @@ claude mcp add zeyrek-ai \
56
56
  -e DEFAULT_ENVIRONMENT=development \
57
57
  -e USERNAME=admin@zeyrek.com \
58
58
  -e PASSWORD='parolanız' \
59
- -- npx -y @appaflytech/zeyrek-ai-mcp@0.0.5 --stdio
59
+ -- npx -y @appaflytech/zeyrek-ai-mcp@latest --stdio
60
60
  ```
61
61
 
62
62
  **İlk doğrulama:** oturumda `platform_whoami` tool'unu çağırtın — kimlik, org çözümü ve ağ
@@ -64,9 +64,10 @@ erişimi tek seferde test edilir. Tool listesi boş geliyorsa Sorun giderme böl
64
64
 
65
65
  ### Sürüm seçimi
66
66
 
67
- `@0.0.5` gibi sabit sürüm önerilir: npx kopyayı önbelleğe alır, sabit sürümde herkes aynı
68
- gateway'i çalıştırır. `@latest` yazarsanız ekipteki makineler farklı sürümlerde kalabilir;
69
- güncellemek için `.mcp.json`'daki sürümü yükseltip MCP sunucusunu yeniden başlatın.
67
+ `@latest` önerilir: `main`'e giren her gateway değişikliği CI'da otomatik yayınlanır (bkz.
68
+ Yayınlama), yeni sürüm MCP sunucusu yeniden başlatılınca gelir. npx kopyayı önbelleğe aldığı
69
+ için eski sürümde kalan makinede `~/.npm/_npx` silinip sunucu yeniden başlatılır. Herkesin aynı
70
+ gateway'i çalıştırması gereken işlerde (koşu, kabul testi) `@0.0.8` gibi sabit sürüm yazın.
70
71
 
71
72
  ---
72
73
 
@@ -138,10 +139,10 @@ ya da ikisi karışık (`core,page`). `platform` modülü (3 tool) her kümede v
138
139
 
139
140
  | Küme | Tool | Modüller |
140
141
  |---|---|---|
141
- | `core` | 111 | platform, project, application, datasource, entity, row, microservice, query, workflow, automation, function, dto, view, dependency, environment, merge, activity, revision |
142
- | `studio` | 69 | platform, page, component, externalComponent, flow, layout, variable, function, folder, file, access, translation, revision |
143
- | `admin` | 26 | platform, template, organization, deployment, environment, merge, activity |
144
- | (boş) | **185** | 32 modülün tamamı |
142
+ | `core` | 136 | platform, project, application, datasource, entity, row, import, microservice, microfunction, query, workflow, automation, function, dto, view, dependency, environment, merge, activity, revision |
143
+ | `studio` | 78 | platform, page, theme, component, externalComponent, flow, layout, variable, function, folder, file, import, access, translation, revision |
144
+ | `admin` | 27 | platform, template, organization, deployment, environment, merge, activity |
145
+ | (boş) | **208** | 34 modülün tamamı |
145
146
 
146
147
  Bilinmeyen bir modül/küme adı verilirse sunucu açılmaz, geçerli adları listeleyen bir hata yazar.
147
148
 
@@ -165,7 +166,8 @@ Bilinmeyen bir modül/küme adı verilirse sunucu açılmaz, geçerli adları li
165
166
  `platform_guide('page')`.
166
167
  - **Yazma tool'ları replace gövdeleri merge eder:** `page_update`, `layout_update`,
167
168
  `component_update` kaydı önce okur, yalnız verdiğiniz alanları değiştirir; şema/olay kaybı
168
- olmaz. `flow_*` uçlarının istediği barındırıcı gövdesini de gateway kurar.
169
+ olmaz. `flow_*` yazmaları yalnız akışı gönderir; olay bağı `page_schema_patch` /
170
+ `layout_schema_patch` / `component_schema_patch` ile ayrı yazılır.
169
171
  - **Gerçek ekran:** UI değişikliği `page_screenshot` ile doğrulanır (`STUDIO_BASE_URL` gerekir;
170
172
  ilk kullanımda `npx playwright-core install chromium`).
171
173
 
@@ -213,7 +215,7 @@ etiketleri, diğer `LOG_*` ayarları ve maskeleme:
213
215
  | `POST /connect/token` 404 / "kayıt bulunamadı" | Kimlik makine kullanıcı adıyla gidiyor — `USERNAME` üstteki üçüncü davranış maddesi. |
214
216
  | `Host` çözülemedi / 404 | `ORGANIZATION` + `HOST_SUFFIX` birlikte `{org}.{suffix}` üretir; ikisi de doğru olmalı. |
215
217
  | `page_screenshot` hata veriyor | `STUDIO_BASE_URL` boş ya da tarayıcı yok: `npx playwright-core install chromium` veya `SCREENSHOT_BROWSER_PATH`. |
216
- | Eski sürüm çalışıyor | npx önbelleği: `.mcp.json`'da sabit sürüm yazın ve sunucuyu yeniden başlatın. |
218
+ | Eski sürüm çalışıyor | npx önbelleği: `~/.npm/_npx`'i silip sunucuyu yeniden başlatın ya da `.mcp.json`'da sabit sürüm yazın. |
217
219
 
218
220
  Her durumda ilk bakılacak yer günlük dosyasıdır (`LOG_DIR`).
219
221
 
@@ -225,7 +227,7 @@ Gateway'i ortak bir sunucu olarak çalıştırmak için (stateless streamable HT
225
227
  POST):
226
228
 
227
229
  ```bash
228
- npx -y @appaflytech/zeyrek-ai-mcp@0.0.5 # --stdio olmadan
230
+ npx -y @appaflytech/zeyrek-ai-mcp@latest # --stdio olmadan
229
231
  ```
230
232
 
231
233
  Sağlık ucu: `http://localhost:5100/healthz` (port `MCP_PORT`). İstemci kaydı:
@@ -276,19 +278,23 @@ kural envanteri [`docs/08-kural-envanteri.md`](docs/08-kural-envanteri.md).
276
278
 
277
279
  ### Yayınlama (paket bakımcısı)
278
280
 
281
+ Yayın otomatiktir: `main`'e `zeyrek-mcp/` değişikliği girince CI işi `publish:mcp:npm`
282
+ ([`scripts/ci-publish-npm.sh`](scripts/ci-publish-npm.sh)) paketi üretip npm'deki son sürümle
283
+ içerik olarak karşılaştırır; pakete giren dosyalar değiştiyse yeni patch sürümünü yayınlar.
284
+ Sürümün kaynağı npm'dir, CI `package.json`'ı geri commit'lemez. Minor/major için
285
+ `package.json`'daki sürümü elle yükseltip merge edin. Yayını o commit için atlamak: commit
286
+ mesajına `DoNotDeploy` (iş elle tetiklenir hâle gelir).
287
+
288
+ Yerelde deneme (yayın yapmaz; `npm ci` çalıştırdığı için bir kopyada ya da Linux container'da):
289
+
279
290
  ```bash
280
- cd zeyrek-mcp
281
- npm run typecheck && npm test
282
- npm version patch # package.json sürümü
283
- npm publish # prepack: tsc -p tsconfig.build.json + varlık kopyalama
291
+ DRY_RUN=1 sh scripts/ci-publish-npm.sh
284
292
  ```
285
293
 
286
294
  `dist/` içine `src/resources/{schemas,guides,examples}` altındaki JSON/MD varlıklarını
287
295
  `scripts/copy-assets.ts` kopyalar — `tsc` tek başına bunları taşımaz. Yayınlanan paket
288
296
  `dist/`, `README.md` ve iki `.env` şablonundan oluşur.
289
297
 
290
- Sürüm yükseldikten sonra ekipteki `.mcp.json` örneklerindeki sabit sürümü de güncelleyin.
291
-
292
298
  ---
293
299
 
294
300
  ## Bilinen sınırlar
package/dist/config.js CHANGED
@@ -16,6 +16,7 @@ export const moduleNames = [
16
16
  "dto",
17
17
  "view",
18
18
  "page",
19
+ "theme",
19
20
  "component",
20
21
  "externalComponent",
21
22
  "flow",
@@ -61,6 +62,7 @@ export const modulePresets = {
61
62
  studio: [
62
63
  "platform",
63
64
  "page",
65
+ "theme",
64
66
  "component",
65
67
  "externalComponent",
66
68
  "flow",
@@ -417,6 +417,9 @@ function microserviceFindings(body) {
417
417
  findings.push(`sourceParameters.fields[${index}]: writeOn:'${String(field.writeOn)}' yalnız operation:'upsert' ucunda anlamlı; bu uç '${operation ?? '?'}' — writeOn'u kaldırın ya da 'always' bırakın (Fields.All(WriteOn == Always) when Operation != Upsert).`);
418
418
  }
419
419
  });
420
+ if (body.source === 'table' && operation !== undefined && operation.startsWith('bulk-') && asObjects(parameters.filters).length > 0) {
421
+ findings.push(`sourceParameters.filters: '${operation}' ucu filtre KULLANMAZ — bulk create/update satırları gövdeden, bulk delete birincil anahtarları gövdeden okur. Filtreleri kaldırın.`);
422
+ }
420
423
  flattenFilters(parameters.filters).forEach((filter, index) => {
421
424
  if (!isSingleFilter(filter))
422
425
  return;
@@ -45,9 +45,12 @@ export const STYLE_GROUP_NOTES = {
45
45
  * borderWidth…) yerini alır; kayıtlı appearance değerleri çizilmeye devam eder
46
46
  * ama Studio'da artık düzenlenmez — yeni karar surfaceStyle'a yazılır.
47
47
  */
48
+ const DATE_VALUE_FORMAT_NOTE = "YEREL duvar saati 'yyyy-MM-ddTHH:mm:ss' metni (dilim eki yok; valueOffset verilmişse o kadar saat kaydırılmış) ya da null. Biçim uygulama dilinden ve locale'den BAĞIMSIZDIR — locale yalnız ekrandaki metni değiştirir. Uca type:'text' parametreyle aynen gider; SQL'de ::timestamp ile okunur, to_timestamp/AM-PM ayrıştırması GEREKMEZ (G-01: backend bu değeri eskiden en-US '9/15/2026 10:23:00 PM' metnine çeviriyordu, 2026-09-15'te düzeltildi).";
48
49
  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
50
  const NODE_SURFACE_NOTE = STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens +
50
51
  " 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.";
52
+ /** layout ve navigations'ın ORTAK menü satırı grubu (frontend style-group-schemas.ts navigationStyleGroup + navigation-style-logic.ts). */
53
+ const NAVIGATION_STYLE_NOTE = "Menü satırlarının TİPLİ grubu, menünün TAMAMINA uygulanır: padding (CSS kutu kısayolu; sol değer satırın derinlik girintisiyle TOPLANIR), radius (tema kademesi 'sm|md|lg|pill' ya da CSS), textColor · iconColor · hoverBackground (seçili OLMAYAN satırlar), activeBackground · activeTextColor · activeIconColor (seçili satır; zemin hover'da da korunur). Boş iconColor yazı rengini izler. Renkler hex ya da var(--ng-…) token'ı alır. ÖĞE BAZLI STİL YOKTUR: navigations[].style diye bir alan tanımlı değil, yazılan değer sessizce düşer — satır görünümünü yalnız bu grup verir. Grubun yazmadığı CSS için style'ın &__item / &__item--active / &__icon parçaları kullanılır (yalnız navigations bileşeninde; layout'un style'ı menüye ulaşmaz). resolvedStyle raporu bu grubu İÇERMEZ.";
51
54
  export const componentKnowledge = {
52
55
  html: {
53
56
  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.',
@@ -114,7 +117,7 @@ export const componentKnowledge = {
114
117
  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.",
115
118
  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.",
116
119
  selectedRow: "Salt okunur: satır aksiyonu tıklanınca komponentin kendisi doldurur; akış ifadesinde '<elementId>.selectedRow' ile okunur.",
117
- 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.",
120
+ 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. TAG HÜCRE ŞEKLİ: metin, { label, color? } ya da { value, color? } (label yoksa value okunur; ikisi de yoksa obje ham JSON basılır) veya bunların dizisi. RENK (veri objesinde de colorMap'te de): semantik ad primary · info · success · warning · error TEMA TOKEN'INA çözülür (tema değişince izler, ham hex yazmaya gerek yok); hex/rgb/'var(--ng-…)'/CSS renk adı olduğu gibi geçer; başka bir değer (yazım hatası, çözülmemiş '{{ }}') o kademeyi ATLAR ve sıradakine düşer. Veri objesindeki variant OKUNMAZ — outline/solid kolonun appearance'ından gelir. Solid rozetin yazı rengi token'da CSS ile siyah/beyaz seçilir.",
118
121
  tableStyle: "zebra | plain — satır zemini varyantı; CSS DEĞİL. Serbest CSS için ayrı `style` prop'u vardır (parça bazlı).",
119
122
  style: "Parça bazlı CSS: &__toolbar · &__title · &__head · &__th · &__body · &__row · &__td. designerCss her bildirime !important ekler, yani komponentin kendi çerçeve/ayraç varsayılanlarını ezer — Figma tipi düz tablo bununla kurulur."
120
123
  }
@@ -124,10 +127,12 @@ export const componentKnowledge = {
124
127
  behavior: [
125
128
  "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).",
126
129
  "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.",
127
- "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."
130
+ "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.",
131
+ "Formu temizlemek için akış kodunda resetForm('<form adı>') çağrılır (2026-09-15): alanlar başlangıç değerine döner, form gönderilmemiş hâline geçer, hata işaretleri ve form-validation-errors listesi silinir. Alanları setter ile tek tek null'a çekmek YANLIŞ yoldur: form bir kez gönderildiyse her change doğrulamayı yeniden koşturur ve boş form 'zorunlu alan' mesajlarıyla açılır. Gönderim sonrası otomatik temizlik isteniyorsa isClearFormInputsAfterSubmit aynı sıfırlamayı yapar."
128
132
  ],
129
133
  propNotes: {
130
- surfaceStyle: NODE_SURFACE_NOTE,
134
+ surfaceStyle: STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens + " Form grubu KENDİ derler (2026-09-15): değer <form> KÖKÜNE iner, contentOverflow gövdeye ('&__body'). Formu bir kartın içine koyup kendi zeminini/çerçevesini kaldırmanın yolu budur: { background: 'transparent', borderTop: 'none', borderRight: 'none', borderBottom: 'none', borderLeft: 'none' }.",
135
+ style: STYLE_GROUP_NOTES.styleEscape + " Parçalar: KÖK (anahtarlar doğrudan style'da) <form> elemanıdır ve TABAN zemini (--ng-background-surface), 1px çerçeveyi (--ng-border-primary) ve köşeyi (--ng-rounded) ÇİZER; '&__body' yalnız kaydırma/esneme taşır — çerçeveyi '&__body'ye yazarak kaldıramazsın. '&__header' başlık bandı, '&__title' başlık metni. DOM yapısına bağlı seçiciler ('& > div') belgelenmemiştir ve iç yapı değişince sessizce kırılır.",
131
136
  errorElementId: "Sayfadaki form-validation-errors komponentinin elementId'si — bağlanmazsa alan doğrulama mesajları görünmez."
132
137
  }
133
138
  },
@@ -244,7 +249,7 @@ export const componentKnowledge = {
244
249
  behavior: [
245
250
  "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).",
246
251
  "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.",
247
- "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.",
252
+ "TEMA PRESET'İ: bir tema preset'ini düğüme bağlamak için style.$preset: '<ad>' yaz (adlar theme_get → presets, sayfa bağlamında page_runtime_get → theme.presets; preset'i yazmak/silmek theme_update; 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.",
248
253
  "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.",
249
254
  "Yüzey neden prop: düğümün appearance kanalı sarmalayıcıyı (.ng-app__node-item) boyar, kutunun İÇİNİ değil — box'ta .ng-box'ın kendi background/border-radius'una hiçbir panelden ulaşılamıyor. surfaceStyle bu boşluğu kapatır.",
250
255
  "Bölge (header/footer) düğümleri box ile ORTAKTIR: hasHeader/hasFooter aynı box-section düğümlerini açar. sectionStyle kuralları bu yüzden '&__header' gibi bir parçaya değil, gerçek '& .ng-box-section--header' seçicisine yazılır.",
@@ -282,7 +287,7 @@ export const componentKnowledge = {
282
287
  layout: "'flex' (katalog varsayılanı; akış) | 'grid' (12 kolonluk ızgara). Prop hiç yazılmamışsa renderer IZGARA çizer — varsayılan yalnız düğüm kurulurken materyalize olur. Aşağıdaki altı prop YALNIZ 'flex' iken etkilidir.",
283
288
  direction: "'row' (varsayılan) | 'column' → flex-direction. Hangi hizalamanın ana eksen olduğunu bu belirler.",
284
289
  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.",
285
- verticalAlign: "top | middle | bottom | stretch. row'da ÇAPRAZ eksen, column'da ANA eksen — horizontalAlign ile rollerini değiştirirler.",
290
+ verticalAlign: "top | middle | bottom | stretch. row'da ÇAPRAZ eksen, column'da ANA eksen — horizontalAlign ile rollerini değiştirirler. row'da stretch seçiliyken çocuğun sabit yüksekliği (heightType fixed → heightString ya da size.h×10px) HEIGHT değil MIN-HEIGHT olarak yazılır (2026-09-15): çocuklar en uzun kardeşin boyuna eşitlenir, sabit ölçü taban kalır — eşit boylu KPI kartları için her karta elle aynı heightString yazmak gerekmez. İstisna: isScrollable çocuk kesin yüksekliğini korur. Izgara kapta (layout grid) bu prop da gap de okunmaz; eşit kart satırı flex row + stretch ile kurulur.",
286
291
  size: "'shrink' (çocuklar flex: 0 1 auto, varsayılan) | 'grow' (flex: 1 1 auto — çocuklar boşluğu paylaşır).",
287
292
  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.",
288
293
  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.",
@@ -300,7 +305,10 @@ export const componentKnowledge = {
300
305
  },
301
306
  modal: {
302
307
  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.',
303
- 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)."],
308
+ behavior: [
309
+ "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).",
310
+ "Kaplama çerçevesidir (isFrame): kanvasta blok tutmaz, runtime kutu stili yazmadan ayrı dalda çizer. Katalog boyutu bilerek BOŞ aralıktır (minSize 1, maxSize 0) ve 2026-09-15'ten beri doğrulayıcı blocks[].size'ı hiçbir eksende DENETLEMEZ (boyut sınırı kanvasın kuralıdır) — add_component ile kökte (parent null) herhangi bir { w, h } ile eklenir. Daha eski backend build'i her boyutu 'Height of modal must be between 1 and 0' (invalid-size) ile reddeder; o durumda imaj yenilenmemiştir. drawer ve toast aynı kuraldadır."
311
+ ],
304
312
  propNotes: { surfaceStyle: NODE_SURFACE_NOTE }
305
313
  },
306
314
  drawer: {
@@ -516,30 +524,105 @@ export const componentKnowledge = {
516
524
  isLoading: 'Grubu KİLİTLER (checkboxgroup deseni): seçenek listesi soluklaşır, tık yutulur, change yayılmaz ve grup aria-busy taşır; etiketin yanında dönen spinner çizilir. Seçenekleri getiren sorgu sürerken aç, gelince kapat.'
517
525
  }
518
526
  },
519
- datepicker: { summary: 'Tarih seçici; change yükü { value, HH, mm }.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
520
- datetimepicker: { summary: 'Tarih+saat seçici; değer event.data’da değil self.value’dadır.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
521
- daterangepicker: { summary: 'Tarih aralığı; change yükü { valueFrom, valueTo, HH, mm }.', propNotes: { locale: LOCALE_FOLLOWS_APP_NOTE } },
527
+ datepicker: {
528
+ summary: 'Tek GÜN seçici (saat yok): takvimden ya da kutuya yazarak; change yükü tek anahtar { value }.',
529
+ behavior: [
530
+ "Değer YEREL duvar saati 'yyyy-MM-ddTHH:mm:ss' metnidir (dilim eki yok, saat kısmı seçimde 00:00) ya da null. change düğümün prop'una yazar: akış değeri self.value ile okur, event.data BOŞTUR (CQ-149). 2026-09-15 öncesinde bu kayıt yükte HH ve mm anahtarları da olduğunu söylüyordu — runtime hiç göndermiyordu, self.HH okuyan ifade boş dönerdi.",
531
+ "minDate/maxDate GÜN sınırıdır ('yyyy-MM-dd'): ikisi de KENDİ günü dahil seçilebilir kalır. 2026-09-15'te düzeltildi — çıplak gün metni UTC gece yarısı olarak okunuyordu, Greenwich'in doğusunda (TR: +03) minDate'in kendi günü takvimde PASİF görünüyordu, yazarak seçilirse değer 03:00'a kaydırılıyordu; batısında (örn. New York −04) aynı kusur maxDate'te çıkıyordu — maxDate'in KENDİ günü pasifti. Sınır dışı günler takvimde pasiftir; kutuya YAZILAN sınır dışı bir tarih hata vermez, o sınır gününün gece yarısına çekilir. Akıştan yazılan değer bu kapıdan GEÇMEZ, olduğu gibi gösterilir.",
532
+ 'Takvim tek gün seçtirir; komşu ayların günleri de seçilebilir, yıl açılır listesi 1900-2100 arasında gezer, popup her ay altı hafta çizer. Seçim anında popup kapanır ve change yayılır — onay adımı yoktur.',
533
+ "Kutuya elle yazmak için çift tıklanır (tek tık takvimi açar). Okunan desenler tr'de 'd MMM yyyy' · 'd MMMM yyyy' · 'd.MM.yyyy' · 'dd.MM.yyyy' · 'dd.MM.yy', diğer dillerde 'MMM d, yyyy' · 'MMMM d, yyyy' · 'MM/dd/yyyy' · 'M/d/yyyy' · 'MM/dd/yy'; 1900 öncesi ve 2100 sonrası yıl reddedilir. Okunamayan yazı tutulan değere döner, değer yoksa kutu BOŞALIR (2026-09-15; öncesinde reddedilen metin değermiş gibi ekranda kalıyordu). Kutu salt okunur olsa da programdan gelen change'i DİNLER: page_screenshot 'set' ile ISO tarih ('2026-08-01', '2026-08-01T09:30') ya da gösterim biçimi yazılabilir ve değer takvimden seçilmiş gibi yayılır; okunamayan metinde actionsReport propWritten:false döner. 2026-09-15 öncesi olay dinlenmiyordu: kutu dolu görünüyor, prop boş kalıyordu (G-14).",
534
+ 'Doğrulama kuralları: required · min · max (2026-09-15; pattern bildirimi kaldırıldı — panel tarih alanına regex kuralı hiç çizmiyordu, yazılan satırı düğüm seçilince siliyordu, CQ-226). min/max değeri gün metnidir ve alanın minDate/maxDate prop\'larından AYRIDIR: minDate/maxDate takvimi kısar, min/max gönderimde hata gösterir. DİKKAT (CQ-252): Form kuralın gün metnini UTC, alanın değerini YEREL okuyor (form/helper.ts dateMs), yani UTC olmayan her dilimde SINIR GÜNÜNÜN KENDİSİ yanlış tarafta kalıyor — Türkiye saatinde tam min gününe eşit bir değer yine de en-az hatası veriyor. Bugünkü güvenli yol: sınırı bir gün geniş tutmak ya da kural yerine minDate/maxDate kullanmak.',
535
+ 'focus/blur ALAN düzeyindedir (2026-09-15): girdi ile takvim arasında dolaşmak olay yaymaz; yalnız alana girmek ve alandan çıkmak yayar. Kilitli alan (isDisabled/isReadonly/isViewmode) hiçbir olay yaymaz.',
536
+ 'isLoading KİLİT DEĞİLDİR (2026-09-15): alan kullanılabilir kalır, sağ uçta dönen spinner çizilir (o sırada X gizlenir) ve kök aria-busy taşır. Kilit için isDisabled kullan.',
537
+ "Değer doluyken sağ uçta X çıkar ve change'i null ile yayar; kilitli kipte X yoktur. Gösterim dili locale'den (boşsa uygulama dili): tr'de 'd MMM yyyy', diğer dillerde 'MMM d, yyyy'. Biçim ve haftanın ilk günü prop'u YOK (ARC-071).",
538
+ 'Saat gerekiyorsa datetimepicker, aralık gerekiyorsa daterangepicker kullanılır; bu bileşen saat taşımaz.'
539
+ ],
540
+ propNotes: {
541
+ value: "Seçili gün: 'yyyy-MM-ddTHH:mm:ss' ya da 'yyyy-MM-dd' metni, ya da null. Çıplak gün YEREL okunur (UTC kaydırması yok). Okunamayan metin alanı BOŞ gösterir, hata vermez. " + DATE_VALUE_FORMAT_NOTE,
542
+ minDate: "En erken gün ('yyyy-MM-dd'); o günün kendisi seçilebilir, öncesi takvimde pasif kalır.",
543
+ maxDate: 'En geç gün; o günün kendisi seçilebilir, sonrası pasif kalır.',
544
+ placeholder: "Boş alanda görünen metin; varsayılanı '01.01.2000' ama kutu değeri her zaman dilin desenine göre yazar (tr '10 Eyl 2026', diğer diller 'Sep 10, 2026') — yer tutucu bir biçim SÖZÜ DEĞİLDİR.",
545
+ displayOffset: 'GÖSTERİMİ kaydıran saat sayısı (+3: saklanan değeri üç saat ileri göster); saklanan değer değişmez. Gün sınırına yakın bir değeri komşu güne kaydırabilir.',
546
+ valueOffset: 'YAZILAN değeri kaydıran saat sayısı; gösterim değişmez. Bir dilim farkını sabit tutmak için displayOffset ile ters işaretli kullanılır (+3 / -3).',
547
+ locale: LOCALE_FOLLOWS_APP_NOTE,
548
+ isLoading: 'Akıştan açılır; alan kilitlenmez, yalnız spinner çizilir ve kök aria-busy taşır.'
549
+ }
550
+ },
551
+ datetimepicker: {
552
+ summary: 'Tek alanda tarih + saat seçici: takvim + dakika adımlı saat listesi; değer tek value, change yükü { value }.',
553
+ behavior: [
554
+ "Değer YEREL duvar saati 'yyyy-MM-ddTHH:mm:ss' metnidir (dilim eki yok) ya da null. change düğümün prop'una yazar: akış değeri self.value ile okur, event.data BOŞTUR (CQ-149). 2026-09-16 öncesinde olay kaydında yük anahtarı yoktu; runtime her zaman { value } gönderiyordu.",
555
+ "Değer ancak TARİH + SAAT ikisi de seçilince yazılır: boş alanda takvimden yalnız gün seçmek change YAYMAZ — gün ekranda taslak kalır (kutu günü ve 00:00'ı gösterir ama self.value null'dır, required düşer — CQ-237), saat listesi kendiliğinden açılır ve saat seçilince yayılır. Günü yeniden seçmek de taslağı YAZMAZ (2026-09-16'da düzeltildi: ikinci gün seçimi …T00:00:00 yayıyordu). Alan zaten bir değer tutuyorsa (kullanıcı seçmiş ya da akış yazmış) tarihi değiştirmek TUTULAN saati koruyup hemen yayar. Gün seçmeden saat seçmek bugünün gününü kullanır ve hemen yayar. Akıştan gelen değer taslağı düşürür.",
556
+ "Saat listesi minutesStep adımıyla gösterilen günün (yoksa bugünün) üstünde üretilir; minDate öncesi ve maxDate sonrası saatler listede KALIR ama pasiftir. Liste minutesStep ve dil değişince yeniden üretilir (2026-09-16 öncesinde yalnız değer ya da sınır değişince yenileniyordu).",
557
+ "minDate/maxDate tarih-saat ANIDIR ('yyyy-MM-ddTHH:mm'): takvimde sınırın KENDİ günü seçilebilir kalır, o günün sınır dışı saatleri listede pasiftir. 2026-09-16'da düzeltildi — takvim sınırı an olarak karşılaştırıyordu, saati olan bir minDate (örn. …T14:00) kendi gününü takvimde PASİF gösteriyordu, o gün hiç seçilemiyordu. Yayma öncesi sarmalayıcı seçimi [minDate, maxDate] içine çeker (sınır dışı seçim hata VERMEZ, sınıra yapışır); akıştan yazılan değer bu kapıdan GEÇMEZ, olduğu gibi gösterilir.",
558
+ "Kutulara elle yazmak için çift tıklanır (tek tık takvimi / saat listesini açar). Tarih kutusu datepicker desenlerini okur (tr 'd MMM yyyy' · 'd.MM.yyyy' …, diğer dillerde 'MMM d, yyyy' · 'MM/dd/yyyy' …, her dilde ISO 'yyyy-MM-dd') ve takvimden gün seçmekle AYNI kurala girer. Saat kutusu '14:30', '14', en'de '02:30 PM' okur; okunamayan saat kutuyu tutulan saate geri çevirir, değeri bozmaz. 2026-09-16 öncesinde saat kutusu çift tıklamadan da yazılabiliyordu ve yazılan metin yok sayılıp ekranda kalıyordu.",
559
+ "page_screenshot 'set' kökte TARİH kutusuna yazar ve takvimden gün seçmekle aynı kuraldır: alan değer tutuyorsa tutulan saat korunup yazılır; boşsa yalnız taslak olur ve actionsReport propWritten:false döner. ISO metnindeki saat kısmı ('2026-08-01T09:30') KULLANILMAZ ve saat kutusunun iç kimliği yoktur, 'set' ile hedeflenemez (TOOL-020). Tam değeri doğrulamak için önce akıştan self.value yazın.",
560
+ "Doğrulama kuralları: required · min · max (2026-09-16; pattern bildirimi kaldırıldı — panel tarih-saat alanına regex kuralı hiç çizmiyordu, yazılan satırı düğüm seçilince siliyordu, CQ-226). min/max değeri 'yyyy-MM-ddTHH:mm' metnidir ve alanın minDate/maxDate prop'larından AYRIDIR: minDate/maxDate takvimi ve listeyi kısar, min/max gönderimde hata gösterir. Kurala çıplak gün ('2026-09-10') yazma — Form onu UTC okur ve UTC olmayan dilimde sınır birkaç saat kayar (CQ-252); saatiyle yaz.",
561
+ 'focus/blur ALAN düzeyindedir (2026-09-16): tarih kutusu, saat kutusu, takvim ve saat listesi arasında dolaşmak olay yaymaz; yalnız alana girmek ve alandan çıkmak yayar. Kilitli alan (isDisabled/isReadonly/isViewmode) hiçbir olay yaymaz.',
562
+ 'isLoading KİLİT DEĞİLDİR (2026-09-16): alan kullanılabilir kalır, sağ uçta dönen spinner çizilir (o sırada X gizlenir) ve kök aria-busy taşır. Kilit için isDisabled kullan.',
563
+ "Değer (ya da taslak gün) doluyken sağ uçta X çıkar; değeri boşaltıp change'i null ile yayar, yalnız taslak gün varsa taslağı düşürür ve change YAYMAZ (değer zaten boştu); kilitli kipte X yoktur. Gösterim dili locale'den (boşsa uygulama dili): tarih tr'de 'd MMM yyyy', diğer dillerde 'MMM d, yyyy'; saat en'de 12 saat (01:00 PM), diğer dillerde 24 saat. Değer her dilde aynı 24 saatlik metindir. Biçim ve haftanın ilk günü prop'u YOK (ARC-071, CQ-098). Yalnız gün için datepicker, aralık için datetimerangepicker kullanılır."
564
+ ],
565
+ propNotes: {
566
+ value: "Seçili an: 'yyyy-MM-ddTHH:mm:ss' ya da null. Çıplak 'yyyy-MM-dd' YEREL gece yarısı okunur (UTC kaydırması yok). Okunamayan metin alanı BOŞ gösterir, hata vermez. " + DATE_VALUE_FORMAT_NOTE,
567
+ minutesStep: "Saat listesinin adımı, METİN enum: '1' | '2' | '3' | '4' | '5' | '6' | '10' | '12' | '15' | '30' | '60' (varsayılan '15'). 0, negatif ya da okunamayan değer 15'e düşer; 1 dakika 1440 satır demektir.",
568
+ minDate: "En erken an ('yyyy-MM-ddTHH:mm'); o günün kendisi takvimde seçilebilir, günün sınırdan önceki saatleri listede pasif kalır. Sınır dışına düşen seçim sınıra çekilir.",
569
+ maxDate: "En geç an; o gün seçilebilir, sınırdan sonraki saatler pasif, sınır dışı seçim sınıra çekilir. Saatiyle yaz ('…T23:59'): çıplak gün ('2026-09-20') o günün 00:00'ıdır, günün geri kalanı pasif kalır.",
570
+ placeholder: "Boş tarih kutusunda görünen metin (varsayılan 'Select date'); kutu değeri her zaman dilin desenine göre yazar — yer tutucu bir biçim SÖZÜ DEĞİLDİR.",
571
+ timePlaceholder: "Boş saat kutusunda görünen metin (varsayılan 'Select time'). Taslak gün seçilince kutu 00:00 gösterir, yer tutucu görünmez (CQ-237).",
572
+ displayOffset: 'GÖSTERİMİ kaydıran saat sayısı (+3: saklanan değeri üç saat ileri göster); saklanan değer değişmez.',
573
+ valueOffset: 'YAZILAN değeri kaydıran saat sayısı; gösterim değişmez. displayOffset ile HER ZAMAN ters işaretli ver (+3 / -3): kullanıcı gösterilen saati seçer ve korur, yalnız biri verilirse her tarih değişikliğinde tutulan saat o kadar kayar.',
574
+ locale: LOCALE_FOLLOWS_APP_NOTE,
575
+ isLoading: 'Akıştan açılır; alan kilitlenmez, yalnız spinner çizilir ve kök aria-busy taşır.'
576
+ }
577
+ },
578
+ daterangepicker: {
579
+ summary: 'GÜN aralığı (saat yok): değer valueFrom/valueTo İKİLİSİDİR (tek value yok); her yarı bir takvim + yazılabilir kutu; change yükü { valueFrom, valueTo }.',
580
+ behavior: [
581
+ "İki uç da YEREL duvar saati 'yyyy-MM-ddTHH:mm:ss' metnidir (dilim eki yok; seçimde saat 00:00, gece yarısı yaşanmayan yaz saati günlerinde 01:00) ya da null. change her iki anahtarı birden taşır ve düğümün prop'una yazar: akış self.valueFrom / self.valueTo ile okur, event.data BOŞTUR (CQ-149). Form aralığı { valueFrom, valueTo } nesnesi olarak görür; required ancak İKİSİ de doluyken geçer. 2026-09-16 öncesinde bu kayıt yükte HH ve mm anahtarları da olduğunu söylüyordu — runtime hiç göndermiyordu.",
582
+ "minDate/maxDate GÜN sınırıdır ('yyyy-MM-dd'): ikisi de KENDİ günü dahil seçilebilir kalır. Takvimler de gün olarak karşılıklı sınırlanır: bitiş takvimi başlangıç GÜNÜNDEN, başlangıç takvimi bitiş GÜNÜNE kadar açıktır — aynı gün başlayıp biten tek günlük aralık seçilebilir. 2026-09-16'da düzeltildi: çıplak gün metni UTC gece yarısı okunuyordu, Greenwich'in doğusunda (TR: +03) minDate'in kendi günü takvimde PASİF görünüyor ve sınıra çekilen değer 03:00 yazılıyordu, batısında maxDate'in kendi günü pasifti; saatli bir başlangıç (displayOffset, akıştan saatli değer) bitiş takviminde kendi gününü kapatıyordu.",
583
+ "Yayma öncesi sarmalayıcı çifti üç adımda kurar: (1) her uç [minDate, maxDate] günlerinin içine çekilir (sınır dışı yazılan tarih o sınır gününün gece yarısına yapışır, hata VERMEZ); (2) DEĞİŞEN uç seçilen günden valueOffset kaydırılarak yazılır, DOKUNULMAYAN uç tuttuğu değerle yazılır (kırpma onu taşımadıysa) — aynı günü yeniden seçmek de değişikliktir; (3) sıra YAZILAN değerler üzerinde uygulanır: bitiş başlangıçtan önceyse başlangıca eşitlenir, yani kullanıcı değişikliğinden sonra valueFrom <= valueTo her zaman tutar. Bu yüzden başlangıç saat taşıyorsa (akış '…T10:00:00' yazmışsa) aynı günde biten aralığın bitişi de o saati alır. 2026-09-16 öncesinde iki uç da GÖSTERİLEN hâlinden yeniden türetiliyordu: yalnız displayOffset ya da yalnız valueOffset verilmiş alanda dokunulmayan uç her seçimde o kadar saat kayıyordu. Akıştan yazılan değerler bu kapıdan GEÇMEZ, olduğu gibi gösterilir (ta ki kullanıcı bir yarıyı değiştirene dek); çıplak 'yyyy-MM-dd' YEREL gece yarısı okunur, okunamayan metin yarıyı BOŞ gösterir.",
584
+ "Başlangıç seçilince ve bitiş boşsa bitiş takvimi kendiliğinden açılır. Her seçim popup'ı kapatır ve change'i hemen yayar — onay adımı yoktur. Her yarının kendi X'i vardır ve yalnız o yarıyı boşaltır (change o yarı null, diğeri olduğu gibi); kilitli kipte X yoktur.",
585
+ "Kutuya elle yazmak için çift tıklanır (tek tık takvimi açar); okunan desenler datepicker ile aynıdır (tr 'd MMM yyyy' · 'd.MM.yyyy' · 'dd.MM.yyyy' …, diğer dillerde 'MMM d, yyyy' · 'MM/dd/yyyy' …, her dilde ISO 'yyyy-MM-dd'). İki kutu ayrı adreslenir: '<elementId>__start' (valueFrom) ve '<elementId>__end' (valueTo). page_screenshot 'set' kökü REDDEDER (tek değer yok); kutuya ISO tarih ('2026-08-01') ya da gösterim biçimi ('1 Ağu 2026') yazılır, kutu değeri kendi change yoluna sokar ve prop'a yazar. Okunamayan metin kutuyu tutulan değere geri çevirir; actionsReport propWritten:false döner (G-14, 2026-09-15).",
586
+ "Doğrulama kuralları: required · min · max (2026-09-16; pattern bildirimi kaldırıldı — panel tarih alanına regex kuralı hiç çizmiyordu, yazılan satırı düğüm seçilince siliyordu, CQ-226). min/max değeri gün metnidir, Form aralığın İKİ ucunu da sınırla karşılaştırır ve alanın minDate/maxDate prop'larından AYRIDIR: minDate/maxDate takvimi kısar, min/max gönderimde hata gösterir. DİKKAT (CQ-252): Form kuralın gün metnini UTC, alanın değerini YEREL okuyor, yani UTC olmayan her dilimde SINIR GÜNÜNÜN KENDİSİ yanlış tarafta kalıyor. Bugünkü güvenli yol: kural yerine minDate/maxDate kullanmak ya da sınırı bir gün geniş tutmak.",
587
+ "focus/blur ALAN düzeyindedir (2026-09-16): iki kutu ve iki takvim arasında dolaşmak olay yaymaz; yalnız alana girmek ve alandan çıkmak yayar. Kilitli alan (isDisabled/isReadonly/isViewmode) hiçbir olay yaymaz.",
588
+ "isLoading KİLİT DEĞİLDİR (2026-09-16): alan kullanılabilir kalır, iki yarının X'i yerinde durur, satırın sağ ucunda dönen spinner çizilir ve kutu aria-busy taşır (450px altındaki dikey yığılmada spinner çizilmez, aria-busy kalır). Kilit için isDisabled kullan.",
589
+ "Gösterim dili locale'den (boşsa uygulama dili): tr'de 'd MMM yyyy', diğer dillerde 'MMM d, yyyy'. Değer her dilde aynı metindir. Biçim ve haftanın ilk günü prop'u YOK (ARC-071). Saat gerekiyorsa datetimerangepicker, tek gün gerekiyorsa datepicker kullanılır."
590
+ ],
591
+ propNotes: {
592
+ valueFrom: "Başlangıç günü: 'yyyy-MM-ddTHH:mm:ss' ya da 'yyyy-MM-dd' metni, ya da null. Çıplak gün YEREL okunur (UTC kaydırması yok). " + DATE_VALUE_FORMAT_NOTE,
593
+ valueTo: "Bitiş günü; valueFrom ile aynı biçim. Akıştan başlangıçtan önce bir bitiş yazılırsa olduğu gibi gösterilir — sıra kuralı yalnız kullanıcı seçiminde uygulanır.",
594
+ minDate: "En erken gün ('yyyy-MM-dd'); o günün kendisi iki takvimde de seçilebilir, öncesi pasif kalır.",
595
+ maxDate: 'En geç gün; o günün kendisi seçilebilir, sonrası pasif kalır.',
596
+ startPlaceholder: "Başlangıç kutusu boşken görünen metin; varsayılanı '01.01.2000' ama kutu değeri her zaman dilin desenine göre yazar (tr '10 Eyl 2026', diğer diller 'Sep 10, 2026') — yer tutucu bir biçim SÖZÜ DEĞİLDİR.",
597
+ endPlaceholder: "Bitiş kutusunun yer tutucusu; startPlaceholder ile aynı kural (varsayılan '05.01.2000').",
598
+ displayOffset: 'GÖSTERİMİ kaydıran saat sayısı (+3: saklanan değeri üç saat ileri göster); saklanan değer değişmez. Gün sınırına yakın bir değeri komşu güne kaydırabilir.',
599
+ valueOffset: 'DEĞİŞEN ucu yazarken kaydıran saat sayısı; dokunulmayan uç tuttuğu değerle kalır. Gösterim yazılan değerden okunduğu için tek başına verilirse ekranda da görünür (-3: 15 seçilir, \'…14T21:00:00\' yazılır, kutu 14 gösterir) ve min/max kurallarının karşılaştırdığı değeri de kaydırır. Bir dilim farkını sabit tutmak için displayOffset ile ters işaretli kullanılır (+3 / -3).',
600
+ locale: LOCALE_FOLLOWS_APP_NOTE,
601
+ isLoading: 'Akıştan açılır; alan kilitlenmez, yalnız spinner çizilir ve kutu aria-busy taşır.'
602
+ }
603
+ },
522
604
  datetimerangepicker: {
523
605
  summary: 'Tarih+saat aralığı: değer valueFrom/valueTo İKİLİSİDİR (tek value yok); her yarı bir takvim + dakika adımlı saat listesi; change yükü { valueFrom, valueTo }.',
524
606
  behavior: [
525
607
  "İki uç da YEREL duvar saati 'yyyy-MM-ddTHH:mm:ss' metnidir (dilim eki yok) ya da null. change her iki anahtarı birden taşır ve düğümün prop'una yazar: akış self.valueFrom / self.valueTo ile okur. Form aralığı { valueFrom, valueTo } nesnesi olarak görür; required ancak İKİSİ de doluyken geçer.",
526
608
  "Bir yarının değeri ancak TARİH + SAAT ikisi de seçilince yazılır: takvimden yalnız gün seçmek change YAYMAZ — gün ekranda taslak olarak kalır (kutu günü ve 00:00'ı gösterir, CQ-237), günü yeniden seçmek de, diğer yarıyı tamamlamak da o taslağı YAZMAZ; saat listesi kendiliğinden açılır ve saat seçilince yayılır (tek datetimepicker ile aynı kural). Yarı zaten bir değer tutuyorsa (kullanıcı seçmiş ya da akış yazmış) tarihi değiştirmek TUTULAN saati koruyup hemen yayar — 2026-09-12'de düzeltildi, öncesinde listeden saat seçildikten sonra tarih değişikliği ekranda görünüp değere hiç yazılmıyordu. Akıştan gelen değer taslağı düşürür.",
527
- "Saat listeleri minutesStep adımıyla o yarının GÜNÜ üstünde üretilir; minDate/maxDate DIŞI ve karşı ucun ÖTESİNDEKİ saatler (başlangıç için bitişten sonrası, bitiş için başlangıçtan öncesi) listede KALIR ama pasiftir. Takvimler de karşılıklı sınırlanır: bitiş takvimi başlangıç gününden önceye, başlangıç takvimi bitiş gününden sonraya izin vermez.",
528
- "Yayma öncesi sarmalayıcı sınırlar: her uç [minDate, maxDate] içine çekilir, bitiş başlangıçtan önce kalırsa bitiş başlangıca eşitlenir. Sınır dışı bir seçim hata VERMEZ, sınıra yapışır. Akıştan yazılan değerler bu kapıdan GEÇMEZ, olduğu gibi gösterilir.",
609
+ "Saat listeleri minutesStep adımıyla o yarının GÜNÜ üstünde üretilir; minDate/maxDate DIŞI ve karşı ucun ÖTESİNDEKİ saatler (başlangıç için bitişten sonrası, bitiş için başlangıçtan öncesi) listede KALIR ama pasiftir. Takvimler GÜN olarak sınırlanır: bitiş takvimi başlangıç GÜNÜNDEN önceye, başlangıç takvimi bitiş GÜNÜNDEN sonraya, ikisi de minDate/maxDate GÜNLERİNİN dışına izin vermez — sınırın KENDİ günü her zaman seçilebilir, saat düzeyindeki sınır yalnız listede ve kırpmada uygulanır (2026-09-16; öncesinde saat taşıyan sınırın kendi günü takvimde pasifti: başlangıç 10:30 iken aynı gün biten aralık, minDate 09:00 iken o gün seçilemiyordu).",
610
+ "Yayma öncesi sarmalayıcı sınırlar: her uç [minDate, maxDate] ANINA çekilir; bir yarı değişince DOKUNULMAYAN uç kırpma onu taşımadıkça tuttuğu değerle yazılır; sıra YAZILAN çift üzerinde uygulanır, bitiş başlangıçtan önce kalırsa başlangıca eşitlenir. Sınır dışı bir seçim hata VERMEZ, sınıra yapışır. Akıştan yazılan değerler yazıldıkları anda bu kapıdan GEÇMEZ, olduğu gibi gösterilir; ama HANGİ yarıda olursa olsun bir sonraki kullanıcı seçimi onları da kırpar/sıraya sokar (sınır dışı uç sınıra, başlangıçtan önceki bitiş başlangıca) ve dokunulmayan ucu yayılan biçimde ('yyyy-MM-ddTHH:mm:ss', çıplak gün '…T00:00:00') yeniden yazar. Etkinleşen sınır günlerinin yan etkisi: tutulan saat o günde sınırın dışına düşerse (başlangıç 10:30, bitiş ertesi gün 09:00 → bitişte başlangıç günü seçilir) uç sessizce sınıra çekilir; sınırı günün son adımından geç olan gün (minDate 23:50, adım 15) seçilebilir ama listede seçilebilir saat yoktur, saat elle yazılır. 2026-09-16'ya kadar iki uç da gösterilen hâlinden yeniden türetiliyordu: yalnız displayOffset ya da yalnız valueOffset verilmiş alanda dokunulmayan uç her seçimde o kadar saat kayıyordu.",
529
611
  "Doğrulama kuralları: required · min · max (2026-09-12; pattern bildirimi kaldırıldı — panel tarih-saat alanına regex kuralı hiç çizmiyordu, yazılan satırı düğüm seçilince siliyordu). min/max değeri 'yyyy-MM-ddTHH:mm' tarih-saat metnidir ve Form aralığın İKİ ucunu da sınırla karşılaştırır.",
530
612
  "focus/blur ALAN düzeyindedir (2026-09-12): iki tarih girdisi, iki saat girdisi, takvim ve saat listesi arasında dolaşmak olay yaymaz; yalnız alana girmek ve alandan çıkmak yayar. Kilitli alan (isDisabled/isReadonly/isViewmode) hiçbir olay yaymaz.",
531
613
  "isLoading KİLİT DEĞİLDİR (2026-09-12): alan odaklanabilir kalır, sağ uçta dönen spinner çizilir, kök aria-busy taşır (478px altındaki dikey yığılmada spinner çizilmez, aria-busy kalır). Kilit için isDisabled kullan.",
532
- "Her yarının kendi X'i vardır ve yalnız o yarıyı boşaltır (change o yarı null, diğeri olduğu gibi). İkisini birden boşaltmanın yolu iki X ya da akıştan iki prop'u null yazmaktır.",
533
- "Gösterim dili locale'den (boşsa uygulama dili): tarih tr'de 'd MMM yyyy', diğer dillerde 'MMM d, yyyy'; saat en'de 12 saat (01:00 PM), diğer dillerde 24 saat. Değer her dilde aynı 24 saatlik metindir. Biçim prop'u YOK (ARC-071)."
614
+ "Her yarının kendi X'i vardır ve yalnız o yarıyı boşaltır (change o yarı null, diğeri tuttuğu değerle yazılır). Yalnız taslak gün tutan yarının X'i taslağı siler ama change YAYMAZ — değer zaten boştu (2026-09-16; öncesinde değişmeyen değerle change yayıyordu). İkisini birden boşaltmanın yolu iki X ya da akıştan iki prop'u null yazmaktır.",
615
+ "Gösterim dili locale'den (boşsa uygulama dili): tarih tr'de 'd MMM yyyy', diğer dillerde 'MMM d, yyyy'; saat en'de 12 saat (01:00 PM), diğer dillerde 24 saat. Değer her dilde aynı 24 saatlik metindir. Biçim prop'u YOK (ARC-071).",
616
+ "Tarih kutuları '<elementId>__start' / '<elementId>__end' ile adreslenir (G-14, 2026-09-15). page_screenshot 'set' kökü reddeder; kutuya ISO tarih yazmak takvimden gün seçmekle AYNI kuraldır: yarı değer tutmuyorsa yalnız taslak olur ve propWritten:false döner — saat kutusu ayrıca hedeflenemez, önce akıştan değer yazın ya da saati tıklamayla seçin."
534
617
  ],
535
618
  propNotes: {
536
- valueFrom: "Başlangıç: 'yyyy-MM-ddTHH:mm:ss' ya da null. Akıştan yazarken bu biçimi (ya da Date'in okuduğu bir ISO metnini) kullan; okunamayan metin yarıyı BOŞ gösterir, hata vermez.",
537
- valueTo: "Bitiş; valueFrom ile aynı biçim. Akıştan başlangıçtan önce bir bitiş yazılırsa olduğu gibi gösterilir — sıra kuralı yalnız kullanıcı seçiminde uygulanır.",
619
+ valueFrom: "Başlangıç: 'yyyy-MM-ddTHH:mm:ss' ya da null. Akıştan yazarken bu biçimi (ya da Date'in okuduğu bir ISO metnini) kullan; çıplak 'yyyy-MM-dd' o günün YEREL gece yarısıdır (2026-09-16'ya kadar UTC okunuyordu, TR'de 03:00 görünüyordu); okunamayan metin yarıyı BOŞ gösterir, hata vermez.",
620
+ valueTo: "Bitiş; valueFrom ile aynı biçim. Akıştan başlangıçtan önce bir bitiş yazılırsa olduğu gibi gösterilir — sıra kuralı kullanıcı seçiminde uygulanır: hangi yarıda olursa olsun bir sonraki seçim bitişi başlangıca çeker.",
538
621
  minutesStep: "Saat listelerinin adımı, METİN enum: '1' | '2' | '3' | '4' | '5' | '6' | '10' | '12' | '15' | '30' | '60' (varsayılan '15'). 0, negatif ya da okunamayan değer 15'e düşer; 1 dakika 1440 satır demektir.",
539
- minDate: "En erken tarih-saat ('yyyy-MM-ddTHH:mm:ss'); öncesi takvimde ve saat listesinde pasif kalır, yine de sınır dışına düşen seçim sınıra çekilir.",
540
- maxDate: 'En geç tarih-saat; sonrası pasif, sınır dışı seçim sınıra çekilir.',
541
- displayOffset: 'GÖSTERİMİ kaydıran saat sayısı (+3: saklanan değeri üç saat ileri göster); saklanan değer değişmez.',
542
- valueOffset: 'YAZILAN değeri kaydıran saat sayısı; gösterim değişmez. Bir dilim farkını sabit tutmak için displayOffset ile ters işaretli kullanılır (+3 / -3): kullanıcının gördüğü saat seçtiği saattir, saklanan değer sabit kalır.',
622
+ minDate: "En erken tarih-saat ('yyyy-MM-ddTHH:mm:ss'); takvimde o GÜNÜN öncesi, saat listesinde o ANIN öncesi pasif kalır (kendi günü seçilebilir); sınır dışına düşen seçim sınır anına çekilir.",
623
+ maxDate: 'En geç tarih-saat; takvimde o GÜNÜN sonrası, saat listesinde o ANIN sonrası pasif (kendi günü seçilebilir); sınır dışı seçim sınır anına çekilir.',
624
+ displayOffset: 'GÖSTERİMİ kaydıran saat sayısı (+3: saklanan değeri üç saat ileri göster); yalnız gösterilir, yazmada geri alınmaz — seçilen/korunan saat GÖSTERİLEN saattir. valueOffset ile ters işaretli verilmezse bir yarının tarihini değiştirmek o yarının saklanan saatini her seferinde +3 kaydırır (…06T18:00 → …07T21:00 → …08T00:00).',
625
+ valueOffset: "DEĞİŞEN ucu yazarken kaydıran saat sayısı; dokunulmayan uç tuttuğu değerle kalır (2026-09-16'ya kadar o uç da her seçimde kayıyordu). Gösterim yazılan değerden okunduğu için tek başına verilirse ekranda da görünür (-3: 15:00 seçilir, '…T12:00:00' yazılır, kutu 12:00 gösterir) ve değişen yarının tarihini her değiştirmede saati o kadar kaydırır. displayOffset ile HER ZAMAN ters işaretli ver (+3 / -3): kullanıcı gösterilen saati seçer ve korur, saklanan değer sabit kalır.",
543
626
  locale: LOCALE_FOLLOWS_APP_NOTE,
544
627
  isLoading: 'Akıştan açılır; alan kilitlenmez, yalnız spinner çizilir ve kök aria-busy taşır.'
545
628
  }
@@ -549,10 +632,20 @@ export const componentKnowledge = {
549
632
  filebutton: { summary: 'Buton görünümlü dosya seçici; fileInput ile aynı yük.' },
550
633
  'file-viewer': { summary: 'Dosya önizleyici (pdf/görsel); file prop’una drive dosyası bağlanır.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
551
634
  button: {
552
- summary: 'Tıklama kaynağı; click olayı veri taşımaz, bağlam gerekiyorsa ifadede komponent adlarıyla okunur.',
635
+ summary: "Tıklama kaynağı; type üç işten birini seçer — 'button' click akışını çalıştırır, 'submit' bir Form'u gönderir, 'link' bir adrese gider. click olayı veri taşımaz, bağlam gerekiyorsa ifadede komponent adlarıyla okunur.",
636
+ behavior: [
637
+ "click YALNIZ type:'button' iken yayılır. type:'submit' click YAYMAZ, formu gönderir: akışı butona değil formun submit olayına bağla (form doğrulaması geçerse yayılır). type:'link' de click yaymaz, tarayıcı href'e gider.",
638
+ "type:'submit' buton bir Form'un İÇİNDEYSE formId gerekmez, tarayıcı en yakın forma bağlar; formun DIŞINDAYSA formId = formun elementId'si ya da adı. isDisabled/isLoading açıkken gönderim de kilitlidir.",
639
+ "href ve isNewTabLink YALNIZ type:'link' iken okunur — href doldurmak butonu link yapmaz. İç sayfaya link için href'e URL değil { type:'page', ref:<pageId> } Prop'u yaz. Link kilitliyken (isDisabled/isLoading) ya da href boşken hiçbir yere gitmez ve odak almaz; kilit kalkınca ya da href akışla değişince adres anında güncellenir (2026-09-11 öncesi ilk çizimdeki adreste donuyordu: kilitli açılan link hep ölü kalıyordu).",
640
+ "Kayıtlı sayfalardaki eski kind:'custom' + customColor ikilisi VERİ GÖÇÜ OLMADAN aynen çizilir (render-anı takma adı, text emsali): surfaceStyle.background boşsa eski renk `--ng-btn-custom-color` değişkeniyle solid/outline/text varyantına eskisi gibi iner; Background doldurulunca takma ad susar. page_runtime_get(resolveStyles:true) bu değişkeni resolvedStyle'da gösterir. DİKKAT: platformun doğrulama duvarı sayfanın TAMAMINI okur — böyle bir düğüm taşıyan sayfaya yapılan her page_schema_patch, dokunulmayan düğüm yüzünden invalid-enum-value/unknown-prop ile reddedilir (ARC-036). Sayfayı yeniden yazmadan önce o butonu kind: semantik değer + surfaceStyle.background'a çevir (aynı çağrıda replace_prop ile).",
641
+ "İpucu (tooltip) prop'u YOKTUR: düğümün ortak appearance.tooltip alanı (+ tooltipPosition: top · bottom · left · right) her komponentte ipucu çizer. Tıklamadan önce onay istemek de butonun değil akışın işidir: click akışının başına confirm adımı koy (yalnız web — mobil barındırıcıda confirm adımı yok)."
642
+ ],
553
643
  propNotes: {
554
- 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.",
555
- customColor: "Yalnız kind:'custom' iken okunur; hex ya da tema token'ı ('var(--ng-accent-primary)').",
644
+ kind: "primary | info | success | warning | error — beş semantik renk, temayı izler; temizlenirse (null) primary çizilir. ÖZEL ZEMİN için kind değil surfaceStyle.background yazılır (Dalga 4, 2026-09-14): eski custom seçeneği ve customColor şemadan kalktı, yazılırsa invalid-enum-value / unknown-prop. `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.",
645
+ icon: "{ name: 'save', color? } nesnesi; düz 'save' metni de okunur (AI sözlüğü icon'u string gösteriyor — CQ-164). Adı boş ya da temizlenmiş ikon hiç çizilmez. Yükleniyorken ikonun yerinde spinner döner (link tipinde spinner yok).",
646
+ iconPosition: "'left' (varsayılan) | 'right' — ikonun (ve yükleniyorken spinner'ın) metnin hangi yanında durduğu; ikon yokken etkisiz.",
647
+ align: "'none' (varsayılan) butonu hücrenin TÜM genişliğine yayar; 'left' · 'center' · 'right' butonu içerik kadar daraltıp yaslar. Temizlenirse (null) 'none' sayılır.",
648
+ surfaceStyle: STYLE_GROUP_NOTES.surfaceStyle + ' ' + STYLE_GROUP_NOTES.colorTokens + " Buton yüzeyi (contentOverflow YOK — kaydırılan içerik yok). Her alan kind/variant CSS'inin ÜSTÜNE biner (tasarımcı kazanır): background Outline/Text varyantında da zemini doldurur ve o varyantların kind renkli hover dolgusu görünmez olur; shadow hover/active'de sabit kalır; opacity devre dışı görünümünü (0.5) de ezer. Metin rengi kind'dan gelir, burada alan yok — açık bir zeminde okunur kalması için style: { color: '…' }.",
556
649
  badgeValue: "Butonun köşe rozetinin metni ('3', '99+'); boşsa rozet çizilmez. Rengi badgeColor'dan gelir (primary|info|success|warning|error, varsayılan 'error')."
557
650
  }
558
651
  },
@@ -563,11 +656,11 @@ export const componentKnowledge = {
563
656
  summary: 'Görsel + başlık + açıklama + buton metni taşıyan hazır KART; click yayar.',
564
657
  behavior: [
565
658
  "KAPSAYICI DEĞİLDİR: çocuk kabul etmez (not-droppable). Kutu içinde serbest içerik göstermek için container kullanın (istenirse states.isWrapper:true).",
566
- 'Yükseklik 10-50 aralığındadır (min 10, max 20 varsayılan, max 50); aralık dışı blocks[].size.h invalid-size ile reddedilir.'
659
+ 'Yükseklik 10-50 aralığındadır (min 10, max 20 varsayılan, max 50). Bu sınır grid kanvasının kuralıdır, API aralık dışı blocks[].size.h değerini reddetmez; yine de aralıkta tutun.'
567
660
  ]
568
661
  },
569
662
  'qm-card': { summary: 'QM gösterge kartı; click yayar.' },
570
- 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 } },
663
+ tabs: { summary: "Sekmeler; tab olayı seçilen sekme anahtarını taşır, her view children sırasıyla eşleşir. Etkin OLMAYAN sekmenin içeriği DOM'da YOKTUR (keepAlways dışında): o sekmedeki bir düğümü hedefleyen doğrulama önce page_screenshot actions { action: 'tab', value: '<sekme anahtarı>' } ister — sekme grubunun köküne click ilk sekmeye gider ve reddedilir.", propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
571
664
  'collapsible-container': { summary: 'Aç/kapa kapsayıcı; collapsed/expanded olayları yayar.', propNotes: { surfaceStyle: NODE_SURFACE_NOTE } },
572
665
  'tree-view': {
573
666
  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.',
@@ -594,7 +687,7 @@ export const componentKnowledge = {
594
687
  text: {
595
688
  summary: 'Tek satırlık metin gösterimi; değer type:code prop’uyla ifadeye bağlanabilir. Görünümü TİPLİ stil gruplarındadır (typographyStyle · surfaceStyle), ham CSS değil.',
596
689
  behavior: [
597
- "Yükseklik SABİTTİR: maxSize.h = 1 (katalog varsayılanı 8'dir ve olduğu gibi kopyalanırsa reddedilir; gateway 1'e çeker ve gatewayNormalized ile bildirir). Çok satırlı metin için textarea ya da html kullanın.",
690
+ "Yükseklik SABİTTİR: maxSize.h = 1 (katalog varsayılanı 8'dir ve olduğu gibi kopyalanırsa kanvasta boyutlandırılamaz; gateway 1'e çeker ve gatewayNormalized ile bildirir). Çok satırlı metin için textarea ya da html kullanın.",
598
691
  "type HTML etiketi + ölçek seviyesidir (h1|h2|h3|label|p|small; a11y için doğru seviyeyi seç). Punto/kalınlık/renk/hiza kararı typographyStyle'dadır: fontSize seviyenin puntosunu ezer, boş bırakılırsa seviye ölçeği (tema --ng-font-size-*) geçerlidir.",
599
692
  "EMEKLİ prop'lar: isBold, align, color, customColor şemada YOK — yazılırsa unknown-prop ile reddedilir. Karşılıkları: typographyStyle.fontWeight ('600'), textAlign, textColor (semantik renk için tema token'ı: var(--ng-status-success) gibi). Kayıtlı eski sayfalarda bu prop'lar render anında takma adla çözülür (resolvedStyle bunu gösterir), yeni yazımda kullanılmaz.",
600
693
  "Hover grubu yok (metnin hover semantiği yok); surfaceStyle etiket/vurgulu paragraf içindir (zemin, köşe, çerçeve, gölge) ve contentOverflow alanı yoktur (taşma hedefi yok).",
@@ -614,7 +707,7 @@ export const componentKnowledge = {
614
707
  summary: 'Sayfa içi geri bildirim kutusu; akışlarda alert adımı yerine bu komponent kullanılır.',
615
708
  behavior: [
616
709
  "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.",
617
- "appearance.isHidden bir çözüm DEĞİLDİR: kaydedilir ama runtime hiçbir yerde okumaz (renderer yalnız props.isHidden'a bakar).",
710
+ "Görünürlük yalnız props.isHidden'dır: appearance'ta isHidden alanı YOKTUR (2026-09-15'te platformdan kaldırıldı, S-16). Gövdeye appearance.isHidden konursa gateway onu props.isHidden'a taşır ve gatewayNormalized'da bildirir.",
618
711
  "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.",
619
712
  'Başlangıçta gizli bir bildirim: şemaya props.isHidden:true yaz, açılışı akıştaki setter ile yap; timeout verilirse kendini kapatır.'
620
713
  ],
@@ -623,7 +716,25 @@ export const componentKnowledge = {
623
716
  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.'
624
717
  }
625
718
  },
626
- 'form-validation-errors': { summary: 'Formun doğrulama hatalarını listeler; form.errorElementId buraya işaret eder.' },
719
+ 'form-validation-errors': {
720
+ summary: 'Formun doğrulama hatalarını tek kutuda listeler; form.errorElementId buraya işaret eder. Liste boşken hiçbir şey çizmez.',
721
+ behavior: [
722
+ "BAĞLAMA: form'un errorElementId prop'una bu düğümün elementId'sini HER ZAMAN yaz. Yazılmazsa Form yalnız KENDİ İÇİNDEKİ ve elementId'si 'formvalidation-errors' ile başlayan düğümü bulur — o önek yalnız studio kenar çubuğundan sürüklenen düğümde oluşur; MCP ile kurulan, kopyalanan ya da formun dışına konan liste bulunmaz ve hiç dolmaz (docs/issues.md CQ-259).",
723
+ 'Listeyi Form doldurur: ilk submit’ten sonra hatalı alanları yazar, sonra her alan değişikliğinde listeyi yeniden yazar (düzelen alan düşer); başarısız submit’te kutuya kaydırır. resetForm listeyi de siler. Liste boşken bileşen DOM’a hiçbir şey basmaz.',
724
+ "errors öğesi { id, label, message }: id hatalı alanın elementId'si, label alan adı, message metin. Aynı id'li hatalar alan adının altında tek grupta toplanır. id'si olan satır düğmedir — tıklanınca alan kapalı bir sekmedeyse o sekme açılır, alana odaklanılır, kaydırılır ve 2 sn vurgulanır; id'siz satır düz metindir ve gruplanmaz. Dizi yerine düz metin öğesi ('Kayıt reddedildi') { message } sayılır; message'ı boş öğe ve dizi olmayan değer (null) çizilmez.",
725
+ 'Sunucu hatalarını (ör. 422 yanıtı) göstermek için akıştan setter ile errors yaz — Form’un yazdığıyla aynı biçim. Form bir sonraki submit/alan değişikliğinde listeyi kendi sonucuyla EZER.',
726
+ "message DÜZ METİN olarak basılır (2026-09-16'ya kadar HTML yorumlanıyordu; akıştan gelen metinde betik çalışabilirdi) — alanın kendi hata metniyle aynı. label'daki HTML etiketleri sökülür.",
727
+ "Paneldeki errors listesi yalnız KANVAS ÖNİZLEMESİDİR: sayfa açılınca düğümde statik değer silinir (2026-09-16'ya kadar yalnız bileşenin yerel kopyası siliniyordu ve düğümün başka bir prop'u — title, isHidden — akıştan değişince 'Username / Zip / Expiration date' örnekleri canlı sayfada beliriyordu). {{ }} ile bağlı errors silinmez. Silme düğüm başına bir kez yapılır: sekme değişimi ya da isHidden ile yeniden takılan liste Form'un yazdığı hataları korur.",
728
+ "Kutu role='alert' taşır: belirdiğinde ekran okuyucu içeriği duyurur; ikonlar okunmaz. Satır düğmelerinin klavye odak halkası vardır.",
729
+ 'Olay: yalnız mount (render katmanı atar); tıklama olayı yoktur, satır tıkı alana gider.'
730
+ ],
731
+ propNotes: {
732
+ title: "Kutunun başlığı (kırmızı, listenin üstünde); boşsa başlık satırı çizilmez. Varsayılan 'Please review the form and correct the following errors.' — 2026-09-16'ya kadar 'formula' yazım hatasıyla bırakılıyordu, eski düğümler kendi değerini taşır.",
733
+ errors: "Çalışma anında Form'a (ya da setter yazan akışa) aittir: [{ id, label, message }]. Statik değer yalnız kanvas önizlemesidir ve sayfa açılınca silinir — kalıcı bir mesaj için notification kullan.",
734
+ hideFormValidationMessage: "true → Form alanların altındaki hata metnini yazmaz, yalnız kırmızı çerçeve kalır; mesajlar yalnız bu listede görünür. Form bunu BU düğümden okur, yani errorElementId bağlı değilse etkisizdir. Kanvasta görünür etkisi yoktur.",
735
+ style: STYLE_GROUP_NOTES.styleEscape + " Parçalar: KÖK (anahtarlar doğrudan style'da) kırmızı kutudur — zemin (--ng-red-50), çerçeve (--ng-red-200), köşe; '&__title' başlık metni. Satır sınıfları belgelenmemiştir."
736
+ }
737
+ },
627
738
  breadcrumb: { summary: 'Sayfa yolu; currentTitle setter ile kip etiketine (Yeni/Düzenle) çevrilir.' },
628
739
  'page-header': {
629
740
  summary: 'Sayfa başlığı bandı; yalnız mount yayar.',
@@ -676,6 +787,7 @@ export const componentKnowledge = {
676
787
  ],
677
788
  propNotes: {
678
789
  surfaceStyle: NODE_SURFACE_NOTE,
790
+ navigationStyle: NAVIGATION_STYLE_NOTE,
679
791
  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.",
680
792
  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ı."
681
793
  }
@@ -686,14 +798,15 @@ export const componentKnowledge = {
686
798
  'Ağacı layout ve sidebar ile ORTAK renderer çizer (Navigations/NavigationsItem) — üçünün menü davranışı birebir aynıdır; ayrıntı sidebar kaydında.',
687
799
  "Öğe bazlı isHidden BU şemada VARDIR: canlıda elenir, Studio'nun düzenleyici kipinde görünmeye devam eder (Navigations.svelte: {#if isEditorMode || item.isHidden !== true}).",
688
800
  "activeHref boşken menü tarayıcıdaki adrese uyan satırı kendi bulur; adres mount'ta BİR KEZ okunur (bağlantılar tam sayfa yükler).",
689
- "style PROP'U ÖLÜDÜR: şema &__item ve &__item--active parçalarını sunuyor ama Navigations.svelte style diye bir prop KABUL ETMİYOR (yalnız navigations ve activeHref alır) ve satırları NavigationsItem kendi scoped .nav-btn sınıfıyla çizer. Panelden yazılan hiçbir şey uygulanmaz — sidebar'daki aynı kusurun ikizi (zeyrek-frontend docs/issues.md CQ-143 / CQ-132).",
801
+ "Satır görünümü TİPLİ navigationStyle grubuyla verilir (2026-09-15, layout ile aynı grup); serbest style'ın &__item, &__item--active ve &__icon parçaları da artık gerçek elemanlara basılır ve gruptan SONRA gelir (son söz).",
690
802
  'Menü olay YAYMAZ (şemada yalnız mount); gezinme ham <a href> ile tam sayfa yükleyerek olur.'
691
803
  ],
692
804
  propNotes: {
693
805
  navigations: "Ağaç: { title, href, icon, isHidden, isExpandable, isExpanded, children }. href düz metin ya da bağlantı seçicisinin nesnesi ({ type:'page', data, ref }) olabilir; ikisini de getHref çözer.",
694
806
  activeHref: 'Vurgulanacak satırın adresi; boş bırakılırsa tarayıcıdaki adres karar verir.',
695
807
  isHidden: 'Komponentin tamamını gizler (ortak katman prop’u); öğe bazlı gizleme navigations[].isHidden’dır.',
696
- style: 'ÖLÜ — hiçbir etkisi yok (bkz. davranış). Menü biçimini değiştirmek bugün mümkün değil.'
808
+ navigationStyle: NAVIGATION_STYLE_NOTE,
809
+ style: "Kaçış kanalı: &__item (her satır), &__item--active (seçili satır), &__icon (satır ikonu). Önce navigationStyle'ı kullan; burada yalnız grubun karşılamadığı CSS yazılır."
697
810
  }
698
811
  },
699
812
  'layout-shell': {
@@ -733,7 +846,7 @@ export const componentKnowledge = {
733
846
  behavior: [
734
847
  "Layout'ta TEK slot olmalı. Enjeksiyon HER slot'a ayrı ayrı kopyalar (layout.functions.ts injectPageSchemaIntoSlots ağacı dolaşır ve type==='slot' gören her düğümün children'ına sayfa şemasının klonunu yazar; önizleme tarafında page.mappers.mapPageLayoutToPreview aynısını yapar): iki slot = sayfa iki kez çizilir, elementId'ler çakışır. Doğrulama: page_state.layoutSlots.",
735
848
  "Slot'un yerleşimi ızgara bloğundan gelir ve satır yüksekliği AÇIK UÇLUDUR — runtime slot için grid-row'u `y / auto` yazar (render.functions.ts), yani sayfa içeriği ne kadar uzarsa slot o kadar uzar. Slot'a sabit yükseklik vermeye çalışmak anlamsızdır.",
736
- "Blok boyu yine de katalog sınırının içinde kalmalı: maxSize.h 200 (2026-09-12'den önce 80'di ve katalog varsayılanı 110 bile sınırın dışındaydı). Sınırı aşan TEK blok bütün yerleşimi/sayfayı PageSchemaValidationException + `invalid-size` ile düşürür."
849
+ "Blok boyu yine de katalog sınırının içinde kalmalı: maxSize.h 200 (2026-09-12'den önce 80'di ve katalog varsayılanı 110 bile sınırın dışındaydı). Bu sınır grid kanvasının kuralıdır; API blok boyunu denetlemez, ama sınırı aşan blok kanvasta boyutlandırılamaz."
737
850
  ]
738
851
  },
739
852
  view: { summary: 'Tabs/switcher altındaki görünüm kapsayıcısı; children gruplamaya yarar.' },
@@ -859,6 +972,7 @@ export const componentKnowledge = {
859
972
  categoryField: 'Kategori ekseni alan adı (varsayılan "label").',
860
973
  series: "[{ valueField, type?, axis?, color?, stack?, lowField/highField?, targetField? }] — yFields'ın yerini aldı.",
861
974
  stackMode: 'none | stacked | percent.',
975
+ dataLabels: "{ show, position, content, showTotal }. showTotal YALNIZ stackMode:'stacked' iken iş görür: her yığının tepesine (yatayda sağına) dilimlerin toplamını yazar, valueFormat uygulanır; toplam EKSENE ve LEGEND'e GİRMEZ. 2026-09-15 düzeltmesi — öncesinde toplam şeffaf bir seri olarak yığına ekleniyordu: değer ekseni gerçek en yüksek yığının İKİ KATINA çıkıyor, toplam etiketi barın çok üstünde boşlukta duruyor ve legend'de '<grup>__total' diye dördüncü bir madde beliriyordu (S-18). percent modunda etkisizdir.",
862
976
  isDrillDown: 'Veri noktası tıklamasını kırıntı seviyesine çevirir; sorguyu akış yeniler.'
863
977
  }
864
978
  },
@@ -1163,13 +1277,14 @@ export const componentKnowledge = {
1163
1277
  "Sürükleyerek taşıma yalnız isEditable ile: ay görünümünde gün kayar (saat korunur), hafta/gün kolonunda saat 30 dakikaya yuvarlanır; tüm-gün etkinlik saat KAZANMAZ. Yeniden boyutlandırma (resize) yoktur.",
1164
1278
  "Süresi >=24 saat ya da 00:00→00:00 olan etkinlik 'tüm gün' sayılır ve saat ızgarası yerine üst şeride çizilir; çok günlü etkinlik ay görünümünde kapladığı HER güne ayrı çip düşer (sürekli bant yok).",
1165
1279
  "defaultDate ve defaultView yalnız İLK kurulumda okunur — sonradan prop değişse de takvim kımıldamaz.",
1166
- "Hafta başlangıcı yerele bağlıdır (tr Pazartesi, en Pazar); startHour/endHour saat penceresini belirler, pencereye kısmen taşan blok en az 15 dakikalık yükseklikle çizilir."
1280
+ "Hafta başlangıcı yerele bağlıdır (tr Pazartesi, en Pazar); startHour/endHour saat penceresini belirler, pencereye kısmen taşan blok en az 15 dakikalık yükseklikle çizilir.",
1281
+ "Etkinlik her görünümde <elementId>__event-<id> data-element-id'si taşır; bırakma hedefleri ay hücresi ve hafta/gün kolonu <elementId>__day-<yyyy-MM-dd>, tüm gün hücresi __allday-<yyyy-MM-dd>, saat dilimi __slot-<yyyy-MM-dd>T<HH> (YEREL tarih). eventClick 'click' ile, eventMove page_screenshot actions 'drag' (target etkinlik, to gün/slot) ile doğrulanır; slota bırakma o saatin ortasına düşer ve 30 dakikaya yuvarlanır. Çok günlü etkinlik aynı kimliği her gününde taşır, hedef ilk eşleşmedir."
1167
1282
  ],
1168
1283
  propNotes: {
1169
1284
  rows: 'Etkinlik satırları; mount/monthChange akışlarının setter hedefi. Alan adları titleField/startField/endField/colorField/idField ile çözülür (nokta ayraçlı iç içe yol çalışır).',
1170
1285
  colorField: "Geçerli CSS rengi olduğu gibi kullanılır, semantik durum adı tema değişkenine çözülür, tanınmayan değer accent'e düşer. 'info' beyaz temada görünmezdir — ham renk ver.",
1171
1286
  maxVisibleEvents: 'Ay hücresinde görünen çip sayısı (varsayılan 3); fazlası +n olur.',
1172
- defaultDate: "Açılış ayı (yyyy-MM-dd; katalog varsayılanı örnek veriyle aynı ay olan '2026-08-01'). YALNIZ ilk kurulumda okunur.",
1287
+ defaultDate: "Açılış ayı (yyyy-MM-dd ya da ISO tarih-saat; katalog varsayılanı örnek veriyle aynı ay olan '2026-08-01'). YALNIZ ilk kurulumda okunur. BOŞ bırakılırsa bugünün ayı açılır — 'bugün' için sabit tarih yazmayın. İfadeyle bağlanacaksa code prop'u '{{ }}' içinde yazılır: { type: 'code', ref: \"{{ new Date().toISOString().slice(0, 10) }}\" } — parantezsiz ifade metin olarak gelir (PAGE-032 reddeder). Bu iki biçim dışındaki her metin bugüne düşer.",
1173
1288
  startHour: 'Hafta/gün görünümünün ilk saati (varsayılan 8); endHour en az startHour+1 olacak biçimde kırpılır (varsayılan 18).'
1174
1289
  }
1175
1290
  },
@@ -1198,7 +1313,8 @@ export const componentKnowledge = {
1198
1313
  "Kolon içi SIRALAMA yoktur: kartlar rows dizisinin sırasıyla çizilir, aynı kolona bırakma no-op'tur, sıra ne taşınır ne yayılır.",
1199
1314
  "wipLimit yalnız GÖRSELDİR: aşımda kolon başlığı kırmızılaşır ama bırakma engellenmez; 0/boş = sınırsız.",
1200
1315
  "Kart şablonu sabittir: başlığın ilk harfi (TR büyük harf) rozet + başlık + alt başlık — resim avatarı, etiket, tarih alanı yoktur. Renk karta sol şerit, kolona üst şerit olarak iner (ham CSS ya da semantik ad).",
1201
- "Sürükleme yerel HTML5 DnD'dir; klavyeyle taşıma yolu yoktur (Enter yalnız cardClick yayar). isLoading sürüklemeyi de kilitler."
1316
+ "Sürükleme yerel HTML5 DnD'dir; klavyeyle taşıma yolu yoktur (Enter yalnız cardClick yayar). isLoading sürüklemeyi de kilitler.",
1317
+ "Kart <elementId>__card-<id>, kolon <elementId>__col-<value> data-element-id'si taşır (id = idField değeri, yoksa card-<sıra>): cardClick page_screenshot actions 'click' ile, cardMove 'drag' (target kart, to kolon) ile uçtan uca doğrulanır — actionsReport flowRuns/requests taşımanın akışı gerçekten koşturduğunu gösterir."
1202
1318
  ],
1203
1319
  propNotes: {
1204
1320
  rows: 'Kart satırları; alan adları columnField/titleField/subtitleField/idField/colorField ile çözülür (nokta ayraçlı iç içe yol çalışır).',
@@ -1326,6 +1442,29 @@ export const INTERNAL_ROLES_BY_TYPE = {
1326
1442
  'colmenu',
1327
1443
  'search (arama kutusu, isSearchable)',
1328
1444
  'row-<0 tabanlı sıra; sayfa değişince yeniden numaralanır>'
1445
+ ],
1446
+ kanban: [
1447
+ "card-<kart id'si (idField; yoksa card-<sıra>)>",
1448
+ "col-<kolon value'su> (bırakma hedefi)"
1449
+ ],
1450
+ eventcalendar: [
1451
+ "event-<etkinlik id'si (idField; yoksa event-<sıra>)>",
1452
+ 'day-<yyyy-MM-dd> (ay hücresi / hafta-gün kolonu, bırakma hedefi)',
1453
+ 'allday-<yyyy-MM-dd> (tüm gün hücresi, bırakma hedefi)',
1454
+ 'slot-<yyyy-MM-dd>T<HH> (saat dilimi, bırakma hedefi)'
1455
+ ],
1456
+ // Sekme butonu kimliğini sekmenin KEY'inden alır (başlık çeviriye bağlıdır, dil değişince
1457
+ // kayardı). Kök düğüme click DOM sırasındaki ilk sekmeye gider; seçim `tab` eyleminin işidir.
1458
+ tabs: [
1459
+ "tab-<sekme anahtarı>"
1460
+ ],
1461
+ daterangepicker: [
1462
+ 'start (başlangıç tarihi kutusu, valueFrom)',
1463
+ 'end (bitiş tarihi kutusu, valueTo)'
1464
+ ],
1465
+ datetimerangepicker: [
1466
+ 'start (başlangıç tarihi kutusu, valueFrom)',
1467
+ 'end (bitiş tarihi kutusu, valueTo)'
1329
1468
  ]
1330
1469
  };
1331
1470
  /** Hedefteki `__` ayracından sonrası bir iç roldür; gateway bulunamayan hedefte listeyi basar. */