fix: improve article detection and quote tweet display

- Use startsWith check instead of fragile length heuristic for
  detecting full article body vs preview mode
- Add media display for quote tweets (🖼️/🎬/🔄 indicators)
- Add preview_text to GraphqlTweetResult type definition to avoid
  type assertions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
George Zhang
2026-01-11 23:35:03 +00:00
committed by Peter Steinberger
co-authored by Claude Opus 4.5
parent 0475a2e1b6
commit d40c048333
3 changed files with 15 additions and 8 deletions
+11 -4
View File
@@ -323,10 +323,10 @@ export function createCliContext(normalizedArgs: string[], env: NodeJS.ProcessEn
// Display tweet text, with article indicator if present // Display tweet text, with article indicator if present
if (tweet.article) { if (tweet.article) {
// For articles: show preview if text is just the title (home timeline), // Full body mode: text starts with article title (from extractArticleText)
// otherwise show full text (single tweet detail) // Preview mode: text is short tweet intro that doesn't start with title
const isFullBody = tweet.text.length > tweet.article.title.length + 50; const hasFullBody = tweet.text.startsWith(tweet.article.title);
if (isFullBody) { if (hasFullBody) {
console.log(`📰 ${tweet.text}`); console.log(`📰 ${tweet.text}`);
} else { } else {
console.log(`📰 ${tweet.article.title}`); console.log(`📰 ${tweet.article.title}`);
@@ -356,6 +356,13 @@ export function createCliContext(normalizedArgs: string[], env: NodeJS.ProcessEn
for (const line of truncated.split('\n').slice(0, 4)) { for (const line of truncated.split('\n').slice(0, 4)) {
console.log(`│ ${line}`); console.log(`│ ${line}`);
} }
// Display quoted tweet media
if (tweet.quotedTweet.media && tweet.quotedTweet.media.length > 0) {
for (const m of tweet.quotedTweet.media) {
const label = m.type === 'video' ? '🎬' : m.type === 'animated_gif' ? '🔄' : '🖼️';
console.log(`│ ${label} ${m.url}`);
}
}
console.log(`└─ https://x.com/${tweet.quotedTweet.author.username}/status/${tweet.quotedTweet.id}`); console.log(`└─ https://x.com/${tweet.quotedTweet.author.username}/status/${tweet.quotedTweet.id}`);
} }
+3
View File
@@ -80,9 +80,12 @@ export type GraphqlTweetResult = {
}; };
}; };
article?: { article?: {
title?: string;
preview_text?: string;
article_results?: { article_results?: {
result?: { result?: {
title?: string; title?: string;
preview_text?: string;
plain_text?: string; plain_text?: string;
text?: string; text?: string;
richtext?: { richtext?: {
+1 -4
View File
@@ -168,10 +168,7 @@ export function extractArticleMetadata(
} }
// preview_text is available in home timeline responses // preview_text is available in home timeline responses
const previewText = firstText( const previewText = firstText(articleResult.preview_text, article.preview_text);
(articleResult as { preview_text?: string }).preview_text,
(article as { preview_text?: string }).preview_text,
);
return { title, previewText }; return { title, previewText };
} }