@upstart.gg/sdk
Version:
You can test the CLI without recompiling by running:
457 lines (448 loc) • 12.1 kB
JavaScript
import { defineBrickManifest } from "../../brick-manifest.js";
import { cssLength } from "../props/css-length.js";
import { defineProps } from "../props/helpers.js";
import { image } from "../props/image.js";
import { shadow } from "../props/effects.js";
import { colorPreset } from "../props/color-preset.js";
import { loop } from "../props/dynamic.js";
import { border, rounding } from "../props/border.js";
import { Type } from "@sinclair/typebox";
import { RxImage } from "react-icons/rx";
//#region src/shared/bricks/manifests/image.manifest.ts
const manifest = defineBrickManifest({
type: "image",
name: "Image",
category: "media",
description: "An image brick",
aiInstructions: `PURPOSE
Display a single image with optional framing (padding, rounding, border, shadow, colorPreset) and attribution (author/provider). Can also loop over a dataset showing one image per item.
REQUIRED
• 'image.src' and 'image.alt' (alt must be meaningful, not empty; may interpolate page queries fields).
STYLING
• padding should be a single css length value (like '1rem' or '0.75rem').
• rounding defaults to a modest radius; override for circular avatars (rounded-full) or strong accent (rounded-xl).
• border optional: keep subtle (border / border-2) unless emphasis required.
• Use shadow sparingly (shadow-sm / shadow-md); large (shadow-xl) for hero or banner impact.
ATTRIBUTION
• If using external photography include author { name, url } and provider (e.g. 'unsplash'). Omit provider if internal asset.
DYNAMIC DATA
• Interpolate dataset fields with {{dataset.field}} in src and alt.
• For lists: use loop.over = "datasetName". Provide exactly one example object (this component itself) when looping (do not replicate multiple objects).
RESPONSIVE
• mobileProps may reduce padding or alter rounding only if it materially improves mobile layout. Always repeat required fields (image.src, image.alt) if providing mobileProps.
DON'TS
✗ Don't add unrelated props.
✗ Don't fabricate color tokens (no success-, warning-, danger- etc.).
✗ Don't use HTML markup inside alt.
DO
✓ Provide descriptive alt text (<125 chars) describing content or function.
✓ Use semantic color presets consistently.
✓ Add author/provider when you have them.
`,
defaultWidth: {
desktop: "auto",
mobile: "100%"
},
icon: RxImage,
props: defineProps({
image: image({ metadata: { category: "content" } }),
colorPreset: Type.Optional(colorPreset()),
padding: Type.Optional(cssLength({
description: "Padding inside the image.",
title: "Padding",
"ai:instructions": "Use only a single value like '1rem' or '10px'",
"ui:responsive": true,
"ui:placeholder": "Not specified",
"ui:styleId": "styles:padding"
})),
rounding: Type.Optional(rounding()),
border: Type.Optional(border()),
shadow: Type.Optional(shadow()),
blurHash: Type.Optional(Type.String({
title: "Blur hash",
"ui:field": "hidden",
description: "A placeholder for the image while it is loading. Use a blur hash string."
})),
author: Type.Optional(Type.Object({
name: Type.String({
title: "Image Author",
description: "Image author. Use this to give credit to the author"
}),
url: Type.String({
title: "Image Author URL",
format: "uri",
description: "Image author URL. Use this to give credit to the author"
})
}, { "ui:field": "hidden" })),
provider: Type.Optional(Type.String({
title: "Image Provider",
"ui:field": "hidden",
"ai:instructions": "The provider of the image, e.g. 'unsplash', 'pexels', etc."
})),
loop: Type.Optional(loop())
})
});
const examples = [
{
description: "Hero landscape image with large shadow",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/800x400.png?text=Hero+Landscape",
alt: "Beautiful landscape view for hero section"
},
shadow: "shadow-lg"
}
},
{
description: "Framed image with accent gradient background and padding",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/640x360.png?text=Showcase",
alt: "Showcase screenshot inside accent gradient frame"
},
colorPreset: {
color: "accent-gradient-400",
gradientDirection: "bg-gradient-to-br"
},
padding: "1rem",
rounding: "rounded-xl",
shadow: "shadow-md"
}
},
{
description: "Team member profile photo, full rounded",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/300x300.png?text=Profile+Photo",
alt: "Team member profile photo"
},
shadow: "shadow-md",
rounding: "rounded-full"
}
},
{
description: "Product showcase image",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/400x400.png?text=Product+Image",
alt: "Premium product showcase"
},
shadow: "shadow-sm"
}
},
{
description: "Blog article featured image with medium shadow and full author info",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/600x300.png?text=Article+Featured",
alt: "Featured image for blog article"
},
author: {
name: "John Photographer",
url: "https://example.com/john"
},
provider: "unsplash",
shadow: "shadow-md"
}
},
{
description: "Logo image with padding and small shadow",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/200x80.png?text=Company+Logo",
alt: "Company logo"
},
padding: "p-8",
shadow: "shadow-sm"
}
},
{
description: "Dark framed image with neutral-800 background and subtle border",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/500x300.png?text=Dark+Mode",
alt: "Interface preview in dark mode"
},
colorPreset: { color: "neutral-800" },
padding: "1rem",
border: {
width: "border",
color: "border-neutral-700"
},
rounding: "rounded-lg",
shadow: "shadow-md"
}
},
{
description: "Testimonial customer photo with large shadow and small padding",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/120x120.png?text=Customer",
alt: "Happy customer testimonial photo"
},
shadow: "shadow-lg",
padding: "p-2"
}
},
{
description: "Event banner image with neutral-500 background",
type: "image",
props: {
colorPreset: { color: "neutral-500" },
image: {
src: "https://via.placeholder.com/800x200.png?text=Event+Banner",
alt: "Annual conference event banner"
},
shadow: "shadow-xl",
author: {
name: "Event Photographer",
url: "https://example.com/photographer"
},
provider: "pexels"
}
},
{
description: "Illustration with background and padding",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/150x150.png?text=Blabla+Feature",
alt: "Feature illustration"
},
padding: "p-8",
shadow: "shadow-sm"
}
},
{
description: "Image with blurHash",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/300x200.png?text=Card+Image",
alt: "My image"
},
shadow: "shadow-md",
blurHash: "L6PZfSi_.AyE_3t7t7R**0o#DgR4"
}
},
{
description: "Responsive image with reduced mobile padding",
type: "image",
props: {
image: {
src: "https://via.placeholder.com/640x360.png?text=Responsive",
alt: "Responsive framed screenshot"
},
padding: "2rem",
colorPreset: { color: "primary-50" },
rounding: "rounded-xl",
shadow: "shadow-sm"
},
mobileProps: {
image: {
src: "https://via.placeholder.com/640x360.png?text=Responsive",
alt: "Responsive framed screenshot"
},
padding: "1rem",
colorPreset: { color: "primary-50" },
rounding: "rounded-lg"
}
},
{
description: "Dynamic employee photo using employee query",
type: "image",
props: {
image: {
src: "{{employee.photo}}",
alt: "Photo of {{employee.fullName}}"
},
rounding: "rounded-full",
shadow: "shadow-lg",
padding: "1rem"
}
},
{
description: "Dynamic product image using product query with author attribution",
type: "image",
props: {
image: {
src: "{{product.featuredImage}}",
alt: "{{product.name}} - {{product.category}}"
},
author: {
name: "{{product.photographer}}",
url: "{{product.photographerUrl}}"
},
provider: "company-assets",
shadow: "shadow-md",
rounding: "rounded-lg"
}
},
{
description: "Dynamic company logo using company query",
type: "image",
props: {
image: {
src: "{{company.logo}}",
alt: "{{company.name}} logo"
},
padding: "2rem",
shadow: "shadow-sm",
colorPreset: { color: "neutral-100" }
}
},
{
description: "Dynamic event banner using event query",
type: "image",
props: {
image: {
src: "{{event.bannerImage}}",
alt: "{{event.title}} event banner"
},
shadow: "shadow-xl",
rounding: "rounded-xl",
author: {
name: "{{event.photographer}}",
url: "{{event.photographerProfile}}"
},
provider: "event-photography"
}
},
{
description: "Loop template: product gallery (one definition, repeated by loop.over)",
type: "image",
props: {
image: {
src: "{{products.mainImage}}",
alt: "{{products.name}} – {{products.category}}"
},
rounding: "rounded-md",
padding: "0.5rem",
shadow: "shadow-sm",
loop: { over: "products" }
}
},
{
description: "Dynamic article featured image using article query",
type: "image",
props: {
image: {
src: "{{article.featuredImage}}",
alt: "Featured image for {{article.title}}"
},
author: {
name: "{{article.imageCredit}}",
url: "{{article.imageCreditUrl}}"
},
provider: "{{article.imageProvider}}",
shadow: "shadow-md",
rounding: "rounded-lg"
}
},
{
description: "Dynamic property photo using property query",
type: "image",
props: {
image: {
src: "{{property.mainPhoto}}",
alt: "{{property.address}} - {{property.propertyType}}"
},
shadow: "shadow-lg",
rounding: "rounded-xl",
colorPreset: { color: "primary-50" },
padding: "1rem"
}
},
{
description: "Dynamic portfolio piece using portfolio query",
type: "image",
props: {
image: {
src: "{{portfolio.imageUrl}}",
alt: "{{portfolio.projectName}} for {{portfolio.clientName}}"
},
author: {
name: "{{portfolio.photographer}}",
url: "{{portfolio.photographerWebsite}}"
},
provider: "portfolio-assets",
shadow: "shadow-md",
rounding: "rounded-lg",
padding: "0.5rem"
}
},
{
description: "Dynamic service illustration using service query",
type: "image",
props: {
image: {
src: "{{service.illustration}}",
alt: "{{service.name}} service illustration"
},
colorPreset: { color: "secondary-100" },
padding: "2rem",
shadow: "shadow-sm",
rounding: "rounded-full"
}
},
{
description: "Dynamic testimonial customer photo using testimonial query",
type: "image",
props: {
image: {
src: "{{testimonial.customerPhoto}}",
alt: "{{testimonial.customerName}} from {{testimonial.company}}"
},
rounding: "rounded-full",
shadow: "shadow-lg",
padding: "0.25rem",
colorPreset: { color: "accent-50" }
}
},
{
description: "Product gallery using products query with loop",
type: "image",
props: {
image: {
src: "{{products.mainImage}}",
alt: "{{products.name}} - {{products.category}}"
},
author: {
name: "{{products.photographer}}",
url: "{{products.photographerUrl}}"
},
provider: "product-catalog",
shadow: "shadow-md",
rounding: "rounded-lg",
padding: "0.5rem",
colorPreset: { color: "primary-50" },
loop: { over: "products" }
}
},
{
description: "Team member photos using teamMembers query with loop",
type: "image",
props: {
image: {
src: "{{teamMembers.profilePhoto}}",
alt: "{{teamMembers.fullName}} - {{teamMembers.position}}"
},
rounding: "rounded-full",
shadow: "shadow-lg",
padding: "0.75rem",
colorPreset: { color: "secondary-100" },
loop: { over: "teamMembers" }
}
}
];
//#endregion
export { examples, manifest };
//# sourceMappingURL=image.manifest.js.map