ink-native 0.1.0 → 0.3.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/README.md +96 -49
- package/dist/{chunk-3NCUBVFY.js → chunk-FE3HR4I4.js} +396 -27
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +43 -5
- package/dist/index.js +5 -1
- package/native/fenster.dylib +0 -0
- package/native/fenster.h +31 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ window.on("close", () => process.exit(0));
|
|
|
127
127
|
|
|
128
128
|
## Direct Framebuffer Access
|
|
129
129
|
|
|
130
|
-
For high-framerate graphics (emulators, games, video players), you can write pixels directly to the framebuffer while pausing
|
|
130
|
+
For high-framerate graphics (emulators, games, video players), you can write pixels directly to the framebuffer while pausing Ink:
|
|
131
131
|
|
|
132
132
|
```tsx
|
|
133
133
|
import { createStreams, packColor } from "ink-native";
|
|
@@ -147,12 +147,25 @@ const { stdin, stdout, window, renderer } = createStreams({
|
|
|
147
147
|
|
|
148
148
|
render(<App />, { stdin, stdout });
|
|
149
149
|
|
|
150
|
-
// Pause Ink
|
|
150
|
+
// Pause Ink to take over rendering
|
|
151
151
|
window.pause();
|
|
152
152
|
|
|
153
153
|
const fb = renderer.getFramebuffer();
|
|
154
154
|
|
|
155
|
-
//
|
|
155
|
+
// Keyboard events keep firing when paused
|
|
156
|
+
window.on("keydown", (event) => {
|
|
157
|
+
if (event.key === "q") {
|
|
158
|
+
clearInterval(gameLoop);
|
|
159
|
+
window.resume(); // hand control back to Ink
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
window.on("close", () => {
|
|
164
|
+
clearInterval(gameLoop);
|
|
165
|
+
process.exit(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Game loop — just render, events are handled automatically
|
|
156
169
|
const gameLoop = setInterval(() => {
|
|
157
170
|
// Write pixels directly (0xAARRGGBB format)
|
|
158
171
|
for (let y = 100; y < 200; y++) {
|
|
@@ -161,24 +174,8 @@ const gameLoop = setInterval(() => {
|
|
|
161
174
|
}
|
|
162
175
|
}
|
|
163
176
|
|
|
164
|
-
//
|
|
177
|
+
// Copy to native buffer (the event loop presents it)
|
|
165
178
|
renderer.present();
|
|
166
|
-
const { keyEvents, mod } = renderer.processEventsAndPresent();
|
|
167
|
-
|
|
168
|
-
// Handle input
|
|
169
|
-
for (const event of keyEvents) {
|
|
170
|
-
const seq = renderer.keyEventToSequence(event, mod);
|
|
171
|
-
if (seq === "q") {
|
|
172
|
-
clearInterval(gameLoop);
|
|
173
|
-
window.resume(); // hand control back to Ink
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (renderer.shouldClose()) {
|
|
178
|
-
clearInterval(gameLoop);
|
|
179
|
-
window.close();
|
|
180
|
-
process.exit(0);
|
|
181
|
-
}
|
|
182
179
|
}, 16); // ~60fps
|
|
183
180
|
```
|
|
184
181
|
|
|
@@ -211,31 +208,24 @@ const startEmulator = () => {
|
|
|
211
208
|
window.pause();
|
|
212
209
|
|
|
213
210
|
const fb = renderer.getFramebuffer();
|
|
211
|
+
let emuLoop: ReturnType<typeof setInterval>;
|
|
212
|
+
|
|
213
|
+
// Keyboard events keep firing when paused
|
|
214
|
+
window.on("keydown", (event) => {
|
|
215
|
+
if (event.key === "Escape") {
|
|
216
|
+
// Return to menu
|
|
217
|
+
clearInterval(emuLoop);
|
|
218
|
+
renderer.clear();
|
|
219
|
+
window.resume(); // hand control back to Ink
|
|
220
|
+
}
|
|
221
|
+
});
|
|
214
222
|
|
|
215
|
-
|
|
223
|
+
emuLoop = setInterval(() => {
|
|
216
224
|
// Write emulator frame directly to the framebuffer
|
|
217
225
|
renderEmulatorFrame(fb.pixels, fb.width, fb.height);
|
|
218
226
|
|
|
219
|
-
//
|
|
227
|
+
// Copy to native buffer (the event loop presents it)
|
|
220
228
|
renderer.present();
|
|
221
|
-
const { keyEvents, mod } = renderer.processEventsAndPresent();
|
|
222
|
-
|
|
223
|
-
for (const event of keyEvents) {
|
|
224
|
-
const seq = renderer.keyEventToSequence(event, mod);
|
|
225
|
-
if (seq === "\x1b") {
|
|
226
|
-
// Escape pressed — return to menu
|
|
227
|
-
clearInterval(emuLoop);
|
|
228
|
-
renderer.clear();
|
|
229
|
-
window.resume(); // hand control back to Ink
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (renderer.shouldClose()) {
|
|
235
|
-
clearInterval(emuLoop);
|
|
236
|
-
window.close();
|
|
237
|
-
process.exit(0);
|
|
238
|
-
}
|
|
239
229
|
}, 16);
|
|
240
230
|
};
|
|
241
231
|
```
|
|
@@ -248,9 +238,9 @@ The framebuffer is shared — Ink renders to it when active, and you write pixel
|
|
|
248
238
|
| --------------------------- | ------------------------------------------------------- |
|
|
249
239
|
| `packColor(r, g, b)` | Pack RGB values into `0xAARRGGBB` pixel format |
|
|
250
240
|
| `renderer.getFramebuffer()` | Get `{ pixels, width, height }` — the live pixel buffer |
|
|
251
|
-
| `window.pause()` |
|
|
252
|
-
| `window.resume()` |
|
|
253
|
-
| `window.isPaused()` | Check if
|
|
241
|
+
| `window.pause()` | Pause Ink (events keep firing) |
|
|
242
|
+
| `window.resume()` | Resume Ink |
|
|
243
|
+
| `window.isPaused()` | Check if Ink is paused |
|
|
254
244
|
|
|
255
245
|
## API
|
|
256
246
|
|
|
@@ -286,8 +276,9 @@ Event emitter for window lifecycle and input events.
|
|
|
286
276
|
|
|
287
277
|
#### Events
|
|
288
278
|
|
|
279
|
+
- `keydown` -- Emitted when a key is pressed (with `NativeKeyboardEvent` payload)
|
|
280
|
+
- `keyup` -- Emitted when a key is released (with `NativeKeyboardEvent` payload)
|
|
289
281
|
- `close` -- Emitted when the window is closed
|
|
290
|
-
- `key` -- Emitted on keyboard events
|
|
291
282
|
- `resize` -- Emitted when the window is resized (with `{ columns, rows }`)
|
|
292
283
|
- `sigint` -- Emitted on Ctrl+C (if a listener is registered; otherwise sends SIGINT to the process)
|
|
293
284
|
|
|
@@ -299,13 +290,64 @@ Event emitter for window lifecycle and input events.
|
|
|
299
290
|
- `clear()` -- Clear the screen
|
|
300
291
|
- `close()` -- Close the window
|
|
301
292
|
- `isClosed()` -- Check if the window is closed
|
|
302
|
-
- `pause()` -- Pause
|
|
303
|
-
- `resume()` -- Resume
|
|
304
|
-
- `isPaused()` -- Check if
|
|
293
|
+
- `pause()` -- Pause Ink for manual rendering (keydown/keyup/resize/close events keep firing)
|
|
294
|
+
- `resume()` -- Resume Ink
|
|
295
|
+
- `isPaused()` -- Check if Ink is paused
|
|
296
|
+
- `processEvents()` -- Manually poll events and present the framebuffer (for custom render loops that need explicit control)
|
|
305
297
|
|
|
306
|
-
## Keyboard
|
|
298
|
+
## Keyboard Events
|
|
307
299
|
|
|
308
|
-
The
|
|
300
|
+
The `window` emits `keydown` and `keyup` events with a `NativeKeyboardEvent` payload:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { createStreams, type NativeKeyboardEvent } from "ink-native";
|
|
304
|
+
|
|
305
|
+
const { window } = createStreams({ title: "My Game" });
|
|
306
|
+
|
|
307
|
+
window.on("keydown", (event: NativeKeyboardEvent) => {
|
|
308
|
+
console.log(event.key); // "a", "A", "Enter", "ArrowUp", "Shift"
|
|
309
|
+
console.log(event.code); // "KeyA", "Enter", "ArrowUp", "ShiftLeft"
|
|
310
|
+
console.log(event.ctrlKey); // true if Ctrl is held
|
|
311
|
+
console.log(event.type); // "keydown"
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
window.on("keyup", (event: NativeKeyboardEvent) => {
|
|
315
|
+
console.log(event.key, "released");
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### `NativeKeyboardEvent`
|
|
320
|
+
|
|
321
|
+
| Property | Type | Description |
|
|
322
|
+
| ---------- | ------------------------ | ------------------------------------------------------- |
|
|
323
|
+
| `key` | `string` | The key value: `"a"`, `"A"`, `"Enter"`, `"Shift"` |
|
|
324
|
+
| `code` | `string` | Physical key code: `"KeyA"`, `"Enter"`, `"ShiftLeft"` |
|
|
325
|
+
| `ctrlKey` | `boolean` | Whether Ctrl is held |
|
|
326
|
+
| `shiftKey` | `boolean` | Whether Shift is held |
|
|
327
|
+
| `altKey` | `boolean` | Whether Alt is held |
|
|
328
|
+
| `metaKey` | `boolean` | Whether Meta/Command is held |
|
|
329
|
+
| `repeat` | `false` | Always `false` (fenster only reports transitions) |
|
|
330
|
+
| `type` | `"keydown" \| "keyup"` | Whether the key was pressed or released |
|
|
331
|
+
|
|
332
|
+
Modifier keys fire their own events with left/right distinction — `event.code` will be `"ShiftLeft"` or `"ShiftRight"`, while `event.key` gives the generic name `"Shift"`.
|
|
333
|
+
|
|
334
|
+
#### `isNativeKeyboardEvent(value)`
|
|
335
|
+
|
|
336
|
+
Type guard to check if a value is a `NativeKeyboardEvent`:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { isNativeKeyboardEvent } from "ink-native";
|
|
340
|
+
|
|
341
|
+
window.on("keydown", (event) => {
|
|
342
|
+
if (isNativeKeyboardEvent(event)) {
|
|
343
|
+
// event is typed as NativeKeyboardEvent
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Terminal Sequences
|
|
349
|
+
|
|
350
|
+
In addition to `keydown`/`keyup` events, key presses are also mapped to terminal escape sequences and pushed to `stdin` for Ink's built-in key handling:
|
|
309
351
|
|
|
310
352
|
- Arrow keys (Up, Down, Left, Right)
|
|
311
353
|
- Enter, Escape, Backspace, Tab, Delete
|
|
@@ -327,6 +369,11 @@ import {
|
|
|
327
369
|
InputStream,
|
|
328
370
|
OutputStream,
|
|
329
371
|
|
|
372
|
+
// Keyboard events
|
|
373
|
+
createKeyboardEvent,
|
|
374
|
+
isNativeKeyboardEvent,
|
|
375
|
+
type NativeKeyboardEvent,
|
|
376
|
+
|
|
330
377
|
// Renderer
|
|
331
378
|
UiRenderer,
|
|
332
379
|
packColor,
|
|
@@ -123867,13 +123867,66 @@ var FENSTER_KEY_HOME = 2;
|
|
|
123867
123867
|
var FENSTER_KEY_PAGEUP = 3;
|
|
123868
123868
|
var FENSTER_KEY_PAGEDOWN = 4;
|
|
123869
123869
|
var FENSTER_KEY_END = 5;
|
|
123870
|
+
var FENSTER_KEY_INSERT = 26;
|
|
123870
123871
|
var FENSTER_KEY_A = 65;
|
|
123872
|
+
var FENSTER_KEY_B = 66;
|
|
123873
|
+
var FENSTER_KEY_C = 67;
|
|
123874
|
+
var FENSTER_KEY_D = 68;
|
|
123875
|
+
var FENSTER_KEY_E = 69;
|
|
123876
|
+
var FENSTER_KEY_F = 70;
|
|
123877
|
+
var FENSTER_KEY_G = 71;
|
|
123878
|
+
var FENSTER_KEY_H = 72;
|
|
123879
|
+
var FENSTER_KEY_I = 73;
|
|
123880
|
+
var FENSTER_KEY_J = 74;
|
|
123881
|
+
var FENSTER_KEY_K = 75;
|
|
123882
|
+
var FENSTER_KEY_L = 76;
|
|
123883
|
+
var FENSTER_KEY_M = 77;
|
|
123884
|
+
var FENSTER_KEY_N = 78;
|
|
123885
|
+
var FENSTER_KEY_O = 79;
|
|
123886
|
+
var FENSTER_KEY_P = 80;
|
|
123887
|
+
var FENSTER_KEY_Q = 81;
|
|
123888
|
+
var FENSTER_KEY_R = 82;
|
|
123889
|
+
var FENSTER_KEY_S = 83;
|
|
123890
|
+
var FENSTER_KEY_T = 84;
|
|
123891
|
+
var FENSTER_KEY_U = 85;
|
|
123892
|
+
var FENSTER_KEY_V = 86;
|
|
123893
|
+
var FENSTER_KEY_W = 87;
|
|
123894
|
+
var FENSTER_KEY_X = 88;
|
|
123895
|
+
var FENSTER_KEY_Y = 89;
|
|
123871
123896
|
var FENSTER_KEY_Z = 90;
|
|
123897
|
+
var FENSTER_KEY_0 = 48;
|
|
123898
|
+
var FENSTER_KEY_1 = 49;
|
|
123899
|
+
var FENSTER_KEY_2 = 50;
|
|
123900
|
+
var FENSTER_KEY_3 = 51;
|
|
123901
|
+
var FENSTER_KEY_4 = 52;
|
|
123902
|
+
var FENSTER_KEY_5 = 53;
|
|
123903
|
+
var FENSTER_KEY_6 = 54;
|
|
123904
|
+
var FENSTER_KEY_7 = 55;
|
|
123905
|
+
var FENSTER_KEY_8 = 56;
|
|
123906
|
+
var FENSTER_KEY_9 = 57;
|
|
123907
|
+
var FENSTER_KEY_APOSTROPHE = 39;
|
|
123908
|
+
var FENSTER_KEY_COMMA = 44;
|
|
123909
|
+
var FENSTER_KEY_MINUS = 45;
|
|
123910
|
+
var FENSTER_KEY_PERIOD = 46;
|
|
123911
|
+
var FENSTER_KEY_SLASH = 47;
|
|
123912
|
+
var FENSTER_KEY_SEMICOLON = 59;
|
|
123913
|
+
var FENSTER_KEY_EQUAL = 61;
|
|
123872
123914
|
var FENSTER_KEY_BRACKET_LEFT = 91;
|
|
123873
123915
|
var FENSTER_KEY_BACKSLASH = 92;
|
|
123874
123916
|
var FENSTER_KEY_BRACKET_RIGHT = 93;
|
|
123917
|
+
var FENSTER_KEY_GRAVE = 96;
|
|
123918
|
+
var FENSTER_KEY_SHIFT_LEFT = 128;
|
|
123919
|
+
var FENSTER_KEY_SHIFT_RIGHT = 129;
|
|
123920
|
+
var FENSTER_KEY_CONTROL_LEFT = 130;
|
|
123921
|
+
var FENSTER_KEY_CONTROL_RIGHT = 131;
|
|
123922
|
+
var FENSTER_KEY_ALT_LEFT = 132;
|
|
123923
|
+
var FENSTER_KEY_ALT_RIGHT = 133;
|
|
123924
|
+
var FENSTER_KEY_META_LEFT = 134;
|
|
123925
|
+
var FENSTER_KEY_META_RIGHT = 135;
|
|
123875
123926
|
var FENSTER_MOD_CTRL = 1;
|
|
123876
123927
|
var FENSTER_MOD_SHIFT = 2;
|
|
123928
|
+
var FENSTER_MOD_ALT = 4;
|
|
123929
|
+
var FENSTER_MOD_META = 8;
|
|
123877
123930
|
var KEYS_ARRAY_SIZE = 256;
|
|
123878
123931
|
var INT32_BYTES = 4;
|
|
123879
123932
|
var FENSTER_LIB_PATHS = {
|
|
@@ -123888,6 +123941,10 @@ var findFensterLibrary = () => {
|
|
|
123888
123941
|
if (!libName) {
|
|
123889
123942
|
return null;
|
|
123890
123943
|
}
|
|
123944
|
+
try {
|
|
123945
|
+
return fileURLToPath(import.meta.resolve(`ink-native/${libName}`));
|
|
123946
|
+
} catch {
|
|
123947
|
+
}
|
|
123891
123948
|
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
123892
123949
|
const projectRoot = resolve(currentDir, "../..");
|
|
123893
123950
|
return resolve(projectRoot, libName);
|
|
@@ -124134,9 +124191,249 @@ var InputStream = class extends Readable {
|
|
|
124134
124191
|
}
|
|
124135
124192
|
};
|
|
124136
124193
|
|
|
124194
|
+
// src/KeyboardEvent/consts.ts
|
|
124195
|
+
var FENSTER_TO_CODE = {
|
|
124196
|
+
// Navigation / control
|
|
124197
|
+
[FENSTER_KEY_BACKSPACE]: "Backspace",
|
|
124198
|
+
[FENSTER_KEY_TAB]: "Tab",
|
|
124199
|
+
[FENSTER_KEY_RETURN]: "Enter",
|
|
124200
|
+
[FENSTER_KEY_UP]: "ArrowUp",
|
|
124201
|
+
[FENSTER_KEY_DOWN]: "ArrowDown",
|
|
124202
|
+
[FENSTER_KEY_RIGHT]: "ArrowRight",
|
|
124203
|
+
[FENSTER_KEY_LEFT]: "ArrowLeft",
|
|
124204
|
+
[FENSTER_KEY_ESCAPE]: "Escape",
|
|
124205
|
+
[FENSTER_KEY_SPACE]: "Space",
|
|
124206
|
+
[FENSTER_KEY_DELETE]: "Delete",
|
|
124207
|
+
[FENSTER_KEY_HOME]: "Home",
|
|
124208
|
+
[FENSTER_KEY_PAGEUP]: "PageUp",
|
|
124209
|
+
[FENSTER_KEY_PAGEDOWN]: "PageDown",
|
|
124210
|
+
[FENSTER_KEY_END]: "End",
|
|
124211
|
+
[FENSTER_KEY_INSERT]: "Insert",
|
|
124212
|
+
// Letters (A-Z → KeyA-KeyZ)
|
|
124213
|
+
[FENSTER_KEY_A]: "KeyA",
|
|
124214
|
+
[FENSTER_KEY_B]: "KeyB",
|
|
124215
|
+
[FENSTER_KEY_C]: "KeyC",
|
|
124216
|
+
[FENSTER_KEY_D]: "KeyD",
|
|
124217
|
+
[FENSTER_KEY_E]: "KeyE",
|
|
124218
|
+
[FENSTER_KEY_F]: "KeyF",
|
|
124219
|
+
[FENSTER_KEY_G]: "KeyG",
|
|
124220
|
+
[FENSTER_KEY_H]: "KeyH",
|
|
124221
|
+
[FENSTER_KEY_I]: "KeyI",
|
|
124222
|
+
[FENSTER_KEY_J]: "KeyJ",
|
|
124223
|
+
[FENSTER_KEY_K]: "KeyK",
|
|
124224
|
+
[FENSTER_KEY_L]: "KeyL",
|
|
124225
|
+
[FENSTER_KEY_M]: "KeyM",
|
|
124226
|
+
[FENSTER_KEY_N]: "KeyN",
|
|
124227
|
+
[FENSTER_KEY_O]: "KeyO",
|
|
124228
|
+
[FENSTER_KEY_P]: "KeyP",
|
|
124229
|
+
[FENSTER_KEY_Q]: "KeyQ",
|
|
124230
|
+
[FENSTER_KEY_R]: "KeyR",
|
|
124231
|
+
[FENSTER_KEY_S]: "KeyS",
|
|
124232
|
+
[FENSTER_KEY_T]: "KeyT",
|
|
124233
|
+
[FENSTER_KEY_U]: "KeyU",
|
|
124234
|
+
[FENSTER_KEY_V]: "KeyV",
|
|
124235
|
+
[FENSTER_KEY_W]: "KeyW",
|
|
124236
|
+
[FENSTER_KEY_X]: "KeyX",
|
|
124237
|
+
[FENSTER_KEY_Y]: "KeyY",
|
|
124238
|
+
[FENSTER_KEY_Z]: "KeyZ",
|
|
124239
|
+
// Digits (0-9 → Digit0-Digit9)
|
|
124240
|
+
[FENSTER_KEY_0]: "Digit0",
|
|
124241
|
+
[FENSTER_KEY_1]: "Digit1",
|
|
124242
|
+
[FENSTER_KEY_2]: "Digit2",
|
|
124243
|
+
[FENSTER_KEY_3]: "Digit3",
|
|
124244
|
+
[FENSTER_KEY_4]: "Digit4",
|
|
124245
|
+
[FENSTER_KEY_5]: "Digit5",
|
|
124246
|
+
[FENSTER_KEY_6]: "Digit6",
|
|
124247
|
+
[FENSTER_KEY_7]: "Digit7",
|
|
124248
|
+
[FENSTER_KEY_8]: "Digit8",
|
|
124249
|
+
[FENSTER_KEY_9]: "Digit9",
|
|
124250
|
+
// Symbols
|
|
124251
|
+
[FENSTER_KEY_APOSTROPHE]: "Quote",
|
|
124252
|
+
[FENSTER_KEY_COMMA]: "Comma",
|
|
124253
|
+
[FENSTER_KEY_MINUS]: "Minus",
|
|
124254
|
+
[FENSTER_KEY_PERIOD]: "Period",
|
|
124255
|
+
[FENSTER_KEY_SLASH]: "Slash",
|
|
124256
|
+
[FENSTER_KEY_SEMICOLON]: "Semicolon",
|
|
124257
|
+
[FENSTER_KEY_EQUAL]: "Equal",
|
|
124258
|
+
[FENSTER_KEY_BRACKET_LEFT]: "BracketLeft",
|
|
124259
|
+
[FENSTER_KEY_BACKSLASH]: "Backslash",
|
|
124260
|
+
[FENSTER_KEY_BRACKET_RIGHT]: "BracketRight",
|
|
124261
|
+
[FENSTER_KEY_GRAVE]: "Backquote",
|
|
124262
|
+
// Modifier keys (left/right)
|
|
124263
|
+
[FENSTER_KEY_SHIFT_LEFT]: "ShiftLeft",
|
|
124264
|
+
[FENSTER_KEY_SHIFT_RIGHT]: "ShiftRight",
|
|
124265
|
+
[FENSTER_KEY_CONTROL_LEFT]: "ControlLeft",
|
|
124266
|
+
[FENSTER_KEY_CONTROL_RIGHT]: "ControlRight",
|
|
124267
|
+
[FENSTER_KEY_ALT_LEFT]: "AltLeft",
|
|
124268
|
+
[FENSTER_KEY_ALT_RIGHT]: "AltRight",
|
|
124269
|
+
[FENSTER_KEY_META_LEFT]: "MetaLeft",
|
|
124270
|
+
[FENSTER_KEY_META_RIGHT]: "MetaRight"
|
|
124271
|
+
};
|
|
124272
|
+
var FENSTER_TO_KEY = {
|
|
124273
|
+
// Navigation / control
|
|
124274
|
+
[FENSTER_KEY_BACKSPACE]: "Backspace",
|
|
124275
|
+
[FENSTER_KEY_TAB]: "Tab",
|
|
124276
|
+
[FENSTER_KEY_RETURN]: "Enter",
|
|
124277
|
+
[FENSTER_KEY_UP]: "ArrowUp",
|
|
124278
|
+
[FENSTER_KEY_DOWN]: "ArrowDown",
|
|
124279
|
+
[FENSTER_KEY_RIGHT]: "ArrowRight",
|
|
124280
|
+
[FENSTER_KEY_LEFT]: "ArrowLeft",
|
|
124281
|
+
[FENSTER_KEY_ESCAPE]: "Escape",
|
|
124282
|
+
[FENSTER_KEY_SPACE]: " ",
|
|
124283
|
+
[FENSTER_KEY_DELETE]: "Delete",
|
|
124284
|
+
[FENSTER_KEY_HOME]: "Home",
|
|
124285
|
+
[FENSTER_KEY_PAGEUP]: "PageUp",
|
|
124286
|
+
[FENSTER_KEY_PAGEDOWN]: "PageDown",
|
|
124287
|
+
[FENSTER_KEY_END]: "End",
|
|
124288
|
+
[FENSTER_KEY_INSERT]: "Insert",
|
|
124289
|
+
// Letters (A-Z → a-z lowercase)
|
|
124290
|
+
[FENSTER_KEY_A]: "a",
|
|
124291
|
+
[FENSTER_KEY_B]: "b",
|
|
124292
|
+
[FENSTER_KEY_C]: "c",
|
|
124293
|
+
[FENSTER_KEY_D]: "d",
|
|
124294
|
+
[FENSTER_KEY_E]: "e",
|
|
124295
|
+
[FENSTER_KEY_F]: "f",
|
|
124296
|
+
[FENSTER_KEY_G]: "g",
|
|
124297
|
+
[FENSTER_KEY_H]: "h",
|
|
124298
|
+
[FENSTER_KEY_I]: "i",
|
|
124299
|
+
[FENSTER_KEY_J]: "j",
|
|
124300
|
+
[FENSTER_KEY_K]: "k",
|
|
124301
|
+
[FENSTER_KEY_L]: "l",
|
|
124302
|
+
[FENSTER_KEY_M]: "m",
|
|
124303
|
+
[FENSTER_KEY_N]: "n",
|
|
124304
|
+
[FENSTER_KEY_O]: "o",
|
|
124305
|
+
[FENSTER_KEY_P]: "p",
|
|
124306
|
+
[FENSTER_KEY_Q]: "q",
|
|
124307
|
+
[FENSTER_KEY_R]: "r",
|
|
124308
|
+
[FENSTER_KEY_S]: "s",
|
|
124309
|
+
[FENSTER_KEY_T]: "t",
|
|
124310
|
+
[FENSTER_KEY_U]: "u",
|
|
124311
|
+
[FENSTER_KEY_V]: "v",
|
|
124312
|
+
[FENSTER_KEY_W]: "w",
|
|
124313
|
+
[FENSTER_KEY_X]: "x",
|
|
124314
|
+
[FENSTER_KEY_Y]: "y",
|
|
124315
|
+
[FENSTER_KEY_Z]: "z",
|
|
124316
|
+
// Digits (0-9)
|
|
124317
|
+
[FENSTER_KEY_0]: "0",
|
|
124318
|
+
[FENSTER_KEY_1]: "1",
|
|
124319
|
+
[FENSTER_KEY_2]: "2",
|
|
124320
|
+
[FENSTER_KEY_3]: "3",
|
|
124321
|
+
[FENSTER_KEY_4]: "4",
|
|
124322
|
+
[FENSTER_KEY_5]: "5",
|
|
124323
|
+
[FENSTER_KEY_6]: "6",
|
|
124324
|
+
[FENSTER_KEY_7]: "7",
|
|
124325
|
+
[FENSTER_KEY_8]: "8",
|
|
124326
|
+
[FENSTER_KEY_9]: "9",
|
|
124327
|
+
// Symbols (unshifted)
|
|
124328
|
+
[FENSTER_KEY_APOSTROPHE]: "'",
|
|
124329
|
+
[FENSTER_KEY_COMMA]: ",",
|
|
124330
|
+
[FENSTER_KEY_MINUS]: "-",
|
|
124331
|
+
[FENSTER_KEY_PERIOD]: ".",
|
|
124332
|
+
[FENSTER_KEY_SLASH]: "/",
|
|
124333
|
+
[FENSTER_KEY_SEMICOLON]: ";",
|
|
124334
|
+
[FENSTER_KEY_EQUAL]: "=",
|
|
124335
|
+
[FENSTER_KEY_BRACKET_LEFT]: "[",
|
|
124336
|
+
[FENSTER_KEY_BACKSLASH]: "\\",
|
|
124337
|
+
[FENSTER_KEY_BRACKET_RIGHT]: "]",
|
|
124338
|
+
[FENSTER_KEY_GRAVE]: "`",
|
|
124339
|
+
// Modifier keys (left/right)
|
|
124340
|
+
[FENSTER_KEY_SHIFT_LEFT]: "Shift",
|
|
124341
|
+
[FENSTER_KEY_SHIFT_RIGHT]: "Shift",
|
|
124342
|
+
[FENSTER_KEY_CONTROL_LEFT]: "Control",
|
|
124343
|
+
[FENSTER_KEY_CONTROL_RIGHT]: "Control",
|
|
124344
|
+
[FENSTER_KEY_ALT_LEFT]: "Alt",
|
|
124345
|
+
[FENSTER_KEY_ALT_RIGHT]: "Alt",
|
|
124346
|
+
[FENSTER_KEY_META_LEFT]: "Meta",
|
|
124347
|
+
[FENSTER_KEY_META_RIGHT]: "Meta"
|
|
124348
|
+
};
|
|
124349
|
+
var SHIFTED_KEY = {
|
|
124350
|
+
// Letters (a-z → A-Z)
|
|
124351
|
+
[FENSTER_KEY_A]: "A",
|
|
124352
|
+
[FENSTER_KEY_B]: "B",
|
|
124353
|
+
[FENSTER_KEY_C]: "C",
|
|
124354
|
+
[FENSTER_KEY_D]: "D",
|
|
124355
|
+
[FENSTER_KEY_E]: "E",
|
|
124356
|
+
[FENSTER_KEY_F]: "F",
|
|
124357
|
+
[FENSTER_KEY_G]: "G",
|
|
124358
|
+
[FENSTER_KEY_H]: "H",
|
|
124359
|
+
[FENSTER_KEY_I]: "I",
|
|
124360
|
+
[FENSTER_KEY_J]: "J",
|
|
124361
|
+
[FENSTER_KEY_K]: "K",
|
|
124362
|
+
[FENSTER_KEY_L]: "L",
|
|
124363
|
+
[FENSTER_KEY_M]: "M",
|
|
124364
|
+
[FENSTER_KEY_N]: "N",
|
|
124365
|
+
[FENSTER_KEY_O]: "O",
|
|
124366
|
+
[FENSTER_KEY_P]: "P",
|
|
124367
|
+
[FENSTER_KEY_Q]: "Q",
|
|
124368
|
+
[FENSTER_KEY_R]: "R",
|
|
124369
|
+
[FENSTER_KEY_S]: "S",
|
|
124370
|
+
[FENSTER_KEY_T]: "T",
|
|
124371
|
+
[FENSTER_KEY_U]: "U",
|
|
124372
|
+
[FENSTER_KEY_V]: "V",
|
|
124373
|
+
[FENSTER_KEY_W]: "W",
|
|
124374
|
+
[FENSTER_KEY_X]: "X",
|
|
124375
|
+
[FENSTER_KEY_Y]: "Y",
|
|
124376
|
+
[FENSTER_KEY_Z]: "Z",
|
|
124377
|
+
// Digits (0-9 → shifted symbols)
|
|
124378
|
+
[FENSTER_KEY_0]: ")",
|
|
124379
|
+
[FENSTER_KEY_1]: "!",
|
|
124380
|
+
[FENSTER_KEY_2]: "@",
|
|
124381
|
+
[FENSTER_KEY_3]: "#",
|
|
124382
|
+
[FENSTER_KEY_4]: "$",
|
|
124383
|
+
[FENSTER_KEY_5]: "%",
|
|
124384
|
+
[FENSTER_KEY_6]: "^",
|
|
124385
|
+
[FENSTER_KEY_7]: "&",
|
|
124386
|
+
[FENSTER_KEY_8]: "*",
|
|
124387
|
+
[FENSTER_KEY_9]: "(",
|
|
124388
|
+
// Symbols (shifted)
|
|
124389
|
+
[FENSTER_KEY_APOSTROPHE]: '"',
|
|
124390
|
+
[FENSTER_KEY_COMMA]: "<",
|
|
124391
|
+
[FENSTER_KEY_MINUS]: "_",
|
|
124392
|
+
[FENSTER_KEY_PERIOD]: ">",
|
|
124393
|
+
[FENSTER_KEY_SLASH]: "?",
|
|
124394
|
+
[FENSTER_KEY_SEMICOLON]: ":",
|
|
124395
|
+
[FENSTER_KEY_EQUAL]: "+",
|
|
124396
|
+
[FENSTER_KEY_BRACKET_LEFT]: "{",
|
|
124397
|
+
[FENSTER_KEY_BACKSLASH]: "|",
|
|
124398
|
+
[FENSTER_KEY_BRACKET_RIGHT]: "}",
|
|
124399
|
+
[FENSTER_KEY_GRAVE]: "~"
|
|
124400
|
+
};
|
|
124401
|
+
|
|
124402
|
+
// src/KeyboardEvent/types.ts
|
|
124403
|
+
import { isBoolean, isPlainObject, isString } from "remeda";
|
|
124404
|
+
var isNativeKeyboardEvent = (value) => {
|
|
124405
|
+
if (!isPlainObject(value)) {
|
|
124406
|
+
return false;
|
|
124407
|
+
}
|
|
124408
|
+
return isString(value["key"]) && isString(value["code"]) && isBoolean(value["ctrlKey"]) && isBoolean(value["shiftKey"]) && isBoolean(value["altKey"]) && isBoolean(value["metaKey"]) && value["repeat"] === false && (value["type"] === "keydown" || value["type"] === "keyup");
|
|
124409
|
+
};
|
|
124410
|
+
|
|
124411
|
+
// src/KeyboardEvent/index.ts
|
|
124412
|
+
var createKeyboardEvent = (event, mod) => {
|
|
124413
|
+
const code = FENSTER_TO_CODE[event.keyIndex];
|
|
124414
|
+
if (code === void 0) {
|
|
124415
|
+
return null;
|
|
124416
|
+
}
|
|
124417
|
+
const shiftKey = (mod & FENSTER_MOD_SHIFT) !== 0;
|
|
124418
|
+
const key = (shiftKey ? SHIFTED_KEY[event.keyIndex] : void 0) ?? FENSTER_TO_KEY[event.keyIndex];
|
|
124419
|
+
if (key === void 0) {
|
|
124420
|
+
return null;
|
|
124421
|
+
}
|
|
124422
|
+
return {
|
|
124423
|
+
key,
|
|
124424
|
+
code,
|
|
124425
|
+
ctrlKey: (mod & FENSTER_MOD_CTRL) !== 0,
|
|
124426
|
+
shiftKey,
|
|
124427
|
+
altKey: (mod & FENSTER_MOD_ALT) !== 0,
|
|
124428
|
+
metaKey: (mod & FENSTER_MOD_META) !== 0,
|
|
124429
|
+
repeat: false,
|
|
124430
|
+
type: event.pressed ? "keydown" : "keyup"
|
|
124431
|
+
};
|
|
124432
|
+
};
|
|
124433
|
+
|
|
124137
124434
|
// src/OutputStream/index.ts
|
|
124138
124435
|
import { Writable } from "stream";
|
|
124139
|
-
import { isString } from "remeda";
|
|
124436
|
+
import { isString as isString2 } from "remeda";
|
|
124140
124437
|
|
|
124141
124438
|
// src/OutputStream/consts.ts
|
|
124142
124439
|
var DEFAULT_COLUMNS = 80;
|
|
@@ -124178,7 +124475,7 @@ var OutputStream = class extends Writable {
|
|
|
124178
124475
|
*/
|
|
124179
124476
|
_write(chunk, _encoding, callback) {
|
|
124180
124477
|
try {
|
|
124181
|
-
const text =
|
|
124478
|
+
const text = isString2(chunk) ? chunk : chunk.toString("utf8");
|
|
124182
124479
|
this.uiRenderer.processAnsi(text);
|
|
124183
124480
|
this.uiRenderer.present();
|
|
124184
124481
|
callback(null);
|
|
@@ -124233,9 +124530,53 @@ var FENSTER_REFRESH_RATE = 60;
|
|
|
124233
124530
|
var ALPHA_MASK = 4278190080;
|
|
124234
124531
|
var RED_SHIFT = 16;
|
|
124235
124532
|
var GREEN_SHIFT = 8;
|
|
124236
|
-
var CTRL_KEY_OFFSET =
|
|
124533
|
+
var CTRL_KEY_OFFSET = 64;
|
|
124237
124534
|
var ASCII_PRINTABLE_START = 32;
|
|
124238
124535
|
var ASCII_PRINTABLE_END = 126;
|
|
124536
|
+
var SHIFTED_SYMBOLS = {
|
|
124537
|
+
48: ")",
|
|
124538
|
+
// 0 → )
|
|
124539
|
+
49: "!",
|
|
124540
|
+
// 1 → !
|
|
124541
|
+
50: "@",
|
|
124542
|
+
// 2 → @
|
|
124543
|
+
51: "#",
|
|
124544
|
+
// 3 → #
|
|
124545
|
+
52: "$",
|
|
124546
|
+
// 4 → $
|
|
124547
|
+
53: "%",
|
|
124548
|
+
// 5 → %
|
|
124549
|
+
54: "^",
|
|
124550
|
+
// 6 → ^
|
|
124551
|
+
55: "&",
|
|
124552
|
+
// 7 → &
|
|
124553
|
+
56: "*",
|
|
124554
|
+
// 8 → *
|
|
124555
|
+
57: "(",
|
|
124556
|
+
// 9 → (
|
|
124557
|
+
45: "_",
|
|
124558
|
+
// - → _
|
|
124559
|
+
61: "+",
|
|
124560
|
+
// = → +
|
|
124561
|
+
91: "{",
|
|
124562
|
+
// [ → {
|
|
124563
|
+
93: "}",
|
|
124564
|
+
// ] → }
|
|
124565
|
+
92: "|",
|
|
124566
|
+
// \ → |
|
|
124567
|
+
59: ":",
|
|
124568
|
+
// ; → :
|
|
124569
|
+
39: '"',
|
|
124570
|
+
// ' → "
|
|
124571
|
+
96: "~",
|
|
124572
|
+
// ` → ~
|
|
124573
|
+
44: "<",
|
|
124574
|
+
// , → <
|
|
124575
|
+
46: ">",
|
|
124576
|
+
// . → >
|
|
124577
|
+
47: "?"
|
|
124578
|
+
// / → ?
|
|
124579
|
+
};
|
|
124239
124580
|
|
|
124240
124581
|
// src/UiRenderer/index.ts
|
|
124241
124582
|
var packColor = (r, g, b) => (ALPHA_MASK | r << RED_SHIFT | g << GREEN_SHIFT | b) >>> 0;
|
|
@@ -124471,6 +124812,8 @@ var UiRenderer = class {
|
|
|
124471
124812
|
return shift ? "\x1B[Z" : " ";
|
|
124472
124813
|
case FENSTER_KEY_DELETE:
|
|
124473
124814
|
return "\x1B[3~";
|
|
124815
|
+
case FENSTER_KEY_INSERT:
|
|
124816
|
+
return "\x1B[2~";
|
|
124474
124817
|
case FENSTER_KEY_SPACE:
|
|
124475
124818
|
return ctrl ? "\0" : " ";
|
|
124476
124819
|
}
|
|
@@ -124489,11 +124832,16 @@ var UiRenderer = class {
|
|
|
124489
124832
|
}
|
|
124490
124833
|
}
|
|
124491
124834
|
if (key >= ASCII_PRINTABLE_START && key <= ASCII_PRINTABLE_END) {
|
|
124492
|
-
|
|
124493
|
-
|
|
124494
|
-
|
|
124835
|
+
if (key >= FENSTER_KEY_A && key <= FENSTER_KEY_Z) {
|
|
124836
|
+
return shift ? String.fromCharCode(key) : String.fromCharCode(key).toLowerCase();
|
|
124837
|
+
}
|
|
124838
|
+
if (shift) {
|
|
124839
|
+
const shifted = SHIFTED_SYMBOLS[key];
|
|
124840
|
+
if (shifted) {
|
|
124841
|
+
return shifted;
|
|
124842
|
+
}
|
|
124495
124843
|
}
|
|
124496
|
-
return
|
|
124844
|
+
return String.fromCharCode(key);
|
|
124497
124845
|
}
|
|
124498
124846
|
return null;
|
|
124499
124847
|
}
|
|
@@ -124771,22 +125119,36 @@ var Window = class extends EventEmitter {
|
|
|
124771
125119
|
}
|
|
124772
125120
|
const { keyEvents, mod, resized } = this.renderer.processEventsAndPresent();
|
|
124773
125121
|
for (const event of keyEvents) {
|
|
124774
|
-
const
|
|
124775
|
-
if (
|
|
124776
|
-
|
|
124777
|
-
|
|
124778
|
-
|
|
124779
|
-
|
|
124780
|
-
|
|
125122
|
+
const kbEvent = createKeyboardEvent(event, mod);
|
|
125123
|
+
if (event.pressed) {
|
|
125124
|
+
const sequence = this.renderer.keyEventToSequence(event, mod);
|
|
125125
|
+
if (sequence) {
|
|
125126
|
+
if (sequence === "") {
|
|
125127
|
+
if (this.listenerCount("sigint") > 0) {
|
|
125128
|
+
this.emit("sigint");
|
|
125129
|
+
} else {
|
|
125130
|
+
process.kill(process.pid, "SIGINT");
|
|
125131
|
+
}
|
|
125132
|
+
if (kbEvent) {
|
|
125133
|
+
this.emit("keydown", kbEvent);
|
|
125134
|
+
}
|
|
125135
|
+
continue;
|
|
124781
125136
|
}
|
|
124782
|
-
|
|
125137
|
+
if (!this.paused) {
|
|
125138
|
+
this.inputStream.pushKey(sequence);
|
|
125139
|
+
}
|
|
125140
|
+
}
|
|
125141
|
+
if (kbEvent) {
|
|
125142
|
+
this.emit("keydown", kbEvent);
|
|
124783
125143
|
}
|
|
124784
|
-
|
|
124785
|
-
this.emit("
|
|
125144
|
+
} else if (kbEvent) {
|
|
125145
|
+
this.emit("keyup", kbEvent);
|
|
124786
125146
|
}
|
|
124787
125147
|
}
|
|
124788
125148
|
if (resized) {
|
|
124789
|
-
this.
|
|
125149
|
+
if (!this.paused) {
|
|
125150
|
+
this.outputStream.notifyResize();
|
|
125151
|
+
}
|
|
124790
125152
|
this.emit("resize", this.renderer.getDimensions());
|
|
124791
125153
|
}
|
|
124792
125154
|
if (this.renderer.shouldClose()) {
|
|
@@ -124829,30 +125191,35 @@ var Window = class extends EventEmitter {
|
|
|
124829
125191
|
return this.closed;
|
|
124830
125192
|
}
|
|
124831
125193
|
/**
|
|
124832
|
-
*
|
|
125194
|
+
* Process pending events and present the framebuffer.
|
|
124833
125195
|
*
|
|
124834
|
-
*
|
|
124835
|
-
*
|
|
125196
|
+
* Call this from your own render loop when the event loop is paused.
|
|
125197
|
+
* Polls OS events, emits keydown/keyup, handles resize and close —
|
|
125198
|
+
* equivalent to what the internal event loop does each iteration.
|
|
125199
|
+
*/
|
|
125200
|
+
processEvents() {
|
|
125201
|
+
this.runEventLoopIteration();
|
|
125202
|
+
}
|
|
125203
|
+
/**
|
|
125204
|
+
* Pause Ink so the caller can take over rendering.
|
|
125205
|
+
*
|
|
125206
|
+
* The event loop keeps running — keydown, keyup, resize, and close
|
|
125207
|
+
* events continue to fire. Only Ink's input stream is paused.
|
|
124836
125208
|
*/
|
|
124837
125209
|
pause() {
|
|
124838
125210
|
if (this.paused) {
|
|
124839
125211
|
return;
|
|
124840
125212
|
}
|
|
124841
125213
|
this.paused = true;
|
|
124842
|
-
if (this.eventLoopHandle) {
|
|
124843
|
-
clearInterval(this.eventLoopHandle);
|
|
124844
|
-
this.eventLoopHandle = null;
|
|
124845
|
-
}
|
|
124846
125214
|
}
|
|
124847
125215
|
/**
|
|
124848
|
-
* Resume
|
|
125216
|
+
* Resume Ink after pausing.
|
|
124849
125217
|
*/
|
|
124850
125218
|
resume() {
|
|
124851
125219
|
if (!this.paused) {
|
|
124852
125220
|
return;
|
|
124853
125221
|
}
|
|
124854
125222
|
this.paused = false;
|
|
124855
|
-
this.startEventLoop();
|
|
124856
125223
|
}
|
|
124857
125224
|
/**
|
|
124858
125225
|
* Check if the event loop is paused
|
|
@@ -124907,6 +125274,8 @@ export {
|
|
|
124907
125274
|
getFenster,
|
|
124908
125275
|
isFensterAvailable,
|
|
124909
125276
|
InputStream,
|
|
125277
|
+
isNativeKeyboardEvent,
|
|
125278
|
+
createKeyboardEvent,
|
|
124910
125279
|
OutputStream,
|
|
124911
125280
|
packColor,
|
|
124912
125281
|
UiRenderer,
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -324,6 +324,36 @@ declare class InputStream extends Readable {
|
|
|
324
324
|
unref(): this;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/** Keyboard event from the native window */
|
|
328
|
+
interface NativeKeyboardEvent {
|
|
329
|
+
/** Key value: "a", "A", "Enter", "ArrowUp", "!" */
|
|
330
|
+
readonly key: string;
|
|
331
|
+
/** Physical key code: "KeyA", "Digit1", "ArrowUp" */
|
|
332
|
+
readonly code: string;
|
|
333
|
+
readonly ctrlKey: boolean;
|
|
334
|
+
readonly shiftKey: boolean;
|
|
335
|
+
readonly altKey: boolean;
|
|
336
|
+
readonly metaKey: boolean;
|
|
337
|
+
/** Always false — fenster only reports transitions, not held state */
|
|
338
|
+
readonly repeat: false;
|
|
339
|
+
readonly type: "keydown" | "keyup";
|
|
340
|
+
}
|
|
341
|
+
declare const isNativeKeyboardEvent: (value: unknown) => value is NativeKeyboardEvent;
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* KeyboardEvent
|
|
345
|
+
*
|
|
346
|
+
* Converts fenster key events into NativeKeyboardEvent objects,
|
|
347
|
+
* providing keydown/keyup events for games and interactive apps.
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Create a NativeKeyboardEvent from a fenster key event and modifier bitmask.
|
|
352
|
+
*
|
|
353
|
+
* Returns null for unmapped key indices.
|
|
354
|
+
*/
|
|
355
|
+
declare const createKeyboardEvent: (event: FensterKeyEvent, mod: number) => NativeKeyboardEvent | null;
|
|
356
|
+
|
|
327
357
|
/**
|
|
328
358
|
* OutputStream Types
|
|
329
359
|
*/
|
|
@@ -659,14 +689,22 @@ declare class Window extends EventEmitter {
|
|
|
659
689
|
*/
|
|
660
690
|
isClosed(): boolean;
|
|
661
691
|
/**
|
|
662
|
-
*
|
|
692
|
+
* Process pending events and present the framebuffer.
|
|
693
|
+
*
|
|
694
|
+
* Call this from your own render loop when the event loop is paused.
|
|
695
|
+
* Polls OS events, emits keydown/keyup, handles resize and close —
|
|
696
|
+
* equivalent to what the internal event loop does each iteration.
|
|
697
|
+
*/
|
|
698
|
+
processEvents(): void;
|
|
699
|
+
/**
|
|
700
|
+
* Pause Ink so the caller can take over rendering.
|
|
663
701
|
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
702
|
+
* The event loop keeps running — keydown, keyup, resize, and close
|
|
703
|
+
* events continue to fire. Only Ink's input stream is paused.
|
|
666
704
|
*/
|
|
667
705
|
pause(): void;
|
|
668
706
|
/**
|
|
669
|
-
* Resume
|
|
707
|
+
* Resume Ink after pausing.
|
|
670
708
|
*/
|
|
671
709
|
resume(): void;
|
|
672
710
|
/**
|
|
@@ -711,4 +749,4 @@ declare class Window extends EventEmitter {
|
|
|
711
749
|
*/
|
|
712
750
|
declare const createStreams: (options?: StreamsOptions) => Streams;
|
|
713
751
|
|
|
714
|
-
export { AnsiParser, BitmapFontRenderer, type Color, type DrawCommand, Fenster, type FensterKeyEvent, type FensterPointer, type Framebuffer, InputStream, OutputStream, type ProcessEventsResult, type Streams, type StreamsOptions, UiRenderer, type UiRendererOptions, Window, createStreams, getFenster, isFensterAvailable, packColor };
|
|
752
|
+
export { AnsiParser, BitmapFontRenderer, type Color, type DrawCommand, Fenster, type FensterKeyEvent, type FensterPointer, type Framebuffer, InputStream, type NativeKeyboardEvent, OutputStream, type ProcessEventsResult, type Streams, type StreamsOptions, UiRenderer, type UiRendererOptions, Window, createKeyboardEvent, createStreams, getFenster, isFensterAvailable, isNativeKeyboardEvent, packColor };
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
OutputStream,
|
|
7
7
|
UiRenderer,
|
|
8
8
|
Window,
|
|
9
|
+
createKeyboardEvent,
|
|
9
10
|
createStreams,
|
|
10
11
|
getFenster,
|
|
11
12
|
isFensterAvailable,
|
|
13
|
+
isNativeKeyboardEvent,
|
|
12
14
|
packColor
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-FE3HR4I4.js";
|
|
14
16
|
export {
|
|
15
17
|
AnsiParser,
|
|
16
18
|
BitmapFontRenderer,
|
|
@@ -19,8 +21,10 @@ export {
|
|
|
19
21
|
OutputStream,
|
|
20
22
|
UiRenderer,
|
|
21
23
|
Window,
|
|
24
|
+
createKeyboardEvent,
|
|
22
25
|
createStreams,
|
|
23
26
|
getFenster,
|
|
24
27
|
isFensterAvailable,
|
|
28
|
+
isNativeKeyboardEvent,
|
|
25
29
|
packColor
|
|
26
30
|
};
|
package/native/fenster.dylib
CHANGED
|
Binary file
|
package/native/fenster.h
CHANGED
|
@@ -172,6 +172,24 @@ FENSTER_API int fenster_loop(struct fenster *f) {
|
|
|
172
172
|
f->mod = (mod & 0xc) | ((mod & 1) << 1) | ((mod >> 1) & 1);
|
|
173
173
|
return 0;
|
|
174
174
|
}
|
|
175
|
+
case 12: { /* NSEventTypeFlagsChanged — modifier key press/release */
|
|
176
|
+
NSUInteger k = msg(NSUInteger, ev, "keyCode");
|
|
177
|
+
NSUInteger flags = msg(NSUInteger, ev, "modifierFlags");
|
|
178
|
+
NSUInteger mod = flags >> 17;
|
|
179
|
+
f->mod = (mod & 0xc) | ((mod & 1) << 1) | ((mod >> 1) & 1);
|
|
180
|
+
/* Device-dependent flags distinguish left/right modifiers */
|
|
181
|
+
switch (k) {
|
|
182
|
+
case 56: f->keys[128] = !!(flags & 0x00000002); break; /* Left Shift */
|
|
183
|
+
case 60: f->keys[129] = !!(flags & 0x00000004); break; /* Right Shift */
|
|
184
|
+
case 59: f->keys[130] = !!(flags & 0x00000001); break; /* Left Control */
|
|
185
|
+
case 62: f->keys[131] = !!(flags & 0x00002000); break; /* Right Control */
|
|
186
|
+
case 58: f->keys[132] = !!(flags & 0x00000020); break; /* Left Alt */
|
|
187
|
+
case 61: f->keys[133] = !!(flags & 0x00000040); break; /* Right Alt */
|
|
188
|
+
case 55: f->keys[134] = !!(flags & 0x00000008); break; /* Left Meta */
|
|
189
|
+
case 54: f->keys[135] = !!(flags & 0x00000010); break; /* Right Meta */
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
175
193
|
}
|
|
176
194
|
msg1(void, NSApp, "sendEvent:", id, ev);
|
|
177
195
|
/* Poll content view frame for resize */
|
|
@@ -240,7 +258,17 @@ static LRESULT CALLBACK fenster_wndproc(HWND hwnd, UINT msg, WPARAM wParam,
|
|
|
240
258
|
((GetKeyState(VK_SHIFT) & 0x8000) >> 14) |
|
|
241
259
|
((GetKeyState(VK_MENU) & 0x8000) >> 13) |
|
|
242
260
|
(((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) >> 12);
|
|
243
|
-
|
|
261
|
+
int pressed = !((lParam >> 31) & 1);
|
|
262
|
+
f->keys[FENSTER_KEYCODES[HIWORD(lParam) & 0x1ff]] = pressed;
|
|
263
|
+
/* Left/right modifier key tracking */
|
|
264
|
+
f->keys[128] = !!(GetKeyState(VK_LSHIFT) & 0x8000);
|
|
265
|
+
f->keys[129] = !!(GetKeyState(VK_RSHIFT) & 0x8000);
|
|
266
|
+
f->keys[130] = !!(GetKeyState(VK_LCONTROL) & 0x8000);
|
|
267
|
+
f->keys[131] = !!(GetKeyState(VK_RCONTROL) & 0x8000);
|
|
268
|
+
f->keys[132] = !!(GetKeyState(VK_LMENU) & 0x8000);
|
|
269
|
+
f->keys[133] = !!(GetKeyState(VK_RMENU) & 0x8000);
|
|
270
|
+
f->keys[134] = !!(GetKeyState(VK_LWIN) & 0x8000);
|
|
271
|
+
f->keys[135] = !!(GetKeyState(VK_RWIN) & 0x8000);
|
|
244
272
|
} break;
|
|
245
273
|
case WM_SIZE:
|
|
246
274
|
f->width = LOWORD(lParam);
|
|
@@ -320,7 +348,7 @@ FENSTER_API int fenster_loop(struct fenster *f) {
|
|
|
320
348
|
}
|
|
321
349
|
#else
|
|
322
350
|
// clang-format off
|
|
323
|
-
static int FENSTER_KEYCODES[
|
|
351
|
+
static int FENSTER_KEYCODES[140] = {XK_BackSpace,8,XK_Delete,127,XK_Down,18,XK_End,5,XK_Escape,27,XK_Home,2,XK_Insert,26,XK_Left,20,XK_Page_Down,4,XK_Page_Up,3,XK_Return,10,XK_Right,19,XK_Tab,9,XK_Up,17,XK_apostrophe,39,XK_backslash,92,XK_bracketleft,91,XK_bracketright,93,XK_comma,44,XK_equal,61,XK_grave,96,XK_minus,45,XK_period,46,XK_semicolon,59,XK_slash,47,XK_space,32,XK_a,65,XK_b,66,XK_c,67,XK_d,68,XK_e,69,XK_f,70,XK_g,71,XK_h,72,XK_i,73,XK_j,74,XK_k,75,XK_l,76,XK_m,77,XK_n,78,XK_o,79,XK_p,80,XK_q,81,XK_r,82,XK_s,83,XK_t,84,XK_u,85,XK_v,86,XK_w,87,XK_x,88,XK_y,89,XK_z,90,XK_0,48,XK_1,49,XK_2,50,XK_3,51,XK_4,52,XK_5,53,XK_6,54,XK_7,55,XK_8,56,XK_9,57,XK_Shift_L,128,XK_Shift_R,129,XK_Control_L,130,XK_Control_R,131,XK_Alt_L,132,XK_Alt_R,133,XK_Super_L,134,XK_Super_R,135};
|
|
324
352
|
// clang-format on
|
|
325
353
|
FENSTER_API int fenster_open(struct fenster *f) {
|
|
326
354
|
f->dpy = XOpenDisplay(NULL);
|
|
@@ -363,7 +391,7 @@ FENSTER_API int fenster_loop(struct fenster *f) {
|
|
|
363
391
|
case KeyRelease: {
|
|
364
392
|
int m = ev.xkey.state;
|
|
365
393
|
int k = XkbKeycodeToKeysym(f->dpy, ev.xkey.keycode, 0, 0);
|
|
366
|
-
for (unsigned int i = 0; i <
|
|
394
|
+
for (unsigned int i = 0; i < 140; i += 2) {
|
|
367
395
|
if (FENSTER_KEYCODES[i] == k) {
|
|
368
396
|
f->keys[FENSTER_KEYCODES[i + 1]] = (ev.type == KeyPress);
|
|
369
397
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ink-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Render Ink terminal apps in a native window",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
"./native/*": "./native/*"
|
|
13
14
|
},
|
|
14
15
|
"bin": {
|
|
15
16
|
"ink-native": "./dist/cli.js"
|