@hapico/cli 0.0.14 → 0.0.15
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/bin/index.js +46 -22
- package/bun.lock +6 -0
- package/dist/index.js +46 -22
- package/index.ts +67 -26
- package/package.json +3 -1
package/bin/index.js
CHANGED
|
@@ -38,6 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.compileES5 = void 0;
|
|
41
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
42
|
+
const lodash_1 = require("lodash");
|
|
41
43
|
const commander_1 = require("commander");
|
|
42
44
|
const axios_1 = __importDefault(require("axios"));
|
|
43
45
|
const fs = __importStar(require("fs"));
|
|
@@ -226,7 +228,7 @@ class FileManager {
|
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
class RoomState {
|
|
229
|
-
constructor(roomId) {
|
|
231
|
+
constructor(roomId, onChangeListeners) {
|
|
230
232
|
this.files = [];
|
|
231
233
|
this.roomId = roomId;
|
|
232
234
|
this.state = {};
|
|
@@ -234,6 +236,7 @@ class RoomState {
|
|
|
234
236
|
this.ws = null;
|
|
235
237
|
this.reconnectTimeout = null;
|
|
236
238
|
this.reconnectAttempts = 0;
|
|
239
|
+
this.onChangeListeners = onChangeListeners;
|
|
237
240
|
}
|
|
238
241
|
connect(onConnected) {
|
|
239
242
|
if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN)
|
|
@@ -242,25 +245,50 @@ class RoomState {
|
|
|
242
245
|
clearTimeout(this.reconnectTimeout);
|
|
243
246
|
}
|
|
244
247
|
this.ws = new ws_1.WebSocket(`https://base.myworkbeast.com/ws?room=${this.roomId}`);
|
|
245
|
-
this.ws.
|
|
248
|
+
this.ws.onopen = () => {
|
|
249
|
+
console.log(`Connected to room: ${this.roomId}`);
|
|
246
250
|
this.isConnected = true;
|
|
247
251
|
this.reconnectAttempts = 0;
|
|
248
|
-
onConnected === null || onConnected === void 0 ? void 0 : onConnected();
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
|
|
253
|
+
};
|
|
254
|
+
this.ws.onclose = () => {
|
|
255
|
+
console.log(`Disconnected from room: ${this.roomId}`);
|
|
252
256
|
this.isConnected = false;
|
|
253
257
|
this.reconnectAttempts++;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
259
|
+
console.log(`Attempting to reconnect in ${delay / 1000}s...`);
|
|
260
|
+
this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
|
|
261
|
+
};
|
|
257
262
|
this.ws.on("message", (data) => {
|
|
258
263
|
try {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
const jsonStr = typeof data === "string" ? data : data.toString();
|
|
265
|
+
const parsedData = JSON.parse(jsonStr);
|
|
266
|
+
const includes = ["view", "coding", "refresh_key", "useActiveIdFocus", "activeId", "active_file", "figma"];
|
|
267
|
+
let newState;
|
|
268
|
+
let changedKeys;
|
|
269
|
+
if (parsedData.type === "state") {
|
|
270
|
+
// Full state (e.g., on initial connect)
|
|
271
|
+
newState = parsedData.state;
|
|
272
|
+
changedKeys = Object.keys(newState).filter((key) => !(0, lodash_1.isEqual)(newState[key], this.state[key]));
|
|
273
|
+
}
|
|
274
|
+
else if (parsedData.type === "update") {
|
|
275
|
+
// Delta update
|
|
276
|
+
const delta = parsedData.state;
|
|
277
|
+
newState = { ...this.state, ...delta };
|
|
278
|
+
changedKeys = Object.keys(delta).filter((key) => !(0, lodash_1.isEqual)(delta[key], this.state[key]));
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const filteredChangedKeys = changedKeys.filter((key) => includes.includes(key));
|
|
284
|
+
if (filteredChangedKeys.length > 0) {
|
|
285
|
+
filteredChangedKeys.forEach((key) => {
|
|
286
|
+
const listener = (0, lodash_1.find)(this.onChangeListeners, (l) => l.key === key);
|
|
287
|
+
if (listener) {
|
|
288
|
+
listener.callback(newState[key]);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.state = newState;
|
|
264
292
|
}
|
|
265
293
|
}
|
|
266
294
|
catch (e) {
|
|
@@ -280,16 +308,13 @@ class RoomState {
|
|
|
280
308
|
state: { [key]: value },
|
|
281
309
|
}));
|
|
282
310
|
}
|
|
283
|
-
else {
|
|
284
|
-
console.log("Cannot update state: Not connected");
|
|
285
|
-
}
|
|
286
311
|
}
|
|
287
312
|
disconnect() {
|
|
288
313
|
if (this.reconnectTimeout) {
|
|
289
314
|
clearTimeout(this.reconnectTimeout);
|
|
290
315
|
}
|
|
291
316
|
if (this.ws) {
|
|
292
|
-
this.ws.
|
|
317
|
+
this.ws.onclose = null; // Prevent reconnect on intentional close
|
|
293
318
|
this.ws.close();
|
|
294
319
|
}
|
|
295
320
|
}
|
|
@@ -300,7 +325,7 @@ class RoomState {
|
|
|
300
325
|
return this.isConnected;
|
|
301
326
|
}
|
|
302
327
|
}
|
|
303
|
-
commander_1.program.version("0.0.
|
|
328
|
+
commander_1.program.version("0.0.15").description("Hapico CLI for project management");
|
|
304
329
|
commander_1.program
|
|
305
330
|
.command("clone <id>")
|
|
306
331
|
.description("Clone a project by ID")
|
|
@@ -411,7 +436,7 @@ commander_1.program
|
|
|
411
436
|
return;
|
|
412
437
|
}
|
|
413
438
|
console.log(`Connecting to WebSocket server`);
|
|
414
|
-
const room = new RoomState(`view_${projectId}
|
|
439
|
+
const room = new RoomState(`view_${projectId}`, []);
|
|
415
440
|
room.connect(async () => {
|
|
416
441
|
devSpinner.succeed("Project started in development mode!");
|
|
417
442
|
const fileManager = new FileManager(srcDir);
|
|
@@ -420,7 +445,7 @@ commander_1.program
|
|
|
420
445
|
fileManager.setOnFileChange((filePath, content) => {
|
|
421
446
|
const es5 = (0, exports.compileES5)(content, filePath);
|
|
422
447
|
console.log(`File changed: ${filePath === null || filePath === void 0 ? void 0 : filePath.replace(srcDir, ".")}`);
|
|
423
|
-
const updatedView =
|
|
448
|
+
const updatedView = initialFiles.map((file) => {
|
|
424
449
|
if (path.join(srcDir, file.path) === filePath) {
|
|
425
450
|
return { ...file, content, es5 };
|
|
426
451
|
}
|
|
@@ -445,7 +470,6 @@ commander_1.program
|
|
|
445
470
|
return;
|
|
446
471
|
}
|
|
447
472
|
const projectType = project.data.type || "view";
|
|
448
|
-
console.log("Project type:", projectType);
|
|
449
473
|
if (projectType === "zalominiapp") {
|
|
450
474
|
qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
|
|
451
475
|
console.log("Scan this QR code to connect to the project:");
|
package/bun.lock
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"@babel/standalone": "^7.28.2",
|
|
8
8
|
"axios": "^1.11.0",
|
|
9
9
|
"commander": "^14.0.0",
|
|
10
|
+
"lodash": "^4.17.21",
|
|
10
11
|
"open": "^10.2.0",
|
|
11
12
|
"ora": "^8.2.0",
|
|
12
13
|
"qrcode-terminal": "^0.12.0",
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
"devDependencies": {
|
|
17
18
|
"@types/babel__standalone": "^7.1.9",
|
|
18
19
|
"@types/commander": "^2.12.5",
|
|
20
|
+
"@types/lodash": "^4.17.20",
|
|
19
21
|
"@types/node": "^24.1.0",
|
|
20
22
|
"@types/qrcode-terminal": "^0.12.2",
|
|
21
23
|
"@types/unzipper": "^0.10.11",
|
|
@@ -47,6 +49,8 @@
|
|
|
47
49
|
|
|
48
50
|
"@types/commander": ["@types/commander@2.12.5", "", { "dependencies": { "commander": "*" } }, "sha512-YXGZ/rz+s57VbzcvEV9fUoXeJlBt5HaKu5iUheiIWNsJs23bz6AnRuRiZBRVBLYyPnixNvVnuzM5pSaxr8Yp/g=="],
|
|
49
51
|
|
|
52
|
+
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
|
|
53
|
+
|
|
50
54
|
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
|
51
55
|
|
|
52
56
|
"@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="],
|
|
@@ -141,6 +145,8 @@
|
|
|
141
145
|
|
|
142
146
|
"jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
|
|
143
147
|
|
|
148
|
+
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
149
|
+
|
|
144
150
|
"log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
|
|
145
151
|
|
|
146
152
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.compileES5 = void 0;
|
|
41
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
42
|
+
const lodash_1 = require("lodash");
|
|
41
43
|
const commander_1 = require("commander");
|
|
42
44
|
const axios_1 = __importDefault(require("axios"));
|
|
43
45
|
const fs = __importStar(require("fs"));
|
|
@@ -226,7 +228,7 @@ class FileManager {
|
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
class RoomState {
|
|
229
|
-
constructor(roomId) {
|
|
231
|
+
constructor(roomId, onChangeListeners) {
|
|
230
232
|
this.files = [];
|
|
231
233
|
this.roomId = roomId;
|
|
232
234
|
this.state = {};
|
|
@@ -234,6 +236,7 @@ class RoomState {
|
|
|
234
236
|
this.ws = null;
|
|
235
237
|
this.reconnectTimeout = null;
|
|
236
238
|
this.reconnectAttempts = 0;
|
|
239
|
+
this.onChangeListeners = onChangeListeners;
|
|
237
240
|
}
|
|
238
241
|
connect(onConnected) {
|
|
239
242
|
if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN)
|
|
@@ -242,25 +245,50 @@ class RoomState {
|
|
|
242
245
|
clearTimeout(this.reconnectTimeout);
|
|
243
246
|
}
|
|
244
247
|
this.ws = new ws_1.WebSocket(`https://base.myworkbeast.com/ws?room=${this.roomId}`);
|
|
245
|
-
this.ws.
|
|
248
|
+
this.ws.onopen = () => {
|
|
249
|
+
console.log(`Connected to room: ${this.roomId}`);
|
|
246
250
|
this.isConnected = true;
|
|
247
251
|
this.reconnectAttempts = 0;
|
|
248
|
-
onConnected === null || onConnected === void 0 ? void 0 : onConnected();
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
|
|
253
|
+
};
|
|
254
|
+
this.ws.onclose = () => {
|
|
255
|
+
console.log(`Disconnected from room: ${this.roomId}`);
|
|
252
256
|
this.isConnected = false;
|
|
253
257
|
this.reconnectAttempts++;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
259
|
+
console.log(`Attempting to reconnect in ${delay / 1000}s...`);
|
|
260
|
+
this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
|
|
261
|
+
};
|
|
257
262
|
this.ws.on("message", (data) => {
|
|
258
263
|
try {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
const jsonStr = typeof data === "string" ? data : data.toString();
|
|
265
|
+
const parsedData = JSON.parse(jsonStr);
|
|
266
|
+
const includes = ["view", "coding", "refresh_key", "useActiveIdFocus", "activeId", "active_file", "figma"];
|
|
267
|
+
let newState;
|
|
268
|
+
let changedKeys;
|
|
269
|
+
if (parsedData.type === "state") {
|
|
270
|
+
// Full state (e.g., on initial connect)
|
|
271
|
+
newState = parsedData.state;
|
|
272
|
+
changedKeys = Object.keys(newState).filter((key) => !(0, lodash_1.isEqual)(newState[key], this.state[key]));
|
|
273
|
+
}
|
|
274
|
+
else if (parsedData.type === "update") {
|
|
275
|
+
// Delta update
|
|
276
|
+
const delta = parsedData.state;
|
|
277
|
+
newState = { ...this.state, ...delta };
|
|
278
|
+
changedKeys = Object.keys(delta).filter((key) => !(0, lodash_1.isEqual)(delta[key], this.state[key]));
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const filteredChangedKeys = changedKeys.filter((key) => includes.includes(key));
|
|
284
|
+
if (filteredChangedKeys.length > 0) {
|
|
285
|
+
filteredChangedKeys.forEach((key) => {
|
|
286
|
+
const listener = (0, lodash_1.find)(this.onChangeListeners, (l) => l.key === key);
|
|
287
|
+
if (listener) {
|
|
288
|
+
listener.callback(newState[key]);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.state = newState;
|
|
264
292
|
}
|
|
265
293
|
}
|
|
266
294
|
catch (e) {
|
|
@@ -280,16 +308,13 @@ class RoomState {
|
|
|
280
308
|
state: { [key]: value },
|
|
281
309
|
}));
|
|
282
310
|
}
|
|
283
|
-
else {
|
|
284
|
-
console.log("Cannot update state: Not connected");
|
|
285
|
-
}
|
|
286
311
|
}
|
|
287
312
|
disconnect() {
|
|
288
313
|
if (this.reconnectTimeout) {
|
|
289
314
|
clearTimeout(this.reconnectTimeout);
|
|
290
315
|
}
|
|
291
316
|
if (this.ws) {
|
|
292
|
-
this.ws.
|
|
317
|
+
this.ws.onclose = null; // Prevent reconnect on intentional close
|
|
293
318
|
this.ws.close();
|
|
294
319
|
}
|
|
295
320
|
}
|
|
@@ -300,7 +325,7 @@ class RoomState {
|
|
|
300
325
|
return this.isConnected;
|
|
301
326
|
}
|
|
302
327
|
}
|
|
303
|
-
commander_1.program.version("0.0.
|
|
328
|
+
commander_1.program.version("0.0.15").description("Hapico CLI for project management");
|
|
304
329
|
commander_1.program
|
|
305
330
|
.command("clone <id>")
|
|
306
331
|
.description("Clone a project by ID")
|
|
@@ -411,7 +436,7 @@ commander_1.program
|
|
|
411
436
|
return;
|
|
412
437
|
}
|
|
413
438
|
console.log(`Connecting to WebSocket server`);
|
|
414
|
-
const room = new RoomState(`view_${projectId}
|
|
439
|
+
const room = new RoomState(`view_${projectId}`, []);
|
|
415
440
|
room.connect(async () => {
|
|
416
441
|
devSpinner.succeed("Project started in development mode!");
|
|
417
442
|
const fileManager = new FileManager(srcDir);
|
|
@@ -420,7 +445,7 @@ commander_1.program
|
|
|
420
445
|
fileManager.setOnFileChange((filePath, content) => {
|
|
421
446
|
const es5 = (0, exports.compileES5)(content, filePath);
|
|
422
447
|
console.log(`File changed: ${filePath === null || filePath === void 0 ? void 0 : filePath.replace(srcDir, ".")}`);
|
|
423
|
-
const updatedView =
|
|
448
|
+
const updatedView = initialFiles.map((file) => {
|
|
424
449
|
if (path.join(srcDir, file.path) === filePath) {
|
|
425
450
|
return { ...file, content, es5 };
|
|
426
451
|
}
|
|
@@ -445,7 +470,6 @@ commander_1.program
|
|
|
445
470
|
return;
|
|
446
471
|
}
|
|
447
472
|
const projectType = project.data.type || "view";
|
|
448
|
-
console.log("Project type:", projectType);
|
|
449
473
|
if (projectType === "zalominiapp") {
|
|
450
474
|
qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
|
|
451
475
|
console.log("Scan this QR code to connect to the project:");
|
package/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { isEqual, find } from "lodash";
|
|
3
4
|
import { program } from "commander";
|
|
4
5
|
import axios from "axios";
|
|
5
6
|
import * as fs from "fs";
|
|
@@ -249,6 +250,10 @@ class FileManager {
|
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
interface FileContent {
|
|
254
|
+
// Define the structure of FileContent if needed
|
|
255
|
+
}
|
|
256
|
+
|
|
252
257
|
class RoomState {
|
|
253
258
|
private roomId: string;
|
|
254
259
|
private state: RoomStateData;
|
|
@@ -256,16 +261,21 @@ class RoomState {
|
|
|
256
261
|
private ws: WebSocket | null;
|
|
257
262
|
private reconnectTimeout: NodeJS.Timeout | null;
|
|
258
263
|
private reconnectAttempts: number;
|
|
264
|
+
private onChangeListeners: Array<{ key: string; callback: (value: any) => void }>;
|
|
259
265
|
|
|
260
266
|
public files: FileContent[] = [];
|
|
261
267
|
|
|
262
|
-
constructor(
|
|
268
|
+
constructor(
|
|
269
|
+
roomId: string,
|
|
270
|
+
onChangeListeners: Array<{ key: string; callback: (value: any) => void }>
|
|
271
|
+
) {
|
|
263
272
|
this.roomId = roomId;
|
|
264
273
|
this.state = {};
|
|
265
274
|
this.isConnected = false;
|
|
266
275
|
this.ws = null;
|
|
267
276
|
this.reconnectTimeout = null;
|
|
268
277
|
this.reconnectAttempts = 0;
|
|
278
|
+
this.onChangeListeners = onChangeListeners;
|
|
269
279
|
}
|
|
270
280
|
|
|
271
281
|
connect(onConnected?: () => void): void {
|
|
@@ -279,36 +289,69 @@ class RoomState {
|
|
|
279
289
|
`https://base.myworkbeast.com/ws?room=${this.roomId}`
|
|
280
290
|
);
|
|
281
291
|
|
|
282
|
-
this.ws.
|
|
292
|
+
this.ws.onopen = () => {
|
|
293
|
+
console.log(`Connected to room: ${this.roomId}`);
|
|
283
294
|
this.isConnected = true;
|
|
284
295
|
this.reconnectAttempts = 0;
|
|
285
|
-
onConnected?.();
|
|
286
|
-
|
|
287
|
-
});
|
|
296
|
+
onConnected?.(); // Call the onConnected callback if provided
|
|
297
|
+
};
|
|
288
298
|
|
|
289
|
-
this.ws.
|
|
299
|
+
this.ws.onclose = () => {
|
|
300
|
+
console.log(`Disconnected from room: ${this.roomId}`);
|
|
290
301
|
this.isConnected = false;
|
|
291
302
|
|
|
292
303
|
this.reconnectAttempts++;
|
|
293
|
-
|
|
304
|
+
const delay = Math.min(
|
|
305
|
+
1000 * Math.pow(2, this.reconnectAttempts),
|
|
306
|
+
30000
|
|
307
|
+
);
|
|
308
|
+
console.log(`Attempting to reconnect in ${delay / 1000}s...`);
|
|
294
309
|
|
|
295
|
-
this.connect();
|
|
296
|
-
}
|
|
310
|
+
this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
|
|
311
|
+
};
|
|
297
312
|
|
|
298
|
-
this.ws.on("message", (data: Buffer) => {
|
|
313
|
+
this.ws.on("message", (data: Buffer | string) => {
|
|
299
314
|
try {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
315
|
+
const jsonStr = typeof data === "string" ? data : data.toString();
|
|
316
|
+
const parsedData = JSON.parse(jsonStr);
|
|
317
|
+
const includes = ["view", "coding", "refresh_key", "useActiveIdFocus", "activeId", "active_file", "figma"];
|
|
318
|
+
|
|
319
|
+
let newState: RoomStateData;
|
|
320
|
+
let changedKeys;
|
|
321
|
+
|
|
322
|
+
if (parsedData.type === "state") {
|
|
323
|
+
// Full state (e.g., on initial connect)
|
|
324
|
+
newState = parsedData.state;
|
|
325
|
+
changedKeys = Object.keys(newState).filter(
|
|
326
|
+
(key) => !isEqual(newState[key], this.state[key])
|
|
327
|
+
);
|
|
328
|
+
} else if (parsedData.type === "update") {
|
|
329
|
+
// Delta update
|
|
330
|
+
const delta = parsedData.state;
|
|
331
|
+
newState = { ...this.state, ...delta };
|
|
332
|
+
changedKeys = Object.keys(delta).filter(
|
|
333
|
+
(key) => !isEqual(delta[key], this.state[key])
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const filteredChangedKeys = changedKeys.filter((key) => includes.includes(key));
|
|
340
|
+
if (filteredChangedKeys.length > 0) {
|
|
341
|
+
filteredChangedKeys.forEach((key) => {
|
|
342
|
+
const listener = find(this.onChangeListeners, (l) => l.key === key);
|
|
343
|
+
if (listener) {
|
|
344
|
+
listener.callback(newState[key]);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
this.state = newState;
|
|
305
348
|
}
|
|
306
349
|
} catch (e) {
|
|
307
350
|
console.error("Error processing message:", e);
|
|
308
351
|
}
|
|
309
352
|
});
|
|
310
353
|
|
|
311
|
-
this.ws.on("error", (err
|
|
354
|
+
this.ws.on("error", (err) => {
|
|
312
355
|
console.error("WebSocket error:", err);
|
|
313
356
|
this.ws?.close();
|
|
314
357
|
});
|
|
@@ -322,8 +365,6 @@ class RoomState {
|
|
|
322
365
|
state: { [key]: value },
|
|
323
366
|
})
|
|
324
367
|
);
|
|
325
|
-
} else {
|
|
326
|
-
console.log("Cannot update state: Not connected");
|
|
327
368
|
}
|
|
328
369
|
}
|
|
329
370
|
|
|
@@ -332,7 +373,7 @@ class RoomState {
|
|
|
332
373
|
clearTimeout(this.reconnectTimeout);
|
|
333
374
|
}
|
|
334
375
|
if (this.ws) {
|
|
335
|
-
this.ws.
|
|
376
|
+
this.ws.onclose = null; // Prevent reconnect on intentional close
|
|
336
377
|
this.ws.close();
|
|
337
378
|
}
|
|
338
379
|
}
|
|
@@ -346,7 +387,7 @@ class RoomState {
|
|
|
346
387
|
}
|
|
347
388
|
}
|
|
348
389
|
|
|
349
|
-
program.version("0.0.
|
|
390
|
+
program.version("0.0.15").description("Hapico CLI for project management");
|
|
350
391
|
|
|
351
392
|
program
|
|
352
393
|
.command("clone <id>")
|
|
@@ -431,7 +472,9 @@ program
|
|
|
431
472
|
console.error("You need to login first. Use 'hapico login' command.");
|
|
432
473
|
return;
|
|
433
474
|
}
|
|
434
|
-
const devSpinner = ora(
|
|
475
|
+
const devSpinner = ora(
|
|
476
|
+
"Starting the project in development mode..."
|
|
477
|
+
).start();
|
|
435
478
|
const pwd = process.cwd();
|
|
436
479
|
const srcDir = path.join(pwd, "src");
|
|
437
480
|
if (!fs.existsSync(srcDir)) {
|
|
@@ -492,7 +535,7 @@ program
|
|
|
492
535
|
}
|
|
493
536
|
|
|
494
537
|
console.log(`Connecting to WebSocket server`);
|
|
495
|
-
const room = new RoomState(`view_${projectId}
|
|
538
|
+
const room = new RoomState(`view_${projectId}`, []);
|
|
496
539
|
|
|
497
540
|
room.connect(async () => {
|
|
498
541
|
devSpinner.succeed("Project started in development mode!");
|
|
@@ -505,7 +548,7 @@ program
|
|
|
505
548
|
fileManager.setOnFileChange((filePath, content) => {
|
|
506
549
|
const es5 = compileES5(content, filePath);
|
|
507
550
|
console.log(`File changed: ${filePath?.replace(srcDir, ".")}`);
|
|
508
|
-
const updatedView =
|
|
551
|
+
const updatedView = initialFiles.map((file) => {
|
|
509
552
|
if (path.join(srcDir, file.path) === filePath) {
|
|
510
553
|
return { ...file, content, es5 };
|
|
511
554
|
}
|
|
@@ -539,8 +582,6 @@ program
|
|
|
539
582
|
|
|
540
583
|
const projectType = project.data.type || "view";
|
|
541
584
|
|
|
542
|
-
console.log("Project type:", projectType);
|
|
543
|
-
|
|
544
585
|
if (projectType === "zalominiapp") {
|
|
545
586
|
QRCode.generate(
|
|
546
587
|
`https://zalo.me/s/3218692650896662017/player/${projectId}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hapico/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "A simple CLI tool for project management",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"@babel/standalone": "^7.28.2",
|
|
24
24
|
"axios": "^1.11.0",
|
|
25
25
|
"commander": "^14.0.0",
|
|
26
|
+
"lodash": "^4.17.21",
|
|
26
27
|
"open": "^10.2.0",
|
|
27
28
|
"ora": "^8.2.0",
|
|
28
29
|
"qrcode-terminal": "^0.12.0",
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@types/babel__standalone": "^7.1.9",
|
|
34
35
|
"@types/commander": "^2.12.5",
|
|
36
|
+
"@types/lodash": "^4.17.20",
|
|
35
37
|
"@types/node": "^24.1.0",
|
|
36
38
|
"@types/qrcode-terminal": "^0.12.2",
|
|
37
39
|
"@types/unzipper": "^0.10.11",
|