xgen-dex-cli 1.4.0 → 1.5.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.
@@ -8,12 +8,12 @@ import { render } from "ink";
8
8
  import { stdout } from "node:process";
9
9
 
10
10
  // src/tui/app.tsx
11
- import { useCallback, useEffect as useEffect7, useState as useState10 } from "react";
12
- import { Box as Box9, Text as Text10, useApp as useApp2, useInput as useInput9 } from "ink";
11
+ import { useCallback, useEffect as useEffect8, useState as useState11 } from "react";
12
+ import { Box as Box10, Text as Text10, useApp as useApp2, useInput as useInput9 } from "ink";
13
13
 
14
14
  // src/tui/dashboard.tsx
15
- import { useEffect as useEffect5, useReducer, useRef as useRef2, useState as useState6 } from "react";
16
- import { Box as Box5, Text as Text6, useApp, useInput as useInput5 } from "ink";
15
+ import { useEffect as useEffect6, useReducer, useRef as useRef3, useState as useState7 } from "react";
16
+ import { Box as Box6, Text as Text6, useApp, useInput as useInput5 } from "ink";
17
17
 
18
18
  // src/tui/chat-state.ts
19
19
  var initialChatState = { messages: [], running: false };
@@ -134,18 +134,178 @@ function chatReducer(state, action) {
134
134
  }
135
135
  }
136
136
 
137
+ // src/tui/measure.ts
138
+ import { useEffect, useRef, useState } from "react";
139
+ function placementOf(node) {
140
+ const yogaSelf = node?.yogaNode;
141
+ if (!node || !yogaSelf) return void 0;
142
+ let x = 0;
143
+ let y = 0;
144
+ for (let current = node; current; current = current.parentNode) {
145
+ const yoga = current.yogaNode;
146
+ if (!yoga) continue;
147
+ x += yoga.getComputedLeft();
148
+ y += yoga.getComputedTop();
149
+ }
150
+ return { x, y, width: yogaSelf.getComputedWidth(), height: yogaSelf.getComputedHeight() };
151
+ }
152
+ function useMeasured() {
153
+ const ref = useRef(null);
154
+ const [placement, setPlacement] = useState(void 0);
155
+ useEffect(() => {
156
+ const measured = placementOf(ref.current);
157
+ if (!measured) return;
158
+ if (placement?.x !== measured.x || placement?.y !== measured.y || placement?.width !== measured.width || placement?.height !== measured.height) {
159
+ setPlacement(measured);
160
+ }
161
+ });
162
+ return [ref, placement];
163
+ }
164
+
165
+ // src/tui/transcript.ts
166
+ import stringWidth from "string-width";
167
+ var INDENT = " ";
168
+ function colorOf(role) {
169
+ if (role === "user") return "cyan";
170
+ if (role === "assistant") return "green";
171
+ if (role === "activity") return "yellow";
172
+ if (role === "system") return "red";
173
+ return void 0;
174
+ }
175
+ function labelOf(role, agentName) {
176
+ if (role === "user") return "You";
177
+ if (role === "assistant") return agentName;
178
+ if (role === "activity") return "Tool";
179
+ return "System";
180
+ }
181
+ function wrapToWidth(text, width) {
182
+ if (width <= 0) return [text];
183
+ const lines = [];
184
+ for (const paragraph of text.split("\n")) {
185
+ if (paragraph === "") {
186
+ lines.push("");
187
+ continue;
188
+ }
189
+ let line = "";
190
+ let lineWidth = 0;
191
+ const flush = () => {
192
+ lines.push(line);
193
+ line = "";
194
+ lineWidth = 0;
195
+ };
196
+ for (const word of paragraph.match(/\s+|\S+/g) ?? []) {
197
+ const wordWidth = stringWidth(word);
198
+ if (lineWidth > 0 && lineWidth + wordWidth > width) {
199
+ flush();
200
+ if (/^\s+$/.test(word)) continue;
201
+ }
202
+ if (wordWidth <= width) {
203
+ line += word;
204
+ lineWidth += wordWidth;
205
+ continue;
206
+ }
207
+ for (const char of word) {
208
+ const charWidth = stringWidth(char);
209
+ if (lineWidth + charWidth > width) flush();
210
+ line += char;
211
+ lineWidth += charWidth;
212
+ }
213
+ }
214
+ flush();
215
+ }
216
+ return lines;
217
+ }
218
+ function ruleFor(label, width) {
219
+ const room = Math.max(0, width - stringWidth("\u2500\u2500 "));
220
+ let trimmed = "";
221
+ let used = 0;
222
+ for (const char of label) {
223
+ const charWidth = stringWidth(char);
224
+ if (used + charWidth > room) {
225
+ trimmed = trimmed.slice(0, -1) + "\u2026";
226
+ break;
227
+ }
228
+ trimmed += char;
229
+ used += charWidth;
230
+ }
231
+ const head = `\u2500\u2500 ${trimmed} `;
232
+ return head + "\u2500".repeat(Math.max(0, width - stringWidth(head)));
233
+ }
234
+ function renderTranscript(messages, agentName, width) {
235
+ const lines = [];
236
+ const bodyWidth = Math.max(1, width - INDENT.length);
237
+ for (const message of messages) {
238
+ const color = colorOf(message.role);
239
+ if (message.role === "activity") {
240
+ for (const [index, text] of wrapToWidth(message.text, bodyWidth).entries()) {
241
+ lines.push({
242
+ key: `${message.id}:${index}`,
243
+ text: `${index === 0 ? "\xB7 " : INDENT}${text}`,
244
+ role: "activity",
245
+ color
246
+ });
247
+ }
248
+ continue;
249
+ }
250
+ if (lines.length > 0) lines.push({ key: `${message.id}:gap`, text: "", role: "text" });
251
+ lines.push({
252
+ key: `${message.id}:label`,
253
+ text: ruleFor(labelOf(message.role, agentName), width),
254
+ role: "label",
255
+ color
256
+ });
257
+ const body = message.text || (message.role === "assistant" ? "\u2026" : "");
258
+ for (const [index, text] of wrapToWidth(body, bodyWidth).entries()) {
259
+ lines.push({
260
+ key: `${message.id}:${index}`,
261
+ text: INDENT + text,
262
+ role: message.role === "system" ? "system" : "text",
263
+ color: message.role === "system" ? color : void 0
264
+ });
265
+ }
266
+ }
267
+ return lines;
268
+ }
269
+ function viewportOf(lines, height, scrollUp) {
270
+ if (height <= 0) return { lines: [], above: lines.length, below: 0 };
271
+ const maximumScroll2 = Math.max(0, lines.length - height);
272
+ const up = Math.min(Math.max(0, scrollUp), maximumScroll2);
273
+ const end = lines.length - up;
274
+ const start = Math.max(0, end - height);
275
+ return { lines: lines.slice(start, end), above: start, below: lines.length - end };
276
+ }
277
+ function maximumScroll(lineCount, height) {
278
+ return Math.max(0, lineCount - Math.max(0, height));
279
+ }
280
+
137
281
  // src/tui/command-palette.tsx
138
- import { useState as useState2 } from "react";
139
- import { Box as Box2, Text as Text3, useInput as useInput2 } from "ink";
282
+ import { useState as useState3 } from "react";
283
+ import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
140
284
 
141
285
  // src/tui/components.tsx
142
- import { Box, Text as Text2 } from "ink";
286
+ import { Box as Box2, Text as Text2 } from "ink";
143
287
 
144
288
  // src/tui/ime-text-input.tsx
145
- import { useEffect, useRef, useState } from "react";
146
- import { Text, useCursor, useInput, useStdout } from "ink";
147
- import stringWidth from "string-width";
148
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
289
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
290
+ import { Box, Text, useCursor, useInput } from "ink";
291
+ import stringWidth2 from "string-width";
292
+
293
+ // src/tui/terminal-input.ts
294
+ var PASTE_START = "[200~";
295
+ var PASTE_END = "[201~";
296
+ var CONTROL_TAIL = /^(?:\[[\d;:<>?!"'$ ]*[@-~]|O[@-~])$/;
297
+ var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
298
+ function classifyInput(input, pasting = false) {
299
+ if (input === PASTE_START) return { kind: "paste-start" };
300
+ if (input === PASTE_END) return { kind: "paste-end" };
301
+ if (!pasting && input.length > 1 && CONTROL_TAIL.test(input)) return { kind: "ignore" };
302
+ const flattened = input.replace(/\r\n|[\r\n]/g, " ");
303
+ const text = flattened.replace(CONTROL_CHARS, "");
304
+ return text ? { kind: "text", text } : { kind: "ignore" };
305
+ }
306
+
307
+ // src/tui/ime-text-input.tsx
308
+ import { jsx, jsxs } from "react/jsx-runtime";
149
309
  var segmenter = new Intl.Segmenter("ko", { granularity: "grapheme" });
150
310
  function graphemes(value) {
151
311
  return [...segmenter.segment(value)].map(({ segment }) => segment);
@@ -156,10 +316,10 @@ function clamp(value, minimum, maximum) {
156
316
  function visibleInput(segments, cursor, maximumWidth) {
157
317
  let start = cursor;
158
318
  let widthBeforeCursor = 0;
159
- const followingWidth = cursor < segments.length ? stringWidth(segments[cursor] ?? "") : 0;
319
+ const followingWidth = cursor < segments.length ? stringWidth2(segments[cursor] ?? "") : 0;
160
320
  const beforeLimit = Math.max(0, maximumWidth - Math.min(followingWidth, maximumWidth));
161
321
  while (start > 0) {
162
- const width = stringWidth(segments[start - 1] ?? "");
322
+ const width = stringWidth2(segments[start - 1] ?? "");
163
323
  if (widthBeforeCursor + width > beforeLimit) break;
164
324
  widthBeforeCursor += width;
165
325
  start -= 1;
@@ -167,7 +327,7 @@ function visibleInput(segments, cursor, maximumWidth) {
167
327
  let end = cursor;
168
328
  let totalWidth = widthBeforeCursor;
169
329
  while (end < segments.length) {
170
- const width = stringWidth(segments[end] ?? "");
330
+ const width = stringWidth2(segments[end] ?? "");
171
331
  if (totalWidth + width > maximumWidth) break;
172
332
  totalWidth += width;
173
333
  end += 1;
@@ -177,17 +337,18 @@ function visibleInput(segments, cursor, maximumWidth) {
177
337
  cursorWidth: widthBeforeCursor
178
338
  };
179
339
  }
180
- function TerminalCursor({ origin, offset }) {
340
+ function TerminalCursor({ x, y }) {
181
341
  const { setCursorPosition } = useCursor();
182
- setCursorPosition({ x: origin.x + offset, y: origin.y });
342
+ setCursorPosition({ x, y });
183
343
  return null;
184
344
  }
185
345
  function ImeTextInput(props) {
186
- const { stdout: stdout2 } = useStdout();
346
+ const [ref, placement] = useMeasured();
187
347
  const initialSegments = graphemes(props.value);
188
- const [cursor, setCursor] = useState(initialSegments.length);
189
- const valueRef = useRef(props.value);
190
- const cursorRef = useRef(initialSegments.length);
348
+ const [cursor, setCursor] = useState2(initialSegments.length);
349
+ const valueRef = useRef2(props.value);
350
+ const cursorRef = useRef2(initialSegments.length);
351
+ const pastingRef = useRef2(false);
191
352
  const moveCursor = (next, length) => {
192
353
  const resolved = clamp(next, 0, length);
193
354
  cursorRef.current = resolved;
@@ -199,7 +360,7 @@ function ImeTextInput(props) {
199
360
  moveCursor(nextCursor, segments.length);
200
361
  props.onChange(nextValue);
201
362
  };
202
- useEffect(() => {
363
+ useEffect2(() => {
203
364
  if (props.value === valueRef.current) return;
204
365
  const previousLength = graphemes(valueRef.current).length;
205
366
  const nextLength = graphemes(props.value).length;
@@ -211,6 +372,23 @@ function ImeTextInput(props) {
211
372
  (input, key) => {
212
373
  const current = graphemes(valueRef.current);
213
374
  const currentCursor = clamp(cursorRef.current, 0, current.length);
375
+ const event = classifyInput(input, pastingRef.current);
376
+ if (event.kind === "paste-start") {
377
+ pastingRef.current = true;
378
+ return;
379
+ }
380
+ if (event.kind === "paste-end") {
381
+ pastingRef.current = false;
382
+ return;
383
+ }
384
+ if (pastingRef.current) {
385
+ if (event.kind === "text") {
386
+ const inserted2 = graphemes(event.text);
387
+ current.splice(currentCursor, 0, ...inserted2);
388
+ updateValue(current, currentCursor + inserted2.length);
389
+ }
390
+ return;
391
+ }
214
392
  if (key.return) {
215
393
  props.onSubmit?.(valueRef.current);
216
394
  return;
@@ -237,10 +415,11 @@ function ImeTextInput(props) {
237
415
  updateValue(current, currentCursor - 1);
238
416
  return;
239
417
  }
240
- if (!input || key.ctrl || key.meta || key.tab || key.escape || key.upArrow || key.downArrow || key.pageUp || key.pageDown) {
418
+ if (key.ctrl || key.meta || key.tab || key.escape || key.upArrow || key.downArrow || key.pageUp || key.pageDown) {
241
419
  return;
242
420
  }
243
- const inserted = graphemes(input);
421
+ if (event.kind !== "text") return;
422
+ const inserted = graphemes(event.text);
244
423
  current.splice(currentCursor, 0, ...inserted);
245
424
  updateValue(current, currentCursor + inserted.length);
246
425
  },
@@ -249,18 +428,18 @@ function ImeTextInput(props) {
249
428
  const rawSegments = graphemes(props.value);
250
429
  const safeCursor = clamp(cursor, 0, rawSegments.length);
251
430
  const displayedSegments = props.mask ? rawSegments.map(() => props.mask ?? "") : rawSegments;
252
- const maximumWidth = Math.max(1, (stdout2.columns || 100) - props.cursorOrigin.x - 3);
431
+ const maximumWidth = Math.max(1, (placement?.width ?? 40) - 1);
253
432
  const visible = visibleInput(displayedSegments, safeCursor, maximumWidth);
254
- return /* @__PURE__ */ jsxs(Fragment, { children: [
433
+ return /* @__PURE__ */ jsxs(Box, { ref, flexGrow: 1, children: [
255
434
  rawSegments.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: props.placeholder ?? "" }) : /* @__PURE__ */ jsx(Text, { children: visible.text }),
256
- props.focus ? /* @__PURE__ */ jsx(TerminalCursor, { origin: props.cursorOrigin, offset: visible.cursorWidth }) : null
435
+ props.focus && placement ? /* @__PURE__ */ jsx(TerminalCursor, { x: placement.x + visible.cursorWidth, y: placement.y }) : null
257
436
  ] });
258
437
  }
259
438
 
260
439
  // src/tui/components.tsx
261
440
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
262
441
  function Header(props) {
263
- return /* @__PURE__ */ jsxs2(Box, { paddingX: 1, justifyContent: "space-between", children: [
442
+ return /* @__PURE__ */ jsxs2(Box2, { paddingX: 1, justifyContent: "space-between", children: [
264
443
  /* @__PURE__ */ jsx2(Text2, { bold: true, color: "blueBright", children: "XGEN Dex" }),
265
444
  props.profile ? /* @__PURE__ */ jsxs2(Text2, { children: [
266
445
  props.profile,
@@ -273,52 +452,55 @@ function Header(props) {
273
452
  ] });
274
453
  }
275
454
  function Footer({ text }) {
276
- return /* @__PURE__ */ jsx2(Box, { paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: text }) });
455
+ return /* @__PURE__ */ jsx2(Box2, { paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: text }) });
277
456
  }
278
457
  function Loading({ label = "\uBD88\uB7EC\uC624\uB294 \uC911..." }) {
279
- return /* @__PURE__ */ jsx2(Box, { padding: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
458
+ return /* @__PURE__ */ jsx2(Box2, { padding: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
280
459
  "\u25C6 ",
281
460
  label
282
461
  ] }) });
283
462
  }
284
463
  function Notice({ children, error = false }) {
285
- return /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: error ? "red" : "cyan", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { color: error ? "red" : void 0, children }) });
464
+ return /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: error ? "red" : "cyan", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { color: error ? "red" : void 0, children }) });
286
465
  }
287
466
  function FormField(props) {
288
- return /* @__PURE__ */ jsxs2(Box, { children: [
289
- /* @__PURE__ */ jsx2(Box, { width: 14, children: /* @__PURE__ */ jsxs2(Text2, { color: props.focus ? "cyan" : void 0, children: [
290
- props.focus ? "\u203A" : " ",
291
- " ",
292
- props.label
293
- ] }) }),
294
- /* @__PURE__ */ jsx2(
295
- ImeTextInput,
296
- {
297
- value: props.value,
298
- onChange: props.onChange,
299
- onSubmit: props.onSubmit,
300
- focus: props.focus,
301
- cursorOrigin: props.cursorOrigin,
302
- placeholder: props.placeholder,
303
- mask: props.secret ? "\u2022" : void 0
304
- }
305
- )
306
- ] });
467
+ return (
468
+ // 가로로 늘려 둬야 입력 칸이 남는 폭을 있다. 내용만큼만 넓으면 칸의 폭이
469
+ // 글자 폭이 되어, 긴 주소가 스스로를 잘라 내는 꼴이 된다.
470
+ /* @__PURE__ */ jsxs2(Box2, { flexGrow: 1, children: [
471
+ /* @__PURE__ */ jsx2(Box2, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsxs2(Text2, { color: props.focus ? "cyan" : void 0, children: [
472
+ props.focus ? "\u203A" : " ",
473
+ " ",
474
+ props.label
475
+ ] }) }),
476
+ /* @__PURE__ */ jsx2(
477
+ ImeTextInput,
478
+ {
479
+ value: props.value,
480
+ onChange: props.onChange,
481
+ onSubmit: props.onSubmit,
482
+ focus: props.focus,
483
+ placeholder: props.placeholder,
484
+ mask: props.secret ? "\u2022" : void 0
485
+ }
486
+ )
487
+ ] })
488
+ );
307
489
  }
308
490
 
309
491
  // src/tui/command-palette.tsx
310
492
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
311
493
  function CommandPalette(props) {
312
- const [cursor, setCursor] = useState2(0);
494
+ const [cursor, setCursor] = useState3(0);
313
495
  useInput2((_input, key) => {
314
496
  if (key.escape) props.onCancel();
315
497
  if (key.upArrow) setCursor((current) => Math.max(0, current - 1));
316
498
  if (key.downArrow) setCursor((current) => Math.min(props.actions.length - 1, current + 1));
317
499
  if (key.return && props.actions[cursor]) props.actions[cursor].run();
318
500
  });
319
- return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", flexGrow: 1, borderStyle: "double", borderColor: "magenta", padding: 1, children: [
501
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", flexGrow: 1, borderStyle: "double", borderColor: "magenta", padding: 1, children: [
320
502
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: "\uBA85\uB839" }),
321
- /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginTop: 1, children: props.actions.map((action, index) => /* @__PURE__ */ jsxs3(Text3, { color: index === cursor ? "magentaBright" : void 0, children: [
503
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginTop: 1, children: props.actions.map((action, index) => /* @__PURE__ */ jsxs3(Text3, { color: index === cursor ? "magentaBright" : void 0, children: [
322
504
  index === cursor ? "\u203A" : " ",
323
505
  " ",
324
506
  action.label
@@ -328,15 +510,15 @@ function CommandPalette(props) {
328
510
  }
329
511
 
330
512
  // src/tui/history-screen.tsx
331
- import { useEffect as useEffect2, useState as useState3 } from "react";
332
- import { Box as Box3, Text as Text4, useInput as useInput3 } from "ink";
513
+ import { useEffect as useEffect3, useState as useState4 } from "react";
514
+ import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
333
515
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
334
516
  function HistoryScreen(props) {
335
- const [items, setItems] = useState3([]);
336
- const [cursor, setCursor] = useState3(0);
337
- const [loading, setLoading] = useState3(true);
338
- const [error, setError] = useState3();
339
- useEffect2(() => {
517
+ const [items, setItems] = useState4([]);
518
+ const [cursor, setCursor] = useState4(0);
519
+ const [loading, setLoading] = useState4(true);
520
+ const [error, setError] = useState4();
521
+ useEffect3(() => {
340
522
  let alive = true;
341
523
  props.engine.listConversations(props.profile).then((result) => alive && setItems(result)).catch((reason) => alive && setError(publicError(reason).message)).finally(() => alive && setLoading(false));
342
524
  return () => {
@@ -364,7 +546,7 @@ function HistoryScreen(props) {
364
546
  },
365
547
  { isActive: !loading }
366
548
  );
367
- return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", padding: 1, children: [
549
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", padding: 1, children: [
368
550
  /* @__PURE__ */ jsx4(Text4, { bold: true, children: "\uB300\uD654 \uAE30\uB85D" }),
369
551
  loading ? /* @__PURE__ */ jsx4(Loading, {}) : null,
370
552
  !loading && items.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\uB300\uD654 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }) : null,
@@ -385,18 +567,18 @@ function HistoryScreen(props) {
385
567
  }
386
568
 
387
569
  // src/tui/start-panel.tsx
388
- import { useEffect as useEffect3, useState as useState4 } from "react";
389
- import { Box as Box4, Text as Text5, useInput as useInput4 } from "ink";
570
+ import { useEffect as useEffect4, useState as useState5 } from "react";
571
+ import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
390
572
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
391
573
  function StartPanel(props) {
392
574
  const rows = [
393
575
  { kind: "new" },
394
576
  ...props.conversations.map((conversation) => ({ kind: "conversation", conversation }))
395
577
  ];
396
- const [cursor, setCursor] = useState4(0);
397
- const [opening, setOpening] = useState4(false);
398
- const [error, setError] = useState4();
399
- useEffect3(() => {
578
+ const [cursor, setCursor] = useState5(0);
579
+ const [opening, setOpening] = useState5(false);
580
+ const [error, setError] = useState5();
581
+ useEffect4(() => {
400
582
  setCursor((current) => Math.min(current, Math.max(0, rows.length - 1)));
401
583
  }, [rows.length]);
402
584
  useInput4(
@@ -429,10 +611,10 @@ function StartPanel(props) {
429
611
  },
430
612
  { isActive: !opening }
431
613
  );
432
- return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", padding: 1, children: [
614
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", padding: 1, children: [
433
615
  /* @__PURE__ */ jsx5(Text5, { bold: true, children: props.agentName }),
434
616
  /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\uC5B4\uB5BB\uAC8C \uC2DC\uC791\uD560\uAE4C\uC694?" }),
435
- /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginTop: 1, children: rows.map((row, index) => {
617
+ /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, children: rows.map((row, index) => {
436
618
  const active = index === cursor;
437
619
  const mark = active ? "\u203A" : " ";
438
620
  if (row.kind === "new") {
@@ -457,7 +639,7 @@ function StartPanel(props) {
457
639
  }) }),
458
640
  opening ? /* @__PURE__ */ jsx5(Loading, { label: "\uB300\uD654\uB97C \uBD88\uB7EC\uC624\uB294 \uC911..." }) : null,
459
641
  error ? /* @__PURE__ */ jsx5(Notice, { error: true, children: error }) : null,
460
- /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191\u2193 \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 Esc \uBAA9\uB85D\uC73C\uB85C" }) })
642
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191\u2193 \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 Esc \uBAA9\uB85D\uC73C\uB85C" }) })
461
643
  ] });
462
644
  }
463
645
  function when(conversation) {
@@ -471,17 +653,17 @@ function when(conversation) {
471
653
  }
472
654
 
473
655
  // src/tui/use-terminal-size.ts
474
- import { useEffect as useEffect4, useState as useState5 } from "react";
475
- import { useStdout as useStdout2 } from "ink";
656
+ import { useEffect as useEffect5, useState as useState6 } from "react";
657
+ import { useStdout } from "ink";
476
658
  function useTerminalSize() {
477
- const { stdout: stdout2 } = useStdout2();
659
+ const { stdout: stdout2 } = useStdout();
478
660
  const read = () => {
479
661
  const columns = stdout2.columns || 100;
480
662
  const rows = stdout2.rows || 30;
481
663
  return { columns, rows, wide: columns >= 88 };
482
664
  };
483
- const [size, setSize] = useState5(read);
484
- useEffect4(() => {
665
+ const [size, setSize] = useState6(read);
666
+ useEffect5(() => {
485
667
  const resize = () => setSize(read());
486
668
  stdout2.on("resize", resize);
487
669
  return () => {
@@ -497,7 +679,7 @@ function AgentSidebar(props) {
497
679
  const radius = Math.max(3, Math.floor((props.height - 4) / 2));
498
680
  const start = Math.max(0, props.cursor - radius);
499
681
  const visible = props.agents.slice(start, start + radius * 2 + 1);
500
- return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", width: 30, borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
682
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", width: 30, borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
501
683
  /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Agents" }),
502
684
  visible.map((agent) => {
503
685
  const index = props.agents.indexOf(agent);
@@ -514,39 +696,46 @@ function AgentSidebar(props) {
514
696
  props.agents.length === 0 ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C Agent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) : null
515
697
  ] });
516
698
  }
517
- function messageColor(role) {
518
- if (role === "user") return "cyan";
519
- if (role === "assistant") return "green";
520
- if (role === "activity") return "yellow";
521
- if (role === "system") return "red";
522
- return void 0;
523
- }
524
- function labelOf(role, agentName) {
525
- if (role === "user") return "You";
526
- if (role === "assistant") return agentName;
527
- if (role === "activity") return "Tool";
528
- return "System";
529
- }
530
699
  function ChatPane(props) {
531
- const visibleCount = Math.max(4, Math.floor((props.height - 5) / 2));
532
- const visible = props.messages.slice(-visibleCount);
533
- return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
534
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: props.agent?.workflowName ?? "Agent\uB97C \uC120\uD0DD\uD558\uC138\uC694" }),
535
- /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", flexGrow: 1, children: [
536
- visible.length === 0 ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, 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,
537
- visible.map((message) => /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginTop: message.role === "activity" ? 0 : 1, children: [
538
- /* @__PURE__ */ jsx6(Text6, { bold: true, color: messageColor(message.role), children: labelOf(message.role, props.agent?.workflowName ?? "Agent") }),
539
- /* @__PURE__ */ jsx6(Text6, { dimColor: message.role === "activity", children: message.text || (message.role === "assistant" ? "\u2026" : "") })
540
- ] }, message.id))
700
+ const [ref, box] = useMeasured();
701
+ const width = box?.width ?? 20;
702
+ const height = box?.height ?? 1;
703
+ const lines = renderTranscript(props.messages, props.agent?.workflowName ?? "Agent", width);
704
+ const view = viewportOf(lines, height, props.scrollUp);
705
+ useEffect6(() => {
706
+ props.onViewport(lines.length, height);
707
+ }, [lines.length, height]);
708
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
709
+ /* @__PURE__ */ jsxs6(Box6, { children: [
710
+ /* @__PURE__ */ jsx6(Text6, { bold: true, wrap: "truncate-end", children: props.agent?.workflowName ?? "Agent\uB97C \uC120\uD0DD\uD558\uC138\uC694" }),
711
+ view.below > 0 ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
712
+ " \xB7 \u2193",
713
+ view.below,
714
+ "\uC904"
715
+ ] }) : null
541
716
  ] }),
542
- props.status ? /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
717
+ /* @__PURE__ */ jsxs6(Box6, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [
718
+ view.lines.length === 0 ? /* @__PURE__ */ jsx6(Text6, { 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,
719
+ view.lines.map((line) => /* @__PURE__ */ jsx6(
720
+ Text6,
721
+ {
722
+ wrap: "truncate-end",
723
+ bold: line.role === "label",
724
+ dimColor: line.role === "activity",
725
+ color: line.color,
726
+ children: line.text || " "
727
+ },
728
+ line.key
729
+ ))
730
+ ] }),
731
+ props.status ? /* @__PURE__ */ jsxs6(Text6, { color: "yellow", wrap: "truncate-end", children: [
543
732
  "\u25C6 ",
544
733
  props.status
545
734
  ] }) : null
546
735
  ] });
547
736
  }
548
737
  function Composer(props) {
549
- return /* @__PURE__ */ jsxs6(Box5, { borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
738
+ return /* @__PURE__ */ jsxs6(Box6, { borderStyle: "round", borderColor: props.focused ? "cyan" : "gray", paddingX: 1, children: [
550
739
  /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u203A " }),
551
740
  props.disabled ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\uC751\uB2F5\uC744 \uAE30\uB2E4\uB9AC\uB294 \uC911..." }) : /* @__PURE__ */ jsx6(
552
741
  ImeTextInput,
@@ -555,7 +744,6 @@ function Composer(props) {
555
744
  onChange: props.onChange,
556
745
  onSubmit: props.onSubmit,
557
746
  focus: props.focused,
558
- cursorOrigin: props.cursorOrigin,
559
747
  placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694"
560
748
  }
561
749
  )
@@ -565,20 +753,22 @@ function Dashboard(props) {
565
753
  const { exit } = useApp();
566
754
  const size = useTerminalSize();
567
755
  const bodyHeight = Math.max(12, size.rows - 5);
568
- const [focus, setFocus] = useState6("agents");
569
- const [cursor, setCursor] = useState6(0);
570
- const [selected, setSelected] = useState6(() => {
756
+ const [focus, setFocus] = useState7("agents");
757
+ const [cursor, setCursor] = useState7(0);
758
+ const [selected, setSelected] = useState7(() => {
571
759
  const first = props.session.agents[0];
572
760
  return first ? { workflowId: first.workflowId, workflowName: first.workflowName } : void 0;
573
761
  });
574
- const [input, setInput] = useState6("");
762
+ const [input, setInput] = useState7("");
575
763
  const [chat, dispatch] = useReducer(chatReducer, initialChatState);
576
- const [palette, setPalette] = useState6(false);
577
- const [history, setHistory] = useState6(false);
578
- const [start, setStart] = useState6();
579
- const [starting, setStarting] = useState6(false);
580
- const controller = useRef2(null);
581
- useEffect5(() => () => controller.current?.abort(), []);
764
+ const [palette, setPalette] = useState7(false);
765
+ const [history, setHistory] = useState7(false);
766
+ const [start, setStart] = useState7();
767
+ const [scrollUp, setScrollUp] = useState7(0);
768
+ const viewport = useRef3({ lineCount: 0, height: 0 });
769
+ const [starting, setStarting] = useState7(false);
770
+ const controller = useRef3(null);
771
+ useEffect6(() => () => controller.current?.abort(), []);
582
772
  const selectAgent = async () => {
583
773
  const agent = props.session.agents[cursor];
584
774
  if (!agent || chat.running || starting) return;
@@ -604,12 +794,14 @@ function Dashboard(props) {
604
794
  setSelected(ref);
605
795
  dispatch({ type: "reset" });
606
796
  setInput("");
797
+ setScrollUp(0);
607
798
  setStart(void 0);
608
799
  setFocus("composer");
609
800
  };
610
801
  const openHistory = (conversation, turns) => {
611
802
  setSelected({ workflowId: conversation.workflowId, workflowName: conversation.workflowName });
612
803
  dispatch({ type: "history_loaded", interactionId: conversation.interactionId, turns });
804
+ setScrollUp(0);
613
805
  const index = props.session.agents.findIndex((agent) => agent.workflowId === conversation.workflowId);
614
806
  if (index >= 0) setCursor(index);
615
807
  setHistory(false);
@@ -624,6 +816,7 @@ function Dashboard(props) {
624
816
  if (chat.running) return;
625
817
  dispatch({ type: "reset" });
626
818
  setInput("");
819
+ setScrollUp(0);
627
820
  setPalette(false);
628
821
  setFocus("composer");
629
822
  };
@@ -639,6 +832,7 @@ function Dashboard(props) {
639
832
  input: text
640
833
  });
641
834
  setInput("");
835
+ setScrollUp(0);
642
836
  dispatch({ type: "turn_started", interactionId: resolved.interactionId, input: text });
643
837
  const active = new AbortController();
644
838
  controller.current = active;
@@ -654,6 +848,12 @@ function Dashboard(props) {
654
848
  controller.current = null;
655
849
  }
656
850
  };
851
+ const scrollBy = (direction) => {
852
+ const { lineCount, height } = viewport.current;
853
+ const step = Math.max(1, Math.floor(height / 2));
854
+ const limit = maximumScroll(lineCount, height);
855
+ setScrollUp((current) => Math.min(limit, Math.max(0, current - direction * step)));
856
+ };
657
857
  useInput5(
658
858
  (keyInput, key) => {
659
859
  if (key.ctrl && keyInput === "k") setPalette(true);
@@ -664,6 +864,8 @@ function Dashboard(props) {
664
864
  if (chat.running) cancelTurn();
665
865
  setHistory(true);
666
866
  } else if (key.ctrl && keyInput === "n") newConversation();
867
+ else if (key.pageUp) scrollBy(-1);
868
+ else if (key.pageDown) scrollBy(1);
667
869
  else if (key.escape && chat.running) cancelTurn();
668
870
  else if (key.escape) setFocus("agents");
669
871
  else if (key.tab) setFocus((current) => current === "agents" ? "composer" : "agents");
@@ -744,8 +946,19 @@ function Dashboard(props) {
744
946
  setFocus("agents");
745
947
  }
746
948
  }
747
- ) : /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", flexGrow: 1, children: [
748
- /* @__PURE__ */ jsx6(ChatPane, { agent: selected, messages: chat.messages, status: chat.status, height: bodyHeight - 3 }),
949
+ ) : /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, children: [
950
+ /* @__PURE__ */ jsx6(
951
+ ChatPane,
952
+ {
953
+ agent: selected,
954
+ messages: chat.messages,
955
+ status: chat.status,
956
+ scrollUp,
957
+ onViewport: (lineCount, height) => {
958
+ viewport.current = { lineCount, height };
959
+ }
960
+ }
961
+ ),
749
962
  /* @__PURE__ */ jsx6(
750
963
  Composer,
751
964
  {
@@ -753,17 +966,16 @@ function Dashboard(props) {
753
966
  onChange: setInput,
754
967
  onSubmit: (value) => void send(value),
755
968
  focused: focus === "composer",
756
- disabled: chat.running || !selected,
757
- cursorOrigin: { x: size.wide ? 34 : 4, y: bodyHeight - 1 }
969
+ disabled: chat.running || !selected
758
970
  }
759
971
  )
760
972
  ] });
761
- body = size.wide ? /* @__PURE__ */ jsxs6(Box5, { height: bodyHeight, children: [
973
+ body = size.wide ? /* @__PURE__ */ jsxs6(Box6, { height: bodyHeight, children: [
762
974
  sidebar,
763
975
  conversation
764
- ] }) : focus === "agents" ? /* @__PURE__ */ jsx6(Box5, { height: bodyHeight, children: sidebar }) : /* @__PURE__ */ jsx6(Box5, { height: bodyHeight, children: conversation });
976
+ ] }) : focus === "agents" ? /* @__PURE__ */ jsx6(Box6, { height: bodyHeight, children: sidebar }) : /* @__PURE__ */ jsx6(Box6, { height: bodyHeight, children: conversation });
765
977
  }
766
- return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", children: [
978
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
767
979
  /* @__PURE__ */ jsx6(
768
980
  Header,
769
981
  {
@@ -773,18 +985,18 @@ function Dashboard(props) {
773
985
  }
774
986
  ),
775
987
  body,
776
- /* @__PURE__ */ jsx6(Footer, { text: "Tab \uD328\uB110 \xB7 Ctrl+K \uBA85\uB839 \xB7 Ctrl+H \uAE30\uB85D \xB7 Ctrl+P \uD504\uB85C\uD544 \xB7 Esc \uCDE8\uC18C \xB7 Ctrl+Q \uC885\uB8CC" })
988
+ /* @__PURE__ */ jsx6(Footer, { text: "Tab \uD328\uB110 \xB7 PgUp/PgDn \uC2A4\uD06C\uB864 \xB7 Ctrl+K \uBA85\uB839 \xB7 Ctrl+H \uAE30\uB85D \xB7 Ctrl+P \uD504\uB85C\uD544 \xB7 Esc \uCDE8\uC18C \xB7 Ctrl+Q \uC885\uB8CC" })
777
989
  ] });
778
990
  }
779
991
 
780
992
  // src/tui/login-screen.tsx
781
- import { useState as useState7 } from "react";
782
- import { Box as Box6, Text as Text7, useInput as useInput6 } from "ink";
993
+ import { useState as useState8 } from "react";
994
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
783
995
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
784
996
  function LoginScreen(props) {
785
- const [email, setEmail] = useState7("");
786
- const [password, setPassword] = useState7("");
787
- const [focus, setFocus] = useState7("email");
997
+ const [email, setEmail] = useState8("");
998
+ const [password, setPassword] = useState8("");
999
+ const [focus, setFocus] = useState8("email");
788
1000
  useInput6(
789
1001
  (input, key) => {
790
1002
  if (key.tab) setFocus((current) => current === "email" ? "password" : "email");
@@ -804,16 +1016,16 @@ function LoginScreen(props) {
804
1016
  props.onSubmit(email.trim(), secret);
805
1017
  }
806
1018
  };
807
- return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
1019
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
808
1020
  /* @__PURE__ */ jsx7(Header, { profile: props.profile, connected: false }),
809
- /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
1021
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
810
1022
  /* @__PURE__ */ jsx7(Text7, { bold: true, children: "\uB85C\uADF8\uC778" }),
811
1023
  /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
812
1024
  props.serverUrl,
813
1025
  " ",
814
1026
  /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "(Ctrl+E \uBC14\uAFB8\uAE30)" })
815
1027
  ] }),
816
- /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", marginTop: 1, children: [
1028
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
817
1029
  /* @__PURE__ */ jsx7(
818
1030
  FormField,
819
1031
  {
@@ -822,7 +1034,6 @@ function LoginScreen(props) {
822
1034
  onChange: setEmail,
823
1035
  onSubmit: () => setFocus("password"),
824
1036
  focus: !props.busy && focus === "email",
825
- cursorOrigin: { x: 16, y: 6 },
826
1037
  placeholder: "me@corp.com"
827
1038
  }
828
1039
  ),
@@ -834,7 +1045,6 @@ function LoginScreen(props) {
834
1045
  onChange: setPassword,
835
1046
  onSubmit: submit,
836
1047
  focus: !props.busy && focus === "password",
837
- cursorOrigin: { x: 16, y: 7 },
838
1048
  secret: true
839
1049
  }
840
1050
  )
@@ -847,16 +1057,16 @@ function LoginScreen(props) {
847
1057
  }
848
1058
 
849
1059
  // src/tui/profile-screen.tsx
850
- import { useEffect as useEffect6, useState as useState8 } from "react";
851
- import { Box as Box7, Text as Text8, useInput as useInput7 } from "ink";
1060
+ import { useEffect as useEffect7, useState as useState9 } from "react";
1061
+ import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
852
1062
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
853
1063
  function ProfileScreen(props) {
854
- const [cursor, setCursor] = useState8(Math.max(0, props.profiles.findIndex((profile) => profile.current)));
855
- const [creating, setCreating] = useState8(false);
856
- const [focus, setFocus] = useState8("name");
857
- const [name, setName] = useState8("");
858
- const [serverUrl, setServerUrl] = useState8("");
859
- useEffect6(() => setCursor((current) => Math.min(current, Math.max(0, props.profiles.length - 1))), [props.profiles]);
1064
+ const [cursor, setCursor] = useState9(Math.max(0, props.profiles.findIndex((profile) => profile.current)));
1065
+ const [creating, setCreating] = useState9(false);
1066
+ const [focus, setFocus] = useState9("name");
1067
+ const [name, setName] = useState9("");
1068
+ const [serverUrl, setServerUrl] = useState9("");
1069
+ useEffect7(() => setCursor((current) => Math.min(current, Math.max(0, props.profiles.length - 1))), [props.profiles]);
860
1070
  useInput7(
861
1071
  (input, key) => {
862
1072
  if (key.escape) {
@@ -887,11 +1097,11 @@ function ProfileScreen(props) {
887
1097
  }
888
1098
  if (name.trim() && serverUrl.trim()) props.onCreate(name.trim(), serverUrl.trim());
889
1099
  };
890
- return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
1100
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
891
1101
  /* @__PURE__ */ jsx8(Header, {}),
892
- /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
1102
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
893
1103
  /* @__PURE__ */ jsx8(Text8, { bold: true, children: creating ? "\uC0C8 \uD504\uB85C\uD544" : "\uD504\uB85C\uD544 \uC804\uD658" }),
894
- creating ? /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginTop: 1, children: [
1104
+ creating ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
895
1105
  /* @__PURE__ */ jsx8(
896
1106
  FormField,
897
1107
  {
@@ -900,7 +1110,6 @@ function ProfileScreen(props) {
900
1110
  onChange: setName,
901
1111
  onSubmit: () => setFocus("url"),
902
1112
  focus: !props.busy && focus === "name",
903
- cursorOrigin: { x: 16, y: 5 },
904
1113
  placeholder: "corp"
905
1114
  }
906
1115
  ),
@@ -912,11 +1121,10 @@ function ProfileScreen(props) {
912
1121
  onChange: setServerUrl,
913
1122
  onSubmit: create,
914
1123
  focus: !props.busy && focus === "url",
915
- cursorOrigin: { x: 16, y: 6 },
916
1124
  placeholder: "https://xgen.example.com"
917
1125
  }
918
1126
  )
919
- ] }) : /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginTop: 1, children: [
1127
+ ] }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
920
1128
  props.profiles.map((profile, index) => /* @__PURE__ */ jsxs8(Text8, { color: index === cursor ? "cyan" : void 0, children: [
921
1129
  index === cursor ? "\u203A" : " ",
922
1130
  " ",
@@ -942,11 +1150,11 @@ function ProfileScreen(props) {
942
1150
  }
943
1151
 
944
1152
  // src/tui/server-screen.tsx
945
- import { useState as useState9 } from "react";
946
- import { Box as Box8, Text as Text9, useInput as useInput8 } from "ink";
1153
+ import { useState as useState10 } from "react";
1154
+ import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
947
1155
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
948
1156
  function ServerScreen(props) {
949
- const [serverUrl, setServerUrl] = useState9(props.initialValue ?? "");
1157
+ const [serverUrl, setServerUrl] = useState10(props.initialValue ?? "");
950
1158
  const editing = props.initialValue !== void 0;
951
1159
  useInput8(
952
1160
  (_input, key) => {
@@ -954,12 +1162,12 @@ function ServerScreen(props) {
954
1162
  },
955
1163
  { isActive: !props.busy && !!props.onCancel }
956
1164
  );
957
- return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", children: [
1165
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
958
1166
  /* @__PURE__ */ jsx9(Header, { profile: props.profile, connected: false }),
959
- /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
1167
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "blue", padding: 1, children: [
960
1168
  /* @__PURE__ */ jsx9(Text9, { bold: true, children: editing ? "\uC11C\uBC84 \uC8FC\uC18C \uBC14\uAFB8\uAE30" : "\uCC98\uC74C \uC624\uC168\uAD70\uC694" }),
961
1169
  /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\uC5F0\uACB0\uD560 XGEN Gateway \uC8FC\uC18C\uB97C \uC785\uB825\uD558\uC138\uC694." }),
962
- /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx9(
1170
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(
963
1171
  FormField,
964
1172
  {
965
1173
  label: "Server URL",
@@ -967,7 +1175,6 @@ function ServerScreen(props) {
967
1175
  onChange: setServerUrl,
968
1176
  onSubmit: (value) => value.trim() && props.onSubmit(value.trim()),
969
1177
  focus: !props.busy,
970
- cursorOrigin: { x: 16, y: 6 },
971
1178
  placeholder: "xgen.example.com"
972
1179
  }
973
1180
  ) }),
@@ -988,13 +1195,13 @@ function ServerScreen(props) {
988
1195
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
989
1196
  function App({ engine }) {
990
1197
  const { exit } = useApp2();
991
- const [route, setRoute] = useState10("boot");
992
- const [session, setSession] = useState10();
993
- const [profiles, setProfiles] = useState10([]);
994
- const [loginTarget, setLoginTarget] = useState10();
995
- const [editingServer, setEditingServer] = useState10();
996
- const [busy, setBusy] = useState10(false);
997
- const [error, setError] = useState10();
1198
+ const [route, setRoute] = useState11("boot");
1199
+ const [session, setSession] = useState11();
1200
+ const [profiles, setProfiles] = useState11([]);
1201
+ const [loginTarget, setLoginTarget] = useState11();
1202
+ const [editingServer, setEditingServer] = useState11();
1203
+ const [busy, setBusy] = useState11(false);
1204
+ const [error, setError] = useState11();
998
1205
  useInput9((input, key) => {
999
1206
  if (key.ctrl && input === "q") exit();
1000
1207
  });
@@ -1045,7 +1252,7 @@ function App({ engine }) {
1045
1252
  },
1046
1253
  [engine]
1047
1254
  );
1048
- useEffect7(() => {
1255
+ useEffect8(() => {
1049
1256
  void bootstrap();
1050
1257
  }, [bootstrap]);
1051
1258
  const configure = async (serverUrl) => {
@@ -1120,7 +1327,7 @@ function App({ engine }) {
1120
1327
  }
1121
1328
  };
1122
1329
  if (route === "boot") {
1123
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", children: [
1330
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1124
1331
  /* @__PURE__ */ jsx10(Header, {}),
1125
1332
  /* @__PURE__ */ jsx10(Loading, { label: "Dex\uB97C \uC900\uBE44\uD558\uB294 \uC911..." }),
1126
1333
  /* @__PURE__ */ jsx10(Footer, { text: "Ctrl+Q \uC885\uB8CC" })
@@ -1185,7 +1392,7 @@ function App({ engine }) {
1185
1392
  }
1186
1393
  );
1187
1394
  }
1188
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", children: [
1395
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1189
1396
  /* @__PURE__ */ jsx10(Header, {}),
1190
1397
  /* @__PURE__ */ jsx10(Notice, { error: true, children: error ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4." }),
1191
1398
  /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\uC11C\uBC84\uC640 \uD0A4\uCCB4\uC778 \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694." }),
@@ -1205,6 +1412,20 @@ var ESC = "\x1B";
1205
1412
  var ENTER_ALT_SCREEN = `${ESC}[?1049h`;
1206
1413
  var LEAVE_ALT_SCREEN = `${ESC}[?1049l`;
1207
1414
  var SHOW_CURSOR = `${ESC}[?25h`;
1415
+ var DISABLE_REPORTS = [
1416
+ `${ESC}[?1004l`,
1417
+ // 포커스 들어옴/나감
1418
+ `${ESC}[?1000l`,
1419
+ // 마우스 클릭
1420
+ `${ESC}[?1002l`,
1421
+ // 마우스 드래그
1422
+ `${ESC}[?1003l`,
1423
+ // 마우스 이동 전부
1424
+ `${ESC}[?1006l`
1425
+ // SGR 확장 좌표
1426
+ ].join("");
1427
+ var ENABLE_BRACKETED_PASTE = `${ESC}[?2004h`;
1428
+ var DISABLE_BRACKETED_PASTE = `${ESC}[?2004l`;
1208
1429
  function createScreenGuard(stream) {
1209
1430
  const alt = Boolean(stream.isTTY);
1210
1431
  let entered = false;
@@ -1213,12 +1434,12 @@ function createScreenGuard(stream) {
1213
1434
  enter() {
1214
1435
  if (entered || !alt) return;
1215
1436
  entered = true;
1216
- stream.write(ENTER_ALT_SCREEN);
1437
+ stream.write(ENTER_ALT_SCREEN + DISABLE_REPORTS + ENABLE_BRACKETED_PASTE);
1217
1438
  },
1218
1439
  restore() {
1219
1440
  if (restored) return;
1220
1441
  restored = true;
1221
- if (entered) stream.write(LEAVE_ALT_SCREEN);
1442
+ if (entered) stream.write(DISABLE_BRACKETED_PASTE + LEAVE_ALT_SCREEN);
1222
1443
  stream.write(SHOW_CURSOR);
1223
1444
  }
1224
1445
  };
@@ -1253,4 +1474,4 @@ async function runTui(engine) {
1253
1474
  export {
1254
1475
  runTui
1255
1476
  };
1256
- //# sourceMappingURL=tui-MLHYSLBS.js.map
1477
+ //# sourceMappingURL=tui-KJAW7DSU.js.map