klaudio 0.8.0 → 0.8.1

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 (3) hide show
  1. package/README.md +20 -4
  2. package/package.json +1 -1
  3. package/src/cli.js +21 -3
package/README.md CHANGED
@@ -11,9 +11,10 @@ npx klaudio
11
11
  The interactive installer walks you through:
12
12
 
13
13
  1. **Choose scope** — install globally (`~/.claude`) or per-project (`.claude/`), or launch the **Music Player**
14
- 2. **Pick a source** — use a built-in preset, scan your Steam & Epic Games library for sounds, or provide custom files
14
+ 2. **Pick a source** — use a built-in preset, OS system sounds, scan your Steam & Epic Games library, or provide custom files
15
15
  3. **Preview & assign** — listen to sounds and assign them to events (tab to switch between events)
16
- 4. **Install** — writes Claude Code hooks to your `settings.json`
16
+ 4. **Toggle voice summary** — enable TTS to hear a spoken summary when tasks complete
17
+ 5. **Install** — writes Claude Code hooks to your `settings.json`
17
18
 
18
19
  ## Sound Sources
19
20
 
@@ -21,6 +22,10 @@ The interactive installer walks you through:
21
22
 
22
23
  Ready-made sound packs (Retro 8-bit, Minimal Zen, Sci-Fi Terminal, Victory Fanfare) that work out of the box.
23
24
 
25
+ ### System Sounds
26
+
27
+ Use your OS built-in notification sounds (Windows Media, macOS system sounds, Linux sound themes).
28
+
24
29
  ### Game Sound Scanner
25
30
 
26
31
  Scans your local Steam and Epic Games libraries for audio files:
@@ -47,15 +52,25 @@ Play longer game tracks (90s–4min) as background music while you code:
47
52
 
48
53
  Requires previously extracted game audio (use "Scan local games" first).
49
54
 
55
+ ## Voice Summary (TTS)
56
+
57
+ When enabled, klaudio speaks a short summary of what Claude did after playing the task-complete sound. Uses [Piper](https://github.com/rhasspy/piper) for fast, offline neural text-to-speech (auto-downloaded on first use, ~40MB total).
58
+
59
+ - Toggle with `t` on the scope or confirm screen
60
+ - Reads the first sentence of Claude's last message
61
+ - Uses the `en_GB-alan-medium` voice (British male)
62
+ - Hooks receive data via stdin from Claude Code — no extra setup needed
63
+
50
64
  ## Features
51
65
 
52
66
  - **Auto-preview** — sounds play automatically as you browse the list (toggle with `p`)
53
67
  - **Multi-game selection** — pick sounds from different games, tab between events
54
68
  - **Category filtering** — drill into voice, ambient, SFX, etc. when a game has enough variety
55
69
  - **Type-to-filter** — start typing to narrow down long lists
70
+ - **Duration filter** — type `<10s`, `>5s`, `<=3s` etc. to filter by audio length
56
71
  - **10-second clamp** — long sounds are processed with ffmpeg: silence stripped, fade out baked in
57
72
  - **Background scanning** — game list updates live as directories are scanned
58
- - **Pre-loads existing config** — re-running the installer shows your current sound selections
73
+ - **Re-apply current sounds** — re-running the installer shows your current selections with a quick re-apply option
59
74
 
60
75
  ## Events
61
76
 
@@ -74,7 +89,8 @@ npx klaudio --uninstall
74
89
 
75
90
  - Node.js 18+ (Claude Code already requires this)
76
91
  - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed
77
- - For packed audio extraction: internet connection (vgmstream-cli is downloaded automatically)
92
+ - For packed audio extraction: internet connection (vgmstream-cli downloaded automatically)
93
+ - For voice summaries: internet connection on first use (Piper TTS downloaded automatically)
78
94
  - For best playback with fade effects: [ffmpeg/ffplay](https://ffmpeg.org/) on PATH (falls back to native players)
79
95
 
80
96
  > **Note:** Currently only tested on Windows. macOS and Linux support is planned but not yet verified.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klaudio",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Add sound effects to your coding sessions — play sounds when tasks complete, notifications arrive, and more",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -169,8 +169,13 @@ const ScopeScreen = ({ onNext, onMusic, tts, onToggleTts }) => {
169
169
  };
170
170
 
171
171
  // ── Screen: Preset ──────────────────────────────────────────────
172
- const PresetScreen = ({ onNext, onBack }) => {
172
+ const PresetScreen = ({ existingSounds, onNext, onReapply, onBack }) => {
173
+ const hasExisting = existingSounds && Object.values(existingSounds).some(Boolean);
173
174
  const items = [
175
+ ...(hasExisting ? [{
176
+ label: "✓ Re-apply current sounds — update config with current selections",
177
+ value: "_reapply",
178
+ }] : []),
174
179
  ...Object.entries(PRESETS).map(([id, p]) => ({
175
180
  label: `${p.icon} ${p.name} — ${p.description}`,
176
181
  value: id,
@@ -180,18 +185,29 @@ const PresetScreen = ({ onNext, onBack }) => {
180
185
  { label: "🕹️ Scan your games library — find sounds from Steam & Epic Games", value: "_scan" },
181
186
  { label: "📁 Custom files — provide your own sound files", value: "_custom" },
182
187
  ];
183
- const GAP_AT = Object.keys(PRESETS).length; // separator before non-preset options
188
+ const GAP_AT = (hasExisting ? 1 : 0) + Object.keys(PRESETS).length; // separator before non-preset options
184
189
  const [sel, setSel] = useState(0);
185
190
 
186
191
  useInput((input, key) => {
187
192
  if (key.escape) onBack();
188
193
  else if (input === "k" || key.upArrow) setSel((i) => Math.max(0, i - 1));
189
194
  else if (input === "j" || key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
190
- else if (key.return) onNext(items[sel].value);
195
+ else if (key.return) {
196
+ if (items[sel].value === "_reapply") onReapply();
197
+ else onNext(items[sel].value);
198
+ }
191
199
  });
192
200
 
193
201
  return h(Box, { flexDirection: "column" },
194
202
  h(Text, { bold: true }, " Choose a sound preset:"),
203
+ hasExisting
204
+ ? h(Box, { flexDirection: "column", marginLeft: 4, marginBottom: 1 },
205
+ h(Text, { dimColor: true }, "Current sounds:"),
206
+ ...Object.entries(existingSounds).filter(([_, p]) => p).map(([eid, p]) =>
207
+ h(Text, { key: eid, color: "green", dimColor: true }, ` ✓ ${EVENTS[eid].name}: ${basename(p)}`),
208
+ ),
209
+ )
210
+ : null,
195
211
  h(Box, { flexDirection: "column", marginLeft: 2 },
196
212
  ...items.map((item, i) => h(React.Fragment, { key: item.value },
197
213
  i === GAP_AT ? h(Text, { dimColor: true }, "\n ...or pick your own") : null,
@@ -1480,6 +1496,8 @@ const InstallApp = () => {
1480
1496
 
1481
1497
  case SCREEN.PRESET:
1482
1498
  return h(PresetScreen, {
1499
+ existingSounds: sounds,
1500
+ onReapply: () => setScreen(SCREEN.CONFIRM),
1483
1501
  onNext: (id) => {
1484
1502
  if (id === "_music") {
1485
1503
  setScreen(SCREEN.MUSIC_MODE);