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 +111 -0
- package/index.js +16 -2
- package/package.json +1 -1
- package/src/ui.js +34 -4
- package/{test-xx-chat.js → test-erase-input.js} +9 -3
- package/{test-xx-chat-interactive.js → test-manual.js} +1 -1
- package/test-send.js +16 -0
- package/test-up-run.js +9 -0
- package/test-up.js +13 -0
- package/test-inquirer.js +0 -17
- package/test-search.js +0 -1
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
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
|
-
|
|
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
|
-
|
|
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
|
|
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('
|
|
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.
|
|
12
|
+
child.stdin.write('hello test erase\r'); // Chat message
|
|
10
13
|
}, 2000);
|
|
14
|
+
setTimeout(() => {
|
|
15
|
+
child.kill();
|
|
16
|
+
}, 3000);
|
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");
|