@gajae-code/tui 0.6.2 → 0.6.4
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/types/components/editor.d.ts +1 -0
- package/package.json +3 -3
- package/src/components/editor.ts +13 -2
- package/src/keys.ts +17 -2
|
@@ -38,6 +38,7 @@ export declare class Editor implements Component, Focusable {
|
|
|
38
38
|
onAltEnter?: (text: string) => void;
|
|
39
39
|
onChange?: (text: string) => void;
|
|
40
40
|
onAutocompleteCancel?: () => void;
|
|
41
|
+
onTabDeclined?: (text: string) => void;
|
|
41
42
|
disableSubmit: boolean;
|
|
42
43
|
constructor(theme: EditorTheme);
|
|
43
44
|
setAutocompleteProvider(provider: AutocompleteProvider): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/tui",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.4",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"fmt": "biome format --write ."
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@gajae-code/natives": "0.6.
|
|
42
|
-
"@gajae-code/utils": "0.6.
|
|
41
|
+
"@gajae-code/natives": "0.6.4",
|
|
42
|
+
"@gajae-code/utils": "0.6.4",
|
|
43
43
|
"lru-cache": "11.3.6",
|
|
44
44
|
"marked": "^18.0.3"
|
|
45
45
|
},
|
package/src/components/editor.ts
CHANGED
|
@@ -447,6 +447,7 @@ export class Editor implements Component, Focusable {
|
|
|
447
447
|
onAltEnter?: (text: string) => void;
|
|
448
448
|
onChange?: (text: string) => void;
|
|
449
449
|
onAutocompleteCancel?: () => void;
|
|
450
|
+
onTabDeclined?: (text: string) => void;
|
|
450
451
|
disableSubmit: boolean = false;
|
|
451
452
|
|
|
452
453
|
// Custom top border (for status line integration)
|
|
@@ -1244,11 +1245,12 @@ export class Editor implements Component, Focusable {
|
|
|
1244
1245
|
else if (
|
|
1245
1246
|
(data.charCodeAt(0) === 10 && data.length > 1) || // Ctrl+Enter with modifiers
|
|
1246
1247
|
matchesKey(data, "ctrl+enter") || // Ctrl+Enter (Kitty/modifyOtherKeys, including lock bits/keypad Enter)
|
|
1248
|
+
matchesKey(data, "ctrl+shift+enter") || // Ctrl+Shift+Enter (Kitty/modifyOtherKeys combined modifier)
|
|
1247
1249
|
data === "\x1b\r" || // Option+Enter in some terminals (legacy)
|
|
1248
1250
|
data === "\x1b[13;2~" || // Shift+Enter in some terminals (legacy format)
|
|
1249
1251
|
kb.matches(data, "tui.input.newLine") || // Shift+Enter (Kitty protocol, handles lock bits)
|
|
1250
1252
|
(data.length > 1 && data.includes("\x1b") && data.includes("\r")) ||
|
|
1251
|
-
(data === "\n" && data.length === 1) // Shift+Enter from iTerm2 mapping
|
|
1253
|
+
(data === "\n" && data.length === 1 && process.platform !== "win32") // Shift+Enter from iTerm2 mapping
|
|
1252
1254
|
) {
|
|
1253
1255
|
if (this.#shouldSubmitOnBackslashEnter(data, kb)) {
|
|
1254
1256
|
this.#handleBackspace();
|
|
@@ -2612,6 +2614,9 @@ export class Editor implements Component, Focusable {
|
|
|
2612
2614
|
|
|
2613
2615
|
// Autocomplete methods
|
|
2614
2616
|
async #tryTriggerAutocomplete(explicitTab: boolean = false): Promise<void> {
|
|
2617
|
+
const declineExplicitTab = (): void => {
|
|
2618
|
+
if (explicitTab) this.onTabDeclined?.(this.getText());
|
|
2619
|
+
};
|
|
2615
2620
|
if (!this.#autocompleteProvider) return;
|
|
2616
2621
|
// Check if we should trigger file completion on Tab
|
|
2617
2622
|
if (explicitTab) {
|
|
@@ -2620,6 +2625,7 @@ export class Editor implements Component, Focusable {
|
|
|
2620
2625
|
!provider.shouldTriggerFileCompletion ||
|
|
2621
2626
|
provider.shouldTriggerFileCompletion(this.#state.lines, this.#state.cursorLine, this.#state.cursorCol);
|
|
2622
2627
|
if (!shouldTrigger) {
|
|
2628
|
+
declineExplicitTab();
|
|
2623
2629
|
return;
|
|
2624
2630
|
}
|
|
2625
2631
|
}
|
|
@@ -2641,6 +2647,7 @@ export class Editor implements Component, Focusable {
|
|
|
2641
2647
|
} else {
|
|
2642
2648
|
this.#cancelAutocomplete();
|
|
2643
2649
|
this.onAutocompleteUpdate?.();
|
|
2650
|
+
declineExplicitTab();
|
|
2644
2651
|
}
|
|
2645
2652
|
}
|
|
2646
2653
|
#createAutocompleteList(
|
|
@@ -2655,7 +2662,10 @@ export class Editor implements Component, Focusable {
|
|
|
2655
2662
|
}
|
|
2656
2663
|
|
|
2657
2664
|
#handleTabCompletion(): void {
|
|
2658
|
-
if (!this.#autocompleteProvider)
|
|
2665
|
+
if (!this.#autocompleteProvider) {
|
|
2666
|
+
this.onTabDeclined?.(this.getText());
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2659
2669
|
|
|
2660
2670
|
const currentLine = this.#state.lines[this.#state.cursorLine] || "";
|
|
2661
2671
|
const beforeCursor = currentLine.slice(0, this.#state.cursorCol);
|
|
@@ -2726,6 +2736,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
|
|
|
2726
2736
|
} else {
|
|
2727
2737
|
this.#cancelAutocomplete();
|
|
2728
2738
|
this.onAutocompleteUpdate?.();
|
|
2739
|
+
if (explicitTab) this.onTabDeclined?.(this.getText());
|
|
2729
2740
|
}
|
|
2730
2741
|
}
|
|
2731
2742
|
|
package/src/keys.ts
CHANGED
|
@@ -305,6 +305,7 @@ const KITTY_MOD_SUPER = 8;
|
|
|
305
305
|
const KITTY_MOD_NUM_LOCK = 128;
|
|
306
306
|
const KITTY_LOCK_MASK = 64 + 128; // Caps Lock + Num Lock
|
|
307
307
|
const MODIFY_OTHER_KEYS_PATTERN = /^\x1b\[27;(\d+);(\d+)~$/;
|
|
308
|
+
const PSMUX_MODIFIED_ENTER_PATTERN = /^\x1b\[13;(2|6)~$/;
|
|
308
309
|
const KITTY_KEYPAD_OPERATOR_TEXT: Record<number, string> = {
|
|
309
310
|
57410: "/",
|
|
310
311
|
57411: "*",
|
|
@@ -378,6 +379,20 @@ export function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
|
378
379
|
};
|
|
379
380
|
}
|
|
380
381
|
|
|
382
|
+
function parsePsmuxModifiedEnter(data: string): string | undefined {
|
|
383
|
+
const match = data.match(PSMUX_MODIFIED_ENTER_PATTERN);
|
|
384
|
+
if (!match) return undefined;
|
|
385
|
+
return match[1] === "6" ? "shift+ctrl+enter" : "shift+enter";
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function matchesPsmuxModifiedEnter(data: string, keyId: KeyId): boolean {
|
|
389
|
+
const parsed = parsePsmuxModifiedEnter(data);
|
|
390
|
+
if (!parsed) return false;
|
|
391
|
+
const expected = String(keyId);
|
|
392
|
+
if (parsed === expected) return true;
|
|
393
|
+
return parsed === "shift+ctrl+enter" && expected === "ctrl+shift+enter";
|
|
394
|
+
}
|
|
395
|
+
|
|
381
396
|
function hasControlChars(data: string): boolean {
|
|
382
397
|
return [...data].some(ch => {
|
|
383
398
|
const code = ch.charCodeAt(0);
|
|
@@ -521,7 +536,7 @@ export function decodePrintableKey(data: string): string | undefined {
|
|
|
521
536
|
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
522
537
|
*/
|
|
523
538
|
export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
524
|
-
return matchesKeyNative(data, keyId, kittyProtocolActive);
|
|
539
|
+
return matchesPsmuxModifiedEnter(data, keyId) || matchesKeyNative(data, keyId, kittyProtocolActive);
|
|
525
540
|
}
|
|
526
541
|
|
|
527
542
|
/**
|
|
@@ -533,5 +548,5 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
533
548
|
* @param data - Raw input data from terminal
|
|
534
549
|
*/
|
|
535
550
|
export function parseKey(data: string): string | undefined {
|
|
536
|
-
return parseKeyNative(data, kittyProtocolActive) ?? undefined;
|
|
551
|
+
return parsePsmuxModifiedEnter(data) ?? parseKeyNative(data, kittyProtocolActive) ?? undefined;
|
|
537
552
|
}
|