opencode-plugin-boops 2.1.3 → 2.2.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.
package/README.md CHANGED
@@ -49,13 +49,14 @@ Then restart OpenCode. The plugin will be automatically downloaded and installed
49
49
  # Install plugin (adds to OpenCode config)
50
50
  npx opencode-plugin-boops install
51
51
 
52
+ # Configure sounds (TUI browser)
53
+ npx opencode-plugin-boops config # Same as browse
54
+ npx opencode-plugin-boops browse # Browse 448 sounds with semantic tags
55
+
52
56
  # Uninstall plugin (removes from config)
53
57
  npx opencode-plugin-boops uninstall # Interactive - asks about data cleanup
54
58
  npx opencode-plugin-boops uninstall --full # Remove plugin + data
55
59
 
56
- # Browse 448 sounds with semantic tags
57
- npx opencode-plugin-boops browse
58
-
59
60
  # Show help
60
61
  npx opencode-plugin-boops
61
62
  ```
package/cli/browse CHANGED
@@ -96,7 +96,7 @@ function loadCurrentConfig() {
96
96
  }
97
97
  }
98
98
 
99
- // Save config to boops.toml
99
+ // Save config to boops.toml (preserves existing entries and comments)
100
100
  function saveConfig(config) {
101
101
  if (!PLUGIN_INSTALLED) {
102
102
  console.error("\n⚠️ Cannot save: Plugin not installed");
@@ -106,14 +106,51 @@ function saveConfig(config) {
106
106
  }
107
107
 
108
108
  const configPath = join(homedir(), ".config", "opencode", "plugins", "boops", "boops.toml");
109
- let content = "# OpenCode Boops Plugin Configuration\n\n[sounds]\n";
110
109
 
111
- if (config.sounds) {
112
- for (const [event, sound] of Object.entries(config.sounds)) {
113
- content += `"${event}" = "${sound}"\n`;
110
+ // Read existing config to preserve comments and other sections
111
+ let existingContent = "";
112
+ if (existsSync(configPath)) {
113
+ existingContent = readFileSync(configPath, "utf-8");
114
+ }
115
+
116
+ // Parse existing [sounds] section to merge with new values
117
+ const existingSounds = {};
118
+ if (existingContent) {
119
+ const lines = existingContent.split("\n");
120
+ let inSoundsSection = false;
121
+
122
+ for (const line of lines) {
123
+ const trimmed = line.trim();
124
+
125
+ if (trimmed === "[sounds]") {
126
+ inSoundsSection = true;
127
+ continue;
128
+ }
129
+
130
+ if (trimmed.startsWith("[") && trimmed !== "[sounds]") {
131
+ inSoundsSection = false;
132
+ }
133
+
134
+ if (inSoundsSection && !trimmed.startsWith("#") && trimmed) {
135
+ const match = trimmed.match(/^"?([^"=]+)"?\s*=\s*"?([^"#]+)"?/);
136
+ if (match) {
137
+ const [, key, value] = match;
138
+ existingSounds[key.trim()] = value.trim().replace(/"/g, "");
139
+ }
140
+ }
114
141
  }
115
142
  }
116
143
 
144
+ // Merge new sounds with existing
145
+ const mergedSounds = { ...existingSounds, ...(config.sounds || {}) };
146
+
147
+ // Write back, preserving the header
148
+ let content = "# OpenCode Boops Plugin Configuration\n# Sounds can be local file paths OR URLs (automatically downloaded and cached)\n\n[sounds]\n";
149
+
150
+ for (const [event, sound] of Object.entries(mergedSounds).sort()) {
151
+ content += `"${event}" = "${sound}"\n`;
152
+ }
153
+
117
154
  writeFileSync(configPath, content);
118
155
  return true;
119
156
  }
@@ -1116,9 +1153,20 @@ async function browse() {
1116
1153
  const currentSound = config.sounds ? config.sounds[event] : null;
1117
1154
 
1118
1155
  const bg = isSelected ? '\x1b[48;5;235m' : '';
1119
- const eventText = event.length > pickerWidth - 6
1120
- ? event.slice(0, pickerWidth - 9) + '...'
1121
- : event.padEnd(pickerWidth - 6);
1156
+
1157
+ // Show current sound if selected event has one
1158
+ let eventText;
1159
+ if (isSelected && currentSound) {
1160
+ const soundName = currentSound.length > 15 ? currentSound.slice(0, 12) + '...' : currentSound;
1161
+ const label = event.length > pickerWidth - soundName.length - 10
1162
+ ? event.slice(0, pickerWidth - soundName.length - 13) + '...'
1163
+ : event;
1164
+ eventText = `${label} \x1b[38;5;240m→ \x1b[38;5;76m${soundName}\x1b[0m${bg}`.padEnd(pickerWidth - 6 + 20); // +20 for ANSI codes
1165
+ } else {
1166
+ eventText = event.length > pickerWidth - 6
1167
+ ? event.slice(0, pickerWidth - 9) + '...'
1168
+ : event.padEnd(pickerWidth - 6);
1169
+ }
1122
1170
 
1123
1171
  const indicator = currentSound ? '•' : ' ';
1124
1172
  const indicatorColor = currentSound ? '\x1b[38;5;76m' : '';
@@ -1835,20 +1883,17 @@ async function browse() {
1835
1883
  }
1836
1884
  return;
1837
1885
  } else if (key === '\u0013') {
1838
- // Ctrl+S - save assignment
1886
+ // Ctrl+S - save assignment (stay in picker mode to assign more)
1839
1887
  const config = loadCurrentConfig();
1840
1888
  const event = availableEvents[state.pickerSelectedEvent];
1841
1889
 
1842
1890
  if (!config.sounds) config.sounds = {};
1843
- config.sounds[event] = state.pickerSound.id;
1891
+ config.sounds[event] = state.pickerSound.name; // Save friendly name, not ID
1844
1892
 
1845
1893
  // Save to config file
1846
- const saved = saveConfig(config);
1894
+ saveConfig(config);
1847
1895
 
1848
- if (saved) {
1849
- state.pickerMode = false;
1850
- state.pickerSound = null;
1851
- }
1896
+ // Stay in picker mode, just show it was saved
1852
1897
  render();
1853
1898
  return;
1854
1899
  }
package/cli/main CHANGED
@@ -17,6 +17,7 @@ const commands = {
17
17
  install: join(__dirname, "install"),
18
18
  uninstall: join(__dirname, "uninstall"),
19
19
  browse: join(__dirname, "browse"),
20
+ config: join(__dirname, "browse"), // Alias for browse
20
21
  };
21
22
 
22
23
  // Show help if no command or invalid command
@@ -25,7 +26,8 @@ if (!command || !commands[command]) {
25
26
  console.log("Usage:");
26
27
  console.log(" npx opencode-plugin-boops install - Add plugin to OpenCode config");
27
28
  console.log(" npx opencode-plugin-boops uninstall - Remove plugin from config");
28
- console.log(" npx opencode-plugin-boops browse - Browse and test sounds");
29
+ console.log(" npx opencode-plugin-boops config - Configure sounds (TUI)");
30
+ console.log(" npx opencode-plugin-boops browse - Browse and test sounds (same as config)");
29
31
  console.log("");
30
32
  console.log("448 sounds with semantic tags (human, speech, ding, alarm, etc.)");
31
33
  process.exit(command ? 1 : 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-plugin-boops",
3
- "version": "2.1.3",
3
+ "version": "2.2.1",
4
4
  "description": "Sound notifications for OpenCode - plays pleasant sounds when tasks complete or input is needed",
5
5
  "main": "index.ts",
6
6
  "type": "module",