個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A personal bookmark organizer for reels, shorts, and links you find on social media, save them into folders, come back later, tap through to the original post.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Stop losing the good stuff. That recipe reel, the 20-minute workout, the thread you swore you'd read later. Backpocket catches everything you find on Instagram, TikTok, YouTube, X and the rest of the web, keeps it tidy, and actually gets you to come back to it.
Free, private by default, and it installs like an app in a few seconds: https://backpocketapp.pages.dev/
Save from anywhere, in one tap. Share straight from Instagram, TikTok, YouTube, Facebook or X on Android. Use a ready-to-go Shortcut on iPhone, or a bookmarklet on your computer. Or just paste a link anywhere in the app. No account needed.
It organises itself. Nested, colour-coded folders keep things neat, and anything you don't file lands in your Inbox. Turn on AI and it files new saves for you, writes a clean title and adds tags, without ever overriding what you typed.
You'll actually come back to it. This is the part other bookmark apps forget:
Find anything in seconds. Instant search across titles, notes, captions, tags and folders. With AI, search by meaning ("that chess opening video", "tiktok from last week"). On any save, More like this finds its closest matches, and you get a heads-up when you save something very similar to what you already have.
Turn a folder into a plan. Saved ten videos about learning to cook? Make a plan turns them into a step-by-step checklist, with every step linked back to the saves it came from.
Private by default. Without an account, your saves live only in your browser on your device. Backpocket has no copy of them, and Settings says so plainly. Sign in only if you want sync across devices.
Looks great, works everywhere. A calm dark "obsidian" design with warm amber accents, plus a light mode. It works offline, installs on your phone or computer, and is built for real thumbs and real eyes: 44px tap targets, visible keyboard focus, readable contrast and reduced-motion support.
utm_*, igsh and si is stripped, youtu.be expanded), so re-saving shows "Already saved in X · Open / Save anyway". With AI on, near-duplicates from different links get flagged too.Once installed from Chrome, Backpocket appears in the Android share sheet.
If the shared content has no http(s) link, Backpocket shows an error under the link field so you can paste the link yourself.
Installed Backpocket before share support was added? Uninstall it (long-press the icon → Uninstall, or Chrome → Settings → Apps), then reinstall it from the site. Android only registers share targets at install time. Your saves aren't affected: they live in Chrome's storage for the site, not in the installed app. Don't clear the site's data while doing this.
| Chrome | Brave | |
|---|---|---|
| App (desktop and Android) | ✅ | ✅ |
| Install as an app | ✅ desktop and Android | ✅ desktop. On Android it's a home-screen shortcut only |
| Appears in the Android share sheet | ✅ | ❌ Brave on Android can't create WebAPKs (brave-browser#7357), and Android only registers share targets for WebAPKs |
| Works with Shields / ad blockers on | n/a | ✅ Fonts fall back to system fonts and favicons fall back to platform icons if they're blocked |
To use the share sheet on Android, install Backpocket from Chrome, even if Brave is your everyday browser. Each browser keeps its own storage, so saves in Brave don't show up in the Chrome-installed app. Sign in to the same account in both and they sync, or use Backup & import to move them across without an account.
chrome://webapks in Chrome on the phone. Backpocket should be listed, with Manifest URL ending in /manifest.webmanifest.
manifest.json: the install came from an older version of the page. Uninstall it, open the site in Chrome, reload once, then install again.iOS doesn't let web apps join the share sheet, so Backpocket uses a free Apple Shortcut instead (no App Store). Follow the step-by-step guide, also linked from Settings → Add to iPhone share sheet. Backpocket suggests it once the first time you open it on an iPhone. The Shortcut opens https://backpocketapp.pages.dev/?url=<link>, which goes through the same path as an Android share.
The Shortcut opens Safari, not the home-screen app, and iOS keeps their storage separate. Sign in to the same account in both so saves made through the Shortcut sync to the home-screen app.
Settings → Save from other apps has a bookmarklet. Drag it to your bookmarks bar, then click it on any page to save that page. Pasting a link anywhere in the app (outside a text field) opens it in the Save sheet, N starts a new save, and / jumps to search.
Turn them on in Settings → Reminders. At most one nudge a day.
AI is optional and needs a signed-in account. Guests get keyword search and everything else except AI. One switch in Settings turns it all off.
| Account | How AI calls run |
|---|---|
| Guest | No AI. The app shows "Sign in to use AI". |
| Signed in and on the allowlist | Through the ai Supabase Edge Function, using the project's Gemini key. |
| Signed in, not on the allowlist | The app offers Use your own free Gemini key (from Google AI Studio). The key stays in your browser, and calls go straight from the browser to Google, never through Backpocket's server or its quota. |
The model is Gemini 3.5 Flash-Lite with the lowest thinking level and structured JSON output. Embeddings use gemini-embedding-2 at 768 dimensions. Both kinds of access share the same prompts, schemas and validation (supabase/functions/_shared/), so they behave the same.
Rate limits are token buckets in Postgres, so a burst across a minute boundary can't get around them. Each user gets 5 requests at once, then one every 6 seconds, up to 150 a day. A global bucket and a global daily cap keep all users together under the free tier. Going over returns HTTP 429 with Retry-After, and the app shows "Slow down, try again in Xs". Bring-your-own-key users get the same limits in the browser, to protect their own quota. Costs per action (sort 1, search 1, embed 1, nudge 1, recap 2, plan 3) and every threshold are in supabase/functions/_shared/config.ts.
localStorage) and is only ever sent to Google.dist/.On the device, everything lives in the IndexedDB database backpocket:
| Store | Holds |
|---|---|
folders | id, parentId, name, color, isSystem (Inbox), position, timestamps |
links | id, folderId, url, platform, title, note, sharedText (the shared caption), thumbnailUrl, tags, status, openedAt, aiMeta, timestamps |
plans | id, folderId, title, summary, items ({text, done, linkIds}), timestamps |
meta | settings, schema version, sync cursors, and reminder state (nudgeAt, nudgeNext, nudgeHistory, recap, recapNotifiedAt) |
outbox | rows changed locally that haven't been pushed to Supabase yet |
Deletes are soft (deletedAt) so they can sync. A few small things stay in localStorage: backpocket_theme (read before first paint), backpocket-auth (the Supabase session), and backpocket_gemini_key plus its rate-limit counters if you bring your own key.
Upgrading from v1: on first load, v2 copies everything from the old localStorage key backpocket_data_v1 into IndexedDB. Links at the root move into Inbox and every id becomes a UUID. The old key is left untouched, and a raw copy is also kept as backpocket_data_v1_backup.
In the cloud (signed in), the Supabase tables folders, links and plans mirror the local stores, plus an embedding vector(768) column on links. Sync pulls rows changed since the last sync and pushes the outbox. If two devices edit the same row, the last write wins. Sync runs on load, when the app regains focus, when it comes back online, and shortly after each change. Settings (tap your avatar, top right) shows the status at the top ("Synced · 2 min ago" / "Offline - changes will sync"). On first sign-in with local saves, the app offers to upload them, skipping duplicates and merging folders by path.
Backup & import exports a JSON backup (backpocket-backup-YYYY-MM-DD.json) or a bookmarks HTML file, and imports either one, or a v1 backup. Clearing site data removes the local copy. If you're signed in, it comes back from your account on the next sync. If you're a guest, only a backup brings it back.
Vite + TypeScript, with no UI framework. Also idb for IndexedDB, @supabase/supabase-js, a hand-written service worker built with vite-plugin-pwa (injectManifest), and Vitest. The backend is Supabase (Postgres + pgvector, Auth, Edge Functions in Deno). See CLAUDE.md for conventions and gotchas.
npm i
npm run dev # http://localhost:5173/Backpocket/ (guest mode unless .env is set)
npm test # Vitest
npm run build # type-check + production build into dist/
npm run preview # serve dist/ with the service worker on
npm run check:functions # type-check the Edge Functions with Deno
Merging develop into release runs .github/workflows/release.yml, which tests, builds, publishes to GitHub Pages and tags the next version (v2.0.1, ...). Cloudflare Pages builds the same branch on its own. To roll back GitHub Pages, run that workflow by hand with an older tag; it waits for approval. Roll back Cloudflare from its dashboard (Deployments, Rollback). Backend changes deploy separately:
npx supabase db push # apply new migrations
npx supabase functions deploy ai # redeploy the AI Edge Function
src/
main.ts, actions.ts, config.ts, platform.ts, sw.ts
styles/ app.css: the whole design system (tokens for light + dark, then components)
data/ IndexedDB store, v1 migration, sync, backup, URL normalisation, reminders (nudge.ts), weekly recap (recap.ts)
cloud/ Supabase client and auth
ai/ Edge Function client, bring-your-own-key client, sort on save, search, embeddings, related.ts
search/ keyword filters and query parsing
share/ share-target URL extraction (+ tests against the v1 code)
ui/ tab bar, settings page, library, queue, list, sheets, modals, settings, reminders, recap
public/ manifest.webmanifest, icons/ (all rendered from icons/mark.svg)
supabase/
migrations/ schema, RLS, pgvector, rate limits, match_links, related_links, revisit_picks
functions/ai/ classify / search / plan / embed / nudge / recap
functions/delete-account/
functions/_shared/ config, prompts and schemas shared with the browser
public/manifest.webmanifest declares a GET share_target pointing at ./index.html, with title, text and url params. All paths are relative, so it works under /Backpocket/ without hardcoding the repo name.text, so most social apps send the link there, sometimes inside a caption ("Check out … https://vm.tiktok.com/…"). src/share/extractSharedUrl.ts checks url, then text, then title, and takes the first valid http/https URL. It drops trailing punctuation but keeps balanced parentheses. The iPhone Shortcut and the bookmarklet use the same ?url= path.history.replaceState, so a reload or Back doesn't apply them again.src/sw.ts, built to sw.js, the same URL as v1) fetches the page and manifest from the network first and uses the cache only offline, so the first visit after a deploy never installs last release's manifest. Page loads are cached by path, ignoring the query string, so a share launch still opens offline. Fonts and YouTube/TikTok thumbnails are cached at runtime. Supabase, Gemini and oEmbed calls are never served from the cache. The cache name is derived from the build's file list, so every deploy refreshes the offline copy automatically. The same worker handles the background reminder (periodicsync) and notification taps.public/icons/, all rendered from mark.svg (also #brand-mark in index.html). favicon.svg is the browser-tab icon: a light tile on light systems, a dark tile on dark ones. Home-screen icons can't switch with the theme, so the PNGs use the dark tile: icon-192.png and icon-512.png (purpose: any), plus icon-512-maskable.png, which keeps the artwork inside the 80% safe zone on an opaque background so Android's adaptive-icon masks don't clip it.