xgen-dex-cli 1.7.0 → 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,58 +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
|
-
async function supportsKittyKeyboard(streams, timeoutMs = 200) {
|
|
531
|
-
if (process.env.DEX_NO_KITTY === "1") return false;
|
|
532
|
-
if (!streams.stdout.isTTY || !streams.isTTY) return false;
|
|
533
|
-
const wasRaw = streams.stdin.isRaw === true;
|
|
534
|
-
const wasPaused = !wasRaw;
|
|
535
|
-
streams.stdin.setRawMode?.(true);
|
|
536
|
-
streams.stdin.resume?.();
|
|
537
|
-
return new Promise((resolve) => {
|
|
538
|
-
let settled = false;
|
|
539
|
-
let seen = "";
|
|
540
|
-
const finish = (supported) => {
|
|
541
|
-
if (settled) return;
|
|
542
|
-
settled = true;
|
|
543
|
-
clearTimeout(timer);
|
|
544
|
-
streams.stdin.removeListener("data", onData);
|
|
545
|
-
if (!wasRaw) streams.stdin.setRawMode?.(false);
|
|
546
|
-
if (wasPaused) streams.stdin.pause?.();
|
|
547
|
-
resolve(supported);
|
|
548
|
-
};
|
|
549
|
-
const onData = (data) => {
|
|
550
|
-
seen += typeof data === "string" ? data : data.toString("utf8");
|
|
551
|
-
if (/\u001B\[\?\d*u/.test(seen)) finish(true);
|
|
552
|
-
};
|
|
553
|
-
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
554
|
-
streams.stdin.on("data", onData);
|
|
555
|
-
streams.stdout.write(QUERY);
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
|
|
559
507
|
// src/tui/ime-text-input.tsx
|
|
560
508
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
561
509
|
var segmenter = new Intl.Segmenter("ko", { granularity: "grapheme" });
|
|
@@ -589,6 +537,12 @@ function visibleInput(segments, cursor, maximumWidth) {
|
|
|
589
537
|
cursorWidth: widthBeforeCursor
|
|
590
538
|
};
|
|
591
539
|
}
|
|
540
|
+
function isHangulToggle(input, key) {
|
|
541
|
+
if (key.ctrl && (input === "`" || input === " " || input === "l")) return true;
|
|
542
|
+
if (key.meta && input === " ") return true;
|
|
543
|
+
if (key.shift && input === " ") return true;
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
592
546
|
function TerminalCursor({ x, y }) {
|
|
593
547
|
const { setCursorPosition } = useCursor();
|
|
594
548
|
setCursorPosition({ x, y });
|
|
@@ -613,22 +567,6 @@ function ImeTextInput(props) {
|
|
|
613
567
|
moveCursor(nextCursor, segments.length);
|
|
614
568
|
props.onChange(nextValue);
|
|
615
569
|
};
|
|
616
|
-
const { stdin: stdin2, isRawModeSupported } = useStdin();
|
|
617
|
-
const onModeKeyRef = useRef2(void 0);
|
|
618
|
-
onModeKeyRef.current = () => props.onHangulModeChange?.(!props.hangulMode);
|
|
619
|
-
useEffect2(() => {
|
|
620
|
-
if (!props.focus || !isRawModeSupported) return void 0;
|
|
621
|
-
const onData = (data) => {
|
|
622
|
-
const chunk = typeof data === "string" ? data : data.toString("utf8");
|
|
623
|
-
for (const event of parseKeyEvents(chunk)) {
|
|
624
|
-
typingRef.current = { ...typingRef.current, capsLock: event.capsLock };
|
|
625
|
-
if (event.eventType !== 1) continue;
|
|
626
|
-
if (event.name === "rightalt" || event.name === "capslock") onModeKeyRef.current?.();
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
stdin2?.on("data", onData);
|
|
630
|
-
return () => void stdin2?.off("data", onData);
|
|
631
|
-
}, [props.focus, isRawModeSupported, stdin2]);
|
|
632
570
|
useEffect2(() => {
|
|
633
571
|
if (!props.focus || !props.hangulMode) {
|
|
634
572
|
typingRef.current = { ...IDLE, capsLock: typingRef.current.capsLock };
|
|
@@ -674,7 +612,7 @@ function ImeTextInput(props) {
|
|
|
674
612
|
}
|
|
675
613
|
return;
|
|
676
614
|
}
|
|
677
|
-
if (
|
|
615
|
+
if (isHangulToggle(input, key)) {
|
|
678
616
|
settle();
|
|
679
617
|
props.onHangulModeChange?.(!props.hangulMode);
|
|
680
618
|
return;
|
|
@@ -1100,6 +1038,15 @@ function Dashboard(props) {
|
|
|
1100
1038
|
setHangulMode(enabled);
|
|
1101
1039
|
props.preferences?.onHangulModeChange?.(enabled);
|
|
1102
1040
|
};
|
|
1041
|
+
useEffect6(
|
|
1042
|
+
() => props.preferences?.onModeKey?.(
|
|
1043
|
+
() => setHangulMode((current) => {
|
|
1044
|
+
props.preferences?.onHangulModeChange?.(!current);
|
|
1045
|
+
return !current;
|
|
1046
|
+
})
|
|
1047
|
+
),
|
|
1048
|
+
[props.preferences]
|
|
1049
|
+
);
|
|
1103
1050
|
const viewport = useRef3({ lineCount: 0, height: 0 });
|
|
1104
1051
|
const [starting, setStarting] = useState7(false);
|
|
1105
1052
|
const controller = useRef3(null);
|
|
@@ -1755,25 +1702,25 @@ function RetryInput({ onRetry }) {
|
|
|
1755
1702
|
}
|
|
1756
1703
|
|
|
1757
1704
|
// src/tui/screen.ts
|
|
1758
|
-
var
|
|
1759
|
-
var ENTER_ALT_SCREEN = `${
|
|
1760
|
-
var LEAVE_ALT_SCREEN = `${
|
|
1761
|
-
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`;
|
|
1762
1709
|
var DISABLE_REPORTS = [
|
|
1763
|
-
`${
|
|
1710
|
+
`${ESC}[?1004l`,
|
|
1764
1711
|
// 포커스 들어옴/나감
|
|
1765
|
-
`${
|
|
1712
|
+
`${ESC}[?1000l`,
|
|
1766
1713
|
// 마우스 클릭
|
|
1767
|
-
`${
|
|
1714
|
+
`${ESC}[?1002l`,
|
|
1768
1715
|
// 마우스 드래그
|
|
1769
|
-
`${
|
|
1716
|
+
`${ESC}[?1003l`,
|
|
1770
1717
|
// 마우스 이동 전부
|
|
1771
|
-
`${
|
|
1718
|
+
`${ESC}[?1006l`
|
|
1772
1719
|
// SGR 확장 좌표
|
|
1773
1720
|
].join("");
|
|
1774
|
-
var ENABLE_BRACKETED_PASTE = `${
|
|
1775
|
-
var DISABLE_BRACKETED_PASTE = `${
|
|
1776
|
-
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`;
|
|
1777
1724
|
function createScreenGuard(stream, options = {}) {
|
|
1778
1725
|
const alt = Boolean(stream.isTTY);
|
|
1779
1726
|
let entered = false;
|
|
@@ -1826,6 +1773,131 @@ async function writePreferences(preferences, env = process.env) {
|
|
|
1826
1773
|
}
|
|
1827
1774
|
}
|
|
1828
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
|
+
|
|
1829
1901
|
// src/tui/index.tsx
|
|
1830
1902
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1831
1903
|
async function runTui(engine) {
|
|
@@ -1841,6 +1913,7 @@ async function runTui(engine) {
|
|
|
1841
1913
|
const preferences = await readPreferences();
|
|
1842
1914
|
const kitty = await supportsKittyKeyboard({ stdin, stdout, isTTY: stdin.isTTY });
|
|
1843
1915
|
screen = createScreenGuard(stdout, { kittyKeyboard: kitty });
|
|
1916
|
+
const modeKeys = createKeySignal();
|
|
1844
1917
|
screen.enter();
|
|
1845
1918
|
try {
|
|
1846
1919
|
const instance = render(
|
|
@@ -1850,18 +1923,33 @@ async function runTui(engine) {
|
|
|
1850
1923
|
engine,
|
|
1851
1924
|
preferences: {
|
|
1852
1925
|
hangulMode: preferences.hangulMode,
|
|
1853
|
-
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled })
|
|
1926
|
+
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled }),
|
|
1927
|
+
onModeKey: modeKeys.subscribe
|
|
1854
1928
|
}
|
|
1855
1929
|
}
|
|
1856
1930
|
),
|
|
1857
1931
|
{
|
|
1858
1932
|
exitOnCtrlC: true,
|
|
1859
1933
|
patchConsole: true,
|
|
1860
|
-
//
|
|
1934
|
+
// 프로토콜을 켜면 kitty 가 ink 이 못 읽는 모양으로 키를 보낸다 — 사이에서
|
|
1935
|
+
// 고쳐 넘기지 않으면 글자가 하나도 입력되지 않는다.
|
|
1936
|
+
...kitty ? {
|
|
1937
|
+
stdin: normalizedStdin(stdin, (name) => {
|
|
1938
|
+
if (name === "rightalt" || name === "capslock") modeKeys.notify(name);
|
|
1939
|
+
})
|
|
1940
|
+
} : {},
|
|
1941
|
+
// 모든 키를 이스케이프로 받아야 한/영 키(오른쪽 Alt)와 Caps Lock 이 사건으로
|
|
1942
|
+
// 온다. 그런데 그렇게만 켜면 터미널이 **글쇠 코드만** 보내서 Shift+r 이
|
|
1943
|
+
// 소문자 `r` 로 도착한다 — 된소리도 영문 대문자도 사라진다. 그래서 글자까지
|
|
1944
|
+
// 함께 보내 달라고(reportAssociatedText) 요청한다.
|
|
1861
1945
|
...kitty ? {
|
|
1862
1946
|
kittyKeyboard: {
|
|
1863
1947
|
mode: "enabled",
|
|
1864
|
-
flags: [
|
|
1948
|
+
flags: [
|
|
1949
|
+
"disambiguateEscapeCodes",
|
|
1950
|
+
"reportAllKeysAsEscapeCodes",
|
|
1951
|
+
"reportAssociatedText"
|
|
1952
|
+
]
|
|
1865
1953
|
}
|
|
1866
1954
|
} : {}
|
|
1867
1955
|
}
|
|
@@ -1877,4 +1965,4 @@ async function runTui(engine) {
|
|
|
1877
1965
|
export {
|
|
1878
1966
|
runTui
|
|
1879
1967
|
};
|
|
1880
|
-
//# sourceMappingURL=tui-
|
|
1968
|
+
//# sourceMappingURL=tui-VLPJMWUK.js.map
|