xx-chat 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # xx-chat
2
+
3
+ English | [中文](#中文)
4
+
5
+ A simple, lightweight, and real-time LAN CLI chat tool based on Node.js and Socket.io. With `xx-chat`, you can easily set up a chat room on your local network and chat with your friends right in your terminal.
6
+
7
+ ## Features
8
+
9
+ - **Quick Setup**: Start a server or join as a client with simple commands or interactive prompts.
10
+ - **Real-time Messaging**: Instant message broadcasting powered by Socket.io.
11
+ - **Clean UI**: Solved terminal input conflicts, providing a modern chat experience in the CLI.
12
+ - **Customizable Colors**: Personalize your message colors (supports standard color names and Hex codes like `#ff0000`).
13
+ - **Timestamps**: Messages are displayed with clear timestamps.
14
+ - **Command Support**: Use `/` to trigger commands like changing nicknames or quitting.
15
+
16
+ ## Installation
17
+
18
+ You can install it globally via npm or yarn:
19
+
20
+ ```bash
21
+ npm install -g xx-chat
22
+ # or
23
+ yarn global add xx-chat
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Start as a Server (Host)
29
+
30
+ Start a chat server on your machine and join the chat room:
31
+
32
+ ```bash
33
+ xx-chat --role server --nickname <YourNickname>
34
+ ```
35
+
36
+ ### Start as a Client (Guest)
37
+
38
+ Join an existing chat server using its IP address:
39
+
40
+ ```bash
41
+ xx-chat --role client --ip <Server_IP> --nickname <YourNickname>
42
+ ```
43
+
44
+ *Tip: If you run `xx-chat` without any arguments, it will launch an interactive prompt to help you configure these settings.*
45
+
46
+ ### Available Commands
47
+
48
+ In the chat interface, type `/` and press `Enter` to see available commands (Tab completion is supported):
49
+
50
+ - `/nickname <NewName>` - Change your nickname.
51
+ - `/color-send <Color>` - Change the color of the messages you send.
52
+ - `/color-recv <Color>` - Change the color of the messages you receive.
53
+ - `/quit` - Exit the chat room.
54
+
55
+ *Supported colors: `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`, or any Hex color code (e.g., `#ff00ff`).*
56
+
57
+ ---
58
+
59
+ <h2 id="中文">中文</h2>
60
+
61
+ 一个简单、轻量级的局域网命令行(CLI)聊天工具,基于 Node.js 和 Socket.io 开发。使用 `xx-chat`,你可以轻松在局域网内搭建聊天室,并直接在终端里与朋友进行实时聊天。
62
+
63
+ ## 特性
64
+
65
+ - **快速启动**:通过简单的命令或交互式问答,即可快速作为服务端或客户端启动。
66
+ - **实时通信**:基于 Socket.io 的低延迟消息广播。
67
+ - **纯净的 UI**:解决了终端输入与接收消息时的冲突问题,带来类似现代聊天软件的体验。
68
+ - **自定义颜色**:支持自定义发送和接收消息的颜色(支持标准颜色名及 `#ff0000` 等十六进制颜色值)。
69
+ - **时间戳**:每条消息都会清晰地展示发送时间。
70
+ - **命令支持**:支持输入 `/` 触发快捷命令,如修改昵称或退出。
71
+
72
+ ## 安装
73
+
74
+ 你可以通过 npm 或 yarn 将其安装到全局:
75
+
76
+ ```bash
77
+ npm install -g xx-chat
78
+ # 或者
79
+ yarn global add xx-chat
80
+ ```
81
+
82
+ ## 使用方法
83
+
84
+ ### 作为服务端(房主)启动
85
+
86
+ 在你的电脑上启动聊天服务,并作为第一个用户加入:
87
+
88
+ ```bash
89
+ xx-chat --role server --nickname <你的昵称>
90
+ ```
91
+
92
+ ### 作为客户端(访客)加入
93
+
94
+ 使用服务端的 IP 地址连接到现有的聊天室:
95
+
96
+ ```bash
97
+ xx-chat --role client --ip <服务器的局域网IP> --nickname <你的昵称>
98
+ ```
99
+
100
+ *提示:如果你直接输入 `xx-chat` 不带任何参数,程序会通过交互式的问答引导你完成这些配置。*
101
+
102
+ ### 聊天室命令
103
+
104
+ 在聊天输入框中,输入 `/` 即可使用以下命令(支持按 `Tab` 键补全):
105
+
106
+ - `/nickname <新昵称>` - 修改你的聊天昵称
107
+ - `/color-send <颜色>` - 修改你发送消息的显示颜色
108
+ - `/color-recv <颜色>` - 修改你接收消息的显示颜色
109
+ - `/quit` - 退出聊天室
110
+
111
+ *支持的颜色:`red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`,或者十六进制颜色代码(例如:`#ff00ff`)。*
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { parseCli } from './src/cli.js';
3
3
  import { startServer } from './src/server.js';
4
4
  import { startClient } from './src/client.js';
5
- import { initUI, renderSystemMessage, renderChatMessage } from './src/ui.js';
5
+ import { initUI, renderSystemMessage, renderChatMessage, setSendColor, setRecvColor } from './src/ui.js';
6
6
 
7
7
  async function main() {
8
8
  const { role, ip, nickname } = await parseCli();
@@ -28,7 +28,7 @@ async function main() {
28
28
  const command = parts[0];
29
29
 
30
30
  if (command === '/' || command === '/help') {
31
- renderSystemMessage('可用命令:\n /nickname <新昵称> - 修改昵称\n /quit - 退出聊天');
31
+ renderSystemMessage('可用命令:\n /nickname <新昵称> - 修改昵称\n /color-send <颜色> - 修改发送消息颜色\n /color-recv <颜色> - 修改接收消息颜色\n /quit - 退出聊天\n支持的颜色: red, green, yellow, blue, magenta, cyan, white, gray, 或者十六进制颜色(如 #ff0000)');
32
32
  } else if (command === '/nickname') {
33
33
  const newNickname = parts.slice(1).join(' ').trim();
34
34
  if (newNickname) {
@@ -38,6 +38,20 @@ async function main() {
38
38
  } else {
39
39
  renderSystemMessage('用法: /nickname <新昵称>');
40
40
  }
41
+ } else if (command === '/color-send') {
42
+ const color = parts[1];
43
+ if (color && setSendColor(color)) {
44
+ renderSystemMessage(`发送消息颜色已修改为 ${color}`);
45
+ } else {
46
+ renderSystemMessage('用法: /color-send <颜色> (如: green, red, cyan, 或十六进制如 #ff0000)');
47
+ }
48
+ } else if (command === '/color-recv') {
49
+ const color = parts[1];
50
+ if (color && setRecvColor(color)) {
51
+ renderSystemMessage(`接收消息颜色已修改为 ${color}`);
52
+ } else {
53
+ renderSystemMessage('用法: /color-recv <颜色> (如: green, red, cyan, 或十六进制如 #ff0000)');
54
+ }
41
55
  } else if (command === '/quit') {
42
56
  renderSystemMessage('正在退出聊天...');
43
57
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xx-chat",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A simple LAN CLI chat tool",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/ui.js CHANGED
@@ -2,9 +2,32 @@ import readline from 'readline';
2
2
  import chalk from 'chalk';
3
3
 
4
4
  let rl;
5
+ let sendColor = 'green';
6
+ let recvColor = 'cyan';
7
+ const validColors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'gray'];
8
+
9
+ function isValidHexColor(color) {
10
+ return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/i.test(color);
11
+ }
12
+
13
+ export function setSendColor(color) {
14
+ if (validColors.includes(color) || isValidHexColor(color)) {
15
+ sendColor = color;
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+
21
+ export function setRecvColor(color) {
22
+ if (validColors.includes(color) || isValidHexColor(color)) {
23
+ recvColor = color;
24
+ return true;
25
+ }
26
+ return false;
27
+ }
5
28
 
6
29
  function completer(line) {
7
- const completions = ['/nickname', '/quit', '/help'];
30
+ const completions = ['/nickname', '/quit', '/help', '/color-send', '/color-recv'];
8
31
  const hits = completions.filter((c) => c.startsWith(line));
9
32
  return [hits.length ? hits : line.startsWith('/') ? completions : [], line];
10
33
  }
@@ -27,11 +50,15 @@ export function initUI(onMessage) {
27
50
  rl.prompt();
28
51
 
29
52
  rl.on('line', (line) => {
53
+ // 擦除用户按下回车后留在屏幕上的原始输入内容
54
+ process.stdout.write('\x1B[1A\x1B[2K\x1B[0G');
55
+
30
56
  const text = line.trim();
31
57
  if (text) {
32
58
  onMessage(text);
59
+ } else {
60
+ rl.prompt();
33
61
  }
34
- rl.prompt();
35
62
  });
36
63
  }
37
64
 
@@ -49,10 +76,13 @@ export function renderSystemMessage(msg) {
49
76
 
50
77
  export function renderChatMessage(nickname, message, isSelf = false) {
51
78
  clearLine();
79
+ const time = new Date().toLocaleTimeString();
52
80
  if (isSelf) {
53
- console.log(chalk.green(`[我(${nickname})] ${message}`));
81
+ const colorFn = isValidHexColor(sendColor) ? chalk.hex(sendColor) : (chalk[sendColor] || chalk.green);
82
+ console.log(colorFn(`[我(${nickname})] ${message} `) + chalk.gray(`[${time}]`));
54
83
  } else {
55
- console.log(chalk.blue(`[${nickname}] ${message}`));
84
+ const colorFn = isValidHexColor(recvColor) ? chalk.hex(recvColor) : (chalk[recvColor] || chalk.cyan);
85
+ console.log(colorFn(`[${nickname}] ${message} `) + chalk.gray(`[${time}]`));
56
86
  }
57
87
  if (rl) rl.prompt(true);
58
88
  }
@@ -1,10 +1,16 @@
1
1
  import { exec } from 'child_process';
2
- const child = exec('xx-chat --role server --nickname Host');
2
+ const child = exec('xx-chat');
3
3
  child.stdout.on('data', d => console.log('OUT:', JSON.stringify(d)));
4
4
  child.stderr.on('data', d => console.log('ERR:', JSON.stringify(d)));
5
5
  setTimeout(() => {
6
- child.stdin.write('hello\r');
6
+ child.stdin.write('\r'); // Select server
7
+ }, 500);
8
+ setTimeout(() => {
9
+ child.stdin.write('Host\r'); // Type nickname
7
10
  }, 1000);
8
11
  setTimeout(() => {
9
- child.kill();
12
+ child.stdin.write('hello test erase\r'); // Chat message
10
13
  }, 2000);
14
+ setTimeout(() => {
15
+ child.kill();
16
+ }, 3000);
@@ -9,7 +9,7 @@ setTimeout(() => {
9
9
  child.stdin.write('Host\r'); // Type nickname
10
10
  }, 1000);
11
11
  setTimeout(() => {
12
- child.stdin.write('hello\r'); // Chat message
12
+ child.stdin.write('hello world\r'); // Chat message
13
13
  }, 2000);
14
14
  setTimeout(() => {
15
15
  child.kill();
package/test-send.js ADDED
@@ -0,0 +1,16 @@
1
+ import { exec } from 'child_process';
2
+ const child = exec('node index.js');
3
+ child.stdout.on('data', d => console.log('OUT:', JSON.stringify(d)));
4
+ child.stderr.on('data', d => console.log('ERR:', JSON.stringify(d)));
5
+ setTimeout(() => {
6
+ child.stdin.write('\r'); // Select server
7
+ }, 500);
8
+ setTimeout(() => {
9
+ child.stdin.write('Host\r'); // Type nickname
10
+ }, 1000);
11
+ setTimeout(() => {
12
+ child.stdin.write('hello world\r'); // Chat message
13
+ }, 2000);
14
+ setTimeout(() => {
15
+ child.kill();
16
+ }, 3000);
package/test-up-run.js ADDED
@@ -0,0 +1,9 @@
1
+ import { exec } from 'child_process';
2
+ const child = exec('node test-up.js');
3
+ child.stdout.on('data', d => console.log('OUT:', JSON.stringify(d)));
4
+ setTimeout(() => {
5
+ child.stdin.write('hello\r\n');
6
+ }, 500);
7
+ setTimeout(() => {
8
+ child.kill();
9
+ }, 1000);
package/test-up.js ADDED
@@ -0,0 +1,13 @@
1
+ import readline from 'readline';
2
+ const rl = readline.createInterface({
3
+ input: process.stdin,
4
+ output: process.stdout,
5
+ prompt: '> '
6
+ });
7
+ rl.prompt();
8
+ rl.on('line', (line) => {
9
+ // Move up one line and clear it
10
+ process.stdout.write('\x1B[1A\x1B[2K\x1B[0G');
11
+ console.log(`[ME] ${line}`);
12
+ rl.prompt();
13
+ });
package/test-inquirer.js DELETED
@@ -1,17 +0,0 @@
1
- import inquirer from 'inquirer';
2
- import readline from 'readline';
3
-
4
- async function run() {
5
- await inquirer.prompt([{ type: 'input', name: 'test', message: 'test' }]);
6
- const rl = readline.createInterface({
7
- input: process.stdin,
8
- output: process.stdout,
9
- prompt: '> '
10
- });
11
- rl.prompt();
12
- rl.on('line', (line) => {
13
- console.log('You entered:', line);
14
- rl.prompt();
15
- });
16
- }
17
- run();
package/test-search.js DELETED
@@ -1 +0,0 @@
1
- console.log("Searching for similar issues");