Add md2pdf: Markdown-to-PDF CLI with Finder Quick Action
- bin/md2pdf: converts .md to PDF via pandoc + headless Chrome (no LaTeX needed); writes alongside source, supports -o and --css - bin/md2pdf.css: clean GitHub-style default stylesheet - bin/md2pdf-install-quickaction: installs the Finder right-click action - macos/md2pdf.workflow: version-controlled Quick Action bundle (right-click .md files to Convert to PDF)
This commit is contained in:
Executable
+152
@@ -0,0 +1,152 @@
|
||||
#!/bin/zsh
|
||||
#
|
||||
# md2pdf — convert Markdown files to PDF.
|
||||
#
|
||||
# Pipeline: pandoc (md -> self-contained styled HTML) -> Chrome headless
|
||||
# (HTML -> PDF). Uses only pandoc + Google Chrome, both assumed installed.
|
||||
#
|
||||
# Usage:
|
||||
# md2pdf FILE.md [FILE2.md ...] convert each; PDF written next to source
|
||||
# md2pdf FILE.md -o OUT.pdf explicit output (single input only)
|
||||
# md2pdf FILE.md --css STYLE.css use a custom stylesheet
|
||||
# md2pdf -h | --help
|
||||
#
|
||||
# Output defaults to the same directory and basename as the input
|
||||
# (report.md -> report.pdf). An existing PDF is overwritten.
|
||||
|
||||
emulate -L zsh
|
||||
set -o pipefail
|
||||
|
||||
prog=${0:t}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
$prog — convert Markdown files to PDF
|
||||
|
||||
Usage:
|
||||
$prog FILE.md [FILE2.md ...] Convert each file; PDF written beside the source
|
||||
$prog FILE.md -o OUT.pdf Write to an explicit path (single input only)
|
||||
$prog FILE.md --css STYLE.css Render with a custom stylesheet
|
||||
$prog -h | --help Show this help
|
||||
|
||||
Notes:
|
||||
- Output defaults to the source directory with a .pdf extension
|
||||
(e.g. notes.md -> notes.pdf) and overwrites any existing PDF.
|
||||
- Requires pandoc and Google Chrome.
|
||||
EOF
|
||||
}
|
||||
|
||||
die() { print -u2 "$prog: $*"; exit 1; }
|
||||
|
||||
# --- locate Google Chrome ---------------------------------------------------
|
||||
find_chrome() {
|
||||
local candidates=(
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
"$HOME/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
||||
)
|
||||
local c
|
||||
for c in $candidates; do
|
||||
[[ -x $c ]] && { print -r -- "$c"; return 0; }
|
||||
done
|
||||
# Fall back to Spotlight lookup by bundle id.
|
||||
local app
|
||||
app=$(mdfind "kMDItemCFBundleIdentifier == 'com.google.Chrome'" 2>/dev/null | head -1)
|
||||
if [[ -n $app && -x "$app/Contents/MacOS/Google Chrome" ]]; then
|
||||
print -r -- "$app/Contents/MacOS/Google Chrome"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- parse arguments --------------------------------------------------------
|
||||
typeset -a inputs
|
||||
local out="" css=""
|
||||
|
||||
while (( $# )); do
|
||||
case $1 in
|
||||
-h|--help) usage; exit 0 ;;
|
||||
-o|--output)
|
||||
[[ -n $2 ]] || die "option $1 requires a path"
|
||||
out=$2; shift 2 ;;
|
||||
--output=*) out=${1#*=}; shift ;;
|
||||
--css)
|
||||
[[ -n $2 ]] || die "option $1 requires a path"
|
||||
css=$2; shift 2 ;;
|
||||
--css=*) css=${1#*=}; shift ;;
|
||||
--) shift; inputs+=("$@"); break ;;
|
||||
-*) die "unknown option: $1 (try --help)" ;;
|
||||
*) inputs+=("$1"); shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
(( ${#inputs} )) || { usage; exit 1; }
|
||||
[[ -n $out && ${#inputs} -gt 1 ]] && die "-o/--output cannot be used with multiple input files"
|
||||
|
||||
# --- resolve dependencies once ----------------------------------------------
|
||||
command -v pandoc >/dev/null 2>&1 || die "pandoc not found (install with: brew install pandoc)"
|
||||
|
||||
local chrome
|
||||
chrome=$(find_chrome) || die "Google Chrome not found — install it or use a Chromium build"
|
||||
|
||||
# Default stylesheet lives next to the real script (resolve through symlinks).
|
||||
local default_css="${0:A:h}/md2pdf.css"
|
||||
if [[ -n $css ]]; then
|
||||
[[ -f $css ]] || die "stylesheet not found: $css"
|
||||
elif [[ -f $default_css ]]; then
|
||||
css=$default_css
|
||||
fi
|
||||
|
||||
# --- per-run temp workspace, cleaned on exit --------------------------------
|
||||
local workdir
|
||||
workdir=$(mktemp -d "${TMPDIR:-/tmp}/md2pdf.XXXXXX") || die "could not create temp dir"
|
||||
trap 'rm -rf -- "$workdir"' EXIT INT TERM
|
||||
|
||||
# --- convert one file -------------------------------------------------------
|
||||
convert_one() {
|
||||
local in=$1 dest=$2
|
||||
[[ -f $in ]] || { print -u2 "✗ $in: no such file"; return 1; }
|
||||
|
||||
local html="$workdir/${in:t:r}.html"
|
||||
# --standalone gives a full HTML doc (head + linked CSS); an empty title
|
||||
# metadata suppresses pandoc's "please specify a title" warning without
|
||||
# emitting a visible title block above the document's own H1.
|
||||
local -a pandoc_args=(
|
||||
"$in" -f markdown -t html5 --standalone --embed-resources
|
||||
--metadata "title=" -o "$html"
|
||||
)
|
||||
[[ -n $css ]] && pandoc_args+=(--css "$css")
|
||||
|
||||
if ! pandoc $pandoc_args 2>"$workdir/pandoc.err"; then
|
||||
print -u2 "✗ $in: pandoc failed"
|
||||
[[ -s "$workdir/pandoc.err" ]] && print -u2 -- "$(<"$workdir/pandoc.err")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Chrome's headless PDF mode runs an independent instance and does not
|
||||
# disturb a running Chrome profile, so no --user-data-dir is needed. (An
|
||||
# isolated --user-data-dir actually makes headless Chrome hang on exit.)
|
||||
if ! "$chrome" --headless=new --disable-gpu --no-pdf-header-footer \
|
||||
--print-to-pdf="$dest" "file://$html" >/dev/null 2>"$workdir/chrome.err"; then
|
||||
print -u2 "✗ $in: Chrome failed to render PDF"
|
||||
[[ -s "$workdir/chrome.err" ]] && print -u2 -- "$(<"$workdir/chrome.err")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
[[ -s $dest ]] || { print -u2 "✗ $in: no PDF produced"; return 1; }
|
||||
print -- "✓ wrote ${dest}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- drive all inputs -------------------------------------------------------
|
||||
local failures=0 f dest
|
||||
for f in $inputs; do
|
||||
if [[ -n $out ]]; then
|
||||
dest=$out
|
||||
else
|
||||
dest="${f:h}/${f:t:r}.pdf"
|
||||
fi
|
||||
convert_one "$f" "$dest" || (( failures++ ))
|
||||
done
|
||||
|
||||
(( failures == 0 )) || exit 1
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/zsh
|
||||
#
|
||||
# md2pdf-install-quickaction — install (or refresh) the "Convert to PDF"
|
||||
# Finder Quick Action.
|
||||
#
|
||||
# Symlinks the version-controlled workflow bundle from the dotfiles repo into
|
||||
# ~/Library/Services so edits in the repo take effect without reinstalling,
|
||||
# then flushes the Services cache so the menu item appears immediately.
|
||||
#
|
||||
# Usage: md2pdf-install-quickaction [--copy]
|
||||
# --copy Install a copy instead of a symlink (use if you prefer the
|
||||
# installed action to be independent of the repo).
|
||||
|
||||
emulate -L zsh
|
||||
set -o pipefail
|
||||
|
||||
prog=${0:t}
|
||||
die() { print -u2 "$prog: $*"; exit 1; }
|
||||
|
||||
local mode=symlink
|
||||
[[ $1 == --copy ]] && mode=copy
|
||||
[[ -n $1 && $1 != --copy ]] && die "unknown argument: $1 (only --copy is supported)"
|
||||
|
||||
# Source bundle lives next to this script: ../macos/md2pdf.workflow
|
||||
local src="${0:A:h:h}/macos/md2pdf.workflow"
|
||||
[[ -d $src ]] || die "workflow bundle not found at $src"
|
||||
|
||||
local services="$HOME/Library/Services"
|
||||
local dest="$services/md2pdf.workflow"
|
||||
|
||||
mkdir -p "$services" || die "could not create $services"
|
||||
|
||||
# Remove any prior install (symlink or directory) so we start clean.
|
||||
if [[ -L $dest || -e $dest ]]; then
|
||||
rm -rf -- "$dest" || die "could not remove existing $dest"
|
||||
fi
|
||||
|
||||
if [[ $mode == symlink ]]; then
|
||||
ln -s "$src" "$dest" || die "could not symlink workflow into $services"
|
||||
print -- "✓ linked $dest -> $src"
|
||||
else
|
||||
cp -R "$src" "$dest" || die "could not copy workflow into $services"
|
||||
print -- "✓ copied workflow to $dest"
|
||||
fi
|
||||
|
||||
# Refresh the Services/Quick Actions registry so the item shows up now.
|
||||
local pbs="/System/Library/CoreServices/pbs"
|
||||
if [[ -x $pbs ]]; then
|
||||
"$pbs" -flush 2>/dev/null
|
||||
print -- "✓ flushed Services cache"
|
||||
fi
|
||||
# Nudge the Extensions/Quick Actions registration too (harmless if absent).
|
||||
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
|
||||
-f "$dest" 2>/dev/null
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Installed. To use it:
|
||||
• In Finder, right-click one or more .md files
|
||||
• Choose Quick Actions ▸ Convert to PDF (md2pdf) (may be under a "..." submenu)
|
||||
|
||||
If it doesn't appear immediately, toggle it on in:
|
||||
System Settings ▸ Keyboard ▸ Keyboard Shortcuts… ▸ Services ▸ Files and Folders
|
||||
(or log out and back in to fully refresh the Services menu).
|
||||
EOF
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/* md2pdf default stylesheet — clean, GitHub-flavored, print-friendly. */
|
||||
|
||||
@page {
|
||||
margin: 2cm 1.9cm;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
|
||||
sans-serif;
|
||||
line-height: 1.55;
|
||||
color: #1f2328;
|
||||
max-width: 46em;
|
||||
margin: 0 auto;
|
||||
padding: 0 0.5em;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* --- Headings --- */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 1.4em;
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
h1 { font-size: 1.9em; border-bottom: 1px solid #d1d9e0; padding-bottom: 0.3em; }
|
||||
h2 { font-size: 1.45em; border-bottom: 1px solid #d1d9e0; padding-bottom: 0.3em; }
|
||||
h3 { font-size: 1.2em; }
|
||||
h4 { font-size: 1em; }
|
||||
h5 { font-size: 0.9em; }
|
||||
h6 { font-size: 0.85em; color: #59636e; }
|
||||
h1:first-child, h2:first-child, h3:first-child { margin-top: 0; }
|
||||
|
||||
/* Keep a heading with the content that follows it. */
|
||||
h1, h2, h3, h4, h5, h6 { break-after: avoid; }
|
||||
|
||||
/* --- Body elements --- */
|
||||
p, ul, ol, dl, table, pre, blockquote { margin-top: 0; margin-bottom: 1em; }
|
||||
|
||||
a { color: #0969da; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
strong { font-weight: 600; }
|
||||
|
||||
ul, ol { padding-left: 2em; }
|
||||
li + li { margin-top: 0.25em; }
|
||||
li > ul, li > ol { margin-top: 0.25em; margin-bottom: 0; }
|
||||
|
||||
/* --- Code --- */
|
||||
code, pre, kbd, samp {
|
||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
|
||||
"Liberation Mono", monospace;
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: rgba(129, 139, 152, 0.15);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f6f8fa;
|
||||
border: 1px solid #d1d9e0;
|
||||
border-radius: 6px;
|
||||
padding: 1em;
|
||||
overflow: auto;
|
||||
line-height: 1.45;
|
||||
break-inside: avoid;
|
||||
}
|
||||
pre code {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-size: 100%;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* --- Blockquotes --- */
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
padding: 0 1em;
|
||||
color: #59636e;
|
||||
border-left: 0.25em solid #d1d9e0;
|
||||
}
|
||||
blockquote > :last-child { margin-bottom: 0; }
|
||||
|
||||
/* --- Tables --- */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
break-inside: avoid;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #d1d9e0;
|
||||
padding: 0.5em 0.85em;
|
||||
text-align: left;
|
||||
}
|
||||
th { background-color: #f6f8fa; font-weight: 600; }
|
||||
tr:nth-child(2n) td { background-color: #f6f8fa; }
|
||||
|
||||
/* --- Rules & images --- */
|
||||
hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background-color: #d1d9e0;
|
||||
margin: 1.8em 0;
|
||||
}
|
||||
|
||||
img { max-width: 100%; box-sizing: border-box; }
|
||||
|
||||
/* pandoc wraps the --metadata title in a header; keep it unobtrusive. */
|
||||
header#title-block-header h1.title { margin-bottom: 0.8em; }
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSServices</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSMenuItem</key>
|
||||
<dict>
|
||||
<key>default</key>
|
||||
<string>Convert to PDF (md2pdf)</string>
|
||||
</dict>
|
||||
<key>NSMessage</key>
|
||||
<string>runWorkflowAsService</string>
|
||||
<key>NSRequiredContext</key>
|
||||
<dict>
|
||||
<key>NSApplicationIdentifier</key>
|
||||
<string>com.apple.finder</string>
|
||||
</dict>
|
||||
<key>NSSendFileTypes</key>
|
||||
<array>
|
||||
<string>net.daringfireball.markdown</string>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
<key>NSSendTypes</key>
|
||||
<array>
|
||||
<string>NSFilenamesPboardType</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,222 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AMApplicationBuild</key>
|
||||
<string>521</string>
|
||||
<key>AMApplicationVersion</key>
|
||||
<string>2.10</string>
|
||||
<key>AMDocumentVersion</key>
|
||||
<string>2</string>
|
||||
<key>actions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>action</key>
|
||||
<dict>
|
||||
<key>AMAccepts</key>
|
||||
<dict>
|
||||
<key>Container</key>
|
||||
<string>List</string>
|
||||
<key>Optional</key>
|
||||
<true/>
|
||||
<key>Types</key>
|
||||
<array>
|
||||
<string>com.apple.cocoa.string</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>AMActionVersion</key>
|
||||
<string>2.0.3</string>
|
||||
<key>AMApplication</key>
|
||||
<array>
|
||||
<string>Automator</string>
|
||||
</array>
|
||||
<key>AMParameterProperties</key>
|
||||
<dict>
|
||||
<key>COMMAND_STRING</key>
|
||||
<dict/>
|
||||
<key>CheckedForUserDefaultShell</key>
|
||||
<dict/>
|
||||
<key>inputMethod</key>
|
||||
<dict/>
|
||||
<key>shell</key>
|
||||
<dict/>
|
||||
<key>source</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>AMProvides</key>
|
||||
<dict>
|
||||
<key>Container</key>
|
||||
<string>List</string>
|
||||
<key>Types</key>
|
||||
<array>
|
||||
<string>com.apple.cocoa.string</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>ActionBundlePath</key>
|
||||
<string>/System/Library/Automator/Run Shell Script.action</string>
|
||||
<key>ActionName</key>
|
||||
<string>Run Shell Script</string>
|
||||
<key>ActionParameters</key>
|
||||
<dict>
|
||||
<key>COMMAND_STRING</key>
|
||||
<string># md2pdf Quick Action — convert each selected Markdown file to PDF.
|
||||
# Files arrive as arguments ("$@") because inputMethod is set to 1.
|
||||
tool="$HOME/bin/md2pdf"
|
||||
[ -x "$tool" ] || tool="$HOME/dotfiles/bin/md2pdf"
|
||||
|
||||
ok=0
|
||||
fail=0
|
||||
for f in "$@"; do
|
||||
case "$f" in
|
||||
*.md|*.markdown|*.mdown|*.mkd)
|
||||
if "$tool" "$f"; then
|
||||
ok=$((ok + 1))
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Skip anything that isn't Markdown.
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$fail" -gt 0 ]; then
|
||||
osascript -e "display notification \"$ok converted, $fail failed\" with title \"md2pdf\" sound name \"Basso\""
|
||||
elif [ "$ok" -gt 0 ]; then
|
||||
noun="files"
|
||||
if [ "$ok" -eq 1 ]; then noun="file"; fi
|
||||
osascript -e "display notification \"$ok $noun converted to PDF\" with title \"md2pdf\""
|
||||
fi
|
||||
</string>
|
||||
<key>CheckedForUserDefaultShell</key>
|
||||
<true/>
|
||||
<key>inputMethod</key>
|
||||
<integer>1</integer>
|
||||
<key>shell</key>
|
||||
<string>/bin/zsh</string>
|
||||
<key>source</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<key>BundleIdentifier</key>
|
||||
<string>com.apple.RunShellScript</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.3</string>
|
||||
<key>CanShowSelectedItemsWhenRun</key>
|
||||
<false/>
|
||||
<key>CanShowWhenRun</key>
|
||||
<true/>
|
||||
<key>Category</key>
|
||||
<array>
|
||||
<string>AMCategoryUtilities</string>
|
||||
</array>
|
||||
<key>Class Name</key>
|
||||
<string>RunShellScriptAction</string>
|
||||
<key>InputUUID</key>
|
||||
<string>2A1B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D</string>
|
||||
<key>Keywords</key>
|
||||
<array>
|
||||
<string>Shell</string>
|
||||
<string>Script</string>
|
||||
<string>Command</string>
|
||||
<string>Run</string>
|
||||
<string>Unix</string>
|
||||
</array>
|
||||
<key>OutputUUID</key>
|
||||
<string>3B2C4D5E-6F7A-4B8C-9D0E-1F2A3B4C5D6E</string>
|
||||
<key>UUID</key>
|
||||
<string>1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5E</string>
|
||||
<key>arguments</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<integer>0</integer>
|
||||
<key>name</key>
|
||||
<string>inputMethod</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>0</string>
|
||||
</dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<false/>
|
||||
<key>name</key>
|
||||
<string>CheckedForUserDefaultShell</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string></string>
|
||||
<key>name</key>
|
||||
<string>source</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>2</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string>/bin/sh</string>
|
||||
<key>name</key>
|
||||
<string>shell</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>3</string>
|
||||
</dict>
|
||||
<key>4</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string></string>
|
||||
<key>name</key>
|
||||
<string>COMMAND_STRING</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>isViewVisible</key>
|
||||
<integer>1</integer>
|
||||
<key>location</key>
|
||||
<string>309.000000:253.000000</string>
|
||||
<key>nibPath</key>
|
||||
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
|
||||
</dict>
|
||||
<key>isViewVisible</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>connectors</key>
|
||||
<dict/>
|
||||
<key>workflowMetaData</key>
|
||||
<dict>
|
||||
<key>serviceInputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.fileSystemObject</string>
|
||||
<key>serviceOutputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.nothing</string>
|
||||
<key>serviceProcessesInput</key>
|
||||
<integer>0</integer>
|
||||
<key>workflowTypeIdentifier</key>
|
||||
<string>com.apple.Automator.servicesMenu</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user