@three333/termbuddy 0.1.0 → 0.1.1

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.
Files changed (38) hide show
  1. package/dist/cli.js +1097 -260
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +3 -2
  4. package/pnpm-workspace.yaml +2 -0
  5. package/src/app/App.tsx +94 -53
  6. package/src/app/index.ts +1 -2
  7. package/src/components/AiConsole.tsx +171 -73
  8. package/src/components/StatusHeader.tsx +36 -36
  9. package/src/components/index.ts +8 -4
  10. package/src/components/sprite/BuddyAvatar.tsx +49 -0
  11. package/src/components/sprite/CountdownClockSprite.tsx +146 -0
  12. package/src/components/sprite/ProjectileThrowSprite.tsx +86 -0
  13. package/src/components/tool/createCountdownTool.ts +32 -0
  14. package/src/components/tool/createInteractionTool.ts +67 -0
  15. package/src/components/tool/createSessionInfoTool.ts +29 -0
  16. package/src/components/tool/index.ts +4 -0
  17. package/src/hooks/globalKeyboard.ts +146 -0
  18. package/src/hooks/index.ts +5 -7
  19. package/src/hooks/useActivityMonitor.ts +61 -24
  20. package/src/hooks/useAiAgent.ts +200 -165
  21. package/src/hooks/useBroadcaster.ts +55 -47
  22. package/src/hooks/useScanner.ts +59 -55
  23. package/src/hooks/useTcpSync.ts +166 -145
  24. package/src/net/broadcast.ts +21 -21
  25. package/src/net/index.ts +1 -2
  26. package/src/page/LeavePage.tsx +85 -0
  27. package/src/{views → page}/MainMenu.tsx +32 -28
  28. package/src/page/NicknamePrompt.tsx +62 -0
  29. package/src/{views → page}/RoomScanner.tsx +4 -1
  30. package/src/page/Session.tsx +364 -0
  31. package/src/page/index.ts +5 -0
  32. package/src/storage/apiKey.ts +36 -0
  33. package/src/types.ts +8 -0
  34. package/src/components/AvatarDisplay.tsx +0 -18
  35. package/src/components/BuddyAvatar.tsx +0 -32
  36. package/src/hooks/useCountdown.ts +0 -42
  37. package/src/views/Session.tsx +0 -127
  38. package/src/views/index.ts +0 -4
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/views/MainMenu.tsx","../src/hooks/useActivityMonitor.ts","../src/hooks/useAiAgent.ts","../src/constants.ts","../src/net/broadcast.ts","../src/net/index.ts","../src/hooks/useBroadcaster.ts","../src/hooks/useCountdown.ts","../src/hooks/useScanner.ts","../src/hooks/useTcpSync.ts","../src/hooks/index.ts","../src/views/RoomScanner.tsx","../src/components/AiConsole.tsx","../src/components/AvatarDisplay.tsx","../src/components/BuddyAvatar.tsx","../src/components/StatusHeader.tsx","../src/components/index.ts","../src/views/Session.tsx","../src/views/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: {onHost: () => void; onJoin: () => void; onExit: () => void}) {\n\tuseInput((input, key) => {\n\t\tif (key.escape || input === 'q') props.onExit();\n\t\tif (input === '1') props.onHost();\n\t\tif (input === '2') props.onJoin();\n\t});\n\n\treturn (\n\t\t<Box flexDirection=\"column\" padding={1}>\n\t\t\t<Text>\n\t\t\t\t{String.raw`\n████████╗███████╗██████╗ ███╗ ███╗██████╗ ██╗ ██╗██████╗ ██████╗ ██╗ ██╗\n╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔══██╗██║ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝\n ██║ █████╗ ██████╔╝██╔████╔██║██████╔╝██║ ██║██║ ██║██║ ██║ ╚████╔╝ \n ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══██╗██║ ██║██║ ██║██║ ██║ ╚██╔╝ \n ██║ ███████╗██║ ██║██║ ╚═╝ ██║██████╔╝╚██████╔╝██████╔╝██████╔╝ ██║ \n ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ \n`}\n\t\t\t</Text>\n\t\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t\t<Text>Terminal Body Doubling — 极简 / 极客 / 私密</Text>\n\t\t\t\t<Text> </Text>\n\t\t\t\t<Text>\n\t\t\t\t\t<Text color=\"cyan\">[1]</Text> 建房 (Host)\n\t\t\t\t</Text>\n\t\t\t\t<Text>\n\t\t\t\t\t<Text color=\"cyan\">[2]</Text> 加入 (Join)\n\t\t\t\t</Text>\n\t\t\t\t<Text>\n\t\t\t\t\t<Text color=\"cyan\">[q]</Text> 退出\n\t\t\t\t</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t);\n}\n","import {useEffect, useRef, useState} from 'react';\nimport {useInput} from 'ink';\nimport type {ActivityState} from '../protocol.js';\n\nexport function useActivityMonitor(options?: {idleAfterMs?: number}): {state: ActivityState} {\n\tconst idleAfterMs = options?.idleAfterMs ?? 1500;\n\tconst [state, setState] = useState<ActivityState>('IDLE');\n\n\tconst lastActivityRef = useRef<number>(Date.now());\n\n\tuseInput(() => {\n\t\tlastActivityRef.current = Date.now();\n\t\tsetState('TYPING');\n\t});\n\n\tuseEffect(() => {\n\t\tconst id = setInterval(() => {\n\t\t\tconst delta = Date.now() - lastActivityRef.current;\n\t\t\tif (delta >= idleAfterMs) setState('IDLE');\n\t\t}, 200);\n\t\treturn () => clearInterval(id);\n\t}, [idleAfterMs]);\n\n\treturn {state};\n}\n","import {useCallback, useEffect, useRef, useState} from 'react';\nimport {createAgent, initChatModel, tool} from 'langchain';\n\ntype LineKind = 'user' | 'ai' | 'system';\nexport type AiLine = {kind: LineKind; text: string; at: number};\n\nfunction contentToText(content: unknown): string {\n\tif (typeof content === 'string') return content;\n\tif (!content) return '';\n\tif (Array.isArray(content)) {\n\t\treturn content\n\t\t\t.map((part) => {\n\t\t\t\tif (typeof part === 'string') return part;\n\t\t\t\tif (typeof part === 'object' && part && 'text' in part) return String((part as any).text ?? '');\n\t\t\t\treturn '';\n\t\t\t})\n\t\t\t.join('');\n\t}\n\tif (typeof content === 'object' && 'text' in (content as any)) return String((content as any).text ?? '');\n\treturn String(content);\n}\n\nfunction lastAiText(messages: unknown[]): string | null {\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst m: any = messages[i];\n\t\tconst type = typeof m?.getType === 'function' ? m.getType() : typeof m?._getType === 'function' ? m._getType() : m?.type;\n\t\tif (type === 'ai') {\n\t\t\tconst t = contentToText(m?.content);\n\t\t\treturn t || '';\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction createSystemPrompt(context: {localName: string; peerName: string}) {\n\treturn [\n\t\t'你是 TermBuddy 里的“壳中幽灵 (Ghost in the Shell)”。',\n\t\t'默认隐形;被 / 唤醒时出现。风格:极简、干练、少废话。',\n\t\t'你可以使用工具来操控应用功能(例如倒计时)。',\n\t\t'如果用户提到“倒计时/专注/计时/countdown”,优先调用 start_countdown。',\n\t\t`当前上下文:我叫 ${context.localName};同桌叫 ${context.peerName}。`\n\t].join('\\n');\n}\n\nexport function useAiAgent(options: {\n\tlocalName: string;\n\tpeerName: string;\n\tonStartCountdown?: (minutes: number) => void;\n}) {\n\tconst [lines, setLines] = useState<AiLine[]>([]);\n\tconst [busy, setBusy] = useState(false);\n\n\tconst agentRef = useRef<Awaited<ReturnType<typeof createAgent>> | null>(null);\n\tconst agentInitRef = useRef<Promise<Awaited<ReturnType<typeof createAgent>>> | null>(null);\n\tconst stateRef = useRef<{messages: unknown[]}>({messages: []});\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tconst append = useCallback((line: AiLine) => {\n\t\tsetLines((prev) => [...prev, line]);\n\t}, []);\n\n\tconst updateLine = useCallback((at: number, text: string) => {\n\t\tsetLines((prev) => {\n\t\t\tconst idx = prev.findIndex((l) => l.at === at);\n\t\t\tif (idx === -1) return prev;\n\t\t\tconst next = [...prev];\n\t\t\tnext[idx] = {...next[idx], text};\n\t\t\treturn next;\n\t\t});\n\t}, []);\n\n\tconst ensureAgent = useCallback(async () => {\n\t\tif (agentRef.current) return agentRef.current;\n\t\tagentInitRef.current ??= (async () => {\n\t\t\tconst startCountdown = tool(\n\t\t\t\tasync (input: {minutes: number}) => {\n\t\t\t\t\tconst minutes = Number(input.minutes);\n\t\t\t\t\tif (!Number.isFinite(minutes) || minutes <= 0) return '倒计时分钟数无效。';\n\t\t\t\t\toptions.onStartCountdown?.(minutes);\n\t\t\t\t\treturn `已开始倒计时 ${minutes} 分钟。`;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'start_countdown',\n\t\t\t\t\tdescription: '开始一个专注倒计时(分钟)。',\n\t\t\t\t\tschema: {\n\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tminutes: {type: 'integer', minimum: 1, maximum: 180, description: '倒计时分钟数'}\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: ['minutes'],\n\t\t\t\t\t\tadditionalProperties: false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tconst sessionInfo = tool(\n\t\t\t\tasync () => {\n\t\t\t\t\treturn JSON.stringify(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlocalName: options.localName,\n\t\t\t\t\t\t\tpeerName: options.peerName\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'session_info',\n\t\t\t\t\tdescription: '获取当前会话上下文(本地昵称、同桌昵称)。',\n\t\t\t\t\tschema: {type: 'object', properties: {}, additionalProperties: false}\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tconst modelId = process.env.TERMBUDDY_MODEL ?? 'openai:gpt-4o-mini';\n\t\t\tconst llm = await initChatModel(modelId, {\n\t\t\t\ttemperature: 0.2,\n\t\t\t\tmaxTokens: 800,\n\t\t\t\ttimeout: 30_000\n\t\t\t});\n\n\t\t\treturn createAgent({\n\t\t\t\tllm,\n\t\t\t\ttools: [startCountdown, sessionInfo],\n\t\t\t\tprompt: createSystemPrompt({localName: options.localName, peerName: options.peerName}),\n\t\t\t\tname: 'ghost'\n\t\t\t});\n\t\t})();\n\n\t\tagentRef.current = await agentInitRef.current;\n\t\treturn agentRef.current;\n\t}, [options.localName, options.onStartCountdown, options.peerName]);\n\n\tconst ask = useCallback(\n\t\tasync (text: string) => {\n\t\t\tappend({kind: 'user', text: `> ${text}`, at: Date.now()});\n\n\t\t\tconst aiAt = Date.now() + 1;\n\t\t\tappend({kind: 'ai', text: '…', at: aiAt});\n\n\t\t\tabortRef.current?.abort();\n\t\t\tabortRef.current = new AbortController();\n\n\t\t\tsetBusy(true);\n\t\t\ttry {\n\t\t\t\tconst agent = await ensureAgent();\n\t\t\t\tconst stream = await agent.stream(\n\t\t\t\t\t{\n\t\t\t\t\t\tmessages: [...stateRef.current.messages, {role: 'user', content: text}]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tstreamMode: 'values',\n\t\t\t\t\t\tsignal: abortRef.current.signal\n\t\t\t\t\t} as any\n\t\t\t\t);\n\n\t\t\t\tfor await (const chunk of stream as any) {\n\t\t\t\t\tconst messages = (chunk?.messages ?? []) as unknown[];\n\t\t\t\t\tif (messages.length > 0) stateRef.current.messages = messages;\n\t\t\t\t\tconst t = lastAiText(messages);\n\t\t\t\t\tif (t !== null) updateLine(aiAt, t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\t\t\tupdateLine(aiAt, `(AI 出错)${msg}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(false);\n\t\t\t}\n\t\t},\n\t\t[append, ensureAgent, updateLine]\n\t);\n\n\tuseEffect(() => {\n\t\treturn () => abortRef.current?.abort();\n\t}, []);\n\n\treturn {lines, ask, busy};\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\treturn ip\n\t\t.split('.')\n\t\t.map((n) => Number.parseInt(n, 10))\n\t\t.reduce((acc, n) => ((acc << 8) | (n & 255)) >>> 0, 0);\n}\n\nfunction intToIpv4(n: number) {\n\treturn [24, 16, 8, 0].map((shift) => String((n >>> shift) & 255)).join('.');\n}\n\nexport function getBroadcastTargets(): string[] {\n\tconst out = new Set<string>(['255.255.255.255']);\n\n\tconst ifaces = os.networkInterfaces();\n\tfor (const entries of Object.values(ifaces)) {\n\t\tif (!entries) continue;\n\t\tfor (const e of entries) {\n\t\t\tif (e.family !== 'IPv4') continue;\n\t\t\tif (e.internal) continue;\n\t\t\tif (!e.address || !e.netmask) continue;\n\t\t\tconst ip = ipv4ToInt(e.address);\n\t\t\tconst mask = ipv4ToInt(e.netmask);\n\t\t\tconst broadcast = (ip | (~mask >>> 0)) >>> 0;\n\t\t\tout.add(intToIpv4(broadcast));\n\t\t}\n\t}\n\n\treturn [...out];\n}\n","export {getBroadcastTargets} from './broadcast.js';\n\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\t| {enabled: false}\n\t| {enabled: true; hostName: string; roomName: string; tcpPort?: number | null; intervalMs?: number};\n\nexport function useBroadcaster(options: Options) {\n\tconst depKey = options.enabled\n\t\t? `${options.hostName}|${options.roomName}|${options.tcpPort ?? ''}|${options.intervalMs ?? 1000}`\n\t\t: 'disabled';\n\n\tuseEffect(() => {\n\t\tif (!options.enabled) return;\n\t\tif (!options.tcpPort) return;\n\n\t\tconst socket = dgram.createSocket('udp4');\n\t\tsocket.on('error', () => {});\n\n\t\tsocket.bind(() => {\n\t\t\tsocket.setBroadcast(true);\n\t\t});\n\n\t\tconst targets = getBroadcastTargets();\n\n\t\tconst send = () => {\n\t\t\tconst packet: DiscoveryPacket = {\n\t\t\t\ttype: 'termbuddy_discovery',\n\t\t\t\tversion: DISCOVERY_VERSION,\n\t\t\t\thostName: options.hostName,\n\t\t\t\troomName: options.roomName,\n\t\t\t\ttcpPort: options.tcpPort!,\n\t\t\t\tsentAt: Date.now()\n\t\t\t};\n\t\t\tconst msg = Buffer.from(JSON.stringify(packet));\n\t\t\tfor (const address of targets) {\n\t\t\t\tsocket.send(msg, UDP_PORT, address);\n\t\t\t}\n\t\t};\n\n\t\tsend();\n\t\tconst id = setInterval(send, options.intervalMs ?? 1000);\n\n\t\treturn () => {\n\t\t\tclearInterval(id);\n\t\t\tsocket.close();\n\t\t};\n\t}, [depKey]);\n}\n","import {useCallback, useEffect, useRef, useState} from 'react';\n\nfunction formatMMSS(totalSeconds: number) {\n\tconst m = Math.floor(totalSeconds / 60);\n\tconst s = totalSeconds % 60;\n\treturn `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;\n}\n\nexport function useCountdown() {\n\tconst [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);\n\tconst timerRef = useRef<NodeJS.Timeout | null>(null);\n\n\tconst start = useCallback((minutes: number) => {\n\t\tconst seconds = Math.max(1, Math.floor(minutes * 60));\n\t\tsetRemainingSeconds(seconds);\n\t\tif (timerRef.current) clearInterval(timerRef.current);\n\t\ttimerRef.current = setInterval(() => {\n\t\t\tsetRemainingSeconds((prev) => {\n\t\t\t\tif (prev === null) return null;\n\t\t\t\tif (prev <= 1) return null;\n\t\t\t\treturn prev - 1;\n\t\t\t});\n\t\t}, 1000);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (remainingSeconds !== null) return;\n\t\tif (timerRef.current) clearInterval(timerRef.current);\n\t\ttimerRef.current = null;\n\t}, [remainingSeconds]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (timerRef.current) clearInterval(timerRef.current);\n\t\t};\n\t}, []);\n\n\treturn {\n\t\tstart,\n\t\tlabel: remainingSeconds === null ? null : formatMMSS(remainingSeconds)\n\t};\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\ttry {\n\t\tconst parsed = JSON.parse(msg.toString('utf8')) as DiscoveryPacket;\n\t\tif (parsed?.type !== 'termbuddy_discovery') return null;\n\t\tif (parsed?.version !== DISCOVERY_VERSION) return null;\n\t\tif (!parsed.hostName || !parsed.roomName || !parsed.tcpPort) return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function useScanner(options?: {staleAfterMs?: number}): DiscoveredRoom[] {\n\tconst staleAfterMs = options?.staleAfterMs ?? 3500;\n\tconst [rooms, setRooms] = useState<DiscoveredRoom[]>([]);\n\n\tuseEffect(() => {\n\t\tconst socket = dgram.createSocket('udp4');\n\t\tsocket.on('error', () => {});\n\n\t\tsocket.on('message', (msg, rinfo) => {\n\t\t\tconst packet = safeParse(msg);\n\t\t\tif (!packet) return;\n\n\t\t\tconst now = Date.now();\n\t\t\tsetRooms((prev) => {\n\t\t\t\tconst key = `${rinfo.address}:${packet.tcpPort}`;\n\t\t\t\tconst next = prev.filter((r) => `${r.ip}:${r.tcpPort}` !== key);\n\t\t\t\tnext.push({\n\t\t\t\t\tip: rinfo.address,\n\t\t\t\t\thostName: packet.hostName,\n\t\t\t\t\troomName: packet.roomName,\n\t\t\t\t\ttcpPort: packet.tcpPort,\n\t\t\t\t\tlastSeenAt: now\n\t\t\t\t});\n\t\t\t\treturn next;\n\t\t\t});\n\t\t});\n\n\t\tsocket.bind(UDP_PORT, () => {});\n\n\t\tconst prune = setInterval(() => {\n\t\t\tconst now = Date.now();\n\t\t\tsetRooms((prev) => prev.filter((r) => now - r.lastSeenAt <= staleAfterMs));\n\t\t}, 500);\n\n\t\treturn () => {\n\t\t\tclearInterval(prune);\n\t\t\tsocket.close();\n\t\t};\n\t}, [staleAfterMs]);\n\n\treturn rooms;\n}\n","import {useCallback, useEffect, useRef, useState} from 'react';\nimport net from 'node:net';\nimport type {ActivityState, ConnectionStatus, TcpPacket} from '../protocol.js';\nimport {TCP_DEFAULT_PORT} from '../constants.js';\n\ntype HostOptions = {role: 'host'; localName: string; port?: number};\ntype ClientOptions = {role: 'client'; localName: string; hostIp: string; tcpPort: number; hostName?: string};\ntype Options = HostOptions | ClientOptions;\n\nfunction writePacket(socket: net.Socket, packet: TcpPacket) {\n\tsocket.write(`${JSON.stringify(packet)}\\n`, 'utf8');\n}\n\nexport function useTcpSync(options: Options): {\n\tstatus: ConnectionStatus;\n\tlistenPort?: number;\n\tpeerName?: string;\n\tremoteState?: ActivityState;\n\tsendStatus: (state: ActivityState) => void;\n} {\n\tconst [status, setStatus] = useState<ConnectionStatus>(options.role === 'host' ? 'waiting' : 'connecting');\n\tconst [listenPort, setListenPort] = useState<number | undefined>(undefined);\n\tconst [peerName, setPeerName] = useState<string | undefined>(undefined);\n\tconst [remoteState, setRemoteState] = useState<ActivityState | undefined>(undefined);\n\n\tconst socketRef = useRef<net.Socket | null>(null);\n\tconst lastSeenRef = useRef<number>(Date.now());\n\tconst heartbeatRef = useRef<NodeJS.Timeout | null>(null);\n\n\tconst cleanupSocket = useCallback(() => {\n\t\tif (heartbeatRef.current) clearInterval(heartbeatRef.current);\n\t\theartbeatRef.current = null;\n\n\t\tconst s = socketRef.current;\n\t\tsocketRef.current = null;\n\t\tif (s && !s.destroyed) s.destroy();\n\t}, []);\n\n\tconst attachSocket = useCallback(\n\t\t(s: net.Socket) => {\n\t\t\tcleanupSocket();\n\t\t\tsocketRef.current = s;\n\t\t\tlastSeenRef.current = Date.now();\n\n\t\t\tsetStatus('connected');\n\t\t\tsetRemoteState('IDLE');\n\n\t\t\tlet buf = '';\n\t\t\ts.setNoDelay(true);\n\t\t\ts.setEncoding('utf8');\n\n\t\t\tconst onData = (chunk: string) => {\n\t\t\t\tbuf += chunk;\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst idx = buf.indexOf('\\n');\n\t\t\t\t\tif (idx === -1) break;\n\t\t\t\t\tconst line = buf.slice(0, idx).trim();\n\t\t\t\t\tbuf = buf.slice(idx + 1);\n\t\t\t\t\tif (!line) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst packet = JSON.parse(line) as TcpPacket;\n\t\t\t\t\t\tlastSeenRef.current = Date.now();\n\t\t\t\t\t\tif (packet.type === 'hello') {\n\t\t\t\t\t\t\tif (options.role === 'host') setPeerName(packet.clientName);\n\t\t\t\t\t\t\telse setPeerName(packet.hostName);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (packet.type === 'status') setRemoteState(packet.state);\n\t\t\t\t\t\tif (packet.type === 'ping') writePacket(s, {type: 'pong', sentAt: Date.now()});\n\t\t\t\t\t\tif (packet.type === 'pong') {\n\t\t\t\t\t\t\t// no-op\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\ts.on('data', onData);\n\t\t\ts.on('close', () => {\n\t\t\t\tsetStatus(options.role === 'host' ? 'waiting' : 'disconnected');\n\t\t\t\tsetRemoteState('OFFLINE');\n\t\t\t\tcleanupSocket();\n\t\t\t});\n\t\t\ts.on('error', () => {\n\t\t\t\tsetStatus(options.role === 'host' ? 'waiting' : 'disconnected');\n\t\t\t\tsetRemoteState('OFFLINE');\n\t\t\t});\n\n\t\t\t// Hello handshake.\n\t\t\twritePacket(s, {\n\t\t\t\ttype: 'hello',\n\t\t\t\thostName: options.role === 'host' ? options.localName : options.hostName ?? 'Host',\n\t\t\t\tclientName: options.role === 'client' ? options.localName : 'Client',\n\t\t\t\tsentAt: Date.now()\n\t\t\t});\n\n\t\t\theartbeatRef.current = setInterval(() => {\n\t\t\t\tconst sock = socketRef.current;\n\t\t\t\tif (!sock || sock.destroyed) return;\n\t\t\t\twritePacket(sock, {type: 'ping', sentAt: Date.now()});\n\t\t\t\tconst age = Date.now() - lastSeenRef.current;\n\t\t\t\tif (age > 6000) {\n\t\t\t\t\tsetStatus('disconnected');\n\t\t\t\t\tsetRemoteState('OFFLINE');\n\t\t\t\t\tcleanupSocket();\n\t\t\t\t}\n\t\t\t}, 2000);\n\t\t},\n\t\t[cleanupSocket, options]\n\t);\n\n\tuseEffect(() => {\n\t\tif (options.role === 'host') {\n\t\t\tconst server = net.createServer((socket) => {\n\t\t\t\tattachSocket(socket);\n\t\t\t});\n\n\t\t\tserver.on('error', () => {});\n\n\t\t\tserver.listen(options.port ?? TCP_DEFAULT_PORT, () => {\n\t\t\t\tconst address = server.address();\n\t\t\t\tif (address && typeof address === 'object') setListenPort(address.port);\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcleanupSocket();\n\t\t\t\tserver.close();\n\t\t\t};\n\t\t}\n\n\t\tsetStatus('connecting');\n\t\tconst socket = net.createConnection({host: options.hostIp, port: options.tcpPort}, () => {\n\t\t\tattachSocket(socket);\n\t\t});\n\t\tsocket.on('error', () => {\n\t\t\tsetStatus('disconnected');\n\t\t\tsetRemoteState('OFFLINE');\n\t\t});\n\n\t\treturn () => {\n\t\t\tsocket.destroy();\n\t\t\tcleanupSocket();\n\t\t};\n\t}, [attachSocket, cleanupSocket, options]);\n\n\tconst sendStatus = useCallback((state: ActivityState) => {\n\t\tconst socket = socketRef.current;\n\t\tif (!socket || socket.destroyed) return;\n\t\twritePacket(socket, {type: 'status', state, sentAt: Date.now()});\n\t}, []);\n\n\treturn {status, listenPort, peerName, remoteState, sendStatus};\n}\n","export {useActivityMonitor} from './useActivityMonitor.js';\nexport {useAiAgent} from './useAiAgent.js';\nexport {useBroadcaster} from './useBroadcaster.js';\nexport {useCountdown} from './useCountdown.js';\nexport {useScanner} from './useScanner.js';\nexport {useTcpSync} from './useTcpSync.js';\n\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.roomName} — {room.hostName} @ {room.ip}:{room.tcpPort}\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, useState} from 'react';\nimport {Box, Text, useInput} from 'ink';\nimport {useAiAgent} from '../hooks/index.js';\n\nexport function AiConsole(props: {\n\tonClose: () => void;\n\tonStartCountdown: (minutes: number) => void;\n\tlocalName: string;\n\tpeerName: string;\n}) {\n\tconst [input, setInput] = useState('');\n\tconst agent = useAiAgent({\n\t\tlocalName: props.localName,\n\t\tpeerName: props.peerName,\n\t\tonStartCountdown: props.onStartCountdown\n\t});\n\n\tconst helpLine = useMemo(\n\t\t() => '示例:倒计时20分钟 / countdown 20 / 问个技术问题',\n\t\t[]\n\t);\n\n\tuseInput(\n\t\t(ch, key) => {\n\t\t\tif (key.escape) {\n\t\t\t\tprops.onClose();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (key.return) {\n\t\t\t\tconst line = input.trim();\n\t\t\t\tsetInput('');\n\t\t\t\tif (!line) return;\n\t\t\t\tvoid agent.ask(line);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (key.backspace || key.delete) {\n\t\t\t\tsetInput((s) => s.slice(0, -1));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (key.ctrl || key.meta) return;\n\t\t\tif (ch) setInput((s) => s + ch);\n\t\t},\n\t\t{isActive: true}\n\t);\n\n\tconst lines = agent.lines.slice(-12);\n\n\treturn (\n\t\t<Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1} paddingY={0}>\n\t\t\t<Box justifyContent=\"space-between\">\n\t\t\t\t<Text color=\"cyan\">AI Console</Text>\n\t\t\t\t<Text color=\"gray\">{agent.busy ? 'Thinking…' : 'Esc 关闭'}</Text>\n\t\t\t</Box>\n\n\t\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t\t<Text color=\"gray\">{helpLine}</Text>\n\t\t\t</Box>\n\n\t\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t\t{lines.length === 0 ? <Text color=\"gray\">(幽灵还在壳里…)</Text> : null}\n\t\t\t\t{lines.map((l, i) => (\n\t\t\t\t\t<Text key={`${l.kind}:${l.at}:${i}`} color={l.kind === 'user' ? 'yellow' : 'white'}>\n\t\t\t\t\t\t{l.text}\n\t\t\t\t\t</Text>\n\t\t\t\t))}\n\t\t\t</Box>\n\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text color=\"green\">{'>'} </Text>\n\t\t\t\t<Text>{input}</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t);\n}\n","import React from 'react';\nimport {Box, Text} from 'ink';\nimport type {ActivityState} from '../protocol.js';\n\nconst AVATAR: Record<ActivityState, {text: string; color?: string}> = {\n\tTYPING: {text: '( >_<)===3', color: 'green'},\n\tIDLE: {text: '( -.-)Zzz', color: 'yellow'},\n\tOFFLINE: {text: '( x_x)', color: 'gray'}\n};\n\nexport function AvatarDisplay(props: {state: ActivityState}) {\n\tconst avatar = AVATAR[props.state];\n\treturn (\n\t\t<Box marginTop={1}>\n\t\t\t<Text color={avatar.color}>{avatar.text}</Text>\n\t\t</Box>\n\t);\n}\n","import React from 'react';\nimport {Box, Text} from 'ink';\nimport type {ActivityState} from '../protocol.js';\n\nconst FRAMES: Record<ActivityState, {color?: string; lines: string[]}> = {\n\tTYPING: {\n\t\tcolor: 'green',\n\t\tlines: [' /\\\\_/\\\\ ', '( >_<) ', ' /|_|\\\\\\\\ ', ' / \\\\\\\\ ']\n\t},\n\tIDLE: {\n\t\tcolor: 'yellow',\n\t\tlines: [' /\\\\_/\\\\ ', '( -.-) ', ' /|_|\\\\\\\\ ', ' / \\\\\\\\ ']\n\t},\n\tOFFLINE: {\n\t\tcolor: 'gray',\n\t\tlines: [' /\\\\_/\\\\ ', '( x_x) ', ' /|_|\\\\\\\\ ', ' / \\\\\\\\ ']\n\t}\n};\n\nexport function BuddyAvatar(props: {state: ActivityState}) {\n\tconst frame = FRAMES[props.state];\n\treturn (\n\t\t<Box flexDirection=\"column\" marginTop={1}>\n\t\t\t{frame.lines.map((line, i) => (\n\t\t\t\t<Text key={`${props.state}:${i}`} color={frame.color}>\n\t\t\t\t\t{line}\n\t\t\t\t</Text>\n\t\t\t))}\n\t\t</Box>\n\t);\n}\n\n","import React from 'react';\nimport {Box, Text} from 'ink';\nimport type {ConnectionStatus} from '../protocol.js';\n\nfunction statusText(status: ConnectionStatus) {\n\tswitch (status) {\n\t\tcase 'waiting':\n\t\t\treturn {label: 'Waiting', color: 'yellow'};\n\t\tcase 'connecting':\n\t\t\treturn {label: 'Connecting', color: 'yellow'};\n\t\tcase 'connected':\n\t\t\treturn {label: 'Connected via TCP', color: 'green'};\n\t\tcase 'disconnected':\n\t\t\treturn {label: 'Disconnected', color: 'red'};\n\t}\n}\n\nexport function StatusHeader(props: {\n\trole: 'host' | 'client';\n\tstatus: ConnectionStatus;\n\thostIp?: string;\n\ttcpPort?: number;\n\tcountdownLabel?: string | null;\n}) {\n\tconst st = statusText(props.status);\n\treturn (\n\t\t<Box justifyContent=\"space-between\">\n\t\t\t<Box>\n\t\t\t\t<Text color={st.color}>{st.label}</Text>\n\t\t\t\t{props.role === 'host' ? (\n\t\t\t\t\t<Text color=\"gray\">{props.tcpPort ? ` — TCP :${props.tcpPort}` : ''}</Text>\n\t\t\t\t) : (\n\t\t\t\t\t<Text color=\"gray\">\n\t\t\t\t\t\t{props.hostIp && props.tcpPort ? ` — ${props.hostIp}:${props.tcpPort}` : ''}\n\t\t\t\t\t</Text>\n\t\t\t\t)}\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t{props.countdownLabel ? <Text color=\"cyan\">Focus {props.countdownLabel}</Text> : <Text> </Text>}\n\t\t\t</Box>\n\t\t</Box>\n\t);\n}\n","export {AiConsole} from './AiConsole.js';\nexport {AvatarDisplay} from './AvatarDisplay.js';\nexport {BuddyAvatar} from './BuddyAvatar.js';\nexport {StatusHeader} from './StatusHeader.js';\n","import React, {useCallback, useEffect, useMemo, useState} from 'react';\nimport {Box, Text, useInput} from 'ink';\nimport type {ActivityState} from '../protocol.js';\nimport {AiConsole, BuddyAvatar, StatusHeader} from '../components/index.js';\nimport {useActivityMonitor, useBroadcaster, useCountdown, useTcpSync} from '../hooks/index.js';\n\nexport function Session(\n\tprops:\n\t\t| {role: 'host'; localName: string; onExit: () => void}\n\t\t| {\n\t\t\t\trole: 'client';\n\t\t\t\tlocalName: string;\n\t\t\t\tonExit: () => void;\n\t\t\t\thostIp: string;\n\t\t\t\ttcpPort: number;\n\t\t\t\troomName: string;\n\t\t\t\thostName: string;\n\t\t }\n) {\n\tconst roomName = useMemo(() => `${props.localName}'s Room`, [props.localName]);\n\n\tconst [showAi, setShowAi] = useState(false);\n\tconst countdown = useCountdown();\n\n\tconst tcpOptions = useMemo(() => {\n\t\treturn props.role === 'host'\n\t\t\t? ({role: 'host', localName: props.localName} as const)\n\t\t\t: ({\n\t\t\t\t\trole: 'client',\n\t\t\t\t\tlocalName: props.localName,\n\t\t\t\t\thostIp: props.hostIp,\n\t\t\t\t\ttcpPort: props.tcpPort,\n\t\t\t\t\thostName: props.hostName\n\t\t\t } as const);\n\t}, [\n\t\tprops.role,\n\t\tprops.localName,\n\t\tprops.role === 'client' ? props.hostIp : '',\n\t\tprops.role === 'client' ? props.tcpPort : 0,\n\t\tprops.role === 'client' ? props.hostName : ''\n\t]);\n\n\tconst tcp = useTcpSync(tcpOptions);\n\n\tconst broadcasterOptions = useMemo(() => {\n\t\treturn props.role === 'host'\n\t\t\t? ({\n\t\t\t\t\tenabled: true,\n\t\t\t\t\thostName: props.localName,\n\t\t\t\t\troomName,\n\t\t\t\t\ttcpPort: tcp.listenPort\n\t\t\t } as const)\n\t\t\t: ({enabled: false} as const);\n\t}, [props.role, props.localName, roomName, tcp.listenPort]);\n\n\tuseBroadcaster(broadcasterOptions);\n\n\tconst localActivity = useActivityMonitor();\n\n\tconst remoteActivity: ActivityState = tcp.remoteState ?? 'OFFLINE';\n\n\tconst onToggleAi = useCallback(() => setShowAi((v) => !v), []);\n\tconst onCloseAi = useCallback(() => setShowAi(false), []);\n\n\tuseInput(\n\t\t(input, key) => {\n\t\t\tif (input === 'q') props.onExit();\n\t\t\tif (input === '/' && !key.ctrl && !key.meta) onToggleAi();\n\t\t},\n\t\t{isActive: !showAi}\n\t);\n\n\tconst buddyName =\n\t\tprops.role === 'host'\n\t\t\t? tcp.peerName ?? 'Waiting...'\n\t\t\t: `${props.hostName ?? 'Host'} (${props.roomName ?? 'Room'})`;\n\n\tconst localState = localActivity.state;\n\tconst localLabel = props.role === 'host' ? `${props.localName} (Host)` : `${props.localName} (Client)`;\n\n\t// Sync local activity state to peer.\n\tuseEffect(() => {\n\t\tif (tcp.status !== 'connected') return;\n\t\ttcp.sendStatus(localState);\n\t}, [localState, tcp.status, tcp.sendStatus]);\n\n\treturn (\n\t\t<Box flexDirection=\"column\" padding={1}>\n\t\t\t<StatusHeader\n\t\t\t\trole={props.role}\n\t\t\t\tstatus={tcp.status}\n\t\t\t\thostIp={props.role === 'client' ? props.hostIp : undefined}\n\t\t\t\ttcpPort={props.role === 'client' ? props.tcpPort : tcp.listenPort}\n\t\t\t\tcountdownLabel={countdown.label}\n\t\t\t/>\n\n\t\t\t<Box flexDirection=\"row\" gap={4} marginTop={1}>\n\t\t\t\t<Box flexDirection=\"column\" width=\"50%\">\n\t\t\t\t\t<Text color=\"cyan\">{localLabel}</Text>\n\t\t\t\t\t<BuddyAvatar state={localState} />\n\t\t\t\t</Box>\n\n\t\t\t\t<Box flexDirection=\"column\" width=\"50%\">\n\t\t\t\t\t<Text color=\"magenta\">{buddyName}</Text>\n\t\t\t\t\t<BuddyAvatar state={remoteActivity} />\n\t\t\t\t</Box>\n\t\t\t</Box>\n\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text color=\"gray\">\n\t\t\t\t\t按 <Text color=\"cyan\">/</Text> 召唤 AI Console,按 <Text color=\"cyan\">q</Text> 返回菜单。\n\t\t\t\t</Text>\n\t\t\t</Box>\n\n\t\t\t{showAi ? (\n\t\t\t\t<Box marginTop={1}>\n\t\t\t\t\t<AiConsole\n\t\t\t\t\t\tonClose={onCloseAi}\n\t\t\t\t\t\tonStartCountdown={countdown.start}\n\t\t\t\t\t\tlocalName={props.localName}\n\t\t\t\t\t\tpeerName={tcp.peerName ?? (props.role === 'client' ? props.hostName : undefined) ?? 'Buddy'}\n\t\t\t\t\t/>\n\t\t\t\t</Box>\n\t\t\t) : null}\n\t\t</Box>\n\t);\n}\n","export {MainMenu} from './MainMenu.js';\nexport {RoomScanner} from './RoomScanner.js';\nexport {Session} from './Session.js';\n\n","import React, {useCallback, useMemo, useState} from 'react';\nimport os from 'node:os';\nimport {useApp} from 'ink';\nimport {MainMenu, RoomScanner, Session} from '../views/index.js';\n\ntype View =\n\t| {name: 'MENU'}\n\t| {name: 'SCANNING'}\n\t| {name: 'SESSION'; role: 'host'}\n\t| {name: 'SESSION'; role: 'client'; hostIp: string; tcpPort: number; roomName: string; hostName: string};\n\nexport function App() {\n\tconst {exit} = useApp();\n\tconst [view, setView] = useState<View>({name: 'MENU'});\n\n\tconst localName = useMemo(() => os.hostname(), []);\n\n\tconst goMenu = useCallback(() => setView({name: 'MENU'}), []);\n\n\tif (view.name === 'MENU') {\n\t\treturn (\n\t\t\t<MainMenu\n\t\t\t\tonHost={() => setView({name: 'SESSION', role: 'host'})}\n\t\t\t\tonJoin={() => setView({name: 'SCANNING'})}\n\t\t\t\tonExit={() => exit()}\n\t\t\t/>\n\t\t);\n\t}\n\n\tif (view.name === 'SCANNING') {\n\t\treturn (\n\t\t\t<RoomScanner\n\t\t\t\tonBack={goMenu}\n\t\t\t\tonExit={() => exit()}\n\t\t\t\tonSelectRoom={(room) =>\n\t\t\t\t\tsetView({\n\t\t\t\t\t\tname: 'SESSION',\n\t\t\t\t\t\trole: 'client',\n\t\t\t\t\t\thostIp: room.ip,\n\t\t\t\t\t\ttcpPort: room.tcpPort,\n\t\t\t\t\t\troomName: room.roomName,\n\t\t\t\t\t\thostName: room.hostName\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t/>\n\t\t);\n\t}\n\n\tif (view.name === 'SESSION' && view.role === 'host') {\n\t\treturn <Session localName={localName} role=\"host\" onExit={goMenu} />;\n\t}\n\n\treturn (\n\t\t<Session\n\t\t\tlocalName={localName}\n\t\t\trole=\"client\"\n\t\t\tonExit={goMenu}\n\t\t\thostIp={view.hostIp}\n\t\t\ttcpPort={view.tcpPort}\n\t\t\troomName={view.roomName}\n\t\t\thostName={view.hostName}\n\t\t/>\n\t);\n}\n","export {App} from './App.js';\n\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":["useInput","useEffect","useRef","useState","useEffect","useCallback","useEffect","useRef","useState","useEffect","useState","dgram","useCallback","useEffect","useRef","useState","socket","Box","Text","useInput","jsx","jsxs","useMemo","useState","Box","Text","useInput","jsx","jsxs","Box","Text","jsx","Box","Text","jsx","Box","Text","jsx","jsxs","useCallback","useEffect","useMemo","useState","Box","Text","useInput","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/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/BuddyAvatar.tsx","../src/components/sprite/CountdownClockSprite.tsx","../src/components/sprite/ProjectileThrowSprite.tsx","../src/components/StatusHeader.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\";\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\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 { createInteractionTool } from \"./createInteractionTool.js\";\nexport { createSessionInfoTool } from \"./createSessionInfoTool.js\";\n\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createAgent } from \"langchain\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport {\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 }) {\n return [\n \"你是 TermBuddy 里的“壳中幽灵 (Ghost in the Shell)”。\",\n \"默认隐形;被 / 唤醒时出现。风格:极简、干练、少废话。\",\n \"你可以使用工具来操控应用功能(例如倒计时)。\",\n \"如果用户提到“倒计时/专注/计时/countdown”,优先调用 start_countdown。\",\n \"如果用户提到“互动/扔/投掷/throw”,优先调用 throw_projectile。\",\n `当前上下文:我叫 ${context.localName};同桌叫 ${context.peerName}。`,\n ].join(\"\\n\");\n}\n\nexport function useAiAgent(options: {\n localName: string;\n peerName: string;\n onStartCountdown?: (minutes: number) => 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\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 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, interaction, sessionInfo],\n systemPrompt: createSystemPrompt({\n localName: options.localName,\n peerName: options.peerName,\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.onThrowProjectile,\n options.peerName,\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 if (msg === \"missing_api_key\")\n updateLine(aiAt, \"请先在 AI Console 输入 DeepSeek API Key。\");\n else updateLine(aiAt, `(AI 出错)${msg}`);\n } finally {\n setBusy(false);\n }\n },\n [append, ensureAgent, updateLine]\n );\n\n useEffect(() => {\n return () => abortRef.current?.abort();\n }, []);\n\n return { lines, ask, busy };\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} 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\nexport function useTcpSync(options: Options): {\n status: ConnectionStatus;\n listenPort?: number;\n peerName?: string;\n remoteState?: ActivityState;\n sendStatus: (state: ActivityState) => 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 [peerName, setPeerName] = useState<string | undefined>(undefined);\n const [remoteState, setRemoteState] = useState<ActivityState | undefined>(\n undefined\n );\n\n const socketRef = useRef<net.Socket | null>(null);\n const lastSeenRef = useRef<number>(Date.now());\n const heartbeatRef = useRef<NodeJS.Timeout | null>(null);\n\n const cleanupSocket = useCallback(() => {\n if (heartbeatRef.current) clearInterval(heartbeatRef.current);\n heartbeatRef.current = null;\n\n const s = socketRef.current;\n socketRef.current = null;\n if (s && !s.destroyed) s.destroy();\n }, []);\n\n const attachSocket = useCallback(\n (s: net.Socket) => {\n cleanupSocket();\n socketRef.current = s;\n lastSeenRef.current = Date.now();\n\n setStatus(\"connected\");\n setRemoteState(\"IDLE\");\n\n let buf = \"\";\n s.setNoDelay(true);\n s.setEncoding(\"utf8\");\n\n const onData = (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 try {\n const packet = JSON.parse(line) as TcpPacket;\n lastSeenRef.current = Date.now();\n if (packet.type === \"hello\") {\n if (options.role === \"host\") setPeerName(packet.clientName);\n else setPeerName(packet.hostName);\n }\n if (packet.type === \"status\") setRemoteState(packet.state);\n if (packet.type === \"ping\")\n writePacket(s, { type: \"pong\", sentAt: Date.now() });\n if (packet.type === \"pong\") {\n // no-op\n }\n } catch {\n // ignore\n }\n }\n };\n\n s.on(\"data\", onData);\n s.on(\"close\", () => {\n setStatus(options.role === \"host\" ? \"waiting\" : \"disconnected\");\n setRemoteState(\"OFFLINE\");\n cleanupSocket();\n });\n s.on(\"error\", () => {\n setStatus(options.role === \"host\" ? \"waiting\" : \"disconnected\");\n setRemoteState(\"OFFLINE\");\n });\n\n // Hello handshake.\n writePacket(s, {\n type: \"hello\",\n hostName:\n options.role === \"host\"\n ? options.localName\n : options.hostName ?? \"Host\",\n clientName: options.role === \"client\" ? options.localName : \"Client\",\n sentAt: Date.now(),\n });\n\n heartbeatRef.current = setInterval(() => {\n const sock = socketRef.current;\n if (!sock || sock.destroyed) return;\n writePacket(sock, { type: \"ping\", sentAt: Date.now() });\n const age = Date.now() - lastSeenRef.current;\n if (age > 6000) {\n setStatus(\"disconnected\");\n setRemoteState(\"OFFLINE\");\n cleanupSocket();\n }\n }, 2000);\n },\n [cleanupSocket, options]\n );\n\n useEffect(() => {\n if (options.role === \"host\") {\n const server = net.createServer((socket) => {\n attachSocket(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 cleanupSocket();\n server.close();\n };\n }\n\n setStatus(\"connecting\");\n const socket = net.createConnection(\n { host: options.hostIp, port: options.tcpPort },\n () => {\n attachSocket(socket);\n }\n );\n socket.on(\"error\", () => {\n setStatus(\"disconnected\");\n setRemoteState(\"OFFLINE\");\n });\n\n return () => {\n socket.destroy();\n cleanupSocket();\n };\n }, [attachSocket, cleanupSocket, options]);\n\n const sendStatus = useCallback((state: ActivityState) => {\n const socket = socketRef.current;\n if (!socket || socket.destroyed) return;\n writePacket(socket, { type: \"status\", state, sentAt: Date.now() });\n }, []);\n\n return { status, listenPort, peerName, remoteState, sendStatus };\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 absolute = path.resolve(process.cwd(), KEY_RELATIVE_PATH);\n\tawait ensureDirForFile(absolute);\n\tconst payload: KeyFile = {apiKey};\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 onThrowProjectile: (\n kind: ProjectileKind,\n direction: ProjectileDirection\n ) => void;\n localName: string;\n peerName: string;\n}) {\n const [input, setInput] = useState(\"\");\n const [apiKey, setApiKey] = useState<string | null>(null);\n const [keyDraft, setKeyDraft] = useState(\"\");\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 const agent = useAiAgent({\n localName: props.localName,\n peerName: props.peerName,\n onStartCountdown: props.onStartCountdown,\n onThrowProjectile: props.onThrowProjectile,\n apiKey: apiKey ?? undefined,\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 (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.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 <Text>{input}</Text>\n ) : (\n <Text>\n {keyDraft.length === 0\n ? \"\"\n : \"*\".repeat(Math.min(64, keyDraft.length))}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityState } from \"../../protocol.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 marginTop?: number;\n}) {\n const sprite = SPRITES[props.state];\n if (props.variant === \"compact\") {\n return (\n <Box marginTop={props.marginTop ?? 1}>\n <Text color={sprite.color}>{sprite.compact}</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" marginTop={props.marginTop ?? 1}>\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}) {\n const st = statusText(props.status);\n return (\n <Box>\n <Box>\n <Text color={st.color}>{st.label}</Text>\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","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\";\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} 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\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 const remoteActivity: ActivityState = tcp.remoteState ?? \"OFFLINE\";\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: tcp.peerName,\n };\n props.onLeave(stats);\n }, [props, tcp.peerName]);\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 }, []);\n\n const throwProjectile = useCallback(\n (kind: ProjectileKind, direction: ProjectileDirection) => {\n const id = Date.now() + Math.floor(Math.random() * 1000);\n setShots((prev) => [...prev, { id, kind, direction }]);\n },\n []\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 buddyName =\n props.role === \"host\"\n ? tcp.peerName ?? \"Waiting...\"\n : props.hostName ?? \"Host\";\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 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 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 />\n\n {/* Main Stage: 3 Columns */}\n <Box\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n marginTop={1}\n gap={2}\n >\n {/* Left: Local User */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={20}>\n {countdown ? (\n <Box flexDirection=\"column\" alignItems=\"center\" marginBottom={0}>\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 ) : (\n <Box height={4} />\n )}\n <BuddyAvatar state={localState} marginTop={0} />\n <Text color=\"cyan\">{localLabel}</Text>\n </Box>\n\n {/* Center: Stage (Projectiles only) */}\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n flexGrow={1}\n minWidth={40}\n >\n {/* Projectile Area */}\n <Box flexDirection=\"column\" width=\"100%\" alignItems=\"center\">\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={36}\n onDone={() =>\n setShots((prev) => prev.filter((x) => x.id !== s.id))\n }\n />\n ))}\n {/* Spacer to maintain layout stability when shots appear/disappear */}\n {shots.length === 0 ? <Box height={1} /> : null}\n </Box>\n </Box>\n\n {/* Right: Remote User */}\n <Box flexDirection=\"column\" alignItems=\"center\" minWidth={20}>\n {/* Placeholder for remote clock to keep alignment with local user */}\n <Box height={4} />\n <BuddyAvatar state={remoteActivity} marginTop={0} />\n <Text color=\"magenta\">{buddyName}</Text>\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 Overlay */}\n {showAi ? (\n <Box\n marginTop={1}\n width=\"100%\"\n flexDirection=\"row\"\n justifyContent=\"center\"\n >\n <Box width={64}>\n <AiConsole\n onClose={onCloseAi}\n onStartCountdown={startCountdown}\n onThrowProjectile={throwProjectile}\n localName={props.localName}\n peerName={\n tcp.peerName ??\n (props.role === \"client\" ? props.hostName : undefined) ??\n \"Buddy\"\n }\n />\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","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","Box","Text","jsx","useMemo","Box","Text","jsx","jsxs","face","useEffect","useMemo","useState","Box","Text","jsx","jsxs","clamp01","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.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,8 @@
16
16
  "@langchain/openai": "^1.2.0",
17
17
  "ink": "^6.6.0",
18
18
  "langchain": "^1.2.3",
19
- "react": "^19.2.3"
19
+ "react": "^19.2.3",
20
+ "uiohook-napi": "^1.5.4"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@types/node": "^25.0.3",
@@ -0,0 +1,2 @@
1
+ ignoredBuiltDependencies:
2
+ - uiohook-napi
package/src/app/App.tsx CHANGED
@@ -1,64 +1,105 @@
1
- import React, {useCallback, useMemo, useState} from 'react';
2
- import os from 'node:os';
3
- import {useApp} from 'ink';
4
- import {MainMenu, RoomScanner, Session} from '../views/index.js';
1
+ import React, { useCallback, useMemo, useState } from "react";
2
+ import os from "node:os";
3
+ import { useApp } from "ink";
4
+ import {
5
+ LeavePage,
6
+ MainMenu,
7
+ NicknamePrompt,
8
+ RoomScanner,
9
+ Session,
10
+ } from "../page/index.js";
11
+ import type { LeaveStats } from "../types.js";
5
12
 
6
13
  type View =
7
- | {name: 'MENU'}
8
- | {name: 'SCANNING'}
9
- | {name: 'SESSION'; role: 'host'}
10
- | {name: 'SESSION'; role: 'client'; hostIp: string; tcpPort: number; roomName: string; hostName: string};
14
+ | { name: "NICKNAME" }
15
+ | { name: "MENU" }
16
+ | { name: "SCANNING" }
17
+ | { name: "LEAVE"; stats: LeaveStats }
18
+ | { name: "SESSION"; role: "host" }
19
+ | {
20
+ name: "SESSION";
21
+ role: "client";
22
+ hostIp: string;
23
+ tcpPort: number;
24
+ roomName: string;
25
+ hostName: string;
26
+ };
11
27
 
12
28
  export function App() {
13
- const {exit} = useApp();
14
- const [view, setView] = useState<View>({name: 'MENU'});
29
+ const { exit } = useApp();
30
+ const [view, setView] = useState<View>({ name: "NICKNAME" });
31
+ const [nickname, setNickname] = useState<string | null>(null);
15
32
 
16
- const localName = useMemo(() => os.hostname(), []);
33
+ const localName = useMemo(() => nickname ?? os.hostname(), [nickname]);
17
34
 
18
- const goMenu = useCallback(() => setView({name: 'MENU'}), []);
35
+ const goMenu = useCallback(() => setView({ name: "MENU" }), []);
19
36
 
20
- if (view.name === 'MENU') {
21
- return (
22
- <MainMenu
23
- onHost={() => setView({name: 'SESSION', role: 'host'})}
24
- onJoin={() => setView({name: 'SCANNING'})}
25
- onExit={() => exit()}
26
- />
27
- );
28
- }
37
+ if (view.name === "NICKNAME") {
38
+ return (
39
+ <NicknamePrompt
40
+ onExit={() => exit()}
41
+ onSubmit={(name) => {
42
+ setNickname(name);
43
+ setView({ name: "MENU" });
44
+ }}
45
+ />
46
+ );
47
+ }
29
48
 
30
- if (view.name === 'SCANNING') {
31
- return (
32
- <RoomScanner
33
- onBack={goMenu}
34
- onExit={() => exit()}
35
- onSelectRoom={(room) =>
36
- setView({
37
- name: 'SESSION',
38
- role: 'client',
39
- hostIp: room.ip,
40
- tcpPort: room.tcpPort,
41
- roomName: room.roomName,
42
- hostName: room.hostName
43
- })
44
- }
45
- />
46
- );
47
- }
49
+ if (view.name === "MENU") {
50
+ return (
51
+ <MainMenu
52
+ onHost={() => setView({ name: "SESSION", role: "host" })}
53
+ onJoin={() => setView({ name: "SCANNING" })}
54
+ onExit={() => exit()}
55
+ />
56
+ );
57
+ }
48
58
 
49
- if (view.name === 'SESSION' && view.role === 'host') {
50
- return <Session localName={localName} role="host" onExit={goMenu} />;
51
- }
59
+ if (view.name === "LEAVE") {
60
+ return (
61
+ <LeavePage stats={view.stats} onBack={goMenu} onExit={() => exit()} />
62
+ );
63
+ }
52
64
 
53
- return (
54
- <Session
55
- localName={localName}
56
- role="client"
57
- onExit={goMenu}
58
- hostIp={view.hostIp}
59
- tcpPort={view.tcpPort}
60
- roomName={view.roomName}
61
- hostName={view.hostName}
62
- />
63
- );
65
+ if (view.name === "SCANNING") {
66
+ return (
67
+ <RoomScanner
68
+ onBack={goMenu}
69
+ onExit={() => exit()}
70
+ onSelectRoom={(room) =>
71
+ setView({
72
+ name: "SESSION",
73
+ role: "client",
74
+ hostIp: room.ip,
75
+ tcpPort: room.tcpPort,
76
+ roomName: room.roomName,
77
+ hostName: room.hostName,
78
+ })
79
+ }
80
+ />
81
+ );
82
+ }
83
+
84
+ if (view.name === "SESSION" && view.role === "host") {
85
+ return (
86
+ <Session
87
+ localName={localName}
88
+ role="host"
89
+ onLeave={(stats) => setView({ name: "LEAVE", stats })}
90
+ />
91
+ );
92
+ }
93
+
94
+ return (
95
+ <Session
96
+ localName={localName}
97
+ role="client"
98
+ onLeave={(stats) => setView({ name: "LEAVE", stats })}
99
+ hostIp={view.hostIp}
100
+ tcpPort={view.tcpPort}
101
+ roomName={view.roomName}
102
+ hostName={view.hostName}
103
+ />
104
+ );
64
105
  }
package/src/app/index.ts CHANGED
@@ -1,2 +1 @@
1
- export {App} from './App.js';
2
-
1
+ export { App } from "./App.js";