xgen-dex-cli 1.7.1 → 1.8.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/dist/chunks/{chunk-ULMEXJOR.js → chunk-KY3K4GV4.js} +75 -1
- package/dist/chunks/chunk-KY3K4GV4.js.map +7 -0
- package/dist/chunks/{tui-H3VM2MGV.js → tui-WG5AIWUR.js} +517 -231
- package/dist/chunks/tui-WG5AIWUR.js.map +7 -0
- package/dist/cli.js +3 -3
- package/package.json +1 -1
- package/dist/chunks/chunk-ULMEXJOR.js.map +0 -7
- package/dist/chunks/tui-H3VM2MGV.js.map +0 -7
|
@@ -2,19 +2,19 @@ import {
|
|
|
2
2
|
DexError,
|
|
3
3
|
dataDirectory,
|
|
4
4
|
publicError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KY3K4GV4.js";
|
|
6
6
|
|
|
7
7
|
// src/tui/index.tsx
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
import { stdin, stdout } from "node:process";
|
|
10
10
|
|
|
11
11
|
// src/tui/app.tsx
|
|
12
|
-
import { useCallback, useEffect as
|
|
13
|
-
import { Box as
|
|
12
|
+
import { useCallback, useEffect as useEffect9, useState as useState12 } from "react";
|
|
13
|
+
import { Box as Box11, Text as Text11, useApp as useApp2, useInput as useInput10 } from "ink";
|
|
14
14
|
|
|
15
15
|
// src/tui/dashboard.tsx
|
|
16
|
-
import { useEffect as
|
|
17
|
-
import { Box as
|
|
16
|
+
import { useEffect as useEffect7, useReducer, useRef as useRef3, useState as useState8 } from "react";
|
|
17
|
+
import { Box as Box7, Text as Text7, useApp, useInput as useInput6 } from "ink";
|
|
18
18
|
|
|
19
19
|
// src/tui/chat-state.ts
|
|
20
20
|
var initialChatState = { messages: [], running: false };
|
|
@@ -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 };
|
|
@@ -994,8 +917,204 @@ function when(conversation) {
|
|
|
994
917
|
return sameDay ? `\uC624\uB298 ${pad(date.getHours())}:${pad(date.getMinutes())}` : `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
995
918
|
}
|
|
996
919
|
|
|
920
|
+
// src/tui/agent-create.tsx
|
|
921
|
+
import { useEffect as useEffect5, useMemo, useState as useState6 } from "react";
|
|
922
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
923
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
924
|
+
var ADVANCED_ORDER = [
|
|
925
|
+
"system_prompt",
|
|
926
|
+
"temperature",
|
|
927
|
+
"max_tokens",
|
|
928
|
+
"max_iterations",
|
|
929
|
+
"context_window",
|
|
930
|
+
"tool_exposure",
|
|
931
|
+
"enable_builtin_tools",
|
|
932
|
+
"enable_self_evolution",
|
|
933
|
+
"enable_delegation",
|
|
934
|
+
"enable_memory",
|
|
935
|
+
"enable_compaction",
|
|
936
|
+
"streaming",
|
|
937
|
+
"base_url"
|
|
938
|
+
];
|
|
939
|
+
function advancedFields(settings) {
|
|
940
|
+
const rank = (id) => {
|
|
941
|
+
const i = ADVANCED_ORDER.indexOf(id);
|
|
942
|
+
return i === -1 ? ADVANCED_ORDER.length : i;
|
|
943
|
+
};
|
|
944
|
+
return [...settings].sort((a, b) => rank(a.id) - rank(b.id)).map((setting) => {
|
|
945
|
+
const type = (setting.type || "").toUpperCase();
|
|
946
|
+
if (type === "BOOL") return { key: setting.id, label: setting.label, kind: "toggle" };
|
|
947
|
+
if (setting.options && setting.options.length > 0) {
|
|
948
|
+
return {
|
|
949
|
+
key: setting.id,
|
|
950
|
+
label: setting.label,
|
|
951
|
+
kind: "choice",
|
|
952
|
+
choices: setting.options
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
return { key: setting.id, label: setting.label, kind: "text" };
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
function AgentCreateScreen(props) {
|
|
959
|
+
const [options, setOptions] = useState6();
|
|
960
|
+
const [loadError, setLoadError] = useState6();
|
|
961
|
+
const [values, setValues] = useState6({});
|
|
962
|
+
const [cursor, setCursor] = useState6(0);
|
|
963
|
+
const [advanced, setAdvanced] = useState6(false);
|
|
964
|
+
const [busy, setBusy] = useState6(false);
|
|
965
|
+
const [error, setError] = useState6();
|
|
966
|
+
useEffect5(() => {
|
|
967
|
+
let cancelled = false;
|
|
968
|
+
props.engine.agentCreateOptions(props.profile).then((data) => {
|
|
969
|
+
if (cancelled) return;
|
|
970
|
+
const provider = data.providers.find((p) => p.value === data.defaultProvider)?.value ?? data.providers[0]?.value ?? "";
|
|
971
|
+
const info = data.providers.find((p) => p.value === provider);
|
|
972
|
+
setOptions(data);
|
|
973
|
+
const seeded = {};
|
|
974
|
+
for (const setting of data.settings) seeded[setting.id] = setting.default;
|
|
975
|
+
setValues({
|
|
976
|
+
...seeded,
|
|
977
|
+
...data.defaults,
|
|
978
|
+
name: "",
|
|
979
|
+
provider,
|
|
980
|
+
model: info?.defaultModel || info?.models[0]?.value || ""
|
|
981
|
+
});
|
|
982
|
+
}).catch((err) => {
|
|
983
|
+
if (!cancelled) setLoadError(publicError(err).message);
|
|
984
|
+
});
|
|
985
|
+
return () => {
|
|
986
|
+
cancelled = true;
|
|
987
|
+
};
|
|
988
|
+
}, [props.engine, props.profile]);
|
|
989
|
+
const providerInfo = useMemo(
|
|
990
|
+
() => options?.providers.find((p) => p.value === values.provider),
|
|
991
|
+
[options, values.provider]
|
|
992
|
+
);
|
|
993
|
+
const fields = useMemo(() => {
|
|
994
|
+
if (!options) return [];
|
|
995
|
+
const base = [
|
|
996
|
+
{ key: "name", label: "\uC774\uB984", kind: "text", hint: "\uC608: \uC601\uC5C5 \uB9AC\uC11C\uCE58 \uB3C4\uC6B0\uBBF8" },
|
|
997
|
+
{
|
|
998
|
+
key: "provider",
|
|
999
|
+
label: "AI \uC81C\uACF5\uC0AC",
|
|
1000
|
+
kind: "choice",
|
|
1001
|
+
choices: options.providers.map((p) => ({ value: p.value, label: p.label }))
|
|
1002
|
+
},
|
|
1003
|
+
{ key: "model", label: "\uBAA8\uB378", kind: "choice", choices: providerInfo?.models ?? [] }
|
|
1004
|
+
];
|
|
1005
|
+
return advanced ? [...base, ...advancedFields(options.settings)] : base;
|
|
1006
|
+
}, [options, providerInfo, advanced]);
|
|
1007
|
+
const active = fields[cursor];
|
|
1008
|
+
const name = String(values.name ?? "").trim();
|
|
1009
|
+
const setValue = (key, value) => {
|
|
1010
|
+
setValues((prev) => {
|
|
1011
|
+
if (key !== "provider") return { ...prev, [key]: value };
|
|
1012
|
+
const info = options?.providers.find((p) => p.value === value);
|
|
1013
|
+
return { ...prev, provider: value, model: info?.defaultModel || info?.models[0]?.value || "" };
|
|
1014
|
+
});
|
|
1015
|
+
};
|
|
1016
|
+
const cycle = (field, step) => {
|
|
1017
|
+
if (field.kind === "toggle") {
|
|
1018
|
+
setValue(field.key, !values[field.key]);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const choices = field.choices ?? [];
|
|
1022
|
+
if (choices.length === 0) return;
|
|
1023
|
+
const at = Math.max(0, choices.findIndex((c) => c.value === String(values[field.key] ?? "")));
|
|
1024
|
+
const next = (at + step + choices.length) % choices.length;
|
|
1025
|
+
setValue(field.key, choices[next].value);
|
|
1026
|
+
};
|
|
1027
|
+
const submit = async () => {
|
|
1028
|
+
if (!name || busy) return;
|
|
1029
|
+
setBusy(true);
|
|
1030
|
+
setError(void 0);
|
|
1031
|
+
try {
|
|
1032
|
+
const { name: _n, provider: _p, model: _m, ...settings } = values;
|
|
1033
|
+
const created = await props.engine.createAgent(
|
|
1034
|
+
{
|
|
1035
|
+
name,
|
|
1036
|
+
provider: String(values.provider ?? ""),
|
|
1037
|
+
model: String(values.model ?? ""),
|
|
1038
|
+
settings
|
|
1039
|
+
},
|
|
1040
|
+
props.profile
|
|
1041
|
+
);
|
|
1042
|
+
props.onCreated(created);
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
setError(publicError(err).message);
|
|
1045
|
+
} finally {
|
|
1046
|
+
setBusy(false);
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
useInput5((input, key) => {
|
|
1050
|
+
const onText = active?.kind === "text";
|
|
1051
|
+
if (key.escape) {
|
|
1052
|
+
props.onCancel();
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
if (key.tab) {
|
|
1056
|
+
setAdvanced((v) => !v);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (key.upArrow) {
|
|
1060
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (key.downArrow) {
|
|
1064
|
+
setCursor((c) => Math.min(fields.length - 1, c + 1));
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (!active || onText) return;
|
|
1068
|
+
if (key.leftArrow) cycle(active, -1);
|
|
1069
|
+
else if (key.rightArrow) cycle(active, 1);
|
|
1070
|
+
else if (input === " " && !key.ctrl && !key.meta) cycle(active, 1);
|
|
1071
|
+
else if (key.return) void submit();
|
|
1072
|
+
});
|
|
1073
|
+
if (loadError) {
|
|
1074
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
|
|
1075
|
+
/* @__PURE__ */ jsx6(Text6, { color: "red", children: loadError }),
|
|
1076
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Esc \uB85C \uB3CC\uC544\uAC11\uB2C8\uB2E4." })
|
|
1077
|
+
] });
|
|
1078
|
+
}
|
|
1079
|
+
if (!options) {
|
|
1080
|
+
return /* @__PURE__ */ jsx6(Box6, { padding: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\uBD88\uB7EC\uC624\uB294 \uC911..." }) });
|
|
1081
|
+
}
|
|
1082
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
1083
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "\uC0C8 \uC5D0\uC774\uC804\uD2B8" }),
|
|
1084
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: "\uC774\uB984\uACFC \uBAA8\uB378\uB9CC \uC815\uD558\uBA74 \uB429\uB2C8\uB2E4. \uB3C4\uAD6C\xB7\uAE30\uC5B5\xB7\uC790\uAE30\uC9C4\uD654\uB294 \uC774\uBBF8 \uC548\uC5D0 \uC788\uC2B5\uB2C8\uB2E4." }),
|
|
1085
|
+
/* @__PURE__ */ jsx6(Box6, { height: 1 }),
|
|
1086
|
+
fields.map((field, index) => {
|
|
1087
|
+
const focused = index === cursor;
|
|
1088
|
+
const raw = values[field.key];
|
|
1089
|
+
const shown = field.kind === "toggle" ? raw ? "\uCF2C" : "\uB054" : field.kind === "choice" ? field.choices?.find((c) => c.value === String(raw ?? ""))?.label ?? String(raw ?? "") : String(raw ?? "");
|
|
1090
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1091
|
+
/* @__PURE__ */ jsx6(Box6, { width: 24, flexShrink: 0, children: /* @__PURE__ */ jsxs6(Text6, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1092
|
+
focused ? "\u203A" : " ",
|
|
1093
|
+
" ",
|
|
1094
|
+
field.label
|
|
1095
|
+
] }) }),
|
|
1096
|
+
field.kind === "text" && focused ? /* @__PURE__ */ jsx6(
|
|
1097
|
+
ImeTextInput,
|
|
1098
|
+
{
|
|
1099
|
+
value: String(raw ?? ""),
|
|
1100
|
+
onChange: (v) => setValue(field.key, v),
|
|
1101
|
+
onSubmit: () => void submit(),
|
|
1102
|
+
focus: true,
|
|
1103
|
+
placeholder: field.hint ?? "",
|
|
1104
|
+
hangulMode: props.hangulMode,
|
|
1105
|
+
onHangulModeChange: props.onHangulModeChange
|
|
1106
|
+
}
|
|
1107
|
+
) : /* @__PURE__ */ jsx6(Text6, { dimColor: !focused, wrap: "truncate-end", children: field.kind === "text" ? shown || field.hint || "" : `\u2039 ${shown} \u203A` })
|
|
1108
|
+
] }, field.key);
|
|
1109
|
+
}),
|
|
1110
|
+
/* @__PURE__ */ jsx6(Box6, { height: 1 }),
|
|
1111
|
+
error ? /* @__PURE__ */ jsx6(Text6, { color: "red", children: error }) : null,
|
|
1112
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: busy ? "\uB9CC\uB4DC\uB294 \uC911..." : `\u2191\u2193 \uC774\uB3D9 \xB7 \u2190\u2192 \uACE0\uB974\uAE30 \xB7 Tab ${advanced ? "\uC138\uBD80\uC124\uC815 \uC811\uAE30" : "\uC138\uBD80\uC124\uC815"} \xB7 Enter \uB9CC\uB4E4\uAE30 \xB7 Esc \uCDE8\uC18C` })
|
|
1113
|
+
] });
|
|
1114
|
+
}
|
|
1115
|
+
|
|
997
1116
|
// src/tui/use-terminal-size.ts
|
|
998
|
-
import { useEffect as
|
|
1117
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
999
1118
|
import { useStdout } from "ink";
|
|
1000
1119
|
function useTerminalSize() {
|
|
1001
1120
|
const { stdout: stdout2 } = useStdout();
|
|
@@ -1004,8 +1123,8 @@ function useTerminalSize() {
|
|
|
1004
1123
|
const rows = stdout2.rows || 30;
|
|
1005
1124
|
return { columns, rows, wide: columns >= 88 };
|
|
1006
1125
|
};
|
|
1007
|
-
const [size, setSize] =
|
|
1008
|
-
|
|
1126
|
+
const [size, setSize] = useState7(read);
|
|
1127
|
+
useEffect6(() => {
|
|
1009
1128
|
const resize = () => setSize(read());
|
|
1010
1129
|
stdout2.on("resize", resize);
|
|
1011
1130
|
return () => {
|
|
@@ -1016,18 +1135,22 @@ function useTerminalSize() {
|
|
|
1016
1135
|
}
|
|
1017
1136
|
|
|
1018
1137
|
// src/tui/dashboard.tsx
|
|
1019
|
-
import { jsx as
|
|
1138
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1020
1139
|
function AgentSidebar(props) {
|
|
1021
1140
|
const radius = Math.max(3, Math.floor((props.height - 4) / 2));
|
|
1022
1141
|
const start = Math.max(0, props.cursor - radius);
|
|
1023
1142
|
const visible = props.agents.slice(start, start + radius * 2 + 1);
|
|
1024
|
-
return /* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1143
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: 30, borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
|
|
1144
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: "Agents" }),
|
|
1145
|
+
/* @__PURE__ */ jsxs7(Text7, { color: props.cursor === -1 && props.focused ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1146
|
+
props.cursor === -1 ? "\u203A" : " ",
|
|
1147
|
+
" \uFF0B \uC0C8 \uC5D0\uC774\uC804\uD2B8"
|
|
1148
|
+
] }),
|
|
1026
1149
|
visible.map((agent) => {
|
|
1027
1150
|
const index = props.agents.indexOf(agent);
|
|
1028
1151
|
const cursor = index === props.cursor;
|
|
1029
1152
|
const selected = agent.workflowId === props.selected;
|
|
1030
|
-
return /* @__PURE__ */
|
|
1153
|
+
return /* @__PURE__ */ jsxs7(Text7, { color: cursor && props.focused ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1031
1154
|
cursor ? "\u203A" : " ",
|
|
1032
1155
|
" ",
|
|
1033
1156
|
selected ? "\u25CF" : "\u25CB",
|
|
@@ -1035,7 +1158,7 @@ function AgentSidebar(props) {
|
|
|
1035
1158
|
agent.workflowName
|
|
1036
1159
|
] }, agent.workflowId);
|
|
1037
1160
|
}),
|
|
1038
|
-
props.agents.length === 0 ? /* @__PURE__ */
|
|
1161
|
+
props.agents.length === 0 ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C Agent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) : null
|
|
1039
1162
|
] });
|
|
1040
1163
|
}
|
|
1041
1164
|
function ChatPane(props) {
|
|
@@ -1044,22 +1167,22 @@ function ChatPane(props) {
|
|
|
1044
1167
|
const height = box?.height ?? 1;
|
|
1045
1168
|
const lines = renderTranscript(props.messages, props.agent?.workflowName ?? "Agent", width);
|
|
1046
1169
|
const view = viewportOf(lines, height, props.scrollUp);
|
|
1047
|
-
|
|
1170
|
+
useEffect7(() => {
|
|
1048
1171
|
props.onViewport(lines.length, height);
|
|
1049
1172
|
}, [lines.length, height]);
|
|
1050
|
-
return /* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
view.below > 0 ? /* @__PURE__ */
|
|
1173
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
|
|
1174
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1175
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, wrap: "truncate-end", children: props.agent?.workflowName ?? "Agent\uB97C \uC120\uD0DD\uD558\uC138\uC694" }),
|
|
1176
|
+
view.below > 0 ? /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1054
1177
|
" \xB7 \u2193",
|
|
1055
1178
|
view.below,
|
|
1056
1179
|
"\uC904"
|
|
1057
1180
|
] }) : null
|
|
1058
1181
|
] }),
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
view.lines.length === 0 ? /* @__PURE__ */
|
|
1061
|
-
view.lines.map((line) => /* @__PURE__ */
|
|
1062
|
-
|
|
1182
|
+
/* @__PURE__ */ jsxs7(Box7, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [
|
|
1183
|
+
view.lines.length === 0 ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate-end", children: props.agent ? "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD574 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694." : "\uC67C\uCABD\uC5D0\uC11C Agent\uB97C \uC120\uD0DD\uD558\uC138\uC694." }) : null,
|
|
1184
|
+
view.lines.map((line) => /* @__PURE__ */ jsx7(
|
|
1185
|
+
Text7,
|
|
1063
1186
|
{
|
|
1064
1187
|
wrap: "truncate-end",
|
|
1065
1188
|
bold: line.role === "label",
|
|
@@ -1070,17 +1193,17 @@ function ChatPane(props) {
|
|
|
1070
1193
|
line.key
|
|
1071
1194
|
))
|
|
1072
1195
|
] }),
|
|
1073
|
-
props.status ? /* @__PURE__ */
|
|
1196
|
+
props.status ? /* @__PURE__ */ jsxs7(Text7, { color: "yellow", wrap: "truncate-end", children: [
|
|
1074
1197
|
"\u25C6 ",
|
|
1075
1198
|
props.status
|
|
1076
1199
|
] }) : null
|
|
1077
1200
|
] });
|
|
1078
1201
|
}
|
|
1079
1202
|
function Composer(props) {
|
|
1080
|
-
return /* @__PURE__ */
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
props.disabled ? /* @__PURE__ */
|
|
1203
|
+
return /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
|
|
1204
|
+
/* @__PURE__ */ jsx7(Text7, { color: props.hangulMode ? "yellow" : void 0, dimColor: !props.hangulMode, children: props.hangulMode ? "\uD55C" : "EN" }),
|
|
1205
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: " \u203A " }),
|
|
1206
|
+
props.disabled ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\uC751\uB2F5\uC744 \uAE30\uB2E4\uB9AC\uB294 \uC911..." }) : /* @__PURE__ */ jsx7(
|
|
1084
1207
|
ImeTextInput,
|
|
1085
1208
|
{
|
|
1086
1209
|
value: props.value,
|
|
@@ -1098,28 +1221,42 @@ function Dashboard(props) {
|
|
|
1098
1221
|
const { exit } = useApp();
|
|
1099
1222
|
const size = useTerminalSize();
|
|
1100
1223
|
const bodyHeight = Math.max(12, size.rows - 5);
|
|
1101
|
-
const [focus, setFocus] =
|
|
1102
|
-
const [cursor, setCursor] =
|
|
1103
|
-
const [
|
|
1224
|
+
const [focus, setFocus] = useState8("agents");
|
|
1225
|
+
const [cursor, setCursor] = useState8(0);
|
|
1226
|
+
const [creatingAgent, setCreatingAgent] = useState8(false);
|
|
1227
|
+
const [selected, setSelected] = useState8(() => {
|
|
1104
1228
|
const first = props.session.agents[0];
|
|
1105
1229
|
return first ? { workflowId: first.workflowId, workflowName: first.workflowName } : void 0;
|
|
1106
1230
|
});
|
|
1107
|
-
const [input, setInput] =
|
|
1231
|
+
const [input, setInput] = useState8("");
|
|
1108
1232
|
const [chat, dispatch] = useReducer(chatReducer, initialChatState);
|
|
1109
|
-
const [palette, setPalette] =
|
|
1110
|
-
const [history, setHistory] =
|
|
1111
|
-
const [start, setStart] =
|
|
1112
|
-
const [scrollUp, setScrollUp] =
|
|
1113
|
-
const [hangulMode, setHangulMode] =
|
|
1233
|
+
const [palette, setPalette] = useState8(false);
|
|
1234
|
+
const [history, setHistory] = useState8(false);
|
|
1235
|
+
const [start, setStart] = useState8();
|
|
1236
|
+
const [scrollUp, setScrollUp] = useState8(0);
|
|
1237
|
+
const [hangulMode, setHangulMode] = useState8(props.preferences?.hangulMode ?? false);
|
|
1114
1238
|
const changeHangulMode = (enabled) => {
|
|
1115
1239
|
setHangulMode(enabled);
|
|
1116
1240
|
props.preferences?.onHangulModeChange?.(enabled);
|
|
1117
1241
|
};
|
|
1242
|
+
useEffect7(
|
|
1243
|
+
() => props.preferences?.onModeKey?.(
|
|
1244
|
+
() => setHangulMode((current) => {
|
|
1245
|
+
props.preferences?.onHangulModeChange?.(!current);
|
|
1246
|
+
return !current;
|
|
1247
|
+
})
|
|
1248
|
+
),
|
|
1249
|
+
[props.preferences]
|
|
1250
|
+
);
|
|
1118
1251
|
const viewport = useRef3({ lineCount: 0, height: 0 });
|
|
1119
|
-
const [starting, setStarting] =
|
|
1252
|
+
const [starting, setStarting] = useState8(false);
|
|
1120
1253
|
const controller = useRef3(null);
|
|
1121
|
-
|
|
1254
|
+
useEffect7(() => () => controller.current?.abort(), []);
|
|
1122
1255
|
const selectAgent = async () => {
|
|
1256
|
+
if (cursor === -1) {
|
|
1257
|
+
setCreatingAgent(true);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1123
1260
|
const agent = props.session.agents[cursor];
|
|
1124
1261
|
if (!agent || chat.running || starting) return;
|
|
1125
1262
|
const ref = { workflowId: agent.workflowId, workflowName: agent.workflowName };
|
|
@@ -1204,7 +1341,7 @@ function Dashboard(props) {
|
|
|
1204
1341
|
const limit = maximumScroll(lineCount, height);
|
|
1205
1342
|
setScrollUp((current) => Math.min(limit, Math.max(0, current - direction * step)));
|
|
1206
1343
|
};
|
|
1207
|
-
|
|
1344
|
+
useInput6(
|
|
1208
1345
|
(keyInput, key) => {
|
|
1209
1346
|
if (key.ctrl && keyInput === "k") setPalette(true);
|
|
1210
1347
|
else if (key.ctrl && keyInput === "p") {
|
|
@@ -1219,14 +1356,14 @@ function Dashboard(props) {
|
|
|
1219
1356
|
else if (key.escape && chat.running) cancelTurn();
|
|
1220
1357
|
else if (key.escape) setFocus("agents");
|
|
1221
1358
|
else if (key.tab) setFocus((current) => current === "agents" ? "composer" : "agents");
|
|
1222
|
-
else if (focus === "agents" && key.upArrow) setCursor((current) => Math.max(
|
|
1359
|
+
else if (focus === "agents" && key.upArrow) setCursor((current) => Math.max(-1, current - 1));
|
|
1223
1360
|
else if (focus === "agents" && key.downArrow && props.session.agents.length > 0) {
|
|
1224
1361
|
setCursor((current) => Math.min(props.session.agents.length - 1, current + 1));
|
|
1225
1362
|
} else if (focus === "agents" && key.return) void selectAgent();
|
|
1226
1363
|
},
|
|
1227
1364
|
// 갈림길·팔레트·이력 화면이 떠 있으면 그 화면이 키를 갖는다 — 여기서도 받으면
|
|
1228
1365
|
// 방향키 하나가 두 곳에서 움직인다.
|
|
1229
|
-
{ isActive: !palette && !history && !start }
|
|
1366
|
+
{ isActive: !palette && !history && !start && !creatingAgent }
|
|
1230
1367
|
);
|
|
1231
1368
|
const paletteActions = [
|
|
1232
1369
|
{ id: "new", label: "\uC0C8 \uB300\uD654", run: newConversation },
|
|
@@ -1259,10 +1396,25 @@ function Dashboard(props) {
|
|
|
1259
1396
|
{ id: "quit", label: "\uC885\uB8CC", run: exit }
|
|
1260
1397
|
];
|
|
1261
1398
|
let body;
|
|
1262
|
-
if (
|
|
1263
|
-
body = /* @__PURE__ */
|
|
1399
|
+
if (creatingAgent) {
|
|
1400
|
+
body = /* @__PURE__ */ jsx7(
|
|
1401
|
+
AgentCreateScreen,
|
|
1402
|
+
{
|
|
1403
|
+
engine: props.engine,
|
|
1404
|
+
profile: props.session.profile,
|
|
1405
|
+
hangulMode,
|
|
1406
|
+
onHangulModeChange: changeHangulMode,
|
|
1407
|
+
onCancel: () => setCreatingAgent(false),
|
|
1408
|
+
onCreated: (agent) => {
|
|
1409
|
+
setCreatingAgent(false);
|
|
1410
|
+
openNewChat(agent);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
);
|
|
1414
|
+
} else if (palette) {
|
|
1415
|
+
body = /* @__PURE__ */ jsx7(CommandPalette, { actions: paletteActions, onCancel: () => setPalette(false) });
|
|
1264
1416
|
} else if (history) {
|
|
1265
|
-
body = /* @__PURE__ */
|
|
1417
|
+
body = /* @__PURE__ */ jsx7(
|
|
1266
1418
|
HistoryScreen,
|
|
1267
1419
|
{
|
|
1268
1420
|
engine: props.engine,
|
|
@@ -1272,7 +1424,7 @@ function Dashboard(props) {
|
|
|
1272
1424
|
}
|
|
1273
1425
|
);
|
|
1274
1426
|
} else {
|
|
1275
|
-
const sidebar = /* @__PURE__ */
|
|
1427
|
+
const sidebar = /* @__PURE__ */ jsx7(
|
|
1276
1428
|
AgentSidebar,
|
|
1277
1429
|
{
|
|
1278
1430
|
agents: props.session.agents,
|
|
@@ -1282,7 +1434,7 @@ function Dashboard(props) {
|
|
|
1282
1434
|
height: bodyHeight
|
|
1283
1435
|
}
|
|
1284
1436
|
);
|
|
1285
|
-
const conversation = start ? /* @__PURE__ */
|
|
1437
|
+
const conversation = start ? /* @__PURE__ */ jsx7(
|
|
1286
1438
|
StartPanel,
|
|
1287
1439
|
{
|
|
1288
1440
|
engine: props.engine,
|
|
@@ -1296,8 +1448,8 @@ function Dashboard(props) {
|
|
|
1296
1448
|
setFocus("agents");
|
|
1297
1449
|
}
|
|
1298
1450
|
}
|
|
1299
|
-
) : /* @__PURE__ */
|
|
1300
|
-
/* @__PURE__ */
|
|
1451
|
+
) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", flexGrow: 1, children: [
|
|
1452
|
+
/* @__PURE__ */ jsx7(
|
|
1301
1453
|
ChatPane,
|
|
1302
1454
|
{
|
|
1303
1455
|
agent: selected,
|
|
@@ -1309,7 +1461,7 @@ function Dashboard(props) {
|
|
|
1309
1461
|
}
|
|
1310
1462
|
}
|
|
1311
1463
|
),
|
|
1312
|
-
/* @__PURE__ */
|
|
1464
|
+
/* @__PURE__ */ jsx7(
|
|
1313
1465
|
Composer,
|
|
1314
1466
|
{
|
|
1315
1467
|
value: input,
|
|
@@ -1322,13 +1474,13 @@ function Dashboard(props) {
|
|
|
1322
1474
|
}
|
|
1323
1475
|
)
|
|
1324
1476
|
] });
|
|
1325
|
-
body = size.wide ? /* @__PURE__ */
|
|
1477
|
+
body = size.wide ? /* @__PURE__ */ jsxs7(Box7, { height: bodyHeight, children: [
|
|
1326
1478
|
sidebar,
|
|
1327
1479
|
conversation
|
|
1328
|
-
] }) : focus === "agents" ? /* @__PURE__ */
|
|
1480
|
+
] }) : focus === "agents" ? /* @__PURE__ */ jsx7(Box7, { height: bodyHeight, children: sidebar }) : /* @__PURE__ */ jsx7(Box7, { height: bodyHeight, children: conversation });
|
|
1329
1481
|
}
|
|
1330
|
-
return /* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1482
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1483
|
+
/* @__PURE__ */ jsx7(
|
|
1332
1484
|
Header,
|
|
1333
1485
|
{
|
|
1334
1486
|
profile: props.session.profile,
|
|
@@ -1337,7 +1489,7 @@ function Dashboard(props) {
|
|
|
1337
1489
|
}
|
|
1338
1490
|
),
|
|
1339
1491
|
body,
|
|
1340
|
-
/* @__PURE__ */
|
|
1492
|
+
/* @__PURE__ */ jsx7(
|
|
1341
1493
|
Footer,
|
|
1342
1494
|
{
|
|
1343
1495
|
mode: hangulMode ? "\uD55C" : "EN",
|
|
@@ -1348,14 +1500,14 @@ function Dashboard(props) {
|
|
|
1348
1500
|
}
|
|
1349
1501
|
|
|
1350
1502
|
// src/tui/login-screen.tsx
|
|
1351
|
-
import { useState as
|
|
1352
|
-
import { Box as
|
|
1353
|
-
import { jsx as
|
|
1503
|
+
import { useState as useState9 } from "react";
|
|
1504
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
1505
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1354
1506
|
function LoginScreen(props) {
|
|
1355
|
-
const [email, setEmail] =
|
|
1356
|
-
const [password, setPassword] =
|
|
1357
|
-
const [focus, setFocus] =
|
|
1358
|
-
|
|
1507
|
+
const [email, setEmail] = useState9("");
|
|
1508
|
+
const [password, setPassword] = useState9("");
|
|
1509
|
+
const [focus, setFocus] = useState9("email");
|
|
1510
|
+
useInput7(
|
|
1359
1511
|
(input, key) => {
|
|
1360
1512
|
if (key.tab) setFocus((current) => current === "email" ? "password" : "email");
|
|
1361
1513
|
if (key.ctrl && input === "p") props.onProfiles();
|
|
@@ -1374,17 +1526,17 @@ function LoginScreen(props) {
|
|
|
1374
1526
|
props.onSubmit(email.trim(), secret);
|
|
1375
1527
|
}
|
|
1376
1528
|
};
|
|
1377
|
-
return /* @__PURE__ */
|
|
1378
|
-
/* @__PURE__ */
|
|
1379
|
-
/* @__PURE__ */
|
|
1380
|
-
/* @__PURE__ */
|
|
1381
|
-
/* @__PURE__ */
|
|
1529
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1530
|
+
/* @__PURE__ */ jsx8(Header, { profile: props.profile, connected: false }),
|
|
1531
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
|
|
1532
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: "\uB85C\uADF8\uC778" }),
|
|
1533
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
1382
1534
|
props.serverUrl,
|
|
1383
1535
|
" ",
|
|
1384
|
-
/* @__PURE__ */
|
|
1536
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "(Ctrl+E \uBC14\uAFB8\uAE30)" })
|
|
1385
1537
|
] }),
|
|
1386
|
-
/* @__PURE__ */
|
|
1387
|
-
/* @__PURE__ */
|
|
1538
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
1539
|
+
/* @__PURE__ */ jsx8(
|
|
1388
1540
|
FormField,
|
|
1389
1541
|
{
|
|
1390
1542
|
label: "Email",
|
|
@@ -1395,7 +1547,7 @@ function LoginScreen(props) {
|
|
|
1395
1547
|
placeholder: "me@corp.com"
|
|
1396
1548
|
}
|
|
1397
1549
|
),
|
|
1398
|
-
/* @__PURE__ */
|
|
1550
|
+
/* @__PURE__ */ jsx8(
|
|
1399
1551
|
FormField,
|
|
1400
1552
|
{
|
|
1401
1553
|
label: "Password",
|
|
@@ -1407,25 +1559,25 @@ function LoginScreen(props) {
|
|
|
1407
1559
|
}
|
|
1408
1560
|
)
|
|
1409
1561
|
] }),
|
|
1410
|
-
props.busy ? /* @__PURE__ */
|
|
1411
|
-
props.error ? /* @__PURE__ */
|
|
1562
|
+
props.busy ? /* @__PURE__ */ jsx8(Loading, { label: "\uB85C\uADF8\uC778\uD558\uB294 \uC911..." }) : null,
|
|
1563
|
+
props.error ? /* @__PURE__ */ jsx8(Notice, { error: true, children: props.error }) : null
|
|
1412
1564
|
] }),
|
|
1413
|
-
/* @__PURE__ */
|
|
1565
|
+
/* @__PURE__ */ jsx8(Footer, { text: "Tab \uC774\uB3D9 \xB7 Enter \uB85C\uADF8\uC778 \xB7 Ctrl+E \uC11C\uBC84 \xB7 Ctrl+P \uD504\uB85C\uD544 \xB7 Ctrl+Q \uC885\uB8CC" })
|
|
1414
1566
|
] });
|
|
1415
1567
|
}
|
|
1416
1568
|
|
|
1417
1569
|
// src/tui/profile-screen.tsx
|
|
1418
|
-
import { useEffect as
|
|
1419
|
-
import { Box as
|
|
1420
|
-
import { jsx as
|
|
1570
|
+
import { useEffect as useEffect8, useState as useState10 } from "react";
|
|
1571
|
+
import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
|
|
1572
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1421
1573
|
function ProfileScreen(props) {
|
|
1422
|
-
const [cursor, setCursor] =
|
|
1423
|
-
const [creating, setCreating] =
|
|
1424
|
-
const [focus, setFocus] =
|
|
1425
|
-
const [name, setName] =
|
|
1426
|
-
const [serverUrl, setServerUrl] =
|
|
1427
|
-
|
|
1428
|
-
|
|
1574
|
+
const [cursor, setCursor] = useState10(Math.max(0, props.profiles.findIndex((profile) => profile.current)));
|
|
1575
|
+
const [creating, setCreating] = useState10(false);
|
|
1576
|
+
const [focus, setFocus] = useState10("name");
|
|
1577
|
+
const [name, setName] = useState10("");
|
|
1578
|
+
const [serverUrl, setServerUrl] = useState10("");
|
|
1579
|
+
useEffect8(() => setCursor((current) => Math.min(current, Math.max(0, props.profiles.length - 1))), [props.profiles]);
|
|
1580
|
+
useInput8(
|
|
1429
1581
|
(input, key) => {
|
|
1430
1582
|
if (key.escape) {
|
|
1431
1583
|
if (creating) setCreating(false);
|
|
@@ -1455,12 +1607,12 @@ function ProfileScreen(props) {
|
|
|
1455
1607
|
}
|
|
1456
1608
|
if (name.trim() && serverUrl.trim()) props.onCreate(name.trim(), serverUrl.trim());
|
|
1457
1609
|
};
|
|
1458
|
-
return /* @__PURE__ */
|
|
1459
|
-
/* @__PURE__ */
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
creating ? /* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1610
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1611
|
+
/* @__PURE__ */ jsx9(Header, {}),
|
|
1612
|
+
/* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
|
|
1613
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: creating ? "\uC0C8 \uD504\uB85C\uD544" : "\uD504\uB85C\uD544 \uC804\uD658" }),
|
|
1614
|
+
creating ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
1615
|
+
/* @__PURE__ */ jsx9(
|
|
1464
1616
|
FormField,
|
|
1465
1617
|
{
|
|
1466
1618
|
label: "Name",
|
|
@@ -1471,7 +1623,7 @@ function ProfileScreen(props) {
|
|
|
1471
1623
|
placeholder: "corp"
|
|
1472
1624
|
}
|
|
1473
1625
|
),
|
|
1474
|
-
/* @__PURE__ */
|
|
1626
|
+
/* @__PURE__ */ jsx9(
|
|
1475
1627
|
FormField,
|
|
1476
1628
|
{
|
|
1477
1629
|
label: "Server URL",
|
|
@@ -1482,8 +1634,8 @@ function ProfileScreen(props) {
|
|
|
1482
1634
|
placeholder: "https://xgen.example.com"
|
|
1483
1635
|
}
|
|
1484
1636
|
)
|
|
1485
|
-
] }) : /* @__PURE__ */
|
|
1486
|
-
props.profiles.map((profile, index) => /* @__PURE__ */
|
|
1637
|
+
] }) : /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
1638
|
+
props.profiles.map((profile, index) => /* @__PURE__ */ jsxs9(Text9, { color: index === cursor ? "cyan" : void 0, children: [
|
|
1487
1639
|
index === cursor ? "\u203A" : " ",
|
|
1488
1640
|
" ",
|
|
1489
1641
|
profile.current ? "\u25CF" : "\u25CB",
|
|
@@ -1491,14 +1643,14 @@ function ProfileScreen(props) {
|
|
|
1491
1643
|
profile.name,
|
|
1492
1644
|
" \xB7",
|
|
1493
1645
|
" ",
|
|
1494
|
-
/* @__PURE__ */
|
|
1646
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: profile.serverUrl })
|
|
1495
1647
|
] }, profile.name)),
|
|
1496
|
-
props.profiles.length === 0 ? /* @__PURE__ */
|
|
1648
|
+
props.profiles.length === 0 ? /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\uC800\uC7A5\uB41C \uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }) : null
|
|
1497
1649
|
] }),
|
|
1498
|
-
props.busy ? /* @__PURE__ */
|
|
1499
|
-
props.error ? /* @__PURE__ */
|
|
1650
|
+
props.busy ? /* @__PURE__ */ jsx9(Loading, { label: "\uD504\uB85C\uD544\uC744 \uC804\uD658\uD558\uB294 \uC911..." }) : null,
|
|
1651
|
+
props.error ? /* @__PURE__ */ jsx9(Notice, { error: true, children: props.error }) : null
|
|
1500
1652
|
] }),
|
|
1501
|
-
/* @__PURE__ */
|
|
1653
|
+
/* @__PURE__ */ jsx9(
|
|
1502
1654
|
Footer,
|
|
1503
1655
|
{
|
|
1504
1656
|
text: creating ? "Tab \uC774\uB3D9 \xB7 Enter \uC800\uC7A5 \xB7 Esc \uB4A4\uB85C" : "\u2191\u2193 \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 N \uC0C8 \uD504\uB85C\uD544 \xB7 Esc \uB2EB\uAE30"
|
|
@@ -1508,24 +1660,24 @@ function ProfileScreen(props) {
|
|
|
1508
1660
|
}
|
|
1509
1661
|
|
|
1510
1662
|
// src/tui/server-screen.tsx
|
|
1511
|
-
import { useState as
|
|
1512
|
-
import { Box as
|
|
1513
|
-
import { jsx as
|
|
1663
|
+
import { useState as useState11 } from "react";
|
|
1664
|
+
import { Box as Box10, Text as Text10, useInput as useInput9 } from "ink";
|
|
1665
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1514
1666
|
function ServerScreen(props) {
|
|
1515
|
-
const [serverUrl, setServerUrl] =
|
|
1667
|
+
const [serverUrl, setServerUrl] = useState11(props.initialValue ?? "");
|
|
1516
1668
|
const editing = props.initialValue !== void 0;
|
|
1517
|
-
|
|
1669
|
+
useInput9(
|
|
1518
1670
|
(_input, key) => {
|
|
1519
1671
|
if (key.escape) props.onCancel?.();
|
|
1520
1672
|
},
|
|
1521
1673
|
{ isActive: !props.busy && !!props.onCancel }
|
|
1522
1674
|
);
|
|
1523
|
-
return /* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
/* @__PURE__ */
|
|
1526
|
-
/* @__PURE__ */
|
|
1527
|
-
/* @__PURE__ */
|
|
1528
|
-
/* @__PURE__ */
|
|
1675
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
1676
|
+
/* @__PURE__ */ jsx10(Header, { profile: props.profile, connected: false }),
|
|
1677
|
+
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
|
|
1678
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: editing ? "\uC11C\uBC84 \uC8FC\uC18C \uBC14\uAFB8\uAE30" : "\uCC98\uC74C \uC624\uC168\uAD70\uC694" }),
|
|
1679
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\uC5F0\uACB0\uD560 XGEN Gateway \uC8FC\uC18C\uB97C \uC785\uB825\uD558\uC138\uC694." }),
|
|
1680
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(
|
|
1529
1681
|
FormField,
|
|
1530
1682
|
{
|
|
1531
1683
|
label: "Server URL",
|
|
@@ -1536,11 +1688,11 @@ function ServerScreen(props) {
|
|
|
1536
1688
|
placeholder: "xgen.example.com"
|
|
1537
1689
|
}
|
|
1538
1690
|
) }),
|
|
1539
|
-
/* @__PURE__ */
|
|
1540
|
-
props.busy ? /* @__PURE__ */
|
|
1541
|
-
props.error ? /* @__PURE__ */
|
|
1691
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "https:// \uB294 \uC0DD\uB7B5\uD574\uB3C4 \uB429\uB2C8\uB2E4." }),
|
|
1692
|
+
props.busy ? /* @__PURE__ */ jsx10(Loading, { label: "\uC11C\uBC84 \uD504\uB85C\uD544\uC744 \uC800\uC7A5\uD558\uB294 \uC911..." }) : null,
|
|
1693
|
+
props.error ? /* @__PURE__ */ jsx10(Notice, { error: true, children: props.error }) : null
|
|
1542
1694
|
] }),
|
|
1543
|
-
/* @__PURE__ */
|
|
1695
|
+
/* @__PURE__ */ jsx10(
|
|
1544
1696
|
Footer,
|
|
1545
1697
|
{
|
|
1546
1698
|
text: props.onCancel ? "Enter \uC800\uC7A5 \xB7 Esc \uCDE8\uC18C \xB7 Ctrl+Q \uC885\uB8CC" : "Enter \uACC4\uC18D \xB7 Ctrl+Q \uC885\uB8CC"
|
|
@@ -1550,20 +1702,20 @@ function ServerScreen(props) {
|
|
|
1550
1702
|
}
|
|
1551
1703
|
|
|
1552
1704
|
// src/tui/app.tsx
|
|
1553
|
-
import { jsx as
|
|
1705
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1554
1706
|
function App({
|
|
1555
1707
|
engine,
|
|
1556
1708
|
preferences
|
|
1557
1709
|
}) {
|
|
1558
1710
|
const { exit } = useApp2();
|
|
1559
|
-
const [route, setRoute] =
|
|
1560
|
-
const [session, setSession] =
|
|
1561
|
-
const [profiles, setProfiles] =
|
|
1562
|
-
const [loginTarget, setLoginTarget] =
|
|
1563
|
-
const [editingServer, setEditingServer] =
|
|
1564
|
-
const [busy, setBusy] =
|
|
1565
|
-
const [error, setError] =
|
|
1566
|
-
|
|
1711
|
+
const [route, setRoute] = useState12("boot");
|
|
1712
|
+
const [session, setSession] = useState12();
|
|
1713
|
+
const [profiles, setProfiles] = useState12([]);
|
|
1714
|
+
const [loginTarget, setLoginTarget] = useState12();
|
|
1715
|
+
const [editingServer, setEditingServer] = useState12();
|
|
1716
|
+
const [busy, setBusy] = useState12(false);
|
|
1717
|
+
const [error, setError] = useState12();
|
|
1718
|
+
useInput10((input, key) => {
|
|
1567
1719
|
if (key.ctrl && input === "q") exit();
|
|
1568
1720
|
});
|
|
1569
1721
|
const bootstrap = useCallback(
|
|
@@ -1613,7 +1765,7 @@ function App({
|
|
|
1613
1765
|
},
|
|
1614
1766
|
[engine]
|
|
1615
1767
|
);
|
|
1616
|
-
|
|
1768
|
+
useEffect9(() => {
|
|
1617
1769
|
void bootstrap();
|
|
1618
1770
|
}, [bootstrap]);
|
|
1619
1771
|
const configure = async (serverUrl) => {
|
|
@@ -1688,14 +1840,14 @@ function App({
|
|
|
1688
1840
|
}
|
|
1689
1841
|
};
|
|
1690
1842
|
if (route === "boot") {
|
|
1691
|
-
return /* @__PURE__ */
|
|
1692
|
-
/* @__PURE__ */
|
|
1693
|
-
/* @__PURE__ */
|
|
1694
|
-
/* @__PURE__ */
|
|
1843
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1844
|
+
/* @__PURE__ */ jsx11(Header, {}),
|
|
1845
|
+
/* @__PURE__ */ jsx11(Loading, { label: "Dex\uB97C \uC900\uBE44\uD558\uB294 \uC911..." }),
|
|
1846
|
+
/* @__PURE__ */ jsx11(Footer, { text: "Ctrl+Q \uC885\uB8CC" })
|
|
1695
1847
|
] });
|
|
1696
1848
|
}
|
|
1697
1849
|
if (route === "server") {
|
|
1698
|
-
return /* @__PURE__ */
|
|
1850
|
+
return /* @__PURE__ */ jsx11(
|
|
1699
1851
|
ServerScreen,
|
|
1700
1852
|
{
|
|
1701
1853
|
initialValue: editingServer?.serverUrl,
|
|
@@ -1712,7 +1864,7 @@ function App({
|
|
|
1712
1864
|
);
|
|
1713
1865
|
}
|
|
1714
1866
|
if (route === "login" && loginTarget) {
|
|
1715
|
-
return /* @__PURE__ */
|
|
1867
|
+
return /* @__PURE__ */ jsx11(
|
|
1716
1868
|
LoginScreen,
|
|
1717
1869
|
{
|
|
1718
1870
|
profile: loginTarget.profile,
|
|
@@ -1730,7 +1882,7 @@ function App({
|
|
|
1730
1882
|
);
|
|
1731
1883
|
}
|
|
1732
1884
|
if (route === "profiles") {
|
|
1733
|
-
return /* @__PURE__ */
|
|
1885
|
+
return /* @__PURE__ */ jsx11(
|
|
1734
1886
|
ProfileScreen,
|
|
1735
1887
|
{
|
|
1736
1888
|
profiles,
|
|
@@ -1743,7 +1895,7 @@ function App({
|
|
|
1743
1895
|
);
|
|
1744
1896
|
}
|
|
1745
1897
|
if (route === "dashboard" && session) {
|
|
1746
|
-
return /* @__PURE__ */
|
|
1898
|
+
return /* @__PURE__ */ jsx11(
|
|
1747
1899
|
Dashboard,
|
|
1748
1900
|
{
|
|
1749
1901
|
preferences,
|
|
@@ -1754,41 +1906,41 @@ function App({
|
|
|
1754
1906
|
}
|
|
1755
1907
|
);
|
|
1756
1908
|
}
|
|
1757
|
-
return /* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1759
|
-
/* @__PURE__ */
|
|
1760
|
-
/* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
1909
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1910
|
+
/* @__PURE__ */ jsx11(Header, {}),
|
|
1911
|
+
/* @__PURE__ */ jsx11(Notice, { error: true, children: error ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4." }),
|
|
1912
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "\uC11C\uBC84\uC640 \uD0A4\uCCB4\uC778 \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694." }),
|
|
1913
|
+
/* @__PURE__ */ jsx11(Footer, { text: "R \uB2E4\uC2DC \uC2DC\uB3C4 \xB7 Ctrl+Q \uC885\uB8CC" }),
|
|
1914
|
+
/* @__PURE__ */ jsx11(RetryInput, { onRetry: () => void bootstrap() })
|
|
1763
1915
|
] });
|
|
1764
1916
|
}
|
|
1765
1917
|
function RetryInput({ onRetry }) {
|
|
1766
|
-
|
|
1918
|
+
useInput10((input) => {
|
|
1767
1919
|
if (input.toLowerCase() === "r") onRetry();
|
|
1768
1920
|
});
|
|
1769
1921
|
return null;
|
|
1770
1922
|
}
|
|
1771
1923
|
|
|
1772
1924
|
// src/tui/screen.ts
|
|
1773
|
-
var
|
|
1774
|
-
var ENTER_ALT_SCREEN = `${
|
|
1775
|
-
var LEAVE_ALT_SCREEN = `${
|
|
1776
|
-
var SHOW_CURSOR = `${
|
|
1925
|
+
var ESC = "\x1B";
|
|
1926
|
+
var ENTER_ALT_SCREEN = `${ESC}[?1049h`;
|
|
1927
|
+
var LEAVE_ALT_SCREEN = `${ESC}[?1049l`;
|
|
1928
|
+
var SHOW_CURSOR = `${ESC}[?25h`;
|
|
1777
1929
|
var DISABLE_REPORTS = [
|
|
1778
|
-
`${
|
|
1930
|
+
`${ESC}[?1004l`,
|
|
1779
1931
|
// 포커스 들어옴/나감
|
|
1780
|
-
`${
|
|
1932
|
+
`${ESC}[?1000l`,
|
|
1781
1933
|
// 마우스 클릭
|
|
1782
|
-
`${
|
|
1934
|
+
`${ESC}[?1002l`,
|
|
1783
1935
|
// 마우스 드래그
|
|
1784
|
-
`${
|
|
1936
|
+
`${ESC}[?1003l`,
|
|
1785
1937
|
// 마우스 이동 전부
|
|
1786
|
-
`${
|
|
1938
|
+
`${ESC}[?1006l`
|
|
1787
1939
|
// SGR 확장 좌표
|
|
1788
1940
|
].join("");
|
|
1789
|
-
var ENABLE_BRACKETED_PASTE = `${
|
|
1790
|
-
var DISABLE_BRACKETED_PASTE = `${
|
|
1791
|
-
var POP_KITTY_KEYBOARD = `${
|
|
1941
|
+
var ENABLE_BRACKETED_PASTE = `${ESC}[?2004h`;
|
|
1942
|
+
var DISABLE_BRACKETED_PASTE = `${ESC}[?2004l`;
|
|
1943
|
+
var POP_KITTY_KEYBOARD = `${ESC}[<u`;
|
|
1792
1944
|
function createScreenGuard(stream, options = {}) {
|
|
1793
1945
|
const alt = Boolean(stream.isTTY);
|
|
1794
1946
|
let entered = false;
|
|
@@ -1841,8 +1993,133 @@ async function writePreferences(preferences, env = process.env) {
|
|
|
1841
1993
|
}
|
|
1842
1994
|
}
|
|
1843
1995
|
|
|
1996
|
+
// src/tui/kitty.ts
|
|
1997
|
+
var ESC2 = "\x1B";
|
|
1998
|
+
var QUERY = `${ESC2}[?u`;
|
|
1999
|
+
var KEY_CODES = {
|
|
2000
|
+
57358: "capslock",
|
|
2001
|
+
57449: "rightalt"
|
|
2002
|
+
};
|
|
2003
|
+
var CAPS_LOCK_BIT = 64;
|
|
2004
|
+
function parseKeyEvents(chunk) {
|
|
2005
|
+
const events = [];
|
|
2006
|
+
const pattern = /\u001B\[(\d+)(?::\d+)*(?:;(\d+)(?::(\d+))?)?(?:;[\d:]*)?u/g;
|
|
2007
|
+
for (const match of chunk.matchAll(pattern)) {
|
|
2008
|
+
const code = Number(match[1]);
|
|
2009
|
+
const modifiers = match[2] ? Number(match[2]) - 1 : 0;
|
|
2010
|
+
events.push({
|
|
2011
|
+
name: KEY_CODES[code],
|
|
2012
|
+
// eslint-disable-next-line no-bitwise
|
|
2013
|
+
capsLock: (modifiers & CAPS_LOCK_BIT) !== 0,
|
|
2014
|
+
eventType: match[3] ? Number(match[3]) : 1
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
return events;
|
|
2018
|
+
}
|
|
2019
|
+
function knownKittyTerminal(env = process.env) {
|
|
2020
|
+
if (env.KITTY_WINDOW_ID) return true;
|
|
2021
|
+
if (env.GHOSTTY_RESOURCES_DIR) return true;
|
|
2022
|
+
const term = (env.TERM ?? "").toLowerCase();
|
|
2023
|
+
if (term === "xterm-kitty" || term === "xterm-ghostty" || term.includes("foot")) return true;
|
|
2024
|
+
const program = (env.TERM_PROGRAM ?? "").toLowerCase();
|
|
2025
|
+
return program === "wezterm" || program === "ghostty" || program === "kitty";
|
|
2026
|
+
}
|
|
2027
|
+
async function supportsKittyKeyboard(streams, timeoutMs = 200, env = process.env) {
|
|
2028
|
+
if (env.DEX_NO_KITTY === "1") return false;
|
|
2029
|
+
if (!streams.stdout.isTTY || !streams.isTTY) return false;
|
|
2030
|
+
if (knownKittyTerminal(env)) return true;
|
|
2031
|
+
const wasRaw = streams.stdin.isRaw === true;
|
|
2032
|
+
const wasPaused = !wasRaw;
|
|
2033
|
+
streams.stdin.setRawMode?.(true);
|
|
2034
|
+
streams.stdin.resume?.();
|
|
2035
|
+
return new Promise((resolve) => {
|
|
2036
|
+
let settled = false;
|
|
2037
|
+
let seen = "";
|
|
2038
|
+
const finish = (supported) => {
|
|
2039
|
+
if (settled) return;
|
|
2040
|
+
settled = true;
|
|
2041
|
+
clearTimeout(timer);
|
|
2042
|
+
streams.stdin.removeListener("data", onData);
|
|
2043
|
+
if (!wasRaw) streams.stdin.setRawMode?.(false);
|
|
2044
|
+
if (wasPaused) streams.stdin.pause?.();
|
|
2045
|
+
resolve(supported);
|
|
2046
|
+
};
|
|
2047
|
+
const onData = (data) => {
|
|
2048
|
+
seen += typeof data === "string" ? data : data.toString("utf8");
|
|
2049
|
+
if (/\u001B\[\?\d*u/.test(seen)) finish(true);
|
|
2050
|
+
};
|
|
2051
|
+
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
2052
|
+
streams.stdin.on("data", onData);
|
|
2053
|
+
streams.stdout.write(QUERY);
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
var KEY_EVENT = /\u001B\[([0-9]+)(?::[0-9:]*)?(?:;([0-9]*(?::[0-9]+)?))?(?:;([0-9:]+))?u/g;
|
|
2057
|
+
function normalizeKeyEvents(chunk) {
|
|
2058
|
+
return chunk.replace(
|
|
2059
|
+
KEY_EVENT,
|
|
2060
|
+
(all, code, modifiers, text) => {
|
|
2061
|
+
const hasAlternate = all.slice(0, all.indexOf(";") === -1 ? all.length : all.indexOf(";")).includes(":");
|
|
2062
|
+
const needsModifier = modifiers !== void 0 && modifiers.length === 0;
|
|
2063
|
+
if (!hasAlternate && !needsModifier) return all;
|
|
2064
|
+
const mods = modifiers === void 0 ? void 0 : modifiers.length > 0 ? modifiers : "1";
|
|
2065
|
+
const fields = [code, mods, text].filter((field) => field !== void 0).join(";");
|
|
2066
|
+
return `\x1B[${fields}u`;
|
|
2067
|
+
}
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
var PARTIAL_TAIL = /\u001B\[[0-9:;]*$/;
|
|
2071
|
+
var MAX_PARTIAL = 64;
|
|
2072
|
+
function createNormalizer() {
|
|
2073
|
+
let pending = "";
|
|
2074
|
+
return (chunk) => {
|
|
2075
|
+
const combined = pending + chunk;
|
|
2076
|
+
const match = PARTIAL_TAIL.exec(combined);
|
|
2077
|
+
if (match && combined.length - match.index <= MAX_PARTIAL) {
|
|
2078
|
+
pending = combined.slice(match.index);
|
|
2079
|
+
return normalizeKeyEvents(combined.slice(0, match.index));
|
|
2080
|
+
}
|
|
2081
|
+
pending = "";
|
|
2082
|
+
return normalizeKeyEvents(combined);
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
function normalizedStdin(stdin2, onSpecialKey) {
|
|
2086
|
+
const normalize = createNormalizer();
|
|
2087
|
+
return new Proxy(stdin2, {
|
|
2088
|
+
get(target, property, receiver) {
|
|
2089
|
+
if (property === "read") {
|
|
2090
|
+
return (...args) => {
|
|
2091
|
+
const chunk = target.read(...args);
|
|
2092
|
+
if (chunk === null || chunk === void 0) return null;
|
|
2093
|
+
const text = typeof chunk === "string" ? chunk : String(chunk);
|
|
2094
|
+
if (onSpecialKey) {
|
|
2095
|
+
for (const event of parseKeyEvents(text)) {
|
|
2096
|
+
if (event.name && event.eventType === 1) onSpecialKey(event.name);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
return normalize(text);
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
void receiver;
|
|
2103
|
+
const value = Reflect.get(target, property, target);
|
|
2104
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
2105
|
+
}
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
function createKeySignal() {
|
|
2109
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2110
|
+
return {
|
|
2111
|
+
notify(name) {
|
|
2112
|
+
for (const listener of listeners) listener(name);
|
|
2113
|
+
},
|
|
2114
|
+
subscribe(listener) {
|
|
2115
|
+
listeners.add(listener);
|
|
2116
|
+
return () => void listeners.delete(listener);
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
|
|
1844
2121
|
// src/tui/index.tsx
|
|
1845
|
-
import { jsx as
|
|
2122
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1846
2123
|
async function runTui(engine) {
|
|
1847
2124
|
let screen = createScreenGuard(stdout);
|
|
1848
2125
|
const restore = () => screen.restore();
|
|
@@ -1856,22 +2133,31 @@ async function runTui(engine) {
|
|
|
1856
2133
|
const preferences = await readPreferences();
|
|
1857
2134
|
const kitty = await supportsKittyKeyboard({ stdin, stdout, isTTY: stdin.isTTY });
|
|
1858
2135
|
screen = createScreenGuard(stdout, { kittyKeyboard: kitty });
|
|
2136
|
+
const modeKeys = createKeySignal();
|
|
1859
2137
|
screen.enter();
|
|
1860
2138
|
try {
|
|
1861
2139
|
const instance = render(
|
|
1862
|
-
/* @__PURE__ */
|
|
2140
|
+
/* @__PURE__ */ jsx12(
|
|
1863
2141
|
App,
|
|
1864
2142
|
{
|
|
1865
2143
|
engine,
|
|
1866
2144
|
preferences: {
|
|
1867
2145
|
hangulMode: preferences.hangulMode,
|
|
1868
|
-
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled })
|
|
2146
|
+
onHangulModeChange: (enabled) => void writePreferences({ hangulMode: enabled }),
|
|
2147
|
+
onModeKey: modeKeys.subscribe
|
|
1869
2148
|
}
|
|
1870
2149
|
}
|
|
1871
2150
|
),
|
|
1872
2151
|
{
|
|
1873
2152
|
exitOnCtrlC: true,
|
|
1874
2153
|
patchConsole: true,
|
|
2154
|
+
// 프로토콜을 켜면 kitty 가 ink 이 못 읽는 모양으로 키를 보낸다 — 사이에서
|
|
2155
|
+
// 고쳐 넘기지 않으면 글자가 하나도 입력되지 않는다.
|
|
2156
|
+
...kitty ? {
|
|
2157
|
+
stdin: normalizedStdin(stdin, (name) => {
|
|
2158
|
+
if (name === "rightalt" || name === "capslock") modeKeys.notify(name);
|
|
2159
|
+
})
|
|
2160
|
+
} : {},
|
|
1875
2161
|
// 모든 키를 이스케이프로 받아야 한/영 키(오른쪽 Alt)와 Caps Lock 이 사건으로
|
|
1876
2162
|
// 온다. 그런데 그렇게만 켜면 터미널이 **글쇠 코드만** 보내서 Shift+r 이
|
|
1877
2163
|
// 소문자 `r` 로 도착한다 — 된소리도 영문 대문자도 사라진다. 그래서 글자까지
|
|
@@ -1899,4 +2185,4 @@ async function runTui(engine) {
|
|
|
1899
2185
|
export {
|
|
1900
2186
|
runTui
|
|
1901
2187
|
};
|
|
1902
|
-
//# sourceMappingURL=tui-
|
|
2188
|
+
//# sourceMappingURL=tui-WG5AIWUR.js.map
|