claude-notification-plugin 1.0.24 → 1.0.27

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.
Files changed (2) hide show
  1. package/notifier/notifier.js +125 -10
  2. package/package.json +2 -3
@@ -5,11 +5,8 @@ import os from 'os';
5
5
  import path from 'path';
6
6
  import process from 'process';
7
7
  import notifier from 'node-notifier';
8
- import player from 'play-sound';
9
8
  import { spawn } from 'child_process';
10
9
 
11
- const audio = player({});
12
-
13
10
  // ----------------------
14
11
  // CONFIG
15
12
  // ----------------------
@@ -222,19 +219,137 @@ function playSound (config) {
222
219
  return;
223
220
  }
224
221
  try {
225
- audio.play(config.sound.file);
222
+ const file = config.sound.file.replace(/'/g, "''");
223
+ const psCommand = `(New-Object Media.SoundPlayer '${file}').PlaySync()`;
224
+ spawn('powershell', ['-Command', psCommand], {
225
+ stdio: 'ignore',
226
+ windowsHide: true,
227
+ });
226
228
  } catch {
227
229
  // silent fail
228
230
  }
229
231
  }
230
232
 
233
+ function pluralize (n, forms) {
234
+ // forms: [one, few, many] e.g. ['секунда', 'секунды', 'секунд']
235
+ if (forms.length === 1) {
236
+ return forms[0];
237
+ }
238
+ if (forms.length === 2) {
239
+ return n === 1 ? forms[0] : forms[1];
240
+ }
241
+ // Slavic pluralization (ru, uk, pl, etc.)
242
+ const abs = Math.abs(n);
243
+ const mod10 = abs % 10;
244
+ const mod100 = abs % 100;
245
+ if (mod10 === 1 && mod100 !== 11) {
246
+ return forms[0];
247
+ }
248
+ if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) {
249
+ return forms[1];
250
+ }
251
+ return forms[2];
252
+ }
253
+
254
+ // ----------------------
255
+ // NUMBER TO WORDS
256
+ // ----------------------
257
+
258
+ const numWordsEn = {
259
+ ones: ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
260
+ 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen',
261
+ 'seventeen', 'eighteen', 'nineteen'],
262
+ tens: ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'],
263
+ };
264
+
265
+ function numberToWordsEn (n) {
266
+ if (n === 0) {
267
+ return 'zero';
268
+ }
269
+ const parts = [];
270
+ if (n >= 1000) {
271
+ parts.push(numWordsEn.ones[Math.floor(n / 1000)] + ' thousand');
272
+ n %= 1000;
273
+ }
274
+ if (n >= 100) {
275
+ parts.push(numWordsEn.ones[Math.floor(n / 100)] + ' hundred');
276
+ n %= 100;
277
+ }
278
+ if (n >= 20) {
279
+ const t = numWordsEn.tens[Math.floor(n / 10)];
280
+ const o = numWordsEn.ones[n % 10];
281
+ parts.push(o ? `${t}-${o}` : t);
282
+ } else if (n > 0) {
283
+ parts.push(numWordsEn.ones[n]);
284
+ }
285
+ return parts.join(' ');
286
+ }
287
+
288
+ // Russian: feminine accusative for "секунду" (одну, две)
289
+ const numWordsRu = {
290
+ ones: ['', 'одну', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять',
291
+ 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать',
292
+ 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
293
+ tens: ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят',
294
+ 'семьдесят', 'восемьдесят', 'девяносто'],
295
+ hundreds: ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот',
296
+ 'семьсот', 'восемьсот', 'девятьсот'],
297
+ thousands: ['тысяча', 'тысячи', 'тысяч'],
298
+ thousandOnes: ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять'],
299
+ };
300
+
301
+ function numberToWordsRu (n) {
302
+ if (n === 0) {
303
+ return 'ноль';
304
+ }
305
+ const parts = [];
306
+ if (n >= 1000) {
307
+ const th = Math.floor(n / 1000);
308
+ if (th >= 100) {
309
+ parts.push(numWordsRu.hundreds[Math.floor(th / 100)]);
310
+ }
311
+ let thRem = th % 100;
312
+ if (thRem >= 20) {
313
+ parts.push(numWordsRu.tens[Math.floor(thRem / 10)]);
314
+ thRem %= 10;
315
+ }
316
+ if (thRem > 0 && thRem < 20) {
317
+ parts.push(thRem < 10 ? numWordsRu.thousandOnes[thRem] : numWordsRu.ones[thRem]);
318
+ }
319
+ parts.push(pluralize(th, numWordsRu.thousands));
320
+ n %= 1000;
321
+ }
322
+ if (n >= 100) {
323
+ parts.push(numWordsRu.hundreds[Math.floor(n / 100)]);
324
+ n %= 100;
325
+ }
326
+ if (n >= 20) {
327
+ parts.push(numWordsRu.tens[Math.floor(n / 10)]);
328
+ n %= 10;
329
+ }
330
+ if (n > 0) {
331
+ parts.push(numWordsRu.ones[n]);
332
+ }
333
+ return parts.join(' ');
334
+ }
335
+
336
+ function numberToWords (n, lang) {
337
+ if (lang === 'ru') {
338
+ return numberToWordsRu(n);
339
+ }
340
+ if (lang === 'en') {
341
+ return numberToWordsEn(n);
342
+ }
343
+ return String(n);
344
+ }
345
+
231
346
  const voicePhrases = {
232
- en: (d) => `Claude finished coding in ${d} seconds`,
233
- ru: (d) => `Клод завершил работу за ${d} секунд`,
234
- de: (d) => `Claude hat die Arbeit in ${d} Sekunden abgeschlossen`,
235
- fr: (d) => `Claude a termine en ${d} secondes`,
236
- es: (d) => `Claude termino en ${d} segundos`,
237
- pt: (d) => `Claude terminou em ${d} segundos`,
347
+ en: (d) => `Claude finished coding in ${numberToWords(d, 'en')} ${pluralize(d, ['second', 'seconds'])}`,
348
+ ru: (d) => `Клод завершил работу за ${numberToWords(d, 'ru')} ${pluralize(d, ['секунду', 'секунды', 'секунд'])}`,
349
+ de: (d) => `Claude hat die Arbeit in ${d} ${pluralize(d, ['Sekunde', 'Sekunden'])} abgeschlossen`,
350
+ fr: (d) => `Claude a termine en ${d} ${pluralize(d, ['seconde', 'secondes'])}`,
351
+ es: (d) => `Claude termino en ${d} ${pluralize(d, ['segundo', 'segundos'])}`,
352
+ pt: (d) => `Claude terminou em ${d} ${pluralize(d, ['segundo', 'segundos'])}`,
238
353
  ja: (d) => `Claudeは${d}秒でコーディングを完了しました`,
239
354
  ko: (d) => `Claude가 ${d}초 만에 코딩을 완료했습니다`,
240
355
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
3
  "productName": "claude-notification-plugin",
4
- "version": "1.0.24",
4
+ "version": "1.0.27",
5
5
  "description": "Telegram and Windows notifications for Claude Code task completion",
6
6
  "type": "module",
7
7
  "engines": {
@@ -43,8 +43,7 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "node-notifier": "^10.0.1",
47
- "play-sound": "^1.1.6"
46
+ "node-notifier": "^10.0.1"
48
47
  },
49
48
  "devDependencies": {
50
49
  "eslint-plugin-import": "^2.31.0"