Skip to content

feat(api): Support X native articles/blogs (appear as empty tweets) #205

@ainergiz

Description

@ainergiz

Summary

X native articles (blogs) appear as empty tweets in xfeed. There's no way to distinguish them from regular tweets, and their content isn't displayed.

Example

Tweet URL: https://x.com/levie/status/2007958155137876183

Current behavior:

  • Tweet shows author header and engagement stats
  • Body is completely empty (no text content)
  • Only indication is the "Links" section showing x.com/i/article/2007...

Screenshot:
![Article showing as empty](Screenshot attached to issue - shows Aaron Levie's tweet with empty body)

Context

X native articles use the URL format x.com/i/article/{id} and are delivered via the article field in the GraphQL response rather than legacy.full_text.

The codebase already has:

  • ArticleData type (types.ts:318-356)
  • extractArticleText() function (client.ts:735-809)
  • Feature flags like articles_preview_enabled, responsive_web_twitter_article_tweet_consumption_enabled

However, article content extraction fails silently for some articles, particularly in timeline/bookmark contexts where the fetchUserArticlePlainText fallback (only in getTweet()) doesn't run.

Current Behavior

// client.ts:1026-1029
const rawText = this.extractTweetText(result);
if (!rawText) {
  return undefined;  // Tweet silently dropped OR shows empty
}

When extractArticleText() returns undefined:

  • If legacy.full_text is also empty/missing → tweet text is empty
  • User sees an empty post with no indication it's an article

Expected Behavior

  1. Detect articles: Add isArticle?: boolean to TweetData type
  2. Display article indicator: Show "📰 Article" or similar in PostCard when content is article-based
  3. Graceful fallback: If article text extraction fails, show:
    • Article title (if available)
    • "View article" prompt with link
    • Not just an empty tweet body
  4. Timeline support: Apply the same fallback logic used in getTweet() to timeline/bookmark parsing

Potential Implementation

Option A: Add article type detection + UI indicator

// TweetData type extension
interface TweetData {
  // ... existing fields
  isArticle?: boolean;
  articleTitle?: string;
}

// In mapTweetResult():
const hasArticle = !!result?.article;
if (hasArticle && !rawText) {
  // Fallback: show title or placeholder
  text = this.extractArticleTitle(result) ?? "[Article - tap to view]";
}

// In PostCard.tsx:
{post.isArticle && (
  <text fg={colors.info}>📰 Article</text>
)}

Option B: Fetch article content for timeline posts

Apply the same fetchUserArticlePlainText fallback used in getTweet() to parseTweetsFromInstructions() for posts with articles.

Files to Modify

  • src/api/types.ts - Add isArticle, articleTitle to TweetData
  • src/api/client.ts - Improve article detection and fallback in mapTweetResult()
  • src/components/PostCard.tsx - Add article indicator UI
  • src/components/PostDetail.tsx - Show article link/content appropriately

Acceptance Criteria

  • Articles are visually distinguished from regular tweets
  • Article title is displayed when available
  • Empty article tweets show informative placeholder (not empty body)
  • Article link (x.com/i/article/...) is actionable
  • Works in timeline, bookmarks, and single tweet views
  • Debug flag XFEED_DEBUG_ARTICLE=1 continues to work
  • All tests passing (bun run test)

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions