GitHubTrends — тренды GitHub
Настройка
В навыке 11 файлов. Их можно скопировать или скачать по отдельности, сохранив указанные имена и папки.
SKILL.md
---
name: GitHubTrends
description: Показывает тренды популярных проектов GitHub и создаёт визуальную панель. ИСПОЛЬЗУЙТЕ, КОГДА нужны тренды GitHub, набирающие популярность проекты, горячие репозитории, популярные проекты GitHub, создание панели или веб-страницы.
version: 2.0.0
---
## Настройка
**Перед выполнением проверьте наличие пользовательских настроек по адресу:**
`~/.claude/skills/CORE/USER/SKILLCUSTOMIZATIONS/GitHubTrends/`
Если этот каталог существует, загрузите и примените все найденные там PREFERENCES.md, конфигурации или ресурсы. Они переопределяют поведение по умолчанию. Если каталога нет, продолжайте со стандартными настройками навыка.
# GitHubTrends — тренды популярных проектов GitHub
**Быстро находите самые популярные проекты с открытым исходным кодом на GitHub.**
---
## Философия
GitHub trending — лучший способ находить качественные проекты с открытым исходным кодом. Этот навык позволяет мне, Лао Вану, быстро получать список самых популярных сейчас проектов, фильтровать их по периоду (день/неделя) и языку программирования, чтобы находить проекты, которые стоит изучать и в которые стоит вносить вклад.
---
## Быстрый старт
```bash
# 查看本周最热门的项目(默认)
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly
# 查看今日最热门的项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts daily
# 按语言筛选
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=TypeScript
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=Python
# 指定显示数量
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --limit=20
```
---
## Когда использовать этот навык
**Основные условия срабатывания — используйте этот навык, когда пользователь говорит:**
### Прямые запросы
- «покажи тренды GitHub» или «популярное на GitHub»
- «покажи популярные проекты» или «посмотрим, какие проекты сейчас популярны»
- «что сейчас популярно на GitHub» или «горячие проекты GitHub»
- «популярные проекты этой недели» или «популярное за неделю»
- «популярные проекты сегодня» или «популярное за день»
### Запросы на поиск нового
- «найди популярные проекты» или «открой популярные проекты»
- «покажи набирающие популярность репозитории» или «покажи трендовые репозитории»
- «что самое горячее на GitHub» или «что сейчас в топе GitHub»
- «найди хорошие проекты для просмотра» или «найди хорошие проекты»
### Для конкретного языка
- «популярные проекты на TypeScript» или «горячие проекты TypeScript»
- «популярное на Python» или «популярные проекты Python»
- «покажи набирающие популярность проекты Rust» или «покажи популярные проекты Rust»
- «популярные проекты на языке Go» или «трендовые проекты Go»
### Панель и визуализация
- «создай панель GitHub trending» или «создай панель трендов»
- «создай веб-страницу трендов» или «создай страницу с популярными проектами»
- «создай интерактивный отчёт» или «сформируй интерактивный отчёт»
- «экспортируй панель трендов» или «экспортируй панель»
- «визуализируй тренды GitHub» или «покажи тренды GitHub наглядно»
---
## Основные возможности
### Получение списка трендов
- **Тренды за день** — самые популярные проекты за последние 24 часа
- **Тренды за неделю** — самые популярные проекты за последние 7 дней (по умолчанию)
- **Фильтрация по языку** — фильтр по языку программирования (TypeScript, Python, Go, Rust и т. д.)
- **Настраиваемое количество** — указание числа возвращаемых проектов (по умолчанию 10)
### Создание визуальной панели 🆕
- **Интерактивный HTML** — создание интерактивной веб-панели
- **Визуализация данных** — круговая диаграмма распределения языков, столбчатая диаграмма прироста Stars
- **Технологические новости** — интеграция технологических новостей Hacker News
- **Фильтрация в реальном времени** — фильтрация по языку, сортировка, поиск
- **Адаптивный дизайн** — поддержка компьютеров, планшетов и телефонов
### Информация о проекте
- Название и описание проекта
- Количество Star и изменение
- Язык программирования
- URL проекта
---
## Использование инструментов
### GetTrending.ts
**Расположение:** `Tools/GetTrending.ts`
**Функция:** получение списка популярных проектов с GitHub
**Параметры:**
- `period` — период: `daily` или `weekly` (по умолчанию: weekly)
- `--language` — фильтр по языку программирования (необязательно)
- `--limit` — число возвращаемых проектов (по умолчанию: 10)
**Примеры использования:**
```bash
# 基本用法
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly
# 带参数
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=TypeScript --limit=15
# 简写
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts daily -l=Python
```
**Способ реализации:**
Используется официальная страница GitHub trending: https://github.com/trending
Содержимое страницы считывается через fetch API и разбирается
---
### GenerateDashboard.ts 🆕
**Расположение:** `Tools/GenerateDashboard.ts`
**Функция:** создание HTML-файла интерактивной панели визуализации данных
**Параметры:**
- `--period` — период: `daily` или `weekly` (по умолчанию: weekly)
- `--language` — фильтр по языку программирования (необязательно)
- `--limit` — число возвращаемых проектов (по умолчанию: 10)
- `--include-news` — включить технологические новости
- `--news-count` — число новостей (по умолчанию: 10)
- `--output` — путь выходного файла (по умолчанию: ./github-trends.html)
**Примеры использования:**
```bash
# 基本用法 - 生成本周仪表板
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts
# 包含技术新闻
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts --include-news
# TypeScript 项目每日仪表板
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts \
--period daily \
--language TypeScript \
--limit 20 \
--include-news \
--output ~/ts-daily.html
```
**Способ реализации:**
- Получение данных о популярных проектах GitHub trending
- Получение технологических новостей Hacker News
- Рендеринг HTML с помощью шаблонизатора Handlebars
- Интеграция Tailwind CSS и Chart.js
- Создание полностью самостоятельного HTML-файла (зависимости загружаются через CDN)
---
## Формат вывода
```markdown
# Популярные проекты GitHub — за неделю (2025-01-19)
## 1. vercel/next.js - ⭐ 125,342 (+1,234 за эту неделю)
**Язык:** TypeScript
**Описание:** React-фреймворк для веба
**URL:** https://github.com/vercel/next.js
## 2. microsoft/vscode - ⭐ 160,890 (+987 за эту неделю)
**Язык:** TypeScript
**Описание:** Visual Studio Code
**URL:** https://github.com/microsoft/vscode
...
---
📊 Всего: 10 проектов | Язык: все | Период: неделя
```
---
## Поддерживаемые языки
Часто используемые языки программирования для фильтрации:
- **TypeScript** — проекты TypeScript
- **JavaScript** — проекты JavaScript
- **Python** — проекты Python
- **Go** — проекты на языке Go
- **Rust** — проекты Rust
- **Java** — проекты Java
- **C++** — проекты C++
- **Ruby** — проекты Ruby
- **Swift** — проекты Swift
- **Kotlin** — проекты Kotlin
---
## Интеграция рабочих процессов
Этот навык может вызываться другими навыками:
- **OSINT** — поиск популярных инструментов при исследовании технологического стека
- **Research** — исследование трендов экосистемы конкретного языка
- **System** — поиск полезных проектов, связанных с PAI
---
## Технические примечания
**Источник данных:** официальная страница GitHub trending
**Частота обновления:** раз в час
**Аутентификация не требуется:** используется общедоступная страница, GitHub API token не нужен
**Способ разбора:** извлечение информации о проектах посредством разбора HTML
**Обработка ошибок:**
- При сетевых ошибках отображается понятное сообщение
- При неудачном разборе возвращается исходный HTML для отладки
- Поддерживаемые параметры языка нечувствительны к регистру
---
## Будущие улучшения
Возможные будущие функции:
- Поддержка месячных трендов (если GitHub их предоставляет)
- Фильтрация по диапазону stars (1k+, 10k+, 100k+)
- Сохранение исторических данных для анализа трендов
- Интеграция в автоматизированные рабочие процессы других навыков
---
## Голосовое уведомление
**При выполнении рабочего процесса сделайте ОБА действия:**
1. **Отправьте голосовое уведомление:**
```bash
curl -s -X POST http://localhost:8888/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the GitHubTrends workflow"}' \
> /dev/null 2>&1 &
```
2. **Выведите текстовое уведомление:**
```
Выполняется рабочий процесс **GitHubTrends**...
```
**Полная документация:** `~/.claude/skills/CORE/SkillNotifications.md`
README.md
# Навык GitHubTrends
**Быстро находите самые популярные проекты с открытым исходным кодом на GitHub и создавайте визуальные панели!**
## Возможности
### Основные функции
- ✅ Получение списков популярных проектов за день/неделю
- ✅ Фильтрация по языку программирования (TypeScript, Python, Go, Rust и т. д.)
- ✅ Настройка числа возвращаемых проектов
- ✅ Отображение общего количества Star и прироста за период
- ✅ GitHub API token не требуется
### Визуальная панель 🆕
- ✨ **Интерактивный HTML** — создание интерактивной веб-панели
- 📊 **Визуализация данных** — круговая диаграмма распределения языков, столбчатая диаграмма прироста Stars
- 📰 **Технологические новости** — интеграция последних новостей Hacker News
- 🔍 **Фильтрация в реальном времени** — фильтрация по языку, сортировка, поиск
- 📱 **Адаптивный дизайн** — поддержка компьютеров, планшетов и телефонов
- 🎨 **Красивый интерфейс** — Tailwind CSS + стиль GitHub
## Быстрый старт
### Просмотр популярных проектов этой недели (по умолчанию)
```bash
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly
```
### Просмотр популярных проектов сегодня
```bash
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts daily
```
### Фильтрация по языку
```bash
# TypeScript热门项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=TypeScript
# Python热门项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=Python
# Go热门项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly -l=Go
```
### Указание числа результатов
```bash
# 返回20个项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --limit=20
# 组合使用:返回15个TypeScript项目
bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=TypeScript --limit=15
```
---
## Создание визуальной панели 🆕
### Основное использование
```bash
# 生成本周趋势仪表板(默认)
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts
```
### Включение технологических новостей
```bash
# 生成包含 Hacker News 的仪表板
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts --include-news
```
### Расширенные параметры
```bash
# 生成 TypeScript 项目每日仪表板,包含 15 条新闻
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts \
--period daily \
--language TypeScript \
--limit 20 \
--include-news \
--news-count 15 \
--output ~/Downloads/ts-daily-trends.html
```
### Функции панели
Созданный HTML-файл содержит:
- **Обзор статистики** — общее число проектов, общее число stars, лучшие проекты
- **Диаграмма распределения языков** — круговая диаграмма показывает долю каждого языка
- **Диаграмма прироста Stars** — столбчатая диаграмма показывает тенденции роста
- **Карточки проектов** — красивое карточное представление проектов
- **Технологические новости** — последние новости Hacker News
- **Интерактивные функции** — фильтрация, сортировка, поиск
- **Адаптивность** — автоматическая подстройка под разные размеры экрана
---
## Пример вывода
```markdown
# Популярные проекты GitHub — за неделю (2026-01-19)
📊 **Всего:** 10 проектов | **Язык:** все | **Период:** неделя
---
## 1. vercel/next.js - ⭐ 125,342 (+1,234 за эту неделю)
**Язык:** TypeScript
**Описание:** React-фреймворк для веба
**URL:** https://github.com/vercel/next.js
## 2. microsoft/vscode - ⭐ 160,890 (+987 за эту неделю)
**Язык:** TypeScript
**Описание:** Visual Studio Code
**URL:** https://github.com/microsoft/vscode
...
```
## Описание параметров
| Параметр | Описание | Значение по умолчанию | Допустимые значения |
|------|------|--------|--------|
| `period` | Период | `weekly` | `daily`, `weekly` |
| `--language` | Фильтр по языку программирования | Все | TypeScript, Python, Go, Rust, Java и т. д. |
| `--limit` | Число возвращаемых проектов | 10 | Любое положительное целое число |
## Поддерживаемые языки
Все распространённые языки программирования можно использовать как условия фильтрации:
- **TypeScript** — проекты TypeScript
- **JavaScript** — проекты JavaScript
- **Python** — проекты Python
- **Go** — проекты на языке Go
- **Rust** — проекты Rust
- **Java** — проекты Java
- **C++** — проекты C++
- **Ruby** — проекты Ruby
- **Swift** — проекты Swift
- **Kotlin** — проекты Kotlin
## Фразы, активирующие навык
Этот навык активируется, когда вы говорите что-либо из следующего:
- «покажи тренды GitHub» / «популярное на GitHub»
- «покажи популярные проекты» / «посмотрим, какие проекты сейчас популярны»
- «популярное за неделю» / «популярные проекты этой недели»
- «популярное за день» / «популярные проекты сегодня»
- «популярное на TypeScript» / «популярное на Python»
- «что сейчас в топе GitHub» / «что самое горячее на GitHub»
## Техническая реализация
- **Источник данных**: официальная страница GitHub trending (https://github.com/trending)
- **Способ разбора**: извлечение информации о проектах посредством разбора HTML
- **Аутентификация**: GitHub API token не требуется
- **Частота обновления**: раз в час
## Структура каталогов
```
~/.claude/skills/GitHubTrends/
├── SKILL.md # Основной файл навыка
├── README.md # Документация по использованию (этот файл)
├── Tools/
│ └── GetTrending.ts # Инструмент получения данных о трендах
└── Workflows/
└── GetTrending.md # Документация рабочего процесса
```
## Важные замечания
1. **Сетевые требования**: необходим доступ к официальному сайту GitHub
2. **Частота обновления**: данные обновляются каждый час, а не в реальном времени
3. **Точность разбора**: изменения структуры страницы GitHub могут повлиять на разбор; при проблемах проверьте `/tmp/github-trending-debug-*.html`
4. **Параметры языка**: регистр не учитывается, `--language=typescript` и `--language=TypeScript` дают одинаковый результат
## Известные проблемы
- HTML-структура страницы GitHub trending сложная; URL и имена некоторых проектов могут разбираться неполностью
- Если структура страницы GitHub изменится, логика разбора в инструменте может потребовать обновления
## Будущие улучшения
- [ ] Поддержка сохранения исторических данных для анализа трендов
- [ ] Фильтрация по диапазону stars (1k+, 10k+, 100k+)
- [ ] Более умный разбор HTML (с библиотекой разбора HTML вместо регулярных выражений)
- [ ] Интеграция в автоматизированные рабочие процессы других навыков
## Вклад в проект
Если вы обнаружили проблему или у вас есть предложение по улучшению, сообщайте!
---
**Создано с ❤️ автором 老王**
Tools/GetTrending.ts
#!/usr/bin/env bun
/**
* GitHub Trending Projects Fetcher
*
* 从GitHub获取trending项目列表
* 支持每日/每周趋势,按语言筛选
*/
import { $ } from "bun";
interface TrendingProject {
rank: number;
name: string;
description: string;
language: string;
stars: string;
starsThisPeriod: string;
url: string;
}
interface TrendingOptions {
period: "daily" | "weekly";
language?: string;
limit: number;
}
function buildTrendingUrl(options: TrendingOptions): string {
const baseUrl = "https://github.com/trending";
const since = options.period === "daily" ? "daily" : "weekly";
let url = `${baseUrl}?since=${since}`;
if (options.language) {
url += `&language=${encodeURIComponent(options.language.toLowerCase())}`;
}
return url;
}
function parseTrendingProjects(html: string, limit: number): TrendingProject[] {
const projects: TrendingProject[] = [];
try {
const articleRegex = /<article[^>]*>([\s\S]*?)<\/article>/g;
const articles = html.match(articleRegex) || [];
const articlesToProcess = articles.slice(0, limit);
articlesToProcess.forEach((article, index) => {
try {
const headingMatch = article.match(/<h[12][^>]*>([\s\S]*?)<\/h[12]>/);
let repoName: string | null = null;
if (headingMatch) {
const headingContent = headingMatch[1];
const validLinkMatch = headingContent.match(
/<a[^>]*href="\/([^\/"\/]+\/[^\/"\/]+)"[^>]*>(?![^<]*login)/
);
if (validLinkMatch) {
repoName = validLinkMatch[1];
}
}
if (!repoName) {
const repoMatch = article.match(
/<a[^>]*href="\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)"[^>]*>(?!.*(?:login|stargazers|forks|issues))/
);
repoName = repoMatch ? repoMatch[1] : null;
}
const descMatch = article.match(/<p[^>]*class="[^"]*col-9[^"]*"[^>]*>([\s\S]*?)<\/p>/);
const description = descMatch
? descMatch[1]
.replace(/<[^>]+>/g, "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.trim()
.substring(0, 200)
: "No description";
const langMatch = article.match(/<span[^>]*itemprop="programmingLanguage"[^>]*>([^<]+)<\/span>/);
const language = langMatch ? langMatch[1].trim() : "Unknown";
const starsMatch = article.match(/<a[^>]*href="\/[^"]+\/stargazers"[^>]*>(\d[\d,]*)\s*stars?/);
const totalStars = starsMatch ? starsMatch[1] : "0";
const starsAddedMatch = article.match(/(\d[\d,]*)\s*stars?\s*(?:today|this week)/i);
const starsAdded = starsAddedMatch ? `+${starsAddedMatch[1]}` : "";
if (repoName && !repoName.includes("login") && !repoName.includes("return_to")) {
projects.push({
rank: index + 1,
name: repoName,
description,
language,
stars: totalStars,
starsThisPeriod: starsAdded,
url: `https://github.com/${repoName}`,
});
}
} catch (error) {
console.error(`解析第${index + 1}个项目失败:`, error);
}
});
} catch (error) {
console.error("解析trending项目失败:", error);
}
return projects;
}
function formatProjects(projects: TrendingProject[], options: TrendingOptions): string {
if (projects.length === 0) {
return "# GitHub Trending - No Projects Found\n\n没有找到trending项目,可能是网络问题或页面结构变化。";
}
const periodLabel = options.period === "daily" ? "Daily" : "Weekly";
const languageLabel = options.language ? `Language: ${options.language}` : "Language: All";
const today = new Date().toISOString().split("T")[0];
let output = `# GitHub Trending Projects - ${periodLabel} (${today})\n\n`;
output += `📊 **Total:** ${projects.length} projects | **${languageLabel}** | **Period:** ${periodLabel}\n\n`;
output += `---\n\n`;
projects.forEach((project) => {
output += `## ${project.rank}. ${project.name} - ⭐ ${project.stars}`;
if (project.starsThisPeriod) {
output += ` (${project.starsThisPeriod} this ${options.period})`;
}
output += `\n`;
output += `**Language:** ${project.language}\n`;
output += `**Description:** ${project.description}\n`;
output += `**URL:** ${project.url}\n\n`;
});
output += `---\n`;
output += `📊 Data from: https://github.com/trending\n`;
return output;
}
async function main() {
const args = process.argv.slice(2);
let period: "daily" | "weekly" = "weekly";
let language: string | undefined;
let limit = 10;
for (const arg of args) {
if (arg === "daily" || arg === "weekly") {
period = arg;
} else if (arg.startsWith("--language=")) {
language = arg.split("=")[1];
} else if (arg.startsWith("-l=")) {
language = arg.split("=")[1];
} else if (arg.startsWith("--limit=")) {
limit = parseInt(arg.split("=")[1]) || 10;
}
}
const options: TrendingOptions = { period, language, limit };
try {
const url = buildTrendingUrl(options);
console.error(`正在获取 GitHub trending 数据: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const html = await response.text();
const projects = parseTrendingProjects(html, limit);
const formatted = formatProjects(projects, options);
console.log(formatted);
if (projects.length === 0) {
const debugFile = `/tmp/github-trending-debug-${Date.now()}.html`;
await Bun.write(debugFile, html);
console.error(`\n调试: 原始HTML已保存到 ${debugFile}`);
}
} catch (error) {
console.error("❌ 获取trending数据失败:");
console.error(error);
process.exit(1);
}
}
main();
Workflows/GetTrending.md
# Рабочий процесс GetTrending Рабочий процесс получения списка проектов GitHub trending. ## Описание Этот рабочий процесс использует инструмент GetTrending.ts для получения с GitHub списка самых популярных сейчас проектов и поддерживает фильтрацию по периоду (день/неделя) и языку программирования. ## Когда использовать Используйте этот рабочий процесс, когда пользователь запрашивает что-либо из следующего: - «покажи тренды GitHub» / «популярное на GitHub» - «покажи популярные проекты» / «посмотрим, какие проекты сейчас популярны» - «популярное за неделю» / «популярные проекты этой недели» - «популярное за день» / «популярные проекты сегодня» - «популярное на TypeScript» / «популярное на Python» / фильтрация по языку - «что сейчас в топе GitHub» / «что самое горячее на GitHub» ## Шаги рабочего процесса ### Шаг 1: определение параметров Подтвердите у пользователя или выведите следующие параметры: - **Период**: daily (день) или weekly (неделя, по умолчанию) - **Язык программирования**: необязательно (например, TypeScript, Python, Go, Rust и т. д.) - **Число проектов**: по умолчанию 10 ### Шаг 2: выполнение инструмента Запустите инструмент GetTrending.ts: ```bash # 基本用法(本周,全部语言,10个项目) bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly # 指定语言 bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --language=TypeScript # 指定数量 bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts weekly --limit=20 # 组合参数 bun ~/.claude/skills/GitHubTrends/Tools/GetTrending.ts daily --language=Python --limit=15 ``` ### Шаг 3: показ результатов Инструмент автоматически форматирует вывод, включая: - Место проекта в рейтинге - Название проекта - Общее число Star и прирост за период - Язык программирования - Описание проекта - URL GitHub ### Шаг 4: дальнейшие действия (необязательно) В зависимости от потребностей пользователя можно: - Открыть страницу определённого проекта - Использовать другие навыки для дальнейшего анализа проекта - Сохранить результаты в файл для последующего обращения ## Интеграция с другими навыками - **OSINT**: поиск популярных инструментов при исследовании технологического стека - **Research**: исследование трендов экосистемы конкретного языка - **Browser**: открытие страницы проекта для подробного анализа ## Примечания - Данные обновляются раз в час - GitHub API token не требуется - Используется общедоступная страница GitHub trending - Поддерживаемые параметры языка нечувствительны к регистру
Tools/GenerateDashboard.ts
#!/usr/bin/env bun
/**
* GitHub Trending Dashboard Generator
*
* 生成交互式数据可视化仪表板
*
* 使用方式:
* ./GenerateDashboard.ts [options]
*
* 选项:
* --period - daily | weekly (默认: weekly)
* --language - 编程语言筛选 (可选)
* --limit - 项目数量 (默认: 10)
* --include-news - 包含技术新闻
* --news-count - 新闻数量 (默认: 10)
* --theme - light | dark | auto (默认: auto)
* --output - 输出文件路径 (默认: ./github-trends.html)
*
* 示例:
* ./GenerateDashboard.ts
* ./GenerateDashboard.ts --period daily --language TypeScript --include-news
* ./GenerateDashboard.ts --limit 20 --output ~/trends.html
*/
import Handlebars from 'handlebars';
import type { DashboardOptions, TrendingProject, TechNewsItem, TemplateData } from './Lib/types';
import { registerHelpers, renderTemplate } from './Lib/template-helpers';
import { analyzeData } from './Lib/visualization-helpers';
// 注册 Handlebars 辅助函数
registerHelpers();
/**
* 构建 GitHub trending URL
*/
function buildTrendingUrl(options: DashboardOptions): string {
const baseUrl = "https://github.com/trending";
const since = options.period === "daily" ? "daily" : "weekly";
let url = `${baseUrl}?since=${since}`;
if (options.language) {
url += `&language=${encodeURIComponent(options.language.toLowerCase())}`;
}
return url;
}
/**
* 解析 HTML 提取 trending 项目
* (从 GetTrending.ts 复制的逻辑)
*/
async function getTrendingProjects(options: DashboardOptions): Promise<TrendingProject[]> {
const url = buildTrendingUrl(options);
console.error(`正在获取 GitHub trending 数据: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const html = await response.text();
return parseTrendingProjects(html, options.limit);
}
/**
* 解析 HTML
*/
function parseTrendingProjects(html: string, limit: number): TrendingProject[] {
const projects: TrendingProject[] = [];
try {
const articleRegex = /<article[^>]*>([\s\S]*?)<\/article>/g;
const articles = html.match(articleRegex) || [];
const articlesToProcess = articles.slice(0, limit);
articlesToProcess.forEach((article, index) => {
try {
const headingMatch = article.match(/<h[12][^>]*>([\s\S]*?)<\/h[12]>/);
let repoName: string | null = null;
if (headingMatch) {
const headingContent = headingMatch[1];
const validLinkMatch = headingContent.match(
/<a[^>]*href="\/([^\/"\/]+\/[^\/"\/]+)"[^>]*>(?![^<]*login)/
);
if (validLinkMatch) {
repoName = validLinkMatch[1];
}
}
if (!repoName) {
const repoMatch = article.match(
/<a[^>]*href="\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)"[^>]*>(?!.*(?:login|stargazers|forks|issues))/
);
repoName = repoMatch ? repoMatch[1] : null;
}
const descMatch = article.match(/<p[^>]*class="[^"]*col-9[^"]*"[^>]*>([\s\S]*?)<\/p>/);
const description = descMatch
? descMatch[1]
.replace(/<[^>]+>/g, "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.trim()
.substring(0, 200)
: "No description";
const langMatch = article.match(/<span[^>]*itemprop="programmingLanguage"[^>]*>([^<]+)<\/span>/);
const language = langMatch ? langMatch[1].trim() : "Unknown";
// 提取stars总数 - GitHub 改了 HTML 结构,数字在 SVG 后面
const starsMatch = article.match(/stargazers[^>]*>[\s\S]*?<\/svg>\s*([\d,]+)/);
const totalStars = starsMatch ? starsMatch[1] : "0";
// 尝试提取新增stars - 格式:XXX stars today/this week
const starsAddedMatch = article.match(/(\d[\d,]*)\s+stars?\s+(?:today|this week)/);
const starsAdded = starsAddedMatch ? `+${starsAddedMatch[1]}` : "";
if (repoName && !repoName.includes("login") && !repoName.includes("return_to")) {
projects.push({
rank: index + 1,
name: repoName,
description,
language,
stars: totalStars,
starsThisPeriod: starsAdded,
url: `https://github.com/${repoName}`,
});
}
} catch (error) {
console.error(`解析第${index + 1}个项目失败:`, error);
}
});
} catch (error) {
console.error("解析trending项目失败:", error);
}
return projects;
}
/**
* 获取技术新闻
*/
async function getTechNews(count: number): Promise<TechNewsItem[]> {
const HN_API = 'https://hn.algolia.com/api/v1/search_by_date';
try {
const response = await fetch(`${HN_API}?tags=story&hitsPerPage=${count}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data.hits.slice(0, count).map((hit: any) => ({
id: hit.objectID,
title: hit.title,
url: hit.url || `https://news.ycombinator.com/item?id=${hit.objectID}`,
source: 'hackernews',
points: hit.points || 0,
comments: hit.num_comments || 0,
timestamp: new Date(hit.created_at).toISOString(),
tags: hit._tags || []
}));
} catch (error) {
console.error('获取 Hacker News 失败:', error);
return [];
}
}
/**
* 生成仪表板
*/
async function generateDashboard(options: DashboardOptions): Promise<void> {
try {
console.error('🚀 开始生成 GitHub Trending Dashboard...\n');
// 1. 获取 GitHub Trending 数据
const projects = await getTrendingProjects(options);
console.error(`✅ 获取到 ${projects.length} 个项目`);
// 2. 获取技术新闻(如果启用)
let news: TechNewsItem[] = [];
if (options.includeNews) {
news = await getTechNews(options.newsCount);
console.error(`✅ 获取到 ${news.length} 条新闻`);
}
// 3. 分析数据
const analytics = analyzeData(projects);
console.error(`✅ 数据分析完成`);
// 4. 准备模板数据
const templateData: TemplateData = {
title: 'GitHub Trending Dashboard',
generatedAt: new Date().toLocaleString('zh-CN'),
period: options.period === 'daily' ? 'Daily' : 'Weekly',
projects,
news,
analytics,
options
};
// 5. 渲染模板
const templatePath = `${import.meta.dir}/../Templates/dashboard.hbs`;
const templateContent = await Bun.file(templatePath).text();
const template = Handlebars.compile(templateContent);
const html = template(templateData);
console.error(`✅ 模板渲染完成`);
// 6. 保存文件
await Bun.write(options.output, html);
console.error(`\n🎉 仪表板生成成功!`);
console.error(`📄 文件路径: ${options.output}`);
console.error(`\n💡 在浏览器中打开查看效果!`);
} catch (error) {
console.error('\n❌ 生成仪表板失败:');
console.error(error);
process.exit(1);
}
}
/**
* 解析命令行参数
*/
function parseArgs(): DashboardOptions {
const args = process.argv.slice(2);
const options: DashboardOptions = {
period: 'weekly',
limit: 10,
output: './github-trends.html',
includeNews: false,
newsCount: 10,
theme: 'auto'
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '--period':
options.period = args[++i] === 'daily' ? 'daily' : 'weekly';
break;
case '--language':
options.language = args[++i];
break;
case '--limit':
options.limit = parseInt(args[++i]) || 10;
break;
case '--include-news':
options.includeNews = true;
break;
case '--news-count':
options.newsCount = parseInt(args[++i]) || 10;
break;
case '--theme':
options.theme = args[++i] === 'light' || args[++i] === 'dark' ? args[i] : 'auto';
break;
case '--output':
options.output = args[++i];
break;
default:
if (arg.startsWith('--output=')) {
options.output = arg.split('=')[1];
} else if (arg.startsWith('--language=')) {
options.language = arg.split('=')[1];
} else if (arg.startsWith('--limit=')) {
options.limit = parseInt(arg.split('=')[1]) || 10;
}
}
}
return options;
}
/**
* 主函数
*/
async function main() {
const options = parseArgs();
await generateDashboard(options);
}
// 如果直接运行此脚本
if (import.meta.main) {
main();
}
// 导出供其他模块使用
export { generateDashboard };
export type { DashboardOptions };
Tools/GetTechNews.ts
#!/usr/bin/env bun
/**
* Tech News Fetcher
*
* 从 Hacker News 和其他来源获取技术新闻
*
* 使用方式:
* ./GetTechNews.ts [count]
*
* 参数:
* count - 获取新闻数量 (默认: 10)
*
* 示例:
* ./GetTechNews.ts
* ./GetTechNews.ts 20
*/
import Parser from 'rss-parser';
import type { TechNewsItem } from './Lib/types';
const HN_API = 'https://hn.algolia.com/api/v1/search';
const parser = new Parser();
/**
* 从 Hacker News Algolia API 获取新闻
*/
async function getHackerNews(count: number): Promise<TechNewsItem[]> {
try {
const response = await fetch(`${HN_API}?tags=front_page&hits=${count}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data.hits.map((hit: any) => ({
id: hit.objectID,
title: hit.title,
url: hit.url || `https://news.ycombinator.com/item?id=${hit.objectID}`,
source: 'hackernews',
points: hit.points || 0,
comments: hit.num_comments || 0,
timestamp: new Date(hit.created_at).toISOString(),
tags: hit._tags || []
}));
} catch (error) {
console.error('获取 Hacker News 失败:', error);
return [];
}
}
/**
* 从 Hacker News RSS 获取新闻(备用方案)
*/
async function getHackerNewsRSS(count: number): Promise<TechNewsItem[]> {
try {
const feed = await parser.parseURL('https://news.ycombinator.com/rss');
return feed.items.slice(0, count).map((item: any) => ({
id: item.guid || item.link,
title: item.title || 'No title',
url: item.link,
source: 'hackernews',
timestamp: item.pubDate || new Date().toISOString(),
tags: ['hackernews', 'rss']
}));
} catch (error) {
console.error('获取 Hacker News RSS 失败:', error);
return [];
}
}
/**
* 获取技术新闻(主函数)
*/
async function getTechNews(count: number = 10): Promise<TechNewsItem[]> {
console.error(`正在获取技术新闻(${count}条)...`);
// 优先使用 Hacker News API
let news = await getHackerNews(count);
// 如果失败,尝试 RSS 备用
if (news.length === 0) {
console.error('Hacker News API 失败,尝试 RSS...');
news = await getHackerNewsRSS(count);
}
console.error(`✅ 获取到 ${news.length} 条新闻`);
return news;
}
/**
* CLI 入口
*/
async function main() {
const args = process.argv.slice(2);
const count = parseInt(args[0]) || 10;
try {
const news = await getTechNews(count);
// 输出 JSON 格式(便于程序调用)
console.log(JSON.stringify(news, null, 2));
} catch (error) {
console.error('❌ 获取新闻失败:');
console.error(error);
process.exit(1);
}
}
// 如果直接运行此脚本
if (import.meta.main) {
main();
}
// 导出供其他模块使用
export { getTechNews };
export type { TechNewsItem };
Tools/Lib/types.ts
/**
* GitHubTrends - 类型定义
*
* 定义所有 TypeScript 接口和类型
*/
/**
* GitHub Trending 项目
*/
export interface TrendingProject {
rank: number;
name: string;
description: string;
language: string;
stars: string;
starsThisPeriod: string;
url: string;
}
/**
* 技术新闻条目
*/
export interface TechNewsItem {
id: string;
title: string;
url: string;
source: string; // 'hackernews', 'reddit', etc.
points?: number;
comments?: number;
timestamp: string;
tags: string[];
}
/**
* 仪表板生成选项
*/
export interface DashboardOptions {
period: 'daily' | 'weekly';
language?: string;
limit: number;
output: string;
includeNews: boolean;
newsCount: number;
theme: 'light' | 'dark' | 'auto';
}
/**
* 数据分析结果
*/
export interface Analytics {
languageDistribution: Record<string, number>;
totalStars: number;
topProject: TrendingProject;
growthStats: {
highest: TrendingProject;
average: number;
};
}
/**
* Trending 查询选项(用于 GetTrending.ts)
*/
export interface TrendingOptions {
period: "daily" | "weekly";
language?: string;
limit: number;
}
/**
* 图表数据
*/
export interface ChartData {
labels: string[];
data: number[];
colors: string[];
}
/**
* 模板渲染数据
*/
export interface TemplateData {
title: string;
generatedAt: string;
period: string;
projects: TrendingProject[];
news?: TechNewsItem[];
analytics: Analytics;
options: DashboardOptions;
}
Tools/Lib/template-helpers.ts
/**
* Template Helpers
*
* Handlebars 自定义辅助函数
*/
import Handlebars from 'handlebars';
/**
* 注册所有自定义辅助函数
*/
export function registerHelpers(): void {
// 格式化数字(添加千位分隔符)
Handlebars.registerHelper('formatNumber', (value: number) => {
return value.toLocaleString();
});
// 截断文本
Handlebars.registerHelper('truncate', (str: string, length: number = 100) => {
if (str.length <= length) return str;
return str.substring(0, length) + '...';
});
// 格式化日期
Handlebars.registerHelper('formatDate', (dateStr: string) => {
const date = new Date(dateStr);
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
});
// JSON 序列化(用于内嵌数据)
Handlebars.registerHelper('json', (context: any) => {
return JSON.stringify(context);
});
// 条件判断
Handlebars.registerHelper('eq', (a: any, b: any) => {
return a === b;
});
Handlebars.registerHelper('ne', (a: any, b: any) => {
return a !== b;
});
Handlebars.registerHelper('gt', (a: number, b: number) => {
return a > b;
});
Handlebars.registerHelper('lt', (a: number, b: number) => {
return a < b;
});
}
/**
* 渲染模板
*/
export async function renderTemplate(
templatePath: string,
data: any
): Promise<string> {
const templateContent = await Bun.file(templatePath).text();
const template = Handlebars.compile(templateContent);
return template(data);
}
export default { registerHelpers, renderTemplate };
Tools/Lib/visualization-helpers.ts
/**
* Visualization Helpers
*
* 数据分析和可视化辅助函数
*/
import type { TrendingProject, Analytics } from './types';
/**
* 分析项目数据
*/
export function analyzeData(projects: TrendingProject[]): Analytics {
// 语言分布统计
const languageDistribution: Record<string, number> = {};
projects.forEach(project => {
const lang = project.language;
languageDistribution[lang] = (languageDistribution[lang] || 0) + 1;
});
// 总 stars 数
const totalStars = projects.reduce((sum, project) => {
return sum + parseInt(project.stars.replace(/,/g, '') || 0);
}, 0);
// 找出 top project
const topProject = projects.reduce((top, project) => {
const topStars = parseInt(top.stars.replace(/,/g, '') || 0);
const projStars = parseInt(project.stars.replace(/,/g, '') || 0);
return projStars > topStars ? project : top;
}, projects[0]);
// 增长统计
const projectsWithGrowth = projects.filter(p => p.starsThisPeriod);
const growthValues = projectsWithGrowth.map(p =>
parseInt(p.starsThisPeriod.replace(/[+,]/g, '') || 0)
);
const highestGrowth = projectsWithGrowth.reduce((highest, project) => {
const highestValue = parseInt(highest.starsThisPeriod.replace(/[+,]/g, '') || 0);
const projValue = parseInt(project.starsThisPeriod.replace(/[+,]/g, '') || 0);
return projValue > highestValue ? project : highest;
}, projectsWithGrowth[0] || projects[0]);
const averageGrowth = growthValues.length > 0
? Math.round(growthValues.reduce((a, b) => a + b, 0) / growthValues.length)
: 0;
// 提取唯一语言列表(用于筛选)
const languages = Object.keys(languageDistribution).sort();
// 生成图表数据
const growthData = projects.slice(0, 10).map(p => ({
name: p.name.split('/')[1] || p.name,
growth: parseInt(p.starsThisPeriod.replace(/[+,]/g, '') || 0)
}));
return {
languageDistribution,
totalStars,
topProject,
growthStats: {
highest: highestGrowth,
average: averageGrowth
},
languages,
growthData
};
}
/**
* 格式化 stars 数字
*/
export function formatStars(starsStr: string): number {
return parseInt(starsStr.replace(/,/g, '') || 0);
}
/**
* 解析增长数值
*/
export function parseGrowth(growthStr: string): number {
if (!growthStr) return 0;
return parseInt(growthStr.replace(/[+,]/g, '') || 0);
}
export default { analyzeData, formatStars, parseGrowth };
Templates/dashboard.hbs
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Trending Dashboard - {{period}}</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
github: {
dark: '#0d1117',
light: '#161b22',
border: '#30363d',
accent: '#58a6ff'
}
}
}
}
}
</script>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
.project-card {
transition: all 0.3s ease;
}
.project-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
}
.news-item {
border-left: 3px solid #58a6ff;
padding-left: 1rem;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- 页头 -->
<header class="bg-white shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-gray-900">🚀 GitHub Trending Dashboard</h1>
<p class="text-gray-600 mt-1">
周期: <span class="font-semibold text-github-accent">{{period}}</span> |
生成时间: <span class="text-gray-500">{{generatedAt}}</span>
</p>
</div>
<div class="flex gap-2">
<button onclick="window.print()" class="px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm font-medium">
🖨️ Print
</button>
</div>
</div>
</div>
</header>
<main class="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
<!-- 统计概览 -->
<section class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="stat-card rounded-xl p-6 text-white shadow-lg">
<h3 class="text-lg font-semibold opacity-90">项目总数</h3>
<p class="text-4xl font-bold mt-2">{{projects.length}}</p>
<p class="text-sm opacity-75 mt-1">{{period}} 热门趋势</p>
</div>
<div class="bg-gradient-to-br from-green-500 to-emerald-600 rounded-xl p-6 text-white shadow-lg">
<h3 class="text-lg font-semibold opacity-90">总 Stars 数</h3>
<p class="text-4xl font-bold mt-2">{{analytics.totalStars}}</p>
<p class="text-sm opacity-75 mt-1">所有项目总计</p>
</div>
<div class="bg-gradient-to-br from-orange-500 to-red-500 rounded-xl p-6 text-white shadow-lg">
<h3 class="text-lg font-semibold opacity-90">最热项目</h3>
<p class="text-xl font-bold mt-2 truncate">{{analytics.topProject.name}}</p>
<p class="text-sm opacity-75 mt-1">{{analytics.topProject.stars}} stars</p>
</div>
</section>
<!-- 筛选和搜索 -->
<section class="bg-white rounded-xl shadow-sm p-6 mb-8">
<div class="flex flex-wrap gap-4 items-center">
<div class="flex-1 min-w-64">
<label class="block text-sm font-medium text-gray-700 mb-1">搜索项目</label>
<input
type="text"
id="searchInput"
placeholder="按名称或描述搜索..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-github-accent focus:border-transparent"
oninput="filterProjects()"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">语言筛选</label>
<select
id="languageFilter"
class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-github-accent focus:border-transparent"
onchange="filterProjects()"
>
<option value="all">全部语言</option>
{{#each analytics.languages}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">排序方式</label>
<select
id="sortSelect"
class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-github-accent focus:border-transparent"
onchange="sortProjects()"
>
<option value="rank">排名</option>
<option value="stars">总 Stars</option>
<option value="growth">本期增长</option>
</select>
</div>
</div>
</section>
<!-- 语言分布图表 -->
<section class="bg-white rounded-xl shadow-sm p-6 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-4">📊 语言分布</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<canvas id="languageChart"></canvas>
</div>
<div>
<canvas id="growthChart"></canvas>
</div>
</div>
</section>
<!-- Trending Projects -->
<section class="mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-4">🔥 热门项目</h2>
<div id="projects-container" class="grid grid-cols-1 gap-4">
{{#each projects}}
<div class="project-card bg-white rounded-xl shadow-sm p-6 border border-gray-200"
data-rank="{{rank}}"
data-language="{{language}}"
data-stars="{{stars}}"
data-growth="{{starsThisPeriod}}"
data-name="{{name}}"
data-description="{{description}}">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center gap-3 mb-2">
<span class="text-2xl font-bold text-github-accent">#{{rank}}</span>
<h3 class="text-xl font-semibold text-gray-900">
<a href="{{url}}" target="_blank" class="hover:text-github-accent">{{name}}</a>
</h3>
<span class="badge bg-blue-100 text-blue-800">{{language}}</span>
</div>
<p class="text-gray-600 mb-3">{{description}}</p>
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>⭐ {{stars}} stars</span>
{{#if starsThisPeriod}}
<span class="text-green-600 font-semibold">(+{{starsThisPeriod}} this {{../period}})</span>
{{/if}}
</div>
</div>
<a href="{{url}}" target="_blank" class="px-4 py-2 bg-github-accent text-white rounded-lg hover:bg-blue-600 transition font-medium">
View →
</a>
</div>
</div>
{{/each}}
</div>
</section>
<!-- Tech News -->
{{#if news}}
<section class="mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-4">📰 技术资讯</h2>
<div class="grid grid-cols-1 gap-4">
{{#each news}}
<div class="news-item bg-white rounded-xl shadow-sm p-5 hover:shadow-md transition">
<div class="flex items-start justify-between">
<div class="flex-1">
<h3 class="text-lg font-semibold text-gray-900 mb-1">
<a href="{{url}}" target="_blank" class="hover:text-github-accent">{{title}}</a>
</h3>
<div class="flex items-center gap-4 text-sm text-gray-500">
<span class="text-orange-600">📰 {{source}}</span>
{{#if points}}
<span>⬆️ {{points}} points</span>
{{/if}}
{{#if comments}}
<span>💬 {{comments}} comments</span>
{{/if}}
</div>
</div>
</div>
</div>
{{/each}}
</div>
</section>
{{/if}}
</main>
<!-- 页脚 -->
<footer class="bg-white border-t border-gray-200 mt-12">
<div class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
<p class="text-center text-gray-500 text-sm">
由 GitHubTrends Skill 生成 | 数据来源:GitHub 和 Hacker News
</p>
</div>
</footer>
<!-- JavaScript -->
<script>
// 注入数据
window.dashboardData = {
projects: {{{json projects}}},
analytics: {
languageDistribution: {{{json analytics.languageDistribution}}},
growthData: {{{json analytics.growthData}}}
}
};
// 初始化图表
document.addEventListener('DOMContentLoaded', function() {
initLanguageChart();
initGrowthChart();
});
// 语言分布饼图
function initLanguageChart() {
const ctx = document.getElementById('languageChart').getContext('2d');
const data = window.dashboardData.analytics.languageDistribution;
new Chart(ctx, {
type: 'pie',
data: {
labels: Object.keys(data),
datasets: [{
data: Object.values(data),
backgroundColor: [
'#58a6ff', '#238636', '#f1e05a', '#d73a49',
'#8957E5', '#e34c26', '#CB3837', '#DA5B0B',
'#4F5D95', '#563d7c'
]
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'right'
},
title: {
display: true,
text: 'Projects by Language'
}
}
}
});
}
// Stars 增长柱状图
function initGrowthChart() {
const ctx = document.getElementById('growthChart').getContext('2d');
const projects = window.dashboardData.projects.slice(0, 10);
new Chart(ctx, {
type: 'bar',
data: {
labels: projects.map(p => p.name.split('/')[1] || p.name),
datasets: [{
label: 'Stars This Period',
data: projects.map(p => parseInt(p.starsThisPeriod.replace('+', '') || 0)),
backgroundColor: 'rgba(88, 166, 255, 0.8)',
borderColor: 'rgba(88, 166, 255, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
indexAxis: 'y',
plugins: {
title: {
display: true,
text: 'Top 10 Growth'
}
},
scales: {
x: {
beginAtZero: true
}
}
}
});
}
// 筛选项目
function filterProjects() {
const searchValue = document.getElementById('searchInput').value.toLowerCase();
const languageValue = document.getElementById('languageFilter').value;
const cards = document.querySelectorAll('.project-card');
cards.forEach(card => {
const name = card.dataset.name.toLowerCase();
const description = card.dataset.description.toLowerCase();
const language = card.dataset.language;
const matchesSearch = name.includes(searchValue) || description.includes(searchValue);
const matchesLanguage = languageValue === 'all' || language === languageValue;
card.style.display = matchesSearch && matchesLanguage ? 'block' : 'none';
});
}
// 排序项目
function sortProjects() {
const sortBy = document.getElementById('sortSelect').value;
const container = document.getElementById('projects-container');
const cards = Array.from(container.children);
cards.sort((a, b) => {
switch(sortBy) {
case 'stars':
return parseInt(b.dataset.stars.replace(/,/g, '')) - parseInt(a.dataset.stars.replace(/,/g, ''));
case 'growth':
const growthA = parseInt(a.dataset.growth.replace(/[+,]/g, '') || 0);
const growthB = parseInt(b.dataset.growth.replace(/[+,]/g, '') || 0);
return growthB - growthA;
case 'rank':
default:
return parseInt(a.dataset.rank) - parseInt(b.dataset.rank);
}
});
cards.forEach(card => container.appendChild(card));
}
</script>
</body>
</html>
Workflows/GenerateDashboard.md
# Рабочий процесс GenerateDashboard
Рабочий процесс создания интерактивной панели визуализации данных.
## Описание
Этот рабочий процесс использует инструмент GenerateDashboard.ts для получения популярных проектов с GitHub и создания интерактивной HTML-панели с поддержкой:
- Представления проектов в карточках
- Круговой диаграммы распределения языков
- Столбчатой диаграммы прироста Stars
- Списка технологических новостей
- Фильтрации, сортировки и поиска в реальном времени
## Когда использовать
Используйте этот рабочий процесс, когда пользователь запрашивает что-либо из следующего:
- «создай панель GitHub trending»
- «создай веб-страницу трендов»
- «создай визуальный отчёт»
- «экспортируй панель трендов»
- «создай интерактивную веб-страницу»
## Шаги рабочего процесса
### Шаг 1: определение параметров
Подтвердите у пользователя или выведите следующие параметры:
- **Период**: daily (день) или weekly (неделя, по умолчанию)
- **Язык программирования**: необязательно (например, TypeScript, Python, Go, Rust и т. д.)
- **Число проектов**: по умолчанию 10
- **Включение новостей**: включать ли технологические новости
- **Число новостей**: по умолчанию 10
- **Путь вывода**: по умолчанию ./github-trends.html
### Шаг 2: выполнение инструмента
Запустите инструмент GenerateDashboard.ts:
```bash
# 基本用法(本周,10个项目)
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts
# 指定语言和新闻
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts \
--period weekly \
--language TypeScript \
--limit 20 \
--include-news \
--news-count 15 \
--output ~/trends.html
# 每日趋势
bun ~/.claude/skills/GitHubTrends/Tools/GenerateDashboard.ts \
--period daily \
--output ~/daily-trends.html
```
### Шаг 3: показ результатов
Инструмент автоматически:
1. Получает данные GitHub trending
2. Получает технологические новости (если включено)
3. Анализирует данные для формирования статистики
4. Рендерит HTML-шаблон
5. Сохраняет результат по указанному пути
### Шаг 4: проверка и открытие
Созданный HTML-файл содержит:
- ✅ Адаптивный макет
- ✅ Представление проектов в карточках
- ✅ Круговую диаграмму распределения языков
- ✅ Столбчатую диаграмму прироста Stars
- ✅ Фильтрацию в реальном времени
- ✅ Сортировку
- ✅ Поиск
- ✅ Список технологических новостей
## Примеры использования
### Пример 1: базовая панель
```
User: «Создай панель GitHub trending за эту неделю»
Assistant:
Запускаю инструмент GenerateDashboard...
[Выполнение команды, создание /tmp/github-trends.html]
✅ Панель успешно создана! Открыта в браузере.
```
### Пример 2: панель с новостями
```
User: «Создай ежедневные тренды проектов TypeScript, включая новости»
Assistant:
Создаю панель ежедневных трендов TypeScript с технологическими новостями...
[Выполнение команды: --period daily --language TypeScript --include-news]
✅ Панель создана в ~/Downloads/ts-daily-trends.html
```
### Пример 3: настраиваемый вывод
```
User: «Создай панель с 20 проектами и сохрани на рабочий стол»
Assistant:
Создаю панель трендов из 20 проектов...
[Выполнение команды: --limit 20 --output ~/Desktop/github-trends.html]
✅ Готово! Файл сохранён на рабочий стол
```
## Параметры инструмента
| Параметр | Описание | Значение по умолчанию | Допустимые значения |
|------|------|--------|--------|
| `--period` | Период | `weekly` | `daily`, `weekly` |
| `--language` | Фильтр по языку программирования | Все | TypeScript, Python, Go, Rust и т. д. |
| `--limit` | Число возвращаемых проектов | 10 | Любое положительное целое число |
| `--include-news` | Включить технологические новости | false | - |
| `--news-count` | Число новостей | 10 | Любое положительное целое число |
| `--theme` | Тема | `auto` | `light`, `dark`, `auto` |
| `--output` | Путь выходного файла | `./github-trends.html` | Любой путь |
## Возможности вывода
### Визуализация данных
- **Круговая диаграмма распределения языков**: показывает долю проектов каждого языка программирования
- **Столбчатая диаграмма прироста Stars**: показывает прирост stars для первых 10 проектов
### Интерактивные функции
- **Поиск**: по названию или описанию проекта
- **Фильтрация**: по языку программирования
- **Сортировка**: по месту в рейтинге, общему числу stars и приросту за период
### Адаптивный дизайн
- Поддержка компьютеров, планшетов и телефонов
- Создание красивого интерфейса с помощью Tailwind CSS
- Цветовая схема в стиле GitHub
## Обработка ошибок
Если возникла ошибка:
1. **Сетевая ошибка**: проверьте соединение с сетью и убедитесь, что GitHub доступен
2. **Ошибка разбора**: структура страницы GitHub могла измениться; инструмент покажет отладочную информацию
3. **Ошибка записи файла**: проверьте права записи по выходному пути
## Голосовое уведомление
При выполнении этого рабочего процесса отправьте голосовое уведомление:
```bash
curl -s -X POST http://localhost:8888/notify \
-H "Content-Type: application/json" \
-d '{"message": "正在生成 GitHub Trending Dashboard..."}' \
> /dev/null 2>&1 &
```
И выведите текстовое уведомление:
```
Выполняется рабочий процесс **GenerateDashboard** из навыка **GitHubTrends**...
```
## Интеграция с другими навыками
- **Browser**: проверка отображения созданной HTML-страницы
- **System**: сохранение снимка панели в MEMORY/
- **OSINT**: анализ трендов технологических стеков
## Примечания
- Данные обновляются раз в час (частота обновления GitHub trending)
- Созданный HTML полностью самостоятельный, сервер не требуется
- Все зависимости загружаются через CDN (Tailwind CSS, Chart.js)
- Поддерживается просмотр без сети (данные диаграмм встроены)
## Расширенное использование
### Пакетное создание
```bash
# 生成多个语言的仪表板
for lang in TypeScript Python Go Rust; do
bun Tools/GenerateDashboard.ts \
--language $lang \
--output ~/trends-$lang.html
done
```
### Задания по расписанию
```bash
# 每小时生成一次快照
# 添加到 crontab:
0 * * * * cd ~/.claude/skills/GitHubTrends && bun Tools/GenerateDashboard.ts --output ~/trends-$(date +%H).html
```
### Настройка темы
Изменяя `Templates/dashboard.hbs`, можно настраивать:
- Цветовую схему
- Структуру макета
- Добавлять новые типы диаграмм
- Добавлять новые интерактивные функции
Текст доступен бесплатно по CC0 1.0. Источники и лицензии.
Как использовать навык
Прочитайте инструкцию и проверьте, какие файлы, инструменты и подключения ей нужны. Перенесите навык в совместимое приложение для AI-агентов или используйте подходящие шаги в чате. Если навык состоит из нескольких файлов, сохраните их структуру.