Add Stargazers plugin (#37)

This commit is contained in:
Simon Lecoq
2021-01-05 12:39:04 +01:00
committed by GitHub
parent 97c28b88fc
commit b06d9ff898
15 changed files with 295 additions and 12 deletions

View File

@@ -6,7 +6,30 @@
-->
**Pull request description**
<!-- A clear and concise description of what your pull request implements or fixs. -->
<!--
A clear and concise description of what your pull request implements or fixs.
Please check the following:
> Documentation update
- Check spelling
- Respect current formatting
> New plugin
- Created new plugin in /source/plugins/ with index.mjs as entry point
- Added tests in /tests/metrics.test.js
- Added mocked data if needed (required for all APIs which requires a token or limited in requests)
- Updated action.yml with new plugin options
- Updated /source/app/action/index.mjs to retrieve plugin options with correct typing
- Updated /source/web/statics/* to support new plugin options
- Updated /settings.example.json with new plugin name
- Updated README.md to explain plugin features
> Code editions
- Ensure retro-compatibility with previous versions
- Unless feature is not released yet
- Respect current formatting
-->
**Additional context and screenshots**
<!-- Add any other context or screenshots about your pull request here. -->

View File

@@ -147,20 +147,32 @@ But there's more with [plugins](https://github.com/lowlighter/metrics/tree/maste
</td>
</tr>
<tr>
<th><a href="https://github.com/lowlighter/metrics#-stargazers">✨ Stargazers evolution</a></th>
<th><a href="https://github.com/lowlighter/metrics#-stars">🌟 Recently starred repositories</a></th>
<th><a href="https://github.com/lowlighter/metrics#%EF%B8%8F-base-content">🗃️ Header special features</a></th>
</tr>
<tr>
<td>
<a href="https://github.com/lowlighter/metrics#-stargazers">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.stargazers.svg" alt="" width="400">
</a>
</td>
<td>
<a href="https://github.com/lowlighter/metrics#-stars">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.stars.svg" alt="" width="400">
</a>
</td>
</tr>
<tr>
<th><a href="https://github.com/lowlighter/metrics#%EF%B8%8F-base-content">🗃️ Header special features</a></th>
<th></th>
</tr>
<tr>
<td>
<a href="https://github.com/lowlighter/metrics#%EF%B8%8F-base-content">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.header.svg" alt="" width="400">
</a>
</td>
<td></td>
</tr>
<tr>
<td colspan="2" align="center">
@@ -516,6 +528,7 @@ Used template defaults to the `classic` one.
<th><span title="Habits">💡</span></th>
<th><span title="Gists">🎫</span></th>
<th><span title="Stars">🌟</span></th>
<th><span title="Stargazers">✨</span></th>
</tr>
<tr>
<th>Classic</th>
@@ -534,6 +547,7 @@ Used template defaults to the `classic` one.
<td>✔️</td>
<td>✔️</td>
<td><span title="Available on master">✔️<sup>M</sup></span></td>
<td><span title="Available on master">✔️<sup>M</sup></span></td>
</tr>
<tr>
<th>Terminal</th>
@@ -552,6 +566,7 @@ Used template defaults to the `classic` one.
<td>❌</td>
<td>✔️</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<th>Repository<sup>R</sup></th>
@@ -570,6 +585,7 @@ Used template defaults to the `classic` one.
<td>❌</td>
<td>❌</td>
<td>❌</td>
<td><span title="Available on master">✔️<sup>M</sup></span></td>
</tr>
</table>
@@ -1281,6 +1297,29 @@ Add the following to your workflow :
</details>
### ✨ Stargazers
🚧 This feature is available on @master
The *stargazers* plugin displays your stargazers evolution across all of your repositories over the last two weeks.
![Stargazers plugin](https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.stargazers.svg)
<details>
<summary>💬 About</summary>
It will consume additional GitHub requests per repository per set of 100 stargazers.
Add the following to your workflow :
```yaml
- uses: lowlighter/metrics@latest
with:
# ... other options
plugin_stargazers: yes
```
</details>
### 🔧 Other options
A few additional options are available.

View File

@@ -357,6 +357,12 @@ inputs:
description: Number of recently starred repositories to display
default: 4
# Display stargazers evolution over the last two weeks
# It shows total stargazers along with increase rate per day
plugin_stagazers:
description: Display stargazers evolution over the last two weeks
default: no
# ====================================================================================
# Options below are mostly used for testing

View File

@@ -60,7 +60,10 @@
"token":null, "//":"Twitter token (required when enabled)"
},
"stars":{ "//":"Stars plugin",
"enabled":true, "//":"Enable or disable recently starred repositories display"
"enabled":false, "//":"Enable or disable recently starred repositories display"
},
"stargazers":{ "//":"Stargazers plugin",
"enabled":false, "//":"Enable or disable stargazers charts display"
}
}
}

View File

@@ -140,6 +140,7 @@
projects:{enabled:input.bool("plugin_projects")},
tweets:{enabled:input.bool("plugin_tweets")},
stars:{enabled:input.bool("plugin_stars")},
stargazers:{enabled:input.bool("plugin_stargazers")},
}
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key))

View File

@@ -296,6 +296,26 @@
}
})
}
//Stargazers query
if (/^query Stargazers /.test(query)) {
console.debug(`metrics/compute/mocks > mocking graphql api result > Stargazers`)
return /after: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/m.test(query) ? ({
repository:{
stargazers:{
edges:[],
}
}
}) : ({
repository:{
stargazers:{
edges:new Array(Math.ceil(20+80*Math.random())).fill(null).map(() => ({
starredAt:new Date(Date.now()-Math.floor(30*Math.random())*24*60*60*1000).toISOString(),
cursor:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}))
}
}
})
}
//Unmocked call
return target(...args)
}

View File

@@ -47,6 +47,7 @@
projects:"🗂️ Projects",
tweets:"🐤 Latest tweets",
stars:"🌟 Recently starred repositories",
stargazers:"✨ Stargazers over last weeks",
"base.header":`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 8a8 8 0 1116 0v5.25a.75.75 0 01-1.5 0V8a6.5 6.5 0 10-13 0v5.25a.75.75 0 01-1.5 0V8zm5.5 4.25a.75.75 0 01.75-.75h3.5a.75.75 0 010 1.5h-3.5a.75.75 0 01-.75-.75zM3 6.75C3 5.784 3.784 5 4.75 5h6.5c.966 0 1.75.784 1.75 1.75v1.5A1.75 1.75 0 0111.25 10h-6.5A1.75 1.75 0 013 8.25v-1.5zm1.47-.53a.75.75 0 011.06 0l.97.97.97-.97a.75.75 0 011.06 0l.97.97.97-.97a.75.75 0 111.06 1.06l-1.5 1.5a.75.75 0 01-1.06 0L8 7.81l-.97.97a.75.75 0 01-1.06 0l-1.5-1.5a.75.75 0 010-1.06z"></path></svg>
Header`,

View File

@@ -17,8 +17,10 @@
<!-- Title -->
<h1 class="title">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" 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"></path></svg>
<a href="https://github.com/lowlighter/metrics">Metrics v{{ version }}</a>
<a href="https://github.com/lowlighter/metrics">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" 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"></path></svg>
Metrics v{{ version }}
</a>
</h1>
<!-- Tabs -->
@@ -29,14 +31,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 1.75A.75.75 0 01.75 1h4.253c1.227 0 2.317.59 3 1.501A3.744 3.744 0 0111.006 1h4.245a.75.75 0 01.75.75v10.5a.75.75 0 01-.75.75h-4.507a2.25 2.25 0 00-1.591.659l-.622.621a.75.75 0 01-1.06 0l-.622-.621A2.25 2.25 0 005.258 13H.75a.75.75 0 01-.75-.75V1.75zm8.755 3a2.25 2.25 0 012.25-2.25H14.5v9h-3.757c-.71 0-1.4.201-1.992.572l.004-7.322zm-1.504 7.324l.004-5.073-.002-2.253A2.25 2.25 0 005.003 2.5H1.5v9h3.757a3.75 3.75 0 011.994.574z"></path></svg>
Overview
</div>
<div @click="tab = 'markdown'" class="tab" :class="{active:tab === 'markdown', disabled:!user}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4 1.75C4 .784 4.784 0 5.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v8.586A1.75 1.75 0 0114.25 15h-9a.75.75 0 010-1.5h9a.25.25 0 00.25-.25V6h-2.75A1.75 1.75 0 0110 4.25V1.5H5.75a.25.25 0 00-.25.25v2.5a.75.75 0 01-1.5 0v-2.5zm7.5-.188V4.25c0 .138.112.25.25.25h2.688a.252.252 0 00-.011-.013l-2.914-2.914a.272.272 0 00-.013-.011zM5.72 6.72a.75.75 0 000 1.06l1.47 1.47-1.47 1.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM3.28 7.78a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 001.06-1.06L1.81 9.25l1.47-1.47z"></path></svg>
Markdown
</div>
<div @click="tab = 'action'" class="tab" :class="{active:tab === 'action', disabled:!user}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>
Action
</div>
<div @click="tab = 'markdown'" class="tab" :class="{active:tab === 'markdown', disabled:!user}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4 1.75C4 .784 4.784 0 5.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v8.586A1.75 1.75 0 0114.25 15h-9a.75.75 0 010-1.5h9a.25.25 0 00.25-.25V6h-2.75A1.75 1.75 0 0110 4.25V1.5H5.75a.25.25 0 00-.25.25v2.5a.75.75 0 01-1.5 0v-2.5zm7.5-.188V4.25c0 .138.112.25.25.25h2.688a.252.252 0 00-.011-.013l-2.914-2.914a.272.272 0 00-.013-.011zM5.72 6.72a.75.75 0 000 1.06l1.47 1.47-1.47 1.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM3.28 7.78a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 001.06-1.06L1.81 9.25l1.47-1.47z"></path></svg>
Markdown
</div>
</div>
</nav>
@@ -242,7 +244,7 @@
</div>
<!-- Overview -->
<section class="preview" v-if="tab == 'overview'">
Once generated, click on tabs above to see the code to embed them on your real readme !
Once generated, click on tabs above to see the code to embed them on your real readme !<br>
<template v-if="generated.content">
<img class="metrics" :src="generated.content" alt="metrics">
</template>

View File

@@ -10,7 +10,6 @@
background-color: var(--color-bg-canvas);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Title */
.title {
@@ -36,6 +35,7 @@
display: flex;
border-bottom: 1px solid var(--color-border-secondary);
flex-shrink: 0;
overflow-x: auto;
}
nav .tab {
flex-shrink: 0;
@@ -287,6 +287,7 @@
}
nav {
margin: 32px 0 24px;
overflow: hidden;
}
nav .left {
display: block;
@@ -297,6 +298,7 @@
main {
height: 100vh;
width: 100vw;
overflow: hidden;
}
.avatar {
display: flex;

View File

@@ -0,0 +1,65 @@
//Setup
export default async function ({login, graphql, data, q, queries, imports}, {enabled = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
if ((!enabled)||(!q.stargazers))
return null
//Retrieve stargazers from graphql api
console.debug(`metrics/compute/${login}/plugins > stargazers > querying api`)
const repositories = data.user.repositories.nodes.map(({name}) => name).slice(0, 2)
const dates = []
for (const repository of repositories) {
//Iterate through stargazers
console.debug(`metrics/compute/${login}/plugins > stargazers > retrieving stargazers of ${repository}`)
let cursor = null
let pushed = 0
do {
console.debug(`metrics/compute/${login}/plugins > stargazers > retrieving stargazers of ${repository} after ${cursor}`)
const {repository:{stargazers:{edges}}} = await graphql(queries.stargazers({login, repository, after:cursor ? `after: "${cursor}"` : ""}))
cursor = edges?.[edges?.length-1]?.cursor
console.log(edges)
dates.push(...edges.map(({starredAt}) => new Date(starredAt)))
pushed = edges.length
} while ((pushed)&&(cursor))
//Limit repositories
console.debug(`metrics/compute/${login}/plugins > stargazers > loaded ${dates.length} stargazers for ${repository}`)
}
console.debug(`metrics/compute/${login}/plugins > stargazers > loaded ${dates.length} stargazers in total`)
//Compute stargazers increments
const days = 14
const increments = {dates:Object.fromEntries([...new Array(days).fill(null).map((_, i) => [new Date(Date.now()-i*24*60*60*1000).toISOString().slice(0, 10), 0]).reverse()]), max:NaN, min:NaN}
dates
.map(date => date.toISOString().slice(0, 10))
.filter(date => date in increments.dates)
.map(date => increments.dates[date]++)
increments.min = Math.min(...Object.values(increments.dates))
increments.max = Math.max(...Object.values(increments.dates))
//Compute total stargazers
let stargazers = data.computed.repositories.stargazers
const total = {dates:{...increments.dates}, max:NaN, min:NaN}
{
const dates = Object.keys(total.dates)
for (let i = dates.length-1; i >= 0; i--) {
const date = dates[i], tomorrow = dates[i+1]
stargazers -= (increments.dates[tomorrow] ?? 0)
total.dates[date] = stargazers
}
}
total.min = Math.min(...Object.values(total.dates))
total.max = Math.max(...Object.values(total.dates))
//Format values
for (const date in increments.dates)
increments.dates[date] = `${increments.dates[date] > 0 ? "+" : ""}${imports.format(increments.dates[date])}`
for (const date in total.dates)
total.dates[date] = imports.format(total.dates[date])
//Months name
const months = ["", "Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."]
//Results
return {total, increments, months}
}
//Handle errors
catch (error) {
throw {error:{message:"An error occured", instance:error}}
}
}

View File

@@ -0,0 +1,10 @@
query Stargazers {
repository(name: "$repository", owner: "$login") {
stargazers($after first: 100, orderBy: {field: STARRED_AT, direction: ASC}) {
edges {
starredAt
cursor
}
}
}
}

View File

@@ -847,6 +847,54 @@
</section>
<% } %>
<% if (plugins.stargazers) { %>
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
Stargazers over the last two weeks
</h2>
<% if (plugins.stargazers.error) { %>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.stargazers.error.message %>
</div>
<% } else { %>
<div class="row">
<section class="column chart">
<h3>Total stargazers</h3>
<div class="chart-bars">
<% { let previous = null; for (const [date, value] of Object.entries(plugins.stargazers.total.dates)) { const p = 0.05+0.95*(value-plugins.stargazers.total.min)/(plugins.stargazers.total.max-plugins.stargazers.total.min); const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= (value-(previous ?? 0)) ? value : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>
<div class="bottom"><%= plugins.stargazers.months[m] %></div>
<% } %>
</div>
<% previous = value } } %>
</div>
</section>
<section class="column chart">
<h3>New stargazers per day</h3>
<div class="chart-bars">
<% { let previous = true; for (const [date, value] of Object.entries(plugins.stargazers.increments.dates)) { const p = value/plugins.stargazers.increments.max; const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= value != 0 ? value : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>
<div class="bottom"><%= plugins.stargazers.months[m] %></div>
<% } %>
</div>
<% previous = value } } %>
</div>
</section>
</div>
<% } %>
</section>
<% } %>
<% if (base.metadata) { %>
<footer>
<span>These metrics <%= !computed.token.scopes.includes("repo") ? "does not include all" : "includes" %> private contributions<% if ((config.timezone?.name)&&(!config.timezone?.error)) { %>, timezone <%= config.timezone.name %><% } %></span>

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -79,6 +79,9 @@
.fill-width {
width: 100%;
}
.margin-bottom {
margin-bottom: 16px;
}
/* User avatar */
.avatar {
@@ -367,6 +370,15 @@
min-width: 30%;
}
.chart-bars .entry {
position: relative;
}
.chart-bars .entry .bottom {
position: absolute;
top: 100%;
}
.chart-bars.horizontal .bar {
height: 7px;
width: auto;

View File

@@ -207,6 +207,54 @@
<% } %>
<% if (plugins.stargazers) { %>
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
Stargazers over the last two weeks
</h2>
<% if (plugins.stargazers.error) { %>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.stargazers.error.message %>
</div>
<% } else { %>
<div class="row margin-bottom">
<section class="column chart">
<h3>Total stargazers</h3>
<div class="chart-bars">
<% { let previous = null; for (const [date, value] of Object.entries(plugins.stargazers.total.dates)) { const p = 0.05+0.95*(value-plugins.stargazers.total.min)/(plugins.stargazers.total.max-plugins.stargazers.total.min); const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= (value-(previous ?? 0)) ? value : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>
<div class="bottom"><%= plugins.stargazers.months[m] %></div>
<% } %>
</div>
<% previous = value } } %>
</div>
</section>
<section class="column chart">
<h3>New stargazers per day</h3>
<div class="chart-bars">
<% { let previous = true; for (const [date, value] of Object.entries(plugins.stargazers.increments.dates)) { const p = value/plugins.stargazers.increments.max; const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= value != 0 ? value : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>
<div class="bottom"><%= plugins.stargazers.months[m] %></div>
<% } %>
</div>
<% previous = value } } %>
</div>
</section>
</div>
<% } %>
</section>
<% } %>
<% if (base.metadata) { %>
<footer>
<span>Last updated <%= new Date().toGMTString() %> with lowlighter/metrics@<%= meta.version %></span>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -242,6 +242,9 @@
["Stars plugin (default)", {
plugin_stars:true,
}, {skip:["terminal"]}],
["Stargazers plugin (default)", {
plugin_stargazers:true,
}, {skip:["terminal"]}],
]
//Tests run