@sametalmdr/word-editor 0.0.0-stage → 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 (182) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/LICENSE +21 -0
  3. package/README.md +285 -2
  4. package/dist/canvas/box-clipboard.d.ts +125 -0
  5. package/dist/canvas/box-clipboard.js +238 -0
  6. package/dist/canvas/box-selection.d.ts +36 -0
  7. package/dist/canvas/box-selection.js +80 -0
  8. package/dist/canvas/canvas-editor.d.ts +251 -0
  9. package/dist/canvas/canvas-editor.js +502 -0
  10. package/dist/canvas/controller.d.ts +243 -0
  11. package/dist/canvas/controller.js +717 -0
  12. package/dist/canvas/geometry.d.ts +100 -0
  13. package/dist/canvas/geometry.js +131 -0
  14. package/dist/canvas/gesture.d.ts +198 -0
  15. package/dist/canvas/gesture.js +289 -0
  16. package/dist/canvas/handles.d.ts +119 -0
  17. package/dist/canvas/handles.js +212 -0
  18. package/dist/canvas/hit-test.d.ts +47 -0
  19. package/dist/canvas/hit-test.js +117 -0
  20. package/dist/canvas/menu.d.ts +62 -0
  21. package/dist/canvas/menu.js +82 -0
  22. package/dist/canvas/mode-controller.d.ts +41 -0
  23. package/dist/canvas/mode-controller.js +81 -0
  24. package/dist/canvas/nudge.d.ts +88 -0
  25. package/dist/canvas/nudge.js +108 -0
  26. package/dist/canvas/overlay.d.ts +86 -0
  27. package/dist/canvas/overlay.js +149 -0
  28. package/dist/canvas/page.d.ts +60 -0
  29. package/dist/canvas/page.js +83 -0
  30. package/dist/canvas/resize.d.ts +72 -0
  31. package/dist/canvas/resize.js +138 -0
  32. package/dist/canvas/snap.d.ts +62 -0
  33. package/dist/canvas/snap.js +132 -0
  34. package/dist/canvas/viewport.d.ts +99 -0
  35. package/dist/canvas/viewport.js +112 -0
  36. package/dist/canvas/z-order.d.ts +22 -0
  37. package/dist/canvas/z-order.js +171 -0
  38. package/dist/commands/alignment.d.ts +49 -0
  39. package/dist/commands/alignment.js +150 -0
  40. package/dist/commands/box.d.ts +111 -0
  41. package/dist/commands/box.js +272 -0
  42. package/dist/commands/clipboard.d.ts +30 -0
  43. package/dist/commands/clipboard.js +93 -0
  44. package/dist/commands/graphemes.d.ts +28 -0
  45. package/dist/commands/graphemes.js +112 -0
  46. package/dist/commands/marks.d.ts +43 -0
  47. package/dist/commands/marks.js +153 -0
  48. package/dist/commands/paragraph-props.d.ts +54 -0
  49. package/dist/commands/paragraph-props.js +176 -0
  50. package/dist/commands/size.d.ts +96 -0
  51. package/dist/commands/size.js +368 -0
  52. package/dist/commands/text.d.ts +95 -0
  53. package/dist/commands/text.js +291 -0
  54. package/dist/dev.d.ts +40 -0
  55. package/dist/dev.js +73 -0
  56. package/dist/docx.d.ts +4 -0
  57. package/dist/docx.js +15 -0
  58. package/dist/editor/editing-host.d.ts +255 -0
  59. package/dist/editor/editing-host.js +434 -0
  60. package/dist/element.d.ts +13 -0
  61. package/dist/element.js +41 -0
  62. package/dist/fonts/OFL-Carlito.txt +94 -0
  63. package/dist/fonts/calibri-metrics.d.ts +27 -0
  64. package/dist/fonts/calibri-metrics.js +88 -0
  65. package/dist/history/history.d.ts +162 -0
  66. package/dist/history/history.js +433 -0
  67. package/dist/index.d.ts +47 -0
  68. package/dist/index.js +63 -0
  69. package/dist/input/beforeinput.d.ts +41 -0
  70. package/dist/input/beforeinput.js +224 -0
  71. package/dist/input/clipboard.d.ts +65 -0
  72. package/dist/input/clipboard.js +107 -0
  73. package/dist/input/composition.d.ts +184 -0
  74. package/dist/input/composition.js +341 -0
  75. package/dist/input/keymap.d.ts +38 -0
  76. package/dist/input/keymap.js +54 -0
  77. package/dist/io/json/document-json.d.ts +34 -0
  78. package/dist/io/json/document-json.js +341 -0
  79. package/dist/io/ooxml/custgeom.d.ts +25 -0
  80. package/dist/io/ooxml/custgeom.js +72 -0
  81. package/dist/io/ooxml/drawingml.d.ts +127 -0
  82. package/dist/io/ooxml/drawingml.js +232 -0
  83. package/dist/io/ooxml/edits.d.ts +31 -0
  84. package/dist/io/ooxml/edits.js +67 -0
  85. package/dist/io/ooxml/export-splice.d.ts +43 -0
  86. package/dist/io/ooxml/export-splice.js +880 -0
  87. package/dist/io/ooxml/export.d.ts +226 -0
  88. package/dist/io/ooxml/export.js +365 -0
  89. package/dist/io/ooxml/fill.d.ts +317 -0
  90. package/dist/io/ooxml/fill.js +618 -0
  91. package/dist/io/ooxml/fit.d.ts +252 -0
  92. package/dist/io/ooxml/fit.js +598 -0
  93. package/dist/io/ooxml/import-gate.d.ts +112 -0
  94. package/dist/io/ooxml/import-gate.js +200 -0
  95. package/dist/io/ooxml/import.d.ts +154 -0
  96. package/dist/io/ooxml/import.js +691 -0
  97. package/dist/io/ooxml/inflate.d.ts +20 -0
  98. package/dist/io/ooxml/inflate.js +126 -0
  99. package/dist/io/ooxml/media.d.ts +12 -0
  100. package/dist/io/ooxml/media.js +87 -0
  101. package/dist/io/ooxml/ns.d.ts +12 -0
  102. package/dist/io/ooxml/ns.js +26 -0
  103. package/dist/io/ooxml/numbering.d.ts +47 -0
  104. package/dist/io/ooxml/numbering.js +290 -0
  105. package/dist/io/ooxml/opc-write.d.ts +12 -0
  106. package/dist/io/ooxml/opc-write.js +171 -0
  107. package/dist/io/ooxml/provenance.d.ts +118 -0
  108. package/dist/io/ooxml/provenance.js +93 -0
  109. package/dist/io/ooxml/recipe.d.ts +173 -0
  110. package/dist/io/ooxml/recipe.js +708 -0
  111. package/dist/io/ooxml/relationships.d.ts +10 -0
  112. package/dist/io/ooxml/relationships.js +59 -0
  113. package/dist/io/ooxml/run-fonts.d.ts +89 -0
  114. package/dist/io/ooxml/run-fonts.js +378 -0
  115. package/dist/io/ooxml/sections.d.ts +36 -0
  116. package/dist/io/ooxml/sections.js +107 -0
  117. package/dist/io/ooxml/slots.d.ts +137 -0
  118. package/dist/io/ooxml/slots.js +582 -0
  119. package/dist/io/ooxml/styles-resolve.d.ts +102 -0
  120. package/dist/io/ooxml/styles-resolve.js +257 -0
  121. package/dist/io/ooxml/units.d.ts +29 -0
  122. package/dist/io/ooxml/units.js +43 -0
  123. package/dist/io/ooxml/value.d.ts +43 -0
  124. package/dist/io/ooxml/value.js +120 -0
  125. package/dist/io/ooxml/xml-scan.d.ts +170 -0
  126. package/dist/io/ooxml/xml-scan.js +616 -0
  127. package/dist/io/ooxml/zip-container.d.ts +63 -0
  128. package/dist/io/ooxml/zip-container.js +228 -0
  129. package/dist/io/ooxml/zip-reject.d.ts +48 -0
  130. package/dist/io/ooxml/zip-reject.js +106 -0
  131. package/dist/measure/bleed.d.ts +25 -0
  132. package/dist/measure/bleed.js +86 -0
  133. package/dist/measure/controller.d.ts +174 -0
  134. package/dist/measure/controller.js +460 -0
  135. package/dist/measure/dirty.d.ts +119 -0
  136. package/dist/measure/dirty.js +146 -0
  137. package/dist/measure/extent.d.ts +85 -0
  138. package/dist/measure/extent.js +157 -0
  139. package/dist/measure/page-bounds.d.ts +50 -0
  140. package/dist/measure/page-bounds.js +62 -0
  141. package/dist/measure/placement.d.ts +39 -0
  142. package/dist/measure/placement.js +83 -0
  143. package/dist/measure/probe.d.ts +67 -0
  144. package/dist/measure/probe.js +84 -0
  145. package/dist/measure/report.d.ts +53 -0
  146. package/dist/measure/report.js +75 -0
  147. package/dist/model/ids.d.ts +39 -0
  148. package/dist/model/ids.js +70 -0
  149. package/dist/model/node.d.ts +933 -0
  150. package/dist/model/node.js +898 -0
  151. package/dist/model/normalize.d.ts +38 -0
  152. package/dist/model/normalize.js +146 -0
  153. package/dist/model/position.d.ts +209 -0
  154. package/dist/model/position.js +283 -0
  155. package/dist/model/runs.d.ts +171 -0
  156. package/dist/model/runs.js +383 -0
  157. package/dist/model/step-apply.d.ts +34 -0
  158. package/dist/model/step-apply.js +563 -0
  159. package/dist/model/step-invert.d.ts +32 -0
  160. package/dist/model/step-invert.js +397 -0
  161. package/dist/model/step-map.d.ts +49 -0
  162. package/dist/model/step-map.js +222 -0
  163. package/dist/model/step.d.ts +360 -0
  164. package/dist/model/step.js +258 -0
  165. package/dist/model/transaction.d.ts +195 -0
  166. package/dist/model/transaction.js +209 -0
  167. package/dist/platform/capabilities.d.ts +21 -0
  168. package/dist/platform/capabilities.js +25 -0
  169. package/dist/react/index.d.ts +6 -0
  170. package/dist/react/index.js +21 -0
  171. package/dist/render/dom.d.ts +226 -0
  172. package/dist/render/dom.js +676 -0
  173. package/dist/render/renderer.d.ts +354 -0
  174. package/dist/render/renderer.js +1003 -0
  175. package/dist/selection/controller.d.ts +209 -0
  176. package/dist/selection/controller.js +315 -0
  177. package/dist/selection/dom-map.d.ts +119 -0
  178. package/dist/selection/dom-map.js +334 -0
  179. package/dist/state/editor-state.d.ts +79 -0
  180. package/dist/state/editor-state.js +109 -0
  181. package/dist/styles.css +66 -0
  182. package/package.json +92 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,108 @@
1
+ # Değişiklik günlüğü
2
+
3
+ ## 0.3.0-alpha — 2026-10-26 (yayını sahip yapar)
4
+
5
+ ### Eklenenler
6
+
7
+ - **`checkFit(bytes, data, {expectedTags?, multiline?, capacity?})`** — `/docx`'ten
8
+ ([ADR-0042](./docs/adr/0042-dom-free-fit-check.md)).
9
+ - **Ne yapar:** doldurmadan önce her değerin satırını şablonun kendi kutusuyla karşılaştırır. Tarayıcı
10
+ gerekmez.
11
+ - **Durumlar:** `fits`, `overflows`, `unknown` ya da `empty`; en dar satırın genişliği, yeri ve payı (pt).
12
+ `unknown`'ın nedeni kapalı bir listeden gelir.
13
+ - **Doğrulama:** `fillTemplate`'in reddettiğini aynı `TemplateError` ile reddeder; hiçbir şey yazmaz.
14
+ Rapor değer taşımaz.
15
+ - **Çok satırlı değer** `capacity` ister. Bağlama manifesti onu `fit` bloğunda taşır:
16
+ `checkFit(b, d, {...m.options, ...m.fit})`.
17
+ - **Word'e karşı:** masaüstü 16'da ön kayıtlı kapıyla sınandı (FR4-WORD-FIT2). 210 satırın hepsi
18
+ `checkFit`'in dediği gibi dizildi: KR09'un bütün kutuları bandın iki yanında, ayrıca 14 kalibrasyon dalı.
19
+ - **Calibri genişlik tablosu** — `dist/fonts/calibri-metrics.js`.
20
+ - Carlito 1.104'ten (SIL OFL-1.1, ayrılmış ad "Carlito"); lisans metni yanında
21
+ (`dist/fonts/OFL-Carlito.txt`).
22
+ - Yalnız Calibri 6.23 ile eşit olduğu 344 kod noktası, dört kesim. Tabloda olmayan karakter `unknown`
23
+ (örneğin ₺).
24
+
25
+ ### Değişenler
26
+
27
+ - Bağlama manifesti (repo içi, `templates/`) `fit: {capacity}` kazandı. `fillTemplate` ve `validateTemplate`
28
+ `capacity`'yi bilinmeyen seçenek saymayı sürdürür.
29
+
30
+ ### Bilinen sınırlar
31
+
32
+ - **Yalnız Calibri** (tablo) ve yalnız sınanan kutu ve paragraf biçimleri ölçülür; geri kalanı `unknown`.
33
+ Başka Calibri yapıları (Mac, Word web) ve `compatibilityMode` 15 doğrulanmadı (ADR-0042 DOĞRULANMALI).
34
+ - **Çok satırlı değerde** taşan bir satırın kaç satır alacağını tarayıcı söyler (FR4b). Kapasitenin
35
+ sığabileceği durumda sonuç `unknown(WRAPS)`.
36
+ - **Word'de sınanmayan dallar `unknown`:** sondaki boşlukların belirlediği satır (`TRAILING_SPACE`, U+0020
37
+ dahil), iç boşluklu kutuda ya da sola dayalı olmayan satırda sekme (`TAB_INSET`, `TAB_ALIGNMENT`), şablonda
38
+ olmayan paragraf stili (`STYLE_MISSING`), `xml:space` korunmayan kenar boşluğu (`WHITESPACE_UNPRESERVED`).
39
+ KR09'un örneklerinde hiçbiri yok (0 `unknown`).
40
+
41
+ ## 0.2.0-alpha — 2026-10-26 (yayınlandı)
42
+
43
+ ### Eklenenler
44
+
45
+ - **Çok satırlı değer** ([ADR-0039](./docs/adr/0039-multi-line-values.md)) —
46
+ `fillTemplate(bytes, data, {expectedTags?, multiline?})` ve
47
+ `validateTemplate(bytes, {expectedTags, multiline?})`. `multiline`'ın adlandırdığı etikette LF bir
48
+ satır sonudur: satırlar etiketin kendi run'ında, aralarında `w:br`; run'ın biçimi her satırda.
49
+ Okuma-geri `w:t` + `w:br` dizisini karşılaştırır. Şablonda olmayan ad `UNKNOWN_MULTILINE_TAG`.
50
+ Word'de (masaüstü 16) KR09'un iki uzun kutusunda ölçüldü: Calibri 8,5 pt'de 3 ve 2 satır sığıyor,
51
+ fazlası kırpılıyor.
52
+
53
+ ### Değişenler
54
+
55
+ - Satır sonu kuralı: tek satırlı etikette LF de, her etikette CR, CRLF, U+2028 ve U+2029 eskisi gibi
56
+ `VALUE_LINE_BREAK`. Motor değeri normalleştirmez.
57
+
58
+ ### Bilinen sınırlar
59
+
60
+ - `importDocx` `w:br`'yi okumuyor: çok satırlı bir değer editörde tek satıra birleşir; düzenlenip
61
+ export edilirse satır sonları kaybolur (FR4).
62
+ - Kutuya sığmayan değer Word'de sessizce kırpılır; sığma denetimi bugün çağıranın işi (FR4).
63
+
64
+ Paketin dışında (repo içi, `files`'a girmez): şablon tarifleri, bağlama manifesti ve alan kaydı v0
65
+ (`templates/`, ADR-0040/0041) — PeriKont'un KR09 şablonu bunlarla üretilir. Şablonun kendisi
66
+ `templates/kr09/KR09-template.docx`'te commit'li.
67
+
68
+ ## 0.1.0-alpha — 2026-10-17 (yayını sahip yapar)
69
+
70
+ İlk açık alfa. Editörün kendi yüzeyi (`.`, `./element`, `./react`) değişmedi ve 1.0
71
+ öncesi.
72
+
73
+ ### Eklenenler
74
+
75
+ - **`/docx` alt yolu** — DOM'suz; Node'da ve Deno'da (ör. Supabase Edge Functions) sınandı,
76
+ tarayıcı için de yazıldı (Chrome 152'de yalnız incelendi). Hiçbir ağ isteği atmaz.
77
+ - `fillTemplate(bytes, data, {expectedTags?})` — bir `.docx` şablonunun `{etiket}`lerini
78
+ doldurur. `{bytes, documentXml, report}` döndürür.
79
+ - `inspectTemplate(bytes)` — şablondaki etiketler (kutunun `wp:docPr` kimliği ve sayfasıyla),
80
+ doldurmayı durduracak sorunlar ve uyarılar. Şablon sorununda atmaz.
81
+ - `validateTemplate(bytes, {expectedTags})` — bağlama listesini tam küme olarak denetler.
82
+ - `TemplateError` — `problems` dizisi; her sorunun `code`'u sözleşmedir.
83
+ - `ENGINE_VERSION`.
84
+ - `importDocx`, `exportDocx` — editörün kendi IO'su; şekilleri alfa boyunca kararsız.
85
+
86
+ ### Doldurma kuralları (ADR-0037, ADR-0038)
87
+
88
+ - Etiket bir `w:t`'nin metninin tamamıdır: `{firma_adi}`, ad `[a-z][a-z0-9_]*`.
89
+ - Etiketler yalnız metin kutularında (`mc:AlternateContent`). Kutunun iki kopyası
90
+ (`mc:Choice` ve `mc:Fallback`) birlikte yazılır; önceden yapısal olarak eşit değillerse
91
+ reddedilir, sonra eşitlikleri yeniden denetlenir.
92
+ - Eksik anahtar doldurmayı durdurur; eksiklerin hepsi tek hatada raporlanır. `null`, `""` ve
93
+ yalnız boşluktan oluşan değer **boştur**: serbesttir, `report.empty`'de listelenir.
94
+ - Değer string olmalı. Satır sonu, sekme ve XML'in kabul etmediği karakterler reddedilir.
95
+ - Rapor, sorunlar ve hata mesajları hiçbir değeri taşımaz.
96
+ - Yalnız `word/document.xml` yeniden yazılır; başka her parçanın baytları aynen kopyalanır.
97
+
98
+ ### Bilinen sınırlar
99
+
100
+ - Yalnız ana belge doldurulur. Üst/alt bilgi, dipnot, son not, yorum, grafik ya da
101
+ `docProps` metnindeki bir parantez hata verir.
102
+ - Döngü (`{#…}`), koşul, görsel (`{%…}`), bölünmüş etiket, gövde paragrafındaki etiket yok.
103
+ - Çok satırlı değer yok (satır sonu reddedilir); politika FR3'te.
104
+ - Sıkıştırılmış `.docx` baytları çalışma ortamına göre değişir; ortamlar arasında
105
+ karşılaştırmayı `documentXml`'in özetiyle yapın.
106
+ - Gereken platform API'leri: `TextEncoder`/`TextDecoder` ve
107
+ `CompressionStream`/`DecompressionStream("deflate-raw")` — Node 20.12+ ya da 21.2+, Deno,
108
+ güncel tarayıcılar. Sınanan: Node 24.13.1, Deno 2.1.4.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samet Alemdaroğlu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,286 @@
1
- # Temporary Holding Version
1
+ # @sametalmdr/word-editor
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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).
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The clipboard payload for a set of boxes — `null` if the selection is
3
+ * empty or any id no longer names a real box (a stale selection copying
4
+ * nothing meaningful is a no-op, not a partial copy).
5
+ *
6
+ * @param {import("../model/node.js").Doc} doc
7
+ * @param {import("../model/node.js").NodeId[]} containerIds
8
+ * @returns {BoxPayload | null}
9
+ */
10
+ export function serializeBoxes(doc: import("../model/node.js").Doc, containerIds: import("../model/node.js").NodeId[]): BoxPayload | null;
11
+ /**
12
+ * The plain-text mirror written to the system clipboard's `text/plain`
13
+ * slot alongside the real payload (§7.1) — every paragraph of every box,
14
+ * in payload order, joined the same way `serializeToText` joins a text
15
+ * selection's paragraphs.
16
+ * @param {BoxPayload} payload
17
+ * @returns {string}
18
+ */
19
+ export function payloadToText(payload: BoxPayload): string;
20
+ /**
21
+ * @typedef {object} MintedBoxIds
22
+ * @property {import("../model/node.js").NodeId} containerId
23
+ * @property {import("../model/node.js").NodeId[]} paragraphIds same length and order as `box.paragraphs`
24
+ */
25
+ /**
26
+ * Fresh ids for one payload box, minted from `ids` — NEVER the ids the
27
+ * payload itself carries (those are the ORIGINAL box's, and reusing them
28
+ * would collide the moment the original still exists, CANV12's own
29
+ * target). Minted before any step exists (ADR-0003: `apply()` never mints
30
+ * an id) — the same ordering `commands/clipboard.js`'s `pasteText` already
31
+ * uses for a pasted paragraph's id, generalized to a whole box.
32
+ *
33
+ * @param {BoxPayloadEntry} box
34
+ * @param {import("../model/ids.js").IdFactory} ids
35
+ * @returns {MintedBoxIds}
36
+ */
37
+ export function mintBoxIds(box: BoxPayloadEntry, ids: import("../model/ids.js").IdFactory): MintedBoxIds;
38
+ /**
39
+ * @typedef {object} PastePage
40
+ * @property {number} page 1-based, the ACTIVE page paste targets — never the page the boxes were copied from (§7.2)
41
+ * @property {number} width
42
+ * @property {number} height
43
+ */
44
+ /**
45
+ * @typedef {object} PastedBox
46
+ * @property {import("../model/node.js").Container} container fresh ids throughout
47
+ * @property {import("../model/node.js").BoxLayout} layout
48
+ */
49
+ /**
50
+ * Rebuilds `payload` as fresh containers + layouts, ready for the (future)
51
+ * caller to turn into `insertContainerStep`s — one transaction, so the
52
+ * whole paste is one undo entry.
53
+ *
54
+ * Relative geometry is preserved EXACTLY (§7.4): the offset from
55
+ * `payload.origin` to its post-paste position is computed ONCE, from the
56
+ * group's own bounding rect, and applied IDENTICALLY to every box's own
57
+ * absolute `{x,y}` — never computed per box. A per-box offset (e.g. each
58
+ * box separately clamped against the page) would let two boxes drift
59
+ * apart or together depending on which one needed clamping (CANV9's own
60
+ * target): two boxes 40pt apart must land 40pt apart, whether or not the
61
+ * paste needed to shift for the page edge.
62
+ *
63
+ * Falling outside the target page CLIPS the whole group's bounding rect
64
+ * into it (§7.2) — smart placement is not attempted; ARCHITECTURE.md:357
65
+ * already calls "no overlap" an unrealistic goal, and clipping the whole
66
+ * group keeps that same "don't pretend to solve overlap" stance instead
67
+ * of inventing a different unsolved problem (per-box collision avoidance).
68
+ *
69
+ * @param {BoxPayload} payload
70
+ * @param {import("../model/ids.js").IdFactory} ids
71
+ * @param {PastePage} targetPage
72
+ * @param {number} pasteOffsetPt pt — injected, no default (an unmeasured UI constant, CanvasMetrics' `pasteOffsetPt`)
73
+ * @returns {PastedBox[]}
74
+ */
75
+ export function pasteBoxes(payload: BoxPayload, ids: import("../model/ids.js").IdFactory, targetPage: PastePage, pasteOffsetPt: number): PastedBox[];
76
+ /** Bumped when the shape changes incompatibly — mirrors document-json.js's own field. */
77
+ export const BOX_PAYLOAD_VERSION: 1;
78
+ export type BoxPayloadParagraph = {
79
+ id: import("../model/node.js").NodeId;
80
+ pPrRef?: string | undefined;
81
+ runs: {
82
+ text: string;
83
+ marks: import("../model/node.js").Marks;
84
+ }[];
85
+ };
86
+ export type BoxPayloadEntry = {
87
+ /**
88
+ * a fresh, field-by-field copy — never a live reference
89
+ */
90
+ layout: Partial<import("../model/node.js").BoxLayout>;
91
+ paragraphs: BoxPayloadParagraph[];
92
+ };
93
+ export type BoxPayload = {
94
+ version: number;
95
+ /**
96
+ * the copied selection's own bounding-rect top-left (ADR-0018 §7.3's "grubun sol-üstü")
97
+ */
98
+ origin: {
99
+ x: number;
100
+ y: number;
101
+ };
102
+ boxes: BoxPayloadEntry[];
103
+ };
104
+ export type MintedBoxIds = {
105
+ containerId: import("../model/node.js").NodeId;
106
+ /**
107
+ * same length and order as `box.paragraphs`
108
+ */
109
+ paragraphIds: import("../model/node.js").NodeId[];
110
+ };
111
+ export type PastePage = {
112
+ /**
113
+ * 1-based, the ACTIVE page paste targets — never the page the boxes were copied from (§7.2)
114
+ */
115
+ page: number;
116
+ width: number;
117
+ height: number;
118
+ };
119
+ export type PastedBox = {
120
+ /**
121
+ * fresh ids throughout
122
+ */
123
+ container: import("../model/node.js").Container;
124
+ layout: import("../model/node.js").BoxLayout;
125
+ };