Skip to content

Sanity Media Plugin#243

Open
diboune wants to merge 53 commits intomainfrom
tin-3485-sanity-media-plugin
Open

Sanity Media Plugin#243
diboune wants to merge 53 commits intomainfrom
tin-3485-sanity-media-plugin

Conversation

@diboune
Copy link
Copy Markdown
Member

@diboune diboune commented Dec 26, 2025

No description provided.

@vercel
Copy link
Copy Markdown

vercel bot commented Dec 26, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sanity-kit-docs Ready Ready Preview, Comment Feb 28, 2026 11:57am

Request Review

Base automatically changed from tin-3444-create-portabletext-utils to tin-3464-blog-examples December 29, 2025 15:23
Base automatically changed from tin-3464-blog-examples to main December 29, 2025 15:28
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Jan 12, 2026

⚠️ No Changeset found

Latest commit: bd19ad9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Copy Markdown
Collaborator

@stilyan-tinloof stilyan-tinloof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, nothing breaking at all

Also don't forget a changeset

I've also created a Linear issue of TIN-3546: Future improvements for @tinloof/sanity-media

Comment thread examples/blog-next/CHANGELOG.md Outdated
Comment thread examples/blog-next/package.json Outdated
Comment thread examples/blog-studio/src/schemas/objects/sections/image.ts
Comment thread examples/blog-studio/src/schemas/objects/sections/media-image.ts Outdated
Comment thread examples/blog-studio/src/schemas/objects/sections/media-image.ts Outdated
Comment thread packages/sanity-media/src/components/media-panel/types.tsx Outdated
Comment thread packages/sanity-media/src/components/shared/hooks/use-bulk-selection.ts Outdated
Comment thread packages/sanity-media/src/components/shared/hooks/use-upload-queue.ts Outdated
Comment thread packages/sanity-media/src/types.ts Outdated
Comment thread packages/sanity-media/src/types.ts Outdated
@stilyan-tinloof stilyan-tinloof changed the title Sanity media plugin Add media library with bulk operations, filtering, and browse navigation fixes Jan 18, 2026
@stilyan-tinloof stilyan-tinloof changed the title Add media library with bulk operations, filtering, and browse navigation fixes Sanity Media Plugin Jan 18, 2026
…prove media browser dialog layout

- Add ResponsiveGrid component using CSS container queries for container-based responsiveness
- Replace viewport-based Grid columns with container-query-based grid in media-grid-view, asset-grid, and media-browser-dialog
- Restructure media browser dialog with proper section separation (header, content, pagination footer)
- Add border dividers between header/content and content/pagination sections
- Fix scroll behavior so cards scroll under borders seamlessly
- Reorganize toolbar into two rows for better small container layout
…dia panel heading

- Move pagination to sticky footer matching media browser dialog layout
- Display item count next to "Media Library" heading as "(X items)"
- Remove redundant item count from scrollable content area
- Add first/last page buttons using double chevron icons
- Show clickable page numbers with ellipsis for large page counts
- Current page highlighted with background, others muted
- Accessible buttons with proper keyboard focus
- Replace simple "Page X of Y" with interactive pagination in media panel and dialog
…close

- Move upload progress display from media panel to staging dialog
- Show per-item progress bars with status icons during upload
- Auto-close dialog 3 seconds after all uploads complete
- Remove separate upload queue in favor of tracking state in staging items
- Add hook to intercept image/video pastes in PortableTextInput
- Support multiple files in a single paste
- Show staging dialog for metadata entry before upload
- Return all uploaded assets in batched callback
- Auto-close dialog after uploads complete
- Fix memory leak: use functional state update in closeStagingDialog
- Add unmount cleanup effect to revoke object URLs
- Fix ID collision: add counter to prevent duplicate IDs when multiple
  files are added/pasted simultaneously
- Make expanded state consistent: only first item expanded in both hooks
- Add missing 'uploading' dependency to renderStagingDialog
- Fix incorrect useState hook usage in media-file-input.tsx by
  changing it to useEffect for side effects
- Replace window.location.href with router.navigateUrl in
  selection-context.tsx to preserve unsaved state and avoid
  full page reloads
- Fix race condition in pending selection by clearing session
  storage immediately and removing fragile ref-based guard
- Fix memory leak in metadata-extractor by ensuring Object URLs are
  revoked in all error paths via try-catch-finally
- Add GROQ query escaping to prevent special character issues and
  limit search length to 100 chars
- Add filename length validation (200 char limit) in storage-client
  to prevent S3 key limit issues
- Add toast notifications for metadata update errors with automatic
  state revert on failure
- Document LQIP_MAX_DIMENSION constant explaining 20px tradeoff
- Replace any type with FileAssetPreview type in media-file-input
- Resize checkboxes and type indicators to 20px (from 14px)
- Desktop: hide checkboxes by default, show on hover/focus/selected
- Mobile/touch: always show checkboxes for accessibility
- Add smooth opacity transitions
- Reorganize header into two rows:
  - Row 1: Full-width search + Upload + Settings buttons
  - Row 2: Select all checkbox + Title/count + Type filter + Sort + View toggle
- Make toolbar responsive on mobile:
  - Hide button text for type/sort filters (show only icons)
  - Hide "Media Library" title, keep item count visible
- Improve selection UX:
  - Show "X of Y selected" instead of replacing title
  - Add accessible select all checkbox with keyboard support
  - Remove bulk delete from toolbar (user has another plan for this)
- Simplify view toggle to single button with tooltip
- Add icons to sort menu items (Calendar, DocumentText, Database)

Note: CSS selectors for hiding button text use internal data-ui attributes
which may need updates if Sanity UI changes its internal structure.
- Show checkmark when all items selected, minus icon when partial
- Simplify count text to always show total items
- Change asset checkboxes from CheckmarkCircleIcon to CheckmarkIcon
This removes the optimized preview thumbnail generation that was added
in commit 1029498. Preview thumbnails are no longer generated or stored
in Cloudflare R2 storage.

Changes:
- Remove preview generation from upload handler
- Remove getPreviewKey and uploadBlob from storage client
- Remove preview constants (PREVIEW_MAX_HEIGHT, PREVIEW_QUALITY, PREVIEW_SUFFIX)
- Remove generatePreviewBlob from metadata extractor
- Remove preview field from imageAsset and videoAsset schemas
- Update getAssetPreviewUrl to use video thumbnails directly
- Clean up preview deletion code from bulk selection hook
Fixed race conditions causing brief flashes of incorrect UI states during initial load:

- Add intermediate state detection for credentials loading to prevent config message flash
- Track countsLoading state to prevent premature "No media yet" message
- Add debug console logs to monitor render state transitions

The component now properly handles the gap between credentialsLoading becoming false and credentials being set by detecting when media exists but credentials aren't ready yet.
Fixed the root cause of loading state flashes by ensuring useCredentials hook keeps loading state true until credentials are fully processed, not just when the API call completes.

Changes:
- Add credentialsReady state to track when credentials are fully set
- Keep loading=true until both secretsLoading is done AND credentials are processed
- Handle edge cases: conversion errors and missing secrets
- Remove debug console logs from media panel
- Remove intermediate state detection workaround (no longer needed)

This eliminates the 1-render gap that was causing UI flashes during initial load.
Address code review findings to make the hook more robust:

- Add adapter memoization by id to prevent infinite re-renders from reference changes
- Reset state when adapter or secrets change to prevent stale credentials
- Clear credentials on conversion errors instead of keeping stale values
- Add effect cleanup with cancellation flag to prevent race conditions
- Add JSDoc for credentialsReady state

These fixes handle edge cases that could occur during adapter changes, rapid secrets updates, or conversion errors.
…sets

Simplify useCredentials hook based on code review findings:

- Remove broken useMemo that could return stale adapter functions
- Remove state reset at effect start that caused 3-render cycle
- Use adapter directly since it's stable at plugin initialization
- Add comprehensive JSDoc for return values
- Keep effect cleanup and error handling improvements

This results in cleaner, more correct code without extra re-renders.
…ation

Adds an optional imageTransformer function to MediaPluginOptions that allows
consumers to route image URLs through a CDN (e.g. Cloudflare Image Resizing,
imgix) without any CDN-specific logic in the plugin itself.

- Adds ImageTransformer type and ImageTransformerOptions type (exported from public API)
- Threads imageTransformer through AdapterContext for input components
- Props-drills imageTransformer through MediaTool → MediaPanel → grid/list/detail views
- Applied to images only (never video thumbnails): ThumbnailCell (200×200),
  AssetGrid/MediaGridView (450×450), MediaDetailPanel (900 scale-down),
  MediaImageInput (600 cover)
- Sizes align with main site srcset widths for shared Cloudflare cache entries
- Fully optional: plugin behaviour is unchanged when not provided
…o thumbnail transformation

- Move ImageTransformer type into types.ts alongside ImageTransformerOptions so
  both are co-located and exported from a single source in the public API
- Remove unused quality field from ImageTransformerOptions (was defined but never
  passed at any call site — avoids false promises in the public API)
- Enable video thumbnail transformation: remove mediaType === "image" guard from
  ThumbnailCell, AssetGrid, and MediaGridView — getAssetPreviewUrl always returns
  an image URL (the asset itself for images, the thumbnail image for videos), so
  both benefit equally from CDN optimisation
- Strengthen MediaImageInput guard to use _type.endsWith("videoAsset") instead of
  the derived mediaType field which is not stored on the Sanity document
Extend StorageAdapter with optional presign/presignDelete functions so
consumers can use server-side presigned URLs instead of browser-side
AWS credentials. Existing credential-based adapters remain unchanged.

A centralized `ready` boolean replaces scattered `!credentials` guards
across all input components, the media panel, upload queue, bulk
selection, and browser dialog.
…Q queries

Extract .text from title/description fields that may be objects (e.g. {alignment, text})
instead of plain strings, preventing React "Objects are not valid as a React child" errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants