TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
A personal internet workspace for organizing bookmarks, tabs and research —
inspired by Toby and Raindrop.io, built independently, deployed on Vercel,
with GitHub as the persistent data store.
Status: All 7 planned phases implemented (the Next work queue was
intentionally skipped by request). See "Roadmap" and "Hardening" below
for what's been verified vs. what you should check yourself once
deployed.
There is no conventional database. All reads/writes to the data repo happen
server-side only — a GitHub token is never sent to the browser.查看完整 README ↗A personal internet workspace for organizing bookmarks, tabs and research — inspired by Toby and Raindrop.io, built independently, deployed on Vercel, with GitHub as the persistent data store.
Status: All 7 planned phases implemented (the Next work queue was intentionally skipped by request). See "Roadmap" and "Hardening" below for what's been verified vs. what you should check yourself once deployed.
Browser
|
v
Vercel / Next.js (this repo) — App Router, server-side API routes
|
v
GitHub Contents API
|
v
A separate, private GitHub repository (the "data repo")
|
+-- data/*.json current state
+-- backups/*.json timestamped full snapshots
+-- Git history every write is a commit
There is no conventional database. All reads/writes to the data repo happen server-side only — a GitHub token is never sent to the browser.
See src/lib/validation/schemas.ts for the full Zod schemas (Space,
Collection, Resource, Tag, Task, Note, QuickLink, Settings, Metadata).
Every write is validated against these schemas before being committed.
Create a separate, private GitHub repository to hold your data (e.g.
your-username/bookmanager-data). It can start empty — the app creates
data/*.json on first run via "Initialize data files".
Create a fine-grained Personal Access Token: GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens.
Never use a broad/classic token with access to other repositories.
Copy .env.example to .env.local for local development:
cp .env.example .env.local
GITHUB_DATA_OWNER=your-username
GITHUB_DATA_REPO=bookmanager-data
GITHUB_DATA_BRANCH=main
GITHUB_TOKEN=github_pat_...
.env.local is git-ignored and must never be committed.
npm install
npm run dev
Open http://localhost:3000. The Phase 1 diagnostics page lets you:
data/*.json in the data repo if missing.settings.json to confirm writes commit correctly.GITHUB_DATA_OWNER=your-username
GITHUB_DATA_REPO=your-data-repo-name
GITHUB_DATA_BRANCH=main
GITHUB_TOKEN=github_pat_...
Use the same fine-grained PAT described in Setup above, scoped only to
the data repo.*.vercel.app URL (or your
custom domain if you add one)./diagnostics on the deployed URL → Test Connection should
show your data repo/branch. If it doesn't, re-check the four env vars
(a typo in GITHUB_DATA_OWNER/GITHUB_DATA_REPO is the most common
cause of a 404 here).sm breakpoint; a hamburger button appears in a
small top bar).This app has no login system. Anyone who has the deployed URL can read and write all of your bookmark data — there's no username/password, no session, nothing gating the API routes beyond "you know the URL." This was a deliberate V1 scope decision (see the original spec: "single-user first"), not an oversight, but it means the URL itself is your only access control. Before relying on this for anything you'd mind a stranger seeing or editing:
/shared/<token> links, which are meant to be public and only
expose the one Space/Collection you explicitly shared).GITHUB_TOKEN env var as fully sensitive: it's the one thing
standing between the public internet and write access to your data
repo if the app URL ever leaks.Every write commits directly to data/*.json in the data repo, so Git
history there is already a recovery mechanism. In addition:
backups/YYYY/YYYY-MM-DD/backup-HH-mm-ss-<reason>.json
in the data repo and are never deleted automatically.Every write from the app sends the sha of the file it last read. If the
file changed on GitHub in the meantime, the write is rejected with a 409
and the app must re-read before retrying — this prevents silently
overwriting newer data.
GITHUB_TOKEN and other secrets are only read in server-side API routes
(src/app/api/**) and are never included in client bundles..env.local is git-ignored.src/lib/data/store.ts) — a malformed request can't corrupt
the data files.src/lib/client/markdown.ts)
are HTML-escaped before any formatting tag is added, so there's no
stored-XSS path through them — no HTML sanitizer library needed because
nothing raw ever reaches dangerouslySetInnerHTML.src/lib/security/ssrf.ts resolves the hostname and rejects
loopback/private/link-local addresses before fetching. This is a
meaningful reduction of risk, not a complete guarantee (it doesn't
defend against DNS-rebinding, where the same hostname resolves
differently a moment later).next.config.ts (X-Content-Type-Options, X-Frame-Options,
Referrer-Policy, Permissions-Policy).extension/ is a small Manifest V3 browser extension that captures every
open tab's URL in one click (copies them to your clipboard) and can open
the app straight to Save Session for pasting. See extension/README.md
for how to load it as an unpacked extension. It talks to nothing but your
clipboard and your browser's tab list — no new server endpoints, no login.
Manual only — nothing runs automatically or on a schedule. From a resource
card, click the link icon to check just that one; from a selection or
Settings → Link Checking, check many at once. Each check is a HEAD request
(GET only as a fallback) with a configurable timeout, batched at low
concurrency so it doesn't hammer the sites being checked. Statuses:
Healthy, Redirected, Warning (401/403/429/503 — the site is up but
blocking or rate-limiting the check), Dead (4xx/5xx/DNS/timeout), Unknown
(not yet checked). I have not verified the exact serverless function
duration limit on Vercel Hobby for large batches — maxDuration is set to
60s in src/app/api/links/check/route.ts as a best effort; if very large
batches ever time out, check your plan's actual limit and adjust the
client-side batch size in src/lib/client/api.ts accordingly.
Any Space or Collection can be shared read-only via its own menu → Share…
This generates a random token and a /shared/<token> link that renders a
clean, standalone public page (no sidebar, no login, no editing) — visitors
never get your other Spaces/Collections, and resource notes are excluded
from what's sent to the page. Turning sharing off (or generating a new
link) immediately invalidates the old link, since the page is looked up by
token + a "public" flag together, both stored on the Space/Collection
itself, and rendered fresh on every request. Sharing is opt-in per item —
nothing is public by default.
What changed in this pass, and what's still worth knowing:
sm
breakpoint (opened via a hamburger button in a small top bar) instead of
always being docked at fixed width, which used to squeeze the whole
layout on a phone.What I have not been able to verify directly in this environment:
this sandbox has no configured GITHUB_TOKEN, so I could not click
through the actual logged-in app UI on a phone-sized viewport or a
real screen reader to confirm these changes look/behave as intended —
only that the code builds, lints, and the underlying logic (verified via
direct API calls) is correct. Please do a quick pass yourself once
deployed: open the app on your phone, try tabbing through a modal with
only the keyboard, and add a large batch of resources to confirm "Load
more" appears and works.
None are set up. Everything in this app has been exercised through
manual/direct testing during development (curl against API routes,
clicking through the UI, npm run build/npm run lint), but there is no
npm test script or test runner installed. If you want unit coverage
going forward, the highest-value, lowest-effort places to start are the
pure functions with no DOM/network dependency: src/lib/client/duplicates.ts
(URL normalization), src/lib/client/markdown.ts (the Markdown-subset
renderer), src/lib/export/exportCsv.ts (CSV escaping), and
src/lib/import/parseBookmarksHtml.ts (needs a DOM — jsdom via Vitest
would cover it).
npm run build) and lints clean (npm run lint)/diagnostics and the
API routes directly, both configured and unconfigured).gitignore covers .env*, verified by
reviewing every commit's staged files before pushing)Built independently. raindropio/app (MIT licensed) was consulted only
as UX/pattern reference (never copied) — see THIRD_PARTY_NOTICES.md for
details. No Raindrop.io branding, backend, or account is used; this app
does not depend on Raindrop.io at all.