Novelist 0.2.4 — image hosting and one-click publishing
Two new core features: upload local images in your Markdown to your image host of choice (Qiniu / OSS / S3·R2 / imgur / sm.ms / Custom), and publish the active document to Ghost / WordPress / Medium with one click. Plus a batch of daily-workflow polish.
0.2.4 is a major release. It finally pulls the last step of writing — getting the piece off your disk and onto the internet — into the editor. Local images in your Markdown can be uploaded straight to your own image host, and the finished document can be published to Ghost / WordPress / Medium without juggling four browser tabs.
Image hosting
Six built-in providers plus a minimal “Custom HTTP” endpoint:
- Qiniu Kodo — HMAC-SHA1 upload-token, multipart POST
- Aliyun OSS — HMAC-SHA1 header signature, virtual-hosted PUT
- Amazon S3 — manual SigV4 path-style PUT (works with Cloudflare R2 and MinIO via endpoint override)
- imgur — Client-ID Bearer
- sm.ms — optional API token, with
image_repeateddedup handling - Custom — multipart POST with optional Bearer; response URL read from
urlordata.url
Entry points:
- Settings → Image Hosts panel: list, add/edit/delete, set active default; per-host Test button (uploads a 1×1 PNG to verify credentials without leaving noisy artifacts).
- Right-click any inline image with a local path → Upload to host — replaces the Markdown URL in place.
- Palette command Upload all local images: iterates the document’s image refs, uploads each sequentially, applies all URL replacements in a single CodeMirror transaction, reports partial failures via toast.
- Optional auto-on-paste / auto-on-drop — off by default. When enabled, every pasted or dropped image immediately uploads to the active host.
For bucket-style hosts, object keys are date-prefixed automatically: {yyyy}/{mm}/{dd}/{8-char-blake3}-{sanitized-name}.{ext} — keeps the remote bucket organized and collision-free.
Credential safety: plaintext credentials live only in global settings. Per-project configs can only override the active-host pointer, so secrets never leak into project files that might be tracked by git.
One-click publishing
Four publish platforms, each as a self-contained adapter:
- Ghost — Admin API key signed as a 5-min HS256 JWT, posts via
?source=htmlso we send HTML and Ghost converts to Lexical server-side - WordPress (self-hosted) — Application Passwords (HTTP Basic), full REST tag-name → ID resolution with auto-create, raw-body media upload
- WordPress.com — OAuth2 personal access token (Bearer), same REST shape with a
/sites/<id>prefix - Medium — legacy Integration Token Bearer; native Markdown body, max 3 tags. Note: Medium removed the Integration Token UI in late 2024; the API still accepts pre-existing tokens, but new users typically can’t generate one.
Entry points:
- Top-right Share button in the editor → Upload Local Images to… + one Publish to
entry per configured channel (mweb-style — single discoverable entry point). - Publish dialog with mweb-shaped fields: title (auto-prefilled from the document’s H1), tags (chip input), excerpt, slug (auto kebab-case), per-platform status dropdown, drag-drop cover image. Inline error on failure with retry; success banner with Open in browser.
Pre-publish image upload: local images in the body are uploaded to the publish target’s own media endpoint and rewritten in the submitted body. So publishing to Ghost doesn’t require a third-party CDN at all. If you’d rather everything route through your own image host, run Upload Local Images to… explicitly first.
No publish history tracking: every publish creates a new post. Republishing an edited document creates duplicates that you prune manually on the platform side; front-matter ID writeback is intentionally out of scope for 0.2.4.
Conversion path: Ghost / WordPress / WordPress.com use Pandoc Markdown → HTML; Medium consumes Markdown natively.
UX polish
- Bilingual command palette — Cmd+Shift+P now shows the active-locale label large with the alternate-locale label as a small subscript below; the search matcher hits both. A Chinese-locale user can find Toggle Outline by typing either outline or 大纲.
- Double-click to rename in the file tree — double-clicking a file row opens inline rename. Folders still toggle expansion. Rename input renders inline under the renamed node (used to render at the top of the sidebar).
- Right-side panel shortcuts moved to Cmd+Shift+1/2/3/4 — outline, draft, snapshot, stats. Easier chord than Cmd+Alt. macOS users may need to disable system screenshot shortcuts (Cmd+Shift+3/4). Toggle-template stays on Cmd+Alt+5 (Cmd+Shift+5 is the macOS screenshot toolbar).
- Drag files from the sidebar into a pane or split-right zone — while dragging, the editor area shows two drop overlays (active pane + right-edge “split right” gutter); dropping opens the file in that pane. Tab bars accept the same payload.
- Wrap long file names — new Settings → Editor toggle keeps long file names on multiple lines instead of truncating to an ellipsis.
- Edge-mounted sidebar toggle — collapse/reopen the sidebar from a small ◁ / ▷ button on the resize handle, in addition to the existing keyboard shortcut.
- Editor max-width is a slider + numeric input (480–9999px) instead of a fixed dropdown.
- Filename macros for new files —
{yyyy},{mm},{dd},{HH},{MM},{ss}are expanded in the new-file template, with live previews under the setting. - Searchable Settings dialog — top filter narrows visible panels and rows by label, hint, or keyword (Chinese aliases included).
- Publish dialog polish — paste-from-clipboard, Ghost tag autocomplete, redesigned pill-grid tag picker, native clipboard read, Test button that actually hits the platform, edit button + secret-reveal in credential rows, fully localized.
{title}-only filename template — the new-file template now accepts{title}without a{N}slot (rendersUntitled.md, thenUntitled 2.md,Untitled 3.mdon collision; H1 auto-rename replaces the whole stem). Typo tokens like{cN}or{Title}are still rejected so a misspelt placeholder doesn’t silently become literal text.- Sort sidebar by creation time — Created (newest) / Created (oldest) alongside the existing modified-time options. Filesystems without birth time fall back to mtime.
- Command-palette shortcut chip uses the UI font — Apple modifier symbols (⇧⌘⌥⌃) match the stroke weight of the letter next to them.
- Settings checkbox rows align with the first text line — the wrap-filenames row used to float above the baseline; it now lines up.
- Sidebar folder right-click adds Expand All / Collapse All — recursive (whole subtree), lazy-loaded BFS expand; collapse keeps the child cache so re-expand is instant. The same items also appear in the empty-area sidebar menu (operates on project root).
Changed
- Filename now follows the H1 heading on every save (was first-save only). Manually renaming a file detaches the sync. See spec
docs/superpowers/specs/2026-05-12-h1-filename-ongoing-sync-design.md. {title}in the new-file template implicitly opts in to H1 rename-on-save. The dedicated Sync filename with H1 on save checkbox is gone — its intent is now expressed by whether the template contains{title}. The shipped default (第{N}章-{title}) keeps the behavior on; remove{title}to opt out.- Sibling-aware new-file naming is always on. The Detect chapter pattern from folder checkbox is gone —
inferNextNamealways reads the folder so{N}slots pick the next free number; templates without{N}are unaffected.
Fixed
- Filename-macro tooltip (the
?next to the default-template field) opens rightward, so content no longer clips against the panel’s left edge. - Numeric sidebar sort orders 第九 < 第十 < 第十一, and no longer misplaces files whose Chinese number is followed by extra characters like
第十章终稿.md. - In-project search is now Unicode case-insensitive and emits UTF-16 offsets, so highlights land on the right glyphs for CJK and Latin-with-diacritic queries; empty queries short-circuit instead of scanning the project.
- File watcher refreshes the right parent on rename/delete on Windows + macOS, emits a separate
directory-changedevent, polls every 15s as a fallback — no more ghost rows after a rename done in another window or by an external tool..canvasand.kanbanfiles now open via the same external open-file path as Markdown. - Plain folders without
.novelist/project.tomlstay global-scoped — previously the app tried to write view settings into folders it had no project file in. - Save (Cmd+S) on a clean tab still runs the H1 → file-name auto-rename check when no edits since the last save introduced a new H1.
- External links open via
tauri-plugin-shellinstead of the default WebKit navigation, so they actually open in the system browser. - Pandoc auto-discovery + user override — no longer relies on the previously-bundled Pandoc binary; falls back to PATH probing with a manual override in Settings → Editor.
Security
- Template zip import is now done via the
zipcrate (instead of shelling out tounzip), rejects symlinks, NUL bytes, path-traversal entries, and caps the archive at 2048 entries / 50 MiB uncompressed. Template ids are validated to[a-z0-9-]{1,96}before they reach the filesystem. - WebDAV sync validates remote file paths before downloading — absolute paths,
..components, and dotfile-prefixed components are rejected, so a hostile server can’t write above the project root.
Platform
- macOS bundle declares
CFBundleLocalizationsforenandzh-Hansvia the newly bundledcore/Info.plist, so Chinese users get localized application menus and system dialogs.
Removed
- Run Release Benchmark command — internal diagnostic, not user-facing. The regular performance benchmark stays available.
- Settings → Editor → Sync filename with H1 on save checkbox — the gate moved to the filename template (use
{title}). Storedauto_rename_from_h1values in existing project configs are still parsed but no longer consulted. - Settings → Editor → Auto-detect chapter pattern from folder checkbox — sibling-aware naming is now unconditional.
Notes for developers
- The
tauri-spectacodegen pipeline is currently broken upstream (specta-serde rejectsskip_serializing_ifon existingProjectConfigfields). New IPC commands and types in 0.2.4 are patched intoapp/lib/ipc/commands.tsmanually. The file remains@ts-nocheckso it’s type-safe at the consumer level. A follow-up release will restore auto-generation. - E2E coverage for the new image-host and publish UIs is deferred. Rust + frontend unit coverage is comprehensive (over 1500 tests total); manual smoke-testing covers the UI surfaces for 0.2.4.
Also
- Full details: upstream CHANGELOG