From 8fe2e55d813ca87a7665004d03f330c30d77bf8c Mon Sep 17 00:00:00 2001 From: koide Date: Sat, 28 Feb 2026 02:16:29 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=88=E3=83=83=E3=83=97=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=AE=E6=9C=80=E6=96=B0=E8=A8=98=E4=BA=8B?= =?UTF-8?q?=E3=82=92=E8=87=AA=E5=8B=95=E7=94=9F=E6=88=90=E3=81=AB=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 3 ++ scripts/generate-recent-posts.js | 77 ++++++++++++++++++++++++++++++++ src/data/recent-posts.json | 66 +++++++++++++++++++++++++++ src/pages/index.tsx | 50 +++++++-------------- 4 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 scripts/generate-recent-posts.js create mode 100644 src/data/recent-posts.json diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 88f67d1..d228445 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -22,6 +22,9 @@ jobs: - name: Install npm dependencies run: npm ci + - name: Generate recent posts + run: node scripts/generate-recent-posts.js + - name: Build static site run: npm run build diff --git a/scripts/generate-recent-posts.js b/scripts/generate-recent-posts.js new file mode 100644 index 0000000..540620c --- /dev/null +++ b/scripts/generate-recent-posts.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..'); + +function parseFrontmatter(content) { + const m = content.match(/^---\n([\s\S]*?)\n---/); + if (!m) return {}; + const fm = {}; + for (const line of m[1].split('\n')) { + const idx = line.indexOf(':'); + if (idx === -1) continue; + const key = line.slice(0, idx).trim(); + let val = line.slice(idx + 1).trim(); + // Remove quotes + if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) + val = val.slice(1, -1); + fm[key] = val; + } + return fm; +} + +function guessTag(folderName) { + if (folderName.startsWith('dgx-spark')) return 'DGX Spark'; + if (folderName.startsWith('gitea')) return 'Gitea'; + if (folderName.startsWith('searxng')) return 'SearXNG'; + if (folderName.startsWith('ollama')) return 'Ollama'; + if (folderName.startsWith('browser')) return 'Browser'; + if (folderName.startsWith('game')) return 'Game'; + return 'Tech'; +} + +function getTag(fm, folderName, defaultTag) { + if (fm.tag) return fm.tag; + if (fm.tags) return fm.tags.replace(/[\[\]]/g, '').split(',')[0].trim(); + if (defaultTag === 'blog') return 'AI'; + return guessTag(folderName); +} + +function scanDir(dir, urlPrefix, isBlog) { + if (!fs.existsSync(dir)) return []; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + const posts = []; + for (const e of entries) { + if (!e.isDirectory()) continue; + const indexPath = path.join(dir, e.name, 'index.md'); + if (!fs.existsSync(indexPath)) continue; + const content = fs.readFileSync(indexPath, 'utf8'); + const fm = parseFrontmatter(content); + const title = fm.title || e.name; + const tag = getTag(fm, e.name, isBlog ? 'blog' : 'tech'); + + // Extract date + let date = null; + const dateMatch = e.name.match(/^(\d{4}-\d{2}-\d{2})/); + if (dateMatch) { + date = dateMatch[1]; + } else if (fm.date) { + date = fm.date.slice(0, 10); + } else { + // Use file mtime + const stat = fs.statSync(indexPath); + date = stat.mtime.toISOString().slice(0, 10); + } + + posts.push({ title, date, tag, url: `${urlPrefix}${e.name}` }); + } + posts.sort((a, b) => b.date.localeCompare(a.date)); + return posts.slice(0, 5); +} + +const tech = scanDir(path.join(ROOT, 'docs-tech'), '/tech/', false); +const blog = scanDir(path.join(ROOT, 'docs'), '/blog/', true); + +const outPath = path.join(ROOT, 'src', 'data', 'recent-posts.json'); +fs.writeFileSync(outPath, JSON.stringify({ tech, blog }, null, 2)); +console.log(`Generated ${outPath} (tech: ${tech.length}, blog: ${blog.length})`); diff --git a/src/data/recent-posts.json b/src/data/recent-posts.json new file mode 100644 index 0000000..46fd176 --- /dev/null +++ b/src/data/recent-posts.json @@ -0,0 +1,66 @@ +{ + "tech": [ + { + "title": "ローカルサーバーでマイク・カメラを使う方法", + "date": "2026-02-28", + "tag": "Browser", + "url": "/tech/browser-secure-context" + }, + { + "title": "DGX SparkにAnythingLLMを導入してローカルLLMエージェントを構築", + "date": "2026-02-28", + "tag": "DGX Spark", + "url": "/tech/dgx-spark-anythingllm" + }, + { + "title": "DGX SparkでClaude Code + Qwen3-Coder-Nextをローカル実行する", + "date": "2026-02-28", + "tag": "DGX Spark", + "url": "/tech/dgx-spark-claude-code-local" + }, + { + "title": "ローカルClaude Code + Playwright CLIでブラウザ自動化エージェントを作る", + "date": "2026-02-28", + "tag": "DGX Spark", + "url": "/tech/dgx-spark-claude-code-playwright" + }, + { + "title": "DGX Spark デュアル構成ガイド", + "date": "2026-02-28", + "tag": "DGX Spark", + "url": "/tech/dgx-spark-dual" + } + ], + "blog": [ + { + "title": "02/28 AIヘッドライン(朝刊)", + "date": "2026-02-28", + "tag": "AI", + "url": "/blog/2026-02-28-morning-headline" + }, + { + "title": "Ollama がローカルAIのハブとしてめちゃ最強な件", + "date": "2026-02-28", + "tag": "AI", + "url": "/blog/ollama-local-ai-hub" + }, + { + "title": "02/27 AIヘッドライン(夕刊)", + "date": "2026-02-27", + "tag": "AI", + "url": "/blog/2026-02-27-evening-headline" + }, + { + "title": "02/27 AIヘッドライン(朝刊)", + "date": "2026-02-27", + "tag": "AI", + "url": "/blog/2026-02-27-morning-headline" + }, + { + "title": "02/26 AIヘッドライン(夕刊)", + "date": "2026-02-26", + "tag": "AI", + "url": "/blog/2026-02-26-evening-headline" + } + ] +} \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 3d44d82..a1d65e9 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; import styles from './index.module.css'; +import recentPosts from '../data/recent-posts.json'; function HomepageHeader() { return ( @@ -25,39 +26,14 @@ function HomepageHeader() { } function RecentTech() { - const posts = [ - { - title: "ローカルGitea × Webhook連携でAI自動コードレビュー", - date: "2026-02-26", - tag: "Gitea", - url: "/tech/gitea-webhook-ai-review", - }, - { - title: "DGX SparkにAnythingLLMでローカルLLMエージェント構築", - date: "2026-02-20", - tag: "DGX Spark", - url: "/tech/dgx-spark-anythingllm", - }, - { - title: 'DGX Spark デュアル構成ガイド', - date: '2026-02-19', - tag: 'DGX Spark', - url: '/tech/dgx-spark-dual', - }, - { - title: 'DGX SparkでMiniMax-M2.5-REAP-172Bを動かす', - date: '2026-02-18', - tag: 'DGX Spark', - url: '/tech/dgx-spark-minimax', - }, - ]; + const posts = recentPosts.tech; return (

// Tech

技術記事・検証レポート

- {posts.map((post, idx) => ( + {posts.length > 0 ? posts.map((post, idx) => (

{post.title}

@@ -65,7 +41,7 @@ function RecentTech() { {post.tag}
- ))} + )) :

記事がまだありません。

}
すべて見る → @@ -75,18 +51,22 @@ function RecentTech() { } function RecentBlog() { + const posts = recentPosts.blog; + return (

// Blog

日常の思考・情報収集

- -

02/19 AIヘッドライン

-
- 2026-02-19 - AI -
- + {posts.length > 0 ? posts.map((post, idx) => ( + +

{post.title}

+
+ {post.date} + {post.tag} +
+ + )) :

記事がまだありません。

}
すべて見る →