coneko-term 0.1.1 → 0.1.3

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/input.d.ts CHANGED
@@ -101,11 +101,15 @@ export declare class KeyHandler {
101
101
  private keyPressHandled;
102
102
  private unprocessedDeadKey;
103
103
  private readonly reported;
104
+ private readonly textPressed;
105
+ private lastDown;
104
106
  constructor(host: KeyHandlerHost, textarea: TextareaLike, composition: CompositionHelper);
105
107
  private cancel;
106
108
  /** macOS の Option(`macOptionIsMeta` が false のとき)と、Windows の AltGr は、字を打つキー。 */
107
109
  private isThirdLevelShift;
108
110
  keyDown(ev: KeyDownLike): boolean | undefined;
111
+ /** 押すを送るキーを覚える(離すを送るため)。 */
112
+ private rememberPress;
109
113
  /** 押したままフォーカスを失うと keyup が来ないので、`textarea` の blur で呼ぶ。 */
110
114
  reset(): void;
111
115
  keyUp(ev: KeyDownLike): void;
package/dist/input.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // `_keyDown`・`_keyPress`・`_inputEvent` と `CompositionHelper` の動きに合わせる。
3
3
  // DOM の要素そのものではなく、使う部分だけの形(`TextareaLike` など)に頼るので、DOM の無い
4
4
  // テストでも動かせる。
5
- import { evaluateKeyboardEvent, KITTY_REPORT_EVENT_TYPES, } from "./keyboard.js";
5
+ import { evaluateKeyboardEvent, KITTY_REPORT_ALL_KEYS, KITTY_REPORT_EVENT_TYPES, } from "./keyboard.js";
6
6
  /**
7
7
  * 変換(IME)の間は送らず、確定した字を送る。変換中の字は、カーソルの位置に見せる。
8
8
  * xterm.js の `CompositionHelper` と同じ流れ。
@@ -152,6 +152,11 @@ export class KeyHandler {
152
152
  // keydown で `CSI` の形で送ったキー。離すを送るのは、これに入っているキーだけ。
153
153
  // 変換の確定の Enter のように、押したのを送っていないキーの離すだけを送ってしまわないため
154
154
  reported = new Set();
155
+ // 字を打つキーの押すを keypress か input で送ったキー。フラグ 8 が無いと、押すは字のまま送るので
156
+ // `reported` に入らない。kitty は、この場合も離すを `CSI` の形で送る
157
+ textPressed = new Set();
158
+ // 直近の keydown のキー(keypress・input の字を、どのキーが打ったか結びつける)。変換のキーと、macOS の Cmd を押したキーでは null
159
+ lastDown = null;
155
160
  constructor(host, textarea, composition) {
156
161
  this.host = host;
157
162
  this.textarea = textarea;
@@ -174,6 +179,7 @@ export class KeyHandler {
174
179
  }
175
180
  keyDown(ev) {
176
181
  this.keyDownSeen = true;
182
+ this.lastDown = null;
177
183
  const custom = this.host.customKeyHandler();
178
184
  if (custom && custom(ev) === false)
179
185
  return false;
@@ -188,6 +194,7 @@ export class KeyHandler {
188
194
  // Cmd を押したキーは送らない(アプリのショートカットに任せる)。変換の確定のあとで判断する
189
195
  if (h.isMac && ev.metaKey)
190
196
  return true;
197
+ this.lastDown = keyId(ev);
191
198
  if (!ignoreComposition && (ev.key === "Dead" || ev.key === "AltGraph")) {
192
199
  this.unprocessedDeadKey = true;
193
200
  }
@@ -206,11 +213,7 @@ export class KeyHandler {
206
213
  return true;
207
214
  // 大文字は keypress で処理する(CapsLock のとき小文字が打てない macOS の IME への対策)。
208
215
  // kitty の `CSI` の形で送るキーは keypress を通らないので、ここで送る
209
- if (result.key === ev.key &&
210
- !ev.ctrlKey &&
211
- !ev.altKey &&
212
- !ev.metaKey &&
213
- ev.key.length === 1) {
216
+ if (result.key === ev.key && hasNoCommandModifier(ev) && ev.key.length === 1) {
214
217
  const c = ev.key.charCodeAt(0);
215
218
  if (c >= 65 && c <= 90)
216
219
  return true;
@@ -226,18 +229,27 @@ export class KeyHandler {
226
229
  (ev.ctrlKey && ev.keyCode === 67)) {
227
230
  this.textarea.value = "";
228
231
  }
229
- // アプリケーションカーソルキーの `ESC O A` などもここに入れる
230
- if (flags & KITTY_REPORT_EVENT_TYPES &&
231
- (result.key.startsWith("\x1b[") || result.key.startsWith("\x1bO"))) {
232
- this.reported.add(keyId(ev));
233
- }
232
+ this.rememberPress(ev, result.key, flags);
234
233
  h.send(result.key);
235
234
  this.cancel(ev);
236
235
  return false;
237
236
  }
237
+ /** 押すを送るキーを覚える(離すを送るため)。 */
238
+ rememberPress(ev, key, flags) {
239
+ // アプリケーションカーソルキーの `ESC O A` などもここに入れる
240
+ if (flags & KITTY_REPORT_EVENT_TYPES && (key.startsWith("\x1b[") || key.startsWith("\x1bO"))) {
241
+ this.reported.add(keyId(ev));
242
+ }
243
+ // keydown が字を送る場合(小文字、Ctrl+a の `\x01`、Alt+a の `ESC a` など)。
244
+ // kitty は、押すを従来の形で送ったキーも、離すは `CSI <code>;<mods>:3u` で送る
245
+ if (!ev.metaKey && [...ev.key].length === 1) {
246
+ this.textPressed.add(keyId(ev));
247
+ }
248
+ }
238
249
  /** 押したままフォーカスを失うと keyup が来ないので、`textarea` の blur で呼ぶ。 */
239
250
  reset() {
240
251
  this.reported.clear();
252
+ this.textPressed.clear();
241
253
  }
242
254
  keyUp(ev) {
243
255
  this.keyDownSeen = false;
@@ -245,8 +257,11 @@ export class KeyHandler {
245
257
  if (custom && custom(ev) === false)
246
258
  return;
247
259
  this.keyPressHandled = false;
248
- // 離す(kitty のフラグ 2)。押したのを `CSI` の形で送ったキーだけ
249
- if (!this.reported.delete(keyId(ev)))
260
+ // 離す(kitty のフラグ 2)。押したのを `CSI` の形か、字として(keypress・input で)送ったキーだけ
261
+ const id = keyId(ev);
262
+ const wasReported = this.reported.delete(id);
263
+ const wasText = this.textPressed.delete(id);
264
+ if (!wasReported && !wasText)
250
265
  return;
251
266
  const h = this.host;
252
267
  const flags = h.keyboardFlags();
@@ -255,7 +270,17 @@ export class KeyHandler {
255
270
  // 変換の途中の keyup は送らない(変換の動きは変えない)
256
271
  if (this.composition.isComposing || ev.keyCode === 229)
257
272
  return;
258
- const result = evaluateKeyboardEvent(ev, h.applicationCursorKeys(), h.isMac, h.macOptionIsMeta, flags, 3);
273
+ // 字を打つ修飾(macOS の Option・Windows の AltGr)。kitty は離すのとき、Option を Alt として
274
+ // 送り、AltGr は修飾に出さない(Linux)。押すは字のまま送るので、ここは離すだけの扱い
275
+ const thirdLevel = wasText && this.isThirdLevelShift(ev);
276
+ const result = evaluateKeyboardEvent(!thirdLevel ? ev : h.isWindows ? override(ev, { ctrlKey: false, altKey: false }) : override(ev, { key: "" }), h.applicationCursorKeys(),
277
+ // kittyKeyOf は macOS の Option を字のキーとして除く。離すは除かない。
278
+ // Option で変わった字(å)ではなく、押したキー(a)の番号で送る(kitty と同じ)
279
+ thirdLevel && h.isMac ? false : h.isMac, h.macOptionIsMeta,
280
+ // 字のキーの離すは、フラグ 8 が無くても `CSI <code>;<mods>:3u`(kitty と同じ)。
281
+ // evaluateKeyboardEvent は字のキーをフラグ 8 のときだけ `CSI` にするので、離すに限って足す。
282
+ // Enter・Tab・Backspace は textPressed に入らず、CSI の形の離すは reported のキーだけ
283
+ wasText ? flags | KITTY_REPORT_ALL_KEYS : flags, 3);
259
284
  if (result.key)
260
285
  h.send(result.key);
261
286
  }
@@ -278,6 +303,8 @@ export class KeyHandler {
278
303
  return false;
279
304
  }
280
305
  this.host.send(String.fromCharCode(key));
306
+ if (this.lastDown)
307
+ this.textPressed.add(this.lastDown);
281
308
  this.keyPressHandled = true;
282
309
  this.unprocessedDeadKey = false;
283
310
  return true;
@@ -291,12 +318,29 @@ export class KeyHandler {
291
318
  return false;
292
319
  this.unprocessedDeadKey = false;
293
320
  this.host.send(ev.data);
321
+ if (this.keyDownSeen && this.lastDown)
322
+ this.textPressed.add(this.lastDown);
294
323
  this.cancel(ev);
295
324
  return true;
296
325
  }
297
326
  return false;
298
327
  }
299
328
  }
329
+ /** `ev` の一部の値を差し替えて見せる。 */
330
+ function override(ev, values) {
331
+ return new Proxy(ev, {
332
+ get(target, prop) {
333
+ if (prop in values)
334
+ return values[prop];
335
+ const v = Reflect.get(target, prop, target);
336
+ return typeof v === "function" ? v.bind(target) : v;
337
+ },
338
+ });
339
+ }
340
+ /** Ctrl・Alt・Cmd のどれも押していない。 */
341
+ function hasNoCommandModifier(ev) {
342
+ return !ev.ctrlKey && !ev.altKey && !ev.metaKey;
343
+ }
300
344
  /** 押したキーと離したキーを結びつける印。 */
301
345
  function keyId(ev) {
302
346
  return ev.code || String(ev.keyCode);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coneko-term",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Terminal building blocks (VT parser and screen model) in Rust, as WebAssembly with TypeScript types",
5
5
  "license": "MIT",
6
6
  "repository": {