Everything shipped under “library”, across every version, newest first.
Added
A Library panel ranks your X posts (archive imports, captures and posts NoteSurge published) by engagement and turns the one you pick into an Instagram carousel with a caption, saved as a draft. Nothing is posted automatically. Archived tweets can now become carousels too, and Notey and MCP clients can do the same.
Added
Break this post down, on a Library card, shows a post’s format, hook, beats, payoff and skeleton, with every quote checked against the post, and maps it onto an idea of your own. Anything it cannot find in the post is labelled not found, and it never invents numbers. The Library also has a chat that answers from your best-scoring pieces and quotes the lines it relied on, each one checked against the piece it names.
Added
A CSV import tile in the Import Hub takes a spreadsheet with one post per row, pasted in or uploaded as a .csv file, and turns each row into a Library piece you can schedule or repurpose.
Fixed
Importing two or more saved LinkedIn posts by the same person used to fail the whole batch. Each post is now kept by its own link, so the whole batch comes in.
Added
Notey can check an image before it goes out: what it shows, any text in it quoted exactly, and anything that looks wrong. It can also open a Library piece or a draft for you and read back the margin notes you leave on it.
Changed
The card menu is grouped into Write & repurpose, Media, Share & publish and Manage, with Compose and Ask Notey as buttons on the card. A piece with mermaid or excalidraw diagrams gets Draw the diagrams, which shows them as images without changing the piece. Viral rewrite drafts a new version shaped by the patterns in your own best posts.
Added
Search, tags, view, origin, dates and order now sit in one filter bar and are kept in the page address, so a filtered Library survives a reload and can be bookmarked or shared.
Added
Upload your Kindle My Clippings file or pull your Readwise highlights, and each book becomes a Library piece you can repurpose. Your Readwise token is used once and never stored. The Import Hub also gains Save inspiration, Upload image and Your Substack Notes tiles.
Added
The render window now shows frame thumbnails and the audio waveform under the start and end fields, so you can see where you are trimming.
Added
Two new standing tasks save a Library piece on a schedule: a weekly review of how your posts did, and a brief on what accounts in your niche are posting. Neither publishes anything. A starter pack of skills is available to install.
Added
Export PNG sits beside Export PDF on Library cards and newsletter drafts and saves branded images, one per page for long pieces. Make a podcast version turns a post, thread or newsletter into a one or two voice episode.
Added
Margin notes now work on queued notes and newsletter drafts as well as Library pieces, and can point at a quoted line. If that line is later edited away, the note says so.
Added
When a video has no captions, Transcribe can fetch its audio and transcribe that instead, and tells you why if it cannot (private video, too long, or a worker error).
Added
When you add a piece of 280 characters or more, Notey can write a short margin note on it in the background. It is limited to a few pieces per hour and ten notes a day, only runs on your own AI key, and respects the stop switch. Turn it off under AI, Agent behavior.
Added
Make an audio version in a Library card menu narrates a piece to an mp3, and Clean up the audio evens out the volume, trims silence and can add an intro and outro. Narration uses your own OpenAI key, or a text-to-speech service you point NoteSurge at.
Added
Export PDF is now on Library cards, drafts and the analytics report. A Terms section also explains that the Substack connection is unofficial.
Fixed
Pasting a link under Library → Import → Article from link now imports the article. Before this, the tile opened an empty panel, and the import itself failed on every first attempt. Importing the same link twice, even at the same moment, now gives you one Library piece instead of two.
Added
MIT-A ships: a second deterministic render transport (satori + sharp, alongside the existing quote-card and carousel satori path) turns any idea piece's markdown into branded slide-deck-shaped images — a presentation form for long-form posts, distinct from the square quote-card satori path. One AI turn writes grounded slide text (Notey's two-rule floor); the deterministic satori transport composes each slide to PNG and sharp emits WebP/AVIF + OG-image sizes. Slides are image attachments on the piece — nothing publishes automatically. A new /api/library/[id]/slides route handles POST (generate) and PATCH (re-render one slide with your edited text, zero AI spend); the Library card unlocks a "Review & edit slides" action and an at-a-glance strip when a deck exists; editing is transport-agnostic (slide-deck or carousel markers both open the same editor surface).
Added
MIT-E: a new "Article from link" import source (clipboard icon in the Import Hub) takes any web article URL, strips it to its article body with @mozilla/readability, converts to clean markdown via turndown, and lands the result as a repurposable Library piece with the canonical URL as provenance. Uses the same SSRF guard the webhook paths use — a user-supplied link can never reach internal metadata endpoints. Idempotent: re-importing the same URL returns the existing piece.
Added
The right-rail RepurposeToday tile (A1-P9.4) gains an overflow ⋯ menu that reuses the same libraryOverflowActions() seam + /api/library/[id]/* dispatch routes as the Library card — repurposing actions (compose, schedule, newsletter draft, transcribe, suggest clips, recycle) are now available from the tile, not just the Library. The tile's primary button is now platform-neutral "Schedule" (was "Turn into a Note", which actually scheduled).
Fixed
The rerenderClip action posted to the media engine without the reRender flag, so the engine's idempotency gate always fired and every re-render attempt bounced back as a harmless-looking 409 — making clips look like they were stuck. The flag now ships in the POST body and re-renders go through on the first try.
Added
The workflow canvas visual renderer (A2-P1 Packets 1–4) was built but unreachable by humans — now wired as its own top-level nav section (?section=canvas) as part of the navigation cleanup. A new CanvasTab lists the org's canvases, offers a "New canvas" action, and hands off to the existing WorkflowCanvasView for the selected canvas. Canvas is now reachable from the left rail, command palette, mobile pill nav, and a pre-connection preview card (#774, PR #775).
Added
R4 stage 4-5: Avatar and NoteyAvatar are wired into the Profile hero, chat rail and Team feed, and Library cards now surface author avatars with an extension payload field — so every piece in your Library and feed is attributable at a glance.
Fixed
carouselSlideLabel and noteMediaLabel get stricter validation to reject malformed input; the Whop platform glyph audit from PR #611 is restored after a spec-doc/ROADMAP drift regression.
Added
Open your channel Shorts tab (https://www.youtube.com/@<your-channel>/shorts) and scroll — every Short you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as TikTok/Instagram/Pinterest. Shorts are keyed off their canonical /shorts/{videoId} permalink so the same Short is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/youtube-shorts with UPSERT semantics.
Added
When a Library card has more than 4 imported media items, the media strip now truncates to 4 visible thumbs with a scannable "+N more" indicator, so you can tell at a glance that additional images/videos/audio exist without opening the piece.
Added
Open your Kick videos page (kick.com/{username}/videos) or Twitch videos page (twitch.tv/{username}/videos) and scroll — every VOD you scroll past flows into your Library, using the same scroll-capture relay pattern as TikTok/YouTube Shorts. Kick VODs are keyed off their 36-char uuid permalink; Twitch VODs off their numeric video id. Engagement maps to captured views (primary) + likes (secondary). Feeds POST /api/import/kick and /api/import/twitch with UPSERT semantics — re-capturing refreshes engagement rather than duplicating.
Added
Open your own Blogger blog (https://yourblog.blogspot.com/) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other blog captures (Substack/WordPress/Ghost/Hashnode/Tumblr). Posts are keyed off their canonical /p/{slug} permalink so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/blogger with UPSERT semantics.
Added
Open your own Whop profile (https://whop.com/{your-handle}) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Posts are keyed off their canonical permalink so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/whop with UPSERT semantics.
Added
Open your own Dribbble profile (https://dribbble.com/{your-handle}) and scroll — every shot you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Shots are keyed off their canonical permalink so the same shot is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/dribbble with UPSERT semantics.
Added
Open your own Warpcast profile (https://warpcast.com/{handle}) and scroll — every cast you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Casts are keyed off their Farcaster hash (0x + hex) so the same cast is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/warpcast with UPSERT semantics.
Added
Open your Lens profile page on hey.xyz (https://hey.xyz/u/{your-handle}) or lenster.xyz (https://lenster.xyz/{your-handle}) and scroll — every publication you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Publications are keyed off their permalink slug (the path segment after /posts/) so the same publication is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/lens with UPSERT semantics.
Added
Open your WordPress author archive page (https://your-domain/author/your-username/) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other blog captures (Substack/Mastodon/Bluesky/Ghost/Hashnode/Tumblr/Reddit/Dev.to). WordPress self-hosted has arbitrary domains, so the manifest match is broad (https://*/*) with a strict path guard that only activates on /author/{username}/ pages — visiting your admin, posts, or any other WordPress page is never captured. Posts are keyed off their permalink slug so the same post is never duplicated, and re-capturing just refreshes engagement (Jetpack likes / comment-count widgets). Feeds POST /api/import/wordpress-posts with UPSERT semantics.
Added
Open your Dev.to profile page (https://dev.to/@{your-handle}) and scroll — every article you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Articles are keyed off their permalink slug so the same article is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/devto with UPSERT semantics.
Added
Open your VK profile page (https://vk.com/{your-id}) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as the other profile-timeline captures. Posts are keyed off their canonical permalink so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/vk with UPSERT semantics.
Added
Scroll your own profile timeline on any Mastodon instance (e.g. mastodon.social) and every post you scroll past is captured into your Library — text, engagement counts, author handle/name, and media attachments. The extension reads the rendered DOM and sends posts via POST /api/import/mastodon using the same x-app-session auth as the other import paths. Captured posts feed the Telegram agent's voice sample and appear under the Library Imported origin chip.
Added
Open your publication archive (https://yourpub.substack.com/archive) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Mastodon/Bluesky. Posts are keyed off their canonical /p/{slug} permalink so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/substack-posts with UPSERT semantics.
Added
Open your Ghost publication homepage (e.g. https://yourblog.ghost.io) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Substack/Mastodon/Bluesky. Posts are keyed off their permalink slug so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/ghost with UPSERT semantics.
Added
Open your Hashnode profile page (https://hashnode.com/@{your-handle}) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Substack/Mastodon/Bluesky/Ghost. Posts are keyed off their permalink slug so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/hashnode with UPSERT semantics.
Added
Open your Tumblr blog posts page (https://yourblog.tumblr.com/blog/{name}/posts) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Substack/Mastodon/Bluesky/Ghost/Hashnode. Posts are keyed off their numeric post id so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/tumblr with UPSERT semantics.
Added
Open your Reddit profile page (https://www.reddit.com/user/{your-username}) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Substack/Mastodon/Bluesky/Ghost/Hashnode/Tumblr. Posts are keyed off their canonical permalink (/r/{sub}/comments/{id}/{slug}) so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/reddit with UPSERT semantics.
Added
Open your VK profile wall (https://vk.com/{your-domain-or-id}) and scroll — every post you scroll past flows into your Library for voice-matching, using the same scroll-capture relay pattern as Substack/Mastodon/Bluesky/Ghost/Hashnode/Tumblr/Reddit. Posts are keyed off their wall permalink (wall{ownerId}_{postId}) so the same post is never duplicated, and re-capturing just refreshes engagement. Feeds POST /api/import/vk with UPSERT semantics.
Changed
The Library card inline Schedule button now reads "Schedule" with a generic clock icon instead of "Schedule to Substack" with the Substack brand glyph — scheduling applies to every connected platform, not just Substack.
Added
The source badge on Library cards now renders the imported platform's human-readable name (e.g. "X post", "YouTube video", "Notion page") alongside the icon, so you can see at a glance where a piece came from and click through to the original.
Changed
The "Fan out" repurposing button on scheduled/posted notes is wrapped in a Tooltip instead of a bare title attribute, matching the A1-P6.1 discoverability pattern across every NoteCard action button.
Fixed
The Library card's at-a-glance quote card thumbnail is now rendered through quoteCardThumb() from the media-display seam instead of an inline img with a hand-built alt string. This keeps alt text and sizing consistent with the rest of the media surface and lets the one-seam source-scan test lock the contract — the card thumb and the full-screen quote card can no longer drift apart.
Added
Asset generation stage 2: a rendered quote card is no longer a one-shot. The AI writes a draft subhead + body, then the card shows as a real at-a-glance image on its Library piece — click it (or "Review & edit quote card" in the ⋯ menu) to open an edit modal where you fix a weak hook or clunky line and Save. Saving re-renders the card through the deterministic satori template (same brand geometry, zero AI spend on the edit) and updates the stored marker + asset audit trail in one transaction. The original card is never touched on render failure, and empty/oversized/no-change edits are refused honestly instead of double-writing.
Fixed
Uploading a video or large file to the Library used to die at the serverless function body cap (~4.5MB) with a bare 413 Request Failed. Files over 4MB now stream directly from the browser to Vercel Blob via a presigned PUT URL, completely bypassing our function body limit. The upload-url route was also routed through the media-storage seam instead of importing the Blob SDK directly, closing a structural violation of the one-seam rule. Smaller uploads keep taking the cheap multipart path.
Added
Drop an audio or video file into the Library and NoteSurge ingests it directly, transcribes it (Whisper for uploads, caption tracks for YouTube links), and writes an auto-summary with chapters so long media becomes searchable, quotable source material.
Added
Three engines that already power the app were invisible to outside callers. They’re now registered capabilities with stable names: get_repurpose_suggestions returns the one ranked “what should I repurpose” answer; get_note_stats pulls back measured engagement per note — Substack restacks and reactions, Bluesky likes and reposts, X likes, reposts and impressions — and says “nothing measured yet” instead of faking zeros; fan_out_piece turns one strong piece into platform-native draft variants that wait for per-child human approval. Everything passes the same token scopes and live role checks as the rest, and the AI tab’s tool list now renders straight from the server so it can’t silently drift out of date again.
Added
"Imported" used to be a single indivisible filter: X posts, threads, archive items, bookmarks and LinkedIn captures were indistinguishable once filtered in. Now clicking an active source chip expands it into per-source chips, so you can narrow to just bookmarks or just threads while the other four group chips stay as they were. When several filters are on at once, a summary row above the list shows each of them as its own removable chip — search words, tag, source, date bounds — with one Clear all that resets them in a click (your sort, density and layout choices stay put; those are preferences, not filters).
Added
The "Choose a source" grid only showed four tiles, so three real ingestion paths were invisible: X bookmarks and both LinkedIn captures existed through the browser extension but had no way in from the app. The grid now lists all seven sources. The three extension ones open a plain explainer instead of pretending to be forms — what gets captured, the exact page to browse so capture runs, and a direct link where one is guaranteed to land (X Bookmarks and LinkedIn's saved posts; LinkedIn posts keeps instructions only, since the app can't know your profile address).
Added
The All and Archived library views had only one ordering: your best-performing pieces first. That's right for the Winners list, but it meant plain browsing of a large archive was stuck on newest-first with no way back to where you started. Both views now open newest-first with an explicit Oldest-first toggle beside them — walk the archive from its very beginning. Long lists also group themselves under month headers in both the cards and table layouts (short lists stay exactly as they were), and a "Jump to:" menu above either layout scrolls you straight to any month already loaded.
Changed
The ⋯ menu on a library card used to offer "Create newsletter draft", which asked you to fill in a title and body right there and could only produce a Substack draft — alongside three separate AI "Adapt for LinkedIn/Facebook/Mastodon" buttons that saved a detached new library piece you then had to go find. Both paths are replaced by a single "Repurpose to note" action: it drops the piece directly into Compose with its content pre-filled, and from there you pick the target platform exactly like on any other note.
Added
Scrolling a big import archive card by card is slow going, so the Library's browse list now has a Cards/Table toggle right beside its other view controls. Cards is exactly the layout you know; Table compresses every piece into one quiet row — title with its source-platform icon and a single-line snippet, status, performance score, and the created date (which links out to the original post when it has one) — built for scanning thousands of rows without losing your place.
Added
The ⋯ menu on library cards used to hold only the AI actions, leaving evergreen and archive as separate buttons and newsletter drafting buried in the expanded card. All three now lead the menu on every card: Mark/Unmark evergreen and Archive/Unarchive work exactly like their durable buttons (and archived pieces can always come back), while Create newsletter draft seeds the composer with the piece's text — wherever you are in the app's flow.
Fixed
Cards for imported X-archive pieces showed no media even though the attachments were there — every archive image was saved pointing at a file that only ever existed on the import machine, so thumbnails silently failed to load and hid themselves. The library now rebuilds each image's real address on X's photo CDN from what was stored, at display time: archive cards render their photos again, with nothing rewritten behind your back.
Fixed
Hundreds of imported archive pieces — mostly photo- and video-only tweets with no text to borrow — carried the literal placeholder title "Archived tweet". Titles are now derived from what the piece does have: the tweet's opening line when one exists, otherwise its media ("Photo tweet · Jun 2021"), always stamped with the original month so the card still tells you something. Fresh imports never write the bare placeholder in the first place.
Added
The Library's search now covers when a piece was created, not just what it says. A new "Created:" row under the Source filter gives you two date fields — pick a range and the list narrows to pieces created inside it, composing with your existing search, tag, and source filters.
Fixed
Pieces imported with a video or GIF attachment used to show nothing at all — the card quietly skipped it, and you couldn't tell a video post from an empty one. Cards now render videos and GIFs as small live thumbnails (muted, looping), right alongside image attachments.
Changed
A busy card used to carry up to nine always-on buttons. The durable pair — Evergreen and Archive — stays put, while Get Note ideas, the Adapt-for-platform actions and Pin as reference moved into a ⋯ More menu on each card. Everything still does exactly what it did; it's just one click deeper instead of always in your face.
Added
Any piece in your Library can now be pinned as a voice reference. Adapt-for-LinkedIn and every other generation leans harder on that piece's style when you do.
Pins show up in the AI tab next to your profile, and you can unpin from either place. Up to 3 at a time.
Added
A new "Source" filter sits right under the existing search: Written here, Imported, Repurposed, or From your agent. Repurposed pieces (the ones you turned into a LinkedIn or Facebook version of something else) finally show up as their own category instead of looking identical to something you typed by hand.
Added
The "Paste text" importer takes a .txt or .md file now, not just a paste. Drop in an old newsletter or essay and it fills the title and body in for you.
Fixed
An image that failed to load in your Library used to leave the browser's default broken-icon sitting there. It just disappears now instead.
Added
The webhook system that already tells you when a note is scheduled, published, or fails now covers your library too. `library.created` fires when something new gets imported or pasted in. `library.archived` fires when a piece gets archived.
Added
Three new AI transforms landed on library pieces.
Fixed
The archive importer now recognizes an actual multi-tweet thread instead of chopping it into disconnected rows.
It follows the reply chain and stitches everything back into one piece.
Added
Visit your own LinkedIn "Recent activity" and NoteSurge quietly captures your posts, along with their reactions, comments, and reposts, for voice-matching and a new LinkedIn analytics card. Visit "My items → Saved posts" and anything you’ve saved from other people lands in your Library as an idea.
Added
Full search across title and body works alongside a tag filter now, and you can combine both to narrow down to exactly the intersection you want.
Tags are also fully editable on any item regardless of how it got into your library. Before this, only the manual-paste importer could ever set one.
Added
A floating "Save to NoteSurge" button shows up on any tweet’s own page now and drops it straight into your Library. Open your X bookmarks page and scroll through it like normal, and NoteSurge captures your whole bookmarks list the same way, with a live counter so you can see what it’s picking up as you go.
Added
Drop your own Anthropic API key into Settings and two buttons show up: "Polish with AI," which turns a raw imported tweet into an actual Substack Note, and "Expand with AI," which grows it out into a full newsletter draft.
Both are editable before you commit to anything.
Added
Mark a library piece as evergreen and it resurfaces on its own for a repost, 60 days after it last went out.
The logic: most of your audience never saw it the first time around, so why should it just sit there.
Added
"Add to queue" drops a note into your next open posting slot automatically now, based on the times you’ve configured in Settings, so most of the time you never even see a date picker.
Queue timestamps read like "in 3 hours" or "tomorrow at 6:00 PM" instead of a raw locale string nobody parses at a glance.