Conversation
✅ Deploy Preview for cell-catalog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds Cloudinary-backed image uploads to the Decap CMS admin flow while preserving existing local-image rendering by introducing a dual image model (Gatsby image data vs external URL) and plumbing that URL through Gatsby GraphQL.
Changes:
- Added a custom Decap CMS widget (
cloudinary-image) that uploads to Cloudinary and writes the resulting URL into frontmatter. - Introduced
ImageRenderer+ImageSourceto render eitherGatsbyImagedata or external<img src=...>URLs, and updated consumers accordingly. - Added an
image_urlGraphQL field (viacreateResolvers) and updated relevant queries to fetch it alongside existing image fields.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| static/admin/config.yml | Switches image widgets to the new cloudinary-image widget (including header background). |
| src/utils/mediaUtils.ts | Adds isExternalUrl, updates image unpacking to support image_url, and changes thumbnail extraction to return ImageSource. |
| src/templates/disease-cell-line.tsx | Queries image_url alongside existing image fields. |
| src/templates/cell-line.tsx | Queries image_url alongside existing image fields (including media + diagrams + rnaseq). |
| src/style/images-and-videos.module.css | Adjusts media container height styling. |
| src/components/shared/ImageRenderer.tsx | New shared renderer for Gatsby images vs external URLs. |
| src/components/shared/DiagramCard.tsx | Uses ImageRenderer and updates prop typing to accept ImageSource. |
| src/components/Thumbnail.tsx | Uses ImageRenderer and updates prop typing to accept ImageSource. |
| src/components/SubPage/convert-data.ts | Uses unpackImageData to normalize images into ImageSource. |
| src/components/ParentalLineModal.tsx | Updates thumbnail rendering to support ImageSource via ImageRenderer. |
| src/components/ImagesAndVideo/MediaCard.tsx | Replaces direct GatsbyImage usage with ImageRenderer. |
| src/components/ImagesAndVideo/ImagePreviewGroup.tsx | Supports previewing external URLs by bypassing getSrc when needed. |
| src/components/CellLineTable/SharedColumns.tsx | Updates thumbnail rendering logic to support external URLs. |
| src/component-queries/types.ts | Introduces ImageSource and updates frontmatter/result types for Cloudinary URL support. |
| src/component-queries/NormalCellLines.tsx | Adds image_url to the table query. |
| src/component-queries/DiseaseCellLines.tsx | Adds image_url to the disease table query. |
| src/cms/widgets/CloudinaryImageWidget.tsx | New Cloudinary upload widget implementation (script loading, folder derivation, preview). |
| src/cms/cms.tsx | Registers the custom cloudinary-image widget with Decap CMS. |
| src/cms/cloudinaryConfig.ts | Adds Cloudinary widget configuration constants. |
| gatsby-node.js | Adds schema/types/resolvers for image_url and introduces an ImagesAndVideos type. |
Comments suppressed due to low confidence (1)
gatsby-node.js:68
ImagesAndVideosis now explicitly typed but only includesimages. Existing GraphQL queries insrc/templates/cell-line.tsxrequestimages_and_videos { videos { ... } }, so this schema will either drop thevideosfield or rely on inference (which is brittle) and can break builds. Define aVideoWithCaptiontype and addvideos: [VideoWithCaption]toImagesAndVideos(or otherwise include the videos field explicitly) so the schema matches frontmatter usage.
` type ImgWithCaption {
image: File @fileByRelativePath
caption: String
}
type ImagesAndVideos {
images: [ImgWithCaption]
}
type Diagram {
title: String
images: [ImgWithCaption]
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
interim17
left a comment
There was a problem hiding this comment.
Haven't run this and left some questions, but non-blocking, looks good!
| interface ImmutableMap { | ||
| get: (key: string) => unknown; | ||
| } |
There was a problem hiding this comment.
Too bad we can't easily share this kind of code between cell catalog and idea board.
| style={{ | ||
| width: 192, | ||
| height: 192, | ||
| objectFit: "cover", |
There was a problem hiding this comment.
Is there a reason to do inline styling here?
| return file.childImageSharp?.gatsbyImageData?.images?.fallback?.src; | ||
| } | ||
| export const getImageSrcFromFileNode = (file: FileNode): string | undefined => { | ||
| return file.childImageSharp?.gatsbyImageData?.images?.fallback?.src; |
There was a problem hiding this comment.
I know its not new but all these chained optional operators look so odd to me after not being in this repo for a bit.
| // Expose the raw image string as `image_url` for Cloudinary URL support. | ||
| // The existing `image: File @fileByRelativePath` returns null for URLs, | ||
| // so we need this field to pass through Cloudinary URLs to the frontend. | ||
| exports.createResolvers = ({ createResolvers }) => { | ||
| const imageUrlResolver = { | ||
| image_url: { | ||
| type: "String", | ||
| resolve: (source) => { | ||
| const img = source.image; | ||
| if (typeof img === "string") return img; | ||
| return null; | ||
| }, | ||
| }, | ||
| }; | ||
| createResolvers({ | ||
| ImgWithCaption: imageUrlResolver, | ||
| RnaSeqRow: imageUrlResolver, | ||
| MarkdownRemarkFrontmatterEditing_designDiagramsImages: imageUrlResolver, | ||
| Diagram: imageUrlResolver, | ||
| MarkdownRemarkFrontmatterHeader: { | ||
| background_url: { | ||
| type: "String", | ||
| resolve: (source) => { | ||
| const bg = source.background; | ||
| if (typeof bg === "string") return bg; | ||
| return null; | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
| }; | ||
|
|
There was a problem hiding this comment.
Love bringing the resolver pattern over into cell catalog!
| // The upload preset restricts what operations are allowed. | ||
| export const CLOUDINARY_CLOUD_NAME = "dkg6lnogl"; | ||
| export const CLOUDINARY_UPLOAD_PRESET = "allen-cell"; | ||
| export const CLOUDINARY_API_KEY = "989839737788897"; |
There was a problem hiding this comment.
Trying to believe the comment about about public facing, but it does smell strange to have something called API_KEY committed publicly! Could use .env? Maybe its fine.
| script.src = | ||
| "https://upload-widget.cloudinary.com/latest/global/all.js"; |
|
Note 5/21:
|
Problem
What is the problem this work solves, including
step 1 of #88
Solution
What I/we did to solve this problem
ImageRendererto handle both gastby images(GatsbyImage) and external urls (<img>)image_urlfield viacreateResolversin gatsby-node.js.imageresolves toGatsbyImage,image_urlis nullimageis null andimage_urlcarries the urlNotes:
This is mainly to show how Cloudinary can work with our current image management. With this change, images can now be uploaded to Cloudinary, while local images rendering still works the same as it does on
main.Things we can decide separately later:
Not yet implemented:
Type of change
Please delete options that are not relevant.
Steps to Verify:
Screenshots (optional):
Show-n-tell images/animations here
Replace Imagefor a local image, the replacement image is uploaded to Cloudinary instead.