Format was a small pill row buried alongside Brand — promoted it to
full-width stacked rows (label + dimensions) since it's the decision
that determines everything downstream (canvas size, available
templates, content rules).
Layout picker moves out of the sidebar entirely into a horizontally
scrollable thumbnail carousel floating above the bottom toolbar, so
browsing layouts feels like flipping through visual samples instead of
scanning a static sidebar grid. Still uses the existing CSS-diagram
LayoutThumb visuals for now — swapping in real rendered examples is a
deferred polish pass, not blocking this reorg.
Sidebar now reads Brand -> Format -> Content, with Content's fields
already gated on a layout being selected (showContentControls), so the
flow reads as sequential steps rather than three parallel groups.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Renamed the "og" format's label from "Blog" to "OG" — the view
toggle picks this up automatically since its labels are derived from
format.label.
- Default view state changed from 'both' to 'og' so the app loads
showing just the primary render instead of the side-by-side pair.
- In-context preview toggle: "Twitter / X" -> "X".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Was rendering at 36px height (~184px wide); bumped to 43px height,
its native SVG size, landing width at ~220px. Lossless since it's an
embedded SVG, not a raster asset — no 2x source needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Was mathematically centered on the vertical midpoint, which read as
mechanically centered against the bottom-anchored text below. Lifts it
40px (1x) higher via a new LOGO_CENTER_LIFT_1X constant.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Compared 400 vs 500 at the new 20px size; 500 reads better at this
smaller size. Also register EYEBROW_PILL_WEIGHT in satoriFonts (was
only loading HEADLINE.weight) so the requested pill weight actually
has its own font file loaded instead of silently falling back.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The ENTER and bracket-override instructions now each get their own line
and the same kbd-chip treatment (↵ / []) instead of being crammed into
one paragraph.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Blog listing mockup:
- Same research approach as the Blog post mockup — read the real /blog
index page (app/blog/page.tsx, BlogGridItem.tsx, FeaturedThumb.tsx,
BlogClient.tsx, BlogFilters.tsx) instead of guessing.
- Real nav (extracted to a shared SiteNav component, reused by both Blog
mockups), "Blog" H1, the real category filter pills + search box, a
featured-post section above the grid (3/2 hero + title + author,
matching FeaturedThumb), and a uniform 3-column grid below (1.91:1
card images, date + category, title) matching BlogGridItem — with the
featured post correctly excluded from the grid loop, same as the real
page's `listPosts = initialPosts.slice(1)`.
- Widened the mockup's max-width to max-w-5xl for this mode too (was
max-w-2xl), since the grid needs the room.
Headline auto-fit sizing:
- Two size tiers instead of one fixed 40-64 range: `default` (48-64,
larger) when there's no icon and the headline fits in <=2 lines;
`compact` (40-56) whenever an icon is present, and also whenever the
headline still needs a 3rd line even at the default tier's max size.
- maxLines raised from 2 to 3 — headlines can now wrap to a 3rd line
instead of only ever 1-2, but never more than 3.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous version took visual liberties (full-width hero, minimal
nav, wrong content width) instead of following the real page structure
in apps/www/components/Blog/BlogPostRenderer.tsx. Rebuilt to match:
- Real nav: Supabase wordmark (public/brand-logos/supabase-wordmark.svg),
actual primary nav labels (Product/Developers/Solutions/Pricing/Docs
from apps/www/data/nav.tsx), and the real Sign in / Start your project
CTAs.
- `data-theme="dark"` on the mockup root scopes the real dark-theme CSS
variables to this subtree (blog pages force dark mode via
useForceDeepDark) — bg-background/border-default/text-foreground/
text-brand now resolve to supabase.com's actual palette instead of an
approximation.
- Back link, "Blog" link, title, date/read-time, and author row placed
exactly as the real page orders them.
- The two-column grid now matches proportions: hero image lives in the
7/12 content column (not full width), with a 5/12 sidebar column
showing the eyebrow as a tag pill + "Share this article" — mirroring
the real page's content/sidebar split instead of a single full-bleed
image.
- Widened the mockup's max-width to max-w-5xl (matching the real page's
title-block max-w-5xl) only for this mode, so the wider two-column
layout doesn't get clipped by the other modes' narrower max-w-2xl.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Modeled on the real supabase.com/blog/[slug] page (BlogPostRenderer.tsx):
sticky nav, back-to-blog link, title, date/read-time, author, then the
hero image — cropped to a fixed 1.91:1 box (object-cover), same as the
live site does regardless of the source image's own aspect ratio, so
this shows exactly how much the Thumb image gets cropped rather than
assuming it survives untouched. Distinct from the existing "Blog
listing" grid mockup, which approximates the index page instead of a
single post.
Wires thumbImgUrl through so the mockup can prefer the Thumb render
(matching the real page's imgThumb-over-imgSocial priority) even when
the OG/Thumb toggle itself isn't currently showing Thumb.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Eyebrow already had maxLength as a belt-and-suspenders alongside the
onChange clamp; headline's textarea was missing it. Blog format was
already the default (og format id, label "Blog") — confirmed via a
fresh reload, no change needed there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The headline counter (70 hard limit) and eyebrow were previously
uncapped — the counter just changed color past the limit but never
stopped input. Added a clampChars helper that truncates on every
keystroke (grapheme-aware, matching the existing [...s].length
counters), applied to both fields, plus a native maxLength as a
belt-and-suspenders measure. Eyebrow gets a new 25-char limit and
counter, matching the headline's existing counter UI. Also capped
eyebrow at 25 chars server-side in /api/og for parity with headline's
existing server-side slice.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added a noEyebrow flag alongside the existing noIcon one — mirrors the
same pattern: hides the Eyebrow control in the sidebar and omits the
eyebrow pill from the render for this template specifically.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Layout 2's text was fully right-aligned (each line's own right edge
flush). Changed to left-aligned text within a right-anchored block —
the bounding box (sized to the widest line) still butts up against
the column's right edge, but each line now starts from the same left
edge (ragged-right), matching normal left-aligned paragraph text.
- Added a `category` field to Template and grouped the sidebar's Layout
picker into labeled sections (e.g. "Icon layouts", "Logo layouts")
instead of one flat grid — needed since the tile count keeps growing
per format.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Blog format gains three new layouts that render the fixed Supabase
wordmark instead of the user-selectable icon system:
- Logo top-left: wordmark pinned top-left, headline/eyebrow bottom-left.
- Logo left, text right: two-column layout — narrow left column with
the wordmark centered vertically/left-aligned, wider right column
with the headline centered vertically and right-aligned (max-width
580px).
- Logo center, text bottom: wordmark vertically centered/left-aligned,
headline/eyebrow anchored bottom-left.
The wordmark is a static brand asset (public/brand-logos/
supabase-wordmark.svg, embedded as a data URI via lib/design/logo.ts),
not a user-uploaded icon/logo asset, so these templates carry a
`noIcon` flag that hides the Icon control in the sidebar. Extended
Template.textAlign/anchorX with a 'right' option and TemplateParts with
logoEl/logoHeight so templates.tsx (shared with the client bundle)
never imports the server-only fs-reading logo module directly — route.tsx
computes the wordmark element and passes it down instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Centering a zoomed card via items-center/justify-center clips the
leading edge once it overflows the scroll container, so zoom>100 was
switching to items-start — but that left-aligned the card even when it
still fit comfortably at modest zoom levels like 125%.
Replaced the alignment approach with mx-auto/my-auto on the card
wrapper: flexbox auto margins center the item when there's room and
automatically collapse to 0 once it overflows, so the card stays
centered at any zoom level that fits and only becomes start-aligned
(scrollable, no clipping) once it doesn't.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Social previously reused the generic OG/Thumb/Both view toggle with a
classic icon-only Thumb crop. It needed a real second full composition
instead: Instagram's 1080x1350 feed size, rendered with the same
headline/eyebrow/icon/template recipe as the 1200x627 Twitter/X/LinkedIn
primary, not a stripped-down icon crop.
- formats.ts: add optional `primaryLabel` (view-toggle label override) and
`secondary` (a second full-composition size distinct from `thumb`) to
the Format interface; Social's twitter format now sets both.
- route.tsx: support `variant=secondary`, swapping in the format's
secondary width/height while reusing the existing template-driven
render path untouched by the icon-only thumb branch.
- page.tsx: view options are now format-derived (hasThumb vs hasSecondary
vs hasSecondSlot) instead of a static OG/Thumb/Both list; fixes a bug
where viewing Instagram alone hid the Layout/Eyebrow/Headline sidebar
controls (it's a full composition, not an icon-only crop, so those
controls should stay visible).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Group the zoom-tool/out/percentage/in buttons in their own gap-0.5 flex
instead of inheriting the toolbar's wider gap-3, and shrink them
slightly (h-7->h-6, 14px->13px icons, narrower percentage readout) so
the cluster reads as one compact control instead of four loosely
spaced buttons.
One centered bar now holds safe-area guide, Preview (view-in-context),
and zoom controls (divided by the same vertical-rule pattern already
used between sections), instead of two separate floating boxes. Zoom
still only shows when inContext === 'none', matching its prior visibility.
- Zoom control moved from bottom-left to bottom-right (mirrors the
Preview toolbar's left/right anchoring pattern) and now matches its
height/padding so the two visually center-align on the shared bottom-6
offset (verified via bounding-rect centers, within 1px)
- New "Z" keyboard shortcut toggles an Illustrator/Photoshop-style zoom
tool: cursor becomes zoom-in over the canvas, click to zoom in one
step, Alt+click (or hold Alt) to zoom out instead — cursor and the
zoom-control icon both reflect the Alt state live. Escape cancels the
tool; typing "z" in a text field is correctly ignored
- New bottom-left zoom control (-, percentage, +) scales the rendered
OG/Thumb image cards from 25% to 200% in 25% steps — only the images
resize, not the surrounding toolbar/panel controls
- Fixed a real overflow bug this surfaced: centering flex content wider
than its scroll container clips the left-side overflow with no way to
reach it via scroll in most browsers. Above 100% zoom the row switches
from center- to start-aligned so the full width stays reachable via
horizontal scroll
Also resolves a leftover git-stash merge-conflict in the local dev
launch.json (og-generator and www configs had collided into one entry).
- All 4 brands' bg.primary now use the same #121212 (was a slightly
different near-black per brand) — verified via pixel sampling of
rendered OG images
- Content group no longer has a top divider, removing the line that sat
directly under the Layout tiles (same noDivider pattern used for Layout
itself against the Format buttons above it)
Same pattern as Newsletter: Social (id "twitter") now swaps the standard
4-tile Layout section for two dedicated tiles — Instagram (centered,
tighter box for square-cropped feeds) and Twitter / X (classic
bottom-left card layout, matching how X actually presents link
previews) — with the same reset-to-valid-default effect on format change.
Group now takes an optional noDivider prop; the Layout group (which
immediately follows Brand & format) uses it so there's no border line
sitting right under the Format button set.
- "Export images" button renamed to "Export" and recolored to the brand
green (matches Download's styling elsewhere)
- Twitter/X in-context preview now always shows both light and dark cards
side by side instead of a toggle — no reason to hide either
- LinkedIn in-context preview is light mode only (LinkedIn's default and
by far its most common surface); the light/dark theme selector is
removed entirely
- Two new placeholder brands (Oriole: orange accent, Supasquad: magenta
accent) following the same pattern as Multigres — real colors TBD
- Format renames: "OG image" -> "Blog", "Twitter / X post" -> "Social"
- Two new formats: Newsletter (1200x600, no Thumb) and Luma (1600x900,
Luma's standard event-cover size, no Thumb)
- Newsletter gets its own two-tile Layout set (Newsletter cover / Section
header) instead of the standard 4 templates, swapped automatically when
the format changes, with a reset effect so the selected template is
always valid for the active format
- Fixed a latent bug where switching to a Thumb-less format while View was
"Both" still rendered an empty Thumb card
- In-context previews (Twitter/X, LinkedIn, Blog listing) now show a title
above the mockup, matching the OG/Thumb preview card labels
Drops the Supaimage/Supabase lightning-bolt mark into public/backgrounds/
as thumb-default.png — picked up automatically by
randomBackgroundDataUri() (single file today, so it's deterministic; more
can be added later without code changes).
- Icon dropdown now opens upward (bottom-full) instead of pushing the
panel taller downward, and closes on outside click like a real dropdown
- Floating toolbars use rounded-md to match the button-set corners instead
of rounded-full
- Remove the headline/WCAG info bar under the OG preview and the
no-icon warning under the Thumb preview
- Remove the random background-image fallback from OG/layout renders
entirely (flat color only, as templates originally worked) — kept only
for the icon-less Thumb, whose asset folder is now empty pending a
real image to drop in (see public/backgrounds/README.md)
- View toggle and the Preview/safe-area toolbar are now siblings of the
scrollable canvas (main), not children of it, so they stay anchored in
place instead of scrolling away with the content
- Twitter/X and LinkedIn in-context mockups use each platform's real hex
palette (not our app's semantic tokens) with a Light/Dark toggle —
X's true-black "Lights out" dark mode, LinkedIn's #1b1f23 dark surface,
accurate borders/secondary-text colors, verified badge, Follow button
- When a social preview is active, the OG/Thumb canvases are hidden and the
mockup takes over the full canvas view instead of sitting below the OG
card
- Remove per-card Copy URL / Download buttons from the OG and Thumb
previews; the header's "Export @2x" checkbox becomes an "Export images"
button that opens a modal
- Modal holds the @2x toggle plus one row per image: a read-only URL field,
a copy button (clipboard icon), and a download button (down-arrow icon)
- Bottom toolbar: add a "Preview" label ahead of the view-in-context
segmented control, and move the safe-area guide button to the other end
Chain height:100% from html -> body -> the app's root div instead of relying
on 100vh alone (min-h-screen on body, h-screen on the root). 100vh can end
up taller or shorter than the actual visible area in embedded iframe
previews (the same class of bug as mobile browser chrome), whereas an
unbroken height:100% percentage chain sizes correctly to whatever the host
gives the iframe.
- Safe area toggle in the bottom toolbar is now a dotted-box icon button
instead of a checkbox + label; the on-image guide is dotted to match
- Bottom toolbar now aligns to the same center line as the View toggle
above it (both toolbars sit in main's padded content box, but absolute
positioning ignores ancestor padding, so the offsets are repeated)
- Eyebrow style selector removed — always renders as a pill now
- Reduced the eyebrow pill's font weight one step (500 -> 400)
- Removed each template's default background pattern (dots/grid/hlines/
vlines) entirely, along with the now-dead grid-snap alignment math;
icon-less compositions still get the random image background
- Removed the "Template" label above the layout tiles
- Remove platform-crop previews (too small to be useful)
- View (OG/Thumb/Both) moves back to the top of the canvas, separate from
the other tools; the floating bottom toolbar now holds the safe-area
guide toggle and View-in-context
- Eyebrow defaults to the pill style, now white background / black text,
set in IBM Plex Mono (self-hosted alongside Manrope for satori + fontkit)
- Sentence case is now always on; wrap text in [brackets] to preserve its
exact casing instead of a manual on/off toggle
- Remove the manual font-size override — always auto-fit
- Remove the "No headline..." caption under the Thumb preview
- Icon-less compositions get a random background image from
public/backgrounds/ (new asset folder, 3 starter SVGs) instead of a flat
fill, on both the OG template root and the Thumb
- Brand and Format are now button sets instead of dropdowns
- Removed the AI art direction panel for now
- Templates render as visual tiles (like the icon grid) instead of a <select>
- Icon picker is now a dropdown with a thumbnail preview, moved to the end
of Content (so it's still reachable in Thumb-only view)
- Removed the Thumb icon-size control and the Background pattern controls —
Thumb uses the format default, patterns use each template's default
- View and View-in-context are now a single floating toolbar, bottom-center
of the canvas, instead of split between the canvas top and the sidebar
- Safe-area guide is now a uniform 80px inset on every edge
Today's platform-crop preview only showed a cropped rectangle, not what the
image looks like in actual use. Adds a "View in context" selector (View
group) with rough mockups of a Twitter/X post, a LinkedIn post, and a
supabase.com blog listing card, each embedding the currently-rendered image
so switching Brand/Format shows how it reads in context.
Also fixes the local dev launch config's pnpm filter for the og-generator ->
supaimage package rename.
Reflects the tool's expanded scope beyond Supabase blog OG images (package
name, page title, sidebar header, README). The apps/og-generator folder path
is intentionally left as-is for now, to avoid turning this PR's diff into a
delete+add and losing review history — a clean fast-follow after merge.
Splits design tokens into Brand (colors, illustration stroke) and Format
(canvas size, icon/thumb sizing) abstractions, threaded explicitly through
the render route, asset library, and editor UI instead of hidden globals.
Adds Multigres as a second (placeholder-color) brand and a Twitter/X post
format alongside the existing OG/Thumb. Brand/Format selectors now lead the
editor panel; uploaded assets are scoped per brand via a new `brand` column.
Confirmed via pixel measurement that grid-snap for top/bottom-anchored
headlines (stacked, bottom-left templates) was aligning to padY/H-padY — the
invisible CSS layout-box edge — while the visible glyph ink sat well inside
it due to line-height leading. Measured a 13px gap at fontSize=64 on the
'stacked' template (grid line at y=72, visible text top at y=85).
Fixed using the actual Manrope font metrics (already loaded via fontkit for
the auto-fit measurement) rather than a guessed pixel constant, so the
correction stays accurate at every font size auto-fit picks:
visualTopInset = halfLeading + ascent - capHeight (box top -> cap-height top)
visualBottomInset = halfLeading + descent (box bottom -> baseline)
Re-measured after the fix: top-anchor gap closed from 13px to 0px, bottom-anchor
to within 1px. Center-anchored templates (split-right, centered) were already
within 1.5-4px with no leading-driven bias -- left unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
changeTemplate() used to overwrite patternType/Scale/Opacity with the new
template's defaultPattern on every switch. Template and background are meant
to be independent controls, so it now only sets the template. AI suggestions
are unaffected — applySuggestion() already sets the pattern separately, right
after calling changeTemplate(), when the suggestion includes one. Verified:
set pattern to vlines, switched template to centered (whose default is dots),
pattern select stayed on vlines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round #19 applied 65% width unconditionally. Per follow-up: it should only
apply when viewing a single image (OG or Thumb alone) — "Both" reverts to
full width. Row width is now `view === 'both' ? 'w-full' : 'w-[65%]'`.
Verified via getBoundingClientRect at 1700px: Both = 100.0%, OG-only = 65.0%,
Thumb-only = 65.0%, zero console errors in all three states.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Split "WCAG:" into its own font-medium text-foreground span, matching
"Headline:" (previously the whole segment, including the label, was
colored by pass/fail state)
- Preview row: w-full -> w-[65%], with the wrapper's horizontal centering
made unconditional (was @4xl-only) so the narrower row stays centered in
both stacked and side-by-side layouts. Verified via getBoundingClientRect:
exactly 65.0% of the canvas's usable width, with equal left/right margins
(225.4px each) at 1700px.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous round's items-start fix aligned the two cards relative to EACH
OTHER (proven equal, but only proves top===center===bottom trivially) — it
never addressed the real complaint: the whole OG+Thumb row sat pinned near
the top of the canvas with dead space below on wide/tall screens. Wrapped the
row in a flex-1 container that centers it (@4xl:items-center
@4xl:justify-center) within the space remaining below the view toggle. Since
flex items don't shrink below their content size, an overflowing row still
just grows the wrapper rather than clipping (avoids the classic
flex-center-clips-on-overflow bug). Verified via getBoundingClientRect: 186.1px
of space above and below the row, exactly equal.
OG info panel: prefixed "Headline:" at the start, moved the fit-mode
indicator to right after the font size and reworded it to "(auto on)" (shown
only when auto-fit is active), and prefixed "WCAG:" before the contrast
score. Reads as "Headline: 64px (auto on) · 2 lines · WCAG: 16.3:1 AAA".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Panel is now position:absolute (not a flex sibling) over a full-bleed
absolute-inset canvas — one continuous dot-grid surface behind it, instead
of two boxes split by a shared flex edge. Canvas content gets pr-[380px]
so it doesn't render under the panel.
- Preview row: items-center -> items-start. OG and Thumb render at identical
size (same aspect ratio, equal flex-basis) with identical-height label rows
above them, so top-aligning is what actually keeps the IMAGES aligned;
items-center was aligning the outer card blocks (image + variable-height
caption below), which visibly offset the images from each other. Verified
via computed styles: both cards now start at the exact same top regardless
of caption height.
- Removed the zoom control entirely (state, buttons, ZOOM_MIN/MAX, the
style={{zoom}} CSS zoom) — it scaled the whole card including buttons/
labels, not just the rendered image.
- OG fit-info pill: added a real border + solid background (was
bg-surface-200 with no border, blending into the canvas) so it reads as a
distinct panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Package the top bar (title + Export @2x) into the sidebar itself, as a
sticky header strip inside the panel rather than a separate full-width bar
- Move the panel from the left to the right, and restyle it as a floating
card (rounded corners, shadow, inset margin) over the canvas instead of a
full-height docked sidebar
- Preview row: add @4xl:items-center so the OG and Thumb cards align by
vertical center when shown side by side on wide screens (they already
stacked at medium widths); verified via computed styles (align-items:
center, cards offset by exactly half their height difference)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Spreading the result straight onto <img> as JSX props meant satori saw
w={..} h={..} instead of width/height — no intrinsic size, so the logo
filled its entire container instead of fitting a small box. Caught via a
real end-to-end upload test against the live Supabase project (secret key +
migrations now applied): a color logo rendered full-canvas instead of boxed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Line icons are stroke-only by brand rule, but a partner's logo (Grafana,
Multigres, etc.) needs to render in ITS OWN colors — a second asset kind
alongside the existing icon system, not a replacement for it.
- supabase/migrations/0002_logo_assets.sql: add width/height to `assets`
(kind + storage_path already existed from 0001)
- lib/assets/sanitize-svg.ts: sanitizeLogoSvg() — a separate allowlist that
PRESERVES fill/stroke/gradients/style color declarations (unlike the icon
sanitizer, which strips them for the stroke-only treatment), while still
blocking scripts, event handlers, and external references (internal `#id`
refs are kept so gradient defs/<use> keep working). Unit-tested against a
mixed color+attack payload.
- lib/supabase/assets.ts: insertLogoAsset() uploads to the og-assets Storage
bucket (raster-safe, unlike inline SVG bodies) and records client-measured
width/height; rowToIcon exposes kind/url/width/height for both kinds.
- app/api/assets/route.ts: POST branches on a `kind` field ('icon', default,
or 'logo'); logo accepts SVG/PNG/JPEG/WebP up to 2MB, requires width+height.
- app/api/og/route.tsx: icon slots render logos as-is (no stroke
normalization), fit to their natural aspect ratio via a new fitBox() helper.
- app/page.tsx: a second "+ Upload logo (color)" control measures the file's
natural dimensions in-browser (Image().onload) before POSTing; the icon
picker renders logo entries as real <img> (actual colors) instead of the
forced-stroke SVG redraw.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop a plain figma-links.txt (one URL per line) instead of pairing every
image with a .figma.txt file — Claude Code pulls the screenshot/design
context per link directly via the Figma integration. Also notes this same
drop zone doubles as the intake for a future image-generation training set.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>