A running tally of every album, film, and series logged here — how often each feeling comes up, which genres keep pulling me back, and how this site got built along the way.
TRACK 01 — SUMMARY TRACK 02 — MOOD MAP TRACK 03 — BUILD
Every album, film, and series gets logged here — what it was, why today, and up to three feelings that came with it.
This site's own history, pulled straight from git — every change, grouped by the day it happened.
Why this log exists, and what the entries add up to when you read them all at once.
Area is how often a feeling was named. Click one to filter the timeline.
Three picks per kind of thing, from the genres you tag most — minus anything already logged, and anything you've turned down.
Things you liked here but haven't logged yet.
This log is also a build log. Every change to the site is a commit, and those commits get read straight out of git each time it deploys — so the numbers below can't drift from what actually shipped.
Each commit is bucketed by what it touched. Many land in more than one.
Layout, components, tabs, and everything you actually see.
Supabase reads and writes, auth, and the lookup APIs.
The entries table, its columns, and moving old data in.
Astro, TypeScript, the build pipeline, and project structure.
Getting it online: Cloudflare Pages, GitHub, deploy scripts.
Keeping the spec honest about what the thing actually does.
Colour, type, spacing — how the whole thing feels.
Versions read from package.json at build time.
The three most recent working days. Full history lives on the Dev Log tab.
0431ce2 Count the version instead of typing it c73f4c3 Record what the hero line actually means e96c842 Show the Mood Map as a treemap, and let it filter the timeline 84b8a4c Log why a receipt was refused instead of discarding it c5287aa Reflow commit bodies so they fill the card 205a4d5 Drop logged suggestions on the spot, and collapse long pagers 79c435a Speed up recommendations, and widen the contact form 1da29b1 Add a contact form and an owner-only inbox 74df920 Add an About tab, auto-fetch covers, and suggest genres up front a4bc487 Add entry editing, pagination, and real cover artwork 2380034 Repaint in pastel, rename Overview to Home, and surface the build story 87a5da3 Give each tab its own hero copy How this site got built, day by day — read straight from the repository's own history at build time.
0431ce2 Count the version instead of typing it The footer version was a literal, so it only moved when someone remembered to move it — and nobody did. It sat at v30 while some thirty releases shipped past it, telling every visitor the site was older than it was. It is counted from the commit history at build time now, alongside the date of the most recent one, so it cannot fall behind what is actually deployed.
Builds without git fall back to naming the stack alone; a confident "v0" would be a worse answer than not giving a number.
c73f4c3 Record what the hero line actually means "Everything I've felt, in one shape" was nearly rewritten when the Mood Map became a treemap, on the reasoning that a treemap is many shapes rather than one. The shape was never the chart — it is the shape the owner has grown into by keeping this log, so the line holds however the map is drawn. Written down so the next reader doesn't make the same correction.
e96c842 Show the Mood Map as a treemap, and let it filter the timeline The radar drew one polygon per entry, which asked for at least two feelings — so entries naming one feeling, or none, were silently absent. That was most of the log. What remained was mostly two-point polygons, which are lines rather than shapes, layered until nothing could be read. Area per feeling shows every entry instead, and holds up as the log grows.
The twelve feelings run as a spectrum rather than sorted into families. Grouping meant ruling on whether nostalgia is a warm feeling or a sad one, and it is honestly both, as is hesitation; a continuous order lets the murky ones take a colour belonging to neither end. Multi-hue is the documented exception for semantic warmth, so the scale is shown beside it. Fill and ink step together and every label clears 6.8:1 against its own tile — a treemap's text sits on the colour, so that is the contrast that matters.
Clicking a feeling filters the timeline, which turns the map from decoration into the way into the log. It is built from every filter except that one: a control that collapses to the cell you just clicked leaves no way back.
The radar survives in the entry form, where a single shape is exactly the job. Its chrome was still dark-theme — near-white gridlines at 8% and a brown hub, both invisible on cream since the pastel redesign.
84b8a4c Log why a receipt was refused instead of discarding it A failed receipt reported only `emailed: false`, which sent the investigation after the sending domain when the real cause was in Resend's response body: recipients at reserved domains are rejected outright, whatever the sender. The refusal is now logged where the site's operator can read it, and deliberately not returned — the reason belongs to whoever runs the site, not to the stranger who filled in the form.
9102bea Let the owner answer a message from the Inbox Replying from a normal mail client would put the owner's private address in front of the visitor, which is what the contact form exists to prevent. Sending as the site's own address instead needs an SMTP relay, and Resend will only send from a domain it has verified — the apex cannot be verified, because Resend's bounce MX would have to sit where Email Routing's receiving MX already is. One domain cannot both send and receive here, so replies are written where the messages already are.
The endpoint mails whatever recipient it is given, so authorisation is the whole design: the caller's Supabase token is resolved against the auth API and the address it returns must equal the OWNER_EMAIL binding. Both sides are server-side. Without that gate this would relay anything to anyone under this domain's name.
Sending a reply also marks the message read, answering it being the clearest evidence it was read.
a678e75 Keep the owner's address out of the repository too The digest fix covered the shipped bundle but not the source, and this repository is public: the address was still readable in the standalone prototype, in every RLS policy quoted by the setup guide, and in the spec's auth section. The prototype now compares digests like the Astro app does, and the guides carry a placeholder with a note that the real address belongs in Postgres and in the Cloudflare bindings, both private.
Also records the reply alias: Reply-To is a forwarding address, deliberately not OWNER_EMAIL, so a visitor can hold a conversation without ever being handed the private inbox.
5406d75 Let the sender and reply address come from configuration Proving a domain in Resend is a DNS exercise that finishes hours after the code is ready, so the sender and reply address are bindings now rather than literals. Switching to a real address becomes a configuration change; until one is set, the shared resend.dev sender still stands in.
Reply-To on the visitor's receipt is the forwarding alias, deliberately not OWNER_EMAIL — the alias is meant to be handed out, the owner's inbox is not. The receipt only invites a reply when there is somewhere for it to go.
fc52639 Stop publishing the owner's address, and send them a copy The address was reaching visitors by two routes, both of which defeat the point of having a contact form at all. It was a plain string in the client bundle, readable by anyone who opened the shipped JavaScript, so the owner check now compares SHA-256 digests instead — the same decision without the address being there to find. It was also the Reply-To on every receipt, handing the real inbox to anyone who filled in the form; receipts now carry no Reply-To.
Submissions also notify the owner, who otherwise learned of a message only by signing in. That copy sets Reply-To to the visitor, so replying takes one keystroke, and it goes to the address held in the server-side binding.
Rendering asks who the owner is dozens of times per pass and hashing is async, so the answer is resolved once per sign-in and cached.
ec1e2cf Fail the contact Function with a reason, not a bare 1101 With the env bindings missing, fetch() was handed an "undefined/rest/v1/…" URL and threw, which Cloudflare surfaced as error code 1101 — no indication of what was wrong. The bindings are now checked up front, and the database call is wrapped so a network failure or a PostgREST error reports what happened.
8cd7997 Email a receipt when someone leaves a message Adds a Cloudflare Pages Function at /api/contact so the mail API key stays server-side. It stores the message then sends the visitor a copy, and treats the two independently: a failed send still returns success, because losing a message to a mail provider's hiccup is the worse outcome. The UI only claims a receipt went out when one actually did.
The requested moodlogremi@noreply.com can't be used as the sender — noreply.com belongs to someone else, so mail sent as it fails SPF/DKIM and lands in spam or bounces. Sending as a domain means proving you own it, and a pages.dev subdomain has no editable DNS. Resend's shared onboarding@resend.dev carries it instead, displayed as "Mood Log".
The client falls back to a direct insert when the Function isn't reachable, so the form still works under astro preview where Functions don't run.
652f337 Add real favicons, and require a valid email on the contact form Bookmarking the site produced a blank tile: the <head> carried no rel="icon" at all, and the manifest pointed only at an SVG, which Safari, most bookmark managers and every OS home-screen ignore. `npm run icons` now renders 16/32px favicons, a 180px apple-touch-icon and 192/512px manifest icons through sharp. The Apple icon is opaque, since iOS masks it itself and renders transparency as black.
The contact form's free-text "how to reach you" is now an email field, checked against a pattern rather than relying on type="email" — that check is bypassable and permissive, and an unreplyable address makes a message worthless.
c5287aa Reflow commit bodies so they fill the card The Dev Log printed commit messages verbatim with whitespace-pre-wrap, so the ~72-character hard wrapping that git messages are written with showed through as a narrow column inside a much wider card. It read as a CSS width problem but the line breaks were in the data.
Bodies are now split on blank lines and each paragraph joined back into one flow. Bullet lists keep their breaks — a list is meaningful line by line, a paragraph isn't — and continuation lines are folded into the item above.
205a4d5 Drop logged suggestions on the spot, and collapse long pagers Logging a suggestion left it sitting in "You might like" until a reload. The cache key already includes the entry count so it self-invalidated eventually, but an action not reflected until you refresh reads as a bug. It's now removed from the suggestions and from Saved for later as soon as the insert succeeds.
The pager collapses past seven pages to 1 … 5 6 7 … 12; beyond that the numbers wrap to a second line and stop being scannable.
79c435a Speed up recommendations, and widen the contact form Measured the sources first: Open Library was the bottleneck at 7-11s (and occasionally 500ing), because search.json?q=subject: scans about a million records. Its prepared /subjects/{name}.json index answers the same question in a fraction of that.
Three more cuts on top: - Cards paint per media type as each source answers, instead of after the slowest. Four of the five now appear in ~1.2s where the section used to sit on a spinner until everything was in - Candidate genres are queried in parallel rather than one at a time, which had meant up to four sequential round trips against already-slow sources - AniList's 19-entry genre list is inlined instead of fetched, removing a whole round trip before the real query could start
Results are cached in sessionStorage, keyed so the cache drops itself when the log changes — returning to Home is now instant.
The contact form also had a 560px cap while the copy above it ran full width.
1da29b1 Add a contact form and an owner-only inbox Visitor messages write to a new `messages` table. RLS lets anyone insert but only the owner select, so the inbox is write-only to the public — no visitor can read another's message, and no email address or handle is published anywhere for a scraper to find.
The owner sees an inbox under the form: unread badges, mark read/unread, and delete behind a confirming second click. A honeypot field catches naive bots; filling it returns the normal success message and stores nothing.
Fixed while testing: the table probe used a HEAD count, and PostgREST answers HEAD on a missing table with a 404 and an empty body, which supabase-js doesn't report as an error — so the probe called a missing table present and the form surfaced a raw schema-cache error instead of explaining itself. A plain select distinguishes the cases correctly, and still returns cleanly for a visitor whose RLS grants insert but not select.
de3d500 Add social preview tags and a generated card image Shared links showed no thumbnail because the page had no Open Graph or Twitter Card tags at all — only a description. Both sets are now emitted with absolute URLs built from `site`, since scrapers don't resolve relative paths and a relative og:image is a common reason a preview stays blank.
The card is a real PNG at /og.png, generated by `npm run og` through sharp. Facebook, LINE and X ignore an SVG og:image outright, so shipping the SVG would have looked correct in the markup and still produced no thumbnail.
1464e20 Run body copy full width, and make the save/dismiss buttons hittable The hero lede was widened last time but every other section kept its own max-w-[Nch] cap, so the paragraphs still wrapped early. Removed from the recommendations, Dev Log, build summary and About too.
Save and dismiss were 16px inline glyphs — well under a comfortable touch target, and hard to hit as reported. They're now 44px round buttons floating over the artwork, with titles and aria-labels so the icons aren't the only cue.
2b7de0e Recommendations get type tabs, like/dismiss, and a saved list Five cards side by side left each too narrow for a title like "la di die (feat. Jaden Hossler) - Single" to fit in fewer than four lines. It's now one wide card with media-type tabs above, still cycling three picks with dots.
♡ saves a pick to a new "Saved for later" list; ✕ dismisses it permanently. Both persist to site_settings rather than the browser, so they follow the account and survive a rebuild. Dismissals are applied inside the sources rather than filtered afterwards, so turning one down doesn't just leave a shorter list.
The hero lede now runs the full column width, as asked — the usual 45-75 character measure reads more easily, but that's the call made.
05e3028 Give each suggestion card three picks, a link out, and a Log this button One suggestion per type meant a single miss left the card useless. Each type now carries up to three, cycled with dots, and the candidate genres are accumulated rather than stopping at the first that answers — with few entries every genre ties at one, and one genre rarely has three unlogged titles behind it.
Cards link out to where the thing actually is: Apple Music, IMDb (via Wikidata's P345), AniList, Open Library. "Log this" hands the pick to the add form with type, title, creator, genre, artwork and link already filled, so a suggestion worth keeping doesn't have to be retyped.
The hero illustration is hidden below md — on a phone a full-width square pushed every word below the fold.
ae638f4 Add a Series type and a timeline search Series joins the other four: Series title / Creator-studio fields, watch contexts, TV genre presets, resolved through Wikidata like films are — IMDb links already cover series, since tt ids span both.
Search matches title, creator, context, genres and the notes. Notes are included because a detail is often written only there, and paging through a growing log to find one entry was the obvious next pain point.
Backend: `entryKey()` now defines "the same work" in one place. The timeline's repeat-folding and the recommender's already-seen check had drifted apart — the latter compared titles alone, so an album could hide a film of the same name. The Wikidata lookup is parameterized by class rather than copied for series.
Needs a widened CHECK constraint for 'series' (see SPEC).
1829a53 Add a bulk importer for pasted lists Paste one item per line — title, title | creator, or title | creator | date — pick a type, and each line runs through the same lookup the form uses. Results are shown with tick boxes for approval before anything is written: a wrong match buried in a batch of thirty is far harder to notice afterwards than before.
Unresolved lines are listed with the reason rather than dropped, and feelings are left empty on purpose — no lookup can supply them, and guessing would defeat the point of the log.
Built to carry an Instagram export into the log, but useful for any list.
c21fd21 Loosen Apple Music matching to accept romanized artist names Requiring the artist to agree as well as the title looked like the safe reading, but it threw away most correct matches: iTunes romanizes names, so 落日飛車 comes back as "Sunset Rollercoaster" and 宋冬野 as "Song Dongye". Seven of eight albums found no link because of it.
An exact title match now stands on its own; a merely partial one still needs the artist to back it up, which is what actually stopped the "愛是唯一" → "I Swear" mismatch. If nothing matches, title and artist are retried swapped — entering an album under the artist's name is an easy slip and the swap recovers it.
5884c34 Stop storing wrong Apple Music links, and widen the ISBN lookup iTunes ranks loosely — searching "愛是唯一" returns Sandy Lam's "I Swear" first, because one of its tracks carries that name, and taking results[0] on faith had already written that wrong link to a real entry. Candidates are now checked against the entry's title and artist, and an unconvincing search stores nothing: a missing link is recoverable, a wrong one silently isn't.
ISBN lookup now tries three shapes — Google Books' isbn: index, Google Books' plain search, then Open Library — and sends country=TW, which Google Books requires from some regions or it errors rather than returning an empty list.
Open Library genuinely has no record of the reported ISBN (numFound: 0), and Taiwanese titles are often missing from both indexes, so the failure message now names what was tried and points at a title search instead of implying the input was wrong.
1fde0b9 Render notes as Markdown, link music to Apple Music, and support phone capture - Notes accept Markdown, rendered by a small in-repo renderer rather than a dependency. Safety comes from ordering: the source is escaped first and only then re-marked-up, so a note can't inject HTML, and link hrefs are restricted to http(s). Checked against <script>, <img onerror>, and javascript: links - Notes over 100 characters collapse to a plain-text preview with Read more; the full Markdown is parsed on demand rather than for every card up front - Saving a vinyl entry now looks up its Apple Music page when it doesn't have one, so albums logged from Spotify, YouTube, or by hand are still one tap from playing. The backfill button fills links as well as artwork - ISBNs are detected and queried exactly, via Google Books then Open Library — more reliable than a title search, and easy to copy off a bookshop page whose link can't be read - Added a web manifest for Add to Home Screen, and URL prefill (?add=1&type=&title=&creator=¬e=) so a phone shortcut can hand data over without holding credentials — it opens a URL in the already-signed-in browser
Fixed while testing: blockquotes never rendered, because escaping runs first and turns "> " into "> " before the block rule sees it.
26e40d9 Link entries out to their source, add All filters, and improve book lookup - Every lookup now also returns where to go to play/read/watch the thing, stored in a new source_url column. Timeline cards get a link named after the destination — "Apple Music ↗" on an album, "IMDb ↗" on a film. Pasted links are kept as-is. Probed like cover_url, so it degrades until the column exists - Books try Google Books before Open Library: it indexes Chinese-language editions far better. A bare ISBN is detected and queried as isbn: - books.com.tw and the other online bookshops block cross-origin reads (403 even server-side), so those links are detected and answered with a nudge to type the title or ISBN, rather than a generic "not recognized" - Both filter rows lead with an explicit All chip — "select nothing to see everything" was never discoverable - Dropped the ↺ marker on remembered genres; their position already says it
a3bd907 Collapse repeat logs in the timeline, and tidy the filter and utility rows Logging the same work again now folds into one timeline card showing ×N and the last date, carrying the most recent log's mood. Identity is title + creator + media type, case-insensitive — title alone would conflate an anime with the book behind it.
Two deliberate limits on that: - The database still stores every log. This only folds them for display, so the history behind a "×3" survives and the choice stays reversible - Deduping is timeline-only. The Mood Map still plots every log so repeat listens keep their own shape, and Summary still counts logs. Remove on a folded card drops only the latest and says so, rather than silently taking three records with one click
UI: backup/maintenance actions move behind a "Manage this log" disclosure instead of reading as developer tooling; the genre filter collapses to the 8 most-used with a "+ N more" toggle (active filters stay visible regardless); and entries without notes render nothing instead of a grey "(no notes)".
Backend: split renderAll into renderAll/renderFiltered so a filter click stops rebuilding the stats and the About copy, and run the two schema probes concurrently instead of sequentially.
d3a2ad1 Drop the Home tab, rework Summary, and add recommendations - The Home tab is gone; the MOOD LOG wordmark navigates there instead, the way a site logo usually does. Nav is now Log · Dev Log · About - Summary reworked: one large total, the date range collapsed to a single line with a day count, per-type counts inline as colored dots, and feelings/genres side by side. Previously six same-sized tiles with dates taking two of them - New "You might like" section: one suggestion per media type, drawn from the genres that type is tagged with and filtered against what's already logged
Three things the obvious implementation got wrong, found while testing: - iTunes only matches titles, so searching it for "Sci-Fi" returns nothing. Films now come from Wikidata, ranked by sitelink count - AniList keeps "Shounen"/"Isekai" as tags, not genres, and querying them as a genre returns an empty list rather than an error. The code now checks GenreCollection and picks the right field, with an alias map for romanization - With few entries every genre ties at one, so the "top" genre is arbitrary and was often a word the source didn't know. Candidates are now tried in rank order until one answers
4bc28f1 Add a Book type, editable About copy, and genre memory - Book joins Vinyl/Movie/Anime: Book/Author fields, reading contexts, and lookups through Open Library (keyless, CORS-open, good on translations). Goodreads links can't be read cross-origin, so the title is recovered from the URL slug and searched instead - About prose now lives in a site_settings key/value table and is editable in place by the owner. Probed like cover_url, so it falls back to built-in copy when the table doesn't exist yet - Genre chips remember what each media type has actually been tagged with, ordered most-used first (↺) ahead of the presets — custom genres stop needing to be retyped - Replaced the capybara with a cozy window scene in the reference style
Needs a schema migration (see SPEC): cover_url, a widened media_type check for 'book', and the site_settings table.
c3bcd7d Paginate the mood map, visualize dev stats, and fix genre language and covers - Mood Map legend paginates at 10. The radar still draws every visible entry, and clicking a polygon jumps the legend to the page holding it, so a click never lands on something off-page - Dev Log gains two wordless summaries: a proportional bar of what the work has been made of, and a per-day activity strip. No figures on purpose - About drops the generated data portrait for a capybara in a hot spring - Genre language: iTunes answers in the storefront's language, so a US lookup now backfills the genre when the first answer is CJK, and Latin-script labels win over CJK ones when both exist ("Alternative", not "另類音樂") - Covers for film and anime: Wikidata's P18 is often empty, so films fall through to Wikipedia's summary endpoint and anime to an AniList search — both keyed on Wikidata's English label, since neither source matches a Chinese title
74df920 Add an About tab, auto-fetch covers, and suggest genres up front - New About tab. The prose is fixed, but the "portrait in data" beside it is generated from the log itself — counts, the feelings that recur, what gets returned to — so it stays true as entries accumulate rather than going stale - Covers are now looked up automatically on save when the entry doesn't already have one, so "Fetch missing covers" is only for backfilling old entries - Genres the lookup suggests are pinned ahead of the presets and marked, so the system's guess is what you see first - Removed "Clear all entries" (and the deleteMany call that only served it)
Backend tidy-up while in here: fetchEntries now sorts newest-first in the database instead of the client re-sorting in three places, and it propagates errors so a failed load says so rather than looking like an empty log.
a4bc487 Add entry editing, pagination, and real cover artwork Editing: each timeline card gets an Edit button for the owner that loads the entry back into the add form, swaps the submit label, and shows a cancel banner. Saving updates in place. An edit survives routine auth-token refreshes — only an actual loss of owner rights discards it.
Pagination: 10 per page on both the Timeline (re-rendered) and the Dev Log (hidden/shown in place, since Astro renders it statically; day headings collapse when a page leaves them empty). The pager hides itself below one page.
Covers: every lookup source now also returns artwork — iTunes (upscaled from the 100px thumb Apple returns), Spotify/YouTube thumbnails, Wikidata P18, AniList coverImage. Entries fall back to the generated SVG when there's no image or it fails to load. A "Fetch missing covers" utility backfills older entries.
The cover_url column is probed once at boot and omitted from writes when absent, so this works against a database that hasn't run the ALTER TABLE yet.
Also wired up astro check (0 errors) and pinned down a Tailwind/Vite types skew.
2380034 Repaint in pastel, rename Overview to Home, and surface the build story - New light pastel palette across tokens, emotions, genre cycle, and type accents, taken from the reference player artwork: soft peach/pink/lavender wash, translucent white cards, muted purple ink - Each tab gets its own illustration instead of sharing the record: a player window (Home), an open journal (Log), a code window (Dev Log). The record only spun on one tab's worth of meaning anyway - Dropped the SIDE A / SIDE B fineprint from Log and Dev Log - Overview renamed Home; hash is now #home - devlog.ts now classifies each commit into work areas (frontend, design, backend & data, database, framework & build, deploy & infra, docs) and reads the stack's versions out of package.json. Home gains a "How it was built" section showing the breakdown, the stack, and recent changes; Dev Log tags each commit with its areas - A tracked pre-push hook redeploys on every push so the live Dev Log always matches GitHub; failures are swallowed so they can't block a push. `npm run ship` does the same explicitly
87a5da3 Give each tab its own hero copy The banner was one static block shared by all three tabs, so its text didn't describe whatever was actually showing below it. Hero now renders three variants (eyebrow/title/lede/fineprint), toggled by the same showTab() that already switches [data-panel] — Overview talks about the aggregate stats and mood map, Log about adding/browsing entries, Dev Log about the build history.
4765583 Scroll to top when switching tabs Keeps the new tab starting at its own beginning instead of inheriting the previous tab's scroll position. Honours prefers-reduced-motion.
c94d83d Add a Dev Log tab and regroup the page into three tabs The nav is now a tab switcher rather than anchor links, with the active tab mirrored to the URL hash so tabs can be linked directly.
- Overview holds Summary + Mood Map; Log holds Add Entry + Timeline (the backup/utility row moves here, next to the entries it acts on); Dev Log is new - Dev Log reads `git log` at build time via src/lib/devlog.ts and groups commits by day. Chosen over the GitHub API so it needs no rate limit budget and keeps working while the repo is private; it refreshes on rebuild - "Locate on map" now switches to the Overview tab before scrolling
597bea3 Point deploys at the moodlogremi.pages.dev project 27dc387 Add deploy script and document the Astro/Cloudflare setup 986cd25 Rebuild as an Astro + Tailwind project Replaces the single hand-written index.html with a real build: Astro for the static shell, Tailwind v4 for styling, TypeScript for the client logic. Output is still a static site — no adapter, no server.
- src/components/* hold the page shell; all dynamic rendering stays client-side in src/lib/app.ts, which fills the sections from Supabase - src/lib splits the old monolith into config (media types, emotions, palette), supabase (typed CRUD), art (cover art + radar), parse (link/title lookup) - Design tokens moved into Tailwind's @theme, so colors and fonts are utilities; only the paper grain, record spin, and timeline rail stay as custom CSS - Behaviour and the Supabase schema are unchanged
Node was upgraded to 26 (Homebrew) since Astro's Vite needs >=20.12; the old system Node 20.10 at /usr/local/bin is untouched and still resolvable.
8a4f30e Fix stale note about the parse box being type-gated 04e2bae Update spec for movie/anime parsing and title search 5766b10 Parse movie and anime sources, and accept a plain title anywhere The parse box is now shown for every media type and takes either a link or a title to search for.
- IMDb links resolve through Wikidata (keyless, CORS-open): localized title, director, and genres normalized against this app's presets - MyAnimeList and AniList links resolve through the AniList GraphQL API - Typing a name searches Apple Music (vinyl), Wikidata (movie), or AniList with a Wikidata fallback (anime) — Wikidata is what makes Chinese titles work, since AniList only indexes romaji/English/Japanese - Streaming hosts (Bahamut, Netflix, Bilibili, …) forbid cross-origin reads, so their pages cannot be inspected client-side at all; those links now say so and point at the title search instead - Enter in the parse box triggers a parse
55e5873 Update spec for single-page lifelog architecture and Supabase backend b2c8fca Restructure as a single lifelog page with a type selector Vinyl/Movies/Anime are no longer separate tabbed pages. The lifelog rollup is now the only header: one hero, then Summary, Add Entry, Timeline, Mood Map as sections of one page, with nav links as anchors.
- One add form; a Type chip row swaps field labels, placeholders, context options, genre presets, submit label, and shows the link parser for Vinyl only - Timeline and Mood Map cover every type at once, filterable by type and by genre (the genre list narrows to whatever the type filter leaves in scope) - Entries normalize to {mediaType,title,creator,...}, dropping the per-type titleKey/creatorKey indirection; DB schema unchanged - Migration is one button covering all three legacy stores - Link parser tolerates messy pasted URLs and supports Spotify - Added cover-art shapes for film/anime genres
8623dc9 Add migration for movie/anime tabs, support Spotify links in parser - migrateLocalLegacyData is now config-driven (per-media legacy storage keys) and checks both Claude storage and localStorage, skipping entries already in the cloud - Parser recognizes Spotify links via oEmbed (title only; Spotify does not expose artist or genre)
7e8cda7 Add Mood Log app (Vinyl/Movies/Anime/Lifelog, Supabase-backed public site) Anything you want to say — a recommendation, a note, or just hello. Goes straight to me and nobody else can read it.
Only visible to you.