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.
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/CHANGELOG.md +29 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/PKG-INFO +45 -1
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/README.md +44 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/claude_stop_hook.py +13 -0
- vocalize_cli-0.7.0/hooks/install_quick_action.py +82 -0
- vocalize_cli-0.7.0/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +30 -0
- vocalize_cli-0.7.0/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +205 -0
- vocalize_cli-0.7.0/hooks/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +26 -0
- vocalize_cli-0.7.0/hooks/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +202 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_claude_stop_hook.py +20 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_cli.py +267 -0
- vocalize_cli-0.7.0/tests/test_clipboard.py +38 -0
- vocalize_cli-0.7.0/tests/test_install_quick_action.py +158 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/__init__.py +1 -1
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/cli.py +149 -5
- vocalize_cli-0.7.0/vocalize/clipboard.py +30 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/exceptions.py +4 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.env.example +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.github/workflows/ci.yml +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/.gitignore +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/LICENSE +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/install_hook.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/hooks/speak_url_gate.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/pyproject.toml +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/conftest.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_audio.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_auth.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_config.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_install_hook.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_preprocess.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_speak_url_gate.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_tts.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/tests/test_wizard.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/__main__.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/audio.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/auth.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/config.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/preprocess.py +0 -0
- {vocalize_cli-0.5.0 → vocalize_cli-0.7.0}/vocalize/tts.py +0 -0
- {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.
|
|
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 >/dev/null 2>&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>
|