pomotty 1.2.0 → 1.2.2

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/README.ja.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](./README.md) | 日本語
4
4
 
5
- Node.js `^22.18.0 || >=24.11.0`で利用できる、ターミナル用のポモドーロタイマーです。
5
+ Node.js `>=22.18.0`で利用できる、ターミナル用のポモドーロタイマーです。
6
6
 
7
7
  ```shell
8
8
  npx pomotty
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [日本語](./README.ja.md)
4
4
 
5
- A Pomodoro timer for the terminal, available on Node.js `^22.18.0 || >=24.11.0`.
5
+ A Pomodoro timer for the terminal, available on Node.js `>=22.18.0`.
6
6
 
7
7
  ```shell
8
8
  npx pomotty
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import path from "node:path";
7
7
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
8
8
  import { on } from "node:events";
9
9
  import { emitKeypressEvents } from "node:readline";
10
- //#region src/diagnostics/writer.ts
10
+ // src/diagnostics/writer.ts
11
11
  /**
12
12
  * 診断の出力先が閉じていても失敗しない書き込み処理を生成します。
13
13
  *
@@ -33,8 +33,7 @@ const createDiagnosticWriter = (stream = process$1.stderr) => {
33
33
  }
34
34
  };
35
35
  };
36
- //#endregion
37
- //#region src/notification/operation.ts
36
+ // src/notification/operation.ts
38
37
  const SUCCESS_EXIT_CODE$1 = 0;
39
38
  const EMPTY_SIZE = 0;
40
39
  const PLAYER_TIMEOUT_MS = 1e4;
@@ -235,8 +234,7 @@ const createOperationTracker = (spawnChild = spawnNodeChild) => {
235
234
  }
236
235
  };
237
236
  };
238
- //#endregion
239
- //#region src/notification/sound.ts
237
+ // src/notification/sound.ts
240
238
  const FALLBACK_SOUNDS = {
241
239
  break: "\x07\x07",
242
240
  work: "\x07"
@@ -285,8 +283,7 @@ const createSoundPlayer = ({ commands, environment = process$1.env, soundDirecto
285
283
  },
286
284
  shutdown: (options) => tracker.shutdown(options)
287
285
  });
288
- //#endregion
289
- //#region src/diagnostics/escape.ts
286
+ // src/diagnostics/escape.ts
290
287
  const BACKSLASH = "\\";
291
288
  const BIDIRECTIONAL_PATTERN = /[\u061C\u200B\u200E\u200F\u2028-\u202E\u2066-\u206F\uFEFF]/u;
292
289
  const CONTROL_HEX_LENGTH = 2;
@@ -318,8 +315,7 @@ const escapeDiagnostic = (value) => {
318
315
  if (codePoints.length > MAXIMUM_CODE_POINTS) return `${QUOTATION_MARK}${escaped}${TRUNCATION_MARK}${QUOTATION_MARK}`;
319
316
  return `${QUOTATION_MARK}${escaped}${QUOTATION_MARK}`;
320
317
  };
321
- //#endregion
322
- //#region src/platform/commands.ts
318
+ // src/platform/commands.ts
323
319
  const AFPLAY_PATH = "/usr/bin/afplay";
324
320
  const DEFAULT_LINUX_PATH = [
325
321
  "/usr/local/bin",
@@ -429,8 +425,7 @@ const resolvePlayerCommands = ({ cwd = process$1.cwd(), environment = process$1.
429
425
  default: return {};
430
426
  }
431
427
  };
432
- //#endregion
433
- //#region src/timer/state.ts
428
+ // src/timer/state.ts
434
429
  const FIRST_CYCLE$1 = 1;
435
430
  const COUNT_INCREMENT = 1;
436
431
  const NO_TIME$1 = 0;
@@ -604,8 +599,7 @@ const runPomodoroTimer = async ({ workDurationMs = DEFAULT_WORK_DURATION_MS, bre
604
599
  onTick?.(state, currentTime);
605
600
  }
606
601
  };
607
- //#endregion
608
- //#region src/terminal/output.ts
602
+ // src/terminal/output.ts
609
603
  /** 出力先が閉じたパイプであることを示すエラーコードです。 */
610
604
  const BROKEN_PIPE_CODE = "EPIPE";
611
605
  const isBrokenPipe = (error) => typeof error === "object" && error !== null && Reflect.get(error, "code") === BROKEN_PIPE_CODE;
@@ -648,15 +642,14 @@ const OPTIONS = [
648
642
  name: "--help"
649
643
  }
650
644
  ];
651
- //#endregion
652
- //#region src/terminal/input.ts
645
+ // src/terminal/input.ts
653
646
  const CHOICES = ["OK", "NG"];
654
647
  const ENTER_KEY_NAMES = /* @__PURE__ */ new Set(["enter", "return"]);
655
648
  const FIRST_CHOICE_INDEX = 0;
656
649
  const KEYPRESS_EVENT_KEY_INDEX = 1;
657
650
  const MOVE_KEY_NAMES = /* @__PURE__ */ new Set(["down", "up"]);
658
651
  const NEXT_INDEX_OFFSET = 1;
659
- const REDRAW_SEQUENCE = `\r\u001B[${CHOICES.length}A\u001B[J`;
652
+ const CLEAR_SELECTION_SEQUENCE = `\r\u001B[${CHOICES.length}A\u001B[J`;
660
653
  const STDIN_CLOSE_EVENTS = ["end", "close"];
661
654
  const SELECTION_QUESTION = "Start working? (Use ↑/↓ to select, Enter to confirm)";
662
655
  const keyFrom = (value) => {
@@ -717,12 +710,16 @@ const handleKey = (key, selectedIndex, terminal) => {
717
710
  }
718
711
  if (MOVE_KEY_NAMES.has(key.name)) {
719
712
  const newSelectedIndex = nextChoiceIndex(selectedIndex);
720
- terminal.write(`${REDRAW_SEQUENCE}${renderSelection(newSelectedIndex)}`);
713
+ terminal.write(`${CLEAR_SELECTION_SEQUENCE}${renderSelection(newSelectedIndex)}`);
721
714
  return newSelectedIndex;
722
715
  }
723
716
  if (ENTER_KEY_NAMES.has(key.name)) {
717
+ if (CHOICES[selectedIndex] === "OK") {
718
+ terminal.write(CLEAR_SELECTION_SEQUENCE);
719
+ return true;
720
+ }
724
721
  terminal.write("\n");
725
- return CHOICES[selectedIndex] === "OK";
722
+ return false;
726
723
  }
727
724
  return selectedIndex;
728
725
  };
@@ -740,8 +737,7 @@ const confirmWorkStart = async ({ createTerminal = createSelectionTerminal, ...t
740
737
  await terminal.restore();
741
738
  }
742
739
  };
743
- //#endregion
744
- //#region src/terminal/constants.ts
740
+ // src/terminal/constants.ts
745
741
  /**
746
742
  * ロゴが収まらない狭い端末で、ロゴの代わりに表示する見出しです。
747
743
  */
@@ -756,8 +752,7 @@ const LOGO = `
756
752
  ██║ ╚██████╔╝██║ ╚═╝ ██║╚██████╔╝ ██║ ██║ ██║
757
753
  ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
758
754
  `;
759
- //#endregion
760
- //#region src/terminal/frame-line.ts
755
+ // src/terminal/frame-line.ts
761
756
  const ELLIPSIS = "…";
762
757
  const NO_WIDTH = 0;
763
758
  const SINGLE_WIDTH = 1;
@@ -813,8 +808,7 @@ const stringifySegment = ({ text, tone }, color) => {
813
808
  };
814
809
  const stringifyFrameLine = (line, color) => line.map((segment) => stringifySegment(segment, color)).join("").trimEnd();
815
810
  const isColorEnabled = (env) => (env.NO_COLOR ?? "") === "";
816
- //#endregion
817
- //#region src/terminal/render.ts
811
+ // src/terminal/render.ts
818
812
  const CLI_OPTION_INDEX = 2;
819
813
  const LOGO_WIDTH = Math.max(...LOGO.split("\n").map((line) => cellWidth(line)));
820
814
  const RIGHT_MARGIN$1 = 1;
@@ -835,8 +829,7 @@ const createCliOutput = ({ columns, options, optionName = process.argv.at(CLI_OP
835
829
  ""
836
830
  ].join("\n");
837
831
  };
838
- //#endregion
839
- //#region src/terminal/inline-renderer.ts
832
+ // src/terminal/inline-renderer.ts
840
833
  const CSI = "\x1B[";
841
834
  const NO_ROWS = 0;
842
835
  const CLEAR_LINE = `${CSI}2K`;
@@ -872,8 +865,7 @@ const createInlineRenderer = (write) => {
872
865
  }
873
866
  };
874
867
  };
875
- //#endregion
876
- //#region src/terminal/terminal-guard.ts
868
+ // src/terminal/terminal-guard.ts
877
869
  const HIDE_CURSOR = "\x1B[?25l";
878
870
  const SHOW_CURSOR = "\x1B[?25h";
879
871
  const TERMINATION_SIGNALS = [
@@ -914,8 +906,7 @@ const createTerminalGuard = ({ onSignal, processEvents, terminal, write }) => {
914
906
  }
915
907
  };
916
908
  };
917
- //#endregion
918
- //#region src/terminal/clock.ts
909
+ // src/terminal/clock.ts
919
910
  const MILLISECONDS_PER_SECOND = 1e3;
920
911
  const SECONDS_PER_MINUTE = 60;
921
912
  const SECONDS_PER_HOUR = 3600;
@@ -946,8 +937,7 @@ const progressFor = (state, now, settings) => {
946
937
  totalSeconds
947
938
  };
948
939
  };
949
- //#endregion
950
- //#region src/terminal/timeline.ts
940
+ // src/terminal/timeline.ts
951
941
  const BLOCK = "█";
952
942
  const EMPTY_CELL = "·";
953
943
  const EIGHTH_BLOCKS = [
@@ -1046,8 +1036,7 @@ const createTimeline = (state, settings, ratio) => {
1046
1036
  [{ text: `${prefix}${createLegend(settings, cells)}` }]
1047
1037
  ];
1048
1038
  };
1049
- //#endregion
1050
- //#region src/terminal/timer-view.ts
1039
+ // src/terminal/timer-view.ts
1051
1040
  const BAR_WIDTH = 40;
1052
1041
  const INDENT = " ";
1053
1042
  const MIN_GAP = 2;
@@ -1253,8 +1242,7 @@ const createTimerFrame = ({ state, now, settings, columns }) => {
1253
1242
  if (width >= COMPACT_MIN_WIDTH) return createCompactFrame(layout).map((line) => truncateLine(line, width));
1254
1243
  return createMinimalFrame(layout).map((line) => truncateLine(line, width));
1255
1244
  };
1256
- //#endregion
1257
- //#region src/terminal/timer-keys.ts
1245
+ // src/terminal/timer-keys.ts
1258
1246
  /** Ctrlなしで受け付けるキー名と操作の対応です。Shift(大文字)は区別しません。 */
1259
1247
  const PLAIN_KEYS = /* @__PURE__ */ new Map([
1260
1248
  ["q", "quit"],
@@ -1288,8 +1276,7 @@ const watchTimerKeys = ({ input = process$1.stdin, onKey }) => {
1288
1276
  if (!inputWasFlowing) input.pause();
1289
1277
  };
1290
1278
  };
1291
- //#endregion
1292
- //#region src/cli/progress-view.ts
1279
+ // src/cli/progress-view.ts
1293
1280
  const PHASE_ICONS = {
1294
1281
  break: "☕",
1295
1282
  work: "🍅"
@@ -1389,8 +1376,7 @@ const createProgressView = ({ color = isColorEnabled(process$1.env), interactive
1389
1376
  write
1390
1377
  });
1391
1378
  };
1392
- //#endregion
1393
- //#region src/cli/parse-arguments.ts
1379
+ // src/cli/parse-arguments.ts
1394
1380
  const ARGUMENT_INDEX_INCREMENT = 1;
1395
1381
  const ARGUMENTS_START_INDEX = 0;
1396
1382
  const DURATION_PATTERN = /^[1-9][0-9]{0,3}$/u;
@@ -1464,8 +1450,7 @@ const parseCliArguments = (arguments_) => {
1464
1450
  workDurationMinutes
1465
1451
  };
1466
1452
  };
1467
- //#endregion
1468
- //#region src/cli/run.ts
1453
+ // src/cli/run.ts
1469
1454
  const CLI_ARGUMENTS_START_INDEX = 2;
1470
1455
  const FAILURE_EXIT_CODE$1 = 1;
1471
1456
  const SUCCESS_EXIT_CODE = 0;
@@ -1593,8 +1578,7 @@ const runCli = async ({ arguments_ = process$1.argv.slice(CLI_ARGUMENTS_START_IN
1593
1578
  throw error;
1594
1579
  }
1595
1580
  };
1596
- //#endregion
1597
- //#region src/cli.ts
1581
+ // src/cli.ts
1598
1582
  const FAILURE_EXIT_CODE = 1;
1599
1583
  const SOUND_DIRECTORY = new URL("../assets/", import.meta.url);
1600
1584
  const UNCONVERTIBLE_VALUE_MESSAGE = "Unable to convert the thrown value to a string";
@@ -1617,5 +1601,4 @@ try {
1617
1601
  } catch (error) {
1618
1602
  handleFailure(error);
1619
1603
  }
1620
- //#endregion
1621
1604
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pomotty",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "An interactive terminal Pomodoro timer with configurable work and break sessions and sound notifications.",
5
5
  "keywords": [
6
6
  "cli",
@@ -34,12 +34,13 @@
34
34
  "registry": "https://registry.npmjs.org/"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "22.20.1",
38
- "oxfmt": "^0.61.0",
39
- "oxlint": "^1.76.0",
40
- "tsdown": "^0.23.0",
37
+ "@types/node": "22.20.5",
38
+ "@vitest/coverage-v8": "^4.1.12",
39
+ "oxfmt": "^0.75.0",
40
+ "oxlint": "^1.90.0",
41
+ "tsdown": "^0.23.4",
41
42
  "typescript": "^7.0.2",
42
- "vitest": "^4.1.10"
43
+ "vitest": "^4.1.12"
43
44
  },
44
45
  "devEngines": {
45
46
  "packageManager": {
@@ -48,17 +49,19 @@
48
49
  }
49
50
  },
50
51
  "engines": {
51
- "node": "^22.18.0 || >=24.11.0"
52
+ "node": ">=22.18.0"
52
53
  },
53
54
  "scripts": {
54
55
  "build": "tsdown",
55
- "typecheck": "tsc -p tsconfig.json",
56
56
  "check": "pnpm typecheck && pnpm build && pnpm test",
57
57
  "start": "pnpm build && node dist/cli.js",
58
+ "typecheck": "tsc --noEmit",
58
59
  "lint": "oxlint . --deny-warnings --report-unused-disable-directives-severity=error",
59
60
  "lint:fix": "oxlint . --fix --deny-warnings --report-unused-disable-directives-severity=error",
60
61
  "format": "oxfmt --write",
61
62
  "format:check": "oxfmt --check",
62
- "test": "vitest run --dir src"
63
+ "test": "vitest run --dir src",
64
+ "test:coverage": "vitest run --dir src --coverage",
65
+ "test:watch": "vitest --dir src"
63
66
  }
64
67
  }