ciphermesh 2.13.0 → 2.14.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/CHANGELOG.md +69 -0
- package/README.md +4 -3
- package/README.pt-BR.md +4 -3
- package/docs/ARCHITECTURE.md +321 -225
- package/docs/SETUP.md +18 -0
- package/docs/commands.json +2 -2
- package/package.json +2 -2
- package/src/client/ChatController.js +22 -4
- package/src/client/UI.js +617 -124
- package/src/client/keyboard.js +388 -0
- package/src/p2p/P2PChatController.js +21 -4
- package/src/shared/desktopNotify.js +204 -0
- package/src/shared/notifyWorker.js +46 -0
- package/src/shared/tips.js +1 -0
package/docs/SETUP.md
CHANGED
|
@@ -293,6 +293,24 @@ Run `/help` in the chat for the **full list**. The main ones:
|
|
|
293
293
|
- Or: `netsh advfirewall firewall add rule name="CipherMesh" dir=in action=allow protocol=TCP localport=3600`
|
|
294
294
|
- Test whether the port responds: `curl ws://IP:3600` or open `http://IP:3600` in the browser (it will error out, but if the port connects it is open)
|
|
295
295
|
|
|
296
|
+
### Shift+Enter does not insert a newline
|
|
297
|
+
|
|
298
|
+
A terminal cannot tell Shift+Enter from Enter unless the application asks it to,
|
|
299
|
+
so on startup CipherMesh requests the two protocols that make the difference
|
|
300
|
+
visible — the kitty keyboard protocol and xterm's `modifyOtherKeys`. Terminals
|
|
301
|
+
that implement neither simply ignore the request.
|
|
302
|
+
|
|
303
|
+
On those, use **Alt+Enter** or **Ctrl+J**, which work everywhere.
|
|
304
|
+
|
|
305
|
+
If a terminal reacts badly to the request, `CIPHERMESH_LEGACY_KEYS=1` turns the
|
|
306
|
+
negotiation off and hands the terminal to blessed untouched:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
CIPHERMESH_LEGACY_KEYS=1 ciphermesh
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Alt+Enter and Ctrl+J keep working in that mode; only Shift+Enter is lost.
|
|
313
|
+
|
|
296
314
|
### "npm install" fails on sodium-native
|
|
297
315
|
|
|
298
316
|
`sodium-native` needs to compile C code. Requirements:
|
package/docs/commands.json
CHANGED
|
@@ -582,7 +582,7 @@
|
|
|
582
582
|
{
|
|
583
583
|
"name": "/notify",
|
|
584
584
|
"args": "",
|
|
585
|
-
"summary": "
|
|
585
|
+
"summary": "Desktop notifications — rate-limited, and muted for the session (with a line saying why) if the OS refuses them",
|
|
586
586
|
"modes": [
|
|
587
587
|
"relay",
|
|
588
588
|
"p2p"
|
|
@@ -618,7 +618,7 @@
|
|
|
618
618
|
{
|
|
619
619
|
"name": "/sound",
|
|
620
620
|
"args": "",
|
|
621
|
-
"summary": "Sound
|
|
621
|
+
"summary": "Sound alerts on incoming messages",
|
|
622
622
|
"modes": [
|
|
623
623
|
"relay",
|
|
624
624
|
"p2p"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ciphermesh",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Secure terminal chat for the local network (LAN) with real end-to-end encryption (E2EE) using libsodium",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/client/index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@eslint/js": "10.0.1",
|
|
70
|
-
"eslint": "10.
|
|
70
|
+
"eslint": "10.9.1",
|
|
71
71
|
"figlet": "^1.11.4",
|
|
72
72
|
"globals": "^17.7.0",
|
|
73
73
|
"prettier": "^3.9.5"
|
|
@@ -2,7 +2,6 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname, resolve } from 'node:path';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import sodium from 'sodium-native';
|
|
5
|
-
import notifier from 'node-notifier';
|
|
6
5
|
import qrcode from 'qrcode-terminal';
|
|
7
6
|
import {
|
|
8
7
|
MSG,
|
|
@@ -84,6 +83,7 @@ import {
|
|
|
84
83
|
import { saveLastSession } from '../shared/lastSession.js';
|
|
85
84
|
import { diagnose, formatDiagnosis } from '../shared/doctor.js';
|
|
86
85
|
import { pluginsCommand } from '../shared/pluginCommand.js';
|
|
86
|
+
import { DesktopNotifier } from '../shared/desktopNotify.js';
|
|
87
87
|
import { COMMANDS } from './UI.js';
|
|
88
88
|
|
|
89
89
|
const TYPING_SEND_INTERVAL = 2000; // debounce: max 1 typing event per 2s
|
|
@@ -132,6 +132,7 @@ export class ChatController {
|
|
|
132
132
|
#inviteRoom;
|
|
133
133
|
#historyStore;
|
|
134
134
|
#receiptsEnabled;
|
|
135
|
+
#desktopNotifier;
|
|
135
136
|
#sentMessageLines; // Map<messageId, { lineIndex, baseLine }>
|
|
136
137
|
#messageReaders; // Map<messageId, Set<nickname>>
|
|
137
138
|
#away;
|
|
@@ -240,6 +241,9 @@ export class ChatController {
|
|
|
240
241
|
this.#coverMode = 'off';
|
|
241
242
|
this.#coverTimer = null;
|
|
242
243
|
this.#paceQueue = [];
|
|
244
|
+
this.#desktopNotifier = new DesktopNotifier({
|
|
245
|
+
onUnavailable: (reason) => this.#onNotifierUnavailable(reason),
|
|
246
|
+
});
|
|
243
247
|
|
|
244
248
|
this.#setupConnectionHandlers();
|
|
245
249
|
this.#setupUIHandlers();
|
|
@@ -2326,7 +2330,7 @@ export class ChatController {
|
|
|
2326
2330
|
|
|
2327
2331
|
// DND / mentions-only gates desktop notifications too.
|
|
2328
2332
|
if (notify && (this.#ui.notifyEnabled || mentioned)) {
|
|
2329
|
-
|
|
2333
|
+
this.#desktopNotifier.notify({
|
|
2330
2334
|
title: mentioned
|
|
2331
2335
|
? `🔔 ${peer.nickname} mentioned you`
|
|
2332
2336
|
: data.isDM
|
|
@@ -2346,6 +2350,17 @@ export class ChatController {
|
|
|
2346
2350
|
}
|
|
2347
2351
|
}
|
|
2348
2352
|
|
|
2353
|
+
// The OS refused a desktop notification (Windows with notifications turned
|
|
2354
|
+
// off for the app is the common case). Say it once, then stay quiet — the
|
|
2355
|
+
// notifier has already stopped trying, so the chat never sees it again.
|
|
2356
|
+
#onNotifierUnavailable(reason) {
|
|
2357
|
+
this.#ui.setNotifyEnabled(false);
|
|
2358
|
+
this.#ui.addInfoMessage(
|
|
2359
|
+
`Desktop notifications unavailable — ${reason}. Muted for this session; ` +
|
|
2360
|
+
'sound alerts still work. Use /notify on to retry.',
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2349
2364
|
// True if an incoming message references my nickname (@nick or standalone word).
|
|
2350
2365
|
#mentionsMe(text) {
|
|
2351
2366
|
return mentionsMe(text, this.#nickname);
|
|
@@ -2664,14 +2679,17 @@ export class ChatController {
|
|
|
2664
2679
|
const notifyArg = parts[1]?.toLowerCase();
|
|
2665
2680
|
if (notifyArg === 'off') {
|
|
2666
2681
|
this.#ui.setNotifyEnabled(false);
|
|
2682
|
+
this.#desktopNotifier.disable();
|
|
2667
2683
|
this.#ui.addInfoMessage('Desktop notifications disabled');
|
|
2668
2684
|
} else if (notifyArg === 'on') {
|
|
2669
2685
|
this.#ui.setNotifyEnabled(true);
|
|
2686
|
+
this.#desktopNotifier.reset(); // give a previously refusing OS another go
|
|
2670
2687
|
this.#ui.addInfoMessage('Desktop notifications enabled');
|
|
2671
2688
|
} else {
|
|
2672
2689
|
const status = this.#ui.notifyEnabled ? 'enabled' : 'disabled';
|
|
2690
|
+
const blocked = this.#desktopNotifier.available ? '' : ' (blocked by the OS)';
|
|
2673
2691
|
this.#ui.addInfoMessage(
|
|
2674
|
-
`Desktop notifications: ${status}. Use /notify on or /notify off`,
|
|
2692
|
+
`Desktop notifications: ${status}${blocked}. Use /notify on or /notify off`,
|
|
2675
2693
|
);
|
|
2676
2694
|
}
|
|
2677
2695
|
break;
|
|
@@ -4605,7 +4623,7 @@ export class ChatController {
|
|
|
4605
4623
|
|
|
4606
4624
|
// Show own message locally
|
|
4607
4625
|
if (replyTo) {
|
|
4608
|
-
this.#ui.addQuoteLine(replyTo.nickname, replyTo.excerpt
|
|
4626
|
+
this.#ui.addQuoteLine(replyTo.nickname, replyTo.excerpt);
|
|
4609
4627
|
}
|
|
4610
4628
|
const ephLabel = this.#ephemeralMode ? this.#formatDuration(this.#ephemeralDurationMs) : null;
|
|
4611
4629
|
const { lineIndex, render } = isAction
|