@dkothule/md2pdf 1.0.0
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/LICENSE +21 -0
- package/README.md +248 -0
- package/assets/mermaid.config.json +12 -0
- package/assets/table-style.tex +7 -0
- package/bin/md2pdf +510 -0
- package/docs/ARCHITECTURE.md +93 -0
- package/install-system-deps.sh +52 -0
- package/lib/pandoc_mermaid_filter.py +128 -0
- package/lib/run_pandoc_mermaid_filter.sh +7 -0
- package/md2pdf.config.example +54 -0
- package/package.json +36 -0
- package/requirements.txt +3 -0
- package/scripts/convert-md-to-pdf.sh +10 -0
- package/scripts/install_md2pdf_quick_action.sh +259 -0
- package/scripts/uninstall_md2pdf_quick_action.sh +24 -0
- package/setup-local-deps.sh +31 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
"""
|
|
5
|
+
Pandoc filter for mermaid code blocks. Outputs SVG by default (vector quality in PDF).
|
|
6
|
+
Based on timofurrer/pandoc-mermaid-filter; modified to use SVG for LaTeX/PDF.
|
|
7
|
+
|
|
8
|
+
Requires: mmdc (mermaid-cli) on PATH or MERMAID_BIN.
|
|
9
|
+
For PDF: install librsvg so pandoc can embed SVG (e.g. brew install librsvg).
|
|
10
|
+
Python: pip install pandocfilters (or use PYTHON env to point to a venv that has it).
|
|
11
|
+
Optional: set MERMAID_CONFIG to pass a Mermaid config JSON to mmdc.
|
|
12
|
+
Optional: set MERMAID_IMAGE_PREFIX to control temporary Mermaid asset directory prefix.
|
|
13
|
+
Optional: set MERMAID_LATEX_FORMAT (pdf|svg|png). Default is svg.
|
|
14
|
+
Optional: set MERMAID_PDF_FIT=true|false (default true). If true, Mermaid PDF
|
|
15
|
+
output uses fit-to-content page boxes to avoid page-sized diagram assets.
|
|
16
|
+
Optional: set MERMAID_AUTO_PDF_FALLBACK=true|false (default true). If SVG uses
|
|
17
|
+
foreignObject labels in PDF output pipeline, fallback rendering uses Mermaid PDF.
|
|
18
|
+
"""
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import subprocess
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from pandocfilters import toJSONFilter, Para, Image
|
|
25
|
+
from pandocfilters import get_filename4code, get_caption, get_extension
|
|
26
|
+
except ImportError:
|
|
27
|
+
sys.stderr.write(
|
|
28
|
+
"Missing Python dependency: pandocfilters.\n"
|
|
29
|
+
"Install with: python3 -m pip install pandocfilters\n"
|
|
30
|
+
)
|
|
31
|
+
raise SystemExit(1)
|
|
32
|
+
|
|
33
|
+
MERMAID_BIN = os.environ.get("MERMAID_BIN", "mmdc")
|
|
34
|
+
MERMAID_CONFIG = os.environ.get("MERMAID_CONFIG", None)
|
|
35
|
+
PUPPETEER_CFG = os.environ.get("PUPPETEER_CFG", None)
|
|
36
|
+
MERMAID_IMAGE_PREFIX = os.environ.get("MERMAID_IMAGE_PREFIX", "mermaid")
|
|
37
|
+
MERMAID_LATEX_FORMAT = os.environ.get("MERMAID_LATEX_FORMAT", "svg")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def env_bool(name, default):
|
|
41
|
+
value = os.environ.get(name)
|
|
42
|
+
if value is None:
|
|
43
|
+
return default
|
|
44
|
+
return value.strip().lower() in {"1", "true", "yes", "on"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
MERMAID_AUTO_PDF_FALLBACK = env_bool("MERMAID_AUTO_PDF_FALLBACK", True)
|
|
48
|
+
MERMAID_PDF_FIT = env_bool("MERMAID_PDF_FIT", True)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render_mermaid(src, dest):
|
|
52
|
+
cmd = [MERMAID_BIN, "-i", src, "-o", dest, "-q"]
|
|
53
|
+
if dest.lower().endswith(".pdf") and MERMAID_PDF_FIT:
|
|
54
|
+
cmd.append("--pdfFit")
|
|
55
|
+
if MERMAID_CONFIG is not None:
|
|
56
|
+
cmd.extend(["-c", MERMAID_CONFIG])
|
|
57
|
+
if PUPPETEER_CFG is not None:
|
|
58
|
+
cmd.extend(["-p", PUPPETEER_CFG])
|
|
59
|
+
if os.path.isfile(".puppeteer.json"):
|
|
60
|
+
cmd.extend(["-p", ".puppeteer.json"])
|
|
61
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
62
|
+
if result.returncode != 0:
|
|
63
|
+
sys.stderr.write("Failed to render Mermaid diagram with mermaid-cli.\n")
|
|
64
|
+
sys.stderr.write("Command: " + " ".join(cmd) + "\n")
|
|
65
|
+
if result.stderr:
|
|
66
|
+
sys.stderr.write(result.stderr.strip() + "\n")
|
|
67
|
+
raise SystemExit(result.returncode)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def svg_has_foreign_object(path):
|
|
71
|
+
try:
|
|
72
|
+
with open(path, "rb") as f:
|
|
73
|
+
return b"<foreignObject" in f.read()
|
|
74
|
+
except OSError:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def mermaid(key, value, format_, _):
|
|
79
|
+
if key == "CodeBlock":
|
|
80
|
+
[[ident, classes, keyvals], code] = value
|
|
81
|
+
if "mermaid" in classes:
|
|
82
|
+
caption, typef, keyvals = get_caption(keyvals)
|
|
83
|
+
filename = get_filename4code(MERMAID_IMAGE_PREFIX, code)
|
|
84
|
+
filetype = get_extension(
|
|
85
|
+
format_,
|
|
86
|
+
MERMAID_LATEX_FORMAT,
|
|
87
|
+
html="svg",
|
|
88
|
+
latex=MERMAID_LATEX_FORMAT,
|
|
89
|
+
pdf=MERMAID_LATEX_FORMAT,
|
|
90
|
+
)
|
|
91
|
+
src = filename + ".mmd"
|
|
92
|
+
dest = filename + "." + filetype
|
|
93
|
+
txt = code.encode(sys.getfilesystemencoding())
|
|
94
|
+
with open(src, "wb") as f:
|
|
95
|
+
f.write(txt)
|
|
96
|
+
|
|
97
|
+
if not os.path.isfile(dest):
|
|
98
|
+
render_mermaid(src, dest)
|
|
99
|
+
sys.stderr.write("Created image " + dest + "\n")
|
|
100
|
+
|
|
101
|
+
# Flowchart/graph SVG labels may be emitted via <foreignObject>, which
|
|
102
|
+
# some SVG->PDF pipelines drop. Keep SVG default, but fallback to Mermaid
|
|
103
|
+
# PDF for only affected diagrams when generating PDF output.
|
|
104
|
+
if (
|
|
105
|
+
MERMAID_AUTO_PDF_FALLBACK
|
|
106
|
+
and filetype == "svg"
|
|
107
|
+
and format_ in {"", "pdf", "latex"}
|
|
108
|
+
and svg_has_foreign_object(dest)
|
|
109
|
+
):
|
|
110
|
+
fallback_dest = filename + ".pdf"
|
|
111
|
+
if not os.path.isfile(fallback_dest):
|
|
112
|
+
render_mermaid(src, fallback_dest)
|
|
113
|
+
sys.stderr.write(
|
|
114
|
+
"Created PDF fallback image "
|
|
115
|
+
+ fallback_dest
|
|
116
|
+
+ " (foreignObject detected)\n"
|
|
117
|
+
)
|
|
118
|
+
dest = fallback_dest
|
|
119
|
+
|
|
120
|
+
return Para([Image([ident, [], keyvals], caption, [dest, typef])])
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def main():
|
|
124
|
+
toJSONFilter(mermaid)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
main()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
# Wrapper so pandoc can run the mermaid filter as a single executable.
|
|
5
|
+
# Uses PYTHON if set (e.g. path to venv python), else python3.
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
exec "${PYTHON:-python3}" "$SCRIPT_DIR/pandoc_mermaid_filter.py"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
3
|
+
#
|
|
4
|
+
# Copy this file to one of:
|
|
5
|
+
# - $HOME/.config/md2pdf/config.env (global defaults)
|
|
6
|
+
# - <folder-containing-markdown>/.md2pdfrc (project/doc defaults)
|
|
7
|
+
# or pass it at runtime:
|
|
8
|
+
# md2pdf input.md --config /path/to/config.env
|
|
9
|
+
#
|
|
10
|
+
# All keys are optional.
|
|
11
|
+
|
|
12
|
+
# PDF engine command used by pandoc.
|
|
13
|
+
PDF_ENGINE=xelatex
|
|
14
|
+
|
|
15
|
+
# Pandoc formatting columns.
|
|
16
|
+
PANDOC_COLUMNS=200
|
|
17
|
+
|
|
18
|
+
# Page margins.
|
|
19
|
+
LR_MARGIN=0.7in
|
|
20
|
+
TB_MARGIN=0.5in
|
|
21
|
+
|
|
22
|
+
# Optional LaTeX header file (set empty to disable custom header).
|
|
23
|
+
# Default uses bundled: assets/table-style.tex
|
|
24
|
+
# TABLE_STYLE=/absolute/path/to/table-style.tex
|
|
25
|
+
|
|
26
|
+
# Mermaid/Python executables (command name or absolute path).
|
|
27
|
+
MERMAID_BIN=mmdc
|
|
28
|
+
# Optional Mermaid CLI config JSON.
|
|
29
|
+
# Default uses bundled: assets/mermaid.config.json
|
|
30
|
+
# MERMAID_CONFIG=/absolute/path/to/mermaid.config.json
|
|
31
|
+
|
|
32
|
+
# Mermaid asset format for LaTeX/PDF output (pdf, svg, png).
|
|
33
|
+
# Default is svg to keep high-resolution vector Mermaid diagrams in PDF output.
|
|
34
|
+
MERMAID_LATEX_FORMAT=svg
|
|
35
|
+
|
|
36
|
+
# When rendering Mermaid to PDF (directly or via fallback), use fit-to-content page bounds.
|
|
37
|
+
# This avoids page-sized Mermaid PDF assets that can force one diagram per page.
|
|
38
|
+
MERMAID_PDF_FIT=true
|
|
39
|
+
|
|
40
|
+
# When true, if Mermaid SVG contains foreignObject labels (common with flowchart/graph),
|
|
41
|
+
# md2pdf auto-renders a Mermaid PDF fallback for that diagram to preserve labels in PDF.
|
|
42
|
+
MERMAID_AUTO_PDF_FALLBACK=true
|
|
43
|
+
|
|
44
|
+
PYTHON=python3
|
|
45
|
+
|
|
46
|
+
# Optional puppeteer config file for mermaid-cli.
|
|
47
|
+
# PUPPETEER_CFG=/absolute/path/to/puppeteer-config.json
|
|
48
|
+
|
|
49
|
+
# Temporary Mermaid asset folder behavior.
|
|
50
|
+
# If true, remove generated "<prefix>-<run-id>-images/" folder after conversion.
|
|
51
|
+
CLEANUP_MERMAID_ASSETS=true
|
|
52
|
+
|
|
53
|
+
# Prefix for generated Mermaid asset folder.
|
|
54
|
+
MERMAID_ASSET_PREFIX=md2pdf-mermaid
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dkothule/md2pdf",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Markdown to PDF converter with high-resolution Mermaid diagram rendering",
|
|
5
|
+
"author": "Deepak Kothule",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"LICENSE",
|
|
12
|
+
"README.md",
|
|
13
|
+
"bin/",
|
|
14
|
+
"lib/pandoc_mermaid_filter.py",
|
|
15
|
+
"lib/run_pandoc_mermaid_filter.sh",
|
|
16
|
+
"assets/table-style.tex",
|
|
17
|
+
"assets/mermaid.config.json",
|
|
18
|
+
"scripts/",
|
|
19
|
+
"docs/ARCHITECTURE.md",
|
|
20
|
+
"md2pdf.config.example",
|
|
21
|
+
"install-system-deps.sh",
|
|
22
|
+
"setup-local-deps.sh",
|
|
23
|
+
"requirements.txt"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"md2pdf": "./bin/md2pdf",
|
|
27
|
+
"md2pdf-install-finder-action": "./scripts/install_md2pdf_quick_action.sh",
|
|
28
|
+
"md2pdf-uninstall-finder-action": "./scripts/uninstall_md2pdf_quick_action.sh"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"postinstall": "echo \"Mermaid CLI installed\""
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@mermaid-js/mermaid-cli": "10.9.1"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/requirements.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
# Simple standalone utility for local use from this repo checkout.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
9
|
+
|
|
10
|
+
exec "$PROJECT_ROOT/bin/md2pdf" "$@"
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
resolve_script_dir() {
|
|
7
|
+
local source="${BASH_SOURCE[0]}"
|
|
8
|
+
while [[ -L "$source" ]]; do
|
|
9
|
+
local dir
|
|
10
|
+
dir="$(cd -P "$(dirname "$source")" && pwd)"
|
|
11
|
+
source="$(readlink "$source")"
|
|
12
|
+
if [[ "$source" != /* ]]; then
|
|
13
|
+
source="$dir/$source"
|
|
14
|
+
fi
|
|
15
|
+
done
|
|
16
|
+
cd -P "$(dirname "$source")" && pwd
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
20
|
+
echo "Error: Finder Quick Action installer is only supported on macOS." >&2
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
WORKFLOW_NAME="${WORKFLOW_NAME:-Convert Markdown to PDF}"
|
|
25
|
+
WORKFLOW_DIR="$HOME/Library/Services/${WORKFLOW_NAME}.workflow"
|
|
26
|
+
CONTENTS_DIR="$WORKFLOW_DIR/Contents"
|
|
27
|
+
SCRIPT_DIR="$(resolve_script_dir)"
|
|
28
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
29
|
+
|
|
30
|
+
MD2PDF_SCRIPT=""
|
|
31
|
+
if [[ -x "$PROJECT_ROOT/bin/md2pdf" ]]; then
|
|
32
|
+
MD2PDF_SCRIPT="$PROJECT_ROOT/bin/md2pdf"
|
|
33
|
+
elif command -v md2pdf >/dev/null 2>&1; then
|
|
34
|
+
MD2PDF_SCRIPT="$(command -v md2pdf)"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [[ -z "$MD2PDF_SCRIPT" ]]; then
|
|
38
|
+
cat >&2 <<'EOF'
|
|
39
|
+
Error: could not find md2pdf executable.
|
|
40
|
+
Install from this repo first:
|
|
41
|
+
./setup-local-deps.sh
|
|
42
|
+
Or install globally:
|
|
43
|
+
npm i -g @dkothule/md2pdf
|
|
44
|
+
EOF
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
ACTION_UUID="$(uuidgen)"
|
|
49
|
+
INPUT_UUID="$(uuidgen)"
|
|
50
|
+
OUTPUT_UUID="$(uuidgen)"
|
|
51
|
+
|
|
52
|
+
mkdir -p "$CONTENTS_DIR"
|
|
53
|
+
|
|
54
|
+
cat > "$CONTENTS_DIR/Info.plist" <<'PLIST'
|
|
55
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
56
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
57
|
+
<plist version="1.0">
|
|
58
|
+
<dict>
|
|
59
|
+
<key>NSServices</key>
|
|
60
|
+
<array>
|
|
61
|
+
<dict>
|
|
62
|
+
<key>NSBackgroundColorName</key>
|
|
63
|
+
<string>background</string>
|
|
64
|
+
<key>NSIconName</key>
|
|
65
|
+
<string>NSActionTemplate</string>
|
|
66
|
+
<key>NSMenuItem</key>
|
|
67
|
+
<dict>
|
|
68
|
+
<key>default</key>
|
|
69
|
+
<string>Convert Markdown to PDF</string>
|
|
70
|
+
</dict>
|
|
71
|
+
<key>NSMessage</key>
|
|
72
|
+
<string>runWorkflowAsService</string>
|
|
73
|
+
<key>NSRequiredContext</key>
|
|
74
|
+
<dict>
|
|
75
|
+
<key>NSApplicationIdentifier</key>
|
|
76
|
+
<string>com.apple.finder</string>
|
|
77
|
+
</dict>
|
|
78
|
+
<key>NSSendFileTypes</key>
|
|
79
|
+
<array>
|
|
80
|
+
<string>net.daringfireball.markdown</string>
|
|
81
|
+
<string>md</string>
|
|
82
|
+
</array>
|
|
83
|
+
</dict>
|
|
84
|
+
</array>
|
|
85
|
+
</dict>
|
|
86
|
+
</plist>
|
|
87
|
+
PLIST
|
|
88
|
+
|
|
89
|
+
cat > "$CONTENTS_DIR/document.wflow" <<PLIST
|
|
90
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
91
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
92
|
+
<plist version="1.0">
|
|
93
|
+
<dict>
|
|
94
|
+
<key>AMApplicationBuild</key>
|
|
95
|
+
<string>533</string>
|
|
96
|
+
<key>AMApplicationVersion</key>
|
|
97
|
+
<string>2.10</string>
|
|
98
|
+
<key>AMDocumentVersion</key>
|
|
99
|
+
<string>2</string>
|
|
100
|
+
<key>actions</key>
|
|
101
|
+
<array>
|
|
102
|
+
<dict>
|
|
103
|
+
<key>action</key>
|
|
104
|
+
<dict>
|
|
105
|
+
<key>ActionBundlePath</key>
|
|
106
|
+
<string>/System/Library/Automator/Run Shell Script.action</string>
|
|
107
|
+
<key>ActionName</key>
|
|
108
|
+
<string>Run Shell Script</string>
|
|
109
|
+
<key>ActionParameters</key>
|
|
110
|
+
<dict>
|
|
111
|
+
<key>CheckedForUserDefaultShell</key>
|
|
112
|
+
<true/>
|
|
113
|
+
<key>COMMAND_STRING</key>
|
|
114
|
+
<string>export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:$HOME/.npm-global/bin"
|
|
115
|
+
MD2PDF_SCRIPT="$MD2PDF_SCRIPT"
|
|
116
|
+
if [[ ! -x "\$MD2PDF_SCRIPT" ]] && [[ -z "\$(command -v md2pdf 2>/dev/null)" ]]; then
|
|
117
|
+
print -u2 -- "Error: md2pdf is not in PATH: \$PATH"
|
|
118
|
+
exit 127
|
|
119
|
+
fi
|
|
120
|
+
for f in "\$@"; do
|
|
121
|
+
case "\$f" in
|
|
122
|
+
*.md|*.MD) ;;
|
|
123
|
+
*) continue ;;
|
|
124
|
+
esac
|
|
125
|
+
if [[ -x "\$MD2PDF_SCRIPT" ]]; then
|
|
126
|
+
"\$MD2PDF_SCRIPT" "\$f"
|
|
127
|
+
else
|
|
128
|
+
md2pdf "\$f"
|
|
129
|
+
fi
|
|
130
|
+
done
|
|
131
|
+
</string>
|
|
132
|
+
<key>inputMethod</key>
|
|
133
|
+
<integer>1</integer>
|
|
134
|
+
<key>shell</key>
|
|
135
|
+
<string>/bin/zsh</string>
|
|
136
|
+
<key>source</key>
|
|
137
|
+
<string></string>
|
|
138
|
+
</dict>
|
|
139
|
+
<key>AMAccepts</key>
|
|
140
|
+
<dict>
|
|
141
|
+
<key>Container</key>
|
|
142
|
+
<string>List</string>
|
|
143
|
+
<key>Optional</key>
|
|
144
|
+
<true/>
|
|
145
|
+
<key>Types</key>
|
|
146
|
+
<array>
|
|
147
|
+
<string>com.apple.cocoa.string</string>
|
|
148
|
+
</array>
|
|
149
|
+
</dict>
|
|
150
|
+
<key>AMActionVersion</key>
|
|
151
|
+
<string>2.0.3</string>
|
|
152
|
+
<key>AMApplication</key>
|
|
153
|
+
<array>
|
|
154
|
+
<string>Automator</string>
|
|
155
|
+
</array>
|
|
156
|
+
<key>AMParameterProperties</key>
|
|
157
|
+
<dict>
|
|
158
|
+
<key>CheckedForUserDefaultShell</key>
|
|
159
|
+
<dict/>
|
|
160
|
+
<key>COMMAND_STRING</key>
|
|
161
|
+
<dict/>
|
|
162
|
+
<key>inputMethod</key>
|
|
163
|
+
<dict/>
|
|
164
|
+
<key>shell</key>
|
|
165
|
+
<dict/>
|
|
166
|
+
<key>source</key>
|
|
167
|
+
<dict/>
|
|
168
|
+
</dict>
|
|
169
|
+
<key>AMProvides</key>
|
|
170
|
+
<dict>
|
|
171
|
+
<key>Container</key>
|
|
172
|
+
<string>List</string>
|
|
173
|
+
<key>Types</key>
|
|
174
|
+
<array>
|
|
175
|
+
<string>com.apple.cocoa.string</string>
|
|
176
|
+
</array>
|
|
177
|
+
</dict>
|
|
178
|
+
<key>BundleIdentifier</key>
|
|
179
|
+
<string>com.apple.RunShellScript</string>
|
|
180
|
+
<key>CanShowSelectedItemsWhenRun</key>
|
|
181
|
+
<false/>
|
|
182
|
+
<key>CanShowWhenRun</key>
|
|
183
|
+
<true/>
|
|
184
|
+
<key>Category</key>
|
|
185
|
+
<array>
|
|
186
|
+
<string>AMCategoryUtilities</string>
|
|
187
|
+
</array>
|
|
188
|
+
<key>CFBundleVersion</key>
|
|
189
|
+
<string>2.0.3</string>
|
|
190
|
+
<key>Class Name</key>
|
|
191
|
+
<string>RunShellScriptAction</string>
|
|
192
|
+
<key>InputUUID</key>
|
|
193
|
+
<string>$INPUT_UUID</string>
|
|
194
|
+
<key>OutputUUID</key>
|
|
195
|
+
<string>$OUTPUT_UUID</string>
|
|
196
|
+
<key>UUID</key>
|
|
197
|
+
<string>$ACTION_UUID</string>
|
|
198
|
+
<key>isViewVisible</key>
|
|
199
|
+
<integer>1</integer>
|
|
200
|
+
</dict>
|
|
201
|
+
<key>isViewVisible</key>
|
|
202
|
+
<integer>1</integer>
|
|
203
|
+
</dict>
|
|
204
|
+
</array>
|
|
205
|
+
<key>connectors</key>
|
|
206
|
+
<dict/>
|
|
207
|
+
<key>workflowMetaData</key>
|
|
208
|
+
<dict>
|
|
209
|
+
<key>applicationBundleID</key>
|
|
210
|
+
<string>com.apple.finder</string>
|
|
211
|
+
<key>applicationBundleIDsByPath</key>
|
|
212
|
+
<dict>
|
|
213
|
+
<key>/System/Library/CoreServices/Finder.app</key>
|
|
214
|
+
<string>com.apple.finder</string>
|
|
215
|
+
</dict>
|
|
216
|
+
<key>applicationPath</key>
|
|
217
|
+
<string>/System/Library/CoreServices/Finder.app</string>
|
|
218
|
+
<key>applicationPaths</key>
|
|
219
|
+
<array>
|
|
220
|
+
<string>/System/Library/CoreServices/Finder.app</string>
|
|
221
|
+
</array>
|
|
222
|
+
<key>inputTypeIdentifier</key>
|
|
223
|
+
<string>com.apple.Automator.fileSystemObject</string>
|
|
224
|
+
<key>outputTypeIdentifier</key>
|
|
225
|
+
<string>com.apple.Automator.nothing</string>
|
|
226
|
+
<key>presentationMode</key>
|
|
227
|
+
<integer>15</integer>
|
|
228
|
+
<key>processesInput</key>
|
|
229
|
+
<false/>
|
|
230
|
+
<key>serviceApplicationBundleID</key>
|
|
231
|
+
<string>com.apple.finder</string>
|
|
232
|
+
<key>serviceApplicationPath</key>
|
|
233
|
+
<string>/System/Library/CoreServices/Finder.app</string>
|
|
234
|
+
<key>serviceInputTypeIdentifier</key>
|
|
235
|
+
<string>com.apple.Automator.fileSystemObject</string>
|
|
236
|
+
<key>serviceOutputTypeIdentifier</key>
|
|
237
|
+
<string>com.apple.Automator.nothing</string>
|
|
238
|
+
<key>serviceProcessesInput</key>
|
|
239
|
+
<false/>
|
|
240
|
+
<key>systemImageName</key>
|
|
241
|
+
<string>NSActionTemplate</string>
|
|
242
|
+
<key>useAutomaticInputType</key>
|
|
243
|
+
<false/>
|
|
244
|
+
<key>workflowTypeIdentifier</key>
|
|
245
|
+
<string>com.apple.Automator.servicesMenu</string>
|
|
246
|
+
</dict>
|
|
247
|
+
</dict>
|
|
248
|
+
</plist>
|
|
249
|
+
PLIST
|
|
250
|
+
|
|
251
|
+
plutil -lint "$CONTENTS_DIR/Info.plist" >/dev/null
|
|
252
|
+
plutil -lint "$CONTENTS_DIR/document.wflow" >/dev/null
|
|
253
|
+
|
|
254
|
+
echo "Installed Quick Action: $WORKFLOW_NAME"
|
|
255
|
+
echo "Location: $WORKFLOW_DIR"
|
|
256
|
+
echo "Using md2pdf executable: $MD2PDF_SCRIPT"
|
|
257
|
+
echo
|
|
258
|
+
echo "If it does not appear immediately in Finder context menu, restart Finder:"
|
|
259
|
+
echo " killall Finder"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
7
|
+
echo "Error: Finder Quick Action uninstaller is only supported on macOS." >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
WORKFLOW_NAME="${WORKFLOW_NAME:-Convert Markdown to PDF}"
|
|
12
|
+
WORKFLOW_DIR="$HOME/Library/Services/${WORKFLOW_NAME}.workflow"
|
|
13
|
+
|
|
14
|
+
if [[ -d "$WORKFLOW_DIR" ]]; then
|
|
15
|
+
rm -rf "$WORKFLOW_DIR"
|
|
16
|
+
echo "Removed Quick Action: $WORKFLOW_NAME"
|
|
17
|
+
echo "Deleted: $WORKFLOW_DIR"
|
|
18
|
+
else
|
|
19
|
+
echo "Quick Action not found: $WORKFLOW_DIR"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
echo
|
|
23
|
+
echo "Restart Finder to refresh the context menu:"
|
|
24
|
+
echo " killall Finder"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 Deepak Kothule
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
cd "$SCRIPT_DIR"
|
|
8
|
+
|
|
9
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
10
|
+
echo "python3 not found. Install Python 3 first."
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
15
|
+
echo "npm not found. Install Node.js/npm first."
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ ! -d ".venv" ]]; then
|
|
20
|
+
python3 -m venv .venv
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
./.venv/bin/python -m pip install --upgrade pip
|
|
24
|
+
./.venv/bin/pip install -r requirements.txt
|
|
25
|
+
|
|
26
|
+
npm install
|
|
27
|
+
|
|
28
|
+
echo
|
|
29
|
+
echo "Local dependencies installed."
|
|
30
|
+
echo "Python: $SCRIPT_DIR/.venv/bin/python"
|
|
31
|
+
echo "Mermaid CLI: $SCRIPT_DIR/node_modules/.bin/mmdc"
|