mesdan 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/YAPMALI.md ADDED
@@ -0,0 +1,18 @@
1
+ ### Suggestions for Further Improvements
2
+
3
+ This library is already forming a robust baseline, but there are some excellent features you could add to make it an essential tool for Ottoman Turkish typing and education:
4
+
5
+ **1. On-Screen Virtual Keyboard (Visual Map)**
6
+ Since the mapping is custom (e.g., `A` -> `ع` with shift, `a` -> `ا`), a visual toggleable on-screen keyboard component mapping out these keys would be incredibly useful. Teaching users the layout is usually the hardest part of custom input methods. You could even create an interactive UI where pressing `Shift` or `Alt` on the physical keyboard updates the characters shown on the virtual map in real time.
7
+
8
+ **2. Ottoman Auto-Complete & Spell Check (Luğat/Dictionary Integration)**
9
+ Unlike modern Turkish, Ottoman Turkish is heavily historical and non-phonetic in its spelling (e.g., writing "olmak" as `اولمق`, or "gök" as `كوك`). Users often misspell words by trying to write them exactly as they sound today. Building an autocomplete dictionary overlay inside the Lexical plugin (suggesting the correct historical spelling from a Kamus-ı Türki dataset when a user types a Latin word) would be a killer feature.
10
+
11
+ **3. Batch Transliteration Tool**
12
+ Currently, users type in real-time. Adding a utility function (or a button in the UI) to "Paste Latin Text -> Output Ottoman Text" in bulk would be great. You already have `tebdilMetniOsmaniye`, but wrapping it in an easy-to-use batch conversion UI feature would attract users who have large texts to transcribe.
13
+
14
+ **4. Visible Zero-Width Non-Joiner (ZWNJ / Kesek) Toggling**
15
+ You use `\u200C` (mapped to `w`) to prevent letters from joining. In a rich text editor like Lexical, `\u200C` is invisible, which can confuse users when they try to delete characters. You could write a Lexical Node modifier that visually highlights ZWNJ characters (perhaps as a tiny vertical dashed line) when the editor is in focus, making it easier for teachers to explain word separation.
16
+
17
+ **5. Ligature Control (Allah, Lam-Elif, etc.)**
18
+ Some Ottoman texts require strict control over whether `Lam` and `Elif` combine into `لا`, or if the word `Allah` automatically forms the beautiful `اللّٰه` ligature. Adding a toggle configuration for "Historical Ligatures" vs "Plain Text" would be a very advanced, professional-grade typographical feature.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mesdan",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Latinî mesdan ile Osmanî metin yazmak için hazırlanmış bir alettir.",
5
5
  "keywords": [
6
6
  "osmanlıca",
@@ -31,6 +31,7 @@
31
31
  "@types/react": "^18.2.64 || ^19.0.8",
32
32
  "@types/react-dom": "^18.2.19 || ^19.0.3",
33
33
  "@vitejs/plugin-react": "^5.1.0",
34
+ "baseline-browser-mapping": "^2.10.9",
34
35
  "jest": "^29.7.0",
35
36
  "lexical": "^0.38.2",
36
37
  "prettier": "^3.6.2",
@@ -54,5 +55,8 @@
54
55
  "@types/react-dom": {
55
56
  "optional": true
56
57
  }
58
+ },
59
+ "dependencies": {
60
+ "wrangler": "^4.76.0"
57
61
  }
58
62
  }
@@ -1,9 +1,8 @@
1
1
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
2
- import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND } from 'lexical';
2
+ import { $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND } from 'lexical';
3
3
  import { useEffect } from 'react';
4
4
  import { harfLatinidenOsmaniye } from '../nuve/lexical/harf-latiniden-osmaniye';
5
5
  import { silOsmaniHarfiBaşra, silOsmaniHarfiSonra } from '../nuve/lexical/sil-harf-metingerde';
6
- import { LATIN_HURUFATI_VE_RAKAMATI_REGEX } from '../nuve/sabit';
7
6
  export const OsmaniMesdanEki = () => {
8
7
  const [metinger] = useLexicalComposerContext();
9
8
  useEffect(() => {
@@ -54,7 +53,8 @@ export const OsmaniMesdanEki = () => {
54
53
  if (ca === 'ArrowUp' || ca === 'ArrowDown') {
55
54
  return false;
56
55
  }
57
- if (LATIN_HURUFATI_VE_RAKAMATI_REGEX.test(ca)) {
56
+ // Bütün basılabilir karakterleri (harfler, rakamlar, noktalama işaretleri) yakala
57
+ if (ca.length === 1) {
58
58
  vaqa.preventDefault();
59
59
  vaqa.stopPropagation();
60
60
  metinger.update(() => {
@@ -72,6 +72,19 @@ export const OsmaniMesdanEki = () => {
72
72
  }
73
73
  else {
74
74
  const osmanliHarf = harfLatinidenOsmaniye(ca, marHarf, shiftKey, altKey);
75
+ // Eğer yazdırılacak harf sadece harekelerden/işaretlerden ibaretse
76
+ const isaretRegex = /^[\u064B-\u0656\u0670]+$/;
77
+ if ($isTextNode(node) && isaretRegex.test(osmanliHarf)) {
78
+ // Öncesinde de hareke/işaret var mı baq
79
+ const match = marMetin.match(/[\u064B-\u0656\u0670]+$/);
80
+ if (match) {
81
+ const matchLength = match[0].length;
82
+ // Önceki harekeleri seç ve sil
83
+ selection.anchor.set(node.getKey(), offset - matchLength, 'text');
84
+ selection.focus.set(node.getKey(), offset, 'text');
85
+ selection.removeText();
86
+ }
87
+ }
75
88
  selection.insertText(osmanliHarf);
76
89
  }
77
90
  });
@@ -79,18 +92,8 @@ export const OsmaniMesdanEki = () => {
79
92
  }
80
93
  return false;
81
94
  }, COMMAND_PRIORITY_HIGH);
82
- // --- Node transform: only handle pasted text ---
83
- // const removeNodeTransform = editor.registerNodeTransform(TextNode, node => {
84
- // const text = node.getTextContent();
85
- // if (!LATIN_HURUFATI_VE_RAKAMATI_REGEX.test(text)) return;
86
- // const transformed = tebdilMetniOsmaniye(text, { shiftKey: false, altKey: false });
87
- // if (transformed !== text) {
88
- // node.setTextContent(transformed);
89
- // }
90
- // });
91
95
  return () => {
92
96
  removeKeyDownListener();
93
- // removeNodeTransform();
94
97
  };
95
98
  }, [metinger]);
96
99
  return null;
@@ -59,7 +59,8 @@ export const useMesdan = ({ ibtidaiMetin = '', tekSatırMı, latiniMi: ibtidaiLa
59
59
  tadiliMetinEmri = TadiliMetinEmri.ALEM_ÜSTE;
60
60
  else if (hadise.key === 'ArrowDown')
61
61
  tadiliMetinEmri = TadiliMetinEmri.ALEM_ALTA;
62
- else if (hadise.ctrlKey) {
62
+ else if (hadise.ctrlKey && !hadise.altKey) {
63
+ // !hadise.altKey guarantees this doesn't block AltGr keys (which produce @, #, etc.)
63
64
  if (hadise.key.toLowerCase() === 'c')
64
65
  tadiliMetinEmri = TadiliMetinEmri.BELLE;
65
66
  else if (hadise.key.toLowerCase() === 'x')
@@ -118,12 +119,12 @@ export const useMesdan = ({ ibtidaiMetin = '', tekSatırMı, latiniMi: ibtidaiLa
118
119
  else if (hadise.inputType === 'insertFromPaste') {
119
120
  tadiliMetinEmri = TadiliMetinEmri.YERLE;
120
121
  }
121
- const mevzuMetin = metinSahası.value;
122
+ const mevzuMetin = metin;
122
123
  const alemMevkisi = hazırlaAlemMevkisiniMenzilli(alemMevkisiEvveli.current.baş, alemMevkisiEvveli.current.son);
123
124
  const netice = await Talimat_MetniTadilEt(mevzuMetin, alemMevkisi, tadiliMetinEmri, mesHadisesi, false);
124
125
  tayinAlemMevkisi(netice.alemMevkisiTadilSonrası);
125
126
  tayinMetin(netice.metinTadilSonrası);
126
- }, [tekSatırMı, tayinMetin]);
127
+ }, [metin, tekSatırMı, tayinMetin]);
127
128
  const handleSelect = useCallback(() => {
128
129
  const metinSahası = metinSahasıİması.current;
129
130
  if (!metinSahası)
@@ -1,30 +1,34 @@
1
1
  import { gayriİşaretlerdenMi, harekeMi, kesekMi, tenvinMi, şeddeMi } from '../../aletler/harf-tedkiki-kume';
2
2
  import { harfLatinidenOsmaniye } from '../lexical/harf-latiniden-osmaniye';
3
3
  export function ekleOsmaniMetneHarf(mevcutMetin, alemMevkisi, mesHadisesi) {
4
- // console.log("ekleOsmaniMetinyeHarf: >" + mesHadisesi.latiniHarf + "<");
5
4
  let metin = mevcutMetin;
6
5
  const niyetHarekeMi = mesHadisesi.capsLockBasılıMı;
7
6
  if (metin.length < 1) {
8
7
  metin = harfLatinidenOsmaniye(mesHadisesi.latiniHarf, '', mesHadisesi.shiftBasılıMı, mesHadisesi.altBasılıMı).replace('\u200C', '');
9
8
  return { metinOsmani: metin, alemBaşMevkisi: metin.length };
10
9
  }
11
- const alemBaşrası = metin.slice(0, alemMevkisi.başMevki);
10
+ let alemBaşrası = metin.slice(0, alemMevkisi.başMevki);
12
11
  const alemSonrası = metin.slice(alemMevkisi.sonMevki);
13
- const alemBaşrasındakiHarf = alemBaşrası.slice(-1);
14
- const alemBaşrasındakiİkiHarf = alemBaşrası.slice(-2);
15
- // const alemSonrasındekiHarf = alemSonrası.slice(0, 1);
16
- // console.log("alemBaşrasındakiHarf: > " + alemBaşrasındakiHarf + " <");
17
- // console.log("alemSonrasındekiHarf: > " + alemSonrasındekiHarf + " <");
12
+ let alemBaşrasındakiHarf = alemBaşrası.slice(-1);
18
13
  const ilave = harfLatinidenOsmaniye(mesHadisesi.latiniHarf, alemBaşrasındakiHarf, mesHadisesi.shiftBasılıMı, mesHadisesi.altBasılıMı);
14
+ // --- Harekeleri/İşaretleri Değiştirme (Replacement) Mantığı ---
15
+ const isaretRegex = /^[\u064B-\u0656\u0670]+$/;
16
+ if (isaretRegex.test(ilave)) {
17
+ const match = alemBaşrası.match(/[\u064B-\u0656\u0670]+$/);
18
+ if (match) {
19
+ // Eğer öncesinde hareke varsa, silip yenisini koymak için alemBaşrası'nı kırpıyoruz.
20
+ alemBaşrası = alemBaşrası.slice(0, -match[0].length);
21
+ alemBaşrasındakiHarf = alemBaşrası.slice(-1);
22
+ }
23
+ }
24
+ // --------------------------------------------------------------
25
+ const alemBaşrasındakiİkiHarf = alemBaşrası.slice(-2);
19
26
  metin = alemBaşrası + ilave + alemSonrası;
20
27
  let alemHareketMiktarı = ilave.length;
21
28
  if (niyetHarekeMi) {
22
29
  const alemBaşrasıHarekeMi = harekeMi(alemBaşrasındakiHarf);
23
- // const ilaveHarekeMi = harekeMi(ilave);
24
30
  const alemBaşrasıTenvinMi = tenvinMi(alemBaşrasındakiHarf);
25
- // const ilaveTenvinMi = tenvinMi(ilave);
26
31
  const alemBaşrasıGayriİşaretlerdenMi = gayriİşaretlerdenMi(alemBaşrasındakiHarf);
27
- // const ilaveGayriİşaretlerdenMi = gayriİşaretlerdenMi(ilave);
28
32
  const alemBaşrasıŞeddeMi = şeddeMi(alemBaşrasındakiİkiHarf);
29
33
  const ilaveŞeddeMi = şeddeMi(ilave);
30
34
  const alemBaşrasıKesmeMi = kesekMi(alemBaşrasındakiHarf);
@@ -40,7 +44,7 @@ export function ekleOsmaniMetneHarf(mevcutMetin, alemMevkisi, mesHadisesi) {
40
44
  const kesekEvveliŞeddeMi = şeddeMi(kesekEvvelindekiİkiHarf);
41
45
  if (alemBaşrasıKesmeMi) {
42
46
  if (ilaveKesmeMi) {
43
- metin = mevcutMetin;
47
+ metin = alemBaşrası + alemSonrası; // Mevcut metne dön (ilaveyi yoksay)
44
48
  }
45
49
  else {
46
50
  if (kesekEvveliŞeddeMi) {
@@ -99,11 +103,9 @@ export function ekleOsmaniMetneHarf(mevcutMetin, alemMevkisi, mesHadisesi) {
99
103
  }
100
104
  }
101
105
  else if (alemBaşrası.endsWith('ا') && ilave === 'ا') {
102
- metin = metin.replace('اا', 'آ');
103
- alemHareketMiktarı = ilave.length - 1;
106
+ metin = alemBaşrası.slice(0, -1) + 'آ' + alemSonrası;
107
+ alemHareketMiktarı = 0;
104
108
  }
105
- // console.log("ilave.length: " + ilave.length);
106
- // console.log("alemHareketMiktarı: " + alemHareketMiktarı);
107
109
  return {
108
110
  metinOsmani: metin,
109
111
  alemBaşMevkisi: alemBaşrası.length + alemHareketMiktarı,
@@ -31,6 +31,8 @@ export function harfLatinidenOsmaniyeEski(latiniHarf, evvelkiHarf, shiftBasılı
31
31
  return '`';
32
32
  case '``':
33
33
  return '``';
34
+ case '@':
35
+ return '@';
34
36
  case '=':
35
37
  return '=';
36
38
  case '-':
@@ -192,6 +194,9 @@ export function harfLatinidenOsmaniyeEski(latiniHarf, evvelkiHarf, shiftBasılı
192
194
  case 'K':
193
195
  return 'ك';
194
196
  case 'q':
197
+ if (altBasılıMı) {
198
+ return '\u25CC'; // Dotted Circle
199
+ }
195
200
  return 'ق';
196
201
  case 'Q':
197
202
  if (harfKelimeBaşındaMı(evvelkiHarf)) {
@@ -60,7 +60,7 @@ export const harfTebdilleri = {
60
60
  J: { asıl: 'ژ' },
61
61
  k: { asıl: 'ك' },
62
62
  K: { asıl: 'ك' },
63
- q: { asıl: 'ق' },
63
+ q: { asıl: 'ق', alt: '\u25CC' /* Dotted Circle */ },
64
64
  Q: {
65
65
  asıl: { baş: 'ق', bel: '\u064E' /* Fetha */ },
66
66
  },
@@ -156,6 +156,7 @@ export const sabitTebdiller = new Map([
156
156
  ['_', '_'],
157
157
  ['<', '<'],
158
158
  ['>', '>'],
159
+ ['@', '@'],
159
160
  ['0', '٠'],
160
161
  ['1', '١'],
161
162
  ['2', '٢'],