Live: xtracticle.com — or just change x.com to xtracticle.com in any post URL.
Features
Export formats
Content
- 📝 X Articles — full Draft.js conversion: cover, inline images, videos, captions, embedded posts
- 🧵 Threads from any post — paste the first, a middle or the last post; the whole self-thread is assembled (other people's replies are excluded)
- 💬 Posts — line breaks, photos, videos and quoted posts
- 🔗 Flexible input — x.com, twitter.com, mobile, fxtwitter/vxtwitter/fixupx, nitter/xcancel links,
/i/web/status/, bare IDs, links inside shared text
Workflow
- 🔁 URL swap —
xtracticle.com/{user}/status/{id}opens a post directly, with rich link previews (Open Graph) when shared - 📚 Batch mode — up to 20 links → one ZIP of Markdown files
- 🔖 Bookmarklet and PWA share target (share from the X app on Android)
- 🔊 Listen — read aloud with the browser's text-to-speech
- 📱 iPhone/iPad Shortcut — share a post from the X app straight to Xtracticle (
public/shortcuts/) - 🤖 MCP server + Markdown API — AI assistants read X posts as Markdown (
/mcp,/api/markdown/:id) - 📜 History, 🌙 dark mode, 🌍 English, Español, Português, 日本語, 简体中文, العربية, Türkçe (Arabic is right-to-left)
- 📈 Analytics events —
extract,download(format),copy,share,listen,batch_extract, errors — in GA4
Getting Started
git clone https://github.com/ahmetdeveci3112-crypto/Xtracticle.git
cd Xtracticle
npm install
npm run dev
The dev server runs at http://localhost:5173. /api/* is served by the same handler as the Worker (src/shared/api.ts), and every landing page is rendered on the fly — no wrangler needed.
Deployment
Cloudflare Workers + Static Assets, deployed automatically on push to main (build: npm run build, deploy: npx wrangler deploy). No environment variables.
wrangler.json:
run_worker_first— only/api/*,/mcp,/video/*and/*/status/*hit the Worker; everything else is served straight from the CDNnot_found_handling: "404-page"— unknown URLs return a real404(no soft-404s)public/_headers— security headers,Link: </llms.txt>, download headers for the iOS Shortcut
Architecture
Browser ──► Cloudflare
├─ Static assets (dist/) /, /x-article-to-pdf, /tr/, … , 404.html, sitemap.xml
└─ Worker (src/worker.ts)
├─ /api/thread/:id, /api/tweet/:id ──► src/shared/api.ts ──► FxTwitter API
│ (edge cache, 5 min)
└─ /{user}/status/{id} ──► app shell + per-post OG tags + preloaded JSON (noindex)
src/
├── App.tsx UI: extraction, exports, batch mode, deep links
├── i18n.ts UI strings (en, tr, es, pt, ja, zh)
├── lib/export.ts PDF (jsPDF + html2canvas-pro), EPUB, ZIP (fflate), Obsidian
├── lib/url.ts Input + path parsing
├── lib/analytics.ts GA4 event helper
├── shared/fx.ts FxTwitter client (v2 thread endpoint + v1 fallback), shared by Worker & dev server
├── shared/api.ts JSON API handler with edge caching
├── shared/convert.ts Posts / threads / Draft.js articles → Markdown + text + metadata (app + Worker)
├── shared/markdown.ts GET /api/markdown/:id
├── shared/mcp.ts Remote MCP server (POST /mcp)
├── shared/text.ts Title extraction
└── worker.ts Cloudflare Worker (API + status-page SSR)
site/
├── blocks.ts Types + localized shared blocks (shortcuts, privacy)
├── pages.ts English pages (one per search intent) + page registry
├── pages-{es,pt,ja,zh,ar,tr}.ts The same pages, localized (linked by `group`)
├── curated.ts Weekly “Best X Articles” pages from site/curated/*.json
├── guides.ts /guides — informational guides
├── mcp-page.ts /mcp-server — MCP setup docs
├── render.ts Head/meta/JSON-LD/content/footer rendering
└── plugin.ts Vite plugin: emits every page, 404.html and sitemap.xml
Heavy export libraries are dynamically imported, so the initial page only loads React + the app.
SEO & AI Discoverability
API
GET /api/thread/:id → { tweets: Tweet[], count, isThread } # self-thread containing :id
GET /api/tweet/:id → Tweet
GET /api/markdown/:id → text/markdown # ?format=markdown|text&thread=0|1&front_matter=0|1
POST /mcp → MCP (Streamable HTTP, stateless), tool read_x_post
Tweet mirrors the FxTwitter status object (text, author, media, quote, article, …). Responses are edge-cached for 5 minutes — please be gentle.
Weekly “Best X Articles”
/best-x-articles is a hand-reviewed weekly selection built from site/curated/*.json (title, author and a one-sentence summary in our own words — never the article text).
npm run curate -- 2026-W41 https://x.com/user/status/123 https://x.com/other/status/456
# → site/curated/2026-w41.json with title/author/lang filled in; write "intro", "topic" and "summary"
npm run build # issues with an empty summary are skipped automatically
The newest issue is shown on /best-x-articles (its /best-x-articles/<week> page canonicals to the hub until the next issue); older issues stay at /best-x-articles/<week>.
Contributing
- Fork the repo
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes
- Open a Pull Request
Notes:
- Landing page copy lives in
site/pages.ts; runnpm run buildto see the generated HTML - Keep
src/shared/*free of DOM/Node-only APIs — it runs in both the Worker and the dev server - Test Worker-only behavior (SSR, 404, headers) with
npm run preview:worker
License
MIT