Repository navigation
Preserve Substack widgets in markdown #64
Description
When publish.py update overwrites a draft it replaces the entire body from Markdown, silently destroying any widgets inserted via the Substack editor. These widgets have no Markdown equivalent and should survive updates.
Widgets identified in a live draft:
| Type | Key attrs |
|---|---|
polymarket |
eventSlug, marketSlug, fullEmbedUrl |
button |
url, text, action, class |
recipe |
id |
Possible implementation
Represent widgets as HTML comment blocks containing the raw server JSON, e.g.:
<!-- widget
{"type": "polymarket", "attrs": {"eventSlug": "gpt-6-released-by", ...}}
-->markdown-it passes HTML comments through as html_block tokens, so _render_block can detect and parse these without any plugin. The JSON is whatever publish.py inspect <id> returns for that node — the renderer inserts it verbatim, with no schema knowledge required. The block renders invisibly in Markdown previewers.
Alternatively, it may also be possible to store the widgets as normal json-formatted code blocks, and have the parser check the "type", but they would need to be distinguishable from code blocks that were intended to be published as code blocks.
Seamless sync via update
Rather than requiring manual inspect + copy-paste, cmd_update can keep the local file in sync automatically:
- Before uploading, fetch the existing draft and find any widget nodes not already present in the local Markdown file as
<!-- widget ... -->blocks. - Write missing widgets into the Markdown file.
- Render and upload the (now complete) Markdown.
Any widget added in the Substack editor is then written back into the local file on the next update run. The Markdown file stays in sync with the server and remains the source of truth for text content.
Image editing preservation
The Substack editor also lets authors set per-image properties (caption, alt text, alignment, full-width), as well as actually modify the image with a web-based editor, after a draft is created. A subsequent update currently stomps the changes because image nodes are rebuilt from scratch.
This is related to the widget problem and could be solved similarly — detect when a Markdown image URL matches an existing captionedImage src in the stored draft and merge editor-set attrs rather than replacing the node wholesale. It's worth investigating whether the editor saves image edits as a new upload or modifies the existing one, as that affects how src matching would work.
Activity
ma2za commentedon Jul 4, 2026
Thanks. The
<!-- widget ... -->approach looks workable for this library.markdown-itexposes comments ashtml_block, sofrom_markdown()can detect widget comments, parse the JSON, and insert the node verbatim. That avoids needing schema-specific support forpolymarket,button,recipe, etc.I do not see
publish.py update,cmd_update, orinspectin this repo. Is that a separate/local publishing script built on top ofApi.get_draft()andApi.put_draft()?I would handle image edit preservation separately from widget preservation, since image preservation needs matching and merging existing
captionedImageattrs.feat: preserve Substack widgets in Markdown (fixes #64) (#76)