sources: thumbnail-first cards + branded clipping thumbnails#117
Merged
Conversation
Previously every resource card fronted with a small uppercase kind label and a two-line title, so the grid read like a dense file manager. The cards now lead with a real thumbnail slot — folder stacks (with an item-count chip overlaid), image/PDF/video thumbs, or a typographic page-preview tile for markdown notes that lack an image. Section headers get a colored badge per group and a tinted count pill so Sources / Drafts / Done feel like curated shelves instead of generic affordances.
Markdown notes that wrap a URL (frontmatter source/url/link or a body http(s) link) used to render as featureless page-preview tiles. The list endpoint now detects the URL, records the host + kind, and kicks off a background fetch — YouTube hosts pull the deterministic i.ytimg.com thumbnail, everything else falls back to an og:image scrape. Results land in the project's .studio-write/thumbs cache; a .err sentinel suppresses retries until the user deletes it. Cards swap the page-preview standin for the cached image with a play-triangle overlay on YouTube tiles and a host pill in the corner, so a "Clippings" folder now reads like a curated shelf.
After the first pass the grid still read as a wall of buttons: every card carried a three-dot menu in the corner, every markdown note — empty or not — rendered a fake-paper preview tile, and bright PDF / og:image thumbnails punched through the dark theme. This commit pulls the visual weight back without losing affordances. - Hide the card menu button and the section "+" affordance until the card cell / header is hovered or focused (kept visible while the menu is open and on keyboard focus for a11y). - Skip the page-preview slot when a markdown note has no excerpt and no clipping host — empty cards collapse to short title-only tiles instead of fake-paper rectangles. - Drop the static box-shadow under every card; the lifted shadow now reads as a real hover state. Border softened to ~5% fg-on-surface, corner radius 10 → 8. - Frame raster thumbnails with an inset hairline and knock back their luminance/saturation in dark mode so PDFs and white og:images sit inside the surface instead of puncturing it. - Calm the meta row: kind chip is sentence-case at 10/500 against a tinted fill (was uppercase 10/600 on a hairline border), the .MD chip drops for markdown (the implicit default), and dates older than 30 days are omitted at card scale where the exact day no longer carries signal. - Section labels: 13 → 14/600 with a touch of negative tracking so "Sources" / "Drafts" / "Done" feel like real headings.
The last round still let every card claim the row's tallest height, so empty markdown notes rendered as 340 px rectangles and notes with content rendered as 16:10 tiles full of illegible 8 px preview text. This round drops both: cards now size to their actual content and excerpts are typeset as readable body copy. - Grid: grid-auto-rows: min-content + align-items: start, so short text cards no longer inflate to the height of the folder cards in the same row. - Drop the fake-paper preview tile for markdown leaves. When a note has an excerpt, render it inline in the card body at 12/400 muted (3-line clamp) — readable text, not a 16:10 rectangle full of 8 px content nobody can parse. - Untitled drafts get an italic + muted title treatment so the eye skips past them as unfinished rather than parsing them as named files. - Clipping cards that never landed an og:image get the host as a small pill in the meta row (next to the kind chip) instead of inside a thumbnail-shaped frame. - Dark-mode raster filter knocked back further: brightness .94 → .86, saturate .9 → .85. - Folder count chip: smaller and quieter, with a subtle blur, so it reads as a secondary indicator rather than competing with the stacked tiles. - Section labels: 14/600 → 15/600 with a touch of negative tracking.
Three changes that together remove the remaining "early-stage product" feeling from the grid: - Each section now renders folder cards and leaf cards into two separate .resources-grid-cards subgrids so they never co-occupy a row. Previously a tall folder beside short text notes left a large dead-space gap on the right of the row; with the split, folders fill their shelf and leaves start fresh on the next row. - Replace the markdown-only folder text-stack with an abstract paper-stack visual. The previous implementation stamped the children's titles + excerpts at 7-8 px, which read as illegible decoration. Three layered tiles with a fan rotation and a faint lined-paper motif on the topmost tile communicate "stack of N notes" without trying to render the contents at thumbnail scale. The folder name now carries the identity; the visual just signals the kind. - Drop the card's `min-height: 64px`. Untitled drafts collapse from ~150 px to ~65 px and text cards with a one-line excerpt sit at ~92 px, so empty cards finally stop dominating the grid. Also removes the orphan page-preview CSS that the previous round stopped rendering.
The previous round replaced the markdown-only folder thumbnails with a generic abstract paper-stack visual. That fixed the illegibility of the old 7 px text but it also stripped every folder of its identity — Logs, Notes, References, Clippings all looked the same. Restore the per-child content tiles, but at readable sizes this time: title goes from 7 px → 10 px / 600 and the excerpt from 6 px → 8 px, with one fewer excerpt line so the bigger text fits. The fan rotation and stack-depth shadow stay, so the visual still reads as "a stack of N notes" — but now you can actually tell that Logs has dated daily entries, References has people, and Clippings has article titles. The abstract paper-stack survives as the fallback for empty folders (no children to summarize). Folders with image / PDF / video children continue to use the real thumbnail stack from earlier rounds.
Two residual visual issues from the last "professional design" pass: - A stranded final folder (e.g. Clippings when the section has 9 folders in a 4-column grid) left a large horizontal void to its right. The split-grid structure prevented leaves from filling the empty cells. New `BucketedGrid` component measures the actual column count via ResizeObserver and promotes the first N leaves into the same row as the orphan folder, then renders the rest in a second grid below. The last folder row now mixes folder + leaf cards (top-aligned), eliminating the void without reintroducing ragged mixed rows on every other shelf. - The Assets folder's white PDF/page thumbnails still punched through the dark surface at the default knockdown. Image tiles inside a folder stack now get a stronger dark-mode filter (brightness .7, saturate .78) while standalone leaf-card thumbs keep the lighter knockdown — those are the card's primary content and shouldn't be dimmed further.
The card's hover state used `transform: translateY(-1px)`. The eye tracks the content inside the card (title, preview, meta), not the card's outer edge, so even a single-pixel shift read as a visible jump. A 120 ms ease wasn't slow enough to smooth it out either. Drop the translate. The shadow, border-color, and a faint surface tint already communicate "this card is hovered" without moving anything. The card sits perfectly still under the cursor now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The project view's Sources grid used to read like a dense Finder grid — tiny uppercase kind labels, two-line titles, no visual identity per section. This PR brings it closer to Heptabase's curated-shelf aesthetic in two steps.
Commit 1 — thumbnail-first cards & section badges
Commit 2 — branded clipping thumbnails
.mdfile wraps a URL (frontmattersource/url/linkor first bodyhttp(s)link) and records the host + kind on theDirEntry.i.ytimg.com/<id>/maxresdefault.jpgfor YouTube hosts, og:image scrape for everything else. Cached under<project>/.studio-write/thumbs/clip-<sha1>.{jpg,png,webp}. A.errsentinel suppresses retries until the user deletes it.resources:thumbReadyevent pings the renderer when a fetch lands; the grid refreshes without the user having to navigate away.Test plan
npm run test:unit— 281 tests pass locallynpm run lintandnpm run lint:css— clean (one pre-existingInlineFileEditorwarning unrelated to this PR)page.emulateMedia({ colorScheme: 'dark' })via Playwright MCP) and re-verify