@timmy6942025/cli-timer 1.1.12 → 1.1.14

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.md CHANGED
@@ -49,6 +49,7 @@ By default, timer and stopwatch output is centered in the terminal.
49
49
 
50
50
  - `p` or `Spacebar`: Pause/Resume
51
51
  - `r`: Restart
52
+ - `f`: Random style/font
52
53
  - `q`, `e` or `Ctrl+C`: Exit
53
54
 
54
55
  ## Font Styles
@@ -113,6 +114,7 @@ This launches a Bubble Tea based screen where you can change:
113
114
  - Completion sound/alarm on completion (default Off)
114
115
  - Pause key / pause alt key
115
116
  - Restart key
117
+ - Style key
116
118
  - Exit key / exit alt key
117
119
 
118
120
  When completion sound/alarm is enabled, it plays 5 terminal bell beeps.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmy6942025/cli-timer",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "Simple customizable terminal timer and stopwatch",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -26,6 +26,7 @@ type keybindings struct {
26
26
  PauseKey string `json:"pauseKey"`
27
27
  PauseAltKey string `json:"pauseAltKey"`
28
28
  RestartKey string `json:"restartKey"`
29
+ StyleKey string `json:"styleKey"`
29
30
  ExitKey string `json:"exitKey"`
30
31
  ExitAltKey string `json:"exitAltKey"`
31
32
  }
@@ -34,6 +35,7 @@ var defaultKeybindings = keybindings{
34
35
  PauseKey: "p",
35
36
  PauseAltKey: "space",
36
37
  RestartKey: "r",
38
+ StyleKey: "f",
37
39
  ExitKey: "q",
38
40
  ExitAltKey: "e",
39
41
  }
@@ -156,6 +158,7 @@ func buildMenuItems(cfg config) []list.Item {
156
158
  menuEntry{id: "pauseKey", title: "Pause key", description: keyTokenLabel(cfg.Keybindings.PauseKey)},
157
159
  menuEntry{id: "pauseAltKey", title: "Pause alt key", description: keyTokenLabel(cfg.Keybindings.PauseAltKey)},
158
160
  menuEntry{id: "restartKey", title: "Restart key", description: keyTokenLabel(cfg.Keybindings.RestartKey)},
161
+ menuEntry{id: "styleKey", title: "Style key", description: keyTokenLabel(cfg.Keybindings.StyleKey)},
159
162
  menuEntry{id: "exitKey", title: "Exit key", description: keyTokenLabel(cfg.Keybindings.ExitKey)},
160
163
  menuEntry{id: "exitAltKey", title: "Exit alt key", description: keyTokenLabel(cfg.Keybindings.ExitAltKey)},
161
164
  menuEntry{id: "save", title: "Save and exit", description: "Write settings and close"},
@@ -221,6 +224,7 @@ func normalizeKeybindings(cfg keybindings) keybindings {
221
224
  result.PauseKey = normalizeKeyToken(cfg.PauseKey, result.PauseKey)
222
225
  result.PauseAltKey = normalizeKeyToken(cfg.PauseAltKey, result.PauseAltKey)
223
226
  result.RestartKey = normalizeKeyToken(cfg.RestartKey, result.RestartKey)
227
+ result.StyleKey = normalizeKeyToken(cfg.StyleKey, result.StyleKey)
224
228
  result.ExitKey = normalizeKeyToken(cfg.ExitKey, result.ExitKey)
225
229
  result.ExitAltKey = normalizeKeyToken(cfg.ExitAltKey, result.ExitAltKey)
226
230
  return result
@@ -379,6 +383,8 @@ func (m *model) keyTokenForTarget(target string) string {
379
383
  return m.payload.Config.Keybindings.PauseAltKey
380
384
  case "restartKey":
381
385
  return m.payload.Config.Keybindings.RestartKey
386
+ case "styleKey":
387
+ return m.payload.Config.Keybindings.StyleKey
382
388
  case "exitKey":
383
389
  return m.payload.Config.Keybindings.ExitKey
384
390
  case "exitAltKey":
@@ -396,6 +402,8 @@ func (m *model) setKeyTokenForTarget(target string, token string) {
396
402
  m.payload.Config.Keybindings.PauseAltKey = token
397
403
  case "restartKey":
398
404
  m.payload.Config.Keybindings.RestartKey = token
405
+ case "styleKey":
406
+ m.payload.Config.Keybindings.StyleKey = token
399
407
  case "exitKey":
400
408
  m.payload.Config.Keybindings.ExitKey = token
401
409
  case "exitAltKey":
@@ -479,6 +487,9 @@ func (m *model) applyMenuAction() tea.Cmd {
479
487
  case "restartKey":
480
488
  m.openKeyPicker("restartKey", "Select Restart Key")
481
489
  return nil
490
+ case "styleKey":
491
+ m.openKeyPicker("styleKey", "Select Style Key")
492
+ return nil
482
493
  case "exitKey":
483
494
  m.openKeyPicker("exitKey", "Select Exit Key")
484
495
  return nil
package/src/index.js CHANGED
@@ -39,6 +39,7 @@ const DEFAULT_KEYBINDINGS = Object.freeze({
39
39
  pauseKey: "p",
40
40
  pauseAltKey: "space",
41
41
  restartKey: "r",
42
+ styleKey: "f",
42
43
  exitKey: "q",
43
44
  exitAltKey: "e"
44
45
  });
@@ -47,6 +48,7 @@ const LEGACY_DEFAULT_KEYBINDINGS = Object.freeze({
47
48
  pauseKey: "p",
48
49
  pauseAltKey: "space",
49
50
  restartKey: "r",
51
+ styleKey: "f",
50
52
  exitKey: "s",
51
53
  exitAltKey: "e"
52
54
  });
@@ -324,6 +326,7 @@ function normalizeKeybindings(raw) {
324
326
  next.pauseKey = normalizeKeyToken(raw.pauseKey, next.pauseKey);
325
327
  next.pauseAltKey = normalizeKeyToken(raw.pauseAltKey, next.pauseAltKey);
326
328
  next.restartKey = normalizeKeyToken(raw.restartKey, next.restartKey);
329
+ next.styleKey = normalizeKeyToken(raw.styleKey, next.styleKey);
327
330
  next.exitKey = normalizeKeyToken(raw.exitKey, next.exitKey);
328
331
  next.exitAltKey = normalizeKeyToken(raw.exitAltKey, next.exitAltKey);
329
332
 
@@ -331,6 +334,7 @@ function normalizeKeybindings(raw) {
331
334
  next.pauseKey === LEGACY_DEFAULT_KEYBINDINGS.pauseKey &&
332
335
  next.pauseAltKey === LEGACY_DEFAULT_KEYBINDINGS.pauseAltKey &&
333
336
  next.restartKey === LEGACY_DEFAULT_KEYBINDINGS.restartKey &&
337
+ next.styleKey === LEGACY_DEFAULT_KEYBINDINGS.styleKey &&
334
338
  next.exitKey === LEGACY_DEFAULT_KEYBINDINGS.exitKey &&
335
339
  next.exitAltKey === LEGACY_DEFAULT_KEYBINDINGS.exitAltKey
336
340
  ) {
@@ -432,6 +436,30 @@ function setFontInConfig(requestedFont) {
432
436
  return { ok: true, reason: null, font: updated.font };
433
437
  }
434
438
 
439
+ function pickRandomFont(fonts, currentFont) {
440
+ if (!Array.isArray(fonts) || fonts.length === 0) {
441
+ return null;
442
+ }
443
+ if (fonts.length === 1) {
444
+ return fonts[0];
445
+ }
446
+
447
+ let candidate = currentFont;
448
+ for (let attempt = 0; attempt < 8 && candidate === currentFont; attempt += 1) {
449
+ candidate = fonts[Math.floor(Math.random() * fonts.length)];
450
+ }
451
+
452
+ if (candidate === currentFont) {
453
+ const currentIndex = fonts.findIndex((fontName) => fontName === currentFont);
454
+ if (currentIndex >= 0) {
455
+ return fonts[(currentIndex + 1) % fonts.length];
456
+ }
457
+ return fonts[0];
458
+ }
459
+
460
+ return candidate;
461
+ }
462
+
435
463
  function parseDurationArgs(args) {
436
464
  if (args.length === 0 || args.length % 2 !== 0) {
437
465
  return { ok: false, error: "Duration must be in <number> <unit> pairs." };
@@ -519,8 +547,9 @@ function keyTokenToLabel(token) {
519
547
  function controlsHelpLine(keybindings) {
520
548
  const pause = `${keyTokenToLabel(keybindings.pauseKey)}/${keyTokenToLabel(keybindings.pauseAltKey)}`;
521
549
  const restart = keyTokenToLabel(keybindings.restartKey);
550
+ const style = keyTokenToLabel(keybindings.styleKey);
522
551
  const exit = `${keyTokenToLabel(keybindings.exitKey)}/${keyTokenToLabel(keybindings.exitAltKey)}/Ctrl+C`;
523
- return `Controls: ${pause} Pause-Resume | ${restart} Restart | ${exit} Exit`;
552
+ return `Controls: ${pause} Pause-Resume | ${restart} Restart | ${style} Random Style | ${exit} Exit`;
524
553
  }
525
554
 
526
555
  function keyTokenFromInput(chunk) {
@@ -983,6 +1012,21 @@ function runClock({ mode, initialSeconds, config }) {
983
1012
  draw(true);
984
1013
  }
985
1014
 
1015
+ function cycleStyle() {
1016
+ const fonts = getAllFonts();
1017
+ if (fonts.length === 0) {
1018
+ return;
1019
+ }
1020
+ const nextFont = pickRandomFont(fonts, config.font);
1021
+ if (!nextFont) {
1022
+ return;
1023
+ }
1024
+ const updated = setFontInConfig(nextFont);
1025
+ config.font = updated.ok ? updated.font : nextFont;
1026
+ lastDrawState = "";
1027
+ draw(true);
1028
+ }
1029
+
986
1030
  function onSignal() {
987
1031
  cleanupAndExit(0);
988
1032
  }
@@ -1048,6 +1092,11 @@ function runClock({ mode, initialSeconds, config }) {
1048
1092
  return;
1049
1093
  }
1050
1094
 
1095
+ if (token === config.keybindings.styleKey) {
1096
+ cycleStyle();
1097
+ return;
1098
+ }
1099
+
1051
1100
  if (token === config.keybindings.exitKey || token === config.keybindings.exitAltKey) {
1052
1101
  cleanupAndExit(0);
1053
1102
  }
@@ -1192,7 +1241,7 @@ function printUsage() {
1192
1241
  process.stdout.write("Settings\n");
1193
1242
  process.stdout.write(" timer settings\n\n");
1194
1243
  process.stdout.write("Controls\n");
1195
- process.stdout.write(" Defaults: p/Space Pause-Resume | r Restart | q/e/Ctrl+C Exit\n");
1244
+ process.stdout.write(" Defaults: p/Space Pause-Resume | r Restart | f Random Style | q/e/Ctrl+C Exit\n");
1196
1245
  process.stdout.write(" Keybindings are customizable in `timer settings`.\n\n");
1197
1246
  process.stdout.write("Font Styles\n");
1198
1247
  process.stdout.write(" timer style\n");
@@ -1252,7 +1301,12 @@ function runTimer(args) {
1252
1301
  process.exitCode = 1;
1253
1302
  return;
1254
1303
  }
1255
- const randomFont = fonts[Math.floor(Math.random() * fonts.length)];
1304
+ const randomFont = pickRandomFont(fonts, getFontFromConfig());
1305
+ if (!randomFont) {
1306
+ process.stderr.write("No fonts are available.\n");
1307
+ process.exitCode = 1;
1308
+ return;
1309
+ }
1256
1310
  const result = setFontInConfig(randomFont);
1257
1311
  if (!result.ok) {
1258
1312
  process.stderr.write(`Failed to set random font: ${randomFont}\n`);