@timmy6942025/cli-timer 1.1.3 → 1.1.5
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 +1 -1
- package/src/index.js +100 -11
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -14,6 +14,8 @@ const TIMER_SAMPLE_TEXT = "00:00:00";
|
|
|
14
14
|
|
|
15
15
|
const MIN_TICK_RATE_MS = 50;
|
|
16
16
|
const MAX_TICK_RATE_MS = 1000;
|
|
17
|
+
const MAC_NOTIFICATION_VERIFY_ATTEMPTS = 8;
|
|
18
|
+
const MAC_NOTIFICATION_VERIFY_DELAY_MS = 75;
|
|
17
19
|
|
|
18
20
|
const DEFAULT_KEYBINDINGS = Object.freeze({
|
|
19
21
|
pauseKey: "p",
|
|
@@ -480,18 +482,88 @@ function spawnOk(command, args, options) {
|
|
|
480
482
|
}
|
|
481
483
|
}
|
|
482
484
|
|
|
485
|
+
function sleepSync(ms) {
|
|
486
|
+
const durationMs = Math.max(0, Number(ms) || 0);
|
|
487
|
+
if (durationMs <= 0) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
const waitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
492
|
+
Atomics.wait(waitArray, 0, 0, durationMs);
|
|
493
|
+
} catch (_error) {
|
|
494
|
+
const started = Date.now();
|
|
495
|
+
while (Date.now() - started < durationMs) {
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function readProcessCommand(pid) {
|
|
501
|
+
try {
|
|
502
|
+
const result = spawnSync("ps", ["-p", String(pid), "-o", "comm="], { encoding: "utf8", stdio: "pipe" });
|
|
503
|
+
if (result.error || result.status !== 0) {
|
|
504
|
+
return "";
|
|
505
|
+
}
|
|
506
|
+
return String(result.stdout || "").trim();
|
|
507
|
+
} catch (_error) {
|
|
508
|
+
return "";
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function resolveMacNotificationSenderCandidates() {
|
|
513
|
+
const candidates = [];
|
|
514
|
+
const explicit = String(process.env.CLI_TIMER_NOTIFICATION_SENDER || "").trim();
|
|
515
|
+
if (explicit) {
|
|
516
|
+
candidates.push(explicit);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const termProgram = String(process.env.TERM_PROGRAM || "").toLowerCase();
|
|
520
|
+
if (termProgram === "apple_terminal") {
|
|
521
|
+
candidates.push("com.apple.Terminal");
|
|
522
|
+
} else if (termProgram === "iterm.app") {
|
|
523
|
+
candidates.push("com.googlecode.iterm2");
|
|
524
|
+
} else if (termProgram === "vscode") {
|
|
525
|
+
candidates.push("com.microsoft.VSCode");
|
|
526
|
+
} else if (termProgram === "warpterminal" || termProgram === "warp") {
|
|
527
|
+
candidates.push("dev.warp.Warp-Stable");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const parentCommand = readProcessCommand(process.ppid).toLowerCase();
|
|
531
|
+
if (parentCommand.includes("terminal.app")) {
|
|
532
|
+
candidates.push("com.apple.Terminal");
|
|
533
|
+
}
|
|
534
|
+
if (parentCommand.includes("iterm")) {
|
|
535
|
+
candidates.push("com.googlecode.iterm2");
|
|
536
|
+
}
|
|
537
|
+
if (parentCommand.includes("warp")) {
|
|
538
|
+
candidates.push("dev.warp.Warp-Stable");
|
|
539
|
+
}
|
|
540
|
+
if (parentCommand.includes("visual studio code") || parentCommand.includes("vscode")) {
|
|
541
|
+
candidates.push("com.microsoft.VSCode");
|
|
542
|
+
}
|
|
543
|
+
if (parentCommand.includes("codex.app")) {
|
|
544
|
+
candidates.push("com.openai.codex");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return [...new Set(candidates)].filter(Boolean);
|
|
548
|
+
}
|
|
549
|
+
|
|
483
550
|
function terminalNotifierDelivered(groupId) {
|
|
484
551
|
try {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
552
|
+
for (let attempt = 0; attempt < MAC_NOTIFICATION_VERIFY_ATTEMPTS; attempt += 1) {
|
|
553
|
+
const listed = spawnSync("terminal-notifier", ["-list", groupId], { encoding: "utf8", stdio: "pipe" });
|
|
554
|
+
if (!listed.error && listed.status === 0) {
|
|
555
|
+
const output = String(listed.stdout || "");
|
|
556
|
+
if (output.includes(groupId)) {
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (attempt + 1 < MAC_NOTIFICATION_VERIFY_ATTEMPTS) {
|
|
561
|
+
sleepSync(MAC_NOTIFICATION_VERIFY_DELAY_MS);
|
|
562
|
+
}
|
|
488
563
|
}
|
|
489
|
-
|
|
490
|
-
return output.includes(groupId);
|
|
564
|
+
return false;
|
|
491
565
|
} catch (_error) {
|
|
492
566
|
return false;
|
|
493
|
-
} finally {
|
|
494
|
-
spawnOk("terminal-notifier", ["-remove", groupId]);
|
|
495
567
|
}
|
|
496
568
|
}
|
|
497
569
|
|
|
@@ -505,10 +577,27 @@ function sendSystemNotification({ title, message }) {
|
|
|
505
577
|
|
|
506
578
|
try {
|
|
507
579
|
if (process.platform === "darwin") {
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
580
|
+
const senderCandidates = resolveMacNotificationSenderCandidates();
|
|
581
|
+
const senderArgsSets = senderCandidates.map((bundleId) => ["-sender", bundleId]);
|
|
582
|
+
senderArgsSets.push([]);
|
|
583
|
+
|
|
584
|
+
for (const senderArgs of senderArgsSets) {
|
|
585
|
+
const groupId = `cli-timer-${Date.now()}-${Math.floor(Math.random() * 1e9)}`;
|
|
586
|
+
if (
|
|
587
|
+
spawnOk("terminal-notifier", [
|
|
588
|
+
"-title",
|
|
589
|
+
safeTitle,
|
|
590
|
+
"-message",
|
|
591
|
+
safeMessage,
|
|
592
|
+
"-group",
|
|
593
|
+
groupId,
|
|
594
|
+
"-ignoreDnD",
|
|
595
|
+
...senderArgs
|
|
596
|
+
])
|
|
597
|
+
) {
|
|
598
|
+
if (terminalNotifierDelivered(groupId)) {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
512
601
|
}
|
|
513
602
|
}
|
|
514
603
|
const script = `display notification "${escapeAppleScriptString(safeMessage)}" with title "${escapeAppleScriptString(safeTitle)}"`;
|