@three333/termbuddy 0.1.4 → 0.1.5

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/cli.js CHANGED
@@ -827,6 +827,7 @@ function useTcpSync(options) {
827
827
  const [listenPort, setListenPort] = useState5(void 0);
828
828
  const [peers, setPeers] = useState5([]);
829
829
  const onRemoteProjectileRef = useRef3(void 0);
830
+ const onRemoteBubbleRef = useRef3(void 0);
830
831
  const peerConnectionsRef = useRef3(/* @__PURE__ */ new Map());
831
832
  const clientSocketRef = useRef3(null);
832
833
  const clientHeartbeatRef = useRef3(null);
@@ -918,6 +919,10 @@ function useTcpSync(options) {
918
919
  onRemoteProjectileRef.current?.(packet.kind, packet.direction, conn.name);
919
920
  broadcastPacket({ ...packet, senderName: conn.name }, peerId);
920
921
  }
922
+ if (packet.type === "bubble") {
923
+ onRemoteBubbleRef.current?.(packet.text, packet.durationMs, conn.name);
924
+ broadcastPacket({ ...packet, senderName: conn.name }, peerId);
925
+ }
921
926
  } catch {
922
927
  }
923
928
  }
@@ -992,6 +997,9 @@ function useTcpSync(options) {
992
997
  if (packet.type === "projectile") {
993
998
  onRemoteProjectileRef.current?.(packet.kind, packet.direction, packet.senderName);
994
999
  }
1000
+ if (packet.type === "bubble") {
1001
+ onRemoteBubbleRef.current?.(packet.text, packet.durationMs, packet.senderName);
1002
+ }
995
1003
  if (packet.type === "peer_joined") {
996
1004
  setPeers((prev) => {
997
1005
  const exists = prev.some((p) => p.name === packet.peerName);
@@ -1092,6 +1100,19 @@ function useTcpSync(options) {
1092
1100
  const setOnRemoteProjectile = useCallback3((callback) => {
1093
1101
  onRemoteProjectileRef.current = callback;
1094
1102
  }, []);
1103
+ const sendBubble = useCallback3((text, durationMs) => {
1104
+ if (options.role === "host") {
1105
+ broadcastPacket({ type: "bubble", text, durationMs, senderName: options.localName, sentAt: Date.now() });
1106
+ } else {
1107
+ const socket = clientSocketRef.current;
1108
+ if (socket && !socket.destroyed) {
1109
+ writePacket(socket, { type: "bubble", text, durationMs, senderName: options.localName, sentAt: Date.now() });
1110
+ }
1111
+ }
1112
+ }, [options, broadcastPacket]);
1113
+ const setOnRemoteBubble = useCallback3((callback) => {
1114
+ onRemoteBubbleRef.current = callback;
1115
+ }, []);
1095
1116
  const firstPeer = peers[0];
1096
1117
  const peerName = firstPeer?.name;
1097
1118
  const remoteState = firstPeer?.state;
@@ -1103,7 +1124,9 @@ function useTcpSync(options) {
1103
1124
  remoteState,
1104
1125
  sendStatus,
1105
1126
  sendProjectile,
1106
- setOnRemoteProjectile
1127
+ setOnRemoteProjectile,
1128
+ sendBubble,
1129
+ setOnRemoteBubble
1107
1130
  };
1108
1131
  }
1109
1132
  var init_useTcpSync = __esm({
@@ -2005,32 +2028,50 @@ function Session(props) {
2005
2028
  },
2006
2029
  [pumpShotQueue, tcp, addInfoRecord]
2007
2030
  );
2008
- const showBubble = useCallback4(
2009
- (args) => {
2010
- const text = args.text.trim();
2011
- if (!text) return;
2012
- const durationMs = Math.max(300, Math.min(15e3, Math.floor(args.durationMs)));
2013
- const targetIndex = Math.max(0, Math.min(3, args.target - 1));
2031
+ const showBubbleAtIndex = useCallback4(
2032
+ (index, text, durationMs) => {
2014
2033
  setPeerBubbles((prev) => {
2015
2034
  const next = [...prev];
2016
- next[targetIndex] = text;
2035
+ next[index] = text;
2017
2036
  return next;
2018
2037
  });
2019
- const prevTimer = bubbleTimersRef.current[targetIndex];
2038
+ const prevTimer = bubbleTimersRef.current[index];
2020
2039
  if (prevTimer) clearTimeout(prevTimer);
2021
2040
  const handle = setTimeout(() => {
2022
2041
  setPeerBubbles((prev) => {
2023
2042
  const next = [...prev];
2024
- next[targetIndex] = null;
2043
+ next[index] = null;
2025
2044
  return next;
2026
2045
  });
2027
- bubbleTimersRef.current[targetIndex] = null;
2046
+ bubbleTimersRef.current[index] = null;
2028
2047
  }, durationMs);
2029
- bubbleTimersRef.current[targetIndex] = handle;
2030
- addInfoRecord("bubble", `No.${args.target}: "${text}"`);
2048
+ bubbleTimersRef.current[index] = handle;
2049
+ },
2050
+ []
2051
+ );
2052
+ const showBubble = useCallback4(
2053
+ (args) => {
2054
+ const text = args.text.trim();
2055
+ if (!text) return;
2056
+ const durationMs = Math.max(300, Math.min(15e3, Math.floor(args.durationMs)));
2057
+ showBubbleAtIndex(0, text, durationMs);
2058
+ if (tcp.status === "connected") {
2059
+ tcp.sendBubble(text, durationMs);
2060
+ }
2061
+ addInfoRecord("bubble", `To No.${args.target}: "${text}"`);
2031
2062
  },
2032
- [addInfoRecord]
2063
+ [showBubbleAtIndex, tcp, addInfoRecord]
2033
2064
  );
2065
+ useEffect8(() => {
2066
+ const handleRemoteBubble = (text, durationMs, senderName) => {
2067
+ const peerIndex = peers.findIndex((p) => p.name === senderName);
2068
+ if (peerIndex !== -1) {
2069
+ showBubbleAtIndex(peerIndex + 1, text, durationMs);
2070
+ addInfoRecord("bubble", `From ${senderName}: "${text}"`);
2071
+ }
2072
+ };
2073
+ tcp.setOnRemoteBubble(handleRemoteBubble);
2074
+ }, [tcp, peers, showBubbleAtIndex, addInfoRecord]);
2034
2075
  useInput7(
2035
2076
  (input, key) => {
2036
2077
  if (input === "q") finishAndLeave();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/page/MainMenu.tsx","../src/page/NicknamePrompt.tsx","../src/hooks/globalKeyboard.ts","../src/hooks/useActivityMonitor.ts","../src/components/tool/createCountdownTool.ts","../src/components/tool/createBubbleTool.ts","../src/components/tool/createInteractionTool.ts","../src/components/tool/createSessionInfoTool.ts","../src/components/tool/index.ts","../src/hooks/useAiAgent.ts","../src/constants.ts","../src/net/broadcast.ts","../src/net/index.ts","../src/hooks/useBroadcaster.ts","../src/hooks/useScanner.ts","../src/hooks/useTcpSync.ts","../src/hooks/index.ts","../src/page/RoomScanner.tsx","../src/page/LeavePage.tsx","../src/storage/apiKey.ts","../src/components/AiConsole.tsx","../src/components/sprite/BubbleSprite.tsx","../src/components/sprite/BuddyAvatar.tsx","../src/components/sprite/CountdownClockSprite.tsx","../src/components/sprite/ProjectileThrowSprite.tsx","../src/components/StatusHeader.tsx","../src/components/InfoPanel.tsx","../src/components/index.ts","../src/page/Session.tsx","../src/page/index.ts","../src/app/App.tsx","../src/app/index.ts","../src/cli.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\n\nexport function MainMenu(props: {\n onHost: () => void;\n onJoin: () => void;\n onExit: () => void;\n}) {\n useInput((input, key) => {\n if (key.escape || input === \"q\") props.onExit();\n if (input === \"1\") props.onHost();\n if (input === \"2\") props.onJoin();\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text>\n {String.raw`\n████████╗███████╗██████╗ ███╗ ███╗██████╗ ██╗ ██╗██████╗ ██████╗ ██╗ ██╗\n╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔══██╗██║ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝\n ██║ █████╗ ██████╔╝██╔████╔██║██████╔╝██║ ██║██║ ██║██║ ██║ ╚████╔╝ \n ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══██╗██║ ██║██║ ██║██║ ██║ ╚██╔╝ \n ██║ ███████╗██║ ██║██║ ╚═╝ ██║██████╔╝╚██████╔╝██████╔╝██████╔╝ ██║ \n ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ \n`}\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n <Text>Terminal Body Doubling — 极简 / 极客 / 私密</Text>\n <Text> </Text>\n <Text>\n <Text color=\"cyan\">[1]</Text> 建房 (Host)\n </Text>\n <Text>\n <Text color=\"cyan\">[2]</Text> 加入 (Join)\n </Text>\n <Text>\n <Text color=\"cyan\">[q]</Text> 退出\n </Text>\n </Box>\n </Box>\n );\n}\n","import React, { useMemo, useState } from \"react\";\nimport os from \"node:os\";\nimport { Box, Text, useInput } from \"ink\";\n\nfunction defaultNick(): string {\n try {\n return os.userInfo().username || os.hostname();\n } catch {\n return os.hostname();\n }\n}\n\nexport function NicknamePrompt(props: {\n onSubmit: (nickname: string) => void;\n onExit: () => void;\n}) {\n const initial = useMemo(() => defaultNick(), []);\n const [nickname, setNickname] = useState(initial);\n const [touched, setTouched] = useState(false);\n\n useInput((input, key) => {\n if (key.escape) props.onExit();\n\n if (key.return) {\n const name = nickname.trim();\n if (!name) return;\n props.onSubmit(name);\n return;\n }\n\n if (key.backspace || key.delete) {\n setTouched(true);\n setNickname((v) => v.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (!input) return;\n if (input === \"\\t\") return;\n\n setTouched(true);\n setNickname((v) => v + input);\n });\n\n const hint = touched ? \"\" : \" (回车确认,可直接用默认值)\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"gray\">欢迎来到 TermBuddy</Text>\n <Box marginTop={1}>\n <Text>\n 请输入你的昵称:<Text color=\"cyan\">{nickname || \"\"}</Text>\n <Text color=\"gray\">{hint}</Text>\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text color=\"gray\">按 Esc 退出。</Text>\n </Box>\n </Box>\n );\n}\n\n","import { spawn } from \"node:child_process\";\n\nexport type GlobalKeyboardBackend = \"uiohook\" | \"xinput\";\n\ntype Listener = () => void;\n\nlet backend: GlobalKeyboardBackend | null = null;\nlet started = false;\nlet starting: Promise<GlobalKeyboardBackend | null> | null = null;\nlet stopBackend: (() => void) | null = null;\n\nconst listeners = new Set<Listener>();\n\nfunction emitKeydown() {\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // ignore\n }\n }\n}\n\nasync function tryStartUiohook(): Promise<GlobalKeyboardBackend | null> {\n try {\n const mod = (await import(\"uiohook-napi\")) as unknown as {\n uIOhook?: {\n on: (event: \"keydown\", listener: () => void) => unknown;\n removeListener?: (event: \"keydown\", listener: () => void) => unknown;\n start: () => void;\n stop: () => void;\n };\n default?: { uIOhook?: unknown };\n };\n\n const uIOhook =\n mod.uIOhook ??\n ((mod.default as { uIOhook?: unknown } | undefined)?.uIOhook as\n | {\n on: (event: \"keydown\", listener: () => void) => unknown;\n removeListener?: (event: \"keydown\", listener: () => void) => unknown;\n start: () => void;\n stop: () => void;\n }\n | undefined);\n if (!uIOhook) return null;\n\n const onKeydown = () => emitKeydown();\n uIOhook.on(\"keydown\", onKeydown);\n uIOhook.start();\n\n stopBackend = () => {\n uIOhook.removeListener?.(\"keydown\", onKeydown);\n uIOhook.stop();\n };\n\n return \"uiohook\";\n } catch {\n return null;\n }\n}\n\nfunction tryStartXinput(): Promise<GlobalKeyboardBackend | null> {\n if (process.platform !== \"linux\") return Promise.resolve(null);\n if (!process.env.DISPLAY) return Promise.resolve(null);\n\n return new Promise((resolve) => {\n let resolved = false;\n const child = spawn(\"xinput\", [\"test-xi2\", \"--root\"], {\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n const resolveOnce = (value: GlobalKeyboardBackend | null) => {\n if (resolved) return;\n resolved = true;\n resolve(value);\n };\n\n child.once(\"error\", () => {\n resolveOnce(null);\n });\n\n // If we got here, treat it as started; parsing may continue.\n resolveOnce(\"xinput\");\n\n let buf = \"\";\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx);\n buf = buf.slice(idx + 1);\n if (/KeyPress/.test(line)) emitKeydown();\n }\n });\n\n stopBackend = () => {\n child.stdout?.removeAllListeners();\n child.removeAllListeners();\n child.kill();\n };\n });\n}\n\nexport async function ensureGlobalKeyboard(): Promise<GlobalKeyboardBackend | null> {\n if (started) return backend;\n if (starting) return starting;\n\n starting = (async () => {\n const uiohook = await tryStartUiohook();\n if (uiohook) return uiohook;\n return await tryStartXinput();\n })();\n\n backend = await starting;\n started = backend !== null;\n if (!started) stopBackend = null;\n starting = null;\n\n return backend;\n}\n\nfunction stopIfIdle() {\n if (listeners.size > 0) return;\n if (!started) return;\n started = false;\n backend = null;\n const stop = stopBackend;\n stopBackend = null;\n try {\n stop?.();\n } catch {\n // ignore\n }\n}\n\nexport function subscribeGlobalKeydown(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n stopIfIdle();\n };\n}\n\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useInput } from \"ink\";\nimport type { ActivityState } from \"../protocol.js\";\nimport {\n ensureGlobalKeyboard,\n subscribeGlobalKeydown,\n} from \"./globalKeyboard.js\";\n\nexport function useActivityMonitor(options?: {\n idleAfterMs?: number;\n source?: \"ink\" | \"keyboard\";\n}): {\n state: ActivityState;\n} {\n const idleAfterMs = options?.idleAfterMs ?? 1500;\n const [state, setState] = useState<ActivityState>(\"IDLE\");\n\n const lastActivityRef = useRef<number>(Date.now());\n\n const markActive = useCallback(() => {\n lastActivityRef.current = Date.now();\n setState(\"TYPING\");\n }, []);\n\n useInput(() => {\n markActive();\n });\n\n // Optional global keyboard activity.\n useEffect(() => {\n const rawSource =\n options?.source ?? process.env.TERMBUDDY_ACTIVITY_SOURCE ?? \"ink\";\n\n // Back-compat: previous env value.\n const source = rawSource === \"xinput\" ? \"keyboard\" : rawSource;\n if (source !== \"keyboard\") return;\n\n let cancelled = false;\n let unsub: (() => void) | null = null;\n void (async () => {\n const ok = await ensureGlobalKeyboard();\n if (cancelled) return;\n if (!ok) return;\n unsub = subscribeGlobalKeydown(markActive);\n })();\n\n return () => {\n cancelled = true;\n unsub?.();\n };\n }, [markActive, options?.source]);\n\n useEffect(() => {\n const id = setInterval(() => {\n const delta = Date.now() - lastActivityRef.current;\n if (delta >= idleAfterMs) setState(\"IDLE\");\n }, 200);\n return () => clearInterval(id);\n }, [idleAfterMs]);\n\n return { state };\n}\n","import { tool } from \"langchain\";\n\nexport function createCountdownTool(options: {\n onStartCountdown?: (minutes: number) => void;\n}) {\n return tool(\n async (input: { minutes: number }) => {\n const minutes = Number(input.minutes);\n if (!Number.isFinite(minutes) || minutes <= 0) return \"倒计时分钟数无效。\";\n options.onStartCountdown?.(minutes);\n return `已开始倒计时 ${minutes} 分钟。`;\n },\n {\n name: \"start_countdown\",\n description: \"开始一个专注倒计时(分钟)。\",\n schema: {\n type: \"object\",\n properties: {\n minutes: {\n type: \"integer\",\n minimum: 1,\n maximum: 180,\n description: \"倒计时分钟数\",\n },\n },\n required: [\"minutes\"],\n additionalProperties: false,\n },\n }\n );\n}\n\n","import { tool } from \"langchain\";\n\n// Target can be 1-4 (No.1 to No.4)\nexport type BubbleTarget = number;\n\nexport function createBubbleTool(options: {\n onShowBubble?: (args: {\n text: string;\n target: BubbleTarget;\n durationMs: number;\n }) => void;\n}) {\n return tool(\n async (input: { text?: string; target?: number | string; durationMs?: number }) => {\n const text = String(input.text ?? \"\").trim();\n if (!text) return \"气泡内容为空。\";\n\n // Parse target: can be number 1-4, or \"local\"/\"buddy\" for backward compatibility\n let target: number = 1; // default to No.1 (local)\n if (typeof input.target === \"number\") {\n target = Math.max(1, Math.min(4, Math.floor(input.target)));\n } else if (typeof input.target === \"string\") {\n const t = input.target.toLowerCase();\n if (t === \"local\" || t === \"1\") {\n target = 1;\n } else if (t === \"buddy\" || t === \"2\") {\n target = 2;\n } else if (t === \"3\") {\n target = 3;\n } else if (t === \"4\") {\n target = 4;\n } else {\n // Try to parse as number\n const parsed = parseInt(t, 10);\n if (!isNaN(parsed) && parsed >= 1 && parsed <= 4) {\n target = parsed;\n }\n }\n }\n\n const durationRaw = Number(input.durationMs ?? 2500);\n const durationMs =\n Number.isFinite(durationRaw) && durationRaw > 0\n ? Math.min(15_000, Math.max(300, Math.floor(durationRaw)))\n : 2500;\n\n options.onShowBubble?.({ text, target, durationMs });\n return `已显示气泡给 No.${target}:${text}`;\n },\n {\n name: \"show_bubble\",\n description: \"在指定用户的小猫头像上显示一个气泡(短消息提示)。\",\n schema: {\n type: \"object\",\n properties: {\n text: {\n type: \"string\",\n minLength: 1,\n maxLength: 120,\n description: \"气泡里的文字\",\n },\n target: {\n type: \"integer\",\n minimum: 1,\n maximum: 4,\n description: \"显示在哪个用户的小猫上(1=No.1/本地用户,2=No.2,3=No.3,4=No.4)\",\n },\n durationMs: {\n type: \"integer\",\n minimum: 300,\n maximum: 15000,\n description: \"显示时长(毫秒,可选)\",\n },\n },\n required: [\"text\"],\n additionalProperties: false,\n },\n }\n );\n}\n\n","import { tool } from \"langchain\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../sprite/ProjectileThrowSprite.js\";\n\nconst KIND_ALIASES: Array<{ kind: ProjectileKind; keys: string[] }> = [\n { kind: \"ROSE\", keys: [\"rose\", \"花\", \"玫瑰\", \"🌹\", \"love\"] },\n { kind: \"POOP\", keys: [\"poop\", \"屎\", \"💩\", \"大便\"] },\n { kind: \"HAMMER\", keys: [\"hammer\", \"锤\", \"🔨\", \"敲\", \"打\"] },\n];\n\nfunction normalizeKind(raw: unknown): ProjectileKind | null {\n if (typeof raw !== \"string\") return null;\n const upper = raw.toUpperCase().trim();\n if (upper === \"ROSE\" || upper === \"POOP\" || upper === \"HAMMER\") return upper;\n\n const lower = raw.toLowerCase();\n for (const item of KIND_ALIASES) {\n if (item.keys.some((k) => lower.includes(k))) return item.kind;\n }\n return null;\n}\n\nfunction normalizeDirection(raw: unknown): ProjectileDirection | null {\n if (typeof raw !== \"string\") return null;\n const upper = raw.toUpperCase().trim();\n if (upper === \"LEFT_TO_RIGHT\" || upper === \"RIGHT_TO_LEFT\") return upper;\n return null;\n}\n\nexport function createInteractionTool(options: {\n onThrow?: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n}) {\n return tool(\n async (input: { kind?: string; direction?: string; message?: string }) => {\n const kind = normalizeKind(input.kind ?? \"\") ?? \"ROSE\";\n const direction = normalizeDirection(input.direction) ?? \"LEFT_TO_RIGHT\";\n options.onThrow?.(kind, direction);\n const msg = (input.message ?? \"\").trim();\n return msg ? `已投掷 ${kind}:${msg}` : `已投掷 ${kind}。`;\n },\n {\n name: \"throw_projectile\",\n description: \"和同桌互动:投掷一个小物品(🌹/💩/🔨)。\",\n schema: {\n type: \"object\",\n properties: {\n kind: {\n type: \"string\",\n description:\n \"投掷物类型(ROSE/POOP/HAMMER,或任意描述如“玫瑰/锤子/💩”)\",\n },\n direction: {\n type: \"string\",\n enum: [\"LEFT_TO_RIGHT\", \"RIGHT_TO_LEFT\"],\n description: \"飞行方向\",\n },\n message: { type: \"string\", description: \"附带一句话(可选)\" },\n },\n required: [],\n additionalProperties: false,\n },\n }\n );\n}\n","import { tool } from \"langchain\";\n\nexport function createSessionInfoTool(options: {\n localName: string;\n peerName: string;\n}) {\n return tool(\n async () => {\n return JSON.stringify(\n {\n localName: options.localName,\n peerName: options.peerName,\n },\n null,\n 2\n );\n },\n {\n name: \"session_info\",\n description: \"获取当前会话上下文(本地昵称、同桌昵称)。\",\n schema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n }\n );\n}\n\n","export { createCountdownTool } from \"./createCountdownTool.js\";\nexport { createBubbleTool } from \"./createBubbleTool.js\";\nexport { createInteractionTool } from \"./createInteractionTool.js\";\nexport { createSessionInfoTool } from \"./createSessionInfoTool.js\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createAgent } from \"langchain\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport {\n createBubbleTool,\n createCountdownTool,\n createInteractionTool,\n createSessionInfoTool,\n} from \"../components/tool/index.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../components/sprite/ProjectileThrowSprite.js\";\ntype LineKind = \"user\" | \"ai\" | \"system\";\nexport type AiLine = { kind: LineKind; text: string; at: number };\n\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!content) return \"\";\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === \"string\") return part;\n if (typeof part === \"object\" && part && \"text\" in part)\n return String((part as any).text ?? \"\");\n return \"\";\n })\n .join(\"\");\n }\n if (typeof content === \"object\" && \"text\" in (content as any))\n return String((content as any).text ?? \"\");\n return String(content);\n}\n\nfunction lastAiText(messages: unknown[]): string | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m: any = messages[i];\n const type =\n typeof m?.getType === \"function\"\n ? m.getType()\n : typeof m?._getType === \"function\"\n ? m._getType()\n : m?.type;\n if (type === \"ai\") {\n const t = contentToText(m?.content);\n return t || \"\";\n }\n }\n return null;\n}\n\nfunction createSystemPrompt(context: { localName: string; peerName: string; peers?: Array<{ id: string; name: string }> }) {\n const peerList = context.peers ?? [];\n const userList = [\n `No.1: ${context.localName} (我/本地用户)`,\n peerList[0] ? `No.2: ${peerList[0].name}` : \"No.2: (空位)\",\n peerList[1] ? `No.3: ${peerList[1].name}` : \"No.3: (空位)\",\n peerList[2] ? `No.4: ${peerList[2].name}` : \"No.4: (空位)\",\n ].join(\"、\");\n\n return [\n `你是 TermBuddy 里的「壳中幽灵 (Ghost in the Shell)」。`,\n `默认隐形;被 / 唤醒时出现。风格:极简、干练、少废话。`,\n `你可以使用工具来操控应用功能(例如倒计时)。`,\n `如果用户提到「倒计时/专注/计时/countdown」,优先调用 start_countdown。`,\n `如果用户提到「气泡/泡泡/提示/说一句/和某人说」,优先调用 show_bubble,并根据目标用户设置 target 参数(1-4)。`,\n `如果用户提到「互动/扔/投掷/throw」,优先调用 throw_projectile。`,\n `当用户明确要求投掷 N 次且 1<=N<=100 时,必须按 N 执行:重复调用 throw_projectile 共 N 次,不要改成「示意/少量几次」。超过 100 则分批多次调用。`,\n `当前房间有4个位置,用户列表:${userList}。`,\n `我是 ${context.localName}(No.1)。`,\n ].join(\"\\n\");\n}\n\nexport function useAiAgent(options: {\n localName: string;\n peerName: string;\n peers?: Array<{ id: string; name: string }>;\n onStartCountdown?: (minutes: number) => void;\n onShowBubble?: (args: {\n text: string;\n target: number; // 1-4 for No.1 to No.4\n durationMs: number;\n }) => void;\n onThrowProjectile?: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n apiKey?: string;\n}) {\n const [lines, setLines] = useState<AiLine[]>([]);\n const [busy, setBusy] = useState(false);\n const [apiKeyError, setApiKeyError] = useState(false);\n\n const agentRef = useRef<Awaited<ReturnType<typeof createAgent>> | null>(null);\n const agentInitRef = useRef<Promise<\n Awaited<ReturnType<typeof createAgent>>\n > | null>(null);\n const agentKeyRef = useRef<string | null>(null);\n const stateRef = useRef<{ messages: unknown[] }>({ messages: [] });\n const abortRef = useRef<AbortController | null>(null);\n\n const append = useCallback((line: AiLine) => {\n setLines((prev) => [...prev, line]);\n }, []);\n\n const updateLine = useCallback((at: number, text: string) => {\n setLines((prev) => {\n const idx = prev.findIndex((l) => l.at === at);\n if (idx === -1) return prev;\n const next = [...prev];\n next[idx] = { ...next[idx], text };\n return next;\n });\n }, []);\n\n const ensureAgent = useCallback(async () => {\n const apiKey = (options.apiKey ?? \"\").trim();\n if (!apiKey) throw new Error(\"missing_api_key\");\n\n if (agentRef.current && agentKeyRef.current === apiKey)\n return agentRef.current;\n\n agentRef.current = null;\n agentInitRef.current = null;\n agentKeyRef.current = apiKey;\n stateRef.current.messages = [];\n\n agentInitRef.current ??= (async () => {\n const startCountdown = createCountdownTool({\n onStartCountdown: options.onStartCountdown,\n });\n\n const showBubble = createBubbleTool({\n onShowBubble: options.onShowBubble,\n });\n\n const sessionInfo = createSessionInfoTool({\n localName: options.localName,\n peerName: options.peerName,\n });\n\n const interaction = createInteractionTool({\n onThrow: options.onThrowProjectile,\n });\n\n const llm = new ChatOpenAI({\n model: \"deepseek-chat\",\n configuration: {\n baseURL: \"https://api.deepseek.com\",\n },\n apiKey: apiKey,\n temperature: 0.1,\n maxTokens: 1000,\n timeout: 30000,\n });\n return createAgent({\n model: llm,\n tools: [startCountdown, showBubble, interaction, sessionInfo],\n systemPrompt: createSystemPrompt({\n localName: options.localName,\n peerName: options.peerName,\n peers: options.peers,\n }),\n name: \"ghost\",\n });\n })();\n\n agentRef.current = await agentInitRef.current;\n return agentRef.current;\n }, [\n options.apiKey,\n options.localName,\n options.onStartCountdown,\n options.onShowBubble,\n options.onThrowProjectile,\n options.peerName,\n options.peers,\n ]);\n\n const ask = useCallback(\n async (text: string) => {\n const userAt = Date.now();\n const aiAt = userAt + 1;\n const toolAt = userAt + 2;\n setLines([\n { kind: \"user\", text: `> ${text}`, at: userAt },\n { kind: \"ai\", text: \"…\", at: aiAt },\n { kind: \"system\", text: \"\", at: toolAt },\n ]);\n\n abortRef.current?.abort();\n abortRef.current = new AbortController();\n\n setBusy(true);\n try {\n const agent = await ensureAgent();\n const stream = await agent.stream(\n {\n messages: [{ role: \"user\", content: text }],\n },\n {\n streamMode: \"values\",\n signal: abortRef.current.signal,\n } as any\n );\n\n for await (const chunk of stream as any) {\n const messages = (chunk?.messages ?? []) as unknown[];\n if (messages.length > 0) stateRef.current.messages = messages;\n\n const latest: any = messages.at(-1);\n if (latest?.tool_calls?.length) {\n const names = latest.tool_calls\n .map((tc: any) => tc?.name)\n .filter(Boolean)\n .join(\", \");\n if (names) updateLine(toolAt, `Calling tools: ${names}`);\n continue;\n }\n\n const t = lastAiText(messages);\n if (t !== null) updateLine(aiAt, t);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n const isApiKeyError =\n msg === \"missing_api_key\" ||\n msg.includes(\"401\") ||\n msg.includes(\"403\") ||\n msg.includes(\"Unauthorized\") ||\n msg.includes(\"Invalid API\");\n\n if (isApiKeyError) {\n setApiKeyError(true);\n updateLine(aiAt, \"API Key 错误或失效,请重新输入(按 R 键)\");\n } else {\n updateLine(aiAt, `(AI 出错)${msg}`);\n }\n } finally {\n setBusy(false);\n }\n },\n [append, ensureAgent, updateLine]\n );\n\n const resetApiKeyError = useCallback(() => {\n setApiKeyError(false);\n }, []);\n\n useEffect(() => {\n return () => abortRef.current?.abort();\n }, []);\n\n return { lines, ask, busy, apiKeyError, resetApiKeyError };\n}\n","export const UDP_PORT = 45888;\nexport const TCP_DEFAULT_PORT = 45999;\n\nexport const DISCOVERY_VERSION = 1;\nexport const APP_NAME = 'TermBuddy';\n","import os from \"node:os\";\n\nfunction ipv4ToInt(ip: string) {\n return ip\n .split(\".\")\n .map((n) => Number.parseInt(n, 10))\n .reduce((acc, n) => ((acc << 8) | (n & 255)) >>> 0, 0);\n}\n\nfunction intToIpv4(n: number) {\n return [24, 16, 8, 0].map((shift) => String((n >>> shift) & 255)).join(\".\");\n}\n\nexport function getBroadcastTargets(): string[] {\n const out = new Set<string>([\"255.255.255.255\"]);\n\n const ifaces = os.networkInterfaces();\n for (const entries of Object.values(ifaces)) {\n if (!entries) continue;\n for (const e of entries) {\n if (e.family !== \"IPv4\") continue;\n if (e.internal) continue;\n if (!e.address || !e.netmask) continue;\n const ip = ipv4ToInt(e.address);\n const mask = ipv4ToInt(e.netmask);\n const broadcast = (ip | (~mask >>> 0)) >>> 0;\n out.add(intToIpv4(broadcast));\n }\n }\n\n return [...out];\n}\n","export { getBroadcastTargets } from \"./broadcast.js\";\n","import { useEffect } from \"react\";\nimport dgram from \"node:dgram\";\nimport { UDP_PORT, DISCOVERY_VERSION } from \"../constants.js\";\nimport type { DiscoveryPacket } from \"../protocol.js\";\nimport { getBroadcastTargets } from \"../net/index.js\";\n\ntype Options =\n | { enabled: false }\n | {\n enabled: true;\n hostName: string;\n roomName: string;\n tcpPort?: number | null;\n intervalMs?: number;\n };\n\nexport function useBroadcaster(options: Options) {\n const depKey = options.enabled\n ? `${options.hostName}|${options.roomName}|${options.tcpPort ?? \"\"}|${\n options.intervalMs ?? 1000\n }`\n : \"disabled\";\n\n useEffect(() => {\n if (!options.enabled) return;\n if (!options.tcpPort) return;\n\n const socket = dgram.createSocket(\"udp4\");\n socket.on(\"error\", () => {});\n\n socket.bind(() => {\n socket.setBroadcast(true);\n });\n\n const targets = getBroadcastTargets();\n\n const send = () => {\n const packet: DiscoveryPacket = {\n type: \"termbuddy_discovery\",\n version: DISCOVERY_VERSION,\n hostName: options.hostName,\n roomName: options.roomName,\n tcpPort: options.tcpPort!,\n sentAt: Date.now(),\n };\n const msg = Buffer.from(JSON.stringify(packet));\n for (const address of targets) {\n socket.send(msg, UDP_PORT, address);\n }\n };\n\n send();\n const id = setInterval(send, options.intervalMs ?? 1000);\n\n return () => {\n clearInterval(id);\n socket.close();\n };\n }, [depKey]);\n}\n","import { useEffect, useState } from \"react\";\nimport dgram from \"node:dgram\";\nimport { UDP_PORT, DISCOVERY_VERSION } from \"../constants.js\";\nimport type { DiscoveryPacket } from \"../protocol.js\";\nimport type { DiscoveredRoom } from \"../types.js\";\n\nfunction safeParse(msg: Buffer): DiscoveryPacket | null {\n try {\n const parsed = JSON.parse(msg.toString(\"utf8\")) as DiscoveryPacket;\n if (parsed?.type !== \"termbuddy_discovery\") return null;\n if (parsed?.version !== DISCOVERY_VERSION) return null;\n if (!parsed.hostName || !parsed.roomName || !parsed.tcpPort) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function useScanner(options?: {\n staleAfterMs?: number;\n}): DiscoveredRoom[] {\n const staleAfterMs = options?.staleAfterMs ?? 3500;\n const [rooms, setRooms] = useState<DiscoveredRoom[]>([]);\n\n useEffect(() => {\n const socket = dgram.createSocket(\"udp4\");\n socket.on(\"error\", () => {});\n\n socket.on(\"message\", (msg, rinfo) => {\n const packet = safeParse(msg);\n if (!packet) return;\n\n const now = Date.now();\n setRooms((prev) => {\n const key = `${rinfo.address}:${packet.tcpPort}`;\n const next = prev.filter((r) => `${r.ip}:${r.tcpPort}` !== key);\n next.push({\n ip: rinfo.address,\n hostName: packet.hostName,\n roomName: packet.roomName,\n tcpPort: packet.tcpPort,\n lastSeenAt: now,\n });\n return next;\n });\n });\n\n socket.bind(UDP_PORT, () => {});\n\n const prune = setInterval(() => {\n const now = Date.now();\n setRooms((prev) =>\n prev.filter((r) => now - r.lastSeenAt <= staleAfterMs)\n );\n }, 500);\n\n return () => {\n clearInterval(prune);\n socket.close();\n };\n }, [staleAfterMs]);\n\n return rooms;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport net from \"node:net\";\nimport type {\n ActivityState,\n ConnectionStatus,\n TcpPacket,\n ProjectileKind,\n ProjectileDirection,\n Peer,\n} from \"../protocol.js\";\nimport { TCP_DEFAULT_PORT } from \"../constants.js\";\n\ntype HostOptions = { role: \"host\"; localName: string; port?: number };\ntype ClientOptions = {\n role: \"client\";\n localName: string;\n hostIp: string;\n tcpPort: number;\n hostName?: string;\n};\ntype Options = HostOptions | ClientOptions;\n\nfunction writePacket(socket: net.Socket, packet: TcpPacket) {\n socket.write(`${JSON.stringify(packet)}\\n`, \"utf8\");\n}\n\ntype PeerConnection = {\n id: string;\n socket: net.Socket;\n name: string;\n state: ActivityState;\n lastSeen: number;\n heartbeatInterval: NodeJS.Timeout | null;\n};\n\nexport function useTcpSync(options: Options): {\n status: ConnectionStatus;\n listenPort?: number;\n peers: Peer[];\n peerName?: string; // For backward compatibility (first peer name)\n remoteState?: ActivityState; // For backward compatibility (first peer state)\n sendStatus: (state: ActivityState) => void;\n sendProjectile: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n setOnRemoteProjectile: (callback: (kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) => void;\n} {\n const [status, setStatus] = useState<ConnectionStatus>(\n options.role === \"host\" ? \"waiting\" : \"connecting\"\n );\n const [listenPort, setListenPort] = useState<number | undefined>(undefined);\n const [peers, setPeers] = useState<Peer[]>([]);\n\n const onRemoteProjectileRef = useRef<((kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) | undefined>(undefined);\n\n // For host: map of all peer connections\n const peerConnectionsRef = useRef<Map<string, PeerConnection>>(new Map());\n // For client: single socket ref\n const clientSocketRef = useRef<net.Socket | null>(null);\n const clientHeartbeatRef = useRef<NodeJS.Timeout | null>(null);\n const clientLastSeenRef = useRef<number>(Date.now());\n\n // Generate unique peer ID\n const generatePeerId = () => `peer_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n // Broadcast packet to all peers (host only)\n const broadcastPacket = useCallback((packet: TcpPacket, excludeId?: string) => {\n peerConnectionsRef.current.forEach((peer, id) => {\n if (id !== excludeId && !peer.socket.destroyed) {\n writePacket(peer.socket, packet);\n }\n });\n }, []);\n\n // Update peers state from connections map\n const syncPeersState = useCallback(() => {\n const peerList: Peer[] = [];\n peerConnectionsRef.current.forEach((conn) => {\n peerList.push({ id: conn.id, name: conn.name, state: conn.state });\n });\n setPeers(peerList);\n\n // Update status based on peer count\n if (options.role === \"host\") {\n setStatus(peerList.length > 0 ? \"connected\" : \"waiting\");\n }\n }, [options.role]);\n\n // Remove a peer connection (host only)\n const removePeerConnection = useCallback((peerId: string) => {\n const conn = peerConnectionsRef.current.get(peerId);\n if (conn) {\n if (conn.heartbeatInterval) clearInterval(conn.heartbeatInterval);\n if (!conn.socket.destroyed) conn.socket.destroy();\n\n // Notify other peers about the left peer\n broadcastPacket({ type: \"peer_left\", peerName: conn.name, sentAt: Date.now() }, peerId);\n\n peerConnectionsRef.current.delete(peerId);\n syncPeersState();\n }\n }, [broadcastPacket, syncPeersState]);\n\n const MAX_PEERS = 4;\n\n // Attach a new peer socket (host only)\n const attachPeerSocket = useCallback((socket: net.Socket) => {\n // Reject if already at max capacity (host + 3 clients = 4 total, so max 3 peer connections)\n if (peerConnectionsRef.current.size >= MAX_PEERS - 1) {\n socket.end();\n socket.destroy();\n return;\n }\n\n const peerId = generatePeerId();\n let buf = \"\";\n\n socket.setNoDelay(true);\n socket.setEncoding(\"utf8\");\n\n const conn: PeerConnection = {\n id: peerId,\n socket,\n name: \"Connecting...\",\n state: \"IDLE\",\n lastSeen: Date.now(),\n heartbeatInterval: null,\n };\n\n peerConnectionsRef.current.set(peerId, conn);\n\n socket.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n\n try {\n const packet = JSON.parse(line) as TcpPacket;\n conn.lastSeen = Date.now();\n\n if (packet.type === \"hello\") {\n conn.name = packet.clientName;\n syncPeersState();\n\n // Send hello back\n writePacket(socket, {\n type: \"hello\",\n hostName: options.localName,\n clientName: \"Host\",\n sentAt: Date.now(),\n });\n\n // Notify other peers about new peer\n broadcastPacket({ type: \"peer_joined\", peerName: conn.name, sentAt: Date.now() }, peerId);\n\n // Send list of existing peers to new peer\n peerConnectionsRef.current.forEach((existingConn, existingId) => {\n if (existingId !== peerId && existingConn.name !== \"Connecting...\") {\n writePacket(socket, { type: \"peer_joined\", peerName: existingConn.name, sentAt: Date.now() });\n }\n });\n }\n\n if (packet.type === \"status\") {\n conn.state = packet.state;\n syncPeersState();\n // Broadcast status to other peers\n broadcastPacket({ ...packet, senderName: conn.name }, peerId);\n }\n\n if (packet.type === \"ping\") {\n writePacket(socket, { type: \"pong\", sentAt: Date.now() });\n }\n\n if (packet.type === \"projectile\") {\n // Call local handler\n onRemoteProjectileRef.current?.(packet.kind, packet.direction, conn.name);\n // Broadcast to other peers\n broadcastPacket({ ...packet, senderName: conn.name }, peerId);\n }\n } catch {\n // ignore parse errors\n }\n }\n });\n\n socket.on(\"close\", () => {\n removePeerConnection(peerId);\n });\n\n socket.on(\"error\", () => {\n removePeerConnection(peerId);\n });\n\n // Heartbeat for this peer\n conn.heartbeatInterval = setInterval(() => {\n if (socket.destroyed) {\n removePeerConnection(peerId);\n return;\n }\n writePacket(socket, { type: \"ping\", sentAt: Date.now() });\n const age = Date.now() - conn.lastSeen;\n if (age > 6000) {\n removePeerConnection(peerId);\n }\n }, 2000);\n\n syncPeersState();\n }, [options.localName, broadcastPacket, syncPeersState, removePeerConnection]);\n\n // Client socket cleanup\n const cleanupClientSocket = useCallback(() => {\n if (clientHeartbeatRef.current) clearInterval(clientHeartbeatRef.current);\n clientHeartbeatRef.current = null;\n\n const s = clientSocketRef.current;\n clientSocketRef.current = null;\n if (s && !s.destroyed) s.destroy();\n }, []);\n\n // Attach client socket\n const attachClientSocket = useCallback((socket: net.Socket) => {\n cleanupClientSocket();\n clientSocketRef.current = socket;\n clientLastSeenRef.current = Date.now();\n\n setStatus(\"connected\");\n\n let buf = \"\";\n socket.setNoDelay(true);\n socket.setEncoding(\"utf8\");\n\n socket.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n\n try {\n const packet = JSON.parse(line) as TcpPacket;\n clientLastSeenRef.current = Date.now();\n\n if (packet.type === \"hello\") {\n // Host connected - add host as first peer\n setPeers((prev) => {\n const hostExists = prev.some((p) => p.id === \"host\");\n if (hostExists) return prev;\n return [{ id: \"host\", name: packet.hostName, state: \"IDLE\" as ActivityState }, ...prev];\n });\n }\n\n if (packet.type === \"status\") {\n // Update peer's state\n const senderName = packet.senderName;\n if (senderName) {\n setPeers((prev) => prev.map((p) =>\n p.name === senderName ? { ...p, state: packet.state } : p\n ));\n } else {\n // From host\n setPeers((prev) => prev.map((p) =>\n p.id === \"host\" ? { ...p, state: packet.state } : p\n ));\n }\n }\n\n if (packet.type === \"ping\") {\n writePacket(socket, { type: \"pong\", sentAt: Date.now() });\n }\n\n if (packet.type === \"projectile\") {\n onRemoteProjectileRef.current?.(packet.kind, packet.direction, packet.senderName);\n }\n\n if (packet.type === \"peer_joined\") {\n setPeers((prev) => {\n const exists = prev.some((p) => p.name === packet.peerName);\n if (exists) return prev;\n return [...prev, { id: `peer_${Date.now()}`, name: packet.peerName, state: \"IDLE\" as ActivityState }];\n });\n }\n\n if (packet.type === \"peer_left\") {\n setPeers((prev) => prev.filter((p) => p.name !== packet.peerName));\n }\n } catch {\n // ignore\n }\n }\n });\n\n socket.on(\"close\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n cleanupClientSocket();\n });\n\n socket.on(\"error\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n });\n\n // Hello handshake\n writePacket(socket, {\n type: \"hello\",\n hostName: options.role === \"client\" ? options.hostName ?? \"Host\" : options.localName,\n clientName: options.localName,\n sentAt: Date.now(),\n });\n\n // Heartbeat\n clientHeartbeatRef.current = setInterval(() => {\n const sock = clientSocketRef.current;\n if (!sock || sock.destroyed) return;\n writePacket(sock, { type: \"ping\", sentAt: Date.now() });\n const age = Date.now() - clientLastSeenRef.current;\n if (age > 6000) {\n setStatus(\"disconnected\");\n setPeers([]);\n cleanupClientSocket();\n }\n }, 2000);\n }, [cleanupClientSocket, options]);\n\n // Main effect: setup server or client\n useEffect(() => {\n if (options.role === \"host\") {\n const server = net.createServer((socket) => {\n attachPeerSocket(socket);\n });\n\n server.on(\"error\", () => {});\n\n server.listen(options.port ?? TCP_DEFAULT_PORT, () => {\n const address = server.address();\n if (address && typeof address === \"object\") setListenPort(address.port);\n });\n\n return () => {\n // Cleanup all peer connections\n peerConnectionsRef.current.forEach((conn) => {\n if (conn.heartbeatInterval) clearInterval(conn.heartbeatInterval);\n if (!conn.socket.destroyed) conn.socket.destroy();\n });\n peerConnectionsRef.current.clear();\n server.close();\n };\n }\n\n // Client mode\n setStatus(\"connecting\");\n const socket = net.createConnection(\n { host: options.hostIp, port: options.tcpPort },\n () => {\n attachClientSocket(socket);\n }\n );\n socket.on(\"error\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n });\n\n return () => {\n socket.destroy();\n cleanupClientSocket();\n };\n }, [attachPeerSocket, attachClientSocket, cleanupClientSocket, options]);\n\n // Send status to all peers\n const sendStatus = useCallback((state: ActivityState) => {\n if (options.role === \"host\") {\n broadcastPacket({ type: \"status\", state, senderName: options.localName, sentAt: Date.now() });\n } else {\n const socket = clientSocketRef.current;\n if (socket && !socket.destroyed) {\n writePacket(socket, { type: \"status\", state, sentAt: Date.now() });\n }\n }\n }, [options, broadcastPacket]);\n\n // Send projectile to all peers\n const sendProjectile = useCallback((kind: ProjectileKind, direction: ProjectileDirection) => {\n if (options.role === \"host\") {\n broadcastPacket({ type: \"projectile\", kind, direction, senderName: options.localName, sentAt: Date.now() });\n } else {\n const socket = clientSocketRef.current;\n if (socket && !socket.destroyed) {\n writePacket(socket, { type: \"projectile\", kind, direction, sentAt: Date.now() });\n }\n }\n }, [options, broadcastPacket]);\n\n const setOnRemoteProjectile = useCallback((callback: (kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) => {\n onRemoteProjectileRef.current = callback;\n }, []);\n\n // Backward compatibility: first peer\n const firstPeer = peers[0];\n const peerName = firstPeer?.name;\n const remoteState = firstPeer?.state;\n\n return {\n status,\n listenPort,\n peers,\n peerName,\n remoteState,\n sendStatus,\n sendProjectile,\n setOnRemoteProjectile\n };\n}\n","export { useActivityMonitor } from \"./useActivityMonitor.js\";\nexport { useAiAgent } from \"./useAiAgent.js\";\nexport { useBroadcaster } from \"./useBroadcaster.js\";\nexport { useScanner } from \"./useScanner.js\";\nexport { useTcpSync } from \"./useTcpSync.js\";\n","import React, {useMemo} from 'react';\nimport {Box, Text, useInput} from 'ink';\nimport {useScanner} from '../hooks/index.js';\nimport type {DiscoveredRoom} from '../types.js';\n\nexport function RoomScanner(props: {\n\tonSelectRoom: (room: DiscoveredRoom) => void;\n\tonBack: () => void;\n\tonExit: () => void;\n}) {\n\tconst rooms = useScanner();\n\n\tconst sortedRooms = useMemo(() => {\n\t\treturn [...rooms].sort((a, b) => b.lastSeenAt - a.lastSeenAt);\n\t}, [rooms]);\n\n\tuseInput((input, key) => {\n\t\tif (key.escape || input === 'b') props.onBack();\n\t\tif (input === 'q') props.onExit();\n\n\t\tconst index = Number.parseInt(input, 10);\n\t\tif (Number.isNaN(index)) return;\n\t\tconst room = sortedRooms[index - 1];\n\t\tif (!room) return;\n\t\tprops.onSelectRoom(room);\n\t});\n\n\treturn (\n\t\t<Box flexDirection=\"column\" padding={1}>\n\t\t\t<Text>\n\t\t\t\t<Text color=\"yellow\">正在扫描局域网...</Text> (按 <Text color=\"cyan\">b</Text> 返回,{' '}\n\t\t\t\t<Text color=\"cyan\">q</Text> 退出)\n\t\t\t</Text>\n\t\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t\t{sortedRooms.length === 0 ? (\n\t\t\t\t\t<Text color=\"gray\">暂无房间广播。</Text>\n\t\t\t\t) : (\n\t\t\t\t\tsortedRooms.map((room, i) => (\n\t\t\t\t\t\t<Text key={`${room.ip}:${room.tcpPort}`}>\n\t\t\t\t\t\t\t<Text color=\"cyan\">[{i + 1}]</Text> {room.hostName} <Text color=\"gray\">@</Text>{' '}\n\t\t\t\t\t\t\t<Text color=\"gray\">\n\t\t\t\t\t\t\t\t{room.ip}:{room.tcpPort}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</Box>\n\t\t</Box>\n\t);\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { LeaveStats } from \"../types.js\";\n\nfunction formatDuration(ms: number) {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n if (hours > 0) return `${hours}小时${minutes}分${seconds}秒`;\n if (minutes > 0) return `${minutes}分${seconds}秒`;\n return `${seconds}秒`;\n}\n\nexport function LeavePage(props: {\n stats: LeaveStats;\n onBack: () => void;\n onExit: () => void;\n}) {\n useInput((input, key) => {\n if (key.escape || input === \"q\") props.onExit();\n if (key.return || input === \" \") props.onBack();\n });\n\n const sessionLabel = useMemo(\n () => formatDuration(props.stats.sessionDurationMs),\n [props.stats.sessionDurationMs]\n );\n const connectedLabel = useMemo(\n () => formatDuration(props.stats.connectedDurationMs),\n [props.stats.connectedDurationMs]\n );\n\n return (\n <Box flexDirection=\"column\" padding={1} alignItems=\"center\">\n {/* <Text color=\"cyan\">\n {String.raw`\n ____ _ _ \n / ___| ___ ___ | | __ _| |_| |\n \\___ \\ / _ \\ _ \\ | | / _' | __| |\n ___) | __/ __/ | |__| (_| | |_|_|\n |____/ \\___|\\___|___|_____\\__,_|\\__(_)\n |_____| \n`}\n </Text> */}\n\n <Box\n flexDirection=\"column\"\n marginTop={1}\n borderStyle=\"round\"\n paddingX={2}\n borderColor=\"gray\"\n >\n <Text color=\"white\" bold>\n {props.stats.peerName\n ? `与 ${props.stats.peerName} 的同频记录`\n : \"本次专注记录\"}\n </Text>\n\n <Box marginTop={1} flexDirection=\"column\" gap={1}>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>⌨️ 键盘敲击</Text>\n <Text color=\"yellow\">{props.stats.keyPresses}</Text>\n </Box>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>⏱️ 总共时长</Text>\n <Text color=\"green\">{sessionLabel}</Text>\n </Box>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>🔗 连线时长</Text>\n <Text color=\"blue\">{connectedLabel}</Text>\n </Box>\n </Box>\n </Box>\n\n <Box marginTop={1}>\n <Text color=\"gray\">\n 按 <Text color=\"white\">Enter</Text> 返回菜单,或{\" \"}\n <Text color=\"red\">q</Text> 退出程序\n </Text>\n </Box>\n </Box>\n );\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\ntype KeyFile = {apiKey?: string};\n\nconst KEY_RELATIVE_PATH = path.join('src', 'assets', 'key.json');\n\nasync function readJsonFile(filePath: string): Promise<KeyFile | null> {\n\ttry {\n\t\tconst raw = await fs.readFile(filePath, 'utf8');\n\t\tconst parsed = JSON.parse(raw) as KeyFile;\n\t\tif (!parsed || typeof parsed !== 'object') return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function ensureDirForFile(filePath: string) {\n\tawait fs.mkdir(path.dirname(filePath), {recursive: true});\n}\n\nexport async function loadStoredApiKey(): Promise<string | null> {\n\tconst absolute = path.resolve(process.cwd(), KEY_RELATIVE_PATH);\n\tconst json = await readJsonFile(absolute);\n\tconst key = (json?.apiKey ?? '').trim();\n\treturn key.length > 0 ? key : null;\n}\n\nexport async function saveStoredApiKey(apiKey: string): Promise<void> {\n\tconst trimmed = apiKey.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('API key cannot be empty');\n\t}\n\tconst absolute = path.resolve(process.cwd(), KEY_RELATIVE_PATH);\n\tawait ensureDirForFile(absolute);\n\tconst payload: KeyFile = {apiKey: trimmed};\n\tawait fs.writeFile(absolute, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAiAgent } from \"../hooks/index.js\";\nimport { loadStoredApiKey, saveStoredApiKey } from \"../storage/apiKey.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"./sprite/ProjectileThrowSprite.js\";\n\nexport function AiConsole(props: {\n onClose: () => void;\n onStartCountdown: (minutes: number) => void;\n onShowBubble?: (args: {\n text: string;\n target: number; // 1-4 for No.1 to No.4\n durationMs: number;\n }) => void;\n onThrowProjectile: (\n kind: ProjectileKind,\n direction: ProjectileDirection\n ) => void;\n localName: string;\n peerName: string;\n peers?: Array<{ id: string; name: string }>; // For session info\n}) {\n const [input, setInput] = useState(\"\");\n const [apiKey, setApiKey] = useState<string | null>(null);\n const [keyDraft, setKeyDraft] = useState(\"\");\n const [cursorOn, setCursorOn] = useState(true);\n const [keyStatus, setKeyStatus] = useState<\n \"loading\" | \"missing\" | \"ready\" | \"saving\"\n >(\"loading\");\n\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n const stored = await loadStoredApiKey();\n if (cancelled) return;\n if (stored) {\n setApiKey(stored);\n setKeyStatus(\"ready\");\n } else {\n setKeyStatus(\"missing\");\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n const handle = setInterval(() => setCursorOn((v) => !v), 500);\n return () => clearInterval(handle);\n }, []);\n\n const agent = useAiAgent({\n localName: props.localName,\n peerName: props.peerName,\n peers: props.peers,\n onStartCountdown: props.onStartCountdown,\n onShowBubble: props.onShowBubble,\n onThrowProjectile: props.onThrowProjectile,\n apiKey: apiKey ?? undefined,\n });\n\n const resetApiKey = () => {\n setApiKey(null);\n setKeyDraft(\"\");\n setKeyStatus(\"missing\");\n agent.resetApiKeyError();\n };\n\n const helpLine = useMemo(\n () => \"示例:倒计时20分钟 / 聊会天 / 和别人互动一下\",\n []\n );\n\n useInput(\n (ch, key) => {\n if (key.escape) {\n props.onClose();\n return;\n }\n\n if (agent.apiKeyError && (ch === \"r\" || ch === \"R\")) {\n resetApiKey();\n return;\n }\n\n if (keyStatus !== \"ready\") {\n if (key.return) {\n const draft = keyDraft.trim();\n if (!draft) return;\n setKeyStatus(\"saving\");\n void (async () => {\n await saveStoredApiKey(draft);\n setApiKey(draft);\n setKeyDraft(\"\");\n setKeyStatus(\"ready\");\n })();\n return;\n }\n\n if (key.backspace || key.delete) {\n setKeyDraft((s) => s.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (ch) setKeyDraft((s) => s + ch);\n return;\n }\n\n if (key.return) {\n const line = input.trim();\n setInput(\"\");\n if (!line) return;\n void agent.ask(line);\n return;\n }\n\n if (key.backspace || key.delete) {\n setInput((s) => s.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (ch) setInput((s) => s + ch);\n },\n { isActive: true }\n );\n\n const lines = agent.lines.filter((l) => l.text.trim().length > 0).slice(-6);\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} paddingY={0}>\n <Box justifyContent=\"space-between\" marginBottom={0}>\n <Text color=\"cyan\">AI Console</Text>\n <Text color=\"gray\">\n {keyStatus === \"saving\"\n ? \"Saving…\"\n : agent.apiKeyError\n ? \"Press R to reset API\"\n : agent.busy\n ? \"Thinking…\"\n : \"Esc Close\"}\n </Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {keyStatus === \"loading\" ? (\n <Text color=\"gray\">Checking API Key...</Text>\n ) : keyStatus === \"missing\" || keyStatus === \"saving\" ? (\n <Text color=\"yellow\">\n Setup: Enter DeepSeek API Key (saves to{\" \"}\n <Text color=\"cyan\">src/assets/key.json</Text>)\n </Text>\n ) : lines.length === 0 ? (\n <Text color=\"gray\">{helpLine}</Text>\n ) : null}\n </Box>\n\n <Box flexDirection=\"column\" marginTop={0} minHeight={6}>\n {keyStatus === \"ready\" ? (\n <>\n {lines.map((l, i) => (\n <Text\n key={`${l.kind}:${l.at}:${i}`}\n color={l.kind === \"user\" ? \"yellow\" : \"white\"}\n wrap=\"truncate-end\"\n >\n {l.kind === \"user\" ? \"> \" : \"\"}\n {l.text}\n </Text>\n ))}\n </>\n ) : (\n <Text color=\"gray\">Please enter API Key to proceed.</Text>\n )}\n </Box>\n\n <Box\n marginTop={0}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n >\n <Text color=\"green\">{\">\"} </Text>\n {keyStatus === \"ready\" ? (\n <>\n <Text>{input}</Text>\n {cursorOn ? <Text inverse> </Text> : <Text> </Text>}\n </>\n ) : (\n <Text>\n {keyDraft.length === 0\n ? \"\"\n : \"*\".repeat(Math.min(64, keyDraft.length))}\n {cursorOn ? <Text inverse> </Text> : <Text> </Text>}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text } from \"ink\";\n\nfunction wrapText(text: string, maxWidth: number) {\n const chars = Array.from(text);\n const lines: string[] = [];\n for (let i = 0; i < chars.length; i += maxWidth) {\n lines.push(chars.slice(i, i + maxWidth).join(\"\"));\n }\n return lines.length ? lines : [\"\"];\n}\n\nfunction renderBubbleLines(text: string, maxInnerWidth: number) {\n const contentLines = wrapText(text, maxInnerWidth);\n const innerWidth = Math.min(\n maxInnerWidth,\n Math.max(...contentLines.map((l) => Array.from(l).length), 1)\n );\n\n const top = `╭${\"─\".repeat(innerWidth + 2)}╮`;\n \n // Create a bottom line with a little tail \"v\" in the middle\n const tailPos = Math.floor((innerWidth + 2) / 2);\n const bottomChars = Array.from(`╰${\"─\".repeat(innerWidth + 2)}╯`);\n if (bottomChars[tailPos]) bottomChars[tailPos] = \"v\"; // Simple tail\n const bottom = bottomChars.join(\"\");\n\n const middle = contentLines.map((l) => {\n const pad = innerWidth - Array.from(l).length;\n return `│ ${l}${\" \".repeat(Math.max(0, pad))} │`;\n });\n return [top, ...middle, bottom];\n}\n\nexport function BubbleSprite(props: {\n text: string;\n maxInnerWidth?: number;\n color?: string;\n}) {\n const trimmed = props.text.trim();\n const maxInnerWidth = props.maxInnerWidth ?? 18;\n\n const lines = useMemo(() => {\n if (!trimmed) return null;\n return renderBubbleLines(trimmed, maxInnerWidth);\n }, [maxInnerWidth, trimmed]);\n\n if (!lines) return null;\n\n return (\n <Box flexDirection=\"column\" alignItems=\"center\">\n {lines.map((line, i) => (\n <Text key={`bubble:${i}`} color={props.color ?? \"gray\"}>\n {line}\n </Text>\n ))}\n </Box>\n );\n}\n\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityState } from \"../../protocol.js\";\nimport { BubbleSprite } from \"./BubbleSprite.js\";\n\nconst SPRITES: Record<\n ActivityState,\n { color?: string; compact: string; frames: string[] }\n> = {\n TYPING: {\n color: \"green\",\n compact: \"( >_<)===3\",\n frames: [\" /\\\\_/\\\\ \", \"( >_<) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n IDLE: {\n color: \"yellow\",\n compact: \"( -.-)Zzz\",\n frames: [\" /\\\\_/\\\\ \", \"( -.-) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n OFFLINE: {\n color: \"gray\",\n compact: \"( x_x)\",\n frames: [\" /\\\\_/\\\\ \", \"( x_x) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n};\n\nexport function BuddyAvatar(props: {\n state: ActivityState;\n variant?: \"frames\" | \"compact\";\n bubbleText?: string | null;\n marginTop?: number;\n}) {\n const sprite = SPRITES[props.state];\n if (props.variant === \"compact\") {\n return (\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n marginTop={props.marginTop ?? 1}\n >\n {props.bubbleText ? <BubbleSprite text={props.bubbleText} /> : null}\n <Text color={sprite.color}>{sprite.compact}</Text>\n </Box>\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n marginTop={props.marginTop ?? 1}\n >\n {props.bubbleText ? <BubbleSprite text={props.bubbleText} /> : null}\n {sprite.frames.map((line, i) => (\n <Text key={`${props.state}:${i}`} color={sprite.color}>\n {line}\n </Text>\n ))}\n </Box>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type CountdownClockType = \"SHORT\" | \"MEDIUM\" | \"LONG\";\nexport type CountdownClockVariant = \"FULL\" | \"COMPACT\";\n\nexport function countdownClockTypeFromMinutes(\n minutes: number\n): CountdownClockType {\n if (minutes <= 10) return \"SHORT\";\n if (minutes <= 30) return \"MEDIUM\";\n return \"LONG\";\n}\n\nfunction clamp01(n: number) {\n if (n <= 0) return 0;\n if (n >= 1) return 1;\n return n;\n}\n\nconst TYPE_STYLE: Record<CountdownClockType, { color: string; label: string }> =\n {\n SHORT: { color: \"green\", label: \"Sprint\" },\n MEDIUM: { color: \"cyan\", label: \"Focus\" },\n LONG: { color: \"magenta\", label: \"Deep\" },\n };\n\ntype HandDir = \"N\" | \"NE\" | \"E\" | \"SE\" | \"S\" | \"SW\" | \"W\" | \"NW\";\n\nfunction handFromProgress(progress01: number): HandDir {\n const idx = Math.round(clamp01(progress01) * 7);\n const dirs: HandDir[] = [\"N\", \"NE\", \"E\", \"SE\", \"S\", \"SW\", \"W\", \"NW\"];\n return dirs[idx]!;\n}\n\nfunction renderClockFace(hand: HandDir) {\n const lines = [\n \" .---. \",\n \" / \\\\ \",\n \"| • |\",\n \" \\\\ / \",\n \" '---' \",\n ].map((s) => s.split(\"\"));\n\n const center = { r: 2, c: 4 };\n const handMap: Record<HandDir, { r: number; c: number; ch: string }> = {\n N: { r: 1, c: 4, ch: \"|\" },\n NE: { r: 1, c: 5, ch: \"/\" },\n E: { r: 2, c: 5, ch: \"-\" },\n SE: { r: 3, c: 5, ch: \"\\\\\" },\n S: { r: 3, c: 4, ch: \"|\" },\n SW: { r: 3, c: 3, ch: \"/\" },\n W: { r: 2, c: 3, ch: \"-\" },\n NW: { r: 1, c: 3, ch: \"\\\\\" },\n };\n\n const tip = handMap[hand];\n lines[center.r][center.c] = \"•\";\n lines[tip.r][tip.c] = tip.ch;\n\n return lines.map((row) => row.join(\"\"));\n}\n\nfunction renderCompactClockFace(hand: HandDir) {\n const lines = [\" .---. \", \"| • |\", \" '---' \"].map((s) => s.split(\"\"));\n const center = { r: 1, c: 3 };\n const handMap: Record<HandDir, { r: number; c: number; ch: string }> = {\n N: { r: 0, c: 3, ch: \"|\" },\n NE: { r: 0, c: 4, ch: \"/\" },\n E: { r: 1, c: 5, ch: \"-\" },\n SE: { r: 2, c: 4, ch: \"\\\\\" },\n S: { r: 2, c: 3, ch: \"|\" },\n SW: { r: 2, c: 2, ch: \"/\" },\n W: { r: 1, c: 1, ch: \"-\" },\n NW: { r: 0, c: 2, ch: \"\\\\\" },\n };\n\n const tip = handMap[hand];\n lines[center.r][center.c] = \"•\";\n lines[tip.r][tip.c] = tip.ch;\n\n return lines.map((row) => row.join(\"\"));\n}\n\nexport function CountdownClockSprite(props: {\n type?: CountdownClockType;\n variant?: CountdownClockVariant;\n minutes?: number;\n label?: string | null;\n showLabel?: boolean;\n totalSeconds?: number;\n remainingSeconds?: number | null;\n}) {\n const type =\n props.type ??\n (typeof props.minutes === \"number\"\n ? countdownClockTypeFromMinutes(props.minutes)\n : \"MEDIUM\");\n\n const progress01 = useMemo(() => {\n if (\n typeof props.totalSeconds !== \"number\" ||\n props.totalSeconds <= 0 ||\n props.remainingSeconds === null ||\n typeof props.remainingSeconds !== \"number\"\n ) {\n return null;\n }\n return clamp01(props.remainingSeconds / props.totalSeconds);\n }, [props.remainingSeconds, props.totalSeconds]);\n\n const style = TYPE_STYLE[type];\n const hand = handFromProgress(progress01 ?? 1);\n const caption = props.label ?? style.label;\n\n if (props.variant === \"COMPACT\") {\n const face = renderCompactClockFace(hand);\n return (\n <Box flexDirection=\"column\">\n {face.map((line, i) => (\n <Text key={`clock:compact:${type}:${hand}:${i}`} color={style.color}>\n {line}\n </Text>\n ))}\n {props.showLabel === false ? null : (\n <Text color=\"gray\">{caption ?? \" \"}</Text>\n )}\n </Box>\n );\n }\n\n const face = renderClockFace(hand);\n\n return (\n <Box flexDirection=\"column\">\n {face.map((line, i) => (\n <Text key={`clock:${type}:${hand}:${i}`} color={style.color}>\n {line}\n </Text>\n ))}\n {props.showLabel === false ? null : (\n <Text color=\"gray\">{caption ?? \" \"}</Text>\n )}\n </Box>\n );\n}\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type ProjectileKind = \"ROSE\" | \"POOP\" | \"HAMMER\";\nexport type ProjectileDirection = \"LEFT_TO_RIGHT\" | \"RIGHT_TO_LEFT\";\n\nconst PROJECTILES: Record<ProjectileKind, { glyph: string; color: string }> = {\n ROSE: { glyph: \"🌹\", color: \"magenta\" },\n POOP: { glyph: \"💩\", color: \"yellow\" },\n HAMMER: { glyph: \"🔨\", color: \"cyan\" },\n};\n\nfunction clamp01(n: number) {\n if (n <= 0) return 0;\n if (n >= 1) return 1;\n return n;\n}\n\nfunction renderTrack(width: number, pos: number, glyph: string) {\n const w = Math.max(8, Math.floor(width));\n const innerWidth = w - 2;\n if (pos < 0) return `|${new Array(innerWidth).fill(\"·\").join(\"\")}|`;\n const clampedPos = Math.max(0, Math.min(innerWidth - 1, Math.floor(pos)));\n\n const track = new Array(innerWidth).fill(\"·\");\n track[clampedPos] = glyph;\n return `|${track.join(\"\")}|`;\n}\n\nexport function ProjectileThrowSprite(props: {\n kind: ProjectileKind;\n direction?: ProjectileDirection;\n width?: number;\n progress?: number;\n shotId?: string | number;\n durationMs?: number;\n leftLabel?: string;\n rightLabel?: string;\n onDone?: () => void;\n}) {\n const direction = props.direction ?? \"LEFT_TO_RIGHT\";\n const width = props.width ?? 28;\n const durationMs = props.durationMs ?? 700;\n\n const [autoProgress, setAutoProgress] = useState<number | null>(null);\n const progress = typeof props.progress === \"number\" ? props.progress : autoProgress;\n\n useEffect(() => {\n if (props.shotId === undefined) return;\n const startedAt = Date.now();\n setAutoProgress(0);\n\n const handle = setInterval(() => {\n const elapsed = Date.now() - startedAt;\n const next = clamp01(elapsed / Math.max(1, durationMs));\n setAutoProgress(next);\n if (next >= 1) {\n clearInterval(handle);\n props.onDone?.();\n }\n }, 33);\n\n return () => clearInterval(handle);\n }, [durationMs, props.onDone, props.shotId]);\n\n const projectile = PROJECTILES[props.kind];\n\n const track = useMemo(() => {\n if (progress === null || !Number.isFinite(progress)) {\n return renderTrack(width, -1, \" \");\n }\n const innerWidth = Math.max(8, Math.floor(width)) - 2;\n const rawPos = clamp01(progress) * (innerWidth - 1);\n const pos =\n direction === \"LEFT_TO_RIGHT\" ? rawPos : (innerWidth - 1 - rawPos);\n return renderTrack(width, pos, projectile.glyph);\n }, [direction, progress, projectile.glyph, width]);\n\n return (\n <Box>\n {props.leftLabel ? <Text color=\"gray\">{props.leftLabel} </Text> : null}\n <Text color={projectile.color}>{track}</Text>\n {props.rightLabel ? <Text color=\"gray\"> {props.rightLabel}</Text> : null}\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ConnectionStatus } from \"../protocol.js\";\n\nfunction statusText(status: ConnectionStatus) {\n switch (status) {\n case \"waiting\":\n return { label: \"Waiting\", color: \"yellow\" };\n case \"connecting\":\n return { label: \"Connecting\", color: \"yellow\" };\n case \"connected\":\n return { label: \"Connected via TCP\", color: \"green\" };\n case \"disconnected\":\n return { label: \"Disconnected\", color: \"red\" };\n }\n}\n\nexport function StatusHeader(props: {\n role: \"host\" | \"client\";\n status: ConnectionStatus;\n hostIp?: string;\n tcpPort?: number;\n peerCount?: number;\n}) {\n const st = statusText(props.status);\n return (\n <Box>\n <Box>\n <Text color={st.color}>{st.label}</Text>\n {props.peerCount !== undefined && props.peerCount > 0 && (\n <Text color=\"cyan\"> ({props.peerCount} online)</Text>\n )}\n {props.role === \"host\" ? (\n <Text color=\"gray\">\n {props.tcpPort ? ` — TCP :${props.tcpPort}` : \"\"}\n </Text>\n ) : (\n <Text color=\"gray\">\n {props.hostIp && props.tcpPort\n ? ` — ${props.hostIp}:${props.tcpPort}`\n : \"\"}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type InfoRecord = {\n id: number;\n timestamp: number;\n type: \"bubble\" | \"countdown\" | \"projectile\" | \"join\" | \"leave\" | \"other\";\n content: string;\n};\n\nexport function InfoPanel(props: {\n records: InfoRecord[];\n maxRecords?: number;\n}) {\n const maxRecords = props.maxRecords ?? 8;\n const displayRecords = props.records.slice(-maxRecords);\n\n const formatTime = (ts: number) => {\n const d = new Date(ts);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n };\n\n const getTypeIcon = (type: InfoRecord[\"type\"]) => {\n switch (type) {\n case \"bubble\": return \"[Msg]\";\n case \"countdown\": return \"[Tmr]\";\n case \"projectile\": return \"[Thr]\";\n case \"join\": return \"[+]\";\n case \"leave\": return \"[-]\";\n default: return \"[*]\";\n }\n };\n\n const getTypeColor = (type: InfoRecord[\"type\"]) => {\n switch (type) {\n case \"bubble\": return \"cyan\";\n case \"countdown\": return \"green\";\n case \"projectile\": return \"magenta\";\n case \"join\": return \"green\";\n case \"leave\": return \"red\";\n default: return \"gray\";\n }\n };\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} paddingY={0}>\n <Box justifyContent=\"space-between\" marginBottom={0}>\n <Text color=\"yellow\">Info Log</Text>\n <Text color=\"gray\">{displayRecords.length} records</Text>\n </Box>\n\n <Box flexDirection=\"column\" minHeight={6}>\n {displayRecords.length === 0 ? (\n <Text color=\"gray\">No records yet...</Text>\n ) : (\n displayRecords.map((record) => (\n <Text key={record.id} wrap=\"truncate-end\">\n <Text color=\"gray\">{formatTime(record.timestamp)} </Text>\n <Text color={getTypeColor(record.type)}>{getTypeIcon(record.type)} </Text>\n <Text>{record.content}</Text>\n </Text>\n ))\n )}\n </Box>\n </Box>\n );\n}\n","export { AiConsole } from \"./AiConsole.js\";\nexport { BuddyAvatar } from \"./sprite/BuddyAvatar.js\";\nexport {\n CountdownClockSprite,\n countdownClockTypeFromMinutes,\n} from \"./sprite/CountdownClockSprite.js\";\nexport { ProjectileThrowSprite } from \"./sprite/ProjectileThrowSprite.js\";\nexport { StatusHeader } from \"./StatusHeader.js\";\nexport { InfoPanel } from \"./InfoPanel.js\";\nexport type { InfoRecord } from \"./InfoPanel.js\";\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { ActivityState } from \"../protocol.js\";\nimport {\n AiConsole,\n BuddyAvatar,\n CountdownClockSprite,\n ProjectileThrowSprite,\n StatusHeader,\n InfoPanel,\n} from \"../components/index.js\";\nimport type { InfoRecord } from \"../components/index.js\";\nimport type { CountdownClockType } from \"../components/sprite/CountdownClockSprite.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../components/sprite/ProjectileThrowSprite.js\";\nimport {\n useActivityMonitor,\n useBroadcaster,\n useTcpSync,\n} from \"../hooks/index.js\";\nimport { ensureGlobalKeyboard, subscribeGlobalKeydown } from \"../hooks/globalKeyboard.js\";\nimport type { LeaveStats } from \"../types.js\";\n\nfunction formatMMSS(totalSeconds: number) {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n}\n\nexport function Session(\n props:\n | { role: \"host\"; localName: string; onLeave: (stats: LeaveStats) => void }\n | {\n role: \"client\";\n localName: string;\n onLeave: (stats: LeaveStats) => void;\n hostIp: string;\n tcpPort: number;\n roomName: string;\n hostName: string;\n }\n) {\n const roomName = useMemo(\n () => `${props.localName}'s Room`,\n [props.localName]\n );\n\n const [showAi, setShowAi] = useState(false);\n const [countdown, setCountdown] = useState<{\n minutes: number;\n totalSeconds: number;\n endsAt: number;\n remainingSeconds: number;\n type: CountdownClockType;\n } | null>(null);\n const [shots, setShots] = useState<\n Array<{\n id: number;\n kind: ProjectileKind;\n direction: ProjectileDirection;\n }>\n >([]);\n // Bubbles for all 4 users: index 0 = local (No.1), 1-3 = peers (No.2-4)\n const [peerBubbles, setPeerBubbles] = useState<(string | null)[]>([null, null, null, null]);\n const bubbleTimersRef = useRef<(NodeJS.Timeout | null)[]>([null, null, null, null]);\n\n // Info records for logging events\n const [infoRecords, setInfoRecords] = useState<InfoRecord[]>([]);\n const nextRecordIdRef = useRef<number>(1);\n\n const addInfoRecord = useCallback((type: InfoRecord[\"type\"], content: string) => {\n const record: InfoRecord = {\n id: nextRecordIdRef.current++,\n timestamp: Date.now(),\n type,\n content,\n };\n setInfoRecords(prev => [...prev, record]);\n }, []);\n\n const tcpOptions = useMemo(() => {\n return props.role === \"host\"\n ? ({ role: \"host\", localName: props.localName } as const)\n : ({\n role: \"client\",\n localName: props.localName,\n hostIp: props.hostIp,\n tcpPort: props.tcpPort,\n hostName: props.hostName,\n } as const);\n }, [\n props.role,\n props.localName,\n props.role === \"client\" ? props.hostIp : \"\",\n props.role === \"client\" ? props.tcpPort : 0,\n props.role === \"client\" ? props.hostName : \"\",\n ]);\n\n const tcp = useTcpSync(tcpOptions);\n\n const broadcasterOptions = useMemo(() => {\n return props.role === \"host\"\n ? ({\n enabled: true,\n hostName: props.localName,\n roomName,\n tcpPort: tcp.listenPort,\n } as const)\n : ({ enabled: false } as const);\n }, [props.role, props.localName, roomName, tcp.listenPort]);\n\n useBroadcaster(broadcasterOptions);\n\n const localActivity = useActivityMonitor();\n\n // Use peers array from TCP sync for multi-peer support\n const peers = tcp.peers;\n const firstPeerName = peers.length > 0 ? peers[0].name : undefined;\n const prevPeersRef = useRef<typeof peers>([]);\n\n // Track peer joins and leaves\n useEffect(() => {\n const prevPeers = prevPeersRef.current;\n const prevNames = new Set(prevPeers.map(p => p.name));\n const currentNames = new Set(peers.map(p => p.name));\n\n // Check for new peers\n peers.forEach(p => {\n if (!prevNames.has(p.name)) {\n addInfoRecord(\"join\", `${p.name} joined`);\n }\n });\n\n // Check for left peers\n prevPeers.forEach(p => {\n if (!currentNames.has(p.name)) {\n addInfoRecord(\"leave\", `${p.name} left`);\n }\n });\n\n prevPeersRef.current = [...peers];\n }, [peers, addInfoRecord]);\n\n const onToggleAi = useCallback(() => setShowAi((v) => !v), []);\n const onCloseAi = useCallback(() => setShowAi(false), []);\n\n const sessionStartAtRef = useRef<number>(Date.now());\n const connectedStartAtRef = useRef<number | null>(null);\n const connectedTotalMsRef = useRef<number>(0);\n const keyPressesRef = useRef<number>(0);\n const useGlobalKeyboardRef = useRef<boolean>(false);\n\n const countKeyPress = useCallback(() => {\n keyPressesRef.current += 1;\n }, []);\n\n // Count terminal key presses unless global keyboard listener is active.\n useInput(\n () => {\n if (!useGlobalKeyboardRef.current) countKeyPress();\n },\n { isActive: true }\n );\n\n // If `TERMBUDDY_ACTIVITY_SOURCE=keyboard`, count global key presses.\n useEffect(() => {\n const raw = process.env.TERMBUDDY_ACTIVITY_SOURCE ?? \"ink\";\n const source = raw === \"xinput\" ? \"keyboard\" : raw;\n if (source !== \"keyboard\") return;\n\n let cancelled = false;\n let unsub: (() => void) | null = null;\n void (async () => {\n const ok = await ensureGlobalKeyboard();\n if (cancelled) return;\n if (!ok) return;\n useGlobalKeyboardRef.current = true;\n unsub = subscribeGlobalKeydown(countKeyPress);\n })();\n\n return () => {\n cancelled = true;\n unsub?.();\n useGlobalKeyboardRef.current = false;\n };\n }, [countKeyPress]);\n\n // Track connected time.\n useEffect(() => {\n if (tcp.status === \"connected\") {\n if (connectedStartAtRef.current === null) {\n connectedStartAtRef.current = Date.now();\n }\n return;\n }\n\n if (connectedStartAtRef.current !== null) {\n connectedTotalMsRef.current += Date.now() - connectedStartAtRef.current;\n connectedStartAtRef.current = null;\n }\n }, [tcp.status]);\n\n const finishAndLeave = useCallback(() => {\n const endedAt = Date.now();\n let connectedDurationMs = connectedTotalMsRef.current;\n if (connectedStartAtRef.current !== null) {\n connectedDurationMs += endedAt - connectedStartAtRef.current;\n }\n\n const stats: LeaveStats = {\n keyPresses: keyPressesRef.current,\n sessionDurationMs: endedAt - sessionStartAtRef.current,\n connectedDurationMs,\n startedAt: sessionStartAtRef.current,\n endedAt,\n peerName: firstPeerName,\n };\n props.onLeave(stats);\n }, [props, firstPeerName]);\n\n const startCountdown = useCallback((minutes: number) => {\n const totalSeconds = Math.max(1, Math.floor(minutes * 60));\n const endsAt = Date.now() + totalSeconds * 1000;\n const type: CountdownClockType =\n minutes <= 10 ? \"SHORT\" : minutes <= 30 ? \"MEDIUM\" : \"LONG\";\n setCountdown({\n minutes,\n totalSeconds,\n endsAt,\n remainingSeconds: totalSeconds,\n type,\n });\n addInfoRecord(\"countdown\", `Started ${minutes}min countdown`);\n }, [addInfoRecord]);\n\n const nextShotIdRef = useRef<number>(1);\n const shotQueueRef = useRef<\n Array<{ kind: ProjectileKind; direction: ProjectileDirection }>\n >([]);\n\n const pumpShotQueue = useCallback(() => {\n setShots((prev) => {\n if (prev.length >= 1) return prev;\n const next = shotQueueRef.current.shift();\n if (!next) return prev;\n const id = nextShotIdRef.current++;\n return [...prev, { id, kind: next.kind, direction: next.direction }];\n });\n }, []);\n\n useEffect(() => {\n if (shots.length === 0) pumpShotQueue();\n }, [shots.length, pumpShotQueue]);\n\n const throwProjectile = useCallback(\n (kind: ProjectileKind, direction: ProjectileDirection) => {\n shotQueueRef.current.push({ kind, direction });\n pumpShotQueue();\n if (tcp.status === \"connected\") tcp.sendProjectile(kind, direction);\n addInfoRecord(\"projectile\", `Threw ${kind}`);\n },\n [pumpShotQueue, tcp, addInfoRecord]\n );\n\n const showBubble = useCallback(\n (args: { text: string; target: number; durationMs: number }) => {\n const text = args.text.trim();\n if (!text) return;\n const durationMs = Math.max(300, Math.min(15_000, Math.floor(args.durationMs)));\n\n // target is 1-4 (No.1 to No.4), convert to 0-3 index\n const targetIndex = Math.max(0, Math.min(3, args.target - 1));\n\n setPeerBubbles(prev => {\n const next = [...prev];\n next[targetIndex] = text;\n return next;\n });\n\n const prevTimer = bubbleTimersRef.current[targetIndex];\n if (prevTimer) clearTimeout(prevTimer);\n\n const handle = setTimeout(() => {\n setPeerBubbles(prev => {\n const next = [...prev];\n next[targetIndex] = null;\n return next;\n });\n bubbleTimersRef.current[targetIndex] = null;\n }, durationMs);\n\n bubbleTimersRef.current[targetIndex] = handle;\n\n addInfoRecord(\"bubble\", `No.${args.target}: \"${text}\"`);\n },\n [addInfoRecord]\n );\n\n useInput(\n (input, key) => {\n if (input === \"q\") finishAndLeave();\n if (input === \"/\" && !key.ctrl && !key.meta) onToggleAi();\n },\n { isActive: !showAi }\n );\n\n useInput(\n (input) => {\n if (input === \"x\") setCountdown(null);\n },\n { isActive: !showAi && countdown !== null }\n );\n\n const localState = localActivity.state;\n const localLabel =\n props.role === \"host\"\n ? `${props.localName} (Host)`\n : `${props.localName} (Client)`;\n\n // Sync local activity state to peer.\n useEffect(() => {\n if (tcp.status !== \"connected\") return;\n tcp.sendStatus(localState);\n }, [localState, tcp.status, tcp.sendStatus]);\n\n // Handle incoming projectiles from peer (flip direction)\n useEffect(() => {\n const handleRemoteProjectile = (kind: ProjectileKind, direction: ProjectileDirection, _senderName?: string) => {\n const flippedDirection: ProjectileDirection =\n direction === \"LEFT_TO_RIGHT\" ? \"RIGHT_TO_LEFT\" : \"LEFT_TO_RIGHT\";\n shotQueueRef.current.push({ kind, direction: flippedDirection });\n pumpShotQueue();\n };\n\n tcp.setOnRemoteProjectile(handleRemoteProjectile);\n }, [tcp, pumpShotQueue]);\n\n useEffect(() => {\n if (!countdown) return;\n const endsAt = countdown.endsAt;\n const handle = setInterval(() => {\n const remaining = Math.max(0, Math.ceil((endsAt - Date.now()) / 1000));\n setCountdown((prev) => {\n if (!prev) return prev;\n if (prev.endsAt !== endsAt) return prev;\n if (remaining <= 0) return null;\n if (prev.remainingSeconds === remaining) return prev;\n return { ...prev, remainingSeconds: remaining };\n });\n }, 250);\n return () => clearInterval(handle);\n }, [countdown?.endsAt]);\n\n useEffect(() => {\n return () => {\n bubbleTimersRef.current.forEach(timer => {\n if (timer) clearTimeout(timer);\n });\n };\n }, []);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <StatusHeader\n role={props.role}\n status={tcp.status}\n hostIp={props.role === \"client\" ? props.hostIp : undefined}\n tcpPort={props.role === \"client\" ? props.tcpPort : tcp.listenPort}\n peerCount={peers.length}\n />\n\n {/* Main Stage: 2x2 Grid Layout for 4 Users */}\n <Box flexDirection=\"column\" marginTop={1}>\n {/* Projectile Area at Top */}\n <Box flexDirection=\"column\" width=\"100%\" alignItems=\"center\" marginBottom={1}>\n {shots.map((s) => (\n <ProjectileThrowSprite\n key={String(s.id)}\n kind={s.kind}\n direction={s.direction}\n shotId={s.id}\n width={50}\n onDone={() =>\n setShots((prev) => prev.filter((x) => x.id !== s.id))\n }\n />\n ))}\n {shots.length === 0 ? <Box height={1} /> : null}\n </Box>\n\n {/* Countdown Timer (shared) */}\n {countdown ? (\n <Box justifyContent=\"center\" marginBottom={1}>\n <Box flexDirection=\"column\" alignItems=\"center\">\n <Text color=\"gray\">{formatMMSS(countdown.remainingSeconds)}</Text>\n <CountdownClockSprite\n variant=\"COMPACT\"\n type={countdown.type}\n minutes={countdown.minutes}\n totalSeconds={countdown.totalSeconds}\n remainingSeconds={countdown.remainingSeconds}\n showLabel={false}\n />\n </Box>\n </Box>\n ) : null}\n\n {/* 2x2 Grid of Users */}\n <Box flexDirection=\"column\" alignItems=\"center\">\n {/* Row 1: User 1 (Local) and User 2 */}\n <Box flexDirection=\"row\" justifyContent=\"center\" gap={4}>\n {/* No.1 - Local User */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n <Text color=\"cyan\" bold>No.1 {props.localName}</Text>\n <BuddyAvatar state={localState} marginTop={0} bubbleText={peerBubbles[0] ?? null} />\n </Box>\n\n {/* No.2 - First Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 1 ? (\n <>\n <Text color=\"magenta\" bold>No.2 {peers[0].name}</Text>\n <BuddyAvatar state={peers[0].state} marginTop={0} bubbleText={peerBubbles[1] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.2 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n </Box>\n\n {/* Row 2: User 3 and User 4 */}\n <Box flexDirection=\"row\" justifyContent=\"center\" gap={4} marginTop={1}>\n {/* No.3 - Second Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 2 ? (\n <>\n <Text color=\"magenta\" bold>No.3 {peers[1].name}</Text>\n <BuddyAvatar state={peers[1].state} marginTop={0} bubbleText={peerBubbles[2] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.3 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n\n {/* No.4 - Third Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 3 ? (\n <>\n <Text color=\"magenta\" bold>No.4 {peers[2].name}</Text>\n <BuddyAvatar state={peers[2].state} marginTop={0} bubbleText={peerBubbles[3] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.4 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n </Box>\n </Box>\n </Box>\n\n {/* Footer Instructions (Hide when AI is open to save space) */}\n {!showAi ? (\n <Box marginTop={1} justifyContent=\"center\">\n <Text color=\"gray\">\n 按 <Text color=\"cyan\">/</Text> 召唤 AI Console,按{\" \"}\n <Text color=\"cyan\">q</Text> 结束本次陪伴。\n {countdown ? (\n <>\n {\" \"}\n <Text color=\"gray\">\n (倒计时中:按 <Text color=\"cyan\">x</Text> 取消)\n </Text>\n </>\n ) : null}\n </Text>\n </Box>\n ) : null}\n\n {/* AI Console and Info Panel (side by side) */}\n {showAi ? (\n <Box\n marginTop={1}\n width=\"100%\"\n flexDirection=\"row\"\n justifyContent=\"center\"\n gap={2}\n >\n <Box width={48}>\n <AiConsole\n onClose={onCloseAi}\n onStartCountdown={startCountdown}\n onShowBubble={showBubble}\n onThrowProjectile={throwProjectile}\n localName={props.localName}\n peerName={\n firstPeerName ??\n (props.role === \"client\" ? props.hostName : undefined) ??\n \"Buddy\"\n }\n peers={peers}\n />\n </Box>\n <Box width={32}>\n <InfoPanel records={infoRecords} maxRecords={6} />\n </Box>\n </Box>\n ) : null}\n </Box>\n );\n}\n","export { MainMenu } from \"./MainMenu.js\";\nexport { NicknamePrompt } from \"./NicknamePrompt.js\";\nexport { RoomScanner } from \"./RoomScanner.js\";\nexport { LeavePage } from \"./LeavePage.js\";\nexport { Session } from \"./Session.js\";\n","import React, { useCallback, useMemo, useState } from \"react\";\nimport os from \"node:os\";\nimport { useApp } from \"ink\";\nimport {\n LeavePage,\n MainMenu,\n NicknamePrompt,\n RoomScanner,\n Session,\n} from \"../page/index.js\";\nimport type { LeaveStats } from \"../types.js\";\n\ntype View =\n | { name: \"NICKNAME\" }\n | { name: \"MENU\" }\n | { name: \"SCANNING\" }\n | { name: \"LEAVE\"; stats: LeaveStats }\n | { name: \"SESSION\"; role: \"host\" }\n | {\n name: \"SESSION\";\n role: \"client\";\n hostIp: string;\n tcpPort: number;\n roomName: string;\n hostName: string;\n };\n\nexport function App() {\n const { exit } = useApp();\n const [view, setView] = useState<View>({ name: \"NICKNAME\" });\n const [nickname, setNickname] = useState<string | null>(null);\n\n const localName = useMemo(() => nickname ?? os.hostname(), [nickname]);\n\n const goMenu = useCallback(() => setView({ name: \"MENU\" }), []);\n\n if (view.name === \"NICKNAME\") {\n return (\n <NicknamePrompt\n onExit={() => exit()}\n onSubmit={(name) => {\n setNickname(name);\n setView({ name: \"MENU\" });\n }}\n />\n );\n }\n\n if (view.name === \"MENU\") {\n return (\n <MainMenu\n onHost={() => setView({ name: \"SESSION\", role: \"host\" })}\n onJoin={() => setView({ name: \"SCANNING\" })}\n onExit={() => exit()}\n />\n );\n }\n\n if (view.name === \"LEAVE\") {\n return (\n <LeavePage stats={view.stats} onBack={goMenu} onExit={() => exit()} />\n );\n }\n\n if (view.name === \"SCANNING\") {\n return (\n <RoomScanner\n onBack={goMenu}\n onExit={() => exit()}\n onSelectRoom={(room) =>\n setView({\n name: \"SESSION\",\n role: \"client\",\n hostIp: room.ip,\n tcpPort: room.tcpPort,\n roomName: room.roomName,\n hostName: room.hostName,\n })\n }\n />\n );\n }\n\n if (view.name === \"SESSION\" && view.role === \"host\") {\n return (\n <Session\n localName={localName}\n role=\"host\"\n onLeave={(stats) => setView({ name: \"LEAVE\", stats })}\n />\n );\n }\n\n return (\n <Session\n localName={localName}\n role=\"client\"\n onLeave={(stats) => setView({ name: \"LEAVE\", stats })}\n hostIp={view.hostIp}\n tcpPort={view.tcpPort}\n roomName={view.roomName}\n hostName={view.hostName}\n />\n );\n}\n","export { App } from \"./App.js\";\n","process.env.NODE_ENV ??= 'production';\n\nconst React = await import('react');\nconst {render} = await import('ink');\nconst {App} = await import('./app/index.js');\n\nrender(React.createElement(App));\n"],"mappings":"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","names":["Box","Text","useInput","jsx","jsxs","useState","useInput","tool","tool","tool","useCallback","useEffect","useRef","useState","os","useEffect","useEffect","useState","dgram","useCallback","useEffect","useRef","useState","socket","useMemo","Box","Text","useInput","jsx","jsxs","useMemo","Box","Text","useInput","jsx","jsxs","useEffect","useMemo","useState","Box","Text","useInput","jsx","jsxs","useMemo","Box","Text","jsx","Box","Text","jsx","jsxs","useMemo","Box","Text","jsx","jsxs","face","useEffect","useMemo","useState","Box","Text","jsx","jsxs","clamp01","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","Box","Text","useInput","Fragment","jsx","jsxs","useCallback","useMemo","useState","os","jsx","React","App"]}
1
+ {"version":3,"sources":["../src/page/MainMenu.tsx","../src/page/NicknamePrompt.tsx","../src/hooks/globalKeyboard.ts","../src/hooks/useActivityMonitor.ts","../src/components/tool/createCountdownTool.ts","../src/components/tool/createBubbleTool.ts","../src/components/tool/createInteractionTool.ts","../src/components/tool/createSessionInfoTool.ts","../src/components/tool/index.ts","../src/hooks/useAiAgent.ts","../src/constants.ts","../src/net/broadcast.ts","../src/net/index.ts","../src/hooks/useBroadcaster.ts","../src/hooks/useScanner.ts","../src/hooks/useTcpSync.ts","../src/hooks/index.ts","../src/page/RoomScanner.tsx","../src/page/LeavePage.tsx","../src/storage/apiKey.ts","../src/components/AiConsole.tsx","../src/components/sprite/BubbleSprite.tsx","../src/components/sprite/BuddyAvatar.tsx","../src/components/sprite/CountdownClockSprite.tsx","../src/components/sprite/ProjectileThrowSprite.tsx","../src/components/StatusHeader.tsx","../src/components/InfoPanel.tsx","../src/components/index.ts","../src/page/Session.tsx","../src/page/index.ts","../src/app/App.tsx","../src/app/index.ts","../src/cli.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\n\nexport function MainMenu(props: {\n onHost: () => void;\n onJoin: () => void;\n onExit: () => void;\n}) {\n useInput((input, key) => {\n if (key.escape || input === \"q\") props.onExit();\n if (input === \"1\") props.onHost();\n if (input === \"2\") props.onJoin();\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text>\n {String.raw`\n████████╗███████╗██████╗ ███╗ ███╗██████╗ ██╗ ██╗██████╗ ██████╗ ██╗ ██╗\n╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔══██╗██║ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝\n ██║ █████╗ ██████╔╝██╔████╔██║██████╔╝██║ ██║██║ ██║██║ ██║ ╚████╔╝ \n ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══██╗██║ ██║██║ ██║██║ ██║ ╚██╔╝ \n ██║ ███████╗██║ ██║██║ ╚═╝ ██║██████╔╝╚██████╔╝██████╔╝██████╔╝ ██║ \n ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ \n`}\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n <Text>Terminal Body Doubling — 极简 / 极客 / 私密</Text>\n <Text> </Text>\n <Text>\n <Text color=\"cyan\">[1]</Text> 建房 (Host)\n </Text>\n <Text>\n <Text color=\"cyan\">[2]</Text> 加入 (Join)\n </Text>\n <Text>\n <Text color=\"cyan\">[q]</Text> 退出\n </Text>\n </Box>\n </Box>\n );\n}\n","import React, { useMemo, useState } from \"react\";\nimport os from \"node:os\";\nimport { Box, Text, useInput } from \"ink\";\n\nfunction defaultNick(): string {\n try {\n return os.userInfo().username || os.hostname();\n } catch {\n return os.hostname();\n }\n}\n\nexport function NicknamePrompt(props: {\n onSubmit: (nickname: string) => void;\n onExit: () => void;\n}) {\n const initial = useMemo(() => defaultNick(), []);\n const [nickname, setNickname] = useState(initial);\n const [touched, setTouched] = useState(false);\n\n useInput((input, key) => {\n if (key.escape) props.onExit();\n\n if (key.return) {\n const name = nickname.trim();\n if (!name) return;\n props.onSubmit(name);\n return;\n }\n\n if (key.backspace || key.delete) {\n setTouched(true);\n setNickname((v) => v.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (!input) return;\n if (input === \"\\t\") return;\n\n setTouched(true);\n setNickname((v) => v + input);\n });\n\n const hint = touched ? \"\" : \" (回车确认,可直接用默认值)\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"gray\">欢迎来到 TermBuddy</Text>\n <Box marginTop={1}>\n <Text>\n 请输入你的昵称:<Text color=\"cyan\">{nickname || \"\"}</Text>\n <Text color=\"gray\">{hint}</Text>\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text color=\"gray\">按 Esc 退出。</Text>\n </Box>\n </Box>\n );\n}\n\n","import { spawn } from \"node:child_process\";\n\nexport type GlobalKeyboardBackend = \"uiohook\" | \"xinput\";\n\ntype Listener = () => void;\n\nlet backend: GlobalKeyboardBackend | null = null;\nlet started = false;\nlet starting: Promise<GlobalKeyboardBackend | null> | null = null;\nlet stopBackend: (() => void) | null = null;\n\nconst listeners = new Set<Listener>();\n\nfunction emitKeydown() {\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // ignore\n }\n }\n}\n\nasync function tryStartUiohook(): Promise<GlobalKeyboardBackend | null> {\n try {\n const mod = (await import(\"uiohook-napi\")) as unknown as {\n uIOhook?: {\n on: (event: \"keydown\", listener: () => void) => unknown;\n removeListener?: (event: \"keydown\", listener: () => void) => unknown;\n start: () => void;\n stop: () => void;\n };\n default?: { uIOhook?: unknown };\n };\n\n const uIOhook =\n mod.uIOhook ??\n ((mod.default as { uIOhook?: unknown } | undefined)?.uIOhook as\n | {\n on: (event: \"keydown\", listener: () => void) => unknown;\n removeListener?: (event: \"keydown\", listener: () => void) => unknown;\n start: () => void;\n stop: () => void;\n }\n | undefined);\n if (!uIOhook) return null;\n\n const onKeydown = () => emitKeydown();\n uIOhook.on(\"keydown\", onKeydown);\n uIOhook.start();\n\n stopBackend = () => {\n uIOhook.removeListener?.(\"keydown\", onKeydown);\n uIOhook.stop();\n };\n\n return \"uiohook\";\n } catch {\n return null;\n }\n}\n\nfunction tryStartXinput(): Promise<GlobalKeyboardBackend | null> {\n if (process.platform !== \"linux\") return Promise.resolve(null);\n if (!process.env.DISPLAY) return Promise.resolve(null);\n\n return new Promise((resolve) => {\n let resolved = false;\n const child = spawn(\"xinput\", [\"test-xi2\", \"--root\"], {\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n const resolveOnce = (value: GlobalKeyboardBackend | null) => {\n if (resolved) return;\n resolved = true;\n resolve(value);\n };\n\n child.once(\"error\", () => {\n resolveOnce(null);\n });\n\n // If we got here, treat it as started; parsing may continue.\n resolveOnce(\"xinput\");\n\n let buf = \"\";\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx);\n buf = buf.slice(idx + 1);\n if (/KeyPress/.test(line)) emitKeydown();\n }\n });\n\n stopBackend = () => {\n child.stdout?.removeAllListeners();\n child.removeAllListeners();\n child.kill();\n };\n });\n}\n\nexport async function ensureGlobalKeyboard(): Promise<GlobalKeyboardBackend | null> {\n if (started) return backend;\n if (starting) return starting;\n\n starting = (async () => {\n const uiohook = await tryStartUiohook();\n if (uiohook) return uiohook;\n return await tryStartXinput();\n })();\n\n backend = await starting;\n started = backend !== null;\n if (!started) stopBackend = null;\n starting = null;\n\n return backend;\n}\n\nfunction stopIfIdle() {\n if (listeners.size > 0) return;\n if (!started) return;\n started = false;\n backend = null;\n const stop = stopBackend;\n stopBackend = null;\n try {\n stop?.();\n } catch {\n // ignore\n }\n}\n\nexport function subscribeGlobalKeydown(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n stopIfIdle();\n };\n}\n\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useInput } from \"ink\";\nimport type { ActivityState } from \"../protocol.js\";\nimport {\n ensureGlobalKeyboard,\n subscribeGlobalKeydown,\n} from \"./globalKeyboard.js\";\n\nexport function useActivityMonitor(options?: {\n idleAfterMs?: number;\n source?: \"ink\" | \"keyboard\";\n}): {\n state: ActivityState;\n} {\n const idleAfterMs = options?.idleAfterMs ?? 1500;\n const [state, setState] = useState<ActivityState>(\"IDLE\");\n\n const lastActivityRef = useRef<number>(Date.now());\n\n const markActive = useCallback(() => {\n lastActivityRef.current = Date.now();\n setState(\"TYPING\");\n }, []);\n\n useInput(() => {\n markActive();\n });\n\n // Optional global keyboard activity.\n useEffect(() => {\n const rawSource =\n options?.source ?? process.env.TERMBUDDY_ACTIVITY_SOURCE ?? \"ink\";\n\n // Back-compat: previous env value.\n const source = rawSource === \"xinput\" ? \"keyboard\" : rawSource;\n if (source !== \"keyboard\") return;\n\n let cancelled = false;\n let unsub: (() => void) | null = null;\n void (async () => {\n const ok = await ensureGlobalKeyboard();\n if (cancelled) return;\n if (!ok) return;\n unsub = subscribeGlobalKeydown(markActive);\n })();\n\n return () => {\n cancelled = true;\n unsub?.();\n };\n }, [markActive, options?.source]);\n\n useEffect(() => {\n const id = setInterval(() => {\n const delta = Date.now() - lastActivityRef.current;\n if (delta >= idleAfterMs) setState(\"IDLE\");\n }, 200);\n return () => clearInterval(id);\n }, [idleAfterMs]);\n\n return { state };\n}\n","import { tool } from \"langchain\";\n\nexport function createCountdownTool(options: {\n onStartCountdown?: (minutes: number) => void;\n}) {\n return tool(\n async (input: { minutes: number }) => {\n const minutes = Number(input.minutes);\n if (!Number.isFinite(minutes) || minutes <= 0) return \"倒计时分钟数无效。\";\n options.onStartCountdown?.(minutes);\n return `已开始倒计时 ${minutes} 分钟。`;\n },\n {\n name: \"start_countdown\",\n description: \"开始一个专注倒计时(分钟)。\",\n schema: {\n type: \"object\",\n properties: {\n minutes: {\n type: \"integer\",\n minimum: 1,\n maximum: 180,\n description: \"倒计时分钟数\",\n },\n },\n required: [\"minutes\"],\n additionalProperties: false,\n },\n }\n );\n}\n\n","import { tool } from \"langchain\";\n\n// Target can be 1-4 (No.1 to No.4)\nexport type BubbleTarget = number;\n\nexport function createBubbleTool(options: {\n onShowBubble?: (args: {\n text: string;\n target: BubbleTarget;\n durationMs: number;\n }) => void;\n}) {\n return tool(\n async (input: { text?: string; target?: number | string; durationMs?: number }) => {\n const text = String(input.text ?? \"\").trim();\n if (!text) return \"气泡内容为空。\";\n\n // Parse target: can be number 1-4, or \"local\"/\"buddy\" for backward compatibility\n let target: number = 1; // default to No.1 (local)\n if (typeof input.target === \"number\") {\n target = Math.max(1, Math.min(4, Math.floor(input.target)));\n } else if (typeof input.target === \"string\") {\n const t = input.target.toLowerCase();\n if (t === \"local\" || t === \"1\") {\n target = 1;\n } else if (t === \"buddy\" || t === \"2\") {\n target = 2;\n } else if (t === \"3\") {\n target = 3;\n } else if (t === \"4\") {\n target = 4;\n } else {\n // Try to parse as number\n const parsed = parseInt(t, 10);\n if (!isNaN(parsed) && parsed >= 1 && parsed <= 4) {\n target = parsed;\n }\n }\n }\n\n const durationRaw = Number(input.durationMs ?? 2500);\n const durationMs =\n Number.isFinite(durationRaw) && durationRaw > 0\n ? Math.min(15_000, Math.max(300, Math.floor(durationRaw)))\n : 2500;\n\n options.onShowBubble?.({ text, target, durationMs });\n return `已显示气泡给 No.${target}:${text}`;\n },\n {\n name: \"show_bubble\",\n description: \"在指定用户的小猫头像上显示一个气泡(短消息提示)。\",\n schema: {\n type: \"object\",\n properties: {\n text: {\n type: \"string\",\n minLength: 1,\n maxLength: 120,\n description: \"气泡里的文字\",\n },\n target: {\n type: \"integer\",\n minimum: 1,\n maximum: 4,\n description: \"显示在哪个用户的小猫上(1=No.1/本地用户,2=No.2,3=No.3,4=No.4)\",\n },\n durationMs: {\n type: \"integer\",\n minimum: 300,\n maximum: 15000,\n description: \"显示时长(毫秒,可选)\",\n },\n },\n required: [\"text\"],\n additionalProperties: false,\n },\n }\n );\n}\n\n","import { tool } from \"langchain\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../sprite/ProjectileThrowSprite.js\";\n\nconst KIND_ALIASES: Array<{ kind: ProjectileKind; keys: string[] }> = [\n { kind: \"ROSE\", keys: [\"rose\", \"花\", \"玫瑰\", \"🌹\", \"love\"] },\n { kind: \"POOP\", keys: [\"poop\", \"屎\", \"💩\", \"大便\"] },\n { kind: \"HAMMER\", keys: [\"hammer\", \"锤\", \"🔨\", \"敲\", \"打\"] },\n];\n\nfunction normalizeKind(raw: unknown): ProjectileKind | null {\n if (typeof raw !== \"string\") return null;\n const upper = raw.toUpperCase().trim();\n if (upper === \"ROSE\" || upper === \"POOP\" || upper === \"HAMMER\") return upper;\n\n const lower = raw.toLowerCase();\n for (const item of KIND_ALIASES) {\n if (item.keys.some((k) => lower.includes(k))) return item.kind;\n }\n return null;\n}\n\nfunction normalizeDirection(raw: unknown): ProjectileDirection | null {\n if (typeof raw !== \"string\") return null;\n const upper = raw.toUpperCase().trim();\n if (upper === \"LEFT_TO_RIGHT\" || upper === \"RIGHT_TO_LEFT\") return upper;\n return null;\n}\n\nexport function createInteractionTool(options: {\n onThrow?: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n}) {\n return tool(\n async (input: { kind?: string; direction?: string; message?: string }) => {\n const kind = normalizeKind(input.kind ?? \"\") ?? \"ROSE\";\n const direction = normalizeDirection(input.direction) ?? \"LEFT_TO_RIGHT\";\n options.onThrow?.(kind, direction);\n const msg = (input.message ?? \"\").trim();\n return msg ? `已投掷 ${kind}:${msg}` : `已投掷 ${kind}。`;\n },\n {\n name: \"throw_projectile\",\n description: \"和同桌互动:投掷一个小物品(🌹/💩/🔨)。\",\n schema: {\n type: \"object\",\n properties: {\n kind: {\n type: \"string\",\n description:\n \"投掷物类型(ROSE/POOP/HAMMER,或任意描述如“玫瑰/锤子/💩”)\",\n },\n direction: {\n type: \"string\",\n enum: [\"LEFT_TO_RIGHT\", \"RIGHT_TO_LEFT\"],\n description: \"飞行方向\",\n },\n message: { type: \"string\", description: \"附带一句话(可选)\" },\n },\n required: [],\n additionalProperties: false,\n },\n }\n );\n}\n","import { tool } from \"langchain\";\n\nexport function createSessionInfoTool(options: {\n localName: string;\n peerName: string;\n}) {\n return tool(\n async () => {\n return JSON.stringify(\n {\n localName: options.localName,\n peerName: options.peerName,\n },\n null,\n 2\n );\n },\n {\n name: \"session_info\",\n description: \"获取当前会话上下文(本地昵称、同桌昵称)。\",\n schema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n }\n );\n}\n\n","export { createCountdownTool } from \"./createCountdownTool.js\";\nexport { createBubbleTool } from \"./createBubbleTool.js\";\nexport { createInteractionTool } from \"./createInteractionTool.js\";\nexport { createSessionInfoTool } from \"./createSessionInfoTool.js\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createAgent } from \"langchain\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport {\n createBubbleTool,\n createCountdownTool,\n createInteractionTool,\n createSessionInfoTool,\n} from \"../components/tool/index.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../components/sprite/ProjectileThrowSprite.js\";\ntype LineKind = \"user\" | \"ai\" | \"system\";\nexport type AiLine = { kind: LineKind; text: string; at: number };\n\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!content) return \"\";\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === \"string\") return part;\n if (typeof part === \"object\" && part && \"text\" in part)\n return String((part as any).text ?? \"\");\n return \"\";\n })\n .join(\"\");\n }\n if (typeof content === \"object\" && \"text\" in (content as any))\n return String((content as any).text ?? \"\");\n return String(content);\n}\n\nfunction lastAiText(messages: unknown[]): string | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m: any = messages[i];\n const type =\n typeof m?.getType === \"function\"\n ? m.getType()\n : typeof m?._getType === \"function\"\n ? m._getType()\n : m?.type;\n if (type === \"ai\") {\n const t = contentToText(m?.content);\n return t || \"\";\n }\n }\n return null;\n}\n\nfunction createSystemPrompt(context: { localName: string; peerName: string; peers?: Array<{ id: string; name: string }> }) {\n const peerList = context.peers ?? [];\n const userList = [\n `No.1: ${context.localName} (我/本地用户)`,\n peerList[0] ? `No.2: ${peerList[0].name}` : \"No.2: (空位)\",\n peerList[1] ? `No.3: ${peerList[1].name}` : \"No.3: (空位)\",\n peerList[2] ? `No.4: ${peerList[2].name}` : \"No.4: (空位)\",\n ].join(\"、\");\n\n return [\n `你是 TermBuddy 里的「壳中幽灵 (Ghost in the Shell)」。`,\n `默认隐形;被 / 唤醒时出现。风格:极简、干练、少废话。`,\n `你可以使用工具来操控应用功能(例如倒计时)。`,\n `如果用户提到「倒计时/专注/计时/countdown」,优先调用 start_countdown。`,\n `如果用户提到「气泡/泡泡/提示/说一句/和某人说」,优先调用 show_bubble,并根据目标用户设置 target 参数(1-4)。`,\n `如果用户提到「互动/扔/投掷/throw」,优先调用 throw_projectile。`,\n `当用户明确要求投掷 N 次且 1<=N<=100 时,必须按 N 执行:重复调用 throw_projectile 共 N 次,不要改成「示意/少量几次」。超过 100 则分批多次调用。`,\n `当前房间有4个位置,用户列表:${userList}。`,\n `我是 ${context.localName}(No.1)。`,\n ].join(\"\\n\");\n}\n\nexport function useAiAgent(options: {\n localName: string;\n peerName: string;\n peers?: Array<{ id: string; name: string }>;\n onStartCountdown?: (minutes: number) => void;\n onShowBubble?: (args: {\n text: string;\n target: number; // 1-4 for No.1 to No.4\n durationMs: number;\n }) => void;\n onThrowProjectile?: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n apiKey?: string;\n}) {\n const [lines, setLines] = useState<AiLine[]>([]);\n const [busy, setBusy] = useState(false);\n const [apiKeyError, setApiKeyError] = useState(false);\n\n const agentRef = useRef<Awaited<ReturnType<typeof createAgent>> | null>(null);\n const agentInitRef = useRef<Promise<\n Awaited<ReturnType<typeof createAgent>>\n > | null>(null);\n const agentKeyRef = useRef<string | null>(null);\n const stateRef = useRef<{ messages: unknown[] }>({ messages: [] });\n const abortRef = useRef<AbortController | null>(null);\n\n const append = useCallback((line: AiLine) => {\n setLines((prev) => [...prev, line]);\n }, []);\n\n const updateLine = useCallback((at: number, text: string) => {\n setLines((prev) => {\n const idx = prev.findIndex((l) => l.at === at);\n if (idx === -1) return prev;\n const next = [...prev];\n next[idx] = { ...next[idx], text };\n return next;\n });\n }, []);\n\n const ensureAgent = useCallback(async () => {\n const apiKey = (options.apiKey ?? \"\").trim();\n if (!apiKey) throw new Error(\"missing_api_key\");\n\n if (agentRef.current && agentKeyRef.current === apiKey)\n return agentRef.current;\n\n agentRef.current = null;\n agentInitRef.current = null;\n agentKeyRef.current = apiKey;\n stateRef.current.messages = [];\n\n agentInitRef.current ??= (async () => {\n const startCountdown = createCountdownTool({\n onStartCountdown: options.onStartCountdown,\n });\n\n const showBubble = createBubbleTool({\n onShowBubble: options.onShowBubble,\n });\n\n const sessionInfo = createSessionInfoTool({\n localName: options.localName,\n peerName: options.peerName,\n });\n\n const interaction = createInteractionTool({\n onThrow: options.onThrowProjectile,\n });\n\n const llm = new ChatOpenAI({\n model: \"deepseek-chat\",\n configuration: {\n baseURL: \"https://api.deepseek.com\",\n },\n apiKey: apiKey,\n temperature: 0.1,\n maxTokens: 1000,\n timeout: 30000,\n });\n return createAgent({\n model: llm,\n tools: [startCountdown, showBubble, interaction, sessionInfo],\n systemPrompt: createSystemPrompt({\n localName: options.localName,\n peerName: options.peerName,\n peers: options.peers,\n }),\n name: \"ghost\",\n });\n })();\n\n agentRef.current = await agentInitRef.current;\n return agentRef.current;\n }, [\n options.apiKey,\n options.localName,\n options.onStartCountdown,\n options.onShowBubble,\n options.onThrowProjectile,\n options.peerName,\n options.peers,\n ]);\n\n const ask = useCallback(\n async (text: string) => {\n const userAt = Date.now();\n const aiAt = userAt + 1;\n const toolAt = userAt + 2;\n setLines([\n { kind: \"user\", text: `> ${text}`, at: userAt },\n { kind: \"ai\", text: \"…\", at: aiAt },\n { kind: \"system\", text: \"\", at: toolAt },\n ]);\n\n abortRef.current?.abort();\n abortRef.current = new AbortController();\n\n setBusy(true);\n try {\n const agent = await ensureAgent();\n const stream = await agent.stream(\n {\n messages: [{ role: \"user\", content: text }],\n },\n {\n streamMode: \"values\",\n signal: abortRef.current.signal,\n } as any\n );\n\n for await (const chunk of stream as any) {\n const messages = (chunk?.messages ?? []) as unknown[];\n if (messages.length > 0) stateRef.current.messages = messages;\n\n const latest: any = messages.at(-1);\n if (latest?.tool_calls?.length) {\n const names = latest.tool_calls\n .map((tc: any) => tc?.name)\n .filter(Boolean)\n .join(\", \");\n if (names) updateLine(toolAt, `Calling tools: ${names}`);\n continue;\n }\n\n const t = lastAiText(messages);\n if (t !== null) updateLine(aiAt, t);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n const isApiKeyError =\n msg === \"missing_api_key\" ||\n msg.includes(\"401\") ||\n msg.includes(\"403\") ||\n msg.includes(\"Unauthorized\") ||\n msg.includes(\"Invalid API\");\n\n if (isApiKeyError) {\n setApiKeyError(true);\n updateLine(aiAt, \"API Key 错误或失效,请重新输入(按 R 键)\");\n } else {\n updateLine(aiAt, `(AI 出错)${msg}`);\n }\n } finally {\n setBusy(false);\n }\n },\n [append, ensureAgent, updateLine]\n );\n\n const resetApiKeyError = useCallback(() => {\n setApiKeyError(false);\n }, []);\n\n useEffect(() => {\n return () => abortRef.current?.abort();\n }, []);\n\n return { lines, ask, busy, apiKeyError, resetApiKeyError };\n}\n","export const UDP_PORT = 45888;\nexport const TCP_DEFAULT_PORT = 45999;\n\nexport const DISCOVERY_VERSION = 1;\nexport const APP_NAME = 'TermBuddy';\n","import os from \"node:os\";\n\nfunction ipv4ToInt(ip: string) {\n return ip\n .split(\".\")\n .map((n) => Number.parseInt(n, 10))\n .reduce((acc, n) => ((acc << 8) | (n & 255)) >>> 0, 0);\n}\n\nfunction intToIpv4(n: number) {\n return [24, 16, 8, 0].map((shift) => String((n >>> shift) & 255)).join(\".\");\n}\n\nexport function getBroadcastTargets(): string[] {\n const out = new Set<string>([\"255.255.255.255\"]);\n\n const ifaces = os.networkInterfaces();\n for (const entries of Object.values(ifaces)) {\n if (!entries) continue;\n for (const e of entries) {\n if (e.family !== \"IPv4\") continue;\n if (e.internal) continue;\n if (!e.address || !e.netmask) continue;\n const ip = ipv4ToInt(e.address);\n const mask = ipv4ToInt(e.netmask);\n const broadcast = (ip | (~mask >>> 0)) >>> 0;\n out.add(intToIpv4(broadcast));\n }\n }\n\n return [...out];\n}\n","export { getBroadcastTargets } from \"./broadcast.js\";\n","import { useEffect } from \"react\";\nimport dgram from \"node:dgram\";\nimport { UDP_PORT, DISCOVERY_VERSION } from \"../constants.js\";\nimport type { DiscoveryPacket } from \"../protocol.js\";\nimport { getBroadcastTargets } from \"../net/index.js\";\n\ntype Options =\n | { enabled: false }\n | {\n enabled: true;\n hostName: string;\n roomName: string;\n tcpPort?: number | null;\n intervalMs?: number;\n };\n\nexport function useBroadcaster(options: Options) {\n const depKey = options.enabled\n ? `${options.hostName}|${options.roomName}|${options.tcpPort ?? \"\"}|${\n options.intervalMs ?? 1000\n }`\n : \"disabled\";\n\n useEffect(() => {\n if (!options.enabled) return;\n if (!options.tcpPort) return;\n\n const socket = dgram.createSocket(\"udp4\");\n socket.on(\"error\", () => {});\n\n socket.bind(() => {\n socket.setBroadcast(true);\n });\n\n const targets = getBroadcastTargets();\n\n const send = () => {\n const packet: DiscoveryPacket = {\n type: \"termbuddy_discovery\",\n version: DISCOVERY_VERSION,\n hostName: options.hostName,\n roomName: options.roomName,\n tcpPort: options.tcpPort!,\n sentAt: Date.now(),\n };\n const msg = Buffer.from(JSON.stringify(packet));\n for (const address of targets) {\n socket.send(msg, UDP_PORT, address);\n }\n };\n\n send();\n const id = setInterval(send, options.intervalMs ?? 1000);\n\n return () => {\n clearInterval(id);\n socket.close();\n };\n }, [depKey]);\n}\n","import { useEffect, useState } from \"react\";\nimport dgram from \"node:dgram\";\nimport { UDP_PORT, DISCOVERY_VERSION } from \"../constants.js\";\nimport type { DiscoveryPacket } from \"../protocol.js\";\nimport type { DiscoveredRoom } from \"../types.js\";\n\nfunction safeParse(msg: Buffer): DiscoveryPacket | null {\n try {\n const parsed = JSON.parse(msg.toString(\"utf8\")) as DiscoveryPacket;\n if (parsed?.type !== \"termbuddy_discovery\") return null;\n if (parsed?.version !== DISCOVERY_VERSION) return null;\n if (!parsed.hostName || !parsed.roomName || !parsed.tcpPort) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function useScanner(options?: {\n staleAfterMs?: number;\n}): DiscoveredRoom[] {\n const staleAfterMs = options?.staleAfterMs ?? 3500;\n const [rooms, setRooms] = useState<DiscoveredRoom[]>([]);\n\n useEffect(() => {\n const socket = dgram.createSocket(\"udp4\");\n socket.on(\"error\", () => {});\n\n socket.on(\"message\", (msg, rinfo) => {\n const packet = safeParse(msg);\n if (!packet) return;\n\n const now = Date.now();\n setRooms((prev) => {\n const key = `${rinfo.address}:${packet.tcpPort}`;\n const next = prev.filter((r) => `${r.ip}:${r.tcpPort}` !== key);\n next.push({\n ip: rinfo.address,\n hostName: packet.hostName,\n roomName: packet.roomName,\n tcpPort: packet.tcpPort,\n lastSeenAt: now,\n });\n return next;\n });\n });\n\n socket.bind(UDP_PORT, () => {});\n\n const prune = setInterval(() => {\n const now = Date.now();\n setRooms((prev) =>\n prev.filter((r) => now - r.lastSeenAt <= staleAfterMs)\n );\n }, 500);\n\n return () => {\n clearInterval(prune);\n socket.close();\n };\n }, [staleAfterMs]);\n\n return rooms;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport net from \"node:net\";\nimport type {\n ActivityState,\n ConnectionStatus,\n TcpPacket,\n ProjectileKind,\n ProjectileDirection,\n Peer,\n} from \"../protocol.js\";\nimport { TCP_DEFAULT_PORT } from \"../constants.js\";\n\ntype HostOptions = { role: \"host\"; localName: string; port?: number };\ntype ClientOptions = {\n role: \"client\";\n localName: string;\n hostIp: string;\n tcpPort: number;\n hostName?: string;\n};\ntype Options = HostOptions | ClientOptions;\n\nfunction writePacket(socket: net.Socket, packet: TcpPacket) {\n socket.write(`${JSON.stringify(packet)}\\n`, \"utf8\");\n}\n\ntype PeerConnection = {\n id: string;\n socket: net.Socket;\n name: string;\n state: ActivityState;\n lastSeen: number;\n heartbeatInterval: NodeJS.Timeout | null;\n};\n\nexport function useTcpSync(options: Options): {\n status: ConnectionStatus;\n listenPort?: number;\n peers: Peer[];\n peerName?: string; // For backward compatibility (first peer name)\n remoteState?: ActivityState; // For backward compatibility (first peer state)\n sendStatus: (state: ActivityState) => void;\n sendProjectile: (kind: ProjectileKind, direction: ProjectileDirection) => void;\n setOnRemoteProjectile: (callback: (kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) => void;\n sendBubble: (text: string, durationMs: number) => void;\n setOnRemoteBubble: (callback: (text: string, durationMs: number, senderName: string) => void) => void;\n} {\n const [status, setStatus] = useState<ConnectionStatus>(\n options.role === \"host\" ? \"waiting\" : \"connecting\"\n );\n const [listenPort, setListenPort] = useState<number | undefined>(undefined);\n const [peers, setPeers] = useState<Peer[]>([]);\n\n const onRemoteProjectileRef = useRef<((kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) | undefined>(undefined);\n const onRemoteBubbleRef = useRef<((text: string, durationMs: number, senderName: string) => void) | undefined>(undefined);\n\n // For host: map of all peer connections\n const peerConnectionsRef = useRef<Map<string, PeerConnection>>(new Map());\n // For client: single socket ref\n const clientSocketRef = useRef<net.Socket | null>(null);\n const clientHeartbeatRef = useRef<NodeJS.Timeout | null>(null);\n const clientLastSeenRef = useRef<number>(Date.now());\n\n // Generate unique peer ID\n const generatePeerId = () => `peer_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n // Broadcast packet to all peers (host only)\n const broadcastPacket = useCallback((packet: TcpPacket, excludeId?: string) => {\n peerConnectionsRef.current.forEach((peer, id) => {\n if (id !== excludeId && !peer.socket.destroyed) {\n writePacket(peer.socket, packet);\n }\n });\n }, []);\n\n // Update peers state from connections map\n const syncPeersState = useCallback(() => {\n const peerList: Peer[] = [];\n peerConnectionsRef.current.forEach((conn) => {\n peerList.push({ id: conn.id, name: conn.name, state: conn.state });\n });\n setPeers(peerList);\n\n // Update status based on peer count\n if (options.role === \"host\") {\n setStatus(peerList.length > 0 ? \"connected\" : \"waiting\");\n }\n }, [options.role]);\n\n // Remove a peer connection (host only)\n const removePeerConnection = useCallback((peerId: string) => {\n const conn = peerConnectionsRef.current.get(peerId);\n if (conn) {\n if (conn.heartbeatInterval) clearInterval(conn.heartbeatInterval);\n if (!conn.socket.destroyed) conn.socket.destroy();\n\n // Notify other peers about the left peer\n broadcastPacket({ type: \"peer_left\", peerName: conn.name, sentAt: Date.now() }, peerId);\n\n peerConnectionsRef.current.delete(peerId);\n syncPeersState();\n }\n }, [broadcastPacket, syncPeersState]);\n\n const MAX_PEERS = 4;\n\n // Attach a new peer socket (host only)\n const attachPeerSocket = useCallback((socket: net.Socket) => {\n // Reject if already at max capacity (host + 3 clients = 4 total, so max 3 peer connections)\n if (peerConnectionsRef.current.size >= MAX_PEERS - 1) {\n socket.end();\n socket.destroy();\n return;\n }\n\n const peerId = generatePeerId();\n let buf = \"\";\n\n socket.setNoDelay(true);\n socket.setEncoding(\"utf8\");\n\n const conn: PeerConnection = {\n id: peerId,\n socket,\n name: \"Connecting...\",\n state: \"IDLE\",\n lastSeen: Date.now(),\n heartbeatInterval: null,\n };\n\n peerConnectionsRef.current.set(peerId, conn);\n\n socket.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n\n try {\n const packet = JSON.parse(line) as TcpPacket;\n conn.lastSeen = Date.now();\n\n if (packet.type === \"hello\") {\n conn.name = packet.clientName;\n syncPeersState();\n\n // Send hello back\n writePacket(socket, {\n type: \"hello\",\n hostName: options.localName,\n clientName: \"Host\",\n sentAt: Date.now(),\n });\n\n // Notify other peers about new peer\n broadcastPacket({ type: \"peer_joined\", peerName: conn.name, sentAt: Date.now() }, peerId);\n\n // Send list of existing peers to new peer\n peerConnectionsRef.current.forEach((existingConn, existingId) => {\n if (existingId !== peerId && existingConn.name !== \"Connecting...\") {\n writePacket(socket, { type: \"peer_joined\", peerName: existingConn.name, sentAt: Date.now() });\n }\n });\n }\n\n if (packet.type === \"status\") {\n conn.state = packet.state;\n syncPeersState();\n // Broadcast status to other peers\n broadcastPacket({ ...packet, senderName: conn.name }, peerId);\n }\n\n if (packet.type === \"ping\") {\n writePacket(socket, { type: \"pong\", sentAt: Date.now() });\n }\n\n if (packet.type === \"projectile\") {\n // Call local handler\n onRemoteProjectileRef.current?.(packet.kind, packet.direction, conn.name);\n // Broadcast to other peers\n broadcastPacket({ ...packet, senderName: conn.name }, peerId);\n }\n\n if (packet.type === \"bubble\") {\n // Call local handler\n onRemoteBubbleRef.current?.(packet.text, packet.durationMs, conn.name);\n // Broadcast to other peers\n broadcastPacket({ ...packet, senderName: conn.name }, peerId);\n }\n } catch {\n // ignore parse errors\n }\n }\n });\n\n socket.on(\"close\", () => {\n removePeerConnection(peerId);\n });\n\n socket.on(\"error\", () => {\n removePeerConnection(peerId);\n });\n\n // Heartbeat for this peer\n conn.heartbeatInterval = setInterval(() => {\n if (socket.destroyed) {\n removePeerConnection(peerId);\n return;\n }\n writePacket(socket, { type: \"ping\", sentAt: Date.now() });\n const age = Date.now() - conn.lastSeen;\n if (age > 6000) {\n removePeerConnection(peerId);\n }\n }, 2000);\n\n syncPeersState();\n }, [options.localName, broadcastPacket, syncPeersState, removePeerConnection]);\n\n // Client socket cleanup\n const cleanupClientSocket = useCallback(() => {\n if (clientHeartbeatRef.current) clearInterval(clientHeartbeatRef.current);\n clientHeartbeatRef.current = null;\n\n const s = clientSocketRef.current;\n clientSocketRef.current = null;\n if (s && !s.destroyed) s.destroy();\n }, []);\n\n // Attach client socket\n const attachClientSocket = useCallback((socket: net.Socket) => {\n cleanupClientSocket();\n clientSocketRef.current = socket;\n clientLastSeenRef.current = Date.now();\n\n setStatus(\"connected\");\n\n let buf = \"\";\n socket.setNoDelay(true);\n socket.setEncoding(\"utf8\");\n\n socket.on(\"data\", (chunk: string) => {\n buf += chunk;\n while (true) {\n const idx = buf.indexOf(\"\\n\");\n if (idx === -1) break;\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n\n try {\n const packet = JSON.parse(line) as TcpPacket;\n clientLastSeenRef.current = Date.now();\n\n if (packet.type === \"hello\") {\n // Host connected - add host as first peer\n setPeers((prev) => {\n const hostExists = prev.some((p) => p.id === \"host\");\n if (hostExists) return prev;\n return [{ id: \"host\", name: packet.hostName, state: \"IDLE\" as ActivityState }, ...prev];\n });\n }\n\n if (packet.type === \"status\") {\n // Update peer's state\n const senderName = packet.senderName;\n if (senderName) {\n setPeers((prev) => prev.map((p) =>\n p.name === senderName ? { ...p, state: packet.state } : p\n ));\n } else {\n // From host\n setPeers((prev) => prev.map((p) =>\n p.id === \"host\" ? { ...p, state: packet.state } : p\n ));\n }\n }\n\n if (packet.type === \"ping\") {\n writePacket(socket, { type: \"pong\", sentAt: Date.now() });\n }\n\n if (packet.type === \"projectile\") {\n onRemoteProjectileRef.current?.(packet.kind, packet.direction, packet.senderName);\n }\n\n if (packet.type === \"bubble\") {\n onRemoteBubbleRef.current?.(packet.text, packet.durationMs, packet.senderName);\n }\n\n if (packet.type === \"peer_joined\") {\n setPeers((prev) => {\n const exists = prev.some((p) => p.name === packet.peerName);\n if (exists) return prev;\n return [...prev, { id: `peer_${Date.now()}`, name: packet.peerName, state: \"IDLE\" as ActivityState }];\n });\n }\n\n if (packet.type === \"peer_left\") {\n setPeers((prev) => prev.filter((p) => p.name !== packet.peerName));\n }\n } catch {\n // ignore\n }\n }\n });\n\n socket.on(\"close\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n cleanupClientSocket();\n });\n\n socket.on(\"error\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n });\n\n // Hello handshake\n writePacket(socket, {\n type: \"hello\",\n hostName: options.role === \"client\" ? options.hostName ?? \"Host\" : options.localName,\n clientName: options.localName,\n sentAt: Date.now(),\n });\n\n // Heartbeat\n clientHeartbeatRef.current = setInterval(() => {\n const sock = clientSocketRef.current;\n if (!sock || sock.destroyed) return;\n writePacket(sock, { type: \"ping\", sentAt: Date.now() });\n const age = Date.now() - clientLastSeenRef.current;\n if (age > 6000) {\n setStatus(\"disconnected\");\n setPeers([]);\n cleanupClientSocket();\n }\n }, 2000);\n }, [cleanupClientSocket, options]);\n\n // Main effect: setup server or client\n useEffect(() => {\n if (options.role === \"host\") {\n const server = net.createServer((socket) => {\n attachPeerSocket(socket);\n });\n\n server.on(\"error\", () => {});\n\n server.listen(options.port ?? TCP_DEFAULT_PORT, () => {\n const address = server.address();\n if (address && typeof address === \"object\") setListenPort(address.port);\n });\n\n return () => {\n // Cleanup all peer connections\n peerConnectionsRef.current.forEach((conn) => {\n if (conn.heartbeatInterval) clearInterval(conn.heartbeatInterval);\n if (!conn.socket.destroyed) conn.socket.destroy();\n });\n peerConnectionsRef.current.clear();\n server.close();\n };\n }\n\n // Client mode\n setStatus(\"connecting\");\n const socket = net.createConnection(\n { host: options.hostIp, port: options.tcpPort },\n () => {\n attachClientSocket(socket);\n }\n );\n socket.on(\"error\", () => {\n setStatus(\"disconnected\");\n setPeers([]);\n });\n\n return () => {\n socket.destroy();\n cleanupClientSocket();\n };\n }, [attachPeerSocket, attachClientSocket, cleanupClientSocket, options]);\n\n // Send status to all peers\n const sendStatus = useCallback((state: ActivityState) => {\n if (options.role === \"host\") {\n broadcastPacket({ type: \"status\", state, senderName: options.localName, sentAt: Date.now() });\n } else {\n const socket = clientSocketRef.current;\n if (socket && !socket.destroyed) {\n writePacket(socket, { type: \"status\", state, sentAt: Date.now() });\n }\n }\n }, [options, broadcastPacket]);\n\n // Send projectile to all peers\n const sendProjectile = useCallback((kind: ProjectileKind, direction: ProjectileDirection) => {\n if (options.role === \"host\") {\n broadcastPacket({ type: \"projectile\", kind, direction, senderName: options.localName, sentAt: Date.now() });\n } else {\n const socket = clientSocketRef.current;\n if (socket && !socket.destroyed) {\n writePacket(socket, { type: \"projectile\", kind, direction, sentAt: Date.now() });\n }\n }\n }, [options, broadcastPacket]);\n\n const setOnRemoteProjectile = useCallback((callback: (kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) => {\n onRemoteProjectileRef.current = callback;\n }, []);\n\n // Send bubble to all peers\n const sendBubble = useCallback((text: string, durationMs: number) => {\n if (options.role === \"host\") {\n broadcastPacket({ type: \"bubble\", text, durationMs, senderName: options.localName, sentAt: Date.now() });\n } else {\n const socket = clientSocketRef.current;\n if (socket && !socket.destroyed) {\n writePacket(socket, { type: \"bubble\", text, durationMs, senderName: options.localName, sentAt: Date.now() });\n }\n }\n }, [options, broadcastPacket]);\n\n const setOnRemoteBubble = useCallback((callback: (text: string, durationMs: number, senderName: string) => void) => {\n onRemoteBubbleRef.current = callback;\n }, []);\n\n // Backward compatibility: first peer\n const firstPeer = peers[0];\n const peerName = firstPeer?.name;\n const remoteState = firstPeer?.state;\n\n return {\n status,\n listenPort,\n peers,\n peerName,\n remoteState,\n sendStatus,\n sendProjectile,\n setOnRemoteProjectile,\n sendBubble,\n setOnRemoteBubble\n };\n}\n","export { useActivityMonitor } from \"./useActivityMonitor.js\";\nexport { useAiAgent } from \"./useAiAgent.js\";\nexport { useBroadcaster } from \"./useBroadcaster.js\";\nexport { useScanner } from \"./useScanner.js\";\nexport { useTcpSync } from \"./useTcpSync.js\";\n","import React, {useMemo} from 'react';\nimport {Box, Text, useInput} from 'ink';\nimport {useScanner} from '../hooks/index.js';\nimport type {DiscoveredRoom} from '../types.js';\n\nexport function RoomScanner(props: {\n\tonSelectRoom: (room: DiscoveredRoom) => void;\n\tonBack: () => void;\n\tonExit: () => void;\n}) {\n\tconst rooms = useScanner();\n\n\tconst sortedRooms = useMemo(() => {\n\t\treturn [...rooms].sort((a, b) => b.lastSeenAt - a.lastSeenAt);\n\t}, [rooms]);\n\n\tuseInput((input, key) => {\n\t\tif (key.escape || input === 'b') props.onBack();\n\t\tif (input === 'q') props.onExit();\n\n\t\tconst index = Number.parseInt(input, 10);\n\t\tif (Number.isNaN(index)) return;\n\t\tconst room = sortedRooms[index - 1];\n\t\tif (!room) return;\n\t\tprops.onSelectRoom(room);\n\t});\n\n\treturn (\n\t\t<Box flexDirection=\"column\" padding={1}>\n\t\t\t<Text>\n\t\t\t\t<Text color=\"yellow\">正在扫描局域网...</Text> (按 <Text color=\"cyan\">b</Text> 返回,{' '}\n\t\t\t\t<Text color=\"cyan\">q</Text> 退出)\n\t\t\t</Text>\n\t\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t\t{sortedRooms.length === 0 ? (\n\t\t\t\t\t<Text color=\"gray\">暂无房间广播。</Text>\n\t\t\t\t) : (\n\t\t\t\t\tsortedRooms.map((room, i) => (\n\t\t\t\t\t\t<Text key={`${room.ip}:${room.tcpPort}`}>\n\t\t\t\t\t\t\t<Text color=\"cyan\">[{i + 1}]</Text> {room.hostName} <Text color=\"gray\">@</Text>{' '}\n\t\t\t\t\t\t\t<Text color=\"gray\">\n\t\t\t\t\t\t\t\t{room.ip}:{room.tcpPort}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</Box>\n\t\t</Box>\n\t);\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { LeaveStats } from \"../types.js\";\n\nfunction formatDuration(ms: number) {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n if (hours > 0) return `${hours}小时${minutes}分${seconds}秒`;\n if (minutes > 0) return `${minutes}分${seconds}秒`;\n return `${seconds}秒`;\n}\n\nexport function LeavePage(props: {\n stats: LeaveStats;\n onBack: () => void;\n onExit: () => void;\n}) {\n useInput((input, key) => {\n if (key.escape || input === \"q\") props.onExit();\n if (key.return || input === \" \") props.onBack();\n });\n\n const sessionLabel = useMemo(\n () => formatDuration(props.stats.sessionDurationMs),\n [props.stats.sessionDurationMs]\n );\n const connectedLabel = useMemo(\n () => formatDuration(props.stats.connectedDurationMs),\n [props.stats.connectedDurationMs]\n );\n\n return (\n <Box flexDirection=\"column\" padding={1} alignItems=\"center\">\n {/* <Text color=\"cyan\">\n {String.raw`\n ____ _ _ \n / ___| ___ ___ | | __ _| |_| |\n \\___ \\ / _ \\ _ \\ | | / _' | __| |\n ___) | __/ __/ | |__| (_| | |_|_|\n |____/ \\___|\\___|___|_____\\__,_|\\__(_)\n |_____| \n`}\n </Text> */}\n\n <Box\n flexDirection=\"column\"\n marginTop={1}\n borderStyle=\"round\"\n paddingX={2}\n borderColor=\"gray\"\n >\n <Text color=\"white\" bold>\n {props.stats.peerName\n ? `与 ${props.stats.peerName} 的同频记录`\n : \"本次专注记录\"}\n </Text>\n\n <Box marginTop={1} flexDirection=\"column\" gap={1}>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>⌨️ 键盘敲击</Text>\n <Text color=\"yellow\">{props.stats.keyPresses}</Text>\n </Box>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>⏱️ 总共时长</Text>\n <Text color=\"green\">{sessionLabel}</Text>\n </Box>\n <Box justifyContent=\"space-between\" width={30}>\n <Text>🔗 连线时长</Text>\n <Text color=\"blue\">{connectedLabel}</Text>\n </Box>\n </Box>\n </Box>\n\n <Box marginTop={1}>\n <Text color=\"gray\">\n 按 <Text color=\"white\">Enter</Text> 返回菜单,或{\" \"}\n <Text color=\"red\">q</Text> 退出程序\n </Text>\n </Box>\n </Box>\n );\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\ntype KeyFile = {apiKey?: string};\n\nconst KEY_RELATIVE_PATH = path.join('src', 'assets', 'key.json');\n\nasync function readJsonFile(filePath: string): Promise<KeyFile | null> {\n\ttry {\n\t\tconst raw = await fs.readFile(filePath, 'utf8');\n\t\tconst parsed = JSON.parse(raw) as KeyFile;\n\t\tif (!parsed || typeof parsed !== 'object') return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function ensureDirForFile(filePath: string) {\n\tawait fs.mkdir(path.dirname(filePath), {recursive: true});\n}\n\nexport async function loadStoredApiKey(): Promise<string | null> {\n\tconst absolute = path.resolve(process.cwd(), KEY_RELATIVE_PATH);\n\tconst json = await readJsonFile(absolute);\n\tconst key = (json?.apiKey ?? '').trim();\n\treturn key.length > 0 ? key : null;\n}\n\nexport async function saveStoredApiKey(apiKey: string): Promise<void> {\n\tconst trimmed = apiKey.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('API key cannot be empty');\n\t}\n\tconst absolute = path.resolve(process.cwd(), KEY_RELATIVE_PATH);\n\tawait ensureDirForFile(absolute);\n\tconst payload: KeyFile = {apiKey: trimmed};\n\tawait fs.writeFile(absolute, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAiAgent } from \"../hooks/index.js\";\nimport { loadStoredApiKey, saveStoredApiKey } from \"../storage/apiKey.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"./sprite/ProjectileThrowSprite.js\";\n\nexport function AiConsole(props: {\n onClose: () => void;\n onStartCountdown: (minutes: number) => void;\n onShowBubble?: (args: {\n text: string;\n target: number; // 1-4 for No.1 to No.4\n durationMs: number;\n }) => void;\n onThrowProjectile: (\n kind: ProjectileKind,\n direction: ProjectileDirection\n ) => void;\n localName: string;\n peerName: string;\n peers?: Array<{ id: string; name: string }>; // For session info\n}) {\n const [input, setInput] = useState(\"\");\n const [apiKey, setApiKey] = useState<string | null>(null);\n const [keyDraft, setKeyDraft] = useState(\"\");\n const [cursorOn, setCursorOn] = useState(true);\n const [keyStatus, setKeyStatus] = useState<\n \"loading\" | \"missing\" | \"ready\" | \"saving\"\n >(\"loading\");\n\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n const stored = await loadStoredApiKey();\n if (cancelled) return;\n if (stored) {\n setApiKey(stored);\n setKeyStatus(\"ready\");\n } else {\n setKeyStatus(\"missing\");\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n const handle = setInterval(() => setCursorOn((v) => !v), 500);\n return () => clearInterval(handle);\n }, []);\n\n const agent = useAiAgent({\n localName: props.localName,\n peerName: props.peerName,\n peers: props.peers,\n onStartCountdown: props.onStartCountdown,\n onShowBubble: props.onShowBubble,\n onThrowProjectile: props.onThrowProjectile,\n apiKey: apiKey ?? undefined,\n });\n\n const resetApiKey = () => {\n setApiKey(null);\n setKeyDraft(\"\");\n setKeyStatus(\"missing\");\n agent.resetApiKeyError();\n };\n\n const helpLine = useMemo(\n () => \"示例:倒计时20分钟 / 聊会天 / 和别人互动一下\",\n []\n );\n\n useInput(\n (ch, key) => {\n if (key.escape) {\n props.onClose();\n return;\n }\n\n if (agent.apiKeyError && (ch === \"r\" || ch === \"R\")) {\n resetApiKey();\n return;\n }\n\n if (keyStatus !== \"ready\") {\n if (key.return) {\n const draft = keyDraft.trim();\n if (!draft) return;\n setKeyStatus(\"saving\");\n void (async () => {\n await saveStoredApiKey(draft);\n setApiKey(draft);\n setKeyDraft(\"\");\n setKeyStatus(\"ready\");\n })();\n return;\n }\n\n if (key.backspace || key.delete) {\n setKeyDraft((s) => s.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (ch) setKeyDraft((s) => s + ch);\n return;\n }\n\n if (key.return) {\n const line = input.trim();\n setInput(\"\");\n if (!line) return;\n void agent.ask(line);\n return;\n }\n\n if (key.backspace || key.delete) {\n setInput((s) => s.slice(0, -1));\n return;\n }\n\n if (key.ctrl || key.meta) return;\n if (ch) setInput((s) => s + ch);\n },\n { isActive: true }\n );\n\n const lines = agent.lines.filter((l) => l.text.trim().length > 0).slice(-6);\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} paddingY={0}>\n <Box justifyContent=\"space-between\" marginBottom={0}>\n <Text color=\"cyan\">AI Console</Text>\n <Text color=\"gray\">\n {keyStatus === \"saving\"\n ? \"Saving…\"\n : agent.apiKeyError\n ? \"Press R to reset API\"\n : agent.busy\n ? \"Thinking…\"\n : \"Esc Close\"}\n </Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {keyStatus === \"loading\" ? (\n <Text color=\"gray\">Checking API Key...</Text>\n ) : keyStatus === \"missing\" || keyStatus === \"saving\" ? (\n <Text color=\"yellow\">\n Setup: Enter DeepSeek API Key (saves to{\" \"}\n <Text color=\"cyan\">src/assets/key.json</Text>)\n </Text>\n ) : lines.length === 0 ? (\n <Text color=\"gray\">{helpLine}</Text>\n ) : null}\n </Box>\n\n <Box flexDirection=\"column\" marginTop={0} minHeight={6}>\n {keyStatus === \"ready\" ? (\n <>\n {lines.map((l, i) => (\n <Text\n key={`${l.kind}:${l.at}:${i}`}\n color={l.kind === \"user\" ? \"yellow\" : \"white\"}\n wrap=\"truncate-end\"\n >\n {l.kind === \"user\" ? \"> \" : \"\"}\n {l.text}\n </Text>\n ))}\n </>\n ) : (\n <Text color=\"gray\">Please enter API Key to proceed.</Text>\n )}\n </Box>\n\n <Box\n marginTop={0}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n >\n <Text color=\"green\">{\">\"} </Text>\n {keyStatus === \"ready\" ? (\n <>\n <Text>{input}</Text>\n {cursorOn ? <Text inverse> </Text> : <Text> </Text>}\n </>\n ) : (\n <Text>\n {keyDraft.length === 0\n ? \"\"\n : \"*\".repeat(Math.min(64, keyDraft.length))}\n {cursorOn ? <Text inverse> </Text> : <Text> </Text>}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text } from \"ink\";\n\nfunction wrapText(text: string, maxWidth: number) {\n const chars = Array.from(text);\n const lines: string[] = [];\n for (let i = 0; i < chars.length; i += maxWidth) {\n lines.push(chars.slice(i, i + maxWidth).join(\"\"));\n }\n return lines.length ? lines : [\"\"];\n}\n\nfunction renderBubbleLines(text: string, maxInnerWidth: number) {\n const contentLines = wrapText(text, maxInnerWidth);\n const innerWidth = Math.min(\n maxInnerWidth,\n Math.max(...contentLines.map((l) => Array.from(l).length), 1)\n );\n\n const top = `╭${\"─\".repeat(innerWidth + 2)}╮`;\n \n // Create a bottom line with a little tail \"v\" in the middle\n const tailPos = Math.floor((innerWidth + 2) / 2);\n const bottomChars = Array.from(`╰${\"─\".repeat(innerWidth + 2)}╯`);\n if (bottomChars[tailPos]) bottomChars[tailPos] = \"v\"; // Simple tail\n const bottom = bottomChars.join(\"\");\n\n const middle = contentLines.map((l) => {\n const pad = innerWidth - Array.from(l).length;\n return `│ ${l}${\" \".repeat(Math.max(0, pad))} │`;\n });\n return [top, ...middle, bottom];\n}\n\nexport function BubbleSprite(props: {\n text: string;\n maxInnerWidth?: number;\n color?: string;\n}) {\n const trimmed = props.text.trim();\n const maxInnerWidth = props.maxInnerWidth ?? 18;\n\n const lines = useMemo(() => {\n if (!trimmed) return null;\n return renderBubbleLines(trimmed, maxInnerWidth);\n }, [maxInnerWidth, trimmed]);\n\n if (!lines) return null;\n\n return (\n <Box flexDirection=\"column\" alignItems=\"center\">\n {lines.map((line, i) => (\n <Text key={`bubble:${i}`} color={props.color ?? \"gray\"}>\n {line}\n </Text>\n ))}\n </Box>\n );\n}\n\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityState } from \"../../protocol.js\";\nimport { BubbleSprite } from \"./BubbleSprite.js\";\n\nconst SPRITES: Record<\n ActivityState,\n { color?: string; compact: string; frames: string[] }\n> = {\n TYPING: {\n color: \"green\",\n compact: \"( >_<)===3\",\n frames: [\" /\\\\_/\\\\ \", \"( >_<) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n IDLE: {\n color: \"yellow\",\n compact: \"( -.-)Zzz\",\n frames: [\" /\\\\_/\\\\ \", \"( -.-) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n OFFLINE: {\n color: \"gray\",\n compact: \"( x_x)\",\n frames: [\" /\\\\_/\\\\ \", \"( x_x) \", \" /|_|\\\\\\\\ \", \" / \\\\\\\\ \"],\n },\n};\n\nexport function BuddyAvatar(props: {\n state: ActivityState;\n variant?: \"frames\" | \"compact\";\n bubbleText?: string | null;\n marginTop?: number;\n}) {\n const sprite = SPRITES[props.state];\n if (props.variant === \"compact\") {\n return (\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n marginTop={props.marginTop ?? 1}\n >\n {props.bubbleText ? <BubbleSprite text={props.bubbleText} /> : null}\n <Text color={sprite.color}>{sprite.compact}</Text>\n </Box>\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n marginTop={props.marginTop ?? 1}\n >\n {props.bubbleText ? <BubbleSprite text={props.bubbleText} /> : null}\n {sprite.frames.map((line, i) => (\n <Text key={`${props.state}:${i}`} color={sprite.color}>\n {line}\n </Text>\n ))}\n </Box>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type CountdownClockType = \"SHORT\" | \"MEDIUM\" | \"LONG\";\nexport type CountdownClockVariant = \"FULL\" | \"COMPACT\";\n\nexport function countdownClockTypeFromMinutes(\n minutes: number\n): CountdownClockType {\n if (minutes <= 10) return \"SHORT\";\n if (minutes <= 30) return \"MEDIUM\";\n return \"LONG\";\n}\n\nfunction clamp01(n: number) {\n if (n <= 0) return 0;\n if (n >= 1) return 1;\n return n;\n}\n\nconst TYPE_STYLE: Record<CountdownClockType, { color: string; label: string }> =\n {\n SHORT: { color: \"green\", label: \"Sprint\" },\n MEDIUM: { color: \"cyan\", label: \"Focus\" },\n LONG: { color: \"magenta\", label: \"Deep\" },\n };\n\ntype HandDir = \"N\" | \"NE\" | \"E\" | \"SE\" | \"S\" | \"SW\" | \"W\" | \"NW\";\n\nfunction handFromProgress(progress01: number): HandDir {\n const idx = Math.round(clamp01(progress01) * 7);\n const dirs: HandDir[] = [\"N\", \"NE\", \"E\", \"SE\", \"S\", \"SW\", \"W\", \"NW\"];\n return dirs[idx]!;\n}\n\nfunction renderClockFace(hand: HandDir) {\n const lines = [\n \" .---. \",\n \" / \\\\ \",\n \"| • |\",\n \" \\\\ / \",\n \" '---' \",\n ].map((s) => s.split(\"\"));\n\n const center = { r: 2, c: 4 };\n const handMap: Record<HandDir, { r: number; c: number; ch: string }> = {\n N: { r: 1, c: 4, ch: \"|\" },\n NE: { r: 1, c: 5, ch: \"/\" },\n E: { r: 2, c: 5, ch: \"-\" },\n SE: { r: 3, c: 5, ch: \"\\\\\" },\n S: { r: 3, c: 4, ch: \"|\" },\n SW: { r: 3, c: 3, ch: \"/\" },\n W: { r: 2, c: 3, ch: \"-\" },\n NW: { r: 1, c: 3, ch: \"\\\\\" },\n };\n\n const tip = handMap[hand];\n lines[center.r][center.c] = \"•\";\n lines[tip.r][tip.c] = tip.ch;\n\n return lines.map((row) => row.join(\"\"));\n}\n\nfunction renderCompactClockFace(hand: HandDir) {\n const lines = [\" .---. \", \"| • |\", \" '---' \"].map((s) => s.split(\"\"));\n const center = { r: 1, c: 3 };\n const handMap: Record<HandDir, { r: number; c: number; ch: string }> = {\n N: { r: 0, c: 3, ch: \"|\" },\n NE: { r: 0, c: 4, ch: \"/\" },\n E: { r: 1, c: 5, ch: \"-\" },\n SE: { r: 2, c: 4, ch: \"\\\\\" },\n S: { r: 2, c: 3, ch: \"|\" },\n SW: { r: 2, c: 2, ch: \"/\" },\n W: { r: 1, c: 1, ch: \"-\" },\n NW: { r: 0, c: 2, ch: \"\\\\\" },\n };\n\n const tip = handMap[hand];\n lines[center.r][center.c] = \"•\";\n lines[tip.r][tip.c] = tip.ch;\n\n return lines.map((row) => row.join(\"\"));\n}\n\nexport function CountdownClockSprite(props: {\n type?: CountdownClockType;\n variant?: CountdownClockVariant;\n minutes?: number;\n label?: string | null;\n showLabel?: boolean;\n totalSeconds?: number;\n remainingSeconds?: number | null;\n}) {\n const type =\n props.type ??\n (typeof props.minutes === \"number\"\n ? countdownClockTypeFromMinutes(props.minutes)\n : \"MEDIUM\");\n\n const progress01 = useMemo(() => {\n if (\n typeof props.totalSeconds !== \"number\" ||\n props.totalSeconds <= 0 ||\n props.remainingSeconds === null ||\n typeof props.remainingSeconds !== \"number\"\n ) {\n return null;\n }\n return clamp01(props.remainingSeconds / props.totalSeconds);\n }, [props.remainingSeconds, props.totalSeconds]);\n\n const style = TYPE_STYLE[type];\n const hand = handFromProgress(progress01 ?? 1);\n const caption = props.label ?? style.label;\n\n if (props.variant === \"COMPACT\") {\n const face = renderCompactClockFace(hand);\n return (\n <Box flexDirection=\"column\">\n {face.map((line, i) => (\n <Text key={`clock:compact:${type}:${hand}:${i}`} color={style.color}>\n {line}\n </Text>\n ))}\n {props.showLabel === false ? null : (\n <Text color=\"gray\">{caption ?? \" \"}</Text>\n )}\n </Box>\n );\n }\n\n const face = renderClockFace(hand);\n\n return (\n <Box flexDirection=\"column\">\n {face.map((line, i) => (\n <Text key={`clock:${type}:${hand}:${i}`} color={style.color}>\n {line}\n </Text>\n ))}\n {props.showLabel === false ? null : (\n <Text color=\"gray\">{caption ?? \" \"}</Text>\n )}\n </Box>\n );\n}\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type ProjectileKind = \"ROSE\" | \"POOP\" | \"HAMMER\";\nexport type ProjectileDirection = \"LEFT_TO_RIGHT\" | \"RIGHT_TO_LEFT\";\n\nconst PROJECTILES: Record<ProjectileKind, { glyph: string; color: string }> = {\n ROSE: { glyph: \"🌹\", color: \"magenta\" },\n POOP: { glyph: \"💩\", color: \"yellow\" },\n HAMMER: { glyph: \"🔨\", color: \"cyan\" },\n};\n\nfunction clamp01(n: number) {\n if (n <= 0) return 0;\n if (n >= 1) return 1;\n return n;\n}\n\nfunction renderTrack(width: number, pos: number, glyph: string) {\n const w = Math.max(8, Math.floor(width));\n const innerWidth = w - 2;\n if (pos < 0) return `|${new Array(innerWidth).fill(\"·\").join(\"\")}|`;\n const clampedPos = Math.max(0, Math.min(innerWidth - 1, Math.floor(pos)));\n\n const track = new Array(innerWidth).fill(\"·\");\n track[clampedPos] = glyph;\n return `|${track.join(\"\")}|`;\n}\n\nexport function ProjectileThrowSprite(props: {\n kind: ProjectileKind;\n direction?: ProjectileDirection;\n width?: number;\n progress?: number;\n shotId?: string | number;\n durationMs?: number;\n leftLabel?: string;\n rightLabel?: string;\n onDone?: () => void;\n}) {\n const direction = props.direction ?? \"LEFT_TO_RIGHT\";\n const width = props.width ?? 28;\n const durationMs = props.durationMs ?? 700;\n\n const [autoProgress, setAutoProgress] = useState<number | null>(null);\n const progress = typeof props.progress === \"number\" ? props.progress : autoProgress;\n\n useEffect(() => {\n if (props.shotId === undefined) return;\n const startedAt = Date.now();\n setAutoProgress(0);\n\n const handle = setInterval(() => {\n const elapsed = Date.now() - startedAt;\n const next = clamp01(elapsed / Math.max(1, durationMs));\n setAutoProgress(next);\n if (next >= 1) {\n clearInterval(handle);\n props.onDone?.();\n }\n }, 33);\n\n return () => clearInterval(handle);\n }, [durationMs, props.onDone, props.shotId]);\n\n const projectile = PROJECTILES[props.kind];\n\n const track = useMemo(() => {\n if (progress === null || !Number.isFinite(progress)) {\n return renderTrack(width, -1, \" \");\n }\n const innerWidth = Math.max(8, Math.floor(width)) - 2;\n const rawPos = clamp01(progress) * (innerWidth - 1);\n const pos =\n direction === \"LEFT_TO_RIGHT\" ? rawPos : (innerWidth - 1 - rawPos);\n return renderTrack(width, pos, projectile.glyph);\n }, [direction, progress, projectile.glyph, width]);\n\n return (\n <Box>\n {props.leftLabel ? <Text color=\"gray\">{props.leftLabel} </Text> : null}\n <Text color={projectile.color}>{track}</Text>\n {props.rightLabel ? <Text color=\"gray\"> {props.rightLabel}</Text> : null}\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ConnectionStatus } from \"../protocol.js\";\n\nfunction statusText(status: ConnectionStatus) {\n switch (status) {\n case \"waiting\":\n return { label: \"Waiting\", color: \"yellow\" };\n case \"connecting\":\n return { label: \"Connecting\", color: \"yellow\" };\n case \"connected\":\n return { label: \"Connected via TCP\", color: \"green\" };\n case \"disconnected\":\n return { label: \"Disconnected\", color: \"red\" };\n }\n}\n\nexport function StatusHeader(props: {\n role: \"host\" | \"client\";\n status: ConnectionStatus;\n hostIp?: string;\n tcpPort?: number;\n peerCount?: number;\n}) {\n const st = statusText(props.status);\n return (\n <Box>\n <Box>\n <Text color={st.color}>{st.label}</Text>\n {props.peerCount !== undefined && props.peerCount > 0 && (\n <Text color=\"cyan\"> ({props.peerCount} online)</Text>\n )}\n {props.role === \"host\" ? (\n <Text color=\"gray\">\n {props.tcpPort ? ` — TCP :${props.tcpPort}` : \"\"}\n </Text>\n ) : (\n <Text color=\"gray\">\n {props.hostIp && props.tcpPort\n ? ` — ${props.hostIp}:${props.tcpPort}`\n : \"\"}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport type InfoRecord = {\n id: number;\n timestamp: number;\n type: \"bubble\" | \"countdown\" | \"projectile\" | \"join\" | \"leave\" | \"other\";\n content: string;\n};\n\nexport function InfoPanel(props: {\n records: InfoRecord[];\n maxRecords?: number;\n}) {\n const maxRecords = props.maxRecords ?? 8;\n const displayRecords = props.records.slice(-maxRecords);\n\n const formatTime = (ts: number) => {\n const d = new Date(ts);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n };\n\n const getTypeIcon = (type: InfoRecord[\"type\"]) => {\n switch (type) {\n case \"bubble\": return \"[Msg]\";\n case \"countdown\": return \"[Tmr]\";\n case \"projectile\": return \"[Thr]\";\n case \"join\": return \"[+]\";\n case \"leave\": return \"[-]\";\n default: return \"[*]\";\n }\n };\n\n const getTypeColor = (type: InfoRecord[\"type\"]) => {\n switch (type) {\n case \"bubble\": return \"cyan\";\n case \"countdown\": return \"green\";\n case \"projectile\": return \"magenta\";\n case \"join\": return \"green\";\n case \"leave\": return \"red\";\n default: return \"gray\";\n }\n };\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} paddingY={0}>\n <Box justifyContent=\"space-between\" marginBottom={0}>\n <Text color=\"yellow\">Info Log</Text>\n <Text color=\"gray\">{displayRecords.length} records</Text>\n </Box>\n\n <Box flexDirection=\"column\" minHeight={6}>\n {displayRecords.length === 0 ? (\n <Text color=\"gray\">No records yet...</Text>\n ) : (\n displayRecords.map((record) => (\n <Text key={record.id} wrap=\"truncate-end\">\n <Text color=\"gray\">{formatTime(record.timestamp)} </Text>\n <Text color={getTypeColor(record.type)}>{getTypeIcon(record.type)} </Text>\n <Text>{record.content}</Text>\n </Text>\n ))\n )}\n </Box>\n </Box>\n );\n}\n","export { AiConsole } from \"./AiConsole.js\";\nexport { BuddyAvatar } from \"./sprite/BuddyAvatar.js\";\nexport {\n CountdownClockSprite,\n countdownClockTypeFromMinutes,\n} from \"./sprite/CountdownClockSprite.js\";\nexport { ProjectileThrowSprite } from \"./sprite/ProjectileThrowSprite.js\";\nexport { StatusHeader } from \"./StatusHeader.js\";\nexport { InfoPanel } from \"./InfoPanel.js\";\nexport type { InfoRecord } from \"./InfoPanel.js\";\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { ActivityState } from \"../protocol.js\";\nimport {\n AiConsole,\n BuddyAvatar,\n CountdownClockSprite,\n ProjectileThrowSprite,\n StatusHeader,\n InfoPanel,\n} from \"../components/index.js\";\nimport type { InfoRecord } from \"../components/index.js\";\nimport type { CountdownClockType } from \"../components/sprite/CountdownClockSprite.js\";\nimport type {\n ProjectileDirection,\n ProjectileKind,\n} from \"../components/sprite/ProjectileThrowSprite.js\";\nimport {\n useActivityMonitor,\n useBroadcaster,\n useTcpSync,\n} from \"../hooks/index.js\";\nimport { ensureGlobalKeyboard, subscribeGlobalKeydown } from \"../hooks/globalKeyboard.js\";\nimport type { LeaveStats } from \"../types.js\";\n\nfunction formatMMSS(totalSeconds: number) {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n}\n\nexport function Session(\n props:\n | { role: \"host\"; localName: string; onLeave: (stats: LeaveStats) => void }\n | {\n role: \"client\";\n localName: string;\n onLeave: (stats: LeaveStats) => void;\n hostIp: string;\n tcpPort: number;\n roomName: string;\n hostName: string;\n }\n) {\n const roomName = useMemo(\n () => `${props.localName}'s Room`,\n [props.localName]\n );\n\n const [showAi, setShowAi] = useState(false);\n const [countdown, setCountdown] = useState<{\n minutes: number;\n totalSeconds: number;\n endsAt: number;\n remainingSeconds: number;\n type: CountdownClockType;\n } | null>(null);\n const [shots, setShots] = useState<\n Array<{\n id: number;\n kind: ProjectileKind;\n direction: ProjectileDirection;\n }>\n >([]);\n // Bubbles for all 4 users: index 0 = local (No.1), 1-3 = peers (No.2-4)\n const [peerBubbles, setPeerBubbles] = useState<(string | null)[]>([null, null, null, null]);\n const bubbleTimersRef = useRef<(NodeJS.Timeout | null)[]>([null, null, null, null]);\n\n // Info records for logging events\n const [infoRecords, setInfoRecords] = useState<InfoRecord[]>([]);\n const nextRecordIdRef = useRef<number>(1);\n\n const addInfoRecord = useCallback((type: InfoRecord[\"type\"], content: string) => {\n const record: InfoRecord = {\n id: nextRecordIdRef.current++,\n timestamp: Date.now(),\n type,\n content,\n };\n setInfoRecords(prev => [...prev, record]);\n }, []);\n\n const tcpOptions = useMemo(() => {\n return props.role === \"host\"\n ? ({ role: \"host\", localName: props.localName } as const)\n : ({\n role: \"client\",\n localName: props.localName,\n hostIp: props.hostIp,\n tcpPort: props.tcpPort,\n hostName: props.hostName,\n } as const);\n }, [\n props.role,\n props.localName,\n props.role === \"client\" ? props.hostIp : \"\",\n props.role === \"client\" ? props.tcpPort : 0,\n props.role === \"client\" ? props.hostName : \"\",\n ]);\n\n const tcp = useTcpSync(tcpOptions);\n\n const broadcasterOptions = useMemo(() => {\n return props.role === \"host\"\n ? ({\n enabled: true,\n hostName: props.localName,\n roomName,\n tcpPort: tcp.listenPort,\n } as const)\n : ({ enabled: false } as const);\n }, [props.role, props.localName, roomName, tcp.listenPort]);\n\n useBroadcaster(broadcasterOptions);\n\n const localActivity = useActivityMonitor();\n\n // Use peers array from TCP sync for multi-peer support\n const peers = tcp.peers;\n const firstPeerName = peers.length > 0 ? peers[0].name : undefined;\n const prevPeersRef = useRef<typeof peers>([]);\n\n // Track peer joins and leaves\n useEffect(() => {\n const prevPeers = prevPeersRef.current;\n const prevNames = new Set(prevPeers.map(p => p.name));\n const currentNames = new Set(peers.map(p => p.name));\n\n // Check for new peers\n peers.forEach(p => {\n if (!prevNames.has(p.name)) {\n addInfoRecord(\"join\", `${p.name} joined`);\n }\n });\n\n // Check for left peers\n prevPeers.forEach(p => {\n if (!currentNames.has(p.name)) {\n addInfoRecord(\"leave\", `${p.name} left`);\n }\n });\n\n prevPeersRef.current = [...peers];\n }, [peers, addInfoRecord]);\n\n const onToggleAi = useCallback(() => setShowAi((v) => !v), []);\n const onCloseAi = useCallback(() => setShowAi(false), []);\n\n const sessionStartAtRef = useRef<number>(Date.now());\n const connectedStartAtRef = useRef<number | null>(null);\n const connectedTotalMsRef = useRef<number>(0);\n const keyPressesRef = useRef<number>(0);\n const useGlobalKeyboardRef = useRef<boolean>(false);\n\n const countKeyPress = useCallback(() => {\n keyPressesRef.current += 1;\n }, []);\n\n // Count terminal key presses unless global keyboard listener is active.\n useInput(\n () => {\n if (!useGlobalKeyboardRef.current) countKeyPress();\n },\n { isActive: true }\n );\n\n // If `TERMBUDDY_ACTIVITY_SOURCE=keyboard`, count global key presses.\n useEffect(() => {\n const raw = process.env.TERMBUDDY_ACTIVITY_SOURCE ?? \"ink\";\n const source = raw === \"xinput\" ? \"keyboard\" : raw;\n if (source !== \"keyboard\") return;\n\n let cancelled = false;\n let unsub: (() => void) | null = null;\n void (async () => {\n const ok = await ensureGlobalKeyboard();\n if (cancelled) return;\n if (!ok) return;\n useGlobalKeyboardRef.current = true;\n unsub = subscribeGlobalKeydown(countKeyPress);\n })();\n\n return () => {\n cancelled = true;\n unsub?.();\n useGlobalKeyboardRef.current = false;\n };\n }, [countKeyPress]);\n\n // Track connected time.\n useEffect(() => {\n if (tcp.status === \"connected\") {\n if (connectedStartAtRef.current === null) {\n connectedStartAtRef.current = Date.now();\n }\n return;\n }\n\n if (connectedStartAtRef.current !== null) {\n connectedTotalMsRef.current += Date.now() - connectedStartAtRef.current;\n connectedStartAtRef.current = null;\n }\n }, [tcp.status]);\n\n const finishAndLeave = useCallback(() => {\n const endedAt = Date.now();\n let connectedDurationMs = connectedTotalMsRef.current;\n if (connectedStartAtRef.current !== null) {\n connectedDurationMs += endedAt - connectedStartAtRef.current;\n }\n\n const stats: LeaveStats = {\n keyPresses: keyPressesRef.current,\n sessionDurationMs: endedAt - sessionStartAtRef.current,\n connectedDurationMs,\n startedAt: sessionStartAtRef.current,\n endedAt,\n peerName: firstPeerName,\n };\n props.onLeave(stats);\n }, [props, firstPeerName]);\n\n const startCountdown = useCallback((minutes: number) => {\n const totalSeconds = Math.max(1, Math.floor(minutes * 60));\n const endsAt = Date.now() + totalSeconds * 1000;\n const type: CountdownClockType =\n minutes <= 10 ? \"SHORT\" : minutes <= 30 ? \"MEDIUM\" : \"LONG\";\n setCountdown({\n minutes,\n totalSeconds,\n endsAt,\n remainingSeconds: totalSeconds,\n type,\n });\n addInfoRecord(\"countdown\", `Started ${minutes}min countdown`);\n }, [addInfoRecord]);\n\n const nextShotIdRef = useRef<number>(1);\n const shotQueueRef = useRef<\n Array<{ kind: ProjectileKind; direction: ProjectileDirection }>\n >([]);\n\n const pumpShotQueue = useCallback(() => {\n setShots((prev) => {\n if (prev.length >= 1) return prev;\n const next = shotQueueRef.current.shift();\n if (!next) return prev;\n const id = nextShotIdRef.current++;\n return [...prev, { id, kind: next.kind, direction: next.direction }];\n });\n }, []);\n\n useEffect(() => {\n if (shots.length === 0) pumpShotQueue();\n }, [shots.length, pumpShotQueue]);\n\n const throwProjectile = useCallback(\n (kind: ProjectileKind, direction: ProjectileDirection) => {\n shotQueueRef.current.push({ kind, direction });\n pumpShotQueue();\n if (tcp.status === \"connected\") tcp.sendProjectile(kind, direction);\n addInfoRecord(\"projectile\", `Threw ${kind}`);\n },\n [pumpShotQueue, tcp, addInfoRecord]\n );\n\n // Helper to show bubble at specific index\n const showBubbleAtIndex = useCallback(\n (index: number, text: string, durationMs: number) => {\n setPeerBubbles(prev => {\n const next = [...prev];\n next[index] = text;\n return next;\n });\n\n const prevTimer = bubbleTimersRef.current[index];\n if (prevTimer) clearTimeout(prevTimer);\n\n const handle = setTimeout(() => {\n setPeerBubbles(prev => {\n const next = [...prev];\n next[index] = null;\n return next;\n });\n bubbleTimersRef.current[index] = null;\n }, durationMs);\n\n bubbleTimersRef.current[index] = handle;\n },\n []\n );\n\n // Called by AI tool - bubble appears on LOCAL user (speaker), not target\n const showBubble = useCallback(\n (args: { text: string; target: number; durationMs: number }) => {\n const text = args.text.trim();\n if (!text) return;\n const durationMs = Math.max(300, Math.min(15_000, Math.floor(args.durationMs)));\n\n // Bubble appears on LOCAL user (index 0) - they are the one speaking\n showBubbleAtIndex(0, text, durationMs);\n\n // Send bubble to other peers via TCP\n if (tcp.status === \"connected\") {\n tcp.sendBubble(text, durationMs);\n }\n\n addInfoRecord(\"bubble\", `To No.${args.target}: \"${text}\"`);\n },\n [showBubbleAtIndex, tcp, addInfoRecord]\n );\n\n // Handle remote bubbles - find sender's position and show bubble there\n useEffect(() => {\n const handleRemoteBubble = (text: string, durationMs: number, senderName: string) => {\n // Find the sender's index in peers array (peers are at index 1-3)\n const peerIndex = peers.findIndex(p => p.name === senderName);\n if (peerIndex !== -1) {\n // peerIndex 0 -> bubbleIndex 1, peerIndex 1 -> bubbleIndex 2, etc.\n showBubbleAtIndex(peerIndex + 1, text, durationMs);\n addInfoRecord(\"bubble\", `From ${senderName}: \"${text}\"`);\n }\n };\n\n tcp.setOnRemoteBubble(handleRemoteBubble);\n }, [tcp, peers, showBubbleAtIndex, addInfoRecord]);\n\n useInput(\n (input, key) => {\n if (input === \"q\") finishAndLeave();\n if (input === \"/\" && !key.ctrl && !key.meta) onToggleAi();\n },\n { isActive: !showAi }\n );\n\n useInput(\n (input) => {\n if (input === \"x\") setCountdown(null);\n },\n { isActive: !showAi && countdown !== null }\n );\n\n const localState = localActivity.state;\n const localLabel =\n props.role === \"host\"\n ? `${props.localName} (Host)`\n : `${props.localName} (Client)`;\n\n // Sync local activity state to peer.\n useEffect(() => {\n if (tcp.status !== \"connected\") return;\n tcp.sendStatus(localState);\n }, [localState, tcp.status, tcp.sendStatus]);\n\n // Handle incoming projectiles from peer (flip direction)\n useEffect(() => {\n const handleRemoteProjectile = (kind: ProjectileKind, direction: ProjectileDirection, _senderName?: string) => {\n const flippedDirection: ProjectileDirection =\n direction === \"LEFT_TO_RIGHT\" ? \"RIGHT_TO_LEFT\" : \"LEFT_TO_RIGHT\";\n shotQueueRef.current.push({ kind, direction: flippedDirection });\n pumpShotQueue();\n };\n\n tcp.setOnRemoteProjectile(handleRemoteProjectile);\n }, [tcp, pumpShotQueue]);\n\n useEffect(() => {\n if (!countdown) return;\n const endsAt = countdown.endsAt;\n const handle = setInterval(() => {\n const remaining = Math.max(0, Math.ceil((endsAt - Date.now()) / 1000));\n setCountdown((prev) => {\n if (!prev) return prev;\n if (prev.endsAt !== endsAt) return prev;\n if (remaining <= 0) return null;\n if (prev.remainingSeconds === remaining) return prev;\n return { ...prev, remainingSeconds: remaining };\n });\n }, 250);\n return () => clearInterval(handle);\n }, [countdown?.endsAt]);\n\n useEffect(() => {\n return () => {\n bubbleTimersRef.current.forEach(timer => {\n if (timer) clearTimeout(timer);\n });\n };\n }, []);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <StatusHeader\n role={props.role}\n status={tcp.status}\n hostIp={props.role === \"client\" ? props.hostIp : undefined}\n tcpPort={props.role === \"client\" ? props.tcpPort : tcp.listenPort}\n peerCount={peers.length}\n />\n\n {/* Main Stage: 2x2 Grid Layout for 4 Users */}\n <Box flexDirection=\"column\" marginTop={1}>\n {/* Projectile Area at Top */}\n <Box flexDirection=\"column\" width=\"100%\" alignItems=\"center\" marginBottom={1}>\n {shots.map((s) => (\n <ProjectileThrowSprite\n key={String(s.id)}\n kind={s.kind}\n direction={s.direction}\n shotId={s.id}\n width={50}\n onDone={() =>\n setShots((prev) => prev.filter((x) => x.id !== s.id))\n }\n />\n ))}\n {shots.length === 0 ? <Box height={1} /> : null}\n </Box>\n\n {/* Countdown Timer (shared) */}\n {countdown ? (\n <Box justifyContent=\"center\" marginBottom={1}>\n <Box flexDirection=\"column\" alignItems=\"center\">\n <Text color=\"gray\">{formatMMSS(countdown.remainingSeconds)}</Text>\n <CountdownClockSprite\n variant=\"COMPACT\"\n type={countdown.type}\n minutes={countdown.minutes}\n totalSeconds={countdown.totalSeconds}\n remainingSeconds={countdown.remainingSeconds}\n showLabel={false}\n />\n </Box>\n </Box>\n ) : null}\n\n {/* 2x2 Grid of Users */}\n <Box flexDirection=\"column\" alignItems=\"center\">\n {/* Row 1: User 1 (Local) and User 2 */}\n <Box flexDirection=\"row\" justifyContent=\"center\" gap={4}>\n {/* No.1 - Local User */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n <Text color=\"cyan\" bold>No.1 {props.localName}</Text>\n <BuddyAvatar state={localState} marginTop={0} bubbleText={peerBubbles[0] ?? null} />\n </Box>\n\n {/* No.2 - First Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 1 ? (\n <>\n <Text color=\"magenta\" bold>No.2 {peers[0].name}</Text>\n <BuddyAvatar state={peers[0].state} marginTop={0} bubbleText={peerBubbles[1] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.2 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n </Box>\n\n {/* Row 2: User 3 and User 4 */}\n <Box flexDirection=\"row\" justifyContent=\"center\" gap={4} marginTop={1}>\n {/* No.3 - Second Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 2 ? (\n <>\n <Text color=\"magenta\" bold>No.3 {peers[1].name}</Text>\n <BuddyAvatar state={peers[1].state} marginTop={0} bubbleText={peerBubbles[2] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.3 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n\n {/* No.4 - Third Peer */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={18}>\n {peers.length >= 3 ? (\n <>\n <Text color=\"magenta\" bold>No.4 {peers[2].name}</Text>\n <BuddyAvatar state={peers[2].state} marginTop={0} bubbleText={peerBubbles[3] ?? null} />\n </>\n ) : (\n <>\n <Text color=\"gray\">No.4 (Empty)</Text>\n <BuddyAvatar state=\"OFFLINE\" marginTop={0} />\n </>\n )}\n </Box>\n </Box>\n </Box>\n </Box>\n\n {/* Footer Instructions (Hide when AI is open to save space) */}\n {!showAi ? (\n <Box marginTop={1} justifyContent=\"center\">\n <Text color=\"gray\">\n 按 <Text color=\"cyan\">/</Text> 召唤 AI Console,按{\" \"}\n <Text color=\"cyan\">q</Text> 结束本次陪伴。\n {countdown ? (\n <>\n {\" \"}\n <Text color=\"gray\">\n (倒计时中:按 <Text color=\"cyan\">x</Text> 取消)\n </Text>\n </>\n ) : null}\n </Text>\n </Box>\n ) : null}\n\n {/* AI Console and Info Panel (side by side) */}\n {showAi ? (\n <Box\n marginTop={1}\n width=\"100%\"\n flexDirection=\"row\"\n justifyContent=\"center\"\n gap={2}\n >\n <Box width={48}>\n <AiConsole\n onClose={onCloseAi}\n onStartCountdown={startCountdown}\n onShowBubble={showBubble}\n onThrowProjectile={throwProjectile}\n localName={props.localName}\n peerName={\n firstPeerName ??\n (props.role === \"client\" ? props.hostName : undefined) ??\n \"Buddy\"\n }\n peers={peers}\n />\n </Box>\n <Box width={32}>\n <InfoPanel records={infoRecords} maxRecords={6} />\n </Box>\n </Box>\n ) : null}\n </Box>\n );\n}\n","export { MainMenu } from \"./MainMenu.js\";\nexport { NicknamePrompt } from \"./NicknamePrompt.js\";\nexport { RoomScanner } from \"./RoomScanner.js\";\nexport { LeavePage } from \"./LeavePage.js\";\nexport { Session } from \"./Session.js\";\n","import React, { useCallback, useMemo, useState } from \"react\";\nimport os from \"node:os\";\nimport { useApp } from \"ink\";\nimport {\n LeavePage,\n MainMenu,\n NicknamePrompt,\n RoomScanner,\n Session,\n} from \"../page/index.js\";\nimport type { LeaveStats } from \"../types.js\";\n\ntype View =\n | { name: \"NICKNAME\" }\n | { name: \"MENU\" }\n | { name: \"SCANNING\" }\n | { name: \"LEAVE\"; stats: LeaveStats }\n | { name: \"SESSION\"; role: \"host\" }\n | {\n name: \"SESSION\";\n role: \"client\";\n hostIp: string;\n tcpPort: number;\n roomName: string;\n hostName: string;\n };\n\nexport function App() {\n const { exit } = useApp();\n const [view, setView] = useState<View>({ name: \"NICKNAME\" });\n const [nickname, setNickname] = useState<string | null>(null);\n\n const localName = useMemo(() => nickname ?? os.hostname(), [nickname]);\n\n const goMenu = useCallback(() => setView({ name: \"MENU\" }), []);\n\n if (view.name === \"NICKNAME\") {\n return (\n <NicknamePrompt\n onExit={() => exit()}\n onSubmit={(name) => {\n setNickname(name);\n setView({ name: \"MENU\" });\n }}\n />\n );\n }\n\n if (view.name === \"MENU\") {\n return (\n <MainMenu\n onHost={() => setView({ name: \"SESSION\", role: \"host\" })}\n onJoin={() => setView({ name: \"SCANNING\" })}\n onExit={() => exit()}\n />\n );\n }\n\n if (view.name === \"LEAVE\") {\n return (\n <LeavePage stats={view.stats} onBack={goMenu} onExit={() => exit()} />\n );\n }\n\n if (view.name === \"SCANNING\") {\n return (\n <RoomScanner\n onBack={goMenu}\n onExit={() => exit()}\n onSelectRoom={(room) =>\n setView({\n name: \"SESSION\",\n role: \"client\",\n hostIp: room.ip,\n tcpPort: room.tcpPort,\n roomName: room.roomName,\n hostName: room.hostName,\n })\n }\n />\n );\n }\n\n if (view.name === \"SESSION\" && view.role === \"host\") {\n return (\n <Session\n localName={localName}\n role=\"host\"\n onLeave={(stats) => setView({ name: \"LEAVE\", stats })}\n />\n );\n }\n\n return (\n <Session\n localName={localName}\n role=\"client\"\n onLeave={(stats) => setView({ name: \"LEAVE\", stats })}\n hostIp={view.hostIp}\n tcpPort={view.tcpPort}\n roomName={view.roomName}\n hostName={view.hostName}\n />\n );\n}\n","export { App } from \"./App.js\";\n","process.env.NODE_ENV ??= 'production';\n\nconst React = await import('react');\nconst {render} = await import('ink');\nconst {App} = await import('./app/index.js');\n\nrender(React.createElement(App));\n"],"mappings":"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","names":["Box","Text","useInput","jsx","jsxs","useState","useInput","tool","tool","tool","useCallback","useEffect","useRef","useState","os","useEffect","useEffect","useState","dgram","useCallback","useEffect","useRef","useState","socket","useMemo","Box","Text","useInput","jsx","jsxs","useMemo","Box","Text","useInput","jsx","jsxs","useEffect","useMemo","useState","Box","Text","useInput","jsx","jsxs","useMemo","Box","Text","jsx","Box","Text","jsx","jsxs","useMemo","Box","Text","jsx","jsxs","face","useEffect","useMemo","useState","Box","Text","jsx","jsxs","clamp01","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","Box","Text","useInput","Fragment","jsx","jsxs","useCallback","useMemo","useState","os","jsx","React","App"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@three333/termbuddy",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,6 +42,8 @@ export function useTcpSync(options: Options): {
42
42
  sendStatus: (state: ActivityState) => void;
43
43
  sendProjectile: (kind: ProjectileKind, direction: ProjectileDirection) => void;
44
44
  setOnRemoteProjectile: (callback: (kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) => void;
45
+ sendBubble: (text: string, durationMs: number) => void;
46
+ setOnRemoteBubble: (callback: (text: string, durationMs: number, senderName: string) => void) => void;
45
47
  } {
46
48
  const [status, setStatus] = useState<ConnectionStatus>(
47
49
  options.role === "host" ? "waiting" : "connecting"
@@ -50,6 +52,7 @@ export function useTcpSync(options: Options): {
50
52
  const [peers, setPeers] = useState<Peer[]>([]);
51
53
 
52
54
  const onRemoteProjectileRef = useRef<((kind: ProjectileKind, direction: ProjectileDirection, senderName?: string) => void) | undefined>(undefined);
55
+ const onRemoteBubbleRef = useRef<((text: string, durationMs: number, senderName: string) => void) | undefined>(undefined);
53
56
 
54
57
  // For host: map of all peer connections
55
58
  const peerConnectionsRef = useRef<Map<string, PeerConnection>>(new Map());
@@ -180,6 +183,13 @@ export function useTcpSync(options: Options): {
180
183
  // Broadcast to other peers
181
184
  broadcastPacket({ ...packet, senderName: conn.name }, peerId);
182
185
  }
186
+
187
+ if (packet.type === "bubble") {
188
+ // Call local handler
189
+ onRemoteBubbleRef.current?.(packet.text, packet.durationMs, conn.name);
190
+ // Broadcast to other peers
191
+ broadcastPacket({ ...packet, senderName: conn.name }, peerId);
192
+ }
183
193
  } catch {
184
194
  // ignore parse errors
185
195
  }
@@ -277,6 +287,10 @@ export function useTcpSync(options: Options): {
277
287
  onRemoteProjectileRef.current?.(packet.kind, packet.direction, packet.senderName);
278
288
  }
279
289
 
290
+ if (packet.type === "bubble") {
291
+ onRemoteBubbleRef.current?.(packet.text, packet.durationMs, packet.senderName);
292
+ }
293
+
280
294
  if (packet.type === "peer_joined") {
281
295
  setPeers((prev) => {
282
296
  const exists = prev.some((p) => p.name === packet.peerName);
@@ -399,6 +413,22 @@ export function useTcpSync(options: Options): {
399
413
  onRemoteProjectileRef.current = callback;
400
414
  }, []);
401
415
 
416
+ // Send bubble to all peers
417
+ const sendBubble = useCallback((text: string, durationMs: number) => {
418
+ if (options.role === "host") {
419
+ broadcastPacket({ type: "bubble", text, durationMs, senderName: options.localName, sentAt: Date.now() });
420
+ } else {
421
+ const socket = clientSocketRef.current;
422
+ if (socket && !socket.destroyed) {
423
+ writePacket(socket, { type: "bubble", text, durationMs, senderName: options.localName, sentAt: Date.now() });
424
+ }
425
+ }
426
+ }, [options, broadcastPacket]);
427
+
428
+ const setOnRemoteBubble = useCallback((callback: (text: string, durationMs: number, senderName: string) => void) => {
429
+ onRemoteBubbleRef.current = callback;
430
+ }, []);
431
+
402
432
  // Backward compatibility: first peer
403
433
  const firstPeer = peers[0];
404
434
  const peerName = firstPeer?.name;
@@ -412,6 +442,8 @@ export function useTcpSync(options: Options): {
412
442
  remoteState,
413
443
  sendStatus,
414
444
  sendProjectile,
415
- setOnRemoteProjectile
445
+ setOnRemoteProjectile,
446
+ sendBubble,
447
+ setOnRemoteBubble
416
448
  };
417
449
  }
@@ -264,40 +264,67 @@ export function Session(
264
264
  [pumpShotQueue, tcp, addInfoRecord]
265
265
  );
266
266
 
267
- const showBubble = useCallback(
268
- (args: { text: string; target: number; durationMs: number }) => {
269
- const text = args.text.trim();
270
- if (!text) return;
271
- const durationMs = Math.max(300, Math.min(15_000, Math.floor(args.durationMs)));
272
-
273
- // target is 1-4 (No.1 to No.4), convert to 0-3 index
274
- const targetIndex = Math.max(0, Math.min(3, args.target - 1));
275
-
267
+ // Helper to show bubble at specific index
268
+ const showBubbleAtIndex = useCallback(
269
+ (index: number, text: string, durationMs: number) => {
276
270
  setPeerBubbles(prev => {
277
271
  const next = [...prev];
278
- next[targetIndex] = text;
272
+ next[index] = text;
279
273
  return next;
280
274
  });
281
275
 
282
- const prevTimer = bubbleTimersRef.current[targetIndex];
276
+ const prevTimer = bubbleTimersRef.current[index];
283
277
  if (prevTimer) clearTimeout(prevTimer);
284
278
 
285
279
  const handle = setTimeout(() => {
286
280
  setPeerBubbles(prev => {
287
281
  const next = [...prev];
288
- next[targetIndex] = null;
282
+ next[index] = null;
289
283
  return next;
290
284
  });
291
- bubbleTimersRef.current[targetIndex] = null;
285
+ bubbleTimersRef.current[index] = null;
292
286
  }, durationMs);
293
287
 
294
- bubbleTimersRef.current[targetIndex] = handle;
288
+ bubbleTimersRef.current[index] = handle;
289
+ },
290
+ []
291
+ );
295
292
 
296
- addInfoRecord("bubble", `No.${args.target}: "${text}"`);
293
+ // Called by AI tool - bubble appears on LOCAL user (speaker), not target
294
+ const showBubble = useCallback(
295
+ (args: { text: string; target: number; durationMs: number }) => {
296
+ const text = args.text.trim();
297
+ if (!text) return;
298
+ const durationMs = Math.max(300, Math.min(15_000, Math.floor(args.durationMs)));
299
+
300
+ // Bubble appears on LOCAL user (index 0) - they are the one speaking
301
+ showBubbleAtIndex(0, text, durationMs);
302
+
303
+ // Send bubble to other peers via TCP
304
+ if (tcp.status === "connected") {
305
+ tcp.sendBubble(text, durationMs);
306
+ }
307
+
308
+ addInfoRecord("bubble", `To No.${args.target}: "${text}"`);
297
309
  },
298
- [addInfoRecord]
310
+ [showBubbleAtIndex, tcp, addInfoRecord]
299
311
  );
300
312
 
313
+ // Handle remote bubbles - find sender's position and show bubble there
314
+ useEffect(() => {
315
+ const handleRemoteBubble = (text: string, durationMs: number, senderName: string) => {
316
+ // Find the sender's index in peers array (peers are at index 1-3)
317
+ const peerIndex = peers.findIndex(p => p.name === senderName);
318
+ if (peerIndex !== -1) {
319
+ // peerIndex 0 -> bubbleIndex 1, peerIndex 1 -> bubbleIndex 2, etc.
320
+ showBubbleAtIndex(peerIndex + 1, text, durationMs);
321
+ addInfoRecord("bubble", `From ${senderName}: "${text}"`);
322
+ }
323
+ };
324
+
325
+ tcp.setOnRemoteBubble(handleRemoteBubble);
326
+ }, [tcp, peers, showBubbleAtIndex, addInfoRecord]);
327
+
301
328
  useInput(
302
329
  (input, key) => {
303
330
  if (input === "q") finishAndLeave();
package/src/protocol.ts CHANGED
@@ -18,6 +18,7 @@ export type TcpPacket =
18
18
  | {type: 'ping'; sentAt: number}
19
19
  | {type: 'pong'; sentAt: number}
20
20
  | {type: 'projectile'; kind: ProjectileKind; direction: ProjectileDirection; senderName?: string; sentAt: number}
21
+ | {type: 'bubble'; text: string; durationMs: number; senderName: string; sentAt: number}
21
22
  | {type: 'peer_joined'; peerName: string; sentAt: number}
22
23
  | {type: 'peer_left'; peerName: string; sentAt: number};
23
24