TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A Vercel-hosted always-on home organizer with a Surface Pro display mode and a phone admin mode.
• Events/tasks are saved to Vercel Blob only.
• Repeating tasks are stored as one master task with a repeat rule. The app no longer needs to create many copied tasks for weekly/biweekly/yearly repeats.
• If the server is busy/offline, phone/admin changes are saved locally on the phone first and retried automatically.
• Pending saves are kept in localStorage until Vercel accepts them.
Create/connect a Vercel Blob store, then add this environment variable:README 전문 보기 ↗A Vercel-hosted always-on home organizer with a Surface Pro display mode and a phone admin mode.
localStorage until Vercel accepts them.Create/connect a Vercel Blob store, then add this environment variable:
ADMIN_PIN=your_pin_here
Vercel Blob should provide BLOB_READ_WRITE_TOKEN automatically when the store is connected to the project.
Redeploy after changing environment variables.
Display mode for the Surface Pro:
https://your-project.vercel.app/?display=1
Phone admin mode:
https://your-project.vercel.app/?admin=1
When you create, edit, complete, or delete a task from the phone, the app first stores the change on the phone in a pending queue. It then sends the change to Vercel. If Vercel is busy or the network fails, the task stays in the local queue and the app retries by itself after 15 seconds, then 30 seconds, 1, 2 and 5 minutes, and whenever the phone comes back online, the screen is tapped or the task list is refreshed. The automatic retries stop after that, because each attempt costs Blob reads; a save that is still stuck waits for the next refresh, tap or reconnect.
Open the hidden config menu by holding anywhere on the dashboard/admin screen. It shows the number of pending saves and has a retry button. It also reports tasks that are drawn on this device but that the server has not confirmed yet, so a task can no longer look saved while it is missing everywhere else. It names the app version this page is running and, when they differ, the version the server answered with — a page can sit open on the wall for days after a deploy, and "it is not doing the new thing" is usually just an old page. Reload it. If the server refused a new picture, that is reported there too, with the task it belongs to.
All tasks live in one shared events.json in Vercel Blob, so every save is a
read-modify-write of the whole file. Three things keep two devices saving at
once from erasing each other:
events.json. A unique query string
does not get past it: the Blob CDN leaves the query out of its cache key
(measured: ?v=<random> comes back as a cache HIT with the same age). So every
read is compared against head(), which answers from the Blob API rather than
the CDN. The byte length has to match, and so does the store's ETag (an MD5 of
the content). Length alone used to let a same-length older copy through —
moving a task from 10:00 to 11:00 keeps the length — and a save built on it
silently undid that move.503 instead of rewriting the whole file from an
old snapshot. That one rule prevents the worst failure this app can have:
rewriting events.json without the tasks and pictures added since the copy
was made — and it also stops a 404 Event not found for a task that plainly
exists, which the phone would treat as permanent and drop the edit.head() is asked whether the stored file is the exact document
just written (same byte length and same MD5 ETag). If it is, the write is
done — no CDN read involved. Otherwise the
document is read back, and the answer is one of three, each handled
differently:
503 "another device saved at the same time" for a change the
server had in fact stored, which left it queued on the phone forever, and
every later change stuck in the queue behind it.5xx rather
than overwriting the file with a guess, and only genuine validation problems
(missing title or start) answer 4xx.If a task still goes missing from the server shortly after it was accepted, the device that created it notices on its next fresh read and sends it once more. Past a ten-minute repair window it assumes the task was deleted from another device and stops drawing it, so a delete is never resurrected.
Repeating tasks use these fields on the single master event:
{
"repeat": "weekly",
"completedDates": ["2026-05-10"],
"excludedDates": ["2026-05-17"]
}
The browser expands those into temporary occurrences for display only. The server keeps just the master task.
/api/reminders-cron, twice a day) sweeps images that no task has referenced for 7 days, tracked in home-organizer/image-sweep.json. A picture lost to a bad save therefore stays in the Blob store for a week: find it under home-organizer/images/ in the Vercel Blob dashboard and paste its URL into the task's image field. The sweep never deletes anything when events.json cannot be read, and never deletes an image uploaded less than 7 days ago.Every save the server confirms also rewrites a tiny marker,
home-organizer/events-version.json, holding the stamp of the stored task list.
Every open screen reads that marker straight off the Blob CDN every 30 seconds
(and when it wakes, comes back online, is tapped, or the camera dimmer sees
someone). Only when the marker has moved does it read /api/events.
Why this costs nothing against the Hobby limits:
304. Blob does not bill a
cache HIT as an operation; it only counts as a CDN request (Hobby includes a
million a month; one screen polling every 30 seconds is about 86,000).events.json itself keeps its short cache life on purpose. The save path reads
it through the same CDN, and a purge that went missing must not be able to
hold a stale copy there for a month.The timed reads stay as a safety net, but while the marker is being read the hourly one only actually runs every few hours, and a tap only looks at the marker unless the last full read is more than 15 minutes old.
When the server can only get the CDN's older copy of the task list it says so
(fresh: false). The screen keeps what it has and asks again after 15, 30 and
60 seconds, instead of showing the old list and refusing to read again for a
minute — which is what used to make a tap right after a change on the phone
show nothing.
Every request the screens make is time-limited. One that never answered used to leave the screen believing a refresh was still under way, and from then on it never refreshed again until somebody reloaded it.
The marker does not exist until the first save after this is deployed. A screen that finds it missing asks the server to create it and falls back to timed reads for half an hour.
The wall display also reloads itself after a deploy, once it is back on the home view and nobody has touched it for a minute, so fixes reach it without anyone reloading it by hand. It does not do this while the page is in browser fullscreen (double-press on the clock), since a reload would drop fullscreen; the config menu says a reload is due instead. Fullscreen started by the browser or a kiosk mode is not affected.
The config menu shows whether live updates are working and when the last full read was.
The blue daughter periods in the month view, and every change to them, used to
be stored only on the device they were made on, so a period moved on the phone
never reached the wall display. They are now stored in events.json next to the
tasks (as daughterPeriods), so every refresh a screen already does brings them
along without an extra Blob read, and the marker carries their changes too.
Sometimes one task keeps showing the calm placeholder while every other task shows its photo. The editor has a Resync image button under the image field for that. It only appears while editing a task that already exists.
What it does:
/api/upload?force=1. A forced upload skips the
reuse-by-content shortcut and writes a brand new path, so the picture comes
back on a link nothing has cached.Why the forced upload matters: a normal upload hashes the file and reuses the existing blob for identical bytes. Re-uploading the same photo therefore returns the same URL — which is useless when the URL itself is the problem (the blob was deleted and re-created at that path, or a CDN/browser is holding a stale copy behind the one-year cache header). That is why uploading the same picture again never fixed the last stuck image, and why a resync mints a fresh path.
If the stored image cannot be downloaded at all — it was deleted from Blob storage — the button says so. Pick the photo again in the file field and resync; that path does not depend on the dead link.
The physio, the doctor and the MR scanner come round again and again, and nobody wants to pick a photo for the twentieth one. So a task whose title names one of these and that is saved without an image is given a random picture from an older task of the same kind, instead of the calm placeholder.
fysio, lege and mr, case and accent insensitive.
fysio and lege count inside a longer word at either end, because that is
how they are written down: Fysioterapi, Fysioterapeut, Tannlege,
Legetime, Legevakt. A word that only happens to end in one of them
(college, privilege) does not. mr is two letters, so it only ever counts
as a word of its own — MR, MR-time, MR undersøkelse, and MRI — never
the inside of some unrelated word, so Mrs and Mormor are left alone.imageInheritedFrom records the task the picture came from, and
imageInheritedKeyword which kind it is. The kind matters because a borrowed
photo keeps the kind of the task it came from, whatever the task carrying it is
called: a fysio card showing a lege photo is holding a lege photo, and
lending that on as a fysio photo is exactly how one stray picture reached every
fysio card. Both marks live and die with the image they describe — a task that
gets a picture of its own loses them.
Pictures borrowed before the kind was written down are traced back through
imageInheritedFrom instead. An origin that can no longer be established — the
task it came from was deleted — is treated as unknown: the picture stays where it
is, but it is never lent on.
A borrowed picture of the wrong kind is let go on the task's next save, and the slot is filled with a photo of the right kind, or left to the placeholder. Only ever a borrowed one: a photo somebody chose is never taken away. Until that save the card keeps showing it, and the editor says so under the image field.
The rule lives in api/_image-rules.js (autoImageKeyword, autoImageKindOf,
isMismatchedBorrowedImage, pickAutoImage) and is applied by api/events.js to
whatever any device sends. The admin page holds a copy of the same rule so a task
saved on the phone draws its borrowed picture straight away rather than a
placeholder that swaps out a second later, and so a task saved with no signal
still gets one. Keep the two in step.
When you upload a task image, the admin page tries to detect faces in the browser and saves a simple focus point with the event. The dashboard then uses that focus point for daily, selected, upcoming, and admin thumbnails so faces are less likely to be cropped out.
This version adds email reminders without adding a separate database.
/api/reminders-cron job refreshes the schedule so repeating/future reminders stay covered.Add these in Vercel → Project → Settings → Environment Variables:
RESEND_API_KEY=re_your_resend_api_key
REMINDER_FROM=Home Organizer <[email protected]>
CRON_SECRET=make-a-long-random-secret
ORGANIZER_TIME_ZONE=Europe/Oslo
Keep your existing variables too:
ADMIN_PIN=your_pin_here
BLOB_READ_WRITE_TOKEN=created_by_vercel_blob
Resend requires a verified sending identity/domain for reliable sending. Use your verified domain in REMINDER_FROM, for example:
REMINDER_FROM=Home Organizer <[email protected]>
For quick testing, Resend accounts often allow sending to your own verified account address, but family reminders should use a verified domain.
The cron in vercel.json runs once per day:
{
"path": "/api/reminders-cron",
"schedule": "22 3 * * *"
}
That works with Vercel Hobby. The exact delivery time is handled by Resend scheduled emails, not by the once-per-day Vercel cron.
After deploying, open this URL in your browser, replacing the domain and secret:
https://your-project.vercel.app/api/reminders-cron?secret=your_CRON_SECRET
A good response looks like JSON with ok: true.
This version is tuned to use far fewer Vercel Blob operations:
/api/events every 10 seconds, 10 minutes, or 2 minutes. It watches the version marker on the Blob CDN instead, which is free while nothing changes.localStorage, so the screen renders instantly from the last known calendar and keeps working if Blob/API is temporarily unavailable./api/events?fresh=1 read so loading/reloading the dashboard gets the newest server data./api/events reads still use short API/CDN caching and a small in-memory cache per serverless instance. Fresh boot/midnight reads bypass that cache on purpose.Tapping the clock opens the day selector in the left card. Picking a day there shows that day's task in the big hero panel on the right, and the selector stays up with the picked day outlined, so the next day is one tap away. The task is deliberately not repeated in the left card. If a day holds several tasks the selector shows the count and the hero cycles through them; flipping the hero gives complete / edit / delete for whichever one is showing. Tapping the clock again returns to today and brings the weather back.
The wall display has a hidden panel by the clock. A slim handle sits on the left edge of the screen, level with the clock: pull it to the right and the panel slides out, like Android's edge panel. A tap on the handle only nudges the panel out and back to show that it wants pulling, so a stray touch on the edge opens nothing. Tap beside the panel, push it back left, or leave it for ten seconds and it slides away again.
The panel has one button, YouTube, which shows a QR code. Scan it and the
phone opens a small page (this same site with ?ytsend=<session>) with a field
for a link. Paste a YouTube link — watch, youtu.be share, Shorts, live or
playlist, with any t= start time kept — and tap Play on the wall screen. The
wall closes the QR code and plays the video in YouTube's own player. Another
video shows a fresh QR code while the current video keeps playing underneath;
the × closes the player.
/sse) to that topic while the QR code is up. Nothing goes through Vercel, so
a video costs no Blob operations and needs no admin PIN.429), the phone says so and the button can be tapped again.Fold the iPhone Duo shut and stand it up like a tent, so its outer screen is landscape, and HomeDash turns into a full-screen slideshow of the next 7 tasks with their photos — the same one the car lockscreen opens. Swipe left and right between them, or leave it and it moves on every 30 seconds. Turn the phone upright or open it and the page underneath is back exactly as it was.
screen.width/height) is phone-sized and between 1:1.34 and 1:1.6.
Either Duo screen passes; other iPhones are long and thin (about 1:2.17).
The reported screen is trusted only this far: after the phone is folded or
opened it can go on describing the screen that was left until a reload;ResizeObserver, so a size change cannot leave the next photo peeking in.The whole dashboard lives in index.html — the <style> and <script> blocks inside it are what the browser runs. There is no separate bundle to edit. (Two exported copies, script.js and inline.js, used to sit next to it; nothing loaded them, and two bug fixes were accidentally applied only to those copies and never reached the app. They have been deleted — put dashboard changes in index.html.)
The weather card paints an ambience layer (#weatherEffect) behind the readout:
is_day. At night the card swings dark and the readout flips to light text (.wx-dark).prefers-reduced-motion keeps the wash and static sky but drops everything that moves.?tent=1 shows it on any screen held in landscape,
so it can be looked at on a PC; ?tent=0 keeps it off. Neither is remembered.