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
248 changes: 83 additions & 165 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,88 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="gitcomment.png">
<img src="gitcomment-light.png" alt="GitComment" width="280">
<img src="gitcomment-light.png" alt="GitComment" width="260">
</picture>
</p>

# GitComment

A comments for your GitHub profile. People leave you a message with the GitHub
account they already have, and it shows up near the bottom of your profile README.
A comment section for your GitHub profile. Visitors leave you a message using
the GitHub account they already have, and it shows up in your profile README.

<table>
<tr><td colspan="2"><b>Comments</b><br><sub>little messages from people passing by.</sub></td></tr>
<tr><td width="60" align="center" valign="top"><a href="https://github.com/octocat"><img src="https://avatars.githubusercontent.com/u/583231?s=80&amp;v=4" width="40" height="40" alt="octocat"></a></td><td valign="top"><a href="https://github.com/octocat"><b>octocat</b></a> · <sub>25m ago</sub><br>found you through OpenNPC. really cool project</td></tr>
<tr><td width="60" align="center" valign="top"><a href="https://github.com/monalisa"><img src="https://avatars.githubusercontent.com/u/9919?s=80&amp;v=4" width="40" height="40" alt="monalisa"></a></td><td valign="top"><a href="https://github.com/monalisa"><b>monalisa</b></a> · <sub>19h ago</sub><br>+rep</td></tr>
<tr><td width="60" align="center" valign="top"><a href="https://github.com/hubot"><img src="https://avatars.githubusercontent.com/u/4004?s=80&amp;v=4" width="40" height="40" alt="hubot"></a></td><td valign="top"><a href="https://github.com/hubot"><b>hubot</b></a> · <sub>4d ago</sub><br>Desktop Drifter is sick :)</td></tr>
<tr><td colspan="2" align="center"><sub><a href="#setup">leave a message</a> · <a href="#setup">older messages (9)</a></sub></td></tr>
</table>
<img src="assets/preview/notes.svg" width="880" alt="the notes theme">

<sub>Example output. The accounts above are GitHub's own placeholder accounts.</sub>
Comments live in a GitHub Discussion on your profile repository. When someone
posts, an Action redraws the board and commits it. No server, no database, no
signup, no account to create.

That block is generated. You never edit it by hand, and nobody but you can write
to your README.
## Themes

## How it works
Four of the nine, at a glance.

Comments live in a GitHub Discussion on your profile repository. When somebody
comments, a workflow runs, reads the newest comments over the GraphQL API,
renders them, and commits the result between two markers in your README.
**notes** — the one above. Paper notes, tape, handwriting.

```
Discussion comment
|
v
discussion_comment event
|
v
GitComment action -> reads the Discussion (GraphQL)
| renders the newest N
v
README.md, between the markers
```
**dev**

Your repository stays yours. The Discussion is the full history; the README is a
small window onto the last few messages. Nothing is deleted when it scrolls off.
<img src="assets/preview/dev.svg" width="880" alt="the dev theme">

There is no database, no server, no signup and no GitComment account. A
visitor's GitHub identity is their identity.
**comic**

## Setup
<img src="assets/preview/comic.svg" width="880" alt="the comic theme">

About five minutes.
**pink**

**1. Enable Discussions** on your profile repository — the one named after your
username. Settings, General, Features, Discussions.
<img src="assets/preview/pink.svg" width="880" alt="the pink theme">

**2. Create the comments Discussion.** One discussion, any category. Something
like:
There are five more: `win95` (Windows 95 dialogue boxes), `drawn`, `cozy`,
`steam` and `minimal`. The last four are plain README HTML rather than images,
so every username in them stays clickable.

```
Comments
### Changing theme

Leave a message on my profile.
Edit `.github/gitcomment.yml` in your profile repository:

Say hi, leave some feedback, +rep me, tell me how you found my
profile, or just leave something stupid.
```yaml
theme: dev
variant: dark
```

Note its number from the URL: `/discussions/1` means `1`.
Commit it, then run the workflow once from the Actions tab. That's the whole
process.

`theme` — `notes` `dev` `pink` `comic` `win95` `drawn` `cozy` `steam` `minimal`

`variant` — `dark` `light` `transparent`

`transparent` draws no background and follows whichever GitHub theme the
visitor is using. The four HTML themes ignore `variant`.

All values are lowercase. A wrong one fails the run with a message naming the
valid options, and leaves your README untouched.

## What it does

- Shows the newest few comments; the rest stay in the Discussion
- Real avatars and profile links
- Nine themes, three variants
- Hide or delete a comment and it disappears on the next run
- Blocklist, and an approval mode where nothing appears until you react to it
- Escapes everything a commenter writes, so nobody can inject markup

## Setup

**1.** Enable Discussions on your profile repository — Settings, Features.

**3. Add the markers** to your README, wherever you want the comments:
**2.** Create one Discussion for the comments. Note its number from the URL.

**3.** Add these two lines to your README where you want the board:

```
<!-- GITCOMMENT:START -->
<!-- GITCOMMENT:END -->
```

**4. Add the workflow** at `.github/workflows/comments.yml`:
**4.** Add `.github/workflows/comments.yml`:

```yaml
name: comments
Expand All @@ -97,32 +103,31 @@ jobs:
contents: write
discussions: read
steps:
- uses: OWNER/gitcomment@v1
- uses: LISK819129/GitComment@v1
with:
discussion-number: 1
```

Commit that to your **default branch**. GitHub only fires `discussion_comment`
It has to be on your default branch — GitHub only fires `discussion_comment`
for workflows that live there.

Then run it once by hand from the Actions tab to fill in the README, and leave
yourself a test comment to check the trigger works.
**5.** Actions tab, run it once by hand. Done.

There is no `actions/checkout` step. The action reads and writes through the
API, which also means two people commenting at the same moment can't clobber
each other.
No `actions/checkout` step. The Action reads and writes over the API, which
also means two people commenting at the same moment can't clobber each other.

## Config

All optional. Put this at `.github/gitcomment.yml`:
All optional, at `.github/gitcomment.yml`:

```yaml
title: "Comments"
subtitle: "little messages from people passing by."

theme: cozy
theme: notes
variant: dark

max_comments: 5
max_comments: 6
message_max_length: 240

show_avatar: true
Expand All @@ -132,92 +137,24 @@ moderation: automatic
blocked_users: []
```

`max_comments` is how many appear in the README, not how many are kept. The rest
stay in the Discussion, behind the "older messages" link.

The action also takes `theme` and `max-comments` as inputs, and those win over
the file. The full input list is in `action.yml`.

### Moderation

`automatic` shows every comment as it arrives. You moderate afterwards using
GitHub itself: hide a comment with the Hide menu and GitComment drops it on the
next run, or add the author to `blocked_users`.

`approved` shows nothing until you react to the comment yourself. Add a 👍 and it
appears on the next run.

```yaml
moderation: approved
approval_reaction: heart
```
`max_comments` is how many are drawn, not how many are kept. Nothing is ever
deleted from the Discussion.

Reactions, because they work from the GitHub mobile app and need no extra
tooling. Only reactions from the repository owner count.
Set `moderation: approved` and nothing appears until you add a 👍 to the
comment yourself. Reactions, because they work from the GitHub mobile app.

## Themes
## Limits worth knowing

Three of them are plain README HTML. Two draw an SVG and commit it next to your
README.

**HTML themes** — a couple of kilobytes, every username clickable, avatars
served live from GitHub.

`cozy` is the default: one bordered block, avatar on the left, title and links
built in.

`steam` borrows the shape of a Steam profile comment — name above a small
timestamp, message underneath, an action button at the bottom. The layout only;
no Steam assets, branding or styling.

`minimal` uses no tables at all. Small avatars, blockquoted messages, looks like
the rest of a README.

**Drawn themes** — GitHub strips CSS from README HTML, so anything with tilt,
shadow, tape or a handwriting font has to be an image.

`notes` draws the whole comments as a corkboard: white notes at slight angles,
tape, circular avatars, handwritten margin scribbles. Around 30 KB, rewritten on
every comment. Because it's one image, nothing inside it is clickable, so the
usernames are repeated as a small text row underneath. The board has a dark
background and does not follow GitHub's light theme.

`drawn` is the middle ground. A handwritten header is generated **once** and then
left alone; the comments below it are ordinary HTML rows with live avatars and
working links. You get the handmade framing without rewriting an image every time
somebody says hi.

Message text in both drawn themes uses the reader's system sans. Only the title
and the margin scribbles use the handwriting face, which ships subsetted to the
glyphs it needs (15 KB) and is embedded in the SVG, since GitHub's image proxy
cannot fetch fonts.

## What GitHub actually allows

Worth knowing before filing a bug about something looking off.

- **No CSS, no JavaScript, no iframes.** GitHub strips `style` and `class` from
README HTML. Everything here is built from tags that survive: `table`, `img`,
`a`, `sub`, `b`, `code`, `kbd`, `wbr`, `blockquote`.
- **Avatars can't be circles in the HTML themes.** That needs
`border-radius: 50%` and inline styles are stripped. GitHub applies a 6px
radius to README images itself, so they come out as rounded squares. The drawn
themes clip them to circles inside the SVG instead.
- **No scrolling box.** A README can't have one, so GitComment doesn't fake one.
You get the newest few messages and a link to the rest.
- **Tables size to their content**, so the block is as wide as its widest
message rather than the full column.
- **Discussions are GraphQL-only.** There is no REST API for them.
- **`discussion_comment` only triggers workflows on the default branch**, and
GitHub still lists Discussions webhooks as public preview.
- **SVGs must be a separate file.** GitHub's renderer drops inline `<svg>`, so a
drawn theme commits an `.svg` and the README points an `<img>` at it. The `src`
carries a content hash so GitHub's image cache picks up new versions.
- **Nothing inside an image is clickable**, and an SVG served that way cannot
fetch anything external — which is why avatars are copied into the file.
- **Dates are drawn when the board is rendered**, not when somebody looks at it,
which is why they are absolute (`22 Sep, 2026`) rather than relative. A picture
cannot tell the time.
- The drawn themes are images, so **nothing inside them is clickable** — just a
`leave a message` link underneath. Use `drawn`, `cozy`, `steam` or `minimal`
if you want every username to be a link to their profile.
- Avatars are copied into the image, because GitHub's image proxy blocks
outside requests. If someone changes their picture, yours updates on the next
comment.
- Dates are drawn when the board is rendered, which is why they're absolute
rather than "2h ago". A picture can't tell the time.
- A README can't scroll, so GitComment doesn't fake it. Newest few, then a link
to the rest.

## Development

Expand All @@ -227,34 +164,15 @@ npm test
npm run build
```

To try it against a real Discussion without writing anything:

```
GITHUB_TOKEN=<token> npm run try -- <owner>/<repo> <discussion-number> [theme]
npm run serve
```

That reads the live Discussion, renders it, and drops the result in `tmp/local/`
plus a preview at `http://127.0.0.1:4173`. Nothing is committed. The token only
needs read access.

`npm run preview` renders every theme against the fixtures in
`test/fixtures.ts`, sends the markdown through GitHub's own Markdown API, and
writes `tmp/preview.html` so you can see what GitHub would do with it. The
fixtures include the awkward cases: XSS attempts, marker forgery, bidi
overrides, a deleted account, a 400-character ramble.
`npm run try -- <owner>/<repo> <discussion> ` renders every theme against a real
Discussion without writing anything, then `npm run serve` to look at them.

`npm run build` bundles `src/` into `dist/index.js` with ncc. That file is
committed, because GitHub Actions runs it directly.
`dist/index.js` is committed because GitHub Actions runs it directly.

## Security
## Credits

Comments are untrusted input. Everything is HTML-escaped before rendering and a
small amount of formatting is re-applied afterwards — bold, italic, code, links,
line breaks. Images are reduced to their alt text. Links are limited to http and
https. Control characters and bidirectional overrides are stripped, and messages
are truncated. A commenter cannot forge the markers or inject markup into your
README.
Artwork by [Atul Krishna](https://github.com/LISK819129). Handwriting is
[Caveat](https://fonts.google.com/specimen/Caveat), SIL Open Font License.

## License

Expand Down
66 changes: 66 additions & 0 deletions assets/preview/comic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading