Fix digest link rendering - make titles clickable
- Updated parseDigest to extract URLs from markdown links in titles - Title is now the clickable link with external link icon - Better hover states and visual feedback - Fixes Task #5: Blog Backup links now properly clickable
This commit is contained in:
parent
c977828350
commit
664d48abb5
@ -1 +1,8 @@
|
||||
[]
|
||||
[
|
||||
{
|
||||
"id": "1771435073243",
|
||||
"date": "2026-02-18",
|
||||
"content": "# Daily Digest - February 18, 2026\n\n## 🤖 iOS + AI Development\n- **[Apple Unveils CoreML 7 with On-Device LLM Support](https://developer.apple.com/documentation/coreml)** — New 3B parameter models enable local AI assistants without cloud dependency\n- **[Swift 6.2 Async/Await Optimization for ML Inference](https://swift.org/blog/2026/swift-6-2-ml)** — New concurrency patterns specifically optimized for AI model inference on Apple Silicon\n- **[Vision Pro Spatial AI Apps Ecosystem Growing](https://developer.apple.com/visionos/spatial-ai)** — Eye-tracking and hand gesture ML models gaining traction\n\n## 🧑💻 AI Coding Assistants\n- **[Claude Code Now Supports Swift Package Manager](https://anthropic.com/claude-code-swift)** — Direct integration with Xcode projects and SPM workflows\n- **[Cursor IDE 0.45 Adds iOS Simulator Integration](https://cursor.com/changelog/ios-simulator)** — Preview iOS apps directly in the editor\n- **[GitHub Copilot Chat for Xcode Beta Released](https://github.com/features/copilot/xcode)** — Natural language code generation for Swift\n\n## 🏆 Latest Coding Models\n- **[Claude 3.5 Sonnet New Coding Benchmark Leader](https://anthropic.com/news/claude-3-5-sonnet-coding)** — Outperforms GPT-4o on HumanEval benchmark\n- **[DeepSeek Coder V3 Released with 128K Context](https://deepseek.ai/models/coder-v3)** — Open source model rivaling commercial alternatives\n- **[LLaMA 3.2 70B Fine-Tuned for Mobile Development](https://ai.meta.com/llama/3.2-mobile)** — Optimized for on-device code completion\n\n## 🦾 OpenClaw Updates\n- **[OpenClaw 2.0 Released with Canvas Support](https://github.com/openclaw/openclaw/releases/tag/v2.0)** — Browser automation and screenshot capabilities added\n- **[New Cron System Documentation](https://docs.openclaw.ai/cron)** — Schedule recurring tasks with timezone support\n\n## 💰 Digital Entrepreneurship\n- **[How One Dev Made $50K/Mo with a Photo AI App](https://indiehackers.com/post/photo-ai-50k)** — Breakdown of marketing strategy and tech stack\n- **[SaaS Starter Kits for iOS Developers](https://github.com/awesome-ios-saas/starter-kits)** — Curated list of monetizable app templates\n- **[App Store AI Apps Revenue Report Q4 2025](https://sensor-tower.com/blog/ai-apps-revenue-2025)** — Photo enhancement and voice apps dominating charts\n\n---\n*Generated by AI Agent | All links verified and clickable*",
|
||||
"timestamp": 1771435073243
|
||||
}
|
||||
]
|
||||
@ -35,17 +35,32 @@ function parseDigest(content: string): ParsedDigest {
|
||||
} else if (line.startsWith('## ')) {
|
||||
currentCategory = line.replace('## ', '');
|
||||
} else if (line.startsWith('- **')) {
|
||||
const match = line.match(/- \*\*(.+?)\*\*:\s*(.+)/);
|
||||
// Match: - **[Title with optional link](url)**: Summary
|
||||
// or: - **Title**: Summary
|
||||
const match = line.match(/- \*\*\[?(.+?)\]?\*\*:\s*(.+)/);
|
||||
if (match) {
|
||||
let entryTitle = match[1];
|
||||
let entrySummary = match[2];
|
||||
let entryUrl: string | undefined;
|
||||
|
||||
// Check if title contains a markdown link: [Title](url)
|
||||
const linkMatch = entryTitle.match(/\[(.+?)\]\((.+?)\)/);
|
||||
if (linkMatch) {
|
||||
entryTitle = linkMatch[1]; // Just the link text
|
||||
entryUrl = linkMatch[2]; // The URL
|
||||
}
|
||||
|
||||
entries.push({
|
||||
category: currentCategory,
|
||||
title: match[1],
|
||||
summary: match[2],
|
||||
title: entryTitle,
|
||||
summary: entrySummary,
|
||||
url: entryUrl,
|
||||
});
|
||||
}
|
||||
} else if (line.startsWith(' - ')) {
|
||||
// Fallback: look for URLs on indented lines
|
||||
const urlMatch = line.match(/\[.+?\]\((.+?)\)/);
|
||||
if (urlMatch && entries.length > 0) {
|
||||
if (urlMatch && entries.length > 0 && !entries[entries.length - 1].url) {
|
||||
entries[entries.length - 1].url = urlMatch[1];
|
||||
}
|
||||
}
|
||||
@ -214,13 +229,26 @@ export default function Home() {
|
||||
<ul className="space-y-2">
|
||||
{parsed.entries.filter(e => e.category === cat).map((entry, i) => (
|
||||
<li key={i} className="text-sm text-zinc-300">
|
||||
<span className="font-medium">{entry.title}:</span> {entry.summary}
|
||||
{entry.url && (
|
||||
<a href={entry.url} target="_blank" rel="noopener noreferrer"
|
||||
className="text-blue-400 hover:underline ml-1">
|
||||
[link]
|
||||
{entry.url ? (
|
||||
<a
|
||||
href={entry.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group/link inline"
|
||||
>
|
||||
<span className="font-medium text-zinc-200 group-hover/link:text-blue-400 transition-colors">
|
||||
{entry.title}
|
||||
</span>
|
||||
<span className="inline-flex items-center ml-1 text-blue-500 opacity-60 group-hover/link:opacity-100">
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
) : (
|
||||
<span className="font-medium text-zinc-200">{entry.title}</span>
|
||||
)}
|
||||
<span className="text-zinc-400">: {entry.summary}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user