Pubcopy

pending

by canartuc

Copy notes as platform-optimized HTML for Medium and Substack.

Updated 12d agoMITDiscovered via Obsidian Unofficial Plugins
View on GitHub

Pubcopy

GitHub release Tests License: MIT CodeRabbit Pull Request Reviews Ko-fi

Obsidian plugin that copies your notes as platform-optimized HTML to clipboard for pasting into Medium and Substack without formatting loss.

Install

From Obsidian (recommended)

Settings > Community plugins > Browse > Search "Pubcopy" > Install > Enable

Manual

  1. Download main.js, manifest.json, styles.css from the latest release
  2. Create folder: <your-vault>/.obsidian/plugins/pubcopy/
  3. Copy the 3 files into that folder
  4. Restart Obsidian
  5. Settings > Community plugins > Enable "Pubcopy"

Usage

Three ways to trigger:

MethodBehavior
Command Palette (Cmd/Ctrl+P) > "Pubcopy"Copies entire note
Right-click in editor > Pubcopy submenuCopies selected text (or entire note if no selection)
Three-dot menu (top-right of note) > Pubcopy submenuCopies entire note

Each method offers two options:

  • Copy for Medium
  • Copy for Substack

After copying, paste (Cmd/Ctrl+V) into the Medium or Substack editor.

What gets converted

56 Obsidian markdown elements are handled:

ElementMediumSubstack
Bold, italic, strikethrough, inline codeYesYes
Highlights ==text==Converted to boldConverted to bold
Headers H1-H4YesYes
Headers H5-H6Flattened to H4Yes
Lists (ordered, unordered, nested)Max 2 levelsUnlimited
Task lists - [ ]Unicode checkboxesUnicode checkboxes
Callouts > [!note]Styled blockquote with labelStyled blockquote with label
Code blocks (with language)<pre><code><pre>
TablesHTML tableHTML table
Images (local)Base64 embeddedBase64 embedded
Images (remote URL)URL passthroughURL passthrough
Image captionsItalic text below image<figcaption>
FootnotesSuperscript + Notes sectionNative
Math (LaTeX)Rendered via KaTeXRendered via KaTeX
Mermaid diagramsStripped (not supported)Stripped (not supported)
Wikilinks, tags, comments, frontmatterStrippedStripped
Embeds ![[note]]Resolved and inlinedResolved and inlined

Settings

Settings > Community plugins > Pubcopy > Settings

SettingDefaultDescription
Strip frontmatterOnRemove YAML frontmatter
Strip tagsOnRemove #tag references
Strip wikilinksOnConvert [[links]] to plain text
Mermaid formatSVGSVG or PNG (for future use)
Image handlingAutoAuto: base64 for local, URL for remote
Show notificationOnDisplay notice after copying

Development

Prerequisites

  • Node.js 18+
  • npm

Setup

git clone https://github.com/canartuc/pubcopy.git
cd pubcopy
npm install

Build

# Development (watch mode, rebuilds on file change)
npm run dev

# Production build
npm run build

Test

# Run all tests
npm test

# Watch mode
npm run test:watch

Deploy to your vault

First run asks for your vault path and saves it to .deploy.json (gitignored):

npm run deploy
# > Obsidian vault path: /path/to/your/vault
# > Saved to .deploy.json
# > Deployed to .../plugins/pubcopy

Every subsequent run just builds and copies:

npm run deploy

After deploying, restart Obsidian or reload the plugin.

Project structure

src/
├── main.ts                    # Plugin entry, commands, menus
├── settings.ts                # Settings interface and tab
├── converter/
│   ├── index.ts               # Conversion pipeline orchestrator
│   ├── preprocessor.ts        # Strip frontmatter, tags, wikilinks, etc.
│   ├── html-converter.ts      # Markdown to HTML via remark/rehype
│   ├── image-handler.ts       # Local base64, remote URL, captions
│   ├── embed-resolver.ts      # Resolve ![[note]] embeds
│   ├── footnote-processor.ts  # Platform-specific footnotes
│   └── math-renderer.ts       # LaTeX via KaTeX
├── platforms/
│   ├── index.ts               # PlatformProfile interface
│   ├── medium.ts              # Medium-specific rules
│   └── substack.ts            # Substack-specific rules
├── clipboard/
│   └── writer.ts              # Clipboard API (text/html + text/plain)
└── utils/
    ├── errors.ts              # PubcopyError, WarningCollector
    └── notifications.ts       # Obsidian Notice wrappers

Release

Push a tag to trigger a GitHub Actions release:

# Update version in manifest.json and package.json first
git tag X.Y.Z
git push origin X.Y.Z

GitHub Actions runs tests, builds, and creates a release with main.js, manifest.json, and styles.css.

License

MIT

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.