cheatchat 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cheatchat",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "chat": "./bin/chat.js"
@@ -4,6 +4,7 @@ import { connectWebSocket } from "../websocket/connect.js";
4
4
  import { disconnectWebSocket } from "../websocket/disconnect.js";
5
5
  import { handleIncomingMessage } from "../websocket/receiver.js";
6
6
  import { sendMessage } from "../websocket/sender.js";
7
+ import { requestHistory, requestOnline, requestUsers } from "../services/socket.js";
7
8
  import { showChatHeader } from "../ui/banner.js";
8
9
  import {
9
10
  printChatHelp,
@@ -109,8 +110,13 @@ export async function connectCommand() {
109
110
  showChatHeader(username, chatMode, dmTarget);
110
111
  printBlank();
111
112
  printSystem("You are in Global Chat. Type a message or /help for commands.");
113
+ printSystem("Loading chat history...");
112
114
  printBlank();
113
115
 
116
+ // ─── Auto-load global history on connect ─────────────────────────────
117
+
118
+ requestHistory(ws, "global");
119
+
114
120
  // ─── Start Interactive Readline ──────────────────────────────────────
115
121
 
116
122
  rl = readline.createInterface({
@@ -141,6 +147,8 @@ export async function connectCommand() {
141
147
  chatMode = CHAT_MODE.GLOBAL;
142
148
  dmTarget = null;
143
149
  printModeSwitch("global");
150
+ // Auto-load global history on mode switch
151
+ requestHistory(ws, "global");
144
152
  refreshPrompt();
145
153
  return;
146
154
 
@@ -159,10 +167,31 @@ export async function connectCommand() {
159
167
  chatMode = CHAT_MODE.PRIVATE;
160
168
  dmTarget = target;
161
169
  printModeSwitch("private", target);
170
+ // Auto-load private history when switching to DM
171
+ requestHistory(ws, "private", target);
162
172
  refreshPrompt();
163
173
  return;
164
174
  }
165
175
 
176
+ case "/history":
177
+ if (chatMode === CHAT_MODE.GLOBAL) {
178
+ requestHistory(ws, "global");
179
+ } else if (chatMode === CHAT_MODE.PRIVATE && dmTarget) {
180
+ requestHistory(ws, "private", dmTarget);
181
+ }
182
+ rl.prompt();
183
+ return;
184
+
185
+ case "/online":
186
+ requestOnline(ws);
187
+ rl.prompt();
188
+ return;
189
+
190
+ case "/users":
191
+ requestUsers(ws);
192
+ rl.prompt();
193
+ return;
194
+
166
195
  case "/help":
167
196
  printChatHelp();
168
197
  rl.prompt();
@@ -27,6 +27,9 @@ export function helpCommand() {
27
27
  const chatCommands = [
28
28
  ["/global", "Switch to global chat"],
29
29
  ["/dm <username>", "Switch to private messaging"],
30
+ ["/history", "Load message history"],
31
+ ["/online", "Show online users"],
32
+ ["/users", "Show all registered users"],
30
33
  ["/help", "Show chat commands"],
31
34
  ["/exit", "Disconnect and exit"],
32
35
  ];
@@ -69,6 +69,37 @@ export function sendPrivateMessage(ws, to, text) {
69
69
  return true;
70
70
  }
71
71
 
72
+ /**
73
+ * Request chat history (global or private).
74
+ */
75
+ export function requestHistory(ws, type, withUser, limit = 50) {
76
+ if (ws.readyState !== WebSocket.OPEN) return false;
77
+ const payload = { action: "history", type, limit };
78
+ if (type === "private" && withUser) {
79
+ payload.with = withUser;
80
+ }
81
+ ws.send(JSON.stringify(payload));
82
+ return true;
83
+ }
84
+
85
+ /**
86
+ * Request the list of currently online users.
87
+ */
88
+ export function requestOnline(ws) {
89
+ if (ws.readyState !== WebSocket.OPEN) return false;
90
+ ws.send(JSON.stringify({ action: "online" }));
91
+ return true;
92
+ }
93
+
94
+ /**
95
+ * Request the list of all registered users.
96
+ */
97
+ export function requestUsers(ws) {
98
+ if (ws.readyState !== WebSocket.OPEN) return false;
99
+ ws.send(JSON.stringify({ action: "users" }));
100
+ return true;
101
+ }
102
+
72
103
  /**
73
104
  * Close the WebSocket connection gracefully.
74
105
  */
package/src/ui/printer.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  warning,
14
14
  accent,
15
15
  brand,
16
+ white,
16
17
  whiteBold,
17
18
  dim,
18
19
  } from "./color.js";
@@ -93,6 +94,9 @@ export function printChatHelp() {
93
94
  console.log(` ${whiteBold("Chat Commands:")}`);
94
95
  console.log(` ${brand("/global")} ${muted("Switch to global chat")}`);
95
96
  console.log(` ${brand("/dm <username>")} ${muted("Switch to private messaging")}`);
97
+ console.log(` ${brand("/history")} ${muted("Load message history for current chat")}`);
98
+ console.log(` ${brand("/online")} ${muted("Show currently connected users")}`);
99
+ console.log(` ${brand("/users")} ${muted("Show all registered users")}`);
96
100
  console.log(` ${brand("/help")} ${muted("Show this help menu")}`);
97
101
  console.log(` ${brand("/exit")} ${muted("Disconnect and exit")}`);
98
102
  console.log();
@@ -111,6 +115,66 @@ export function printModeSwitch(mode, target) {
111
115
  console.log();
112
116
  }
113
117
 
118
+ /**
119
+ * Print a history header.
120
+ */
121
+ export function printHistoryHeader(type, target) {
122
+ console.log();
123
+ const sep = muted("─".repeat(50));
124
+ console.log(` ${sep}`);
125
+ if (type === "global") {
126
+ console.log(` ${systemTag(" HISTORY ")} ${accent("🌍 Global Chat History")}`);
127
+ } else {
128
+ console.log(` ${systemTag(" HISTORY ")} ${accent(`🔒 DM History with ${whiteBold(target)}`)}`);
129
+ }
130
+ console.log(` ${sep}`);
131
+ }
132
+
133
+ /**
134
+ * Print a history footer.
135
+ */
136
+ export function printHistoryFooter(count) {
137
+ const sep = muted("─".repeat(50));
138
+ console.log(` ${sep}`);
139
+ console.log(` ${muted(`${count} message${count !== 1 ? "s" : ""} loaded`)}`);
140
+ console.log(` ${sep}`);
141
+ console.log();
142
+ }
143
+
144
+ /**
145
+ * Print online users list.
146
+ */
147
+ export function printOnlineUsers(users) {
148
+ console.log();
149
+ console.log(` ${whiteBold("Online Users")} ${muted(`(${users.length})`)}`);
150
+ console.log(` ${muted("─".repeat(30))}`);
151
+ if (users.length === 0) {
152
+ console.log(` ${muted("No users online")}`);
153
+ } else {
154
+ for (const u of users) {
155
+ console.log(` ${success("●")} ${accent(u)}`);
156
+ }
157
+ }
158
+ console.log();
159
+ }
160
+
161
+ /**
162
+ * Print all registered users list.
163
+ */
164
+ export function printRegisteredUsers(users) {
165
+ console.log();
166
+ console.log(` ${whiteBold("Registered Users")} ${muted(`(${users.length})`)}`);
167
+ console.log(` ${muted("─".repeat(30))}`);
168
+ if (users.length === 0) {
169
+ console.log(` ${muted("No users found")}`);
170
+ } else {
171
+ for (const u of users) {
172
+ console.log(` ${brand("•")} ${white(u)}`);
173
+ }
174
+ }
175
+ console.log();
176
+ }
177
+
114
178
  /**
115
179
  * Print a blank line.
116
180
  */
@@ -1,4 +1,13 @@
1
- import { printGlobalMessage, printPrivateMessage, printError, printSystem } from "../ui/printer.js";
1
+ import {
2
+ printGlobalMessage,
3
+ printPrivateMessage,
4
+ printError,
5
+ printSystem,
6
+ printHistoryHeader,
7
+ printHistoryFooter,
8
+ printOnlineUsers,
9
+ printRegisteredUsers,
10
+ } from "../ui/printer.js";
2
11
 
3
12
  /**
4
13
  * Handle incoming WebSocket messages and dispatch to the appropriate printer.
@@ -15,6 +24,18 @@ export function handleIncomingMessage(data, currentUser) {
15
24
  printPrivateMessage(data.sender, data.text, data.timestamp, currentUser);
16
25
  break;
17
26
 
27
+ case "history":
28
+ renderHistory(data, currentUser);
29
+ break;
30
+
31
+ case "online":
32
+ printOnlineUsers(data.users || []);
33
+ break;
34
+
35
+ case "users":
36
+ printRegisteredUsers(data.users || []);
37
+ break;
38
+
18
39
  case "error":
19
40
  printError(data.message || "Unknown server error");
20
41
  break;
@@ -24,3 +45,28 @@ export function handleIncomingMessage(data, currentUser) {
24
45
  break;
25
46
  }
26
47
  }
48
+
49
+ /**
50
+ * Render a history response (global or private).
51
+ */
52
+ function renderHistory(data, currentUser) {
53
+ const messages = data.messages || [];
54
+ const type = data.type;
55
+ const target = data.with;
56
+
57
+ printHistoryHeader(type, target);
58
+
59
+ if (messages.length === 0) {
60
+ printSystem("No messages yet. Start the conversation!");
61
+ } else {
62
+ for (const msg of messages) {
63
+ if (type === "global") {
64
+ printGlobalMessage(msg.sender, msg.text, msg.timestamp, currentUser);
65
+ } else {
66
+ printPrivateMessage(msg.sender, msg.text, msg.timestamp, currentUser);
67
+ }
68
+ }
69
+ }
70
+
71
+ printHistoryFooter(messages.length);
72
+ }