@sametalmdr/word-editor 0.2.0-alpha → 0.3.0-alpha

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 (82) hide show
  1. package/CHANGELOG.md +108 -70
  2. package/LICENSE +21 -21
  3. package/README.md +286 -247
  4. package/dist/canvas/box-clipboard.js +238 -238
  5. package/dist/canvas/box-selection.js +80 -80
  6. package/dist/canvas/canvas-editor.js +502 -502
  7. package/dist/canvas/controller.js +717 -717
  8. package/dist/canvas/geometry.js +131 -131
  9. package/dist/canvas/gesture.d.ts +0 -1
  10. package/dist/canvas/gesture.js +289 -289
  11. package/dist/canvas/handles.js +212 -212
  12. package/dist/canvas/menu.js +82 -82
  13. package/dist/canvas/mode-controller.js +81 -81
  14. package/dist/canvas/nudge.js +108 -108
  15. package/dist/canvas/overlay.js +149 -149
  16. package/dist/canvas/snap.js +132 -132
  17. package/dist/canvas/viewport.js +112 -112
  18. package/dist/canvas/z-order.js +171 -171
  19. package/dist/commands/alignment.js +150 -150
  20. package/dist/commands/box.js +272 -272
  21. package/dist/commands/clipboard.js +93 -93
  22. package/dist/commands/graphemes.js +112 -112
  23. package/dist/commands/marks.js +153 -153
  24. package/dist/commands/paragraph-props.js +176 -176
  25. package/dist/commands/size.js +368 -368
  26. package/dist/commands/text.js +291 -291
  27. package/dist/docx.d.ts +1 -0
  28. package/dist/docx.js +15 -13
  29. package/dist/editor/editing-host.js +434 -434
  30. package/dist/element.js +41 -41
  31. package/dist/fonts/OFL-Carlito.txt +94 -0
  32. package/dist/fonts/calibri-metrics.d.ts +27 -0
  33. package/dist/fonts/calibri-metrics.js +88 -0
  34. package/dist/history/history.js +433 -433
  35. package/dist/index.js +63 -63
  36. package/dist/input/beforeinput.js +224 -224
  37. package/dist/input/clipboard.js +107 -107
  38. package/dist/input/composition.js +341 -341
  39. package/dist/input/keymap.js +54 -54
  40. package/dist/io/ooxml/custgeom.js +72 -72
  41. package/dist/io/ooxml/drawingml.js +232 -232
  42. package/dist/io/ooxml/edits.js +67 -67
  43. package/dist/io/ooxml/export-splice.js +880 -880
  44. package/dist/io/ooxml/export.js +365 -365
  45. package/dist/io/ooxml/fill.d.ts +93 -8
  46. package/dist/io/ooxml/fill.js +618 -562
  47. package/dist/io/ooxml/fit.d.ts +252 -0
  48. package/dist/io/ooxml/fit.js +598 -0
  49. package/dist/io/ooxml/import-gate.js +200 -200
  50. package/dist/io/ooxml/import.js +691 -691
  51. package/dist/io/ooxml/inflate.js +126 -126
  52. package/dist/io/ooxml/media.js +87 -87
  53. package/dist/io/ooxml/ns.d.ts +2 -0
  54. package/dist/io/ooxml/ns.js +26 -21
  55. package/dist/io/ooxml/numbering.js +290 -290
  56. package/dist/io/ooxml/opc-write.js +171 -171
  57. package/dist/io/ooxml/provenance.js +93 -93
  58. package/dist/io/ooxml/recipe.js +708 -708
  59. package/dist/io/ooxml/relationships.js +59 -59
  60. package/dist/io/ooxml/run-fonts.d.ts +89 -0
  61. package/dist/io/ooxml/run-fonts.js +378 -0
  62. package/dist/io/ooxml/slots.d.ts +4 -0
  63. package/dist/io/ooxml/slots.js +582 -581
  64. package/dist/io/ooxml/styles-resolve.js +257 -257
  65. package/dist/io/ooxml/units.js +43 -43
  66. package/dist/io/ooxml/value.js +120 -120
  67. package/dist/io/ooxml/xml-scan.js +616 -616
  68. package/dist/io/ooxml/zip-container.js +228 -228
  69. package/dist/io/ooxml/zip-reject.js +106 -106
  70. package/dist/measure/placement.js +83 -83
  71. package/dist/measure/report.js +75 -75
  72. package/dist/model/ids.js +70 -70
  73. package/dist/model/normalize.js +146 -146
  74. package/dist/model/position.js +283 -283
  75. package/dist/model/transaction.js +209 -209
  76. package/dist/platform/capabilities.js +25 -25
  77. package/dist/react/index.js +21 -21
  78. package/dist/selection/controller.js +315 -315
  79. package/dist/selection/dom-map.js +334 -334
  80. package/dist/state/editor-state.js +109 -109
  81. package/dist/styles.css +66 -66
  82. package/package.json +94 -93
package/README.md CHANGED
@@ -1,247 +1,286 @@
1
- # @sametalmdr/word-editor
2
-
3
- Tarayıcıda çalışan bir **sayfa tuvali** editörü: sabit sayfa yüzeyleri
4
- üzerinde mutlak konumlu metin kutularının içeriğini ve yerleşimini düzenler,
5
- gerçek `.docx` dosyalarını açar ve orijinal paketi yamalayarak geri kaydeder.
6
- Akan metin / sayfalama / tablo yok (ADR-0006). Sıfır runtime bağımlılık, saf
7
- ESM, MIT lisanslı.
8
-
9
- > ⚠️ **Durum (2026-10-26): 0.2.0-alpha hazır (0.1.0-alpha'ya çok satırlı değer eklendi); npm'de yayını sahip yapar.** Editör çalışıyor:
10
- > `.docx` aç, metin yaz, kutu taşı, kalın/italik/altı çizili, punto, hizalama,
11
- > girinti, Kaydet. **Gerçek Word'de doğrulanan:** metin, kutu taşıma ve
12
- > kalın — kaydedilen dosya onarımsız açılıyor ve değişiklik görünüyor.
13
- > İtalik ve altı çizili aynı yazma yolundan geçiyor, ama Word'de ayrıca
14
- > açılmadı. Punto, hizalama ve girinti dosyaya yazılıyor ama **Word'de henüz
15
- > doğrulanmadı** (FIDELITY.md'de AÇIK). **Bugün yalnız örnek sayfalardan
16
- > kullanılabiliyor** (aşağıda `app.html`): paketin public API'si `createEditor()`
17
- > (bir `doc` alır, tuval editörünü dışa vermez). `.docx` import/export'u ve şablon doldurma
18
- > **`/docx` alt yolunda** (aşağıda; `0.2.0-alpha`), `/canvas` FR4'te geliyor
19
- > ([docs/MASTER-PLAN.md](./docs/MASTER-PLAN.md)). Kaydedilmeyen düzenlemeler ve bilinen
20
- > iki hata (yapıştırılan kutu kaydı engelliyor, silinen kutu sessizce
21
- > kayboluyor): [FIDELITY.md](./FIDELITY.md) "Kutu yerleşimi" bölümü. Yol
22
- > haritası: [ARCHITECTURE.md](./ARCHITECTURE.md) §6.
23
-
24
- Bu proje Microsoft Word'ün bir klonu değildir ve Microsoft ile hiçbir
25
- bağlantısı yoktur.
26
-
27
- ## Kurulum
28
-
29
- ```bash
30
- npm install @sametalmdr/word-editor@0.2.0-alpha
31
- ```
32
-
33
- ## Saf HTML ile kullanım
34
-
35
- Build adımı gerekmez.
36
-
37
- ```html
38
- <link rel="stylesheet" href="./node_modules/@sametalmdr/word-editor/dist/styles.css" />
39
- <word-editor></word-editor>
40
- <script type="module">
41
- import "@sametalmdr/word-editor/element";
42
- </script>
43
- ```
44
-
45
- ## React ile kullanım
46
-
47
- `react` bir optional peer dependency'dir — kendi React sürümünüzü kurmanız
48
- gerekir.
49
-
50
- ```jsx
51
- import { WordEditor } from "@sametalmdr/word-editor/react";
52
- import "@sametalmdr/word-editor/styles.css";
53
-
54
- function App() {
55
- return <WordEditor />;
56
- }
57
- ```
58
-
59
- ## Framework-agnostic çekirdek API
60
-
61
- ```js
62
- import { createEditor } from "@sametalmdr/word-editor";
63
-
64
- const editor = createEditor({ container: document.getElementById("root") });
65
- // ...
66
- editor.destroy();
67
- ```
68
-
69
- ## `/docx` — şablon doldurma (DOM'suz; Node, Deno ve tarayıcı için yazıldı)
70
-
71
- `{etiket}`li bir `.docx` şablonunu veriyle doldurur. Orijinal paketi yamalar: yalnız
72
- `word/document.xml` yeniden yazılır, başka her parça bayt bayt aynı kalır. Hiçbir ağ isteği
73
- atmaz; sunucuda (ör. Supabase Edge Functions) ve tarayıcıda aynı fonksiyon. Kapılarla sınanan:
74
- Node 24.13.1 ve Deno 2.1.4; tarayıcıda (Chrome 152) yalnız incelendi, kanıt sınıfıyla değil.
75
-
76
- ```js
77
- import { fillTemplate, inspectTemplate, TemplateError } from "@sametalmdr/word-editor/docx";
78
-
79
- // template: bir .docx dosyasının baytları (Uint8Array)
80
- try {
81
- const { bytes, documentXml, report } = await fillTemplate(template, {
82
- firma_adi: "Alrosoft Ltd",
83
- rapor_no: "RPR-2026-0001",
84
- notlar: null, // boş: serbest, report.empty'de listelenir
85
- });
86
- // bytes: doldurulmuş .docx
87
- // documentXml: doldurulmuş word/document.xml, sıkıştırılmamış — çalışma ortamları
88
- // arasında karşılaştırmayı bunun özetiyle yapın (zip baytları ortama göre değişir)
89
- // report: etiket -> [{docPrId, page}], empty, unused, warnings, counts, engine
90
- } catch (err) {
91
- if (!(err instanceof TemplateError)) throw err;
92
- for (const p of err.problems) console.log(p.code, p.tag, p.page); // hepsi birlikte
93
- }
94
-
95
- const info = await inspectTemplate(template); // doldurmadan: etiketler, sorunlar, uyarılar
96
- ```
97
-
98
- Kurallar ([ADR-0037](./docs/adr/0037-template-fill-engine.md),
99
- [ADR-0038](./docs/adr/0038-generated-output-keeps-both-copies-equal.md)):
100
-
101
- - Etiket bir `w:t`'nin metninin tamamıdır: `{firma_adi}`, ad `[a-z][a-z0-9_]*`. Bölünmüş
102
- etiket, `{ ad }`, `{#döngü}`, `{%görsel}` ve "Tarih: {tarih}" gibi karışık metin reddedilir.
103
- - Etiket yalnız metin kutusunda olabilir. Kutunun iki kopyası (`mc:Choice` ve `mc:Fallback`)
104
- birlikte yazılır ve eşit kaldıkları denetlenir — Word yalnız Choice'u okur, yanlış bir
105
- Fallback Word'de görünmez (FILL-E2).
106
- - Eksik anahtar hata; eksiklerin hepsi tek `TemplateError`'da. `null`, `""` ve yalnız boşluk
107
- boştur: serbest, raporlanır.
108
- - Değer string olmalı; sekme ve denetim karakterleri reddedilir. Tarih ve sayı biçimlemek
109
- çağıranın işi.
110
- - Satır sonu yalnız `multiline` seçeneğinin adlandırdığı etikette ve yalnız LF olarak
111
- ([ADR-0039](./docs/adr/0039-multi-line-values.md)): her satır etiketin kendi run'ında, aralarında
112
- `w:br`, run'ın biçimi her satırda. CR, CRLF, U+2028 ve U+2029 her etikette reddedilir — motor
113
- değeri sessizce değiştirmez, normalleştirmek çağıranın işi. Şablonda olmayan bir ad
114
- `UNKNOWN_MULTILINE_TAG`:
115
-
116
- ```js
117
- await fillTemplate(template, { ...data, notlar: "Birinci satır\nİkinci satır" }, { multiline: ["notlar"] });
118
- ```
119
-
120
- Kutunun alabileceğinden fazla satır Word'de sessizce kırpılır (FR3-WORD-ML2); sığmayı denetlemek
121
- bugün çağıranın işi.
122
- - `validateTemplate(bytes, {expectedTags, multiline})` şablonun etiketlerini bir listeyle tam küme
123
- olarak karşılaştırır. Bilinmeyen seçenek (`{linebreaks: true}` gibi) hata verir.
124
- - Rapor, sorunlar ve hata mesajları hiçbir değeri taşımaz.
125
-
126
- Bilinen sınırlar ve sınanan ortamlar: [CHANGELOG.md](./CHANGELOG.md).
127
-
128
- ## Ürün sayfası — `app.html` (DOCX aç → düzenle → kaydet)
129
-
130
- Ribbon'lı tam editör. Paket henüz bu yüzeyi dışa vermediği için (yukarıdaki
131
- durum notu) bugün yalnız repo içinden çalışır:
132
-
133
- ```bash
134
- npm run serve
135
- ```
136
-
137
- ```
138
- http://127.0.0.1:8080/examples/static/app.html
139
- ```
140
-
141
- **Dosya** sekmesindeki **Dosya Seç** ile `test/corpus/` altındaki bir belgeyi
142
- seç (dosya yalnız okunur, değiştirilmez). Çift tık bir kutunun içinde metin moduna geçer,
143
- tek tık kutuyu seçer (ADR-0024). **Kaydet (.docx)** orijinal paketi yamalayarak
144
- indirir. "PDF indir" henüz bağlı değil — bugün PDF için görüntüleyicinin
145
- Ctrl+P'si var (aşağıda).
146
-
147
- ## Corpus görüntüleyici (demo)
148
-
149
- 46 corpus belgesini gerçek renderer ile tarayıcıda gösterir.
150
- **Görüntüleyici — düzenleme yok.** İki komut:
151
-
152
- ```bash
153
- npm run corpus:canvas
154
- ```
155
-
156
- ```bash
157
- npm run serve
158
- ```
159
-
160
- Sonra tarayıcıda aç:
161
-
162
- ```
163
- http://127.0.0.1:8080/examples/static/viewer.html
164
- ```
165
-
166
- Solda 46 belge, seçince sayfaları çizilir; başlıkta kutu/dekorasyon/sayfa
167
- sayısı ve render süresi görünür.
168
-
169
- **Çift tıklayarak açılmaz, ve bu ölçülmüş bir kısıt.** Chrome 151 /
170
- Windows 10 22H2'de `file://` üzerinde statik ES modül import'u, dinamik
171
- `import()` ve `fetch()` **üçü de bloklanıyor**; yalnızca inline klasik
172
- `<script>` koşuyor. Gerçek renderer'ı import etmenin başka yolu yok. Dört
173
- ölçümün tamamı [docs/viewer-measurement.md](./docs/viewer-measurement.md)
174
- §0'da; sunucu `node:http` ile yazıldı, **sıfır bağımlılık**, yalnızca
175
- loopback. Aynı kısıt `examples/static/canvas.html` ve `index.html` için de
176
- geçerli.
177
-
178
- `npm run corpus:canvas` Python ister (`scripts/docx-to-canvas-json.py`
179
- dönüştürücüsünü çağırır) ve bu yüzden `npm run ci`'nin parçası **değildir** —
180
- `corpus:check` ve `corpus:names` ile aynı gerekçe. Çıktı
181
- `examples/static/corpus-json/` altına yazılır, gitignore'ludur, her koşumda
182
- baştan üretilir. `test/corpus/` **okunur, asla yazılmaz** (CLAUDE.md #16).
183
-
184
- ### PDF — Tier 0
185
-
186
- Görüntüleyicide **Ctrl+P → "PDF olarak kaydet"**. Sayfa yüzeyleri belgenin
187
- kendi boyutunda basılır (A4 dikey, A4 yatay ve A5 corpus'ta hepsi var), UI
188
- kromu gizlenir.
189
-
190
- **Bu bizim PDF writer'ımız değil, tarayıcınınki** — `ARCHITECTURE.md` §7'nin
191
- Tier 0'ı. Sunucuda üretilen, gömülü fontlu PDF FR5'te geliyor (eski MP10, yol
192
- haritasındaki P7); yolunu — kendi yazıcımız, pdf-lib ya da Cloudflare Browser Run — FR5
193
- başında sahip seçiyor (ADR-0036 Karar 5).
194
-
195
- **Gözle kontrol — henüz yapılmadı, yapılacaksa şöyle:** üç belgeyi seçip
196
- Ctrl+P ile önizlemeye bak; kâğıt boyutu belgenin kendi boyutuna oturmalı,
197
- kenar boşluğu olmamalı, sol liste ve üst başlık görünmemeli.
198
-
199
- | Belge | Beklenen kâğıt | Neden bu üçü |
200
- |---|---|---|
201
- | `AA01` | A4 dikey (595.5×842 pt) | corpus'un çoğunluğu |
202
- | `PK01` | **A4 YATAY** (842×595.5 pt) | sabit `A4` varsayımının kırılacağı yer |
203
- | `R001` | **A5** (420×595.5 pt) | ikinci kırılma noktası; 71 sayfa |
204
-
205
- Üçü de doğru basıyorsa sayfa boyutu belge başına yazılıyor demektir.
206
-
207
- Ölçüm sonuçları: [docs/viewer-measurement.md](./docs/viewer-measurement.md).
208
-
209
- ## Tuval — kutu düzenleme (demo, P3b-a)
210
-
211
- Görüntüleyiciden farklı olarak **burada düzenleme var** — metin değil, kutu
212
- geometrisi: seç, tutamakla sürükle/boyutlandır, Ctrl+Z ile geri al,
213
- kopyala/yapıştır, dekoratif bir şeklin kenarına yaklaştırıp snap'i gör.
214
- `src/canvas/`'ın dokuz saf modülü + `CanvasController` + `Overlay`, gerçek
215
- bir tarayıcıya bağlı (`docs/adr/0018-canvas-interaction-layer.md`).
216
-
217
- ```bash
218
- npm run serve
219
- ```
220
-
221
- ```
222
- http://127.0.0.1:8080/examples/static/canvas.html
223
- ```
224
-
225
- Dosya seçiciyle bir belge JSON'u yükle: `test/fixtures/canvas/fifty-boxes.json`
226
- (50 kutu, 5 dekorasyon, 2 sayfa) hazır bir örnek; `npm run corpus:canvas`'ın
227
- ürettiği `examples/static/corpus-json/*.json` de aynı formatta çalışır.
228
-
229
- **Ölçülmemiş sabitler, sayfanın kendi geçici değerleri — kütüphaneye değil:**
230
- tutamak boyutu, snap eşiği, kademe eşikleri hiçbiri ölçülmedi (Ö2/Ö4,
231
- ADR-0018 §9.0). `canvas.html` kendi `EXAMPLE_METRICS` nesnesini enjekte
232
- ediyor; `src/canvas/controller.js`'in `requireMetrics`'i eksik alanda hâlâ
233
- gerçek `TypeError` atıyor, `src/` altına hiçbir varsayılan sızmadı.
234
-
235
- Aynı `file://` kısıtı geçerli (yukarıdaki görüntüleyici bölümüne bakınız).
236
- ~~Metin düzenleme bu turun kapsamında değil~~ — 2026-08-29'dan beri
237
- (ADR-0024) çift tık kutunun içinde metin moduna geçiyor, ve sayfa artık
238
- `.docx` da açıp kaydedebiliyor. Ürün yüzeyi ise `app.html` (yukarıda).
239
-
240
- ## Mimari
241
-
242
- Tasarım kararları için [ARCHITECTURE.md](./ARCHITECTURE.md),
243
- uyumluluk sözleşmesi için [FIDELITY.md](./FIDELITY.md).
244
-
245
- ## Lisans
246
-
247
- MIT — bkz. [LICENSE](./LICENSE).
1
+ # @sametalmdr/word-editor
2
+
3
+ Tarayıcıda çalışan bir **sayfa tuvali** editörü: sabit sayfa yüzeyleri
4
+ üzerinde mutlak konumlu metin kutularının içeriğini ve yerleşimini düzenler,
5
+ gerçek `.docx` dosyalarını açar ve orijinal paketi yamalayarak geri kaydeder.
6
+ Akan metin / sayfalama / tablo yok (ADR-0006). Sıfır runtime bağımlılık, saf
7
+ ESM, MIT lisanslı.
8
+
9
+ > ⚠️ **Durum (2026-10-26): 0.3.0-alpha hazır (0.2.0-alpha'ya `checkFit` eklendi); npm'de yayını sahip yapar. 0.2.0-alpha npm'de.** Editör çalışıyor:
10
+ > `.docx` aç, metin yaz, kutu taşı, kalın/italik/altı çizili, punto, hizalama,
11
+ > girinti, Kaydet. **Gerçek Word'de doğrulanan:** metin, kutu taşıma ve
12
+ > kalın — kaydedilen dosya onarımsız açılıyor ve değişiklik görünüyor.
13
+ > İtalik ve altı çizili aynı yazma yolundan geçiyor, ama Word'de ayrıca
14
+ > açılmadı. Punto, hizalama ve girinti dosyaya yazılıyor ama **Word'de henüz
15
+ > doğrulanmadı** (FIDELITY.md'de AÇIK). **Bugün yalnız örnek sayfalardan
16
+ > kullanılabiliyor** (aşağıda `app.html`): paketin public API'si `createEditor()`
17
+ > (bir `doc` alır, tuval editörünü dışa vermez). `.docx` import/export'u ve şablon doldurma
18
+ > **`/docx` alt yolunda** (aşağıda; `0.3.0-alpha`), `/canvas` FR4b'de geliyor
19
+ > ([docs/MASTER-PLAN.md](./docs/MASTER-PLAN.md)). Kaydedilmeyen düzenlemeler ve bilinen
20
+ > iki hata (yapıştırılan kutu kaydı engelliyor, silinen kutu sessizce
21
+ > kayboluyor): [FIDELITY.md](./FIDELITY.md) "Kutu yerleşimi" bölümü. Yol
22
+ > haritası: [ARCHITECTURE.md](./ARCHITECTURE.md) §6.
23
+
24
+ Bu proje Microsoft Word'ün bir klonu değildir ve Microsoft ile hiçbir
25
+ bağlantısı yoktur.
26
+
27
+ ## Kurulum
28
+
29
+ ```bash
30
+ npm install @sametalmdr/word-editor@0.3.0-alpha
31
+ ```
32
+
33
+ ## Saf HTML ile kullanım
34
+
35
+ Build adımı gerekmez.
36
+
37
+ ```html
38
+ <link rel="stylesheet" href="./node_modules/@sametalmdr/word-editor/dist/styles.css" />
39
+ <word-editor></word-editor>
40
+ <script type="module">
41
+ import "@sametalmdr/word-editor/element";
42
+ </script>
43
+ ```
44
+
45
+ ## React ile kullanım
46
+
47
+ `react` bir optional peer dependency'dir — kendi React sürümünüzü kurmanız
48
+ gerekir.
49
+
50
+ ```jsx
51
+ import { WordEditor } from "@sametalmdr/word-editor/react";
52
+ import "@sametalmdr/word-editor/styles.css";
53
+
54
+ function App() {
55
+ return <WordEditor />;
56
+ }
57
+ ```
58
+
59
+ ## Framework-agnostic çekirdek API
60
+
61
+ ```js
62
+ import { createEditor } from "@sametalmdr/word-editor";
63
+
64
+ const editor = createEditor({ container: document.getElementById("root") });
65
+ // ...
66
+ editor.destroy();
67
+ ```
68
+
69
+ ## `/docx` — şablon doldurma (DOM'suz; Node, Deno ve tarayıcı için yazıldı)
70
+
71
+ `{etiket}`li bir `.docx` şablonunu veriyle doldurur. Orijinal paketi yamalar: yalnız
72
+ `word/document.xml` yeniden yazılır, başka her parça bayt bayt aynı kalır. Hiçbir ağ isteği
73
+ atmaz; sunucuda (ör. Supabase Edge Functions) ve tarayıcıda aynı fonksiyon. Kapılarla sınanan:
74
+ Node 24.13.1 ve Deno 2.1.4; tarayıcıda (Chrome 152) yalnız incelendi, kanıt sınıfıyla değil.
75
+
76
+ ```js
77
+ import { fillTemplate, inspectTemplate, TemplateError } from "@sametalmdr/word-editor/docx";
78
+
79
+ // template: bir .docx dosyasının baytları (Uint8Array)
80
+ try {
81
+ const { bytes, documentXml, report } = await fillTemplate(template, {
82
+ firma_adi: "Alrosoft Ltd",
83
+ rapor_no: "RPR-2026-0001",
84
+ notlar: null, // boş: serbest, report.empty'de listelenir
85
+ });
86
+ // bytes: doldurulmuş .docx
87
+ // documentXml: doldurulmuş word/document.xml, sıkıştırılmamış — çalışma ortamları
88
+ // arasında karşılaştırmayı bunun özetiyle yapın (zip baytları ortama göre değişir)
89
+ // report: etiket -> [{docPrId, page}], empty, unused, warnings, counts, engine
90
+ } catch (err) {
91
+ if (!(err instanceof TemplateError)) throw err;
92
+ for (const p of err.problems) console.log(p.code, p.tag, p.page); // hepsi birlikte
93
+ }
94
+
95
+ const info = await inspectTemplate(template); // doldurmadan: etiketler, sorunlar, uyarılar
96
+ ```
97
+
98
+ Kurallar ([ADR-0037](./docs/adr/0037-template-fill-engine.md),
99
+ [ADR-0038](./docs/adr/0038-generated-output-keeps-both-copies-equal.md)):
100
+
101
+ - Etiket bir `w:t`'nin metninin tamamıdır: `{firma_adi}`, ad `[a-z][a-z0-9_]*`. Bölünmüş
102
+ etiket, `{ ad }`, `{#döngü}`, `{%görsel}` ve "Tarih: {tarih}" gibi karışık metin reddedilir.
103
+ - Etiket yalnız metin kutusunda olabilir. Kutunun iki kopyası (`mc:Choice` ve `mc:Fallback`)
104
+ birlikte yazılır ve eşit kaldıkları denetlenir — Word yalnız Choice'u okur, yanlış bir
105
+ Fallback Word'de görünmez (FILL-E2).
106
+ - Eksik anahtar hata; eksiklerin hepsi tek `TemplateError`'da. `null`, `""` ve yalnız boşluk
107
+ boştur: serbest, raporlanır.
108
+ - Değer string olmalı; sekme ve denetim karakterleri reddedilir. Tarih ve sayı biçimlemek
109
+ çağıranın işi.
110
+ - Satır sonu yalnız `multiline` seçeneğinin adlandırdığı etikette ve yalnız LF olarak
111
+ ([ADR-0039](./docs/adr/0039-multi-line-values.md)): her satır etiketin kendi run'ında, aralarında
112
+ `w:br`, run'ın biçimi her satırda. CR, CRLF, U+2028 ve U+2029 her etikette reddedilir — motor
113
+ değeri sessizce değiştirmez, normalleştirmek çağıranın işi. Şablonda olmayan bir ad
114
+ `UNKNOWN_MULTILINE_TAG`:
115
+
116
+ ```js
117
+ await fillTemplate(template, { ...data, notlar: "Birinci satır\nİkinci satır" }, { multiline: ["notlar"] });
118
+ ```
119
+
120
+ Kutunun alabileceğinden fazla satır Word'de sessizce kırpılır (FR3-WORD-ML2); `checkFit` bunu
121
+ doldurmadan önce söyler (aşağıda).
122
+ - `validateTemplate(bytes, {expectedTags, multiline})` şablonun etiketlerini bir listeyle tam küme
123
+ olarak karşılaştırır. Bilinmeyen seçenek (`{linebreaks: true}` gibi) hata verir.
124
+ - Rapor, sorunlar ve hata mesajları hiçbir değeri taşımaz.
125
+
126
+ ### `checkFit` — değer kutusuna sığıyor mu? ([ADR-0042](./docs/adr/0042-dom-free-fit-check.md))
127
+
128
+ Word kutuya sığmayan değeri sessizce kırpar. `checkFit` doldurmadan önce her değerin satırını şablonun
129
+ kendi kutusuyla karşılaştırır. Tarayıcı gerekmez: Calibri'nin genişlikleri, onunla metrik-uyumlu
130
+ Carlito'dan (OFL-1.1) bir tablo olarak paketin içinde.
131
+
132
+ ```js
133
+ import { checkFit, fillTemplate } from "@sametalmdr/word-editor/docx";
134
+
135
+ const fit = await checkFit(template, data, { ...manifest.options, ...manifest.fit });
136
+ if (!fit.ok) {
137
+ for (const t of fit.tags) {
138
+ for (const o of t.occurrences) {
139
+ if (o.status === "overflows" || o.status === "unknown") console.log(t.name, o.status, o.reason, o.marginPt);
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ - **Durumlar:** her occurrence `fits`, `overflows`, `unknown` ya da `empty` döner.
146
+ - Ölçülen occurrence en dar satırının `widthPt`, `availablePt` ve `marginPt` değerlerini taşır.
147
+ - `unknown` (ve sekmesini durağından iten `overflows`) kapalı bir listeden bir `reason` taşır.
148
+ - Etiketin durumu, occurrence'larının en kötüsüdür.
149
+ - **Tolerans:** τ = 0,05 pt + glif başına 0,025 pt.
150
+ - `fits`: satır τ payıyla sığar.
151
+ - `overflows`: satır τ'nun ötesinde taşar.
152
+ - Aradaki `unknown(BORDERLINE)` için karar verilmez.
153
+ - **Sondaki boşluklar:** Word'ün onları kenarın ötesine asılı bırakıp bırakmadığı sınanmadı. `fits` onları
154
+ sayarak sığmayı, `overflows` onlarsız taşmayı ister; yalnız onlar karar veriyorsa `unknown(TRAILING_SPACE)`.
155
+ Değeri kırpmak bu durumu ortadan kaldırır.
156
+ - **Çok satırlı değer** `capacity` ister: `{etiket: satır}`, manifestin `fit` bloğunda. Taşan satır en az
157
+ bir satır daha alır. Satır sayısı ile taşan satır sayısının toplamı kapasiteyi geçerse `overflows`.
158
+ - **Kırma yok:** satır kırma noktası seçilmez. Ölçülemeyen biçim `unknown` döner (başka bir font,
159
+ döndürülmüş ya da gruplanmış kutu, `kern`, sola dayalı olmayan sekme durağı, iç boşluklu kutuda ya da
160
+ sola dayalı olmayan satırda sekme vb.).
161
+ - **Doğrulama:** `fillTemplate`'in reddettiğini aynı `TemplateError` ile reddeder. Hiçbir şey yazmaz;
162
+ rapor değer taşımaz.
163
+ - **Öneri:** onayı `overflows` **ve** `unknown` durumunda kapatın.
164
+
165
+ Bilinen sınırlar ve sınanan ortamlar: [CHANGELOG.md](./CHANGELOG.md).
166
+
167
+ ## Ürün sayfası — `app.html` (DOCX aç → düzenle → kaydet)
168
+
169
+ Ribbon'lı tam editör. Paket henüz bu yüzeyi dışa vermediği için (yukarıdaki
170
+ durum notu) bugün yalnız repo içinden çalışır:
171
+
172
+ ```bash
173
+ npm run serve
174
+ ```
175
+
176
+ ```
177
+ http://127.0.0.1:8080/examples/static/app.html
178
+ ```
179
+
180
+ **Dosya** sekmesindeki **Dosya Seç** ile `test/corpus/` altındaki bir belgeyi
181
+ seç (dosya yalnız okunur, değiştirilmez). Çift tık bir kutunun içinde metin moduna geçer,
182
+ tek tık kutuyu seçer (ADR-0024). **Kaydet (.docx)** orijinal paketi yamalayarak
183
+ indirir. "PDF indir" henüz bağlı değil — bugün PDF için görüntüleyicinin
184
+ Ctrl+P'si var (aşağıda).
185
+
186
+ ## Corpus görüntüleyici (demo)
187
+
188
+ 46 corpus belgesini gerçek renderer ile tarayıcıda gösterir.
189
+ **Görüntüleyici — düzenleme yok.** İki komut:
190
+
191
+ ```bash
192
+ npm run corpus:canvas
193
+ ```
194
+
195
+ ```bash
196
+ npm run serve
197
+ ```
198
+
199
+ Sonra tarayıcıda aç:
200
+
201
+ ```
202
+ http://127.0.0.1:8080/examples/static/viewer.html
203
+ ```
204
+
205
+ Solda 46 belge, seçince sayfaları çizilir; başlıkta kutu/dekorasyon/sayfa
206
+ sayısı ve render süresi görünür.
207
+
208
+ **Çift tıklayarak açılmaz, ve bu ölçülmüş bir kısıt.** Chrome 151 /
209
+ Windows 10 22H2'de `file://` üzerinde statik ES modül import'u, dinamik
210
+ `import()` ve `fetch()` **üçü de bloklanıyor**; yalnızca inline klasik
211
+ `<script>` koşuyor. Gerçek renderer'ı import etmenin başka yolu yok. Dört
212
+ ölçümün tamamı [docs/viewer-measurement.md](./docs/viewer-measurement.md)
213
+ §0'da; sunucu `node:http` ile yazıldı, **sıfır bağımlılık**, yalnızca
214
+ loopback. Aynı kısıt `examples/static/canvas.html` ve `index.html` için de
215
+ geçerli.
216
+
217
+ `npm run corpus:canvas` Python ister (`scripts/docx-to-canvas-json.py`
218
+ dönüştürücüsünü çağırır) ve bu yüzden `npm run ci`'nin parçası **değildir** —
219
+ `corpus:check` ve `corpus:names` ile aynı gerekçe. Çıktı
220
+ `examples/static/corpus-json/` altına yazılır, gitignore'ludur, her koşumda
221
+ baştan üretilir. `test/corpus/` **okunur, asla yazılmaz** (CLAUDE.md #16).
222
+
223
+ ### PDF — Tier 0
224
+
225
+ Görüntüleyicide **Ctrl+P → "PDF olarak kaydet"**. Sayfa yüzeyleri belgenin
226
+ kendi boyutunda basılır (A4 dikey, A4 yatay ve A5 corpus'ta hepsi var), UI
227
+ kromu gizlenir.
228
+
229
+ **Bu bizim PDF writer'ımız değil, tarayıcınınki** — `ARCHITECTURE.md` §7'nin
230
+ Tier 0'ı. Sunucuda üretilen, gömülü fontlu PDF FR5'te geliyor (eski MP10, yol
231
+ haritasındaki P7); yolunu — kendi yazıcımız, pdf-lib ya da Cloudflare Browser Run — FR5
232
+ başında sahip seçiyor (ADR-0036 Karar 5).
233
+
234
+ **Gözle kontrol — henüz yapılmadı, yapılacaksa şöyle:** üç belgeyi seçip
235
+ Ctrl+P ile önizlemeye bak; kâğıt boyutu belgenin kendi boyutuna oturmalı,
236
+ kenar boşluğu olmamalı, sol liste ve üst başlık görünmemeli.
237
+
238
+ | Belge | Beklenen kâğıt | Neden bu üçü |
239
+ |---|---|---|
240
+ | `AA01` | A4 dikey (595.5×842 pt) | corpus'un çoğunluğu |
241
+ | `PK01` | **A4 YATAY** (842×595.5 pt) | sabit `A4` varsayımının kırılacağı yer |
242
+ | `R001` | **A5** (420×595.5 pt) | ikinci kırılma noktası; 71 sayfa |
243
+
244
+ Üçü de doğru basıyorsa sayfa boyutu belge başına yazılıyor demektir.
245
+
246
+ Ölçüm sonuçları: [docs/viewer-measurement.md](./docs/viewer-measurement.md).
247
+
248
+ ## Tuval — kutu düzenleme (demo, P3b-a)
249
+
250
+ Görüntüleyiciden farklı olarak **burada düzenleme var** — metin değil, kutu
251
+ geometrisi: seç, tutamakla sürükle/boyutlandır, Ctrl+Z ile geri al,
252
+ kopyala/yapıştır, dekoratif bir şeklin kenarına yaklaştırıp snap'i gör.
253
+ `src/canvas/`'ın dokuz saf modülü + `CanvasController` + `Overlay`, gerçek
254
+ bir tarayıcıya bağlı (`docs/adr/0018-canvas-interaction-layer.md`).
255
+
256
+ ```bash
257
+ npm run serve
258
+ ```
259
+
260
+ ```
261
+ http://127.0.0.1:8080/examples/static/canvas.html
262
+ ```
263
+
264
+ Dosya seçiciyle bir belge JSON'u yükle: `test/fixtures/canvas/fifty-boxes.json`
265
+ (50 kutu, 5 dekorasyon, 2 sayfa) hazır bir örnek; `npm run corpus:canvas`'ın
266
+ ürettiği `examples/static/corpus-json/*.json` de aynı formatta çalışır.
267
+
268
+ **Ölçülmemiş sabitler, sayfanın kendi geçici değerleri — kütüphaneye değil:**
269
+ tutamak boyutu, snap eşiği, kademe eşikleri hiçbiri ölçülmedi (Ö2/Ö4,
270
+ ADR-0018 §9.0). `canvas.html` kendi `EXAMPLE_METRICS` nesnesini enjekte
271
+ ediyor; `src/canvas/controller.js`'in `requireMetrics`'i eksik alanda hâlâ
272
+ gerçek `TypeError` atıyor, `src/` altına hiçbir varsayılan sızmadı.
273
+
274
+ Aynı `file://` kısıtı geçerli (yukarıdaki görüntüleyici bölümüne bakınız).
275
+ ~~Metin düzenleme bu turun kapsamında değil~~ — 2026-08-29'dan beri
276
+ (ADR-0024) çift tık kutunun içinde metin moduna geçiyor, ve sayfa artık
277
+ `.docx` da açıp kaydedebiliyor. Ürün yüzeyi ise `app.html` (yukarıda).
278
+
279
+ ## Mimari
280
+
281
+ Tasarım kararları için [ARCHITECTURE.md](./ARCHITECTURE.md),
282
+ uyumluluk sözleşmesi için [FIDELITY.md](./FIDELITY.md).
283
+
284
+ ## Lisans
285
+
286
+ MIT — bkz. [LICENSE](./LICENSE).