@writegeez/ime 0.0.2 → 0.1.0

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/dist/index.d.cts CHANGED
@@ -10,8 +10,7 @@ type InputMethodSpec = {
10
10
  };
11
11
 
12
12
  declare class WebGeezIME {
13
- private inputMap;
14
- private geezToLatinMap;
13
+ private transliterator;
15
14
  private activeElement;
16
15
  private elementType;
17
16
  private isContentEditable;
@@ -32,8 +31,20 @@ declare class WebGeezIME {
32
31
  output: string;
33
32
  modifiesPrev: boolean;
34
33
  };
34
+ /** The word before the caret, e.g. `"ሰላ"` while typing `ሰላም`. */
35
+ currentWord(): string;
36
+ /**
37
+ * Write `text` at the caret, as if typed. With `replaceWord`, the word
38
+ * before the caret is replaced (accepting a suggestion). Returns false
39
+ * while an OS IME composition is in progress, so it cannot be corrupted.
40
+ */
41
+ insertText(text: string, options?: {
42
+ replaceWord?: boolean;
43
+ }): boolean;
44
+ private textBeforeCaret;
35
45
  private handleBeforeInput;
36
46
  private insertGeezChar;
47
+ /** Replace `length` UTF-16 units before the caret; defaults to the previous grapheme. */
37
48
  private replaceGeezChar;
38
49
  private dispatchSyntheticInput;
39
50
  private getPreviousCharByTargetRanges;
@@ -50,6 +61,31 @@ declare namespace core {
50
61
 
51
62
  declare const inputMethod: InputMethodSpec;
52
63
 
64
+ /** Every input method bundled with the package, in menu order. */
65
+ declare const inputMethods: InputMethodSpec[];
66
+ /** Look one up by its `code`, e.g. `"ti-ER"`. */
67
+ declare function getInputMethod(code: string): InputMethodSpec | undefined;
68
+
69
+ /**
70
+ * DOM-free transliteration engine. Feed it the character before the caret
71
+ * and the key just typed; it says what to write and whether that replaces
72
+ * the previous character.
73
+ */
74
+ declare class Transliterator {
75
+ private inputMap;
76
+ private geezToLatinMap;
77
+ constructor(spec: InputMethodSpec);
78
+ feed(prevChar: string, key: string): {
79
+ replace: boolean;
80
+ text: string;
81
+ };
82
+ /** Run a whole key sequence, e.g. `"selam"` → `"ሰላም"`. */
83
+ transliterate(keys: string): string;
84
+ }
85
+
86
+ /** The word at the end of `text`: everything after the last space or punctuation mark. */
87
+ declare function trailingWord(text: string): string;
88
+
53
89
  declare const ime: typeof core;
54
90
 
55
- export { WebGeezIME as GeezIME, type InputMethod, type InputMethodSpec, type SymbolMap, inputMethod as TigrinyaEritrean, ime };
91
+ export { WebGeezIME as GeezIME, type InputMethod, type InputMethodSpec, type SymbolMap, inputMethod as TigrinyaEritrean, Transliterator, getInputMethod, ime, inputMethods, trailingWord };
package/dist/index.d.ts CHANGED
@@ -10,8 +10,7 @@ type InputMethodSpec = {
10
10
  };
11
11
 
12
12
  declare class WebGeezIME {
13
- private inputMap;
14
- private geezToLatinMap;
13
+ private transliterator;
15
14
  private activeElement;
16
15
  private elementType;
17
16
  private isContentEditable;
@@ -32,8 +31,20 @@ declare class WebGeezIME {
32
31
  output: string;
33
32
  modifiesPrev: boolean;
34
33
  };
34
+ /** The word before the caret, e.g. `"ሰላ"` while typing `ሰላም`. */
35
+ currentWord(): string;
36
+ /**
37
+ * Write `text` at the caret, as if typed. With `replaceWord`, the word
38
+ * before the caret is replaced (accepting a suggestion). Returns false
39
+ * while an OS IME composition is in progress, so it cannot be corrupted.
40
+ */
41
+ insertText(text: string, options?: {
42
+ replaceWord?: boolean;
43
+ }): boolean;
44
+ private textBeforeCaret;
35
45
  private handleBeforeInput;
36
46
  private insertGeezChar;
47
+ /** Replace `length` UTF-16 units before the caret; defaults to the previous grapheme. */
37
48
  private replaceGeezChar;
38
49
  private dispatchSyntheticInput;
39
50
  private getPreviousCharByTargetRanges;
@@ -50,6 +61,31 @@ declare namespace core {
50
61
 
51
62
  declare const inputMethod: InputMethodSpec;
52
63
 
64
+ /** Every input method bundled with the package, in menu order. */
65
+ declare const inputMethods: InputMethodSpec[];
66
+ /** Look one up by its `code`, e.g. `"ti-ER"`. */
67
+ declare function getInputMethod(code: string): InputMethodSpec | undefined;
68
+
69
+ /**
70
+ * DOM-free transliteration engine. Feed it the character before the caret
71
+ * and the key just typed; it says what to write and whether that replaces
72
+ * the previous character.
73
+ */
74
+ declare class Transliterator {
75
+ private inputMap;
76
+ private geezToLatinMap;
77
+ constructor(spec: InputMethodSpec);
78
+ feed(prevChar: string, key: string): {
79
+ replace: boolean;
80
+ text: string;
81
+ };
82
+ /** Run a whole key sequence, e.g. `"selam"` → `"ሰላም"`. */
83
+ transliterate(keys: string): string;
84
+ }
85
+
86
+ /** The word at the end of `text`: everything after the last space or punctuation mark. */
87
+ declare function trailingWord(text: string): string;
88
+
53
89
  declare const ime: typeof core;
54
90
 
55
- export { WebGeezIME as GeezIME, type InputMethod, type InputMethodSpec, type SymbolMap, inputMethod as TigrinyaEritrean, ime };
91
+ export { WebGeezIME as GeezIME, type InputMethod, type InputMethodSpec, type SymbolMap, inputMethod as TigrinyaEritrean, Transliterator, getInputMethod, ime, inputMethods, trailingWord };
package/dist/index.mjs CHANGED
@@ -28,6 +28,48 @@ function buildReverseMap(inputMethod2, prefix = "") {
28
28
  function isLatinChar(char) {
29
29
  return char >= "a" && char <= "z" || char >= "A" && char <= "Z";
30
30
  }
31
+ function trailingWord(text) {
32
+ return /[^\s\p{P}]*$/u.exec(text)[0];
33
+ }
34
+
35
+ // src/transliterator.ts
36
+ var Transliterator = class {
37
+ constructor(spec) {
38
+ this.inputMap = spec.map;
39
+ this.geezToLatinMap = buildReverseMap(this.inputMap);
40
+ }
41
+ feed(prevChar, key) {
42
+ let prevLatinChar = prevChar;
43
+ if (!isLatinChar(prevChar))
44
+ prevLatinChar = this.geezToLatinMap[prevChar] || prevChar;
45
+ let currentNode = null;
46
+ let tempNode = this.inputMap;
47
+ for (const char of prevLatinChar) {
48
+ if (tempNode[char]) {
49
+ currentNode = tempNode[char];
50
+ tempNode = currentNode.next || {};
51
+ } else {
52
+ currentNode = null;
53
+ break;
54
+ }
55
+ }
56
+ if (currentNode?.next && currentNode.next[key])
57
+ return { replace: true, text: currentNode.next[key].value || key };
58
+ const newNode = this.inputMap[key];
59
+ if (newNode) return { replace: false, text: newNode.value || key };
60
+ return { replace: false, text: key };
61
+ }
62
+ /** Run a whole key sequence, e.g. `"selam"` → `"ሰላም"`. */
63
+ transliterate(keys) {
64
+ const out = [];
65
+ for (const key of keys) {
66
+ const { replace, text } = this.feed(out[out.length - 1] ?? "", key);
67
+ if (replace) out.pop();
68
+ out.push(text);
69
+ }
70
+ return out.join("");
71
+ }
72
+ };
31
73
 
32
74
  // src/webGeezIme.ts
33
75
  var WebGeezIME = class {
@@ -44,8 +86,7 @@ var WebGeezIME = class {
44
86
  this.boundCompositionEnd = () => {
45
87
  this.composing = false;
46
88
  };
47
- this.inputMap = inputMethodSpec.map;
48
- this.geezToLatinMap = buildReverseMap(this.inputMap);
89
+ this.transliterator = new Transliterator(inputMethodSpec);
49
90
  if (typeof options?.enabled === "boolean")
50
91
  this.enabled = options.enabled;
51
92
  this.attachToElement(element);
@@ -101,30 +142,46 @@ var WebGeezIME = class {
101
142
  );
102
143
  }
103
144
  processInput(prevChar, newChar) {
104
- let prevLatinChar = prevChar;
105
- if (!isLatinChar(prevChar))
106
- prevLatinChar = this.geezToLatinMap[prevChar] || prevChar;
107
- let currentNode = null;
108
- let tempNode = this.inputMap;
109
- for (const char of prevLatinChar) {
110
- if (tempNode[char]) {
111
- currentNode = tempNode[char];
112
- tempNode = currentNode.next || {};
113
- } else {
114
- currentNode = null;
115
- break;
116
- }
145
+ const { replace, text } = this.transliterator.feed(prevChar, newChar);
146
+ return { output: text, modifiesPrev: replace };
147
+ }
148
+ /** The word before the caret, e.g. `"ሰላ"` while typing `ሰላም`. */
149
+ currentWord() {
150
+ return trailingWord(this.textBeforeCaret());
151
+ }
152
+ /**
153
+ * Write `text` at the caret, as if typed. With `replaceWord`, the word
154
+ * before the caret is replaced (accepting a suggestion). Returns false
155
+ * while an OS IME composition is in progress, so it cannot be corrupted.
156
+ */
157
+ insertText(text, options) {
158
+ if (!this.activeElement || this.composing) return false;
159
+ const length = options?.replaceWord ? this.currentWord().length : 0;
160
+ if (length) this.replaceGeezChar(text, length);
161
+ else this.insertGeezChar(text);
162
+ this.dispatchSyntheticInput(length ? "insertReplacementText" : "insertText");
163
+ return true;
164
+ }
165
+ textBeforeCaret() {
166
+ if (!this.activeElement) return "";
167
+ if (this.elementType === "input") {
168
+ const el = this.activeElement;
169
+ return el.value.slice(0, el.selectionStart ?? el.value.length);
117
170
  }
118
- if (currentNode?.next && currentNode.next[newChar]) {
119
- return {
120
- output: currentNode.next[newChar].value || newChar,
121
- modifiesPrev: true
122
- };
171
+ if (!this.isContentEditable) return "";
172
+ this.activeElement.normalize();
173
+ const sel = window.getSelection();
174
+ if (!sel) return "";
175
+ if (!sel.rangeCount || !this.activeElement.contains(sel.getRangeAt(0).startContainer)) {
176
+ const range2 = document.createRange();
177
+ range2.selectNodeContents(this.activeElement);
178
+ range2.collapse(false);
179
+ sel.removeAllRanges();
180
+ sel.addRange(range2);
123
181
  }
124
- const newNode = this.inputMap[newChar];
125
- if (newNode)
126
- return { output: newNode.value || newChar, modifiesPrev: false };
127
- return { output: newChar, modifiesPrev: false };
182
+ const range = sel.getRangeAt(0);
183
+ const { node, offset } = this.resolveTextPosition(range.startContainer, range.startOffset);
184
+ return node ? (node.textContent ?? "").slice(0, offset) : "";
128
185
  }
129
186
  handleBeforeInput(event) {
130
187
  if (!this.activeElement) return;
@@ -176,12 +233,13 @@ var WebGeezIME = class {
176
233
  sel.addRange(range);
177
234
  }
178
235
  }
179
- replaceGeezChar(char) {
236
+ /** Replace `length` UTF-16 units before the caret; defaults to the previous grapheme. */
237
+ replaceGeezChar(char, length) {
180
238
  if (!this.activeElement) return;
181
239
  if (this.elementType === "input") {
182
240
  const target = this.activeElement;
183
241
  const caret = target.selectionStart ?? 0;
184
- const start = this.findPrevGraphemeStart(target.value, caret);
242
+ const start = length === void 0 ? this.findPrevGraphemeStart(target.value, caret) : Math.max(0, caret - length);
185
243
  target.setRangeText(char, start, caret, "end");
186
244
  } else if (this.isContentEditable) {
187
245
  const sel = window.getSelection();
@@ -193,7 +251,7 @@ var WebGeezIME = class {
193
251
  );
194
252
  if (node) {
195
253
  const text = node.textContent ?? "";
196
- const start = this.findPrevGraphemeStart(text, offset);
254
+ const start = length === void 0 ? this.findPrevGraphemeStart(text, offset) : Math.max(0, offset - length);
197
255
  const r = document.createRange();
198
256
  r.setStart(node, start);
199
257
  r.setEnd(node, offset);
@@ -2215,12 +2273,14 @@ var inputMethod = {
2215
2273
  var ti_ER_default = inputMethod;
2216
2274
 
2217
2275
  // src/input-methods/index.ts
2218
- var inputMethods = {};
2219
- inputMethods[ti_ER_default.name] = ti_ER_default.map;
2276
+ var inputMethods = [ti_ER_default];
2277
+ function getInputMethod(code) {
2278
+ return inputMethods.find((method) => method.code === code);
2279
+ }
2220
2280
 
2221
2281
  // src/index.ts
2222
2282
  var ime = webGeezIme_exports;
2223
2283
 
2224
- export { WebGeezIME as GeezIME, ti_ER_default as TigrinyaEritrean, ime };
2284
+ export { WebGeezIME as GeezIME, ti_ER_default as TigrinyaEritrean, Transliterator, getInputMethod, ime, inputMethods, trailingWord };
2225
2285
  //# sourceMappingURL=index.mjs.map
2226
2286
  //# sourceMappingURL=index.mjs.map