@sametalmdr/word-editor 0.0.0-stage → 0.2.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 (175) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/README.md +247 -3
  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 +199 -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 +3 -0
  57. package/dist/docx.js +13 -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/history/history.d.ts +162 -0
  63. package/dist/history/history.js +433 -0
  64. package/dist/index.d.ts +47 -0
  65. package/dist/index.js +63 -0
  66. package/dist/input/beforeinput.d.ts +41 -0
  67. package/dist/input/beforeinput.js +224 -0
  68. package/dist/input/clipboard.d.ts +65 -0
  69. package/dist/input/clipboard.js +107 -0
  70. package/dist/input/composition.d.ts +184 -0
  71. package/dist/input/composition.js +341 -0
  72. package/dist/input/keymap.d.ts +38 -0
  73. package/dist/input/keymap.js +54 -0
  74. package/dist/io/json/document-json.d.ts +34 -0
  75. package/dist/io/json/document-json.js +341 -0
  76. package/dist/io/ooxml/custgeom.d.ts +25 -0
  77. package/dist/io/ooxml/custgeom.js +72 -0
  78. package/dist/io/ooxml/drawingml.d.ts +127 -0
  79. package/dist/io/ooxml/drawingml.js +232 -0
  80. package/dist/io/ooxml/edits.d.ts +31 -0
  81. package/dist/io/ooxml/edits.js +67 -0
  82. package/dist/io/ooxml/export-splice.d.ts +43 -0
  83. package/dist/io/ooxml/export-splice.js +880 -0
  84. package/dist/io/ooxml/export.d.ts +226 -0
  85. package/dist/io/ooxml/export.js +365 -0
  86. package/dist/io/ooxml/fill.d.ts +232 -0
  87. package/dist/io/ooxml/fill.js +562 -0
  88. package/dist/io/ooxml/import-gate.d.ts +112 -0
  89. package/dist/io/ooxml/import-gate.js +200 -0
  90. package/dist/io/ooxml/import.d.ts +154 -0
  91. package/dist/io/ooxml/import.js +691 -0
  92. package/dist/io/ooxml/inflate.d.ts +20 -0
  93. package/dist/io/ooxml/inflate.js +126 -0
  94. package/dist/io/ooxml/media.d.ts +12 -0
  95. package/dist/io/ooxml/media.js +87 -0
  96. package/dist/io/ooxml/ns.d.ts +10 -0
  97. package/dist/io/ooxml/ns.js +21 -0
  98. package/dist/io/ooxml/numbering.d.ts +47 -0
  99. package/dist/io/ooxml/numbering.js +290 -0
  100. package/dist/io/ooxml/opc-write.d.ts +12 -0
  101. package/dist/io/ooxml/opc-write.js +171 -0
  102. package/dist/io/ooxml/provenance.d.ts +118 -0
  103. package/dist/io/ooxml/provenance.js +93 -0
  104. package/dist/io/ooxml/recipe.d.ts +173 -0
  105. package/dist/io/ooxml/recipe.js +708 -0
  106. package/dist/io/ooxml/relationships.d.ts +10 -0
  107. package/dist/io/ooxml/relationships.js +59 -0
  108. package/dist/io/ooxml/sections.d.ts +36 -0
  109. package/dist/io/ooxml/sections.js +107 -0
  110. package/dist/io/ooxml/slots.d.ts +133 -0
  111. package/dist/io/ooxml/slots.js +581 -0
  112. package/dist/io/ooxml/styles-resolve.d.ts +102 -0
  113. package/dist/io/ooxml/styles-resolve.js +257 -0
  114. package/dist/io/ooxml/units.d.ts +29 -0
  115. package/dist/io/ooxml/units.js +43 -0
  116. package/dist/io/ooxml/value.d.ts +43 -0
  117. package/dist/io/ooxml/value.js +120 -0
  118. package/dist/io/ooxml/xml-scan.d.ts +170 -0
  119. package/dist/io/ooxml/xml-scan.js +616 -0
  120. package/dist/io/ooxml/zip-container.d.ts +63 -0
  121. package/dist/io/ooxml/zip-container.js +228 -0
  122. package/dist/io/ooxml/zip-reject.d.ts +48 -0
  123. package/dist/io/ooxml/zip-reject.js +106 -0
  124. package/dist/measure/bleed.d.ts +25 -0
  125. package/dist/measure/bleed.js +86 -0
  126. package/dist/measure/controller.d.ts +174 -0
  127. package/dist/measure/controller.js +460 -0
  128. package/dist/measure/dirty.d.ts +119 -0
  129. package/dist/measure/dirty.js +146 -0
  130. package/dist/measure/extent.d.ts +85 -0
  131. package/dist/measure/extent.js +157 -0
  132. package/dist/measure/page-bounds.d.ts +50 -0
  133. package/dist/measure/page-bounds.js +62 -0
  134. package/dist/measure/placement.d.ts +39 -0
  135. package/dist/measure/placement.js +83 -0
  136. package/dist/measure/probe.d.ts +67 -0
  137. package/dist/measure/probe.js +84 -0
  138. package/dist/measure/report.d.ts +53 -0
  139. package/dist/measure/report.js +75 -0
  140. package/dist/model/ids.d.ts +39 -0
  141. package/dist/model/ids.js +70 -0
  142. package/dist/model/node.d.ts +933 -0
  143. package/dist/model/node.js +898 -0
  144. package/dist/model/normalize.d.ts +38 -0
  145. package/dist/model/normalize.js +146 -0
  146. package/dist/model/position.d.ts +209 -0
  147. package/dist/model/position.js +283 -0
  148. package/dist/model/runs.d.ts +171 -0
  149. package/dist/model/runs.js +383 -0
  150. package/dist/model/step-apply.d.ts +34 -0
  151. package/dist/model/step-apply.js +563 -0
  152. package/dist/model/step-invert.d.ts +32 -0
  153. package/dist/model/step-invert.js +397 -0
  154. package/dist/model/step-map.d.ts +49 -0
  155. package/dist/model/step-map.js +222 -0
  156. package/dist/model/step.d.ts +360 -0
  157. package/dist/model/step.js +258 -0
  158. package/dist/model/transaction.d.ts +195 -0
  159. package/dist/model/transaction.js +209 -0
  160. package/dist/platform/capabilities.d.ts +21 -0
  161. package/dist/platform/capabilities.js +25 -0
  162. package/dist/react/index.d.ts +6 -0
  163. package/dist/react/index.js +21 -0
  164. package/dist/render/dom.d.ts +226 -0
  165. package/dist/render/dom.js +676 -0
  166. package/dist/render/renderer.d.ts +354 -0
  167. package/dist/render/renderer.js +1003 -0
  168. package/dist/selection/controller.d.ts +209 -0
  169. package/dist/selection/controller.js +315 -0
  170. package/dist/selection/dom-map.d.ts +119 -0
  171. package/dist/selection/dom-map.js +334 -0
  172. package/dist/state/editor-state.d.ts +79 -0
  173. package/dist/state/editor-state.js +109 -0
  174. package/dist/styles.css +66 -0
  175. package/package.json +93 -6
package/CHANGELOG.md ADDED
@@ -0,0 +1,70 @@
1
+ # Değişiklik günlüğü
2
+
3
+ ## 0.2.0-alpha — 2026-10-26 (yayını sahip yapar)
4
+
5
+ ### Eklenenler
6
+
7
+ - **Çok satırlı değer** ([ADR-0039](./docs/adr/0039-multi-line-values.md)) —
8
+ `fillTemplate(bytes, data, {expectedTags?, multiline?})` ve
9
+ `validateTemplate(bytes, {expectedTags, multiline?})`. `multiline`'ın adlandırdığı etikette LF bir
10
+ satır sonudur: satırlar etiketin kendi run'ında, aralarında `w:br`; run'ın biçimi her satırda.
11
+ Okuma-geri `w:t` + `w:br` dizisini karşılaştırır. Şablonda olmayan ad `UNKNOWN_MULTILINE_TAG`.
12
+ Word'de (masaüstü 16) KR09'un iki uzun kutusunda ölçüldü: Calibri 8,5 pt'de 3 ve 2 satır sığıyor,
13
+ fazlası kırpılıyor.
14
+
15
+ ### Değişenler
16
+
17
+ - Satır sonu kuralı: tek satırlı etikette LF de, her etikette CR, CRLF, U+2028 ve U+2029 eskisi gibi
18
+ `VALUE_LINE_BREAK`. Motor değeri normalleştirmez.
19
+
20
+ ### Bilinen sınırlar
21
+
22
+ - `importDocx` `w:br`'yi okumuyor: çok satırlı bir değer editörde tek satıra birleşir; düzenlenip
23
+ export edilirse satır sonları kaybolur (FR4).
24
+ - Kutuya sığmayan değer Word'de sessizce kırpılır; sığma denetimi bugün çağıranın işi (FR4).
25
+
26
+ Paketin dışında (repo içi, `files`'a girmez): şablon tarifleri, bağlama manifesti ve alan kaydı v0
27
+ (`templates/`, ADR-0040/0041) — PeriKont'un KR09 şablonu bunlarla üretilir. Şablonun kendisi
28
+ `templates/kr09/KR09-template.docx`'te commit'li.
29
+
30
+ ## 0.1.0-alpha — 2026-10-17 (yayını sahip yapar)
31
+
32
+ İlk açık alfa. Editörün kendi yüzeyi (`.`, `./element`, `./react`) değişmedi ve 1.0
33
+ öncesi.
34
+
35
+ ### Eklenenler
36
+
37
+ - **`/docx` alt yolu** — DOM'suz; Node'da ve Deno'da (ör. Supabase Edge Functions) sınandı,
38
+ tarayıcı için de yazıldı (Chrome 152'de yalnız incelendi). Hiçbir ağ isteği atmaz.
39
+ - `fillTemplate(bytes, data, {expectedTags?})` — bir `.docx` şablonunun `{etiket}`lerini
40
+ doldurur. `{bytes, documentXml, report}` döndürür.
41
+ - `inspectTemplate(bytes)` — şablondaki etiketler (kutunun `wp:docPr` kimliği ve sayfasıyla),
42
+ doldurmayı durduracak sorunlar ve uyarılar. Şablon sorununda atmaz.
43
+ - `validateTemplate(bytes, {expectedTags})` — bağlama listesini tam küme olarak denetler.
44
+ - `TemplateError` — `problems` dizisi; her sorunun `code`'u sözleşmedir.
45
+ - `ENGINE_VERSION`.
46
+ - `importDocx`, `exportDocx` — editörün kendi IO'su; şekilleri alfa boyunca kararsız.
47
+
48
+ ### Doldurma kuralları (ADR-0037, ADR-0038)
49
+
50
+ - Etiket bir `w:t`'nin metninin tamamıdır: `{firma_adi}`, ad `[a-z][a-z0-9_]*`.
51
+ - Etiketler yalnız metin kutularında (`mc:AlternateContent`). Kutunun iki kopyası
52
+ (`mc:Choice` ve `mc:Fallback`) birlikte yazılır; önceden yapısal olarak eşit değillerse
53
+ reddedilir, sonra eşitlikleri yeniden denetlenir.
54
+ - Eksik anahtar doldurmayı durdurur; eksiklerin hepsi tek hatada raporlanır. `null`, `""` ve
55
+ yalnız boşluktan oluşan değer **boştur**: serbesttir, `report.empty`'de listelenir.
56
+ - Değer string olmalı. Satır sonu, sekme ve XML'in kabul etmediği karakterler reddedilir.
57
+ - Rapor, sorunlar ve hata mesajları hiçbir değeri taşımaz.
58
+ - Yalnız `word/document.xml` yeniden yazılır; başka her parçanın baytları aynen kopyalanır.
59
+
60
+ ### Bilinen sınırlar
61
+
62
+ - Yalnız ana belge doldurulur. Üst/alt bilgi, dipnot, son not, yorum, grafik ya da
63
+ `docProps` metnindeki bir parantez hata verir.
64
+ - Döngü (`{#…}`), koşul, görsel (`{%…}`), bölünmüş etiket, gövde paragrafındaki etiket yok.
65
+ - Çok satırlı değer yok (satır sonu reddedilir); politika FR3'te.
66
+ - Sıkıştırılmış `.docx` baytları çalışma ortamına göre değişir; ortamlar arasında
67
+ karşılaştırmayı `documentXml`'in özetiyle yapın.
68
+ - Gereken platform API'leri: `TextEncoder`/`TextDecoder` ve
69
+ `CompressionStream`/`DecompressionStream("deflate-raw")` — Node 20.12+ ya da 21.2+, Deno,
70
+ 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,247 @@
1
- # Temporary Holding Version
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.
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).
@@ -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
+ };