④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根

一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
  · 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
    跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
  · 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
  · 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
    横向流程图每步写清页面/功能/操作/完成判据。
  · 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
  · 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
  · 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。

二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
  「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
  同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
  当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。

三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
This commit is contained in:
admin committed 2026-10-15 21:17:28 +08:00
1 parent fa0a9026c8
commit 5afeb6b78a
4184 files changed
+326 -70

No files matched your search

@@ -0,0 +1,130 @@
---
name: github-dashboard
description: |
GitHub repository analytics dashboard — stars, forks, contributors,
issues, pull requests, recent activity, and top contributors. Use when
the brief asks for a GitHub repo dashboard, open-source growth report,
repository health page, or GitHub analytics view.
triggers:
- "github dashboard"
- "repo dashboard"
- "repository dashboard"
- "github analytics"
- "open source dashboard"
- "github growth"
- "仓库看板"
- "GitHub 看板"
od:
mode: prototype
platform: desktop
scenario: operation
preview:
type: html
entry: index.html
design_system:
requires: true
sections: [color, typography, layout, components]
outputs:
primary: index.html
secondary:
- template.html
- data.json
- artifact.json
- provenance.json
capabilities_required:
- shell
- file_write
example_prompt: "Build a GitHub dashboard for nexu-io/open-design — stars, forks, contributors, issues, PRs, recent activity, and top contributors."
---
# GitHub Dashboard Skill
Create a single-screen GitHub repository analytics dashboard in the FlowAI / Soft Paper Workspace visual style: warm off-white canvas, white rounded panels, a fixed left sidebar, compact KPI cards, pastel pills, dense tables, and low-contrast hairlines.
## Resource map
```
github-dashboard/
├── SKILL.md
├── example.html ← rendered reference dashboard
└── references/
├── template.html ← live-artifact-compatible HTML template
├── example-data.json ← normalized public GitHub data shape
├── artifact-example.json ← minimal live-artifact create input
└── provenance-example.json ← safe source/provenance example
```
## When to use this skill
Use this when the user asks for a dashboard or report about a single GitHub repository, for example:
- repository growth dashboard
- open-source project health report
- GitHub stars / forks / contributors analytics
- issue and pull-request activity page
- maintainer / contributor dashboard
If the user asks for refreshability, source auditability, or scheduled updates, produce the live-artifact source set (`template.html`, `data.json`, `artifact.json`, `provenance.json`) and follow the `live-artifact` contract. If they only need a visual artifact, produce a self-contained `index.html`.
## Workflow
1. **Resolve repository scope**
- Parse `owner/repo` from the brief.
- This v1 skill is scoped to one repository. If multiple repositories are requested, ask the user to pick the primary repository or create one dashboard per repository.
- If the repo is missing, ask one concise question for the GitHub URL or `owner/repo`.
2. **Collect public GitHub data**
- Prefer GitHub CLI/API for public repository data when available.
- Current stars/forks/watchers/open issue count: `GET /repos/{owner}/{repo}` (`stargazers_count`, `forks_count`, `watchers_count`, `open_issues_count`).
- Contributors: paginate `GET /repos/{owner}/{repo}/contributors?per_page=100&page=N`, sort by `contributions` descending, and take the top N used by the dashboard. If only page 1 is available, label totals as first-page estimates.
- Issues: use GitHub Search API (`repo:{owner}/{repo} is:issue`) for total counts, or paginate `GET /repos/{owner}/{repo}/issues?state=all` and filter out items with a `pull_request` field.
- Pull requests: use GitHub Search API (`repo:{owner}/{repo} is:pr`) for total counts, or paginate `GET /repos/{owner}/{repo}/pulls?state=all` and count pages via the `Link` header.
- Recent activity: combine the newest issues and pull requests, normalize them into display-ready rows, and cap the preview list at 5–10 items.
- Growth/delta metrics: GitHub REST does not expose complete historical star/fork deltas. Use GraphQL, stargazer event snapshots, the Events API where available, or explicitly mark deltas as estimated/synthetic in `provenance.json`.
- Do not store auth tokens, raw HTTP envelopes, cookies, rate-limit headers, or private metadata.
3. **Normalize into dashboard data**
- Required `repository`: `name`, `fullName`, `url`, `description`, `language`, `license`, `created`, `lastUpdated`.
- Required `metrics`: stars, forks, contributors, issues, pull requests. Store display-ready totals plus small deltas or growth notes.
- Required `contributors`: top 5–8 contributors with `login`, `avatar`, and `contributions`.
- Required `recentActivity`: display-ready rows with `title`, `typeText`, `typeClass`, `label`, `labelClass`, `author`, `authorAvatar`, and `updated`. Do not rely on template conditionals for issue/PR switching.
- Chart data can be synthetic only when GitHub does not expose the exact history; document the transformation in provenance.
4. **Apply the visual system**
- Use the active `DESIGN.md` tokens when present.
- If no design system is provided, use the Soft Paper defaults reflected in `references/template.html`: `#F2F2F0` canvas, white cards, `#ECECEA` borders, `#0A0A0A` ink, Geist/Inter typography, 256px sidebar, 48px topbar, and 16px card radius.
- Keep color small and semantic: green for healthy metrics, amber for warning, blue for feature/PR labels, red only for defects or risk.
5. **Lay out the page**
- Shell: 256px sidebar + main panel, both white, rounded 16px, 1px hairline border.
- Topbar: repo context on the left, refresh/export/action affordances on the right.
- Header: repository name, description, and date/settings/actions row.
- KPI strip: 5 compact cards for stars, forks, contributors, issues, PRs.
- Main grid: 2fr/1fr split with a growth chart or activity table on the left and top contributors/health cards on the right.
- Footer: provenance/last-updated note in small muted text.
6. **Write the artifact**
- For a static artifact, write one self-contained `index.html` with inline CSS and no external JS libraries.
- For a live artifact, write `template.html`, `data.json`, `artifact.json`, and `provenance.json`; `index.html` is derived by the daemon.
- Tag major regions with stable `data-od-id` values: `sidebar`, `topbar`, `repo-header`, `kpi-strip`, `growth-chart`, `contributors`, `activity`, `provenance`.
## Visual rules
- Light mode only.
- 256px fixed sidebar on desktop; stack on narrow screens.
- 4 or 5 KPI cards in the first row.
- Use tabular lining numerals for all counts.
- Avatars are circular, 28–32px in tables and contributor lists.
- Tables use 13px body text, 11px uppercase column labels, 1px row dividers.
- Cards use hairline borders and a barely visible shadow at most: `0 1px 2px rgba(10,10,10,.04), 0 1px 1px rgba(10,10,10,.02)`.
- Do not use gradients except tiny workflow/repo icon placeholders.
- Do not make the page look like GitHub itself. This is a custom operational dashboard, not a GitHub UI clone.
## Self-check
- Every metric has a source or a provenance note.
- No private data or credentials are persisted.
- Data labels are specific to the repository, not placeholders.
- The screen still reads clearly at 50% zoom.
- The dashboard uses at most one solid black primary action per area.
- Status labels and issue/PR chips are pastel pills, not saturated badges.
@@ -0,0 +1,473 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>open-design — GitHub Dashboard</title>
<style>
:root {
--canvas: #f2f2f0;
--surface: #ffffff;
--surface-muted: #fafaf9;
--border: #ececea;
--border-strong: #e2e2df;
--text-primary: #0a0a0a;
--text-secondary: #6b6b6b;
--text-tertiary: #9a9a95;
--active-bg: #e6f4ea;
--active-fg: #1f8a4c;
--pending-bg: #fff6d6;
--pending-fg: #9a7b12;
--feature-bg: #dcebff;
--feature-fg: #2f66c9;
--danger-bg: #fdecec;
--danger-fg: #c0392b;
--green: #34a853;
--amber: #e8a23a;
--red: #d94a3d;
--shadow: 0 1px 2px rgba(10, 10, 10, .04), 0 1px 1px rgba(10, 10, 10, .02);
--radius-card: 16px;
--radius-tile: 12px;
--radius-control: 8px;
font-family: Geist, Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-feature-settings: "tnum", "lnum";
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--canvas); color: var(--text-primary); }
.shell { min-height: 100vh; padding: 16px; display: grid; grid-template-columns: 256px 1fr; gap: 16px; }
.sidebar, .main { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); }
.sidebar { padding: 16px; display: flex; flex-direction: column; gap: 22px; }
.brand { height: 48px; display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 15px; }
.logo { width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg, #2f66c9, #5a8ded); color: white; display: grid; place-items: center; font-size: 18px; font-weight: 600; }
.group-label { display: flex; align-items: center; gap: 8px; color: var(--text-tertiary); font-size: 11px; font-weight: 500; letter-spacing: .02em; text-transform: uppercase; margin-bottom: 8px; margin-top: 4px; }
.dot-ring { width: 14px; height: 14px; border: 1.5px dashed var(--text-tertiary); border-radius: 999px; flex: none; }
.nav { display: grid; gap: 4px; }
.nav-item { min-height: 36px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-radius: var(--radius-control); padding: 8px 10px; color: var(--text-secondary); font-size: 13px; text-decoration: none; transition: all .15s; }
.nav-item:hover { background: var(--surface-muted); }
.nav-item.active { background: var(--surface-muted); color: var(--text-primary); font-weight: 500; }
.count { border-radius: 999px; padding: 2px 8px; background: var(--surface-muted); color: var(--text-secondary); font-size: 11px; font-weight: 500; }
.main { overflow: hidden; }
.topbar { height: 48px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; padding: 0 20px; font-size: 14px; }
.topbar-title { display: flex; align-items: center; gap: 10px; }
.topbar-icon { width: 20px; height: 20px; opacity: .7; }
.content { padding: 24px 28px 32px; display: grid; gap: 24px; }
.page-head { display: flex; align-items: end; justify-content: space-between; gap: 16px; }
h1 { margin: 0; font-size: 22px; line-height: 1.25; font-weight: 600; letter-spacing: -.005em; }
.subtitle { margin-top: 6px; color: var(--text-secondary); font-size: 14px; line-height: 1.4; }
.actions { display: flex; gap: 8px; align-items: center; }
button, .button { border: 1px solid var(--border-strong); background: var(--surface); border-radius: var(--radius-control); padding: 8px 14px; color: var(--text-primary); font: inherit; font-size: 12px; cursor: pointer; transition: all .15s; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; }
button:hover, .button:hover { background: var(--surface-muted); }
button.primary, .button.primary { background: var(--text-primary); color: #fff; border-color: var(--text-primary); }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 20px; }
.tabs { display: inline-flex; background: var(--surface-muted); border-radius: 12px; padding: 4px; gap: 4px; }
.tab { padding: 6px 12px; border-radius: var(--radius-control); color: var(--text-secondary); font-size: 12px; cursor: pointer; transition: all .15s; border: 1px solid transparent; text-decoration: none; }
.tab.active { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); box-shadow: var(--shadow); }
.kpis { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px; }
.kpi { min-height: 84px; border: 1px solid var(--border); border-radius: var(--radius-tile); background: var(--surface-muted); padding: 14px; display: flex; flex-direction: column; }
.kpi-top { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.glyph { width: 28px; height: 28px; border-radius: 8px; background: var(--surface); border: 1px solid var(--border); display: inline-grid; place-items: center; box-shadow: var(--shadow); font-size: 14px; flex: none; }
.kpi-label { color: var(--text-secondary); font-size: 13px; }
.kpi-num { font-size: 28px; font-weight: 600; line-height: 1.1; margin-top: auto; }
.kpi-trend { font-size: 12px; color: var(--green); margin-top: 4px; font-weight: 500; }
.kpi-trend.down { color: var(--text-tertiary); }
.grid { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
.grid-full { display: grid; grid-template-columns: 1fr; gap: 16px; }
.card-title { display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 600; margin-bottom: 16px; }
.tick-rule { height: 24px; margin: 8px 0 16px; background: repeating-linear-gradient(90deg, var(--border) 0 1px, transparent 1px 8px); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 760px; }
th { text-align: left; padding: 10px 12px; color: var(--text-tertiary); font-size: 11px; font-weight: 500; letter-spacing: .01em; background: var(--surface-muted); text-transform: uppercase; }
td { padding: 14px 12px; border-top: 1px solid var(--border); color: var(--text-secondary); }
td strong { color: var(--text-primary); font-weight: 500; }
.mono { font-family: "Geist Mono", "JetBrains Mono", ui-monospace, monospace; font-size: 12px; }
.pill { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px; padding: 3px 10px; font-size: 11px; font-weight: 500; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
.active-pill { background: var(--active-bg); color: var(--active-fg); }
.pending-pill { background: var(--pending-bg); color: var(--pending-fg); }
.feature-pill { background: var(--feature-bg); color: var(--feature-fg); }
.danger-pill { background: var(--danger-bg); color: var(--danger-fg); }
.avatar { width: 28px; height: 28px; border-radius: 999px; border: 1px solid var(--border); background: linear-gradient(135deg, #dcecff, #f8eadb); display: inline-grid; place-items: center; color: var(--text-secondary); font-size: 11px; font-weight: 500; margin-right: 8px; overflow: hidden; flex: none; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.contrib { display: grid; gap: 10px; }
.person { display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--border); border-radius: 12px; padding: 12px; background: var(--surface-muted); }
.person-info { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.person-name { font-weight: 500; font-size: 13px; color: var(--text-primary); }
.person-role { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.chart-container { position: relative; height: 180px; margin-top: 16px; }
.chart { width: 100%; height: 100%; }
.chart-bars { display: flex; align-items: flex-end; justify-content: space-between; height: 100%; gap: 8px; }
.bar { flex: 1; background: linear-gradient(180deg, var(--green) 0%, #2d8a47 100%); border-radius: 4px 4px 0 0; min-height: 4px; position: relative; transition: all .2s; }
.bar:hover { opacity: .85; }
.bar-label { position: absolute; bottom: -22px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--text-tertiary); white-space: nowrap; }
.trend-line { display: flex; align-items: flex-end; height: 100%; gap: 2px; }
.trend-point { flex: 1; background: var(--feature-fg); border-radius: 2px 2px 0 0; min-height: 2px; }
.issue-row { display: flex; align-items: start; gap: 12px; padding: 14px 12px; border-top: 1px solid var(--border); }
.issue-row:first-child { border-top: none; }
.issue-icon { width: 20px; height: 20px; border-radius: 999px; background: var(--active-bg); color: var(--active-fg); display: grid; place-items: center; font-size: 11px; flex: none; margin-top: 2px; }
.issue-icon.pr { background: var(--feature-bg); color: var(--feature-fg); }
.issue-content { flex: 1; min-width: 0; }
.issue-title { font-size: 14px; font-weight: 500; color: var(--text-primary); margin-bottom: 4px; }
.issue-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-tertiary); margin-top: 6px; }
.issue-meta span { display: flex; align-items: center; gap: 4px; }
.provenance-note { color: var(--text-tertiary); font-size: 12px; line-height: 1.5; padding: 4px 2px; }
@media (max-width: 980px) {
.shell { grid-template-columns: 1fr; }
.kpis { grid-template-columns: repeat(2, 1fr); }
.grid { grid-template-columns: 1fr; }
.sidebar { min-height: auto; }
}
</style>
</head>
<body>
<div class="shell">
<aside class="sidebar" data-od-id="sidebar">
<div class="brand">
<div class="logo">OD</div>
<span>open-design</span>
</div>
<section>
<div class="group-label"><span class="dot-ring"></span> Repository</div>
<div class="nav">
<a href="#overview" class="nav-item active">Overview</a>
<a href="#activity" class="nav-item">Activity <span class="count">5</span></a>
<a href="#contributors" class="nav-item">Contributors <span class="count">87</span></a>
<a href="#community" class="nav-item">Community</a>
</div>
</section>
<section>
<div class="group-label"><span class="dot-ring"></span> Metrics</div>
<div class="nav">
<a href="#growth-chart" class="nav-item">Stars Growth</a>
<a href="#fork-trend" class="nav-item">Fork Activity</a>
<a href="#activity" class="nav-item">Issue Trends</a>
<a href="#activity" class="nav-item">PR Velocity</a>
</div>
</section>
<section>
<div class="group-label"><span class="dot-ring"></span> Quick Links</div>
<div class="nav">
<a href="https://github.com/nexu-io/open-design" class="nav-item">GitHub Repository</a>
<a href="https://github.com/nexu-io/open-design#readme" class="nav-item">Documentation</a>
<a href="https://github.com/nexu-io/open-design/discussions" class="nav-item">Discussions</a>
</div>
</section>
</aside>
<main class="main">
<div class="topbar" data-od-id="topbar">
<div class="topbar-title">
<svg class="topbar-icon" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
<span>open-design Dashboard</span>
</div>
<div class="actions">
<button>May 6, 2026</button>
<button>⟳ Refresh</button>
</div>
</div>
<div class="content">
<header class="page-head" data-od-id="repo-header" id="overview">
<div>
<h1>nexu-io/open-design</h1>
<div class="subtitle">Local-first, open-source alternative to Anthropic&#x27;s Claude Design. 19 Skills · 71 brand-grade Design Systems · Generate web/desktop/mobile prototypes · slides · images · videos</div>
</div>
<div class="actions">
<a class="button" href="https://github.com/nexu-io/open-design">View on GitHub →</a>
</div>
</header>
<section class="card">
<div class="tabs" role="tablist" aria-label="Dashboard sections">
<a href="#overview" class="tab active" role="tab">Metrics</a>
<a href="#activity" class="tab" role="tab">Activity</a>
<a href="#contributors" class="tab" role="tab">Contributors</a>
<a href="#community" class="tab" role="tab">Community</a>
</div>
<div class="kpis" style="margin-top:16px" data-od-id="kpi-strip">
<div class="kpi">
<div class="kpi-top">
<span class="glyph">⭐</span>
<span class="kpi-label">Stars</span>
</div>
<div class="kpi-num">29,070</div>
<div class="kpi-trend">+2,847 this week</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">⑂</span>
<span class="kpi-label">Forks</span>
</div>
<div class="kpi-num">3,167</div>
<div class="kpi-trend">+218 this week</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">◎</span>
<span class="kpi-label">Contributors</span>
</div>
<div class="kpi-num">87</div>
<div class="kpi-trend">12 active</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">◌</span>
<span class="kpi-label">Open Issues</span>
</div>
<div class="kpi-num">230</div>
<div class="kpi-trend down">450 total</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">↑</span>
<span class="kpi-label">Pull Requests</span>
</div>
<div class="kpi-num">5</div>
<div class="kpi-trend down">653 total</div>
</div>
</div>
</section>
<section class="grid">
<div class="card" data-od-id="growth-chart" id="growth-chart">
<div class="card-title"><span class="dot-ring"></span> Star Growth (Last 30 Days)</div>
<div class="chart-container">
<div class="chart-bars">
<div class="bar" style="height: 45%;">
<span class="bar-label">W1</span>
</div>
<div class="bar" style="height: 62%;">
<span class="bar-label">W2</span>
</div>
<div class="bar" style="height: 78%;">
<span class="bar-label">W3</span>
</div>
<div class="bar" style="height: 100%;">
<span class="bar-label">W4</span>
</div>
</div>
</div>
</div>
<aside class="card" data-od-id="contributors" id="contributors">
<div class="card-title"><span class="dot-ring"></span> Top Contributors</div>
<div class="tick-rule"></div>
<div class="contrib">
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="https://avatars.githubusercontent.com/u/26423749?v=4" alt="pftom" />
</div>
<div>
<div class="person-name">pftom</div>
<div class="person-role">26 contributions</div>
</div>
</div>
<span class="pill pending-pill">26</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="https://avatars.githubusercontent.com/u/20859779?v=4" alt="lefarcen" />
</div>
<div>
<div class="person-name">lefarcen</div>
<div class="person-role">18 contributions</div>
</div>
</div>
<span class="pill pending-pill">18</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="https://avatars.githubusercontent.com/u/201593046?v=4" alt="Sid-Qin" />
</div>
<div>
<div class="person-name">Sid-Qin</div>
<div class="person-role">17 contributions</div>
</div>
</div>
<span class="pill pending-pill">17</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="https://avatars.githubusercontent.com/u/3953668?v=4" alt="nettee" />
</div>
<div>
<div class="person-name">nettee</div>
<div class="person-role">10 contributions</div>
</div>
</div>
<span class="pill pending-pill">10</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="https://avatars.githubusercontent.com/u/56862773?v=4" alt="alchemistklk" />
</div>
<div>
<div class="person-name">alchemistklk</div>
<div class="person-role">9 contributions</div>
</div>
</div>
<span class="pill pending-pill">9</span>
</div>
</div>
</aside>
</section>
<section class="grid-full">
<div class="card" data-od-id="activity" id="activity">
<div class="card-title"><span class="dot-ring"></span> Recent Activity</div>
<div class="table-scroll">
<table>
<thead>
<tr>
<th style="width: 50px;">Type</th>
<th>Title</th>
<th style="width: 120px;">Status</th>
<th style="width: 140px;">Author</th>
<th style="width: 100px;">Updated</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span class="issue-icon pr">PR</span>
</td>
<td><strong>Add Alipay merchant onboarding skill</strong></td>
<td>
<span class="pill feature-pill">feature</span>
</td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;">
<img src="https://avatars.githubusercontent.com/u/238133142?v=4" alt="leilei926524-tech" />
</span>
<span>leilei926524-tech</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">2 hours ago</td>
</tr>
<tr>
<td><span class="issue-icon issue">ISSUE</span></td>
<td><strong>Add clear success feedback after saving API keys in Settings</strong></td>
<td><span class="pill danger-pill">bug</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="https://avatars.githubusercontent.com/u/237175303?v=4" alt="shangxinyu1" /></span>
<span>shangxinyu1</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">2 hours ago</td>
</tr>
<tr>
<td><span class="issue-icon issue">ISSUE</span></td>
<td><strong>hermes json-rpc id 2: Invalid params</strong></td>
<td><span class="pill danger-pill">bug</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="https://avatars.githubusercontent.com/u/42241934?v=4" alt="zlpawn" /></span>
<span>zlpawn</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">3 hours ago</td>
</tr>
<tr>
<td><span class="issue-icon pr">PR</span></td>
<td><strong>fix: 修复0.4.0版本聊天窗口宽度溢出问题</strong></td>
<td><span class="pill active-pill">bug-fix</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="https://avatars.githubusercontent.com/u/12208097?v=4" alt="1119302165" /></span>
<span>1119302165</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">4 hours ago</td>
</tr>
<tr>
<td><span class="issue-icon issue">ISSUE</span></td>
<td><strong>Fix OpenDesign not reading the latest model list from Codex CLI</strong></td>
<td><span class="pill danger-pill">bug</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="https://avatars.githubusercontent.com/u/237175303?v=4" alt="shangxinyu1" /></span>
<span>shangxinyu1</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">5 hours ago</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="grid">
<div class="card" id="fork-trend">
<div class="card-title"><span class="dot-ring"></span> Fork Trend (Last 30 Days)</div>
<div class="chart-container">
<div class="trend-line">
<div class="trend-point" style="height: 35%;"></div>
<div class="trend-point" style="height: 42%;"></div>
<div class="trend-point" style="height: 48%;"></div>
<div class="trend-point" style="height: 55%;"></div>
<div class="trend-point" style="height: 61%;"></div>
<div class="trend-point" style="height: 68%;"></div>
<div class="trend-point" style="height: 74%;"></div>
<div class="trend-point" style="height: 82%;"></div>
<div class="trend-point" style="height: 88%;"></div>
<div class="trend-point" style="height: 95%;"></div>
<div class="trend-point" style="height: 100%;"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-title"><span class="dot-ring"></span> Repository Stats</div>
<div class="tick-rule"></div>
<table>
<tbody>
<tr>
<td><strong>Primary Language</strong></td>
<td style="text-align: right;">TypeScript</td>
</tr>
<tr>
<td><strong>License</strong></td>
<td style="text-align: right;">Apache-2.0</td>
</tr>
<tr>
<td><strong>Created</strong></td>
<td style="text-align: right;">Apr 28, 2026</td>
</tr>
<tr>
<td><strong>Last Updated</strong></td>
<td style="text-align: right;">May 6, 2026</td>
</tr>
<tr>
<td><strong>Repository Size</strong></td>
<td style="text-align: right;">112 MB</td>
</tr>
</tbody>
</table>
</div>
</section>
<footer class="provenance-note" data-od-id="provenance">
Updated May 6, 2026 from public GitHub API fields. Estimated trend data is documented in provenance.json.
</footer>
</div>
</main>
</div>
</body>
</html>
@@ -0,0 +1,10 @@
# GitHub Dashboard References
These files are examples generated from a public `nexu-io/open-design` dashboard artifact.
- `template.html` — a live-artifact-compatible template using escaped `{{data.path}}` bindings.
- `example-data.json` — compact normalized GitHub dashboard data.
- `artifact-example.json` — minimal daemon create input for a refreshable live artifact.
- `provenance-example.json` — safe provenance notes for public GitHub API sources and transformations.
Use these as implementation references, not mandatory output. The skill may produce a static `index.html` or the live-artifact source set depending on the user's refreshability requirements.
@@ -0,0 +1,15 @@
{
"title": "OpenDesign GitHub Dashboard",
"slug": "open-design-github-dashboard",
"status": "active",
"preview": {
"type": "html",
"entry": "index.html"
},
"document": {
"format": "html_template_v1",
"templatePath": "template.html",
"generatedPreviewPath": "index.html",
"dataPath": "data.json"
}
}
@@ -0,0 +1,138 @@
{
"repository": {
"name": "open-design",
"fullName": "nexu-io/open-design",
"url": "https://github.com/nexu-io/open-design",
"description": "Local-first, open-source alternative to Anthropic's Claude Design. 19 Skills · 71 brand-grade Design Systems · Generate web/desktop/mobile prototypes · slides · images · videos",
"language": "TypeScript",
"license": "Apache-2.0",
"created": "Apr 28, 2026",
"lastUpdated": "May 6, 2026",
"size": "112 MB"
},
"metrics": {
"stars": {
"total": "29,070",
"growth": "2,847"
},
"forks": {
"total": "3,167",
"growth": "218"
},
"contributors": {
"total": "87",
"active": "12"
},
"issues": {
"open": "230",
"total": "450"
},
"pullRequests": {
"open": "5",
"total": "653"
}
},
"charts": {
"starGrowth": [
{ "label": "W1", "height": 45 },
{ "label": "W2", "height": 62 },
{ "label": "W3", "height": 78 },
{ "label": "W4", "height": 100 }
],
"forkTrend": [
{ "height": 35 },
{ "height": 42 },
{ "height": 48 },
{ "height": 55 },
{ "height": 61 },
{ "height": 68 },
{ "height": 74 },
{ "height": 82 },
{ "height": 88 },
{ "height": 95 },
{ "height": 100 }
]
},
"contributors": [
{
"login": "pftom",
"avatar": "https://avatars.githubusercontent.com/u/26423749?v=4",
"contributions": 26
},
{
"login": "lefarcen",
"avatar": "https://avatars.githubusercontent.com/u/20859779?v=4",
"contributions": 18
},
{
"login": "Sid-Qin",
"avatar": "https://avatars.githubusercontent.com/u/201593046?v=4",
"contributions": 17
},
{
"login": "nettee",
"avatar": "https://avatars.githubusercontent.com/u/3953668?v=4",
"contributions": 10
},
{
"login": "alchemistklk",
"avatar": "https://avatars.githubusercontent.com/u/56862773?v=4",
"contributions": 9
}
],
"recentActivity": [
{
"typeText": "PR",
"typeClass": "pr",
"title": "Add Alipay merchant onboarding skill",
"label": "feature",
"labelClass": "feature-pill",
"author": "leilei926524-tech",
"authorAvatar": "https://avatars.githubusercontent.com/u/238133142?v=4",
"updated": "2 hours ago"
},
{
"typeText": "ISSUE",
"typeClass": "issue",
"title": "Add clear success feedback after saving API keys in Settings",
"label": "bug",
"labelClass": "danger-pill",
"author": "shangxinyu1",
"authorAvatar": "https://avatars.githubusercontent.com/u/237175303?v=4",
"updated": "2 hours ago"
},
{
"typeText": "ISSUE",
"typeClass": "issue",
"title": "hermes json-rpc id 2: Invalid params",
"label": "bug",
"labelClass": "danger-pill",
"author": "zlpawn",
"authorAvatar": "https://avatars.githubusercontent.com/u/42241934?v=4",
"updated": "3 hours ago"
},
{
"typeText": "PR",
"typeClass": "pr",
"title": "fix: 修复0.4.0版本聊天窗口宽度溢出问题",
"label": "bug-fix",
"labelClass": "active-pill",
"author": "1119302165",
"authorAvatar": "https://avatars.githubusercontent.com/u/12208097?v=4",
"updated": "4 hours ago"
},
{
"typeText": "ISSUE",
"typeClass": "issue",
"title": "Fix OpenDesign not reading the latest model list from Codex CLI",
"label": "bug",
"labelClass": "danger-pill",
"author": "shangxinyu1",
"authorAvatar": "https://avatars.githubusercontent.com/u/237175303?v=4",
"updated": "5 hours ago"
}
],
"metadata": {
"lastUpdated": "May 6, 2026"
}
}
@@ -0,0 +1,92 @@
{
"sources": [
{
"type": "github_api",
"description": "GitHub REST API v3",
"endpoints": [
"GET /repos/nexu-io/open-design",
"GET /repos/nexu-io/open-design/contributors?per_page=100&page=1",
"GET /search/issues?q=repo:nexu-io/open-design+is:issue",
"GET /search/issues?q=repo:nexu-io/open-design+is:pr"
],
"fetchedAt": "2026-05-06T09:30:00Z"
}
],
"fields": [
{
"path": "repository.fullName",
"source": "GET /repos/nexu-io/open-design",
"field": "full_name"
},
{
"path": "metrics.stars.total",
"source": "GET /repos/nexu-io/open-design",
"field": "stargazers_count"
},
{
"path": "metrics.forks.total",
"source": "GET /repos/nexu-io/open-design",
"field": "forks_count"
},
{
"path": "metrics.contributors.total",
"source": "GET /repos/nexu-io/open-design/contributors?per_page=100&page=N",
"field": "paginated unique contributor count",
"note": "Display value may be capped or labelled as an estimate when only page 1 is available."
},
{
"path": "metrics.issues.total",
"source": "GET /search/issues?q=repo:nexu-io/open-design+is:issue",
"field": "total_count"
},
{
"path": "metrics.pullRequests.total",
"source": "GET /search/issues?q=repo:nexu-io/open-design+is:pr",
"field": "total_count"
},
{
"path": "charts.starGrowth",
"source": "estimated",
"note": "GitHub REST does not expose full historical star deltas; heights are display-normalized from available recent growth signals."
},
{
"path": "charts.forkTrend",
"source": "synthetic",
"note": "Generated as normalized 0-100% chart heights for visual trend preview."
}
],
"transformations": [
{
"step": "Data normalization",
"description": "Converted GitHub API responses to compact dashboard format",
"operations": [
"Formatted large numbers with comma separators",
"Estimated star and fork growth trends and marked them as non-auditable estimates",
"Extracted top 5 contributors by contribution count",
"Selected most recent 5 issues and pull requests",
"Generated normalized chart-height values for star growth and fork trends"
]
},
{
"step": "Privacy filtering",
"description": "Applied safe data practices",
"operations": [
"Preserved only public GitHub profile information",
"Used public avatar URLs",
"Excluded sensitive or internal repository metadata"
]
}
],
"notes": [
"Current repository totals are sourced from public GitHub API endpoints; growth series are explicitly estimated or synthetic as documented in fields[].",
"No authentication tokens or credentials stored",
"Star growth is estimated from available recent growth signals, not directly auditable from GitHub REST alone",
"Fork growth is synthetic for preview purposes",
"Chart heights are normalized percentages for responsive display"
],
"refreshPolicy": {
"recommended": "hourly",
"minInterval": "15 minutes",
"note": "GitHub API has rate limits; recommend caching for production use"
}
}
@@ -0,0 +1,473 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{data.repository.name}} — GitHub Dashboard</title>
<style>
:root {
--canvas: #f2f2f0;
--surface: #ffffff;
--surface-muted: #fafaf9;
--border: #ececea;
--border-strong: #e2e2df;
--text-primary: #0a0a0a;
--text-secondary: #6b6b6b;
--text-tertiary: #9a9a95;
--active-bg: #e6f4ea;
--active-fg: #1f8a4c;
--pending-bg: #fff6d6;
--pending-fg: #9a7b12;
--feature-bg: #dcebff;
--feature-fg: #2f66c9;
--danger-bg: #fdecec;
--danger-fg: #c0392b;
--green: #34a853;
--amber: #e8a23a;
--red: #d94a3d;
--shadow: 0 1px 2px rgba(10, 10, 10, .04), 0 1px 1px rgba(10, 10, 10, .02);
--radius-card: 16px;
--radius-tile: 12px;
--radius-control: 8px;
font-family: Geist, Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-feature-settings: "tnum", "lnum";
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--canvas); color: var(--text-primary); }
.shell { min-height: 100vh; padding: 16px; display: grid; grid-template-columns: 256px 1fr; gap: 16px; }
.sidebar, .main { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); }
.sidebar { padding: 16px; display: flex; flex-direction: column; gap: 22px; }
.brand { height: 48px; display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 15px; }
.logo { width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg, #2f66c9, #5a8ded); color: white; display: grid; place-items: center; font-size: 18px; font-weight: 600; }
.group-label { display: flex; align-items: center; gap: 8px; color: var(--text-tertiary); font-size: 11px; font-weight: 500; letter-spacing: .02em; text-transform: uppercase; margin-bottom: 8px; margin-top: 4px; }
.dot-ring { width: 14px; height: 14px; border: 1.5px dashed var(--text-tertiary); border-radius: 999px; flex: none; }
.nav { display: grid; gap: 4px; }
.nav-item { min-height: 36px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-radius: var(--radius-control); padding: 8px 10px; color: var(--text-secondary); font-size: 13px; text-decoration: none; transition: all .15s; }
.nav-item:hover { background: var(--surface-muted); }
.nav-item.active { background: var(--surface-muted); color: var(--text-primary); font-weight: 500; }
.count { border-radius: 999px; padding: 2px 8px; background: var(--surface-muted); color: var(--text-secondary); font-size: 11px; font-weight: 500; }
.main { overflow: hidden; }
.topbar { height: 48px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; padding: 0 20px; font-size: 14px; }
.topbar-title { display: flex; align-items: center; gap: 10px; }
.topbar-icon { width: 20px; height: 20px; opacity: .7; }
.content { padding: 24px 28px 32px; display: grid; gap: 24px; }
.page-head { display: flex; align-items: end; justify-content: space-between; gap: 16px; }
h1 { margin: 0; font-size: 22px; line-height: 1.25; font-weight: 600; letter-spacing: -.005em; }
.subtitle { margin-top: 6px; color: var(--text-secondary); font-size: 14px; line-height: 1.4; }
.actions { display: flex; gap: 8px; align-items: center; }
button, .button { border: 1px solid var(--border-strong); background: var(--surface); border-radius: var(--radius-control); padding: 8px 14px; color: var(--text-primary); font: inherit; font-size: 12px; cursor: pointer; transition: all .15s; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; }
button:hover, .button:hover { background: var(--surface-muted); }
button.primary, .button.primary { background: var(--text-primary); color: #fff; border-color: var(--text-primary); }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 20px; }
.tabs { display: inline-flex; background: var(--surface-muted); border-radius: 12px; padding: 4px; gap: 4px; }
.tab { padding: 6px 12px; border-radius: var(--radius-control); color: var(--text-secondary); font-size: 12px; cursor: pointer; transition: all .15s; border: 1px solid transparent; text-decoration: none; }
.tab.active { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); box-shadow: var(--shadow); }
.kpis { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px; }
.kpi { min-height: 84px; border: 1px solid var(--border); border-radius: var(--radius-tile); background: var(--surface-muted); padding: 14px; display: flex; flex-direction: column; }
.kpi-top { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.glyph { width: 28px; height: 28px; border-radius: 8px; background: var(--surface); border: 1px solid var(--border); display: inline-grid; place-items: center; box-shadow: var(--shadow); font-size: 14px; flex: none; }
.kpi-label { color: var(--text-secondary); font-size: 13px; }
.kpi-num { font-size: 28px; font-weight: 600; line-height: 1.1; margin-top: auto; }
.kpi-trend { font-size: 12px; color: var(--green); margin-top: 4px; font-weight: 500; }
.kpi-trend.down { color: var(--text-tertiary); }
.grid { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
.grid-full { display: grid; grid-template-columns: 1fr; gap: 16px; }
.card-title { display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 600; margin-bottom: 16px; }
.tick-rule { height: 24px; margin: 8px 0 16px; background: repeating-linear-gradient(90deg, var(--border) 0 1px, transparent 1px 8px); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 760px; }
th { text-align: left; padding: 10px 12px; color: var(--text-tertiary); font-size: 11px; font-weight: 500; letter-spacing: .01em; background: var(--surface-muted); text-transform: uppercase; }
td { padding: 14px 12px; border-top: 1px solid var(--border); color: var(--text-secondary); }
td strong { color: var(--text-primary); font-weight: 500; }
.mono { font-family: "Geist Mono", "JetBrains Mono", ui-monospace, monospace; font-size: 12px; }
.pill { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px; padding: 3px 10px; font-size: 11px; font-weight: 500; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
.active-pill { background: var(--active-bg); color: var(--active-fg); }
.pending-pill { background: var(--pending-bg); color: var(--pending-fg); }
.feature-pill { background: var(--feature-bg); color: var(--feature-fg); }
.danger-pill { background: var(--danger-bg); color: var(--danger-fg); }
.avatar { width: 28px; height: 28px; border-radius: 999px; border: 1px solid var(--border); background: linear-gradient(135deg, #dcecff, #f8eadb); display: inline-grid; place-items: center; color: var(--text-secondary); font-size: 11px; font-weight: 500; margin-right: 8px; overflow: hidden; flex: none; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.contrib { display: grid; gap: 10px; }
.person { display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--border); border-radius: 12px; padding: 12px; background: var(--surface-muted); }
.person-info { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.person-name { font-weight: 500; font-size: 13px; color: var(--text-primary); }
.person-role { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.chart-container { position: relative; height: 180px; margin-top: 16px; }
.chart { width: 100%; height: 100%; }
.chart-bars { display: flex; align-items: flex-end; justify-content: space-between; height: 100%; gap: 8px; }
.bar { flex: 1; background: linear-gradient(180deg, var(--green) 0%, #2d8a47 100%); border-radius: 4px 4px 0 0; min-height: 4px; position: relative; transition: all .2s; }
.bar:hover { opacity: .85; }
.bar-label { position: absolute; bottom: -22px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--text-tertiary); white-space: nowrap; }
.trend-line { display: flex; align-items: flex-end; height: 100%; gap: 2px; }
.trend-point { flex: 1; background: var(--feature-fg); border-radius: 2px 2px 0 0; min-height: 2px; }
.issue-row { display: flex; align-items: start; gap: 12px; padding: 14px 12px; border-top: 1px solid var(--border); }
.issue-row:first-child { border-top: none; }
.issue-icon { width: 20px; height: 20px; border-radius: 999px; background: var(--active-bg); color: var(--active-fg); display: grid; place-items: center; font-size: 11px; flex: none; margin-top: 2px; }
.issue-icon.pr { background: var(--feature-bg); color: var(--feature-fg); }
.issue-content { flex: 1; min-width: 0; }
.issue-title { font-size: 14px; font-weight: 500; color: var(--text-primary); margin-bottom: 4px; }
.issue-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-tertiary); margin-top: 6px; }
.issue-meta span { display: flex; align-items: center; gap: 4px; }
.provenance-note { color: var(--text-tertiary); font-size: 12px; line-height: 1.5; padding: 4px 2px; }
@media (max-width: 980px) {
.shell { grid-template-columns: 1fr; }
.kpis { grid-template-columns: repeat(2, 1fr); }
.grid { grid-template-columns: 1fr; }
.sidebar { min-height: auto; }
}
</style>
</head>
<body>
<div class="shell">
<aside class="sidebar" data-od-id="sidebar">
<div class="brand">
<div class="logo">OD</div>
<span>{{data.repository.name}}</span>
</div>
<section>
<div class="group-label"><span class="dot-ring"></span> Repository</div>
<div class="nav">
<a href="#overview" class="nav-item active">Overview</a>
<a href="#activity" class="nav-item">Activity <span class="count">{{data.metrics.pullRequests.open}}</span></a>
<a href="#contributors" class="nav-item">Contributors <span class="count">{{data.metrics.contributors.total}}</span></a>
<a href="#community" class="nav-item">Community</a>
</div>
</section>
<section>
<div class="group-label"><span class="dot-ring"></span> Metrics</div>
<div class="nav">
<a href="#growth-chart" class="nav-item">Stars Growth</a>
<a href="#fork-trend" class="nav-item">Fork Activity</a>
<a href="#activity" class="nav-item">Issue Trends</a>
<a href="#activity" class="nav-item">PR Velocity</a>
</div>
</section>
<section>
<div class="group-label"><span class="dot-ring"></span> Quick Links</div>
<div class="nav">
<a href="{{data.repository.url}}" class="nav-item">GitHub Repository</a>
<a href="{{data.repository.url}}#readme" class="nav-item">Documentation</a>
<a href="{{data.repository.url}}/discussions" class="nav-item">Discussions</a>
</div>
</section>
</aside>
<main class="main">
<div class="topbar" data-od-id="topbar">
<div class="topbar-title">
<svg class="topbar-icon" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
<span>{{data.repository.name}} Dashboard</span>
</div>
<div class="actions">
<button>{{data.metadata.lastUpdated}}</button>
<button>⟳ Refresh</button>
</div>
</div>
<div class="content">
<header class="page-head" data-od-id="repo-header" id="overview">
<div>
<h1>{{data.repository.fullName}}</h1>
<div class="subtitle">{{data.repository.description}}</div>
</div>
<div class="actions">
<a class="button" href="{{data.repository.url}}">View on GitHub →</a>
</div>
</header>
<section class="card">
<div class="tabs" role="tablist" aria-label="Dashboard sections">
<a href="#overview" class="tab active" role="tab">Metrics</a>
<a href="#activity" class="tab" role="tab">Activity</a>
<a href="#contributors" class="tab" role="tab">Contributors</a>
<a href="#community" class="tab" role="tab">Community</a>
</div>
<div class="kpis" style="margin-top:16px" data-od-id="kpi-strip">
<div class="kpi">
<div class="kpi-top">
<span class="glyph">⭐</span>
<span class="kpi-label">Stars</span>
</div>
<div class="kpi-num">{{data.metrics.stars.total}}</div>
<div class="kpi-trend">+{{data.metrics.stars.growth}} this week</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">⑂</span>
<span class="kpi-label">Forks</span>
</div>
<div class="kpi-num">{{data.metrics.forks.total}}</div>
<div class="kpi-trend">+{{data.metrics.forks.growth}} this week</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">◎</span>
<span class="kpi-label">Contributors</span>
</div>
<div class="kpi-num">{{data.metrics.contributors.total}}</div>
<div class="kpi-trend">{{data.metrics.contributors.active}} active</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">◌</span>
<span class="kpi-label">Open Issues</span>
</div>
<div class="kpi-num">{{data.metrics.issues.open}}</div>
<div class="kpi-trend down">{{data.metrics.issues.total}} total</div>
</div>
<div class="kpi">
<div class="kpi-top">
<span class="glyph">↑</span>
<span class="kpi-label">Pull Requests</span>
</div>
<div class="kpi-num">{{data.metrics.pullRequests.open}}</div>
<div class="kpi-trend down">{{data.metrics.pullRequests.total}} total</div>
</div>
</div>
</section>
<section class="grid">
<div class="card" data-od-id="growth-chart" id="growth-chart">
<div class="card-title"><span class="dot-ring"></span> Star Growth (Last 30 Days)</div>
<div class="chart-container">
<div class="chart-bars">
<div class="bar" style="height: {{data.charts.starGrowth.0.height}}%;">
<span class="bar-label">{{data.charts.starGrowth.0.label}}</span>
</div>
<div class="bar" style="height: {{data.charts.starGrowth.1.height}}%;">
<span class="bar-label">{{data.charts.starGrowth.1.label}}</span>
</div>
<div class="bar" style="height: {{data.charts.starGrowth.2.height}}%;">
<span class="bar-label">{{data.charts.starGrowth.2.label}}</span>
</div>
<div class="bar" style="height: {{data.charts.starGrowth.3.height}}%;">
<span class="bar-label">{{data.charts.starGrowth.3.label}}</span>
</div>
</div>
</div>
</div>
<aside class="card" data-od-id="contributors" id="contributors">
<div class="card-title"><span class="dot-ring"></span> Top Contributors</div>
<div class="tick-rule"></div>
<div class="contrib">
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="{{data.contributors.0.avatar}}" alt="{{data.contributors.0.login}}" />
</div>
<div>
<div class="person-name">{{data.contributors.0.login}}</div>
<div class="person-role">{{data.contributors.0.contributions}} contributions</div>
</div>
</div>
<span class="pill pending-pill">{{data.contributors.0.contributions}}</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="{{data.contributors.1.avatar}}" alt="{{data.contributors.1.login}}" />
</div>
<div>
<div class="person-name">{{data.contributors.1.login}}</div>
<div class="person-role">{{data.contributors.1.contributions}} contributions</div>
</div>
</div>
<span class="pill pending-pill">{{data.contributors.1.contributions}}</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="{{data.contributors.2.avatar}}" alt="{{data.contributors.2.login}}" />
</div>
<div>
<div class="person-name">{{data.contributors.2.login}}</div>
<div class="person-role">{{data.contributors.2.contributions}} contributions</div>
</div>
</div>
<span class="pill pending-pill">{{data.contributors.2.contributions}}</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="{{data.contributors.3.avatar}}" alt="{{data.contributors.3.login}}" />
</div>
<div>
<div class="person-name">{{data.contributors.3.login}}</div>
<div class="person-role">{{data.contributors.3.contributions}} contributions</div>
</div>
</div>
<span class="pill pending-pill">{{data.contributors.3.contributions}}</span>
</div>
<div class="person">
<div class="person-info">
<div class="avatar">
<img src="{{data.contributors.4.avatar}}" alt="{{data.contributors.4.login}}" />
</div>
<div>
<div class="person-name">{{data.contributors.4.login}}</div>
<div class="person-role">{{data.contributors.4.contributions}} contributions</div>
</div>
</div>
<span class="pill pending-pill">{{data.contributors.4.contributions}}</span>
</div>
</div>
</aside>
</section>
<section class="grid-full">
<div class="card" data-od-id="activity" id="activity">
<div class="card-title"><span class="dot-ring"></span> Recent Activity</div>
<div class="table-scroll">
<table>
<thead>
<tr>
<th style="width: 50px;">Type</th>
<th>Title</th>
<th style="width: 120px;">Status</th>
<th style="width: 140px;">Author</th>
<th style="width: 100px;">Updated</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span class="issue-icon {{data.recentActivity.0.typeClass}}">{{data.recentActivity.0.typeText}}</span>
</td>
<td><strong>{{data.recentActivity.0.title}}</strong></td>
<td>
<span class="pill {{data.recentActivity.0.labelClass}}">{{data.recentActivity.0.label}}</span>
</td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;">
<img src="{{data.recentActivity.0.authorAvatar}}" alt="{{data.recentActivity.0.author}}" />
</span>
<span>{{data.recentActivity.0.author}}</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">{{data.recentActivity.0.updated}}</td>
</tr>
<tr>
<td><span class="issue-icon {{data.recentActivity.1.typeClass}}">{{data.recentActivity.1.typeText}}</span></td>
<td><strong>{{data.recentActivity.1.title}}</strong></td>
<td><span class="pill {{data.recentActivity.1.labelClass}}">{{data.recentActivity.1.label}}</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="{{data.recentActivity.1.authorAvatar}}" alt="{{data.recentActivity.1.author}}" /></span>
<span>{{data.recentActivity.1.author}}</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">{{data.recentActivity.1.updated}}</td>
</tr>
<tr>
<td><span class="issue-icon {{data.recentActivity.2.typeClass}}">{{data.recentActivity.2.typeText}}</span></td>
<td><strong>{{data.recentActivity.2.title}}</strong></td>
<td><span class="pill {{data.recentActivity.2.labelClass}}">{{data.recentActivity.2.label}}</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="{{data.recentActivity.2.authorAvatar}}" alt="{{data.recentActivity.2.author}}" /></span>
<span>{{data.recentActivity.2.author}}</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">{{data.recentActivity.2.updated}}</td>
</tr>
<tr>
<td><span class="issue-icon {{data.recentActivity.3.typeClass}}">{{data.recentActivity.3.typeText}}</span></td>
<td><strong>{{data.recentActivity.3.title}}</strong></td>
<td><span class="pill {{data.recentActivity.3.labelClass}}">{{data.recentActivity.3.label}}</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="{{data.recentActivity.3.authorAvatar}}" alt="{{data.recentActivity.3.author}}" /></span>
<span>{{data.recentActivity.3.author}}</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">{{data.recentActivity.3.updated}}</td>
</tr>
<tr>
<td><span class="issue-icon {{data.recentActivity.4.typeClass}}">{{data.recentActivity.4.typeText}}</span></td>
<td><strong>{{data.recentActivity.4.title}}</strong></td>
<td><span class="pill {{data.recentActivity.4.labelClass}}">{{data.recentActivity.4.label}}</span></td>
<td>
<div style="display: flex; align-items: center;">
<span class="avatar" style="margin-right: 8px;"><img src="{{data.recentActivity.4.authorAvatar}}" alt="{{data.recentActivity.4.author}}" /></span>
<span>{{data.recentActivity.4.author}}</span>
</div>
</td>
<td class="mono" style="color: var(--text-tertiary);">{{data.recentActivity.4.updated}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="grid">
<div class="card" id="fork-trend">
<div class="card-title"><span class="dot-ring"></span> Fork Trend (Last 30 Days)</div>
<div class="chart-container">
<div class="trend-line">
<div class="trend-point" style="height: {{data.charts.forkTrend.0.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.1.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.2.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.3.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.4.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.5.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.6.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.7.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.8.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.9.height}}%;"></div>
<div class="trend-point" style="height: {{data.charts.forkTrend.10.height}}%;"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-title"><span class="dot-ring"></span> Repository Stats</div>
<div class="tick-rule"></div>
<table>
<tbody>
<tr>
<td><strong>Primary Language</strong></td>
<td style="text-align: right;">{{data.repository.language}}</td>
</tr>
<tr>
<td><strong>License</strong></td>
<td style="text-align: right;">{{data.repository.license}}</td>
</tr>
<tr>
<td><strong>Created</strong></td>
<td style="text-align: right;">{{data.repository.created}}</td>
</tr>
<tr>
<td><strong>Last Updated</strong></td>
<td style="text-align: right;">{{data.repository.lastUpdated}}</td>
</tr>
<tr>
<td><strong>Repository Size</strong></td>
<td style="text-align: right;">{{data.repository.size}}</td>
</tr>
</tbody>
</table>
</div>
</section>
<footer class="provenance-note" data-od-id="provenance">
Updated {{data.metadata.lastUpdated}} from public GitHub API fields. Estimated trend data is documented in provenance.json.
</footer>
</div>
</main>
</div>
</body>
</html>