vocalize-cli 0.5.0__tar.gz → 0.7.0__tar.gz

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 (40) hide show
  1. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/CHANGELOG.md +29 -0
  2. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/PKG-INFO +45 -1
  3. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/README.md +44 -0
  4. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/claude_stop_hook.py +13 -0
  5. vocalize_cli-0.7.0/hooks/install_quick_action.py +82 -0
  6. vocalize_cli-0.7.0/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +30 -0
  7. vocalize_cli-0.7.0/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +205 -0
  8. vocalize_cli-0.7.0/hooks/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +26 -0
  9. vocalize_cli-0.7.0/hooks/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +202 -0
  10. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_claude_stop_hook.py +20 -0
  11. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_cli.py +267 -0
  12. vocalize_cli-0.7.0/tests/test_clipboard.py +38 -0
  13. vocalize_cli-0.7.0/tests/test_install_quick_action.py +158 -0
  14. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/__init__.py +1 -1
  15. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/cli.py +149 -5
  16. vocalize_cli-0.7.0/vocalize/clipboard.py +30 -0
  17. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/exceptions.py +4 -0
  18. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.env.example +0 -0
  19. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.github/workflows/ci.yml +0 -0
  20. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.gitignore +0 -0
  21. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/LICENSE +0 -0
  22. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/install_hook.py +0 -0
  23. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/speak_url_gate.py +0 -0
  24. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/pyproject.toml +0 -0
  25. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/conftest.py +0 -0
  26. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_audio.py +0 -0
  27. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_auth.py +0 -0
  28. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_config.py +0 -0
  29. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_install_hook.py +0 -0
  30. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_preprocess.py +0 -0
  31. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_speak_url_gate.py +0 -0
  32. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_tts.py +0 -0
  33. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_wizard.py +0 -0
  34. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/__main__.py +0 -0
  35. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/audio.py +0 -0
  36. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/auth.py +0 -0
  37. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/config.py +0 -0
  38. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/preprocess.py +0 -0
  39. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/tts.py +0 -0
  40. {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/wizard.py +0 -0
@@ -3,6 +3,35 @@
3
3
  All notable changes to this project are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## 0.7.0 - 2026-08-31
7
+
8
+ ### Added
9
+
10
+ - `vocalize clip` — speaks the macOS clipboard (pbpaste). Stops any
11
+ current playback first, refuses an empty clipboard, and refuses
12
+ credential-shaped content: a single high-entropy token, or one starting
13
+ with a known secret prefix (sk-, pypi-, ghp\_, op://, eyJ, …), is never
14
+ echoed or sent to ElevenLabs. `--allow-secret` bypasses the guard.
15
+ - `--ask-dialog` on speak/speak-file/clip — when overflow is `ask` and no
16
+ terminal is attached, ask via a native macOS dialog (Truncate / Speak
17
+ all / Cancel, 30 s timeout defaulting to Truncate) instead of silently
18
+ truncating. Off by default; the Claude Code Stop hook never uses it.
19
+ - macOS Quick Actions: "Speak with Vocalize" (highlight text in any app →
20
+ right-click → Services, or a keyboard shortcut) and "Stop Vocalize".
21
+ Install with `python3 hooks/install_quick_action.py`; the checked-in
22
+ bundles live in `hooks/quick_actions/`.
23
+
24
+ ## 0.6.0 - 2026-08-31
25
+
26
+ ### Added
27
+
28
+ - `vocalize settings` — prints the resolved settings, one key=value per
29
+ line, so wrapper scripts (like the /speak slash command) can read the
30
+ effective `overflow` and `max_chars` instead of hardcoding them.
31
+ - `--print-length` on the Stop hook: prints the response's character count
32
+ instead of speaking, so a wrapper can decide to ask about truncation
33
+ interactively before any audio is spent.
34
+
6
35
  ## 0.5.0 - 2026-08-31
7
36
 
8
37
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vocalize-cli
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: A CLI that turns text, markdown, or piped stdin into speech via the ElevenLabs API, with markdown-table-aware preprocessing.
5
5
  Project-URL: Homepage, https://github.com/matthager12-collab/vocalize
6
6
  Project-URL: Repository, https://github.com/matthager12-collab/vocalize
@@ -136,6 +136,9 @@ vocalize speak-file report.md
136
136
  # Pipe anything in
137
137
  cat notes.md | vocalize speak-file -
138
138
 
139
+ # Speak whatever is on the macOS clipboard
140
+ vocalize clip
141
+
139
142
  # List available voices and grab an ID
140
143
  vocalize voices
141
144
 
@@ -171,6 +174,14 @@ possible, then sentences, then words — into requests no bigger than
171
174
  `--chunk-chars` (default 9500), so a long read no longer fails the API's
172
175
  own per-request cap.
173
176
 
177
+ `vocalize clip` stops any current playback first, then speaks the
178
+ clipboard. It refuses content shaped like a secret — a single
179
+ high-entropy token, or one starting with a known credential prefix
180
+ (`sk-`, `pypi-`, `ghp_`, `op://`, `eyJ`, …) — without echoing it anywhere;
181
+ `--allow-secret` bypasses the guard when you're sure. This is a
182
+ habit-breaking guard for text copied out of a password manager, not a
183
+ full secret scanner.
184
+
174
185
  ## Configuration
175
186
 
176
187
  Each setting is resolved on its own, taking the first source that supplies
@@ -225,6 +236,10 @@ regardless. With no cap set anywhere there is no overflow, so the mode
225
236
  never fires. Hook-triggered speech still lives under the hook's 15-minute
226
237
  watchdog described below, whatever the mode.
227
238
 
239
+ `vocalize settings` prints the resolved values (one `key=value` per
240
+ line, env and config applied) — handy for wrapper scripts and for checking
241
+ which source won.
242
+
228
243
  Not having a config file is normal and silent. A file that isn't valid TOML
229
244
  is an error naming the file; a key that isn't recognised is a warning on
230
245
  stderr, so a typo doesn't pass unnoticed but doesn't stop the run either.
@@ -237,6 +252,35 @@ in the current directory, then the OS keychain. `vocalize auth login` sets
237
252
  up the keychain entry; `vocalize auth status` shows which of those sources
238
253
  is currently supplying the key.
239
254
 
255
+ ## macOS Quick Actions (highlight → speak)
256
+
257
+ Two Services let you use vocalize from any app without a terminal:
258
+
259
+ - **Speak with Vocalize** — highlight text anywhere, right-click →
260
+ Services → Speak with Vocalize. It stops whatever was already playing
261
+ and reads the selection. If the selection is over your cap and your
262
+ overflow mode is `ask`, a native dialog asks Truncate / Speak all /
263
+ Cancel (that's the `--ask-dialog` flag at work).
264
+ - **Stop Vocalize** — appears in every app's application menu → Services;
265
+ silences playback from anywhere.
266
+
267
+ Install both:
268
+
269
+ ```bash
270
+ python3 hooks/install_quick_action.py
271
+ ```
272
+
273
+ This copies the bundles from `hooks/quick_actions/` into
274
+ `~/Library/Services/` with this machine's absolute `vocalize` path baked
275
+ in, then refreshes the Services registry. To trigger them from the
276
+ keyboard, assign shortcuts under System Settings → Keyboard → Keyboard
277
+ Shortcuts → Services → Text.
278
+
279
+ Some Electron apps (Claude Code desktop among them) don't expose the
280
+ Services menu for text selected in their own window. There, copy the
281
+ selection and use `/speak clip` (or `vocalize clip` in a terminal)
282
+ instead.
283
+
240
284
  ## Claude Code integration
241
285
 
242
286
  The hook scripts ship in the git repository, not the PyPI package — clone
@@ -102,6 +102,9 @@ vocalize speak-file report.md
102
102
  # Pipe anything in
103
103
  cat notes.md | vocalize speak-file -
104
104
 
105
+ # Speak whatever is on the macOS clipboard
106
+ vocalize clip
107
+
105
108
  # List available voices and grab an ID
106
109
  vocalize voices
107
110
 
@@ -137,6 +140,14 @@ possible, then sentences, then words — into requests no bigger than
137
140
  `--chunk-chars` (default 9500), so a long read no longer fails the API's
138
141
  own per-request cap.
139
142
 
143
+ `vocalize clip` stops any current playback first, then speaks the
144
+ clipboard. It refuses content shaped like a secret — a single
145
+ high-entropy token, or one starting with a known credential prefix
146
+ (`sk-`, `pypi-`, `ghp_`, `op://`, `eyJ`, …) — without echoing it anywhere;
147
+ `--allow-secret` bypasses the guard when you're sure. This is a
148
+ habit-breaking guard for text copied out of a password manager, not a
149
+ full secret scanner.
150
+
140
151
  ## Configuration
141
152
 
142
153
  Each setting is resolved on its own, taking the first source that supplies
@@ -191,6 +202,10 @@ regardless. With no cap set anywhere there is no overflow, so the mode
191
202
  never fires. Hook-triggered speech still lives under the hook's 15-minute
192
203
  watchdog described below, whatever the mode.
193
204
 
205
+ `vocalize settings` prints the resolved values (one `key=value` per
206
+ line, env and config applied) — handy for wrapper scripts and for checking
207
+ which source won.
208
+
194
209
  Not having a config file is normal and silent. A file that isn't valid TOML
195
210
  is an error naming the file; a key that isn't recognised is a warning on
196
211
  stderr, so a typo doesn't pass unnoticed but doesn't stop the run either.
@@ -203,6 +218,35 @@ in the current directory, then the OS keychain. `vocalize auth login` sets
203
218
  up the keychain entry; `vocalize auth status` shows which of those sources
204
219
  is currently supplying the key.
205
220
 
221
+ ## macOS Quick Actions (highlight → speak)
222
+
223
+ Two Services let you use vocalize from any app without a terminal:
224
+
225
+ - **Speak with Vocalize** — highlight text anywhere, right-click →
226
+ Services → Speak with Vocalize. It stops whatever was already playing
227
+ and reads the selection. If the selection is over your cap and your
228
+ overflow mode is `ask`, a native dialog asks Truncate / Speak all /
229
+ Cancel (that's the `--ask-dialog` flag at work).
230
+ - **Stop Vocalize** — appears in every app's application menu → Services;
231
+ silences playback from anywhere.
232
+
233
+ Install both:
234
+
235
+ ```bash
236
+ python3 hooks/install_quick_action.py
237
+ ```
238
+
239
+ This copies the bundles from `hooks/quick_actions/` into
240
+ `~/Library/Services/` with this machine's absolute `vocalize` path baked
241
+ in, then refreshes the Services registry. To trigger them from the
242
+ keyboard, assign shortcuts under System Settings → Keyboard → Keyboard
243
+ Shortcuts → Services → Text.
244
+
245
+ Some Electron apps (Claude Code desktop among them) don't expose the
246
+ Services menu for text selected in their own window. There, copy the
247
+ selection and use `/speak clip` (or `vocalize clip` in a terminal)
248
+ instead.
249
+
206
250
  ## Claude Code integration
207
251
 
208
252
  The hook scripts ship in the git repository, not the PyPI package — clone
@@ -9,6 +9,10 @@
9
9
  that response. Run it when you want speech instead of installing the
10
10
  hook and getting it after every turn.
11
11
 
12
+ Add `--print-length` to either mode to print the response's character
13
+ count instead of speaking — for wrappers deciding whether to ask about
14
+ truncation before committing to audio.
15
+
12
16
  Either way it pulls the most recent assistant text message out of the
13
17
  transcript and pipes it through the `vocalize` CLI (the same one used
14
18
  directly from the command line), so there's exactly one code path for
@@ -125,6 +129,15 @@ def main() -> int:
125
129
 
126
130
  text = _extract_last_assistant_text(transcript_path)
127
131
  if not text.strip():
132
+ if "--print-length" in sys.argv[1:]:
133
+ print(0)
134
+ return 0
135
+
136
+ if "--print-length" in sys.argv[1:]:
137
+ # Report the response's size without speaking — the /speak command
138
+ # uses this to decide whether to ask about truncation in-app. Raw
139
+ # (pre-flattening) length: close enough for an ask threshold.
140
+ print(len(text))
128
141
  return 0
129
142
 
130
143
  # VOCALIZE_BIN wins over PATH so a venv install still resolves when the
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ """Copy the vocalize Quick Actions into ~/Library/Services/.
3
+
4
+ Unlike install_hook.py there is no shared settings file to merge into —
5
+ each Service is a self-contained bundle, so "install" is: copy the bundle
6
+ with this machine's absolute vocalize path baked in. Re-running simply
7
+ overwrites the same two bundles.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+ from pathlib import Path
16
+ from xml.sax.saxutils import escape as xml_escape
17
+
18
+ TEMPLATES_DIR = Path(__file__).resolve().parent / "quick_actions"
19
+ SERVICES_DIR = Path.home() / "Library" / "Services"
20
+ BUNDLE_NAMES = ("Speak with Vocalize.workflow", "Stop Vocalize.workflow")
21
+ PLACEHOLDER = "__VOCALIZE_BIN__"
22
+ PBS = "/System/Library/CoreServices/pbs"
23
+
24
+ # The path lands inside BIN="..." in the Quick Action's zsh script. These
25
+ # characters could escape those quotes, so refuse rather than try to quote.
26
+ _UNSAFE_PATH_CHARS = set('"\\`$')
27
+
28
+
29
+ def _resolve_vocalize_bin() -> Path:
30
+ repo_venv = Path(__file__).resolve().parent.parent / ".venv" / "bin" / "vocalize"
31
+ if repo_venv.is_file():
32
+ return repo_venv
33
+ on_path = shutil.which("vocalize")
34
+ if on_path:
35
+ return Path(on_path).resolve()
36
+ print(
37
+ "Could not find a vocalize binary (checked .venv/bin/vocalize next to "
38
+ "this repo, then PATH). Install vocalize first.",
39
+ file=sys.stderr,
40
+ )
41
+ sys.exit(1)
42
+
43
+
44
+ def _install_one(name: str, bin_path: str) -> None:
45
+ src = TEMPLATES_DIR / name
46
+ dest = SERVICES_DIR / name
47
+ if dest.exists():
48
+ shutil.rmtree(dest)
49
+ shutil.copytree(src, dest)
50
+
51
+ wflow = dest / "Contents" / "Resources" / "document.wflow"
52
+ text = wflow.read_text(encoding="utf-8")
53
+ wflow.write_text(text.replace(PLACEHOLDER, xml_escape(bin_path)), encoding="utf-8")
54
+
55
+
56
+ def main() -> int:
57
+ bin_path = str(_resolve_vocalize_bin())
58
+ if _UNSAFE_PATH_CHARS & set(bin_path):
59
+ print(
60
+ f"Refusing to install: the resolved vocalize path contains characters "
61
+ f"that would break the Quick Action script: {bin_path!r}",
62
+ file=sys.stderr,
63
+ )
64
+ return 1
65
+
66
+ SERVICES_DIR.mkdir(parents=True, exist_ok=True)
67
+ for name in BUNDLE_NAMES:
68
+ _install_one(name, bin_path)
69
+ print(f"Installed {name} -> {SERVICES_DIR / name}")
70
+
71
+ # Nudge the Services registry so the new entries appear without a logout.
72
+ subprocess.run([PBS, "-update"], check=False)
73
+
74
+ print(f"\nUsing vocalize at: {bin_path}")
75
+ print('Highlight text in any app, then right-click -> Services -> "Speak with Vocalize".')
76
+ print("Keyboard shortcuts: System Settings -> Keyboard -> Keyboard Shortcuts -> Services.")
77
+ print("If the actions don't appear, re-open the Services submenu once, or log out and in.")
78
+ return 0
79
+
80
+
81
+ if __name__ == "__main__":
82
+ sys.exit(main())
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSServices</key>
6
+ <array>
7
+ <dict>
8
+ <key>NSMenuItem</key>
9
+ <dict>
10
+ <key>default</key>
11
+ <string>Speak with Vocalize</string>
12
+ </dict>
13
+ <key>NSMessage</key>
14
+ <string>runWorkflowAsService</string>
15
+ <key>NSSendTypes</key>
16
+ <array>
17
+ <string>public.utf8-plain-text</string>
18
+ </array>
19
+ </dict>
20
+ </array>
21
+ <key>CFBundleDevelopmentRegion</key>
22
+ <string>en_US</string>
23
+ <key>CFBundleIdentifier</key>
24
+ <string>cards.arda.vocalize.speak</string>
25
+ <key>CFBundleName</key>
26
+ <string>Speak with Vocalize</string>
27
+ <key>CFBundleShortVersionString</key>
28
+ <string>1.0</string>
29
+ </dict>
30
+ </plist>
@@ -0,0 +1,205 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>AMApplicationBuild</key>
6
+ <string>346</string>
7
+ <key>AMApplicationVersion</key>
8
+ <string>2.3</string>
9
+ <key>AMDocumentVersion</key>
10
+ <string>2</string>
11
+ <key>actions</key>
12
+ <array>
13
+ <dict>
14
+ <key>action</key>
15
+ <dict>
16
+ <key>AMAccepts</key>
17
+ <dict>
18
+ <key>Container</key>
19
+ <string>List</string>
20
+ <key>Optional</key>
21
+ <true/>
22
+ <key>Types</key>
23
+ <array>
24
+ <string>com.apple.cocoa.string</string>
25
+ </array>
26
+ </dict>
27
+ <key>AMActionVersion</key>
28
+ <string>2.0.3</string>
29
+ <key>AMApplication</key>
30
+ <array>
31
+ <string>Automator</string>
32
+ </array>
33
+ <key>AMParameterProperties</key>
34
+ <dict>
35
+ <key>COMMAND_STRING</key>
36
+ <dict/>
37
+ <key>CheckedForUserDefaultShell</key>
38
+ <dict/>
39
+ <key>inputMethod</key>
40
+ <dict/>
41
+ <key>shell</key>
42
+ <dict/>
43
+ <key>source</key>
44
+ <dict/>
45
+ </dict>
46
+ <key>AMProvides</key>
47
+ <dict>
48
+ <key>Container</key>
49
+ <string>List</string>
50
+ <key>Types</key>
51
+ <array>
52
+ <string>com.apple.cocoa.string</string>
53
+ </array>
54
+ </dict>
55
+ <key>ActionBundlePath</key>
56
+ <string>/System/Library/Automator/Run Shell Script.action</string>
57
+ <key>ActionName</key>
58
+ <string>Run Shell Script</string>
59
+ <key>ActionParameters</key>
60
+ <dict>
61
+ <key>COMMAND_STRING</key>
62
+ <string># Selected text arrives on stdin. The only interpolated value is the
63
+ # vocalize path, baked in by hooks/install_quick_action.py.
64
+ BIN="__VOCALIZE_BIN__"
65
+ if [[ ! -x "$BIN" ]]; then
66
+ /usr/bin/osascript -e 'display notification "vocalize not found — re-run hooks/install_quick_action.py" with title "Vocalize"'
67
+ exit 1
68
+ fi
69
+ "$BIN" stop &gt;/dev/null 2&gt;&amp;1
70
+ exec "$BIN" speak-file - --play --ask-dialog</string>
71
+ <key>CheckedForUserDefaultShell</key>
72
+ <false/>
73
+ <key>inputMethod</key>
74
+ <integer>0</integer>
75
+ <key>shell</key>
76
+ <string>/bin/zsh</string>
77
+ <key>source</key>
78
+ <string></string>
79
+ </dict>
80
+ <key>BundleIdentifier</key>
81
+ <string>com.apple.RunShellScript</string>
82
+ <key>CFBundleVersion</key>
83
+ <string>2.0.3</string>
84
+ <key>CanShowSelectedItemsWhenRun</key>
85
+ <false/>
86
+ <key>CanShowWhenRun</key>
87
+ <true/>
88
+ <key>Category</key>
89
+ <array>
90
+ <string>AMCategoryUtilities</string>
91
+ </array>
92
+ <key>Class Name</key>
93
+ <string>RunShellScriptAction</string>
94
+ <key>InputUUID</key>
95
+ <string>8A4A21F5-3C6E-4F58-9C1D-2B7E6A90D101</string>
96
+ <key>Keywords</key>
97
+ <array>
98
+ <string>Shell</string>
99
+ <string>Script</string>
100
+ <string>Command</string>
101
+ <string>Run</string>
102
+ <string>Unix</string>
103
+ </array>
104
+ <key>OutputUUID</key>
105
+ <string>8A4A21F5-3C6E-4F58-9C1D-2B7E6A90D102</string>
106
+ <key>UUID</key>
107
+ <string>8A4A21F5-3C6E-4F58-9C1D-2B7E6A90D103</string>
108
+ <key>UnlocalizedApplications</key>
109
+ <array>
110
+ <string>Automator</string>
111
+ </array>
112
+ <key>arguments</key>
113
+ <dict>
114
+ <key>0</key>
115
+ <dict>
116
+ <key>default value</key>
117
+ <integer>0</integer>
118
+ <key>name</key>
119
+ <string>inputMethod</string>
120
+ <key>required</key>
121
+ <string>0</string>
122
+ <key>type</key>
123
+ <string>0</string>
124
+ <key>uuid</key>
125
+ <string>0</string>
126
+ </dict>
127
+ <key>1</key>
128
+ <dict>
129
+ <key>default value</key>
130
+ <string></string>
131
+ <key>name</key>
132
+ <string>source</string>
133
+ <key>required</key>
134
+ <string>0</string>
135
+ <key>type</key>
136
+ <string>0</string>
137
+ <key>uuid</key>
138
+ <string>1</string>
139
+ </dict>
140
+ <key>2</key>
141
+ <dict>
142
+ <key>default value</key>
143
+ <false/>
144
+ <key>name</key>
145
+ <string>CheckedForUserDefaultShell</string>
146
+ <key>required</key>
147
+ <string>0</string>
148
+ <key>type</key>
149
+ <string>0</string>
150
+ <key>uuid</key>
151
+ <string>2</string>
152
+ </dict>
153
+ <key>3</key>
154
+ <dict>
155
+ <key>default value</key>
156
+ <string></string>
157
+ <key>name</key>
158
+ <string>COMMAND_STRING</string>
159
+ <key>required</key>
160
+ <string>0</string>
161
+ <key>type</key>
162
+ <string>0</string>
163
+ <key>uuid</key>
164
+ <string>3</string>
165
+ </dict>
166
+ <key>4</key>
167
+ <dict>
168
+ <key>default value</key>
169
+ <string>/bin/sh</string>
170
+ <key>name</key>
171
+ <string>shell</string>
172
+ <key>required</key>
173
+ <string>0</string>
174
+ <key>type</key>
175
+ <string>0</string>
176
+ <key>uuid</key>
177
+ <string>4</string>
178
+ </dict>
179
+ </dict>
180
+ <key>isViewVisible</key>
181
+ <true/>
182
+ </dict>
183
+ <key>isViewVisible</key>
184
+ <true/>
185
+ </dict>
186
+ </array>
187
+ <key>connectors</key>
188
+ <dict/>
189
+ <key>workflowMetaData</key>
190
+ <dict>
191
+ <key>serviceApplicationBundleID</key>
192
+ <string></string>
193
+ <key>serviceApplicationPath</key>
194
+ <string></string>
195
+ <key>serviceInputTypeIdentifier</key>
196
+ <string>com.apple.Automator.text</string>
197
+ <key>serviceOutputTypeIdentifier</key>
198
+ <string>com.apple.Automator.nothing</string>
199
+ <key>serviceProcessesInput</key>
200
+ <integer>1</integer>
201
+ <key>workflowTypeIdentifier</key>
202
+ <string>com.apple.Automator.servicesMenu</string>
203
+ </dict>
204
+ </dict>
205
+ </plist>
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSServices</key>
6
+ <array>
7
+ <dict>
8
+ <key>NSMenuItem</key>
9
+ <dict>
10
+ <key>default</key>
11
+ <string>Stop Vocalize</string>
12
+ </dict>
13
+ <key>NSMessage</key>
14
+ <string>runWorkflowAsService</string>
15
+ </dict>
16
+ </array>
17
+ <key>CFBundleDevelopmentRegion</key>
18
+ <string>en_US</string>
19
+ <key>CFBundleIdentifier</key>
20
+ <string>cards.arda.vocalize.stop</string>
21
+ <key>CFBundleName</key>
22
+ <string>Stop Vocalize</string>
23
+ <key>CFBundleShortVersionString</key>
24
+ <string>1.0</string>
25
+ </dict>
26
+ </plist>