A short, opinionated path to a second plugin that holds the collection's standard.
Decide WLX / WCX / WDX / WFX (see ARCHITECTURE.md). Copy only the
slice of the ABI you need into a local listplug.h-style header. On macOS, remember
window handles are NSView* and __stdcall is empty.
Find the exact entry points by inspecting a working plugin:
nm -gU "/Applications/Double Commander.app/Contents/MacOS/plugins/<type>/<name>/<file>"<name>-<type>/
├── <Name>.m
├── listplug.h
├── build.sh # clang -dynamiclib -arch arm64 -arch x86_64 ... ; codesign -s -
├── install.sh
├── assets/ # vendored, attributed
├── test/ # headless harness that dlopens the built plugin
├── docs/
├── README.md
└── THIRD_PARTY_LICENSES.md
Match markdown-wlx/build.sh (universal build + ad-hoc codesign -s - + symbol
dump). Double Commander is ad-hoc signed with no hardened runtime, so an ad-hoc
signed plugin loads fine.
Write a test/ host that dlopens the built plugin, calls the entry points the way
DC would, and asserts an observable outcome (it rendered; a value came back; a key
routed correctly). Loading the real built artifact — not a mock — is what makes
the test meaningful. See markdown-wlx/test/test_host.m and esc_verify.m.
If the plugin is a viewer built on WKWebView, it needs the Esc fix. The web view
swallows Escape and DC's viewer then won't close. Copy the -keyDown: override from
markdown-wlx/MarkdownView.m or book-wlx/BookView.m, ship test/esc_verify.m, and
still press Esc in the real Double Commander before shipping — the mock passes either
way. CLAUDE.md has the full note.
It also needs ListSendCommand. DC handles Cmd+C and Cmd+A itself and routes
them through that entry point (lc_copy, lc_selectall); without it the user can
select text and copy nothing, with no error. Copy the implementation from
markdown-wlx/MarkdownView.m or book-wlx/BookView.m — note it selects the content
element's children rather than calling -[WKWebView selectAll:], which would sweep
the page chrome into the clipboard — and ship test/copy_verify.m. CLAUDE.md has
the full note.
install.sh should be idempotent, back up doublecmd.xml, and place the entry so
detection order is correct (DC uses the first plugin whose detect string matches —
mind the catch-all MacPreview (EXT!="")). Quit DC before editing its config.
- Add a row to the root
README.mdplugins table. - Add a
CHANGELOG.mdentry. - Confirm
bash scripts/leak-guard.shis clean. - CI builds every plugin and checks arches + exports; keep
build.shat the path CI expects, or extend.github/workflows/ci.yml.