doc-fetch-cli 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +2 -0
  2. package/bin/doc-fetch_darwin_amd64 +0 -0
  3. package/bin/doc-fetch_windows_amd64.exe +0 -0
  4. package/doc-fetch +0 -0
  5. package/doc-fetch_darwin_amd64 +0 -0
  6. package/doc-fetch_darwin_arm64 +0 -0
  7. package/doc-fetch_linux_amd64 +0 -0
  8. package/doc-fetch_windows_amd64.exe +0 -0
  9. package/package.json +1 -1
  10. package/website/BLOG-SETUP-SUMMARY.md +385 -0
  11. package/website/DEPLOYMENT.md +189 -0
  12. package/website/LAUNCH-CHECKLIST.md +134 -0
  13. package/website/README.md +75 -0
  14. package/website/SEO-STRATEGY.md +347 -0
  15. package/website/URL-STRUCTURE.md +334 -0
  16. package/website/WEBSITE-SUMMARY.md +246 -0
  17. package/website/package-lock.json +1628 -0
  18. package/website/package.json +39 -0
  19. package/website/pnpm-lock.yaml +1061 -0
  20. package/website/src/app.d.ts +13 -0
  21. package/website/src/app.html +11 -0
  22. package/website/src/lib/actions/addCopyButtons.ts +73 -0
  23. package/website/src/lib/assets/favicon.svg +1 -0
  24. package/website/src/lib/components/CopyCodeButton.svelte +97 -0
  25. package/website/src/lib/components/DarkModeToggle.svelte +140 -0
  26. package/website/src/lib/components/ReadingProgress.svelte +36 -0
  27. package/website/src/lib/components/RelatedPosts.svelte +151 -0
  28. package/website/src/lib/components/TableOfContents.svelte +184 -0
  29. package/website/src/lib/index.ts +1 -0
  30. package/website/src/lib/posts/convert-docs-to-markdown.md +506 -0
  31. package/website/src/routes/+layout.svelte +59 -0
  32. package/website/src/routes/+page.svelte +1033 -0
  33. package/website/src/routes/about/+page.svelte +607 -0
  34. package/website/src/routes/blog/+page.svelte +486 -0
  35. package/website/src/routes/blog/[slug]/+page.svelte +988 -0
  36. package/website/src/routes/blog/[slug]/+page.ts +53 -0
  37. package/website/src/routes/sitemap.xml/+server.ts +62 -0
  38. package/website/static/favicon.svg +10 -0
  39. package/website/static/og.png +2 -0
  40. package/website/static/og.svg +26 -0
  41. package/website/static/robots.txt +43 -0
  42. package/website/svelte.config.js +13 -0
  43. package/website/tsconfig.json +20 -0
  44. package/website/vite.config.ts +6 -0
@@ -0,0 +1,988 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/stores';
3
+ import type { PageData } from './$types';
4
+ import { marked } from 'marked';
5
+ import { onMount } from 'svelte';
6
+ import TableOfContents from '$lib/components/TableOfContents.svelte';
7
+ import ReadingProgress from '$lib/components/ReadingProgress.svelte';
8
+ import RelatedPosts from '$lib/components/RelatedPosts.svelte';
9
+ import { addCopyButtons } from '$lib/actions/addCopyButtons';
10
+
11
+ let { data }: { data: PageData } = $props();
12
+ let post = data.post;
13
+
14
+ const baseUrl = 'https://docfetch.dev';
15
+ const canonicalUrl = `${baseUrl}/blog/${post.slug}`;
16
+
17
+ // Animated read time counter
18
+ let animatedReadTime = '0 min read';
19
+
20
+ onMount(() => {
21
+ // Extract number from post.readTime (e.g., "8 min read" → 8)
22
+ const match = post.readTime.match(/(\d+)/);
23
+ const targetMinutes = match ? parseInt(match[1]) : 0;
24
+
25
+ // Animate from 0 to target
26
+ let current = 0;
27
+ const duration = 1000; // 1 second animation
28
+ const startTime = performance.now();
29
+
30
+ function animate(currentTime: number) {
31
+ const elapsed = currentTime - startTime;
32
+ const progress = Math.min(elapsed / duration, 1);
33
+
34
+ // Ease-out quart for smooth deceleration
35
+ const eased = 1 - Math.pow(1 - progress, 4);
36
+ current = Math.floor(eased * targetMinutes);
37
+
38
+ animatedReadTime = `${current} min read`;
39
+
40
+ if (progress < 1) {
41
+ requestAnimationFrame(animate);
42
+ } else {
43
+ animatedReadTime = post.readTime; // Ensure final value matches exactly
44
+ }
45
+ }
46
+
47
+ requestAnimationFrame(animate);
48
+ });
49
+
50
+ // For now, using placeholder content - will load from markdown file in production
51
+ const placeholderContent = `
52
+ ## Quick Start
53
+
54
+ Install DocFetch and convert your first documentation site:
55
+
56
+ \`\`\`bash
57
+ npm install -g doc-fetch
58
+ doc-fetch --url https://golang.org/doc/ --output go-docs.md --llm-txt
59
+ \`\`\`
60
+
61
+ **Full article coming soon!** This is a placeholder until we properly integrate markdown loading.
62
+
63
+ The complete guide covers:
64
+ - Why LLMs need complete documentation context
65
+ - Three methods compared (manual, extensions, automated)
66
+ - Step-by-step DocFetch tutorial
67
+ - LLM.txt indexing explained
68
+ - Token optimization tips
69
+ - Real-world examples
70
+
71
+ Stay tuned!
72
+ `;
73
+
74
+ const htmlContent = marked(placeholderContent);
75
+
76
+ // Helper functions for related posts
77
+ function getRelatedPostTitle(slug: string): string {
78
+ const titles: Record<string, string> = {
79
+ 'llm-txt-index-guide': 'LLM.txt Explained: The Secret to Better AI Context Navigation',
80
+ 'ai-agent-documentation-problem': 'Why AI Agents Can\'t Read Documentation (And How to Fix It)',
81
+ 'best-practices-rag-context-preparation': 'RAG Context Preparation: 7 Best Practices from Production',
82
+ 'compare-documentation-fetchers': 'Documentation Fetchers Compared: DocFetch vs Alternatives',
83
+ 'token-efficiency-llm-context': 'Token Efficiency: How to Fit More Context in Less Tokens'
84
+ };
85
+ return titles[slug] || 'Related Article';
86
+ }
87
+
88
+ function getRelatedPostExcerpt(slug: string): string {
89
+ const excerpts: Record<string, string> = {
90
+ 'llm-txt-index-guide': 'What is llm.txt? How does it supercharge your AI agents? Complete guide to semantic documentation indexing.',
91
+ 'ai-agent-documentation-problem': 'The fundamental problem with AI agents and web navigation. Real-world solutions for production deployments.',
92
+ 'best-practices-rag-context-preparation': 'Learn from real deployments: chunking strategies, metadata enrichment, and indexing approaches that work.',
93
+ 'compare-documentation-fetchers': 'Honest comparison of tools for converting docs to markdown. Features, limitations, and when to use each.',
94
+ 'token-efficiency-llm-context': 'Reduce LLM costs by 60%+ with smart context preparation. Cleaning strategies and compression techniques.'
95
+ };
96
+ return excerpts[slug] || 'Read more about this topic.';
97
+ }
98
+
99
+ function getRelativeTime(dateStr: string): string {
100
+ const date = new Date(dateStr);
101
+ const now = new Date();
102
+ const diffMs = now.getTime() - date.getTime();
103
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
104
+
105
+ if (diffDays === 0) return 'today';
106
+ if (diffDays === 1) return 'yesterday';
107
+ if (diffDays < 7) return `${diffDays} days ago`;
108
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)} week${Math.floor(diffDays / 7) > 1 ? 's' : ''} ago`;
109
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)} month${Math.floor(diffDays / 30) > 1 ? 's' : ''} ago`;
110
+ return `${Math.floor(diffDays / 365)} year${Math.floor(diffDays / 365) > 1 ? 's' : ''} ago`;
111
+ }
112
+
113
+ // Simulated share counts (in production, fetch from API)
114
+ function getShareCount(platform: string): string {
115
+ const counts: Record<string, number> = {
116
+ twitter: Math.floor(Math.random() * 200) + 50,
117
+ linkedin: Math.floor(Math.random() * 150) + 30,
118
+ hn: Math.floor(Math.random() * 100) + 20
119
+ };
120
+ const count = counts[platform] || 0;
121
+ return count > 999 ? `${(count / 1000).toFixed(1)}k` : count.toString();
122
+ }
123
+
124
+ function getTotalShares(): string {
125
+ const total =
126
+ Math.floor(Math.random() * 200) + 50 +
127
+ Math.floor(Math.random() * 150) + 30 +
128
+ Math.floor(Math.random() * 100) + 20;
129
+ return total > 999 ? `${(total / 1000).toFixed(1)}k` : total.toString();
130
+ }
131
+ </script>
132
+
133
+ <svelte:head>
134
+ <title>{post.title} | DocFetch Blog</title>
135
+ <meta name="description" content={post.excerpt} />
136
+ <meta name="keywords" content="convert documentation to markdown, LLM context, AI documentation, RAG preparation, markdown converter, DocFetch tutorial" />
137
+
138
+ <!-- Open Graph -->
139
+ <meta property="og:type" content="article" />
140
+ <meta property="og:title" content={post.title} />
141
+ <meta property="og:description" content="Complete guide to converting documentation websites into AI-ready markdown" />
142
+ <meta property="og:published_time" content={post.date} />
143
+ <meta property="article:author" content={post.author} />
144
+ <meta property="article:tag" content={post.tags.join(',')} />
145
+
146
+ <!-- Twitter Card -->
147
+ <meta name="twitter:card" content="summary_large_image" />
148
+ <meta name="twitter:title" content={post.title} />
149
+ <meta name="twitter:description" content="Step-by-step guide with code examples and best practices" />
150
+
151
+ <!-- Structured Data: BlogPosting Schema -->
152
+ <script type="application/ld+json">
153
+ {
154
+ "@context": "https://schema.org",
155
+ "@type": "BlogPosting",
156
+ "headline": {post.title},
157
+ "description": "{post.excerpt}",
158
+ "url": "{canonicalUrl}",
159
+ "author": {
160
+ "@type": "Person",
161
+ "name": {post.author},
162
+ "url": "https://github.com/AlphaTechini"
163
+ },
164
+ "datePublished": "{post.date}",
165
+ "dateModified": "{post.modifiedDate || post.date}",
166
+ "publisher": {
167
+ "@type": "Organization",
168
+ "name": "DocFetch",
169
+ "logo": {
170
+ "@type": "ImageObject",
171
+ "url": "{baseUrl}/favicon.svg"
172
+ }
173
+ },
174
+ "mainEntityOfPage": {
175
+ "@type": "WebPage",
176
+ "@id": canonicalUrl
177
+ },
178
+ "articleBody": "Complete guide to converting documentation websites into AI-ready markdown with LLM.txt indexing",
179
+ "wordCount": 3500,
180
+ "inLanguage": "en-US",
181
+ "keywords": {post.tags.join(', ')},
182
+ "articleSection": {post.category || 'Tutorial'}
183
+ }
184
+ </script>
185
+
186
+ <!-- Structured Data: FAQPage Schema (for People Also Ask) -->
187
+ <script type="application/ld+json">
188
+ {
189
+ "@context": "https://schema.org",
190
+ "@type": "FAQPage",
191
+ "mainEntity": {post.faqs.map(faq => ({
192
+ "@type": "Question",
193
+ "name": faq.question,
194
+ "acceptedAnswer": {
195
+ "@type": "Answer",
196
+ "text": faq.answer
197
+ }
198
+ }))}
199
+ }
200
+ </script>
201
+ </svelte:head>
202
+
203
+ <div class="container">
204
+ <ReadingProgress />
205
+
206
+ <header>
207
+ <nav>
208
+ <div class="logo">
209
+ <a href="/">
210
+ <span class="logo-icon">📚</span>
211
+ <span class="logo-text">DocFetch</span>
212
+ </a>
213
+ </div>
214
+ <div class="nav-links">
215
+ <a href="/#features">Features</a>
216
+ <a href="/#installation">Installation</a>
217
+ <a href="/blog" class="active">Blog</a>
218
+ <a href="https://github.com/AlphaTechini/doc-fetch" target="_blank" rel="noopener noreferrer">GitHub →</a>
219
+ </div>
220
+ </nav>
221
+ </header>
222
+
223
+ <div class="post-layout">
224
+ <main class="post-content">
225
+ <article class="post">
226
+ <header class="post-header">
227
+ <div class="breadcrumbs">
228
+ <a href="/blog">Blog</a>
229
+ <span>/</span>
230
+ <span>{post.title}</span>
231
+ </div>
232
+
233
+ <h1>{post.title}</h1>
234
+
235
+ <div class="meta">
236
+ <time datetime={post.date}>{post.date}</time>
237
+ <span class="separator">•</span>
238
+ <span class="author">{post.author}</span>
239
+ <span class="separator">•</span>
240
+ <span class="read-time">
241
+ <span class="read-time-icon">
242
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
243
+ <circle cx="12" cy="12" r="10"></circle>
244
+ <polyline points="12 6 12 12 16 14"></polyline>
245
+ </svg>
246
+ </span>
247
+ <span class="read-time-text">{animatedReadTime}</span>
248
+ </span>
249
+ {#if post.modifiedDate && post.modifiedDate !== post.date}
250
+ <span class="separator">•</span>
251
+ <span class="updated-badge" title="Last updated">
252
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
253
+ <polyline points="23 4 23 10 17 10"></polyline>
254
+ <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
255
+ </svg>
256
+ Updated {getRelativeTime(post.modifiedDate)}
257
+ </span>
258
+ {/if}
259
+ </div>
260
+
261
+ <div class="tags">
262
+ {#each post.tags as tag}
263
+ <span class="tag">{tag}</span>
264
+ {/each}
265
+ </div>
266
+ </header>
267
+
268
+ <div class="content" use:addCopyButtons>
269
+ {@html htmlContent}
270
+ </div>
271
+ </article>
272
+
273
+ <footer class="post-footer">
274
+ <!-- Author Bio Box (E-E-A-T Signal) -->
275
+ <section class="author-bio">
276
+ <div class="author-avatar">
277
+ 👨‍💻
278
+ </div>
279
+ <div class="author-info">
280
+ <h4>{post.author}</h4>
281
+ <p class="author-role">Senior Infrastructure Engineer</p>
282
+ <p class="author-desc">
283
+ Specializing in Web2 + Web3 systems, AI orchestration, and distributed automation.
284
+ Building developer tools that reduce friction and increase leverage.
285
+ </p>
286
+ <div class="author-links">
287
+ <a href="https://github.com/AlphaTechini" target="_blank" rel="noopener noreferrer" class="author-link">
288
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
289
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
290
+ </svg>
291
+ GitHub
292
+ </a>
293
+ <a href="https://www.npmjs.com/~alphatechini" target="_blank" rel="noopener noreferrer" class="author-link">
294
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
295
+ <path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l1.836-.017.016 13.472H5.146zm5.092 0h1.82v9.02h3.669V5.323h1.837v10.86h-9.163zm8.963 0h1.836v13.472h-1.836z"/>
296
+ </svg>
297
+ NPM
298
+ </a>
299
+ </div>
300
+ </div>
301
+ </section>
302
+
303
+ <!-- Related Posts with Thumbnails -->
304
+ <RelatedPosts
305
+ posts={post.relatedPosts?.map(slug => ({
306
+ slug,
307
+ title: getRelatedPostTitle(slug),
308
+ excerpt: getRelatedPostExcerpt(slug),
309
+ readTime: '8 min read'
310
+ })) || []}
311
+ />
312
+
313
+ <div class="cta-box">
314
+ <h3>Ready to Convert Your Documentation?</h3>
315
+ <p>DocFetch automates the entire process. One command, complete docs, AI-ready output.</p>
316
+ <div class="cta-buttons">
317
+ <a href="/#installation" class="btn primary">Install DocFetch</a>
318
+ <a href="https://github.com/AlphaTechini/doc-fetch" target="_blank" rel="noopener noreferrer" class="btn secondary">View on GitHub</a>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="share-section">
323
+ <h4>Share this article</h4>
324
+ <div class="share-buttons">
325
+ <a href="https://twitter.com/intent/tweet?text={encodeURIComponent(post.title)}&url={encodeURIComponent(canonicalUrl)}" target="_blank" rel="noopener noreferrer" class="share-btn twitter">
326
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
327
+ Twitter
328
+ <span class="share-count">{getShareCount('twitter')}</span>
329
+ </a>
330
+ <a href="https://www.linkedin.com/sharing/share-offsite/?url={encodeURIComponent(canonicalUrl)}" target="_blank" rel="noopener noreferrer" class="share-btn linkedin">
331
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M4.98 3.5c0 1.381-1.11 2.5-2.48 2.5s-2.48-1.119-2.48-2.5c0-1.38 1.11-2.5 2.48-2.5s2.48 1.12 2.48 2.5zm.02 4.5h-5v16h5v-16zm7.982 0h-4.968v16h4.969v-8.399c0-4.67 6.029-5.052 6.029 0v8.399h4.988v-10.131c0-7.88-8.922-7.593-11.018-3.714v-2.155z"/></svg>
332
+ LinkedIn
333
+ <span class="share-count">{getShareCount('linkedin')}</span>
334
+ </a>
335
+ <a href="https://news.ycombinator.com/submitlink?u={encodeURIComponent(canonicalUrl)}&t={encodeURIComponent(post.title)}" target="_blank" rel="noopener noreferrer" class="share-btn hn">
336
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2 2h20v20H2V2zm10.994 13.023l4.933-9.023h-2.229l-3.765 7.535L8.152 6H5.924l4.936 9.023h2.134z"/></svg>
337
+ Hacker News
338
+ <span class="share-count">{getShareCount('hn')}</span>
339
+ </a>
340
+ </div>
341
+ <p class="share-note">Join {getTotalShares()} developers who shared this article</p>
342
+ </div>
343
+ </footer>
344
+ </main>
345
+
346
+ <aside class="post-sidebar">
347
+ <TableOfContents contentSelector=".content" title="On this page" />
348
+ </aside>
349
+ </div>
350
+
351
+ <footer class="site-footer">
352
+ <div class="footer-content">
353
+ <div class="footer-left">
354
+ <p>Built with ❤️ for AI developers who deserve better documentation access</p>
355
+ <p class="copyright">&copy; 2026 AlphaTechini. MIT License.</p>
356
+ </div>
357
+ <div class="footer-right">
358
+ <a href="https://github.com/AlphaTechini/doc-fetch" target="_blank" rel="noopener noreferrer">GitHub</a>
359
+ <a href="/blog">Blog</a>
360
+ <a href="https://www.npmjs.com/package/doc-fetch" target="_blank" rel="noopener noreferrer">NPM</a>
361
+ </div>
362
+ </div>
363
+ </footer>
364
+ </div>
365
+
366
+ <style>
367
+ :global(:root) {
368
+ --bg-primary: #ffffff;
369
+ --bg-secondary: #f8f9fa;
370
+ --text-primary: #1a1a1a;
371
+ --text-secondary: #4a4a4a;
372
+ --text-muted: #6b7280;
373
+ --accent: #0066cc;
374
+ --accent-hover: #0052a3;
375
+ --border: #e5e7eb;
376
+ --max-width: 800px;
377
+ }
378
+
379
+ :global(body) {
380
+ margin: 0;
381
+ padding: 0;
382
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
383
+ background: var(--bg-primary);
384
+ color: var(--text-primary);
385
+ line-height: 1.7;
386
+ }
387
+
388
+ .container {
389
+ max-width: 1200px;
390
+ margin: 0 auto;
391
+ padding: 0 2rem;
392
+ }
393
+
394
+ header {
395
+ position: fixed;
396
+ top: 0;
397
+ left: 0;
398
+ right: 0;
399
+ background: rgba(255, 255, 255, 0.95);
400
+ backdrop-filter: blur(10px);
401
+ border-bottom: 1px solid var(--border);
402
+ z-index: 1000;
403
+ }
404
+
405
+ nav {
406
+ display: flex;
407
+ justify-content: space-between;
408
+ align-items: center;
409
+ padding: 1rem 2rem;
410
+ max-width: 1200px;
411
+ margin: 0 auto;
412
+ }
413
+
414
+ .logo a {
415
+ display: flex;
416
+ align-items: center;
417
+ gap: 0.5rem;
418
+ font-weight: 700;
419
+ font-size: 1.25rem;
420
+ text-decoration: none;
421
+ color: var(--text-primary);
422
+ }
423
+
424
+ .logo-icon {
425
+ font-size: 1.5rem;
426
+ }
427
+
428
+ .nav-links {
429
+ display: flex;
430
+ gap: 2rem;
431
+ }
432
+
433
+ .nav-links a {
434
+ color: var(--text-secondary);
435
+ text-decoration: none;
436
+ font-size: 0.95rem;
437
+ transition: color 0.2s;
438
+ }
439
+
440
+ .nav-links a:hover,
441
+ .nav-links a.active {
442
+ color: var(--accent);
443
+ }
444
+
445
+ .post-layout {
446
+ display: grid;
447
+ grid-template-columns: 1fr 280px;
448
+ gap: 3rem;
449
+ padding: 8rem 0 4rem;
450
+ max-width: 1200px;
451
+ margin: 0 auto;
452
+ }
453
+
454
+ .post-content {
455
+ min-width: 0;
456
+ }
457
+
458
+ .post-sidebar {
459
+ position: relative;
460
+ }
461
+
462
+ .post {
463
+ margin-bottom: 3rem;
464
+ }
465
+
466
+ .post-header {
467
+ margin-bottom: 3rem;
468
+ padding-bottom: 2rem;
469
+ border-bottom: 1px solid var(--border);
470
+ }
471
+
472
+ .breadcrumbs {
473
+ font-size: 0.875rem;
474
+ color: var(--text-muted);
475
+ margin-bottom: 1.5rem;
476
+ }
477
+
478
+ .breadcrumbs a {
479
+ color: var(--text-muted);
480
+ text-decoration: none;
481
+ }
482
+
483
+ .breadcrumbs a:hover {
484
+ color: var(--accent);
485
+ }
486
+
487
+ h1 {
488
+ font-size: 2.5rem;
489
+ font-weight: 800;
490
+ line-height: 1.2;
491
+ margin: 0 0 1.5rem;
492
+ letter-spacing: -0.01em;
493
+ }
494
+
495
+ .meta {
496
+ display: flex;
497
+ gap: 0.5rem;
498
+ align-items: center;
499
+ font-size: 0.95rem;
500
+ color: var(--text-muted);
501
+ margin-bottom: 1.5rem;
502
+ flex-wrap: wrap;
503
+ }
504
+
505
+ .separator {
506
+ color: var(--border);
507
+ }
508
+
509
+ .read-time {
510
+ display: inline-flex;
511
+ align-items: center;
512
+ gap: 0.375rem;
513
+ font-weight: 600;
514
+ color: var(--accent);
515
+ }
516
+
517
+ .read-time-icon {
518
+ display: flex;
519
+ align-items: center;
520
+ }
521
+
522
+ .updated-badge {
523
+ display: inline-flex;
524
+ align-items: center;
525
+ gap: 0.375rem;
526
+ background: rgba(39, 201, 63, 0.1);
527
+ color: #27c93f;
528
+ padding: 0.25rem 0.625rem;
529
+ border-radius: 12px;
530
+ font-size: 0.8rem;
531
+ font-weight: 600;
532
+ }
533
+
534
+ .dark .updated-badge {
535
+ background: rgba(39, 201, 63, 0.15);
536
+ color: #3fb950;
537
+ }
538
+
539
+ .tags {
540
+ display: flex;
541
+ gap: 0.5rem;
542
+ flex-wrap: wrap;
543
+ }
544
+
545
+ .tag {
546
+ background: rgba(0, 102, 204, 0.1);
547
+ color: var(--accent);
548
+ padding: 0.25rem 0.75rem;
549
+ border-radius: 12px;
550
+ font-size: 0.8rem;
551
+ font-weight: 500;
552
+ }
553
+
554
+ .author-bio {
555
+ display: flex;
556
+ gap: 1.5rem;
557
+ background: var(--bg-secondary);
558
+ padding: 2rem;
559
+ border-radius: 8px;
560
+ margin-bottom: 3rem;
561
+ align-items: center;
562
+ }
563
+
564
+ .author-avatar {
565
+ font-size: 4rem;
566
+ flex-shrink: 0;
567
+ }
568
+
569
+ .author-info h4 {
570
+ margin: 0 0 0.25rem;
571
+ font-size: 1.25rem;
572
+ }
573
+
574
+ .author-role {
575
+ color: var(--accent);
576
+ font-weight: 600;
577
+ margin: 0 0 0.75rem;
578
+ font-size: 0.95rem;
579
+ }
580
+
581
+ .author-desc {
582
+ color: var(--text-secondary);
583
+ margin: 0 0 1rem;
584
+ line-height: 1.6;
585
+ }
586
+
587
+ .author-links {
588
+ display: flex;
589
+ gap: 1rem;
590
+ }
591
+
592
+ .author-link {
593
+ display: inline-flex;
594
+ align-items: center;
595
+ gap: 0.5rem;
596
+ color: var(--text-secondary);
597
+ text-decoration: none;
598
+ font-weight: 500;
599
+ transition: color 0.2s;
600
+ }
601
+
602
+ .author-link:hover {
603
+ color: var(--accent);
604
+ }
605
+
606
+ .author-link svg {
607
+ width: 18px;
608
+ height: 18px;
609
+ }
610
+
611
+ .content {
612
+ font-size: 1.1875rem; /* 19px - improved readability */
613
+ line-height: 1.85; /* Better vertical rhythm */
614
+ max-width: 65ch; /* Optimal line length for reading */
615
+ }
616
+
617
+ .content h2 {
618
+ font-size: 1.875rem; /* 30px */
619
+ margin-top: 3.5rem;
620
+ margin-bottom: 1.5rem;
621
+ font-weight: 700;
622
+ letter-spacing: -0.01em;
623
+ }
624
+
625
+ .content h3 {
626
+ font-size: 1.5rem; /* 24px */
627
+ margin-top: 2.75rem;
628
+ margin-bottom: 1.25rem;
629
+ font-weight: 600;
630
+ }
631
+
632
+ .content h4 {
633
+ font-size: 1.25rem;
634
+ margin-top: 2rem;
635
+ margin-bottom: 1rem;
636
+ font-weight: 600;
637
+ }
638
+
639
+ .content p {
640
+ margin-bottom: 1.75rem; /* More breathing room */
641
+ }
642
+
643
+ .content ul, .content ol {
644
+ margin-bottom: 1.75rem;
645
+ padding-left: 2rem;
646
+ }
647
+
648
+ .content li {
649
+ margin-bottom: 0.875rem; /* Better spacing between items */
650
+ line-height: 1.7;
651
+ }
652
+
653
+ .content pre {
654
+ background: #1e1e1e;
655
+ color: #d4d4d4;
656
+ padding: 1.5rem;
657
+ border-radius: 6px;
658
+ overflow-x: auto;
659
+ margin: 1.5rem 0;
660
+ font-size: 0.9rem;
661
+ line-height: 1.6;
662
+ position: relative;
663
+ }
664
+
665
+ .content code {
666
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
667
+ color: #d4d4d4;
668
+ }
669
+
670
+ .content pre code {
671
+ color: #d4d4d4;
672
+ }
673
+
674
+ .copy-code-button {
675
+ position: absolute;
676
+ top: 0.75rem;
677
+ right: 0.75rem;
678
+ background: rgba(255, 255, 255, 0.1);
679
+ border: none;
680
+ border-radius: 4px;
681
+ padding: 0.5rem;
682
+ cursor: pointer;
683
+ color: #d4d4d4;
684
+ transition: all 0.2s;
685
+ opacity: 0;
686
+ z-index: 10;
687
+ display: flex;
688
+ align-items: center;
689
+ gap: 0.5rem;
690
+ font-size: 0.875rem;
691
+ }
692
+
693
+ .copy-code-button:hover,
694
+ pre:hover .copy-code-button {
695
+ opacity: 1;
696
+ background: rgba(255, 255, 255, 0.15);
697
+ }
698
+
699
+ .copy-code-button:active {
700
+ transform: scale(0.95);
701
+ }
702
+
703
+ .copy-code-button.copied {
704
+ background: rgba(39, 201, 63, 0.2);
705
+ }
706
+
707
+ .copy-code-button svg {
708
+ display: block;
709
+ }
710
+
711
+ .copy-code-button .tooltip {
712
+ background: rgba(0, 0, 0, 0.8);
713
+ color: white;
714
+ padding: 0.25rem 0.5rem;
715
+ border-radius: 4px;
716
+ font-size: 0.75rem;
717
+ white-space: nowrap;
718
+ pointer-events: none;
719
+ }
720
+
721
+ .content :global(code:not(pre code)) {
722
+ background: var(--bg-secondary);
723
+ padding: 0.2rem 0.5rem;
724
+ border-radius: 3px;
725
+ font-size: 0.9em;
726
+ color: var(--text-primary);
727
+ }
728
+
729
+ .content ul, .content ol {
730
+ margin-bottom: 1.5rem;
731
+ padding-left: 2rem;
732
+ }
733
+
734
+ .content li {
735
+ margin-bottom: 0.75rem;
736
+ }
737
+
738
+ .content blockquote {
739
+ border-left: 3px solid var(--accent);
740
+ padding-left: 1.5rem;
741
+ margin: 1.5rem 0;
742
+ color: var(--text-secondary);
743
+ font-style: italic;
744
+ }
745
+
746
+ .content hr {
747
+ border: none;
748
+ border-top: 1px solid var(--border);
749
+ margin: 3rem 0;
750
+ }
751
+
752
+ .post-footer {
753
+ margin-top: 4rem;
754
+ padding-top: 3rem;
755
+ border-top: 1px solid var(--border);
756
+ }
757
+
758
+ .related-posts {
759
+ margin-bottom: 3rem;
760
+ padding-bottom: 2rem;
761
+ border-bottom: 1px solid var(--border);
762
+ }
763
+
764
+ .related-posts h3 {
765
+ font-size: 1.4rem;
766
+ margin-bottom: 1.5rem;
767
+ }
768
+
769
+ .related-grid {
770
+ display: grid;
771
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
772
+ gap: 1.5rem;
773
+ }
774
+
775
+ .related-card {
776
+ background: var(--bg-secondary);
777
+ padding: 1.5rem;
778
+ border-radius: 6px;
779
+ transition: transform 0.2s;
780
+ }
781
+
782
+ .related-card:hover {
783
+ transform: translateY(-2px);
784
+ }
785
+
786
+ .related-card h4 {
787
+ font-size: 1.1rem;
788
+ margin: 0 0 0.75rem;
789
+ }
790
+
791
+ .related-card h4 a {
792
+ color: var(--text-primary);
793
+ text-decoration: none;
794
+ }
795
+
796
+ .related-card h4 a:hover {
797
+ color: var(--accent);
798
+ }
799
+
800
+ .related-card p {
801
+ font-size: 0.9rem;
802
+ color: var(--text-secondary);
803
+ margin: 0;
804
+ line-height: 1.6;
805
+ }
806
+
807
+ .cta-box {
808
+ background: var(--bg-secondary);
809
+ padding: 2.5rem;
810
+ border-radius: 8px;
811
+ text-align: center;
812
+ margin-bottom: 3rem;
813
+ }
814
+
815
+ .cta-box h3 {
816
+ margin: 0 0 1rem;
817
+ font-size: 1.5rem;
818
+ }
819
+
820
+ .cta-box p {
821
+ color: var(--text-secondary);
822
+ margin: 0 0 2rem;
823
+ }
824
+
825
+ .cta-buttons {
826
+ display: flex;
827
+ gap: 1rem;
828
+ justify-content: center;
829
+ }
830
+
831
+ .btn {
832
+ display: inline-block;
833
+ padding: 0.875rem 2rem;
834
+ border-radius: 6px;
835
+ font-weight: 600;
836
+ text-decoration: none;
837
+ transition: all 0.2s;
838
+ }
839
+
840
+ .btn.primary {
841
+ background: var(--accent);
842
+ color: white;
843
+ }
844
+
845
+ .btn.primary:hover {
846
+ background: var(--accent-hover);
847
+ }
848
+
849
+ .btn.secondary {
850
+ background: transparent;
851
+ color: var(--text-secondary);
852
+ border: 1px solid var(--border);
853
+ }
854
+
855
+ .btn.secondary:hover {
856
+ border-color: var(--text-secondary);
857
+ }
858
+
859
+ .share-section h4 {
860
+ margin: 0 0 1rem;
861
+ font-size: 1.1rem;
862
+ }
863
+
864
+ .share-buttons {
865
+ display: flex;
866
+ gap: 1rem;
867
+ flex-wrap: wrap;
868
+ }
869
+
870
+ .share-btn {
871
+ display: inline-flex;
872
+ align-items: center;
873
+ gap: 0.5rem;
874
+ padding: 0.625rem 1.25rem;
875
+ border-radius: 6px;
876
+ text-decoration: none;
877
+ font-weight: 500;
878
+ font-size: 0.9rem;
879
+ transition: all 0.2s;
880
+ }
881
+
882
+ .share-btn.twitter {
883
+ background: #1DA1F2;
884
+ color: white;
885
+ }
886
+
887
+ .share-btn.linkedin {
888
+ background: #0077B5;
889
+ color: white;
890
+ }
891
+
892
+ .share-btn.hn {
893
+ background: #FF6600;
894
+ color: white;
895
+ }
896
+
897
+ .share-btn:hover {
898
+ opacity: 0.9;
899
+ transform: translateY(-2px);
900
+ }
901
+
902
+ .share-btn svg {
903
+ flex-shrink: 0;
904
+ }
905
+
906
+ .share-count {
907
+ background: rgba(255, 255, 255, 0.2);
908
+ padding: 0.125rem 0.5rem;
909
+ border-radius: 12px;
910
+ font-size: 0.75rem;
911
+ font-weight: 600;
912
+ margin-left: 0.25rem;
913
+ }
914
+
915
+ .share-note {
916
+ margin-top: 1rem;
917
+ font-size: 0.875rem;
918
+ color: var(--text-muted);
919
+ text-align: center;
920
+ }
921
+
922
+ .site-footer {
923
+ border-top: 1px solid var(--border);
924
+ padding: 3rem 0;
925
+ margin-top: 4rem;
926
+ }
927
+
928
+ .footer-content {
929
+ display: flex;
930
+ justify-content: space-between;
931
+ align-items: center;
932
+ }
933
+
934
+ .footer-left p {
935
+ margin: 0;
936
+ color: var(--text-secondary);
937
+ }
938
+
939
+ .copyright {
940
+ font-size: 0.875rem;
941
+ margin-top: 0.5rem !important;
942
+ }
943
+
944
+ .footer-right {
945
+ display: flex;
946
+ gap: 2rem;
947
+ }
948
+
949
+ .footer-right a {
950
+ color: var(--text-secondary);
951
+ text-decoration: none;
952
+ transition: color 0.2s;
953
+ }
954
+
955
+ .footer-right a:hover {
956
+ color: var(--accent);
957
+ }
958
+
959
+ @media (max-width: 1200px) {
960
+ .post-layout {
961
+ grid-template-columns: 1fr;
962
+ }
963
+
964
+ .post-sidebar {
965
+ display: none;
966
+ }
967
+ }
968
+
969
+ @media (max-width: 768px) {
970
+ h1 {
971
+ font-size: 2rem;
972
+ }
973
+
974
+ .content {
975
+ font-size: 1rem;
976
+ }
977
+
978
+ .cta-buttons {
979
+ flex-direction: column;
980
+ }
981
+
982
+ .footer-content {
983
+ flex-direction: column;
984
+ gap: 2rem;
985
+ text-align: center;
986
+ }
987
+ }
988
+ </style>