Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
branches: [main, deploy]

jobs:
build:
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ Customize the appearance of your Activity Graph however you want with URL params
| `from` | date from which the graph starts | format `YYYY-MM-DD` |
| `to` | date where the graph will end | format `YYYY-MM-DD` |
| `grid` | show grid | boolean (default: `true`) |
| `ghosts` | number of prior windows to overlay as | number (0 - 6 inclusive, |
| | faded grey "ghost" lines behind the current | default: `0`) |
| | line | |

⚠ **For `custom_title` please make sure that you are using %20 for spaces**

Expand All @@ -122,6 +125,20 @@ Example:
[![Ashutosh's github activity graph](https://github-readme-activity-graph.vercel.app/graph?username=ashutosh00710&bg_color=fffff0&color=708090&line=24292e&point=24292e&area=true&border_color=ff0000)](https://github.com/ashutosh00710/github-readme-activity-graph)
```

### Ghost lines (`ghosts=N`)

Add `&ghosts=N` to overlay the previous `N` contribution windows as faded grey "ghost" lines behind the current line, so you can see at a glance how this window's pace compares to previous ones:

```md
[![Ashutosh's github activity graph](https://github-readme-activity-graph.vercel.app/graph?username=ashutosh00710&ghosts=2)](https://github.com/ashutosh00710/github-readme-activity-graph)
```

- Each ghost is the current window shifted back by its own length — a 31-day graph with `ghosts=2` shows the current 31 days plus the two 31-day windows before it — index-aligned on the X-axis. Works with any `days` value and custom `from`/`to`.
- Ghosts are drawn in a fixed grey (`#9e9e9e`, ~35% opacity, thinner stroke) regardless of theme, with no points or area fill.
- Ghost data shares the Y-axis scale, so a busy prior window can flatten a quiet current one.
- X-axis labels reflect the current window only; ghosts are unlabeled.
- GitHub only serves contributions for about the last year, so ghost windows reaching further back are skipped — fewer lines than requested may appear for long windows.

## Deploy on your own Replit instance

<details>
Expand Down
220 changes: 215 additions & 5 deletions __test__/__snapshots__/svgs.test.ts.snap

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion __test__/__snapshots__/utils.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`Utilities Test Graph Generation 1`] = `"<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="10" height="5" class="ct-chart-line"><g class="ct-grids"><line x1="50" x2="50" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.2" x2="50.2" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.4" x2="50.4" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.6" x2="50.6" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.8" x2="50.8" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line y1="16" y2="16" x1="50" x2="51" class="ct-grid ct-vertical ct-grid-major"></line></g><g><g class="ct-series ct-series-a"><path d="M50,15.9C50.067,15.883,50.133,15.879,50.2,15.85C50.267,15.821,50.333,15.552,50.4,15.5C50.467,15.448,50.533,15.433,50.6,15.4C50.667,15.367,50.733,15.333,50.8,15.3" class="ct-line"></path><line x1="50" y1="15.9" x2="50.01" y2="15.9" class="ct-point" ct:value="2"></line><line x1="50.2" y1="15.85" x2="50.21" y2="15.85" class="ct-point" ct:value="3"></line><line x1="50.4" y1="15.5" x2="50.41" y2="15.5" class="ct-point" ct:value="10"></line><line x1="50.6" y1="15.4" x2="50.61" y2="15.4" class="ct-point" ct:value="12"></line><line x1="50.8" y1="15.3" x2="50.809999999999995" y2="15.3" class="ct-point" ct:value="14"></line></g></g><g class="ct-labels"><text x="50" y="36" width="0.2" height="20" class="ct-label ct-horizontal ct-end">1</text><text x="50.2" y="36" width="0.2" height="20" class="ct-label ct-horizontal ct-end">2</text><text x="50.4" y="36" width="0.20000000000000007" height="20" class="ct-label ct-horizontal ct-end">3</text><text x="50.6" y="36" width="0.19999999999999996" height="20" class="ct-label ct-horizontal ct-end">4</text><text x="50.8" y="36" width="30" height="20" class="ct-label ct-horizontal ct-end">5</text><text y="16" x="40" height="30" width="30" class="ct-label ct-vertical ct-start">0</text></g></svg>"`;
exports[`Utilities Test Graph Generation 1`] = `"<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="10" height="5" class="ct-chart-line"><g class="ct-grids"><line x1="50" x2="50" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.2" x2="50.2" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.4" x2="50.4" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.6" x2="50.6" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line x1="50.8" x2="50.8" y1="15" y2="16" class="ct-grid ct-horizontal"></line><line y1="16" y2="16" x1="50" x2="51" class="ct-grid ct-vertical ct-grid-major"></line></g><g><g ct:series-name="current" class="ct-series ct-series-current"><path d="M50,15.9C50.067,15.883,50.133,15.879,50.2,15.85C50.267,15.821,50.333,15.552,50.4,15.5C50.467,15.448,50.533,15.433,50.6,15.4C50.667,15.367,50.733,15.333,50.8,15.3" class="ct-line"></path><line x1="50" y1="15.9" x2="50.01" y2="15.9" class="ct-point" ct:value="2"></line><line x1="50.2" y1="15.85" x2="50.21" y2="15.85" class="ct-point" ct:value="3"></line><line x1="50.4" y1="15.5" x2="50.41" y2="15.5" class="ct-point" ct:value="10"></line><line x1="50.6" y1="15.4" x2="50.61" y2="15.4" class="ct-point" ct:value="12"></line><line x1="50.8" y1="15.3" x2="50.809999999999995" y2="15.3" class="ct-point" ct:value="14"></line></g></g><g class="ct-labels"><text x="50" y="36" width="0.2" height="20" class="ct-label ct-horizontal ct-end">1</text><text x="50.2" y="36" width="0.2" height="20" class="ct-label ct-horizontal ct-end">2</text><text x="50.4" y="36" width="0.20000000000000007" height="20" class="ct-label ct-horizontal ct-end">3</text><text x="50.6" y="36" width="0.19999999999999996" height="20" class="ct-label ct-horizontal ct-end">4</text><text x="50.8" y="36" width="30" height="20" class="ct-label ct-horizontal ct-end">5</text><text y="16" x="40" height="30" width="30" class="ct-label ct-vertical ct-start">0</text></g></svg>"`;
209 changes: 192 additions & 17 deletions __test__/fakeInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,26 @@ export let fakeQueryString = [
hide_title: false,
custom_title: undefined,
},
{
username: 'githubusername',
ghosts: '2',
},
{
username: 'githubusername',
ghosts: '9',
},
{
username: 'githubusername',
ghosts: '2.7',
},
{
username: 'githubusername',
ghosts: '-3',
},
{
username: 'githubusername',
ghosts: 'not-a-number',
},
];

export let fakeQueryStringRes = [
Expand All @@ -116,6 +136,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -136,6 +157,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -156,6 +178,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -176,6 +199,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -196,6 +220,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -216,6 +241,7 @@ export let fakeQueryStringRes = [
hide_title: false,
area: true,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -236,6 +262,7 @@ export let fakeQueryStringRes = [
area: true,
hide_title: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -256,6 +283,7 @@ export let fakeQueryStringRes = [
area: true,
hide_title: true,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -277,6 +305,7 @@ export let fakeQueryStringRes = [
hide_title: false,
custom_title: 'some title',
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand All @@ -297,6 +326,112 @@ export let fakeQueryStringRes = [
area: true,
hide_title: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
},
{
username: 'githubusername',
radius: 0,
height: 420,
colors: {
areaColor: '9e4c98',
bgColor: 'ffcfe9',
borderColor: 'ffffff',
color: '9e4c98',
titleColor: '9e4c98',
lineColor: '9e4c98',
pointColor: '403d3d',
},
hide_title: false,
area: false,
days: 31,
ghosts: 2,
from: '',
to: '',
grid: true,
},
{
username: 'githubusername',
radius: 0,
height: 420,
colors: {
areaColor: '9e4c98',
bgColor: 'ffcfe9',
borderColor: 'ffffff',
color: '9e4c98',
titleColor: '9e4c98',
lineColor: '9e4c98',
pointColor: '403d3d',
},
hide_title: false,
area: false,
days: 31,
ghosts: 6,
from: '',
to: '',
grid: true,
},
{
username: 'githubusername',
radius: 0,
height: 420,
colors: {
areaColor: '9e4c98',
bgColor: 'ffcfe9',
borderColor: 'ffffff',
color: '9e4c98',
titleColor: '9e4c98',
lineColor: '9e4c98',
pointColor: '403d3d',
},
hide_title: false,
area: false,
days: 31,
ghosts: 2,
from: '',
to: '',
grid: true,
},
{
username: 'githubusername',
radius: 0,
height: 420,
colors: {
areaColor: '9e4c98',
bgColor: 'ffcfe9',
borderColor: 'ffffff',
color: '9e4c98',
titleColor: '9e4c98',
lineColor: '9e4c98',
pointColor: '403d3d',
},
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
},
{
username: 'githubusername',
radius: 0,
height: 420,
colors: {
areaColor: '9e4c98',
bgColor: 'ffcfe9',
borderColor: 'ffffff',
color: '9e4c98',
titleColor: '9e4c98',
lineColor: '9e4c98',
pointColor: '403d3d',
},
hide_title: false,
area: false,
days: 31,
ghosts: 0,
from: '',
to: '',
grid: true,
Expand Down Expand Up @@ -336,32 +471,45 @@ export let options = {
title: '',
area: false,
plugins: [majorGridPlugin],
series: {} as { [name: string]: { showPoint?: boolean; showArea?: boolean } },
};

export const expectedQuery = (username: string, from: string, to: string) => {
export const expectedQuery = (username: string, windows: Array<{ from: string; to: string }>) => {
const variableDefinitions = windows
.map((_, i) => `$FROM${i}: DateTime!, $TO${i}: DateTime!`)
.join(', ');
const contributions = windows
.map(
(_, i) => `
c${i}: contributionsCollection(from: $FROM${i}, to: $TO${i}) {
contributionCalendar {
weeks {
contributionDays {
contributionCount
date
}
}
}
}`,
)
.join('');
const variables: { LOGIN: string; [name: string]: string } = {
LOGIN: username,
};
windows.forEach((window, i) => {
variables[`FROM${i}`] = window.from;
variables[`TO${i}`] = window.to;
});
return {
query: `
query userInfo($LOGIN: String!, $FROM: DateTime!, $TO: DateTime!) {
query userInfo($LOGIN: String!, ${variableDefinitions}) {
user(login: $LOGIN) {
name
contributionsCollection(from: $FROM, to: $TO) {
contributionCalendar {
weeks {
contributionDays {
contributionCount
date
}
}
}
}
${contributions}
}
}
`,
variables: {
LOGIN: username,
FROM: from,
TO: to,
},
variables,
};
};

Expand Down Expand Up @@ -525,6 +673,33 @@ export const dummyWeeksData = [
},
];

// A 14-day window: fewer days than the 31-day current window, so the
// fetcher must front-pad it with zero days to keep the series aligned.
export const dummyGhostWeeksData = [
{
contributionDays: [
{ contributionCount: 7 },
{ contributionCount: 3 },
{ contributionCount: 1 },
{ contributionCount: 0 },
{ contributionCount: 5 },
{ contributionCount: 2 },
{ contributionCount: 4 },
],
},
{
contributionDays: [
{ contributionCount: 6 },
{ contributionCount: 6 },
{ contributionCount: 0 },
{ contributionCount: 1 },
{ contributionCount: 8 },
{ contributionCount: 3 },
{ contributionCount: 2 },
],
},
];

export const themes = {
dracula: {
areaColor: 'ff79c6',
Expand Down
Loading
Loading