YOKOYAMA Taku104 downloadsAnimals gather in a side panel while you stay focused on your notes. The longer you focus, the more show up. A paid activation code unlocks rarer animals.
Private. This is the source repository for Focus Safari. Users install the plugin from the public distribution repository, seekseep/focus-safari, which holds only
manifest.json,versions.json,styles.cssand the releases. See Releasing.
A small, playful side panel: the longer you stay focused on your notes, the more animals wander into your safari.
💳 Paid features
The Pro animals require a paid activation code. Everything else — the timer, the panel, the six free animals — is fully functional without paying.
🔌 Network use
None. Activation codes are Ed25519-signed and verified entirely offline against a public key embedded in the plugin. Nothing is sent anywhere, and no telemetry is collected.
While the app window has focus, one second of focus time is added per second. Large jumps (for example after the machine wakes from sleep) are clamped to two seconds per tick. When your cumulative focus time crosses an animal's threshold, that animal arrives and you get a notice.
| Animal | Focus required | Tier |
|---|---|---|
| 🐜 Ant | 30s | Free |
| 🐁 Mouse | 2m | Free |
| 🐇 Rabbit | 5m | Free |
| 🦁 Lion | 10m | Pro |
| 🦊 Fox | 15m | Free |
| 🐘 Elephant | 20m | Pro |
| 🦌 Deer | 30m | Free |
| 🐻 Bear | 1h | Free |
| 🐼 Panda | 1.5h | Pro |
| 🦩 Flamingo | 3h | Pro |
The full roster lives in src/animals.ts.
Open the panel from the paw-print ribbon icon, or run the Open safari command. Focus time keeps accumulating whether or not the panel is open.
Settings let you paste an activation code, remove it again, or reset the safari (clears focus time and sends every animal home).
main.js, manifest.json and styles.css from the
latest release.<Vault>/.obsidian/plugins/focus-safari/.npm install
npm run dev # watch build
npm run build # production build -> main.js
Animals are drawn with Twemoji rather than the platform emoji
font, so the safari looks the same on every OS. The SVGs are inlined into the bundle
(src/twemoji-assets.ts) — nothing is fetched at runtime. After changing
the roster in src/animals.ts, regenerate them:
node tools/fetch-twemoji.mjs
Key generation and code issuance live in a separate repository,
focus-safari-licensing — this repo only
verifies codes (src/license.ts) against the public key committed in
src/public-key.ts.
LICENSE_PRIVATE_KEY_PATH points to. Losing it makes future issuance impossible;
replacing it invalidates every code already delivered.src/public-key.ts must stay in step with the signing key. npm run key:check derives the
public key from LICENSE_PRIVATE_KEY_PATH (default ~/.config/focus-safari/private.key) and
fails on drift. It skips silently where no key is present, so CI is unaffected.Two checks guard the contract with the issuer. Run both after touching src/license.ts:
npm run key:check # embedded public key still matches the signing key
npm run verify:roundtrip # sign codes like the issuer does, verify with the real verifier
verify:roundtrip bundles src/license.ts itself, so it tests the shipped logic rather than a
copy of it, and asserts the negative cases too (wrong product, expired, tampered, malformed).
Codes carry product: "focus-safari". Builds up to 1.0.4 expected the old id
"obsidian-safari" and will reject anything issued now — activation requires 1.0.5 or later.
Say so in the release notes.
To test activation end to end, run a Stripe test purchase from the licensing repo and paste the resulting code into Settings → Focus Safari → Activation code.
Never edit package.json, manifest.json and versions.json by hand — that is how
1.0.3 and 1.0.4 shipped a manifest still declaring 1.0.2. Use npm version, which runs
tools/version-sync.mjs and keeps all three in step:
npm version patch # or minor / major
git push origin main --follow-tags
The tag must be bare semver — Obsidian forbids a v prefix, and
tools/check-release-version.mjs rejects one. npm tags as v1.2.3 by default, so
.npmrc sets an empty tag-version-prefix. Keep that file; without it every
npm version produces a tag the release cannot accept.
1.0.3 and 1.0.4 are missing from versions.json on purpose: both shipped a manifest still
declaring 1.0.2, so neither ever existed as a version Obsidian could install. Do not
backfill them.
Pushing the tag runs .github/workflows/release.yml, which:
package.json, manifest.json and versions.json all agree
(tools/check-release-version.mjs) and fails the release if they do not;main.js;main.js, manifest.json and styles.css with cosign (see below);manifest.json, styles.css and versions.json to the public repo's main,
refusing to publish any file outside those three;*.sigstore.json bundles.Nothing from src/ or tools/ is ever copied to the public repo. main.js is build output
and is a release asset only — it is committed nowhere. manifest.json and versions.json do
get committed because Obsidian reads them from the public repo's default branch.
PUBLIC_REPO_TOKENA fine-grained personal access token scoped to seekseep/focus-safari only, with
Contents: Read and write. Stored as an Actions secret on this repository. It expires —
renew it before the expiry date or releases will start failing at the checkout step.
actions/attest-build-provenance cannot be used here: GitHub's attestation store rejects
user-owned private repositories (Feature not available for user-owned private repositories),
and create-storage-record: false does not bypass it. The workflow signs with
cosign attest-blob --new-bundle-format instead, which writes an in-toto SLSA provenance
statement to the Sigstore public-good instance and emits a bundle file per artifact.
Because the bundle travels with the release, anyone can verify offline — no cosign, no authentication, and no access to this repository:
gh attestation verify main.js \
--bundle main.js.sigstore.json \
--repo seekseep/focus-safari-source
Verification fails if the artifact is modified or if a different --repo is claimed.
Note what this does and does not prove. The source is private, so provenance cannot make the
build reproducible or auditable by a third party. What it establishes is that a given
main.js came out of this repository's release workflow at a given tag and has not been
altered since — tamper-evidence, not transparency.
Proprietary — see LICENSE. Versions up to 1.0.4 were released under MIT.
The bundled animal graphics come from Twemoji, © Twitter, Inc and other contributors, licensed under CC-BY 4.0.
ノートに集中している時間が溜まるほど、サイドパネルの「サファリ」に動物が集まってくるプラグインです。無料で6種類、Pro(有料のアクティベーションコードが必要)でさらに10種類のレア動物が登場します。
コードの検証は Ed25519 署名によるオフライン検証で、ネットワーク通信・テレメトリは一切ありません。閾値の調整は src/animals.ts を書き換えるだけです。