xgen-dex-cli 1.7.1 → 1.7.2
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.
|
@@ -288,7 +288,7 @@ import { Box as Box2, Text as Text2 } from "ink";
|
|
|
288
288
|
|
|
289
289
|
// src/tui/ime-text-input.tsx
|
|
290
290
|
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
291
|
-
import { Box, Text, useCursor, useInput
|
|
291
|
+
import { Box, Text, useCursor, useInput } from "ink";
|
|
292
292
|
import stringWidth2 from "string-width";
|
|
293
293
|
|
|
294
294
|
// src/tui/terminal-input.ts
|
|
@@ -504,67 +504,6 @@ function backspace(session) {
|
|
|
504
504
|
};
|
|
505
505
|
}
|
|
506
506
|
|
|
507
|
-
// src/tui/kitty.ts
|
|
508
|
-
var ESC = "\x1B";
|
|
509
|
-
var QUERY = `${ESC}[?u`;
|
|
510
|
-
var KEY_CODES = {
|
|
511
|
-
57358: "capslock",
|
|
512
|
-
57449: "rightalt"
|
|
513
|
-
};
|
|
514
|
-
var CAPS_LOCK_BIT = 64;
|
|
515
|
-
function parseKeyEvents(chunk) {
|
|
516
|
-
const events = [];
|
|
517
|
-
const pattern = /\u001B\[(\d+)(?::\d+)*(?:;(\d+)(?::(\d+))?)?(?:;[\d:]*)?u/g;
|
|
518
|
-
for (const match of chunk.matchAll(pattern)) {
|
|
519
|
-
const code = Number(match[1]);
|
|
520
|
-
const modifiers = match[2] ? Number(match[2]) - 1 : 0;
|
|
521
|
-
events.push({
|
|
522
|
-
name: KEY_CODES[code],
|
|
523
|
-
// eslint-disable-next-line no-bitwise
|
|
524
|
-
capsLock: (modifiers & CAPS_LOCK_BIT) !== 0,
|
|
525
|
-
eventType: match[3] ? Number(match[3]) : 1
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
return events;
|
|
529
|
-
}
|
|
530
|
-
function knownKittyTerminal(env = process.env) {
|
|
531
|
-
if (env.KITTY_WINDOW_ID) return true;
|
|
532
|
-
if (env.GHOSTTY_RESOURCES_DIR) return true;
|
|
533
|
-
const term = (env.TERM ?? "").toLowerCase();
|
|
534
|
-
if (term === "xterm-kitty" || term === "xterm-ghostty" || term.includes("foot")) return true;
|
|
535
|
-
const program = (env.TERM_PROGRAM ?? "").toLowerCase();
|
|
536
|
-
return program === "wezterm" || program === "ghostty" || program === "kitty";
|
|
537
|
-
}
|
|
538
|
-
async function supportsKittyKeyboard(streams, timeoutMs = 200, env = process.env) {
|
|
539
|
-
if (env.DEX_NO_KITTY === "1") return false;
|
|
540
|
-
if (!streams.stdout.isTTY || !streams.isTTY) return false;
|
|
541
|
-
if (knownKittyTerminal(env)) return true;
|
|
542
|
-
const wasRaw = streams.stdin.isRaw === true;
|
|
543
|
-
const wasPaused = !wasRaw;
|
|
544
|
-
streams.stdin.setRawMode?.(true);
|
|
545
|
-
streams.stdin.resume?.();
|
|
546
|
-
return new Promise((resolve) => {
|
|
547
|
-
let settled = false;
|
|
548
|
-
let seen = "";
|
|
549
|
-
const finish = (supported) => {
|
|
550
|
-
if (settled) return;
|
|
551
|
-
settled = true;
|
|
552
|
-
clearTimeout(timer);
|
|
553
|
-
streams.stdin.removeListener("data", onData);
|
|
554
|
-
if (!wasRaw) streams.stdin.setRawMode?.(false);
|
|
555
|
-
if (wasPaused) streams.stdin.pause?.();
|
|
556
|
-
resolve(supported);
|
|
557
|
-
};
|
|
558
|
-
const onData = (data) => {
|
|
559
|
-
seen += typeof data === "string" ? data : data.toString("utf8");
|
|
560
|
-
if (/\u001B\[\?\d*u/.test(seen)) finish(true);
|
|
561
|
-
};
|
|
562
|
-
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
563
|
-
streams.stdin.on("data", onData);
|
|
564
|
-
streams.stdout.write(QUERY);
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
|
|
568
507
|
// src/tui/ime-text-input.tsx
|
|
569
508
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
570
509
|
var segmenter = new Intl.Segmenter("ko", { granularity: "grapheme" });
|
|
@@ -628,22 +567,6 @@ function ImeTextInput(props) {
|
|
|
628
567
|
moveCursor(nextCursor, segments.length);
|
|
629
568
|
props.onChange(nextValue);
|
|
630
569
|
};
|
|
631
|
-
const { stdin: stdin2, isRawModeSupported } = useStdin();
|
|
632
|
-
const onModeKeyRef = useRef2(void 0);
|
|
633
|
-
onModeKeyRef.current = () => props.onHangulModeChange?.(!props.hangulMode);
|
|
634
|
-
useEffect2(() => {
|
|
635
|
-
if (!props.focus || !isRawModeSupported) return void 0;
|
|
636
|
-
const onData = (data) => {
|
|
637
|
-
const chunk = typeof data === "string" ? data : data.toString("utf8");
|
|
638
|
-
for (const event of parseKeyEvents(chunk)) {
|
|
639
|
-
typingRef.current = { ...typingRef.current, capsLock: event.capsLock };
|
|
640
|
-
if (event.eventType !== 1) continue;
|
|
641
|
-
if (event.name === "rightalt" || event.name === "capslock") onModeKeyRef.current?.();
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
stdin2?.on("data", onData);
|
|
645
|
-
return () => void stdin2?.off("data", onData);
|
|
646
|
-
}, [props.focus, isRawModeSupported, stdin2]);
|
|
647
570
|
useEffect2(() => {
|
|
648
571
|
if (!props.focus || !props.hangulMode) {
|
|
649
572
|
typingRef.current = { ...IDLE, capsLock: typingRef.current.capsLock };
|
|
@@ -1115,6 +1038,15 @@ function Dashboard(props) {
|
|
|
1115
1038
|
setHangulMode(enabled);
|
|
1116
1039
|
props.preferences?.onHangulModeChange?.(enabled);
|
|
1117
1040
|
};
|
|
1041
|
+
useEffect6(
|
|
1042
|
+
() => props.preferences?.onModeKey?.(
|
|
1043
|
+
() => setHangulMode((current) => {
|
|
1044
|
+
props.preferences?.onHangulModeChange?.(!current);
|
|
1045
|
+
return !current;
|
|
1046
|
+
})
|
|
1047
|
+
),
|
|
1048
|
+
[props.preferences]
|
|
1049
|
+
);
|
|
1118
1050
|
const viewport = useRef3({ lineCount: 0, height: 0 });
|
|
1119
1051
|
const [starting, setStarting] = useState7(false);
|
|
1120
1052
|
const controller = useRef3(null);
|
|
@@ -1770,25 +1702,25 @@ function RetryInput({ onRetry }) {
|
|
|
1770
1702
|
}
|
|
1771
1703
|
|
|
1772
1704
|
// src/tui/screen.ts
|
|
1773
|
-
var
|
|
1774
|
-
var ENTER_ALT_SCREEN = `${
|
|
1775
|
-
var LEAVE_ALT_SCREEN = `${
|
|
1776
|
-
var SHOW_CURSOR = `${
|
|
1705
|
+
var ESC = "\x1B";
|
|
1706
|
+
var ENTER_ALT_SCREEN = `${ESC}[?1049h`;
|
|
1707
|
+
var LEAVE_ALT_SCREEN = `${ESC}[?1049l`;
|
|
1708
|
+
var SHOW_CURSOR = `${ESC}[?25h`;
|
|
1777
1709
|
var DISABLE_REPORTS = [
|
|
1778
|
-
`${
|
|
1710
|
+
`${ESC}[?1004l`,
|
|
1779
1711
|
// 포커스 들어옴/나감
|
|
1780
|
-
`${
|
|
1712
|
+
`${ESC}[?1000l`,
|
|
1781
1713
|
// 마우스 클릭
|
|
1782
|
-
`${
|
|
1714
|
+
`${ESC}[?1002l`,
|
|
1783
1715
|
// 마우스 드래그
|
|
1784
|
-
`${
|
|
1716
|
+
`${ESC}[?1003l`,
|
|
1785
1717
|
// 마우스 이동 전부
|
|
1786
|
-
`${
|
|
1718
|
+
`${ESC}[?1006l`
|
|
1787
1719
|
// SGR 확장 좌표
|
|
1788
1720
|
].join("");
|
|
1789
|
-
var ENABLE_BRACKETED_PASTE = `${
|
|
1790
|
-
var DISABLE_BRACKETED_PASTE = `${
|
|
1791
|
-
var POP_KITTY_KEYBOARD = `${
|
|
1721
|
+
var ENABLE_BRACKETED_PASTE = `${ESC}[?2004h`;
|
|
1722
|
+
var DISABLE_BRACKETED_PASTE = `${ESC}[?2004l`;
|
|
1723
|
+
var POP_KITTY_KEYBOARD = `${ESC}[<u`;
|
|
1792
1724
|
function createScreenGuard(stream, options = {}) {
|
|
1793
1725
|
const alt = Boolean(stream.isTTY);
|
|
1794
1726
|
let entered = false;
|
|
@@ -1841,6 +1773,131 @@ async function writePreferences(preferences, env = process.env) {
|
|
|
1841
1773
|
}
|
|
1842
1774
|
}
|
|
1843
1775
|
|
|
1776
|
+
// src/tui/kitty.ts
|
|
1777
|
+
var ESC2 = "\x1B";
|
|
1778
|
+
var QUERY = `${ESC2}[?u`;
|
|
1779
|
+
var KEY_CODES = {
|
|
1780
|
+
57358: "capslock",
|
|
1781
|
+
57449: "rightalt"
|
|
1782
|
+
};
|
|
1783
|
+
var CAPS_LOCK_BIT = 64;
|
|
1784
|
+
function parseKeyEvents(chunk) {
|
|
1785
|
+
const events = [];
|
|
1786
|
+
const pattern = /\u001B\[(\d+)(?::\d+)*(?:;(\d+)(?::(\d+))?)?(?:;[\d:]*)?u/g;
|
|
1787
|
+
for (const match of chunk.matchAll(pattern)) {
|
|
1788
|
+
const code = Number(match[1]);
|
|
1789
|
+
const modifiers = match[2] ? Number(match[2]) - 1 : 0;
|
|
1790
|
+
events.push({
|
|
1791
|
+
name: KEY_CODES[code],
|
|
1792
|
+
// eslint-disable-next-line no-bitwise
|
|
1793
|
+
capsLock: (modifiers & CAPS_LOCK_BIT) !== 0,
|
|
1794
|
+
eventType: match[3] ? Number(match[3]) : 1
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
return events;
|
|
1798
|
+
}
|
|
1799
|
+
function knownKittyTerminal(env = process.env) {
|
|
1800
|
+
if (env.KITTY_WINDOW_ID) return true;
|
|
1801
|
+
if (env.GHOSTTY_RESOURCES_DIR) return true;
|
|
1802
|
+
const term = (env.TERM ?? "").toLowerCase();
|
|
1803
|
+
if (term === "xterm-kitty" || term === "xterm-ghostty" || term.includes("foot")) return true;
|
|
1804
|
+
const program = (env.TERM_PROGRAM ?? "").toLowerCase();
|
|
1805
|
+
return program === "wezterm" || program === "ghostty" || program === "kitty";
|
|
1806
|
+
}
|
|
1807
|
+
async function supportsKittyKeyboard(streams, timeoutMs = 200, env = process.env) {
|
|
1808
|
+
if (env.DEX_NO_KITTY === "1") return false;
|
|
1809
|
+
if (!streams.stdout.isTTY || !streams.isTTY) return false;
|
|
1810
|
+
if (knownKittyTerminal(env)) return true;
|
|
1811
|
+
const wasRaw = streams.stdin.isRaw === true;
|
|
1812
|
+
const wasPaused = !wasRaw;
|
|
1813
|
+
streams.stdin.setRawMode?.(true);
|
|
1814
|
+
streams.stdin.resume?.();
|
|
1815
|
+
return new Promise((resolve) => {
|
|
1816
|
+
let settled = false;
|
|
1817
|
+
let seen = "";
|
|
1818
|
+
const finish = (supported) => {
|
|
1819
|
+
if (settled) return;
|
|
1820
|
+
settled = true;
|
|
1821
|
+
clearTimeout(timer);
|
|
1822
|
+
streams.stdin.removeListener("data", onData);
|
|
1823
|
+
if (!wasRaw) streams.stdin.setRawMode?.(false);
|
|
1824
|
+
if (wasPaused) streams.stdin.pause?.();
|
|
1825
|
+
resolve(supported);
|
|
1826
|
+
};
|
|
1827
|
+
const onData = (data) => {
|
|
1828
|
+
seen += typeof data === "string" ? data : data.toString("utf8");
|
|
1829
|
+
if (/\u001B\[\?\d*u/.test(seen)) finish(true);
|
|
1830
|
+
};
|
|
1831
|
+
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
1832
|
+
streams.stdin.on("data", onData);
|
|
1833
|
+
streams.stdout.write(QUERY);
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
var KEY_EVENT = /\u001B\[([0-9]+)(?::[0-9:]*)?(?:;([0-9]*(?::[0-9]+)?))?(?:;([0-9:]+))?u/g;
|
|
1837
|
+
function normalizeKeyEvents(chunk) {
|
|
1838
|
+
return chunk.replace(
|
|
1839
|
+
KEY_EVENT,
|
|
1840
|
+
(all, code, modifiers, text) => {
|
|
1841
|
+
const hasAlternate = all.slice(0, all.indexOf(";") === -1 ? all.length : all.indexOf(";")).includes(":");
|
|
1842
|
+
const needsModifier = modifiers !== void 0 && modifiers.length === 0;
|
|
1843
|
+
if (!hasAlternate && !needsModifier) return all;
|
|
1844
|
+
const mods = modifiers === void 0 ? void 0 : modifiers.length > 0 ? modifiers : "1";
|
|
1845
|
+
const fields = [code, mods, text].filter((field) => field !== void 0).join(";");
|
|
1846
|
+
return `\x1B[${fields}u`;
|
|
1847
|
+
}
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
var PARTIAL_TAIL = /\u001B\[[0-9:;]*$/;
|
|
1851
|
+
var MAX_PARTIAL = 64;
|
|
1852
|
+
function createNormalizer() {
|
|
1853
|
+
let pending = "";
|
|
1854
|
+
return (chunk) => {
|
|
1855
|
+
const combined = pending + chunk;
|
|
1856
|
+
const match = PARTIAL_TAIL.exec(combined);
|
|
1857
|
+
if (match && combined.length - match.index <= MAX_PARTIAL) {
|
|
1858
|
+
pending = combined.slice(match.index);
|
|
1859
|
+
return normalizeKeyEvents(combined.slice(0, match.index));
|
|
1860
|
+
}
|
|
1861
|
+
pending = "";
|
|
1862
|
+
return normalizeKeyEvents(combined);
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
function normalizedStdin(stdin2, onSpecialKey) {
|
|
1866
|
+
const normalize = createNormalizer();
|
|
1867
|
+
return new Proxy(stdin2, {
|
|
1868
|
+
get(target, property, receiver) {
|
|
1869
|
+
if (property === "read") {
|
|
1870
|
+
return (...args) => {
|
|
1871
|
+
const chunk = target.read(...args);
|
|
1872
|
+
if (chunk === null || chunk === void 0) return null;
|
|
1873
|
+
const text = typeof chunk === "string" ? chunk : String(chunk);
|
|
1874
|
+
if (onSpecialKey) {
|
|
1875
|
+
for (const event of parseKeyEvents(text)) {
|
|
1876
|
+
if (event.name && event.eventType === 1) onSpecialKey(event.name);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
return normalize(text);
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
void receiver;
|
|
1883
|
+
const value = Reflect.get(target, property, target);
|
|
1884
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
function createKeySignal() {
|
|
1889
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1890
|
+
return {
|
|
1891
|
+
notify(name) {
|
|
1892
|
+
for (const listener of listeners) listener(name);
|
|
1893
|
+
},
|
|
1894
|
+
subscribe(listener) {
|
|
1895
|
+
listeners.add(listener);
|
|
1896
|
+
return () => void listeners.delete(listener);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1844
1901
|
// src/tui/index.tsx
|
|
1845
1902
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1846
1903
|
async function runTui(engine) {
|
|
@@ -1856,6 +1913,7 @@ async function runTui(engine) {
|
|
|
1856
1913
|
const preferences = await readPreferences();
|
|
1857
1914
|
const kitty = await supportsKittyKeyboard({ stdin, stdout, isTTY: stdin.isTTY });
|
|
1858
1915
|
screen = createScreenGuard(stdout, { kittyKeyboard: kitty });
|
|
1916
|
+
const modeKeys = createKeySignal();
|
|
1859
1917
|
screen.enter();
|
|
1860
1918
|
try {
|
|
1861
1919
|
const instance = render(
|
|
@@ -1865,13 +1923,21 @@ async function runTui(engine) {
|
|
|
1865
1923
|
engine,
|
|
1866
1924
|
preferences: {
|
|
1867
1925
|
hangulMode: preferences.hangulMode,
|
|
1868
|
-
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled })
|
|
1926
|
+
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled }),
|
|
1927
|
+
onModeKey: modeKeys.subscribe
|
|
1869
1928
|
}
|
|
1870
1929
|
}
|
|
1871
1930
|
),
|
|
1872
1931
|
{
|
|
1873
1932
|
exitOnCtrlC: true,
|
|
1874
1933
|
patchConsole: true,
|
|
1934
|
+
// 프로토콜을 켜면 kitty 가 ink 이 못 읽는 모양으로 키를 보낸다 — 사이에서
|
|
1935
|
+
// 고쳐 넘기지 않으면 글자가 하나도 입력되지 않는다.
|
|
1936
|
+
...kitty ? {
|
|
1937
|
+
stdin: normalizedStdin(stdin, (name) => {
|
|
1938
|
+
if (name === "rightalt" || name === "capslock") modeKeys.notify(name);
|
|
1939
|
+
})
|
|
1940
|
+
} : {},
|
|
1875
1941
|
// 모든 키를 이스케이프로 받아야 한/영 키(오른쪽 Alt)와 Caps Lock 이 사건으로
|
|
1876
1942
|
// 온다. 그런데 그렇게만 켜면 터미널이 **글쇠 코드만** 보내서 Shift+r 이
|
|
1877
1943
|
// 소문자 `r` 로 도착한다 — 된소리도 영문 대문자도 사라진다. 그래서 글자까지
|
|
@@ -1899,4 +1965,4 @@ async function runTui(engine) {
|
|
|
1899
1965
|
export {
|
|
1900
1966
|
runTui
|
|
1901
1967
|
};
|
|
1902
|
-
//# sourceMappingURL=tui-
|
|
1968
|
+
//# sourceMappingURL=tui-VLPJMWUK.js.map
|