Full plugins and server redesign
- Languages, issues and pr are now plugins (but enabled by default for retro-compatibility) - Query parameters are now parsed correctly - Redesigned server index with vue.js
This commit is contained in:
BIN
.github/readme/imgs/plugin_followup.png
vendored
Normal file
BIN
.github/readme/imgs/plugin_followup.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
BIN
.github/readme/imgs/plugin_languages.png
vendored
Normal file
BIN
.github/readme/imgs/plugin_languages.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
70
README.md
70
README.md
@@ -501,6 +501,74 @@ Add the following to your `settings.json` and pass `?habits=1` in url when gener
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
#### ✒️ Follow-up
|
||||||
|
|
||||||
|
The *follow-up* plugin allows you to compute the ratio of opened/closed issues and the ratio of opened/merged pull requests on your repositories, which shows whether most of them are maintened or not.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
|
This plugin is enabled by default. To disable it, explicitly opt-out.
|
||||||
|
|
||||||
|
##### Setup with GitHub actions
|
||||||
|
|
||||||
|
Add the following to your workflow :
|
||||||
|
```yaml
|
||||||
|
- uses: lowlighter/metrics@latest
|
||||||
|
with:
|
||||||
|
# ... other options
|
||||||
|
plugin_followup: yes
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Setup in your own instance
|
||||||
|
|
||||||
|
Add the following to your `settings.json` and pass `?followup=1` in url when generating metrics.
|
||||||
|
```json
|
||||||
|
"plugins":{
|
||||||
|
"followup":{
|
||||||
|
"enabled":true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### 🈷️ Languages
|
||||||
|
|
||||||
|
The *languages* plugin allows you to compute which languages you use the most in your repositories.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
|
This plugin is enabled by default. To disable it, explicitly opt-out.
|
||||||
|
|
||||||
|
##### Setup with GitHub actions
|
||||||
|
|
||||||
|
Add the following to your workflow :
|
||||||
|
```yaml
|
||||||
|
- uses: lowlighter/metrics@latest
|
||||||
|
with:
|
||||||
|
# ... other options
|
||||||
|
plugin_languages: yes
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Setup in your own instance
|
||||||
|
|
||||||
|
Add the following to your `settings.json` and pass `?languages=1` in url when generating metrics.
|
||||||
|
```json
|
||||||
|
"plugins":{
|
||||||
|
"languages":{
|
||||||
|
"enabled":true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
#### ⏭️ Selfskip
|
#### ⏭️ Selfskip
|
||||||
|
|
||||||
The *selfskip* plugin allows you to count out all commits tagged with `[Skip GitHub Action]` you authored on your personal repository from your reported commit counts.
|
The *selfskip* plugin allows you to count out all commits tagged with `[Skip GitHub Action]` you authored on your personal repository from your reported commit counts.
|
||||||
@@ -612,6 +680,8 @@ This way you'll be able to rapidly test SVG renders with your browser.
|
|||||||
* To make HTTP/S requests
|
* To make HTTP/S requests
|
||||||
* [actions/toolkit](https://github.com/actions/toolkit/tree/master) and [vercel/ncc](https://github.com/vercel/ncc)
|
* [actions/toolkit](https://github.com/actions/toolkit/tree/master) and [vercel/ncc](https://github.com/vercel/ncc)
|
||||||
* To build the GitHub Action
|
* To build the GitHub Action
|
||||||
|
* [vuejs/vue](https://github.com/vuejs/vue)
|
||||||
|
* To display server application
|
||||||
|
|
||||||
All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub.
|
All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub.
|
||||||
See [GitHub Logos and Usage](https://github.com/logos) for more information.
|
See [GitHub Logos and Usage](https://github.com/logos) for more information.
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ inputs:
|
|||||||
plugin_selfskip:
|
plugin_selfskip:
|
||||||
description: Skip commits flagged with [Skip GitHub Action] from commits count
|
description: Skip commits flagged with [Skip GitHub Action] from commits count
|
||||||
default: no
|
default: no
|
||||||
|
plugin_languages:
|
||||||
|
description: Enable most used languages metrics
|
||||||
|
default: yes
|
||||||
|
plugin_followup:
|
||||||
|
description: Enable owned repositories issues and pull requests metrics
|
||||||
|
default: yes
|
||||||
debug:
|
debug:
|
||||||
description: Enable debug logs
|
description: Enable debug logs
|
||||||
default: no
|
default: no
|
||||||
|
|||||||
2
action/dist/index.js
vendored
2
action/dist/index.js
vendored
File diff suppressed because one or more lines are too long
5
package-lock.json
generated
5
package-lock.json
generated
@@ -1566,6 +1566,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
},
|
},
|
||||||
|
"vue": {
|
||||||
|
"version": "2.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||||
|
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||||
|
},
|
||||||
"wide-align": {
|
"wide-align": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"start": "node index.mjs",
|
"start": "node index.mjs",
|
||||||
"build": "node utils/build.mjs",
|
"build": "node utils/build.mjs",
|
||||||
"test": "node tests/metrics.mjs",
|
"test": "node tests/metrics.mjs",
|
||||||
"upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest compression@latest ejs@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest svgo@latest @vercel/ncc@latest libxmljs@latest"
|
"upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest compression@latest ejs@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest svgo@latest vue@latest @vercel/ncc@latest libxmljs@latest"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -31,7 +31,8 @@
|
|||||||
"express-rate-limit": "^5.1.3",
|
"express-rate-limit": "^5.1.3",
|
||||||
"image-to-base64": "^2.1.1",
|
"image-to-base64": "^2.1.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"svgo": "^1.3.2"
|
"svgo": "^1.3.2",
|
||||||
|
"vue": "^2.6.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vercel/ncc": "^0.24.1",
|
"@vercel/ncc": "^0.24.1",
|
||||||
|
|||||||
@@ -27,6 +27,12 @@
|
|||||||
"habits":{ "//":"Habits plugin",
|
"habits":{ "//":"Habits plugin",
|
||||||
"enabled":true, "//":"Enable or disable coding habits metrics",
|
"enabled":true, "//":"Enable or disable coding habits metrics",
|
||||||
"from":50, "//":"Number of activity events to base habits on (up to 100)"
|
"from":50, "//":"Number of activity events to base habits on (up to 100)"
|
||||||
|
},
|
||||||
|
"languages":{ "//":"Languages plugins",
|
||||||
|
"enabled":true, "//":"Enable or disable most used languages metrics (*this plugin is enabled by default)"
|
||||||
|
},
|
||||||
|
"followup":{ "//":"Follow-up plugin",
|
||||||
|
"enabled":true, "//":"Enable owned repositories issues and pull requests metrics (*this plugin is enabled by default)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
39
src/app.mjs
39
src/app.mjs
@@ -7,6 +7,7 @@
|
|||||||
import compression from "compression"
|
import compression from "compression"
|
||||||
import setup from "./setup.mjs"
|
import setup from "./setup.mjs"
|
||||||
import metrics from "./metrics.mjs"
|
import metrics from "./metrics.mjs"
|
||||||
|
import Templates from "./templates/index.mjs"
|
||||||
|
|
||||||
/** App */
|
/** App */
|
||||||
export default async function () {
|
export default async function () {
|
||||||
@@ -40,10 +41,24 @@
|
|||||||
|
|
||||||
//Base routes
|
//Base routes
|
||||||
const limiter = ratelimit({max:60, windowMs:60*1000})
|
const limiter = ratelimit({max:60, windowMs:60*1000})
|
||||||
|
const templates = [...new Set([conf.settings.templates.default, ...(conf.settings.templates.enabled.length ? Object.keys(Templates).filter(key => conf.settings.templates.enabled.includes(key)) : Object.keys(Templates))])]
|
||||||
|
const enabled = Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key)
|
||||||
app.get("/", limiter, (req, res) => res.sendFile(`${conf.statics}/index.html`))
|
app.get("/", limiter, (req, res) => res.sendFile(`${conf.statics}/index.html`))
|
||||||
app.get("/index.html", limiter, (req, res) => res.sendFile(`${conf.statics}/index.html`))
|
app.get("/index.html", limiter, (req, res) => res.sendFile(`${conf.statics}/index.html`))
|
||||||
app.get("/placeholder.svg", limiter, (req, res) => res.sendFile(`${conf.statics}/placeholder.svg`))
|
|
||||||
app.get("/favicon.ico", limiter, (req, res) => res.sendStatus(204))
|
app.get("/favicon.ico", limiter, (req, res) => res.sendStatus(204))
|
||||||
|
app.get("/plugins.list", limiter, (req, res) => res.status(200).json(enabled))
|
||||||
|
app.get("/templates.list", limiter, (req, res) => res.status(200).json(templates))
|
||||||
|
app.get("/ejs.min.js", limiter, (req, res) => res.sendFile(`${conf.node_modules}/ejs/ejs.min.js`))
|
||||||
|
app.get("/axios.min.js", limiter, (req, res) => res.sendFile(`${conf.node_modules}/axios/dist/axios.min.js`))
|
||||||
|
app.get("/axios.min.map", limiter, (req, res) => res.sendFile(`${conf.node_modules}/axios/dist/axios.min.map`))
|
||||||
|
app.get("/vue.min.js", limiter, (req, res) => res.sendFile(`${conf.node_modules}/vue/dist/vue.min.js`))
|
||||||
|
app.get("/placeholder.svg", limiter, async (req, res) => {
|
||||||
|
const template = req.query.template || conf.settings.templates.default
|
||||||
|
if (!(template in Templates))
|
||||||
|
return res.sendStatus(404)
|
||||||
|
const {style, placeholder} = conf.templates[template]
|
||||||
|
res.status(200).json({style, placeholder})
|
||||||
|
})
|
||||||
|
|
||||||
//Metrics
|
//Metrics
|
||||||
app.get("/:login", ...middlewares, async (req, res) => {
|
app.get("/:login", ...middlewares, async (req, res) => {
|
||||||
@@ -69,7 +84,7 @@
|
|||||||
//Compute rendering
|
//Compute rendering
|
||||||
try {
|
try {
|
||||||
//Render
|
//Render
|
||||||
const rendered = await metrics({login, q:req.query}, {graphql, rest, plugins, conf})
|
const rendered = await metrics({login, q:parse(req.query)}, {graphql, rest, plugins, conf})
|
||||||
//Cache
|
//Cache
|
||||||
if ((!debug)&&(cached))
|
if ((!debug)&&(cached))
|
||||||
cache.put(login, rendered, cached)
|
cache.put(login, rendered, cached)
|
||||||
@@ -103,6 +118,22 @@
|
|||||||
`Cached time | ${cached} seconds`,
|
`Cached time | ${cached} seconds`,
|
||||||
`Rate limiter | ${ratelimiter ? JSON.stringify(ratelimiter) : "(enabled)"}`,
|
`Rate limiter | ${ratelimiter ? JSON.stringify(ratelimiter) : "(enabled)"}`,
|
||||||
`Max simultaneous users | ${maxusers ? `${maxusers} users` : "(unrestricted)"}`,
|
`Max simultaneous users | ${maxusers ? `${maxusers} users` : "(unrestricted)"}`,
|
||||||
`Plugins enabled | ${Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).join(", ")}`
|
`Plugins enabled | ${enabled.join(", ")}`
|
||||||
].join("\n")))
|
].join("\n")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Query parser */
|
||||||
|
function parse(query) {
|
||||||
|
for (const [key, value] of Object.entries(query)) {
|
||||||
|
//Parse number
|
||||||
|
if (/^\d+$/.test(value))
|
||||||
|
query[key] = Number(value)
|
||||||
|
//Parse boolean
|
||||||
|
if (/^true|false$/.test(value))
|
||||||
|
query[key] = !!value
|
||||||
|
//Parse null
|
||||||
|
if (/^null$/.test(value))
|
||||||
|
query[key] = null
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,93 +9,172 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1><a href="https://github.com/lowlighter/metrics">GitHub metrics</a></h1>
|
<main :class="[palette]">
|
||||||
|
|
||||||
<p>
|
<h1><a href="https://github.com/lowlighter/metrics">Generate your metrics !</a></h1>
|
||||||
Enter your GitHub username below to generate your metrics.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<label>
|
<div class="step">
|
||||||
<input type="text" name="user" placeholder="@username" value="">
|
<h2>1. Enter your GitHub username</h2>
|
||||||
</label>
|
<label>
|
||||||
|
<input type="text" v-model="user" maxlength="39" placeholder="GitHub username" :disabled="generated.pending">
|
||||||
<div id="metrics">
|
</label>
|
||||||
<img class="placeholder" src="/placeholder.svg">
|
|
||||||
<img class="generated" src="/placeholder.svg">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<aside>
|
|
||||||
Embed these metrics on your GitHub profile by adding the markdown below in your <i>README.md</i> at <a id="user-repo" href="#"><span class="user"></span>/<span class="user"></span></a>
|
|
||||||
<br>
|
|
||||||
<div class="code">
|
|
||||||

|
|
||||||
</div>
|
</div>
|
||||||
For even more metrics (coding habits, PageSpeed performances, number of line of code you wrote, page views, etc.), setup this as a <a href="https://github.com/marketplace/actions/github-metrics-as-svg-image">GitHub action</a> on your repository !<br>
|
|
||||||
Check out <a href="https://github.com/lowlighter/metrics">lowlighter/metrics</a> for more informations
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h2>2. Select a template and enable additional plugins</h2>
|
||||||
|
<div class="templates">
|
||||||
|
<label v-for="template in templates.list" :key="template">
|
||||||
|
<input type="radio" v-model="templates.selected" :value="template" @change="load" :disabled="generated.pending">
|
||||||
|
{{ templates.descriptions[template] || template }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="plugins">
|
||||||
|
<label v-for="plugin in plugins.list" :key="plugin">
|
||||||
|
<input type="checkbox" v-model="plugins.enabled[plugin]" @change="load" :disabled="generated.pending">
|
||||||
|
{{ plugins.descriptions[plugin] || plugin }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="cache-notice">
|
||||||
|
*To reduce server overhead, metrics are cached. Changes may not be reflected until cache expiration.
|
||||||
|
</div>
|
||||||
|
<div class="palette">
|
||||||
|
Generated metrics use transparency and colors which matches both light and dark modes
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<input type="radio" v-model="palette" value="light">
|
||||||
|
☀️ Light mode
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" v-model="palette" value="dark">
|
||||||
|
🌙 Night mode
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h2>3. Generate your metrics</h2>
|
||||||
|
<template v-if="generated.content">
|
||||||
|
<img :src="generated.content" alt="metrics">
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<img :src="templates.placeholder" alt="metrics">
|
||||||
|
<button @click="generate" :disabled="(!user)||(generated.pending)">{{ generated.pending ? "Generating your metrics..." : user ? "Generate your metrics" : "Enter your GitHub username first" }}</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h2>4. Embed these metrics on your GitHub profile</h2>
|
||||||
|
<template v-if="user">
|
||||||
|
Add the markdown below in your <i>README.md</i> at <a :href="repo">{{ user }}/{{ user }}</a>
|
||||||
|
<div class="code">
|
||||||
|

|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
For even more features, setup <a href="https://github.com/lowlighter/metrics">lowlighter/metrics</a> as a <a href="https://github.com/marketplace/actions/github-metrics-as-svg-image">GitHub action</a> !
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="/axios.min.js"></script>
|
||||||
|
<script src="/ejs.min.js"></script>
|
||||||
|
<script src="/vue.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function () {
|
;(async function() {
|
||||||
//User updater
|
new Vue({
|
||||||
let timeout = null
|
el:"main",
|
||||||
document.querySelector("input[name=user]").onkeyup = function (event) {
|
async mounted() {
|
||||||
//Retrieve user value
|
await this.load()
|
||||||
clearTimeout(timeout)
|
},
|
||||||
const user = event.target.value
|
data:{
|
||||||
//Display placeholder
|
user:"",
|
||||||
document.querySelector("#metrics .placeholder").style.opacity = 1
|
palette:"light",
|
||||||
document.querySelector("#metrics .generated").style.opacity = 0
|
plugins:{
|
||||||
document.querySelector("aside").style.opacity = 0
|
list:(await axios.get("/plugins.list")).data,
|
||||||
//Update github user
|
enabled:{languages:true, followup:true},
|
||||||
document.querySelector(".code .url").innerText = `${window.location.href}${user}`
|
descriptions:{
|
||||||
document.querySelector("#user-repo").href = `https://github.com/${user}/${user}`
|
pagespeed:"Website performances",
|
||||||
document.querySelectorAll(".user").forEach(node => node.innerText = user)
|
languages:"Most used languages",
|
||||||
//Update metrics
|
followup:"Owned repositories issues and pull requests",
|
||||||
if (event.key === "Enter")
|
traffic:"Pages views",
|
||||||
metrics(user)
|
lines:"Lines of code changed",
|
||||||
else
|
habits:"Coding habits",
|
||||||
timeout = setTimeout(() => metrics(user), 1800)
|
selfskip:"Skip metrics commits",
|
||||||
}
|
},
|
||||||
//Metrics updater
|
},
|
||||||
let current = null
|
templates:{
|
||||||
function metrics(user) {
|
list:(await axios.get("/templates.list")).data,
|
||||||
if (!user.trim().length)
|
loaded:{},
|
||||||
return
|
selected:(await axios.get("/templates.list")).data[0],
|
||||||
if (current === user) {
|
placeholder:"",
|
||||||
document.querySelector("#metrics .placeholder").style.opacity = 0
|
descriptions:{
|
||||||
document.querySelector("#metrics .generated").style.opacity = 1
|
classic:"Classic template",
|
||||||
document.querySelector("aside").style.opacity = 1
|
},
|
||||||
}
|
},
|
||||||
else {
|
generated:{
|
||||||
current = user
|
pending:false,
|
||||||
document.querySelector("#metrics .generated").src = `${window.location.href}${user}`
|
content:"",
|
||||||
document.querySelector("#metrics .generated").onload = function () {
|
},
|
||||||
document.querySelector("#metrics .placeholder").style.opacity = 0
|
},
|
||||||
document.querySelector("#metrics .generated").style.opacity = 1
|
computed:{
|
||||||
document.querySelector("aside").style.opacity = 1
|
repo() {
|
||||||
|
return `https://github.com/${this.user}/${this.user}`
|
||||||
|
},
|
||||||
|
url() {
|
||||||
|
const plugins = Object.entries(this.plugins.enabled)
|
||||||
|
.filter(([key, value]) => /^languages|followup$/.test(key) ? !value : value)
|
||||||
|
.map(([key, value]) => `${key}=${+value}`)
|
||||||
|
.join("&")
|
||||||
|
return `${window.location.href}${this.user}${plugins.length ? `?${plugins}` : ""}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
async load() {
|
||||||
|
const template = this.templates.selected
|
||||||
|
if (!this.templates.loaded[template]) {
|
||||||
|
const {data:{placeholder, style}} = await axios.get(`/placeholder.svg?template=${template}`)
|
||||||
|
this.templates.loaded[template] = {placeholder, style}
|
||||||
}
|
}
|
||||||
}
|
const {placeholder = "", style = {}} = this.templates.loaded[this.templates.selected] || {}
|
||||||
}
|
this.templates.placeholder = placeholder ? this.serialize(ejs.render(placeholder, {plugins:this.plugins.enabled, style})) : "#"
|
||||||
}
|
},
|
||||||
|
async generate() {
|
||||||
|
this.generated.pending = true
|
||||||
|
this.generated.content = this.serialize((await axios.get(this.url)).data)
|
||||||
|
},
|
||||||
|
serialize(svg) {
|
||||||
|
return `data:image/svg+xml;base64,${btoa(svg)}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
main {
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
color: #1B1F23;
|
color: #1B1F23;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 100vh;
|
padding-bottom: 2rem;
|
||||||
width: 100vw;
|
width: 100%;
|
||||||
padding: 0;
|
transition: background-color .3s;
|
||||||
margin: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin: 4rem 0 0;
|
font-size: 1.6rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin: 1.5rem 0 1rem;
|
||||||
|
font-size: 1.3rem;
|
||||||
}
|
}
|
||||||
a, a:hover, a:visited {
|
a, a:hover, a:visited {
|
||||||
color: #0366D6;
|
color: #0366D6;
|
||||||
@@ -108,39 +187,47 @@
|
|||||||
transition: color .4s;
|
transition: color .4s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
input {
|
input, button, select {
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
padding: .5rem 1rem;
|
padding: .25rem .5rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
border: 1px solid #E1E4E8;
|
border: 1px solid #E1E4E8;
|
||||||
background-color: #FAFBFC;
|
background-color: #FAFBFC;
|
||||||
color: #1B1F23;
|
color: #1B1F23;
|
||||||
font-size: 1.2rem;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 0 1.5rem;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
input:focus {
|
input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
#metrics {
|
input[type=text], select, button {
|
||||||
position: relative;
|
min-width: 50%;
|
||||||
max-width: 100%;
|
font-size: 1.1rem;
|
||||||
width: 480px;
|
|
||||||
height: 485px;
|
|
||||||
}
|
}
|
||||||
#metrics img {
|
option {
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
transition: opacity .4s;
|
|
||||||
}
|
|
||||||
aside {
|
|
||||||
opacity: 0;
|
|
||||||
padding: .25rem;
|
|
||||||
transition: opacity .4s;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 1rem 0 2rem;
|
}
|
||||||
|
label, button {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
input[disabled], button[disabled], select[disabled] {
|
||||||
|
opacity: .5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.step {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
.plugins {
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.plugins label {
|
||||||
|
margin: 0 1rem;
|
||||||
}
|
}
|
||||||
.code {
|
.code {
|
||||||
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
||||||
@@ -152,6 +239,29 @@
|
|||||||
.code .md-alt {
|
.code .md-alt {
|
||||||
color: #6F42C1;
|
color: #6F42C1;
|
||||||
}
|
}
|
||||||
|
.cache-notice {
|
||||||
|
margin-top: .5rem;
|
||||||
|
font-size: .9rem;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
.palette {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
main.dark {
|
||||||
|
background-color: #181A1B;
|
||||||
|
color: #D4D1C5;
|
||||||
|
}
|
||||||
|
.dark a, .dark a:visited {
|
||||||
|
color: #4CACEE;
|
||||||
|
}
|
||||||
|
.dark input, .dark button {
|
||||||
|
color: #D4D1C5;
|
||||||
|
background-color: #1A1C1E;
|
||||||
|
border-color: #373C3E;
|
||||||
|
}
|
||||||
|
.dark .code {
|
||||||
|
background-color: #1A1C1E;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
//Init
|
//Init
|
||||||
|
console.debug(`metrics/compute/${login} > start`)
|
||||||
|
console.debug(JSON.stringify(q))
|
||||||
const template = q.template || conf.settings.templates.default
|
const template = q.template || conf.settings.templates.default
|
||||||
const pending = []
|
const pending = []
|
||||||
const s = (value, end = "") => value > 1 ? {y:"ies", "":"s"}[end] : end
|
const s = (value, end = "") => value > 1 ? {y:"ies", "":"s"}[end] : end
|
||||||
|
|||||||
41
src/plugins/followup/index.mjs
Normal file
41
src/plugins/followup/index.mjs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
//Setup
|
||||||
|
export default function ({login, data, computed, pending, q}, {enabled = true} = {}) {
|
||||||
|
//Check if plugin is enabled and requirements are met
|
||||||
|
if (!enabled)
|
||||||
|
return computed.plugins.followup = null
|
||||||
|
if (("followup" in q)&&(!q.followup))
|
||||||
|
return computed.plugins.followup = null
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > followup`)
|
||||||
|
computed.svg.height += 70
|
||||||
|
|
||||||
|
//Plugin execution
|
||||||
|
pending.push(new Promise(async solve => {
|
||||||
|
try {
|
||||||
|
//Define getters
|
||||||
|
const followup = {
|
||||||
|
issues:{
|
||||||
|
get count() { return this.open + this.closed },
|
||||||
|
get open() { return computed.repositories.issues_open },
|
||||||
|
get closed() { return computed.repositories.issues_closed },
|
||||||
|
},
|
||||||
|
pr:{
|
||||||
|
get count() { return this.open + this.merged },
|
||||||
|
get open() { return computed.repositories.pr_open },
|
||||||
|
get merged() { return computed.repositories.pr_merged }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Save results
|
||||||
|
computed.plugins.followup = followup
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > followup > success`)
|
||||||
|
console.debug(JSON.stringify(computed.plugins.followup))
|
||||||
|
solve()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
//Generic error
|
||||||
|
computed.plugins.followup = {error:`An error occured`}
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > followup > error`)
|
||||||
|
console.debug(error)
|
||||||
|
solve()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
//Imports
|
//Imports
|
||||||
|
import followup from "./followup/index.mjs"
|
||||||
import habits from "./habits/index.mjs"
|
import habits from "./habits/index.mjs"
|
||||||
|
import languages from "./languages/index.mjs"
|
||||||
import lines from "./lines/index.mjs"
|
import lines from "./lines/index.mjs"
|
||||||
import pagespeed from "./pagespeed/index.mjs"
|
import pagespeed from "./pagespeed/index.mjs"
|
||||||
import selfskip from "./selfskip/index.mjs"
|
import selfskip from "./selfskip/index.mjs"
|
||||||
@@ -7,7 +9,9 @@
|
|||||||
|
|
||||||
//Exports
|
//Exports
|
||||||
export default {
|
export default {
|
||||||
|
followup,
|
||||||
habits,
|
habits,
|
||||||
|
languages,
|
||||||
lines,
|
lines,
|
||||||
pagespeed,
|
pagespeed,
|
||||||
selfskip,
|
selfskip,
|
||||||
|
|||||||
42
src/plugins/languages/index.mjs
Normal file
42
src/plugins/languages/index.mjs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//Setup
|
||||||
|
export default function ({login, data, computed, pending, q}, {enabled = true} = {}) {
|
||||||
|
//Check if plugin is enabled and requirements are met
|
||||||
|
if (!enabled)
|
||||||
|
return computed.plugins.languages = null
|
||||||
|
if (("languages" in q)&&(!q.languages))
|
||||||
|
return computed.plugins.languages = null
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > languages`)
|
||||||
|
computed.svg.height += 90
|
||||||
|
|
||||||
|
//Plugin execution
|
||||||
|
pending.push(new Promise(async solve => {
|
||||||
|
try {
|
||||||
|
//Iterate through user's repositories and retrieve languages data
|
||||||
|
const languages = {colors:{}, total:0, stats:{}}
|
||||||
|
for (const repository of data.user.repositories.nodes) {
|
||||||
|
for (const {size, node:{color, name}} of Object.values(repository.languages.edges)) {
|
||||||
|
languages.stats[name] = (languages.stats[name] || 0) + size
|
||||||
|
languages.colors[name] = color || "#ededed"
|
||||||
|
languages.total += size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Compute languages stats
|
||||||
|
Object.keys(languages.stats).map(name => languages.stats[name] /= languages.total)
|
||||||
|
languages.favorites = Object.entries(languages.stats).sort(([an, a], [bn, b]) => b - a).slice(0, 8).map(([name, value]) => ({name, value, color:languages.colors[name], x:0}))
|
||||||
|
for (let i = 1; i < languages.favorites.length; i++)
|
||||||
|
languages.favorites[i].x = languages.favorites[i-1].x + languages.favorites[i-1].value
|
||||||
|
//Save results
|
||||||
|
computed.plugins.languages = languages
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > languages > success`)
|
||||||
|
console.debug(JSON.stringify(computed.plugins.languages))
|
||||||
|
solve()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
//Generic error
|
||||||
|
computed.plugins.languages = {error:`An error occured`}
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > languages > error`)
|
||||||
|
console.debug(error)
|
||||||
|
solve()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
}
|
}
|
||||||
//Save results
|
//Save results
|
||||||
computed.plugins.selfskip = {commits}
|
computed.plugins.selfskip = {commits}
|
||||||
|
computed.commits -= commits
|
||||||
console.debug(`metrics/compute/${login}/plugins > selfskip > success`)
|
console.debug(`metrics/compute/${login}/plugins > selfskip > success`)
|
||||||
console.debug(JSON.stringify(computed.plugins.selfskip))
|
console.debug(JSON.stringify(computed.plugins.selfskip))
|
||||||
solve()
|
solve()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
if (!q.traffic)
|
if (!q.traffic)
|
||||||
return computed.plugins.traffic = null
|
return computed.plugins.traffic = null
|
||||||
console.debug(`metrics/compute/${login}/plugins > traffic`)
|
console.debug(`metrics/compute/${login}/plugins > traffic`)
|
||||||
computed.svg.height += 20
|
computed.svg.height += !q.lines ? 20 : 0
|
||||||
|
|
||||||
//Plugin execution
|
//Plugin execution
|
||||||
pending.push(new Promise(async solve => {
|
pending.push(new Promise(async solve => {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
const conf = {
|
const conf = {
|
||||||
templates:{},
|
templates:{},
|
||||||
settings:{},
|
settings:{},
|
||||||
statics:path.resolve("src/html")
|
statics:path.resolve("src/html"),
|
||||||
|
node_modules:path.resolve("node_modules"),
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load settings
|
//Load settings
|
||||||
@@ -37,19 +38,20 @@
|
|||||||
const files = [
|
const files = [
|
||||||
`${templates}/${name}/query.graphql`,
|
`${templates}/${name}/query.graphql`,
|
||||||
`${templates}/${name}/image.svg`,
|
`${templates}/${name}/image.svg`,
|
||||||
|
`${templates}/${name}/placeholder.svg`,
|
||||||
`${templates}/${name}/style.css`,
|
`${templates}/${name}/style.css`,
|
||||||
]
|
]
|
||||||
const [query, image, style] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
const [query, image, placeholder, style] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
||||||
conf.templates[name] = {query, image, style}
|
conf.templates[name] = {query, image, placeholder, style}
|
||||||
console.debug(`metrics/setup > load template [${name}] > success`)
|
console.debug(`metrics/setup > load template [${name}] > success`)
|
||||||
//Debug
|
//Debug
|
||||||
if (conf.settings.debug) {
|
if (conf.settings.debug) {
|
||||||
Object.defineProperty(conf.templates, name, {
|
Object.defineProperty(conf.templates, name, {
|
||||||
get() {
|
get() {
|
||||||
console.debug(`metrics/setup > reload template [${name}]`)
|
console.debug(`metrics/setup > reload template [${name}]`)
|
||||||
const [query, image, style] = files.map(file => `${fs.readFileSync(path.resolve(file))}`)
|
const [query, image, placeholder, style] = files.map(file => `${fs.readFileSync(path.resolve(file))}`)
|
||||||
console.debug(`metrics/setup > reload template [${name}] > success`)
|
console.debug(`metrics/setup > reload template [${name}] > success`)
|
||||||
return {query, image, style}
|
return {query, image, placeholder, style}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M10.5 7.75a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm1.43.75a4.002 4.002 0 01-7.86 0H.75a.75.75 0 110-1.5h3.32a4.001 4.001 0 017.86 0h3.32a.75.75 0 110 1.5h-3.32z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M10.5 7.75a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm1.43.75a4.002 4.002 0 01-7.86 0H.75a.75.75 0 110-1.5h3.32a4.001 4.001 0 017.86 0h3.32a.75.75 0 110 1.5h-3.32z"></path></svg>
|
||||||
<%= computed.commits - (computed.plugins.selfskip ? computed.plugins.selfskip.commits||0 : 0) %> Commit<%= s(computed.commits - (computed.plugins.selfskip ? computed.plugins.selfskip.commits||0 : 0)) %>
|
<%= computed.commits %> Commit<%= s(computed.commits) %>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 1.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v7.736a.75.75 0 101.5 0V1.75A1.75 1.75 0 0011.25 0h-8.5A1.75 1.75 0 001 1.75v11.5c0 .966.784 1.75 1.75 1.75h3.17a.75.75 0 000-1.5H2.75a.25.25 0 01-.25-.25V1.75zM4.75 4a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-4.5zM4 7.75A.75.75 0 014.75 7h2a.75.75 0 010 1.5h-2A.75.75 0 014 7.75zm11.774 3.537a.75.75 0 00-1.048-1.074L10.7 14.145 9.281 12.72a.75.75 0 00-1.062 1.058l1.943 1.95a.75.75 0 001.055.008l4.557-4.45z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 1.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v7.736a.75.75 0 101.5 0V1.75A1.75 1.75 0 0011.25 0h-8.5A1.75 1.75 0 001 1.75v11.5c0 .966.784 1.75 1.75 1.75h3.17a.75.75 0 000-1.5H2.75a.25.25 0 01-.25-.25V1.75zM4.75 4a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-4.5zM4 7.75A.75.75 0 014.75 7h2a.75.75 0 010 1.5h-2A.75.75 0 014 7.75zm11.774 3.537a.75.75 0 00-1.048-1.074L10.7 14.145 9.281 12.72a.75.75 0 00-1.062 1.058l1.943 1.95a.75.75 0 001.055.008l4.557-4.45z"></path></svg>
|
||||||
@@ -165,74 +165,105 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="row">
|
<% if (computed.plugins.followup) { %>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
<section class="column">
|
<section class="column">
|
||||||
<h3>Issues</h3>
|
<h3>Issues</h3>
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
<% if (computed.plugins.followup.error) { %>
|
||||||
<mask id="issues-bar">
|
<section>
|
||||||
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
<div class="field error">
|
||||||
</mask>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.47.22A.75.75 0 015 0h6a.75.75 0 01.53.22l4.25 4.25c.141.14.22.331.22.53v6a.75.75 0 01-.22.53l-4.25 4.25A.75.75 0 0111 16H5a.75.75 0 01-.53-.22L.22 11.53A.75.75 0 010 11V5a.75.75 0 01.22-.53L4.47.22zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5H5.31zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 8a1 1 0 100-2 1 1 0 000 2z"></path></svg>
|
||||||
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= computed.repositories.issues_count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
|
<%= computed.plugins.followup.error %>
|
||||||
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= (computed.repositories.issues_closed/computed.repositories.issues_count)*220 || 0 %>" height="8" fill="#d73a49"/>
|
</div>
|
||||||
<rect mask="url(#issues-bar)" x="<%= (computed.repositories.issues_closed/computed.repositories.issues_count)*220 || 0 %>" y="0" width="<%= (1-computed.repositories.issues_closed/computed.repositories.issues_count)*220 || 0 %>" height="8" fill="#28a745"/>
|
</section>
|
||||||
</svg>
|
<% } else { %>
|
||||||
<div class="field horizontal fill-width">
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
||||||
<div class="field center">
|
<mask id="issues-bar">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"></path></svg>
|
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
||||||
<span class="no-wrap"><%= computed.repositories.issues_closed %> Closed</span>
|
</mask>
|
||||||
</div>
|
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= computed.plugins.followup.issues.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
|
||||||
<div class="field center">
|
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= (computed.plugins.followup.issues.closed/computed.plugins.followup.issues.count)*220 || 0 %>" height="8" fill="#d73a49"/>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
|
<rect mask="url(#issues-bar)" x="<%= (computed.plugins.followup.issues.closed/computed.plugins.followup.issues.count)*220 || 0 %>" y="0" width="<%= (1-computed.plugins.followup.issues.closed/computed.plugins.followup.issues.count)*220 || 0 %>" height="8" fill="#28a745"/>
|
||||||
<span class="no-wrap"><%= computed.repositories.issues_open %> Open</span>
|
</svg>
|
||||||
</div>
|
<div class="field horizontal fill-width">
|
||||||
</div>
|
<div class="field center">
|
||||||
</section>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"></path></svg>
|
||||||
|
<span class="no-wrap"><%= computed.plugins.followup.issues.closed %> Closed</span>
|
||||||
|
</div>
|
||||||
|
<div class="field center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
|
||||||
|
<span class="no-wrap"><%= computed.plugins.followup.issues.open %> Open</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="column">
|
<section class="column">
|
||||||
<h3>Pull requests</h3>
|
<h3>Pull requests</h3>
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
<% if (computed.plugins.followup.error) { %>
|
||||||
<mask id="pr-bar">
|
<section>
|
||||||
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
<div class="field error">
|
||||||
</mask>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.47.22A.75.75 0 015 0h6a.75.75 0 01.53.22l4.25 4.25c.141.14.22.331.22.53v6a.75.75 0 01-.22.53l-4.25 4.25A.75.75 0 0111 16H5a.75.75 0 01-.53-.22L.22 11.53A.75.75 0 010 11V5a.75.75 0 01.22-.53L4.47.22zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5H5.31zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 8a1 1 0 100-2 1 1 0 000 2z"></path></svg>
|
||||||
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= computed.repositories.pr_count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
|
<%= computed.plugins.followup.error %>
|
||||||
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= (computed.repositories.pr_merged/computed.repositories.pr_count)*220 || 0 %>" height="8" fill="#6f42c1"/>
|
</div>
|
||||||
<rect mask="url(#pr-bar)" x="<%= (computed.repositories.pr_merged/computed.repositories.pr_count)*220 || 0 %>" y="0" width="<%= (1-computed.repositories.pr_merged/computed.repositories.pr_count)*220 || 0 %>" height="8" fill="#28a745"/>
|
</section>
|
||||||
</svg>
|
<% } else { %>
|
||||||
<div class="field horizontal fill-width">
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
||||||
<div class="field center">
|
<mask id="pr-bar">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
|
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
||||||
<span class="no-wrap"><%= computed.repositories.pr_merged %> Merged</span>
|
</mask>
|
||||||
</div>
|
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= computed.plugins.followup.pr.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
|
||||||
<div class="field center">
|
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= (computed.plugins.followup.pr.merged/computed.plugins.followup.pr.count)*220 || 0 %>" height="8" fill="#6f42c1"/>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
|
<rect mask="url(#pr-bar)" x="<%= (computed.plugins.followup.pr.merged/computed.plugins.followup.pr.count)*220 || 0 %>" y="0" width="<%= (1-computed.plugins.followup.pr.merged/computed.plugins.followup.pr.count)*220 || 0 %>" height="8" fill="#28a745"/>
|
||||||
<span class="no-wrap"><%= computed.repositories.pr_open %> Open</span>
|
</svg>
|
||||||
</div>
|
<div class="field horizontal fill-width">
|
||||||
</div>
|
<div class="field center">
|
||||||
</section>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
|
||||||
|
<span class="no-wrap"><%= computed.plugins.followup.pr.merged %> Merged</span>
|
||||||
|
</div>
|
||||||
|
<div class="field center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
|
||||||
|
<span class="no-wrap"><%= computed.plugins.followup.pr.open %> Open</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="column">
|
|
||||||
<h3>Most used languages</h3>
|
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="460" height="8">
|
|
||||||
<mask id="languages-bar">
|
|
||||||
<rect x="0" y="0" width="460" height="8" fill="white" rx="5"/>
|
|
||||||
</mask>
|
|
||||||
<rect mask="url(#languages-bar)" x="0" y="0" width="<%= computed.languages.favorites.length ? 0 : 460 %>" height="8" fill="#d1d5da"/>
|
|
||||||
<% for (const {name, value, color, x} of computed.languages.favorites) { %>
|
|
||||||
<rect mask="url(#languages-bar)" x="<%= x*460 %>" y="0" width="<%= value*460 %>" height="8" fill="<%= color %>"/>
|
|
||||||
<% } %>
|
|
||||||
</svg>
|
|
||||||
<div class="field center horizontal-wrap fill-width">
|
|
||||||
<% for (const {name, value, color} of computed.languages.favorites) { %>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
|
|
||||||
<%= name %>
|
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (computed.plugins.languages) { %>
|
||||||
|
<section class="column">
|
||||||
|
<h3>Most used languages</h3>
|
||||||
|
<% if (computed.plugins.languages.error) { %>
|
||||||
|
<section>
|
||||||
|
<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="M4.47.22A.75.75 0 015 0h6a.75.75 0 01.53.22l4.25 4.25c.141.14.22.331.22.53v6a.75.75 0 01-.22.53l-4.25 4.25A.75.75 0 0111 16H5a.75.75 0 01-.53-.22L.22 11.53A.75.75 0 010 11V5a.75.75 0 01.22-.53L4.47.22zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5H5.31zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 8a1 1 0 100-2 1 1 0 000 2z"></path></svg>
|
||||||
|
<%= computed.plugins.languages.error %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } else { %>
|
||||||
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="460" height="8">
|
||||||
|
<mask id="languages-bar">
|
||||||
|
<rect x="0" y="0" width="460" height="8" fill="white" rx="5"/>
|
||||||
|
</mask>
|
||||||
|
<rect mask="url(#languages-bar)" x="0" y="0" width="<%= computed.plugins.languages.favorites.length ? 0 : 460 %>" height="8" fill="#d1d5da"/>
|
||||||
|
<% for (const {name, value, color, x} of computed.plugins.languages.favorites) { %>
|
||||||
|
<rect mask="url(#languages-bar)" x="<%= x*460 %>" y="0" width="<%= value*460 %>" height="8" fill="<%= color %>"/>
|
||||||
|
<% } %>
|
||||||
|
</svg>
|
||||||
|
<div class="field center horizontal-wrap fill-width">
|
||||||
|
<% for (const {name, value, color} of computed.plugins.languages.favorites) { %>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
|
||||||
|
<%= name %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<% if (computed.plugins.pagespeed) { %>
|
<% if (computed.plugins.pagespeed) { %>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 33 KiB |
@@ -1,130 +1,38 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="485">
|
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="<%= 355 + (!!plugins.followup)*70 + (!!plugins.habits)*70 + (!!plugins.languages)*90 + ((!!plugins.lines)+(!!plugins.traffic))*20 + (!!plugins.pagespeed)*130 %>">
|
||||||
<style>
|
<style>
|
||||||
/* SVG global context */
|
<%= style %>
|
||||||
svg {
|
/* Avatar */
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
.avatar {
|
||||||
font-size: 14px;
|
background-color: rgba(119,119,119,.62);
|
||||||
color: #777777;
|
border-radius: 50%;
|
||||||
}
|
margin: 0 6px;
|
||||||
|
height: 20px;
|
||||||
/* Headers */
|
width: 20px;
|
||||||
h1, h2, h3 {
|
}
|
||||||
margin: 8px 0 2px;
|
/* Placeholder */
|
||||||
padding: 0;
|
.placeholder {
|
||||||
color: #0366d6;
|
background-color: rgba(119,119,119,.62);
|
||||||
font-weight: normal;
|
margin: 2px 4px 0px;
|
||||||
}
|
height: 12px;
|
||||||
h1 svg, h2 svg, h3 svg {
|
width: 24px;
|
||||||
fill: currentColor;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
h1 {
|
.placeholder.large {
|
||||||
font-size: 20px;
|
width: 48px;
|
||||||
font-weight: bold;
|
}
|
||||||
}
|
.placeholder.xlarge {
|
||||||
h2 {
|
width: 64px;
|
||||||
font-size: 16px;
|
}
|
||||||
}
|
.placeholder.xxlarge {
|
||||||
h3 {
|
margin: 7.2px 0;
|
||||||
font-size: 14px;
|
width: 96px;
|
||||||
}
|
}
|
||||||
|
.placeholder.inline {
|
||||||
/* Fields */
|
display: inline-block;
|
||||||
section > .field {
|
}
|
||||||
margin-left: 5px;
|
h2 .placeholder {
|
||||||
margin-right: 5px;
|
background-color: rgba(3,102,214,.62);
|
||||||
}
|
}
|
||||||
.field {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.field svg {
|
|
||||||
margin: 0 8px;
|
|
||||||
fill: #959da5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Displays */
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.row section {
|
|
||||||
flex: 1 1 0;
|
|
||||||
}
|
|
||||||
.column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.horizontal {
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
.horizontal-wrap {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.horizontal .field {
|
|
||||||
flex: 1 1 0;
|
|
||||||
}
|
|
||||||
.no-wrap {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.fill-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User avatar */
|
|
||||||
.avatar {
|
|
||||||
background-color: rgba(119,119,119,.62);
|
|
||||||
border-radius: 50%;
|
|
||||||
margin: 0 6px;
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Commit calendar */
|
|
||||||
.calendar.field {
|
|
||||||
margin: 4px 0;
|
|
||||||
margin-left: 7px;
|
|
||||||
}
|
|
||||||
.calendar .day {
|
|
||||||
outline: 1px solid rgba(27,31,35,.04);
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Progress bars */
|
|
||||||
svg.bar {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Language */
|
|
||||||
.field.language {
|
|
||||||
margin: 0 8px;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Placeholder */
|
|
||||||
.placeholder {
|
|
||||||
background-color: rgba(119,119,119,.62);
|
|
||||||
margin: 2px 4px 0px;
|
|
||||||
height: 12px;
|
|
||||||
width: 24px;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
.placeholder.large {
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
.placeholder.xlarge {
|
|
||||||
width: 64px;
|
|
||||||
}
|
|
||||||
.placeholder.xxlarge {
|
|
||||||
margin: 7.2px 0;
|
|
||||||
width: 96px;
|
|
||||||
}
|
|
||||||
h2 .placeholder {
|
|
||||||
background-color: rgba(3,102,214,.62);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<foreignObject x="0" y="0" width="100%" height="100%">
|
<foreignObject x="0" y="0" width="100%" height="100%">
|
||||||
@@ -257,6 +165,12 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V1.75a.25.25 0 00-.25-.25H1.75zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm9.22 3.72a.75.75 0 000 1.06L10.69 8 9.22 9.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM6.78 6.53a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 101.06-1.06L5.31 8l1.47-1.47z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V1.75a.25.25 0 00-.25-.25H1.75zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm9.22 3.72a.75.75 0 000 1.06L10.69 8 9.22 9.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM6.78 6.53a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 101.06-1.06L5.31 8l1.47-1.47z"/></svg>
|
||||||
<div class="placeholder"></div> Gists
|
<div class="placeholder"></div> Gists
|
||||||
</div>
|
</div>
|
||||||
|
<% if (plugins.lines) { %>
|
||||||
|
<div class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path></svg>
|
||||||
|
<div class="placeholder"></div> added, <div class="placeholder"></div> removed
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
@@ -272,93 +186,167 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"/></svg>
|
||||||
<div class="placeholder"></div> Watchers
|
<div class="placeholder"></div> Watchers
|
||||||
</div>
|
</div>
|
||||||
|
<% if (plugins.traffic) { %>
|
||||||
|
<div class="field">
|
||||||
|
<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>
|
||||||
|
<div class="placeholder"></div> views in last two weeks
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="row">
|
<% if (plugins.followup) { %>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
<section class="column">
|
<section class="column">
|
||||||
<h3>Issues</h3>
|
<h3>Issues</h3>
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
||||||
<mask id="issues-bar">
|
<mask id="issues-bar">
|
||||||
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
||||||
</mask>
|
</mask>
|
||||||
<rect mask="url(#issues-bar)" x="0" y="0" width="220" height="8" fill="#d1d5da"/>
|
<rect mask="url(#issues-bar)" x="0" y="0" width="220" height="8" fill="#d1d5da"/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="field horizontal fill-width">
|
<div class="field horizontal fill-width">
|
||||||
<div class="field center">
|
<div class="field center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"/></svg>
|
||||||
<div class="placeholder"></div> Closed
|
<div class="placeholder"></div> Closed
|
||||||
|
</div>
|
||||||
|
<div class="field center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"/></svg>
|
||||||
|
<div class="placeholder"></div> Open
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field center">
|
</section>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"/></svg>
|
|
||||||
<div class="placeholder"></div> Open
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="column">
|
<section class="column">
|
||||||
<h3>Pull requests</h3>
|
<h3>Pull requests</h3>
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
|
||||||
<mask id="pr-bar">
|
<mask id="pr-bar">
|
||||||
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
|
||||||
</mask>
|
</mask>
|
||||||
<rect mask="url(#pr-bar)" x="0" y="0" width="220" height="8" fill="#d1d5da"/>
|
<rect mask="url(#pr-bar)" x="0" y="0" width="220" height="8" fill="#d1d5da"/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="field horizontal fill-width">
|
<div class="field horizontal fill-width">
|
||||||
<div class="field center">
|
<div class="field center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"/></svg>
|
||||||
<div class="placeholder"></div> Merged
|
<div class="placeholder"></div> Merged
|
||||||
|
</div>
|
||||||
|
<div class="field center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"/></svg>
|
||||||
|
<div class="placeholder"></div> Open
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field center">
|
</section>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"/></svg>
|
|
||||||
<div class="placeholder"></div> Open
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="column">
|
|
||||||
<h3>Most used languages</h3>
|
|
||||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="460" height="8">
|
|
||||||
<mask id="languages-bar">
|
|
||||||
<rect x="0" y="0" width="460" height="8" fill="white" rx="5"/>
|
|
||||||
</mask>
|
|
||||||
<rect mask="url(#languages-bar)" x="0" y="0" width="460" height="8" fill="#d1d5da"/>
|
|
||||||
</svg>
|
|
||||||
<div class="field horizontal horizontal-wrap fill-width">
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
<div class="field center no-wrap language">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
|
||||||
<div class="placeholder xlarge"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (plugins.languages) { %>
|
||||||
|
<section class="column">
|
||||||
|
<h3>Most used languages</h3>
|
||||||
|
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="460" height="8">
|
||||||
|
<mask id="languages-bar">
|
||||||
|
<rect x="0" y="0" width="460" height="8" fill="white" rx="5"/>
|
||||||
|
</mask>
|
||||||
|
<rect mask="url(#languages-bar)" x="0" y="0" width="460" height="8" fill="#d1d5da"/>
|
||||||
|
</svg>
|
||||||
|
<div class="field horizontal horizontal-wrap fill-width">
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field center no-wrap language">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d1d5da" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (plugins.pagespeed) { %>
|
||||||
|
<div class="row">
|
||||||
|
<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="M14.064 0a8.75 8.75 0 00-6.187 2.563l-.459.458c-.314.314-.616.641-.904.979H3.31a1.75 1.75 0 00-1.49.833L.11 7.607a.75.75 0 00.418 1.11l3.102.954c.037.051.079.1.124.145l2.429 2.428c.046.046.094.088.145.125l.954 3.102a.75.75 0 001.11.418l2.774-1.707a1.75 1.75 0 00.833-1.49V9.485c.338-.288.665-.59.979-.904l.458-.459A8.75 8.75 0 0016 1.936V1.75A1.75 1.75 0 0014.25 0h-.186zM10.5 10.625c-.088.06-.177.118-.266.175l-2.35 1.521.548 1.783 1.949-1.2a.25.25 0 00.119-.213v-2.066zM3.678 8.116L5.2 5.766c.058-.09.117-.178.176-.266H3.309a.25.25 0 00-.213.119l-1.2 1.95 1.782.547zm5.26-4.493A7.25 7.25 0 0114.063 1.5h.186a.25.25 0 01.25.25v.186a7.25 7.25 0 01-2.123 5.127l-.459.458a15.21 15.21 0 01-2.499 2.02l-2.317 1.5-2.143-2.143 1.5-2.317a15.25 15.25 0 012.02-2.5l.458-.458h.002zM12 5a1 1 0 11-2 0 1 1 0 012 0zm-8.44 9.56a1.5 1.5 0 10-2.12-2.12c-.734.73-1.047 2.332-1.15 3.003a.23.23 0 00.265.265c.671-.103 2.273-.416 3.005-1.148z"></path></svg>
|
||||||
|
PageSpeed Insights
|
||||||
|
</h2>
|
||||||
|
<div class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg>
|
||||||
|
<div class="placeholder xlarge"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<div class="row fill-width">
|
||||||
|
<section class="categories">
|
||||||
|
<div class="categorie column">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="50" height="50" class="gauge">
|
||||||
|
<circle class="gauge-base" r="53" cx="60" cy="60"></circle>
|
||||||
|
<text x="60" y="60" dominant-baseline="central" >-</text>
|
||||||
|
</svg>
|
||||||
|
<span class="title">Performance</span>
|
||||||
|
</div>
|
||||||
|
<div class="categorie column">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="50" height="50" class="gauge">
|
||||||
|
<circle class="gauge-base" r="53" cx="60" cy="60"></circle>
|
||||||
|
<text x="60" y="60" dominant-baseline="central" >-</text>
|
||||||
|
</svg>
|
||||||
|
<span class="title">Accessibility</span>
|
||||||
|
</div>
|
||||||
|
<div class="categorie column">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="50" height="50" class="gauge">
|
||||||
|
<circle class="gauge-base" r="53" cx="60" cy="60"></circle>
|
||||||
|
<text x="60" y="60" dominant-baseline="central" >-</text>
|
||||||
|
</svg>
|
||||||
|
<span class="title">Best Practices</span>
|
||||||
|
</div>
|
||||||
|
<div class="categorie column">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="50" height="50" class="gauge">
|
||||||
|
<circle class="gauge-base" r="53" cx="60" cy="60"></circle>
|
||||||
|
<text x="60" y="60" dominant-baseline="central" >-</text>
|
||||||
|
</svg>
|
||||||
|
<span class="title">SEO</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (plugins.habits) { %>
|
||||||
|
<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 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 01-1.484.211c-.04-.282-.163-.547-.37-.847a8.695 8.695 0 00-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.75.75 0 01-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75zM6 15.25a.75.75 0 01.75-.75h2.5a.75.75 0 010 1.5h-2.5a.75.75 0 01-.75-.75zM5.75 12a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-4.5z"></path></svg>
|
||||||
|
Coding habits
|
||||||
|
</h2>
|
||||||
|
<div class="row">
|
||||||
|
<ul class="habits">
|
||||||
|
<li>Use <div class="placeholder inline large"></div> for indents</li>
|
||||||
|
<li>Mostly push code around <div class="placeholder inline"></div></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 30 KiB |
@@ -2,9 +2,7 @@
|
|||||||
export default async function ({login, q}, {data, rest, graphql, plugins}, {s, pending, imports}) {
|
export default async function ({login, q}, {data, rest, graphql, plugins}, {s, pending, imports}) {
|
||||||
|
|
||||||
//Init
|
//Init
|
||||||
const languages = {colors:{}, total:0, stats:{}}
|
const computed = data.computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, svg:{height:355, width:480}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0}, plugins:{}}
|
||||||
const licenses = {favorite:"", used:{}}
|
|
||||||
const computed = data.computed = {commits:0, languages, licenses, svg:{height:505, width:480}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0}, plugins:{}}
|
|
||||||
const avatar = imports.imgb64(data.user.avatarUrl)
|
const avatar = imports.imgb64(data.user.avatarUrl)
|
||||||
|
|
||||||
//Plugins
|
//Plugins
|
||||||
@@ -14,6 +12,8 @@
|
|||||||
imports.plugins.traffic({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.traffic)
|
imports.plugins.traffic({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.traffic)
|
||||||
imports.plugins.habits({login, rest, computed, pending, q}, plugins.habits)
|
imports.plugins.habits({login, rest, computed, pending, q}, plugins.habits)
|
||||||
imports.plugins.selfskip({login, rest, computed, pending, q}, plugins.selfskip)
|
imports.plugins.selfskip({login, rest, computed, pending, q}, plugins.selfskip)
|
||||||
|
imports.plugins.languages({login, data, computed, pending, q}, plugins.languages)
|
||||||
|
imports.plugins.followup({login, data, computed, pending, q}, plugins.followup)
|
||||||
|
|
||||||
//Iterate through user's repositories
|
//Iterate through user's repositories
|
||||||
for (const repository of data.user.repositories.nodes) {
|
for (const repository of data.user.repositories.nodes) {
|
||||||
@@ -22,23 +22,16 @@
|
|||||||
computed.repositories[property] += repository[property].totalCount
|
computed.repositories[property] += repository[property].totalCount
|
||||||
//Forks
|
//Forks
|
||||||
computed.repositories.forks += repository.forkCount
|
computed.repositories.forks += repository.forkCount
|
||||||
//Languages
|
|
||||||
for (const {size, node:{color, name}} of Object.values(repository.languages.edges)) {
|
|
||||||
languages.stats[name] = (languages.stats[name] || 0) + size
|
|
||||||
languages.colors[name] = color || "#ededed"
|
|
||||||
languages.total += size
|
|
||||||
}
|
|
||||||
//License
|
//License
|
||||||
if (repository.licenseInfo)
|
if (repository.licenseInfo)
|
||||||
licenses.used[repository.licenseInfo.spdxId] = (licenses.used[repository.licenseInfo.spdxId] || 0) + 1
|
computed.licenses.used[repository.licenseInfo.spdxId] = (computed.licenses.used[repository.licenseInfo.spdxId] || 0) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
//Compute count for issues and pull requests
|
//Compute licenses stats
|
||||||
for (const property of ["issues", "pr"])
|
computed.licenses.favorite = Object.entries(computed.licenses.used).sort(([an, a], [bn, b]) => b - a).slice(0, 1).map(([name, value]) => name) || ""
|
||||||
computed.repositories[`${property}_count`] = computed.repositories[`${property}_open`] + computed.repositories[`${property}_${property === "pr" ? "merged" : "closed"}`]
|
|
||||||
|
|
||||||
//Compute total commits and sponsorships
|
//Compute total commits and sponsorships
|
||||||
computed.commits = data.user.contributionsCollection.totalCommitContributions + data.user.contributionsCollection.restrictedContributionsCount
|
computed.commits += data.user.contributionsCollection.totalCommitContributions + data.user.contributionsCollection.restrictedContributionsCount
|
||||||
computed.sponsorships = data.user.sponsorshipsAsSponsor.totalCount + data.user.sponsorshipsAsMaintainer.totalCount
|
computed.sponsorships = data.user.sponsorshipsAsSponsor.totalCount + data.user.sponsorshipsAsMaintainer.totalCount
|
||||||
|
|
||||||
//Compute registration date
|
//Compute registration date
|
||||||
@@ -47,15 +40,6 @@
|
|||||||
const months = Math.ceil((diff-years)*12)
|
const months = Math.ceil((diff-years)*12)
|
||||||
computed.registration = years ? `${years} year${s(years)} ago` : `${months} month${s(months)} ago`
|
computed.registration = years ? `${years} year${s(years)} ago` : `${months} month${s(months)} ago`
|
||||||
|
|
||||||
//Compute languages stats
|
|
||||||
Object.keys(languages.stats).map(name => languages.stats[name] /= languages.total)
|
|
||||||
languages.favorites = Object.entries(languages.stats).sort(([an, a], [bn, b]) => b - a).slice(0, 8).map(([name, value]) => ({name, value, color:languages.colors[name], x:0}))
|
|
||||||
for (let i = 1; i < languages.favorites.length; i++)
|
|
||||||
languages.favorites[i].x = languages.favorites[i-1].x + languages.favorites[i-1].value
|
|
||||||
|
|
||||||
//Compute licenses stats
|
|
||||||
licenses.favorite = Object.entries(licenses.used).sort(([an, a], [bn, b]) => b - a).slice(0, 1).map(([name, value]) => name) || ""
|
|
||||||
|
|
||||||
//Compute calendar
|
//Compute calendar
|
||||||
computed.calendar = data.user.calendar.contributionCalendar.weeks.flatMap(({contributionDays}) => contributionDays).slice(0, 14).reverse()
|
computed.calendar = data.user.calendar.contributionCalendar.weeks.flatMap(({contributionDays}) => contributionDays).slice(0, 14).reverse()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user