# 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//metadata.json` — presence manifest. - `presences//dist/presence.js` — built CJS module (produced by `npm run build`). - `presences//` — 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//src cat > presences//metadata.json <<'JSON' { "id": "", "name": "", "description": "", "version": "1.0.0", "author": "", "match": ["https://example.com/*"], "tickInterval": 1000, "sdkVersion": "^0.1.0" } JSON ``` Write your TypeScript entry at `presences//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.