Initial commit

This commit is contained in:
hibna
2026-04-25 23:58:58 +03:00
commit 9d06660901
13 changed files with 528 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
# Source Presences
Public registry of Source Presence extensions, served from
[`gits.hibna.com.tr/hibna/source-presences`](https://gits.hibna.com.tr/hibna/source-presences).
The Source Presence browser extension reads:
- `index.json` — list of presences and their current versions.
- `presences/<id>/metadata.json` — presence manifest.
- `presences/<id>/dist/presence.js` — built CJS module (produced by `npm run build`).
- `presences/<id>/<icon>` — optional 128×128 PNG.
Both `index.json` and each presence's `dist/presence.js` are committed by the
Gitea Actions workflow at [`.gitea/workflows/build.yml`](.gitea/workflows/build.yml)
on every push to `main`.
## Add a new presence
```bash
mkdir -p presences/<id>/src
cat > presences/<id>/metadata.json <<'JSON'
{
"id": "<id>",
"name": "<Display Name>",
"description": "<short description>",
"version": "1.0.0",
"author": "<your name>",
"match": ["https://example.com/*"],
"tickInterval": 1000,
"sdkVersion": "^0.1.0"
}
JSON
```
Write your TypeScript entry at `presences/<id>/src/presence.ts`:
```ts
import { ActivityType, definePresence } from "@source/presence-sdk";
export default definePresence({
match: ["https://example.com/*"],
tick(ctx) {
ctx.setActivity({
type: ActivityType.WATCHING,
name: "Example",
details: document.title,
});
},
});
```
Push to `main`. Gitea Actions builds the presence, regenerates `index.json`,
and commits the artifacts back. The extension picks up the new version on its
next 6-hour refresh, or immediately when users hit "Refresh" / "Update all".
## Local build
```bash
npm install
npm run build # builds every presence under presences/*
npm run index # regenerates index.json
npm run release # both
```
## Presence API
`PresenceDefinition` shape (also defined in [`types/presence-sdk.d.ts`](types/presence-sdk.d.ts)):
| Field | Required | Description |
|----------------|----------|--------------------------------------------------------------------|
| `match` | yes | Array of [match patterns](https://developer.chrome.com/docs/extensions/reference/match_patterns). |
| `tickInterval` | no | Override default 1000ms tick. |
| `onLoad(ctx)` | no | Runs once when a matching tab loads the presence. |
| `tick(ctx)` | no | Runs every `tickInterval`; call `ctx.setActivity(...)` to publish. |
| `onUnload(ctx)`| no | Runs when the presence is torn down (tab nav / disable). |
`PresenceContext`:
- `ctx.url` / `ctx.tabActive` — read-only state.
- `ctx.setActivity(activity)` / `ctx.clear()` — publish or clear.
- `ctx.storage` — namespaced async storage (`get` / `set` / `delete` / `clear`).
- `ctx.log` / `warn` / `error` — surfaced in the extension's background console.
## Activity payload
```ts
{
type: ActivityType.PLAYING | LISTENING | WATCHING,
name: string,
details?: string,
state?: string,
url?: string,
largeImage?: string,
largeText?: string,
smallImage?: string,
smallText?: string,
startedAt?: number, // unix ms — Source renders an elapsed timer
endsAt?: number, // unix ms — Source renders a remaining timer
}
```
## Notes
- Presences run in the **content script's isolated world** with full DOM read
access. They cannot access page JS globals.
- The extension debounces identical payloads; calling `setActivity` with the
same data on every tick is fine.
- Keep presence builds small (< 100 KB). They're shipped with every install.