Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5a0737d
Add site icon, plan name, and creation date to sync site data
shaunandrews Apr 16, 2026
6872c05
Show mshot thumbnails, site icons, and plan names in site list
shaunandrews Apr 16, 2026
e5ec7f5
Widen CSP img-src to allow site icons from any HTTPS domain
shaunandrews Apr 16, 2026
5a619bf
Add sign-out link to add site options screen
shaunandrews Apr 16, 2026
0ae1b08
Switch to v1.3 /me/sites API with server-side filtering and paginatio…
shaunandrews Apr 16, 2026
cc754cd
Redesign site list as card grid with grouped sections and server-side…
shaunandrews Apr 16, 2026
4fa8174
Update pull site flow with cleaner auth UI and debug state switcher
shaunandrews Apr 16, 2026
81412cf
Avoid marking connected sites deleted when they're on a later page
shaunandrews Apr 20, 2026
7a4e702
Default to system color scheme instead of light
shaunandrews Apr 20, 2026
e4a924c
Give the app window a larger default size
shaunandrews Apr 20, 2026
85f15bc
Redesign the Add Site flow around a card-based picker
shaunandrews Apr 20, 2026
f012a46
Collapse JSX attribute formatting from the add-site refactor
shaunandrews Apr 20, 2026
232c31f
Reconcile with trunk: drop blueprint-warnings and narrow /me/sites parse
shaunandrews Apr 20, 2026
3e0c724
Center the sync sites modal title regardless of close button width
shaunandrews Apr 20, 2026
70f9e5f
Merge branch 'trunk' of github.com:Automattic/studio into enrich-site…
Apr 23, 2026
81b76de
Wrap in feature flag the new design
Apr 23, 2026
59fec3b
Clean up preflight
Apr 23, 2026
df0219d
Add WP.com / Pressable badge
Apr 23, 2026
ac61ec2
Remove preview link logic for now
Apr 23, 2026
76bee04
Don't use badge in the old list
Apr 23, 2026
b507b58
Remove read code
Apr 23, 2026
b26bba2
Ensure the trunk is correct
Apr 24, 2026
651daa4
Ensure the trunk is correct
Apr 24, 2026
d460062
Merge branch 'trunk' of github.com:Automattic/studio into enrich-site…
Apr 24, 2026
419c549
Ensure the sync modal looks the same on trunk
Apr 24, 2026
7b99cd9
Cleanup for lint
Apr 24, 2026
b924f8c
linter
Apr 24, 2026
6672683
Ensure translations work correctly
Apr 24, 2026
8a4615f
Merge branch 'trunk' of github.com:Automattic/studio into enrich-site…
Apr 24, 2026
5b761e0
Ensure truk remains the same
Apr 24, 2026
b40d212
Merge branch 'trunk' into enrich-site-list-data
katinthehatsite Apr 24, 2026
01c8f7d
Merge branch 'trunk' of github.com:Automattic/studio into fix/add-blu…
Apr 27, 2026
8a946bd
Pull in blueprints library visually
Apr 27, 2026
0883083
Solve issues with activating blueprints
Apr 28, 2026
005b382
Merge branch 'trunk' of github.com:Automattic/studio into enrich-site…
Apr 28, 2026
f31cc45
Merge branch 'enrich-site-list-data' of github.com:Automattic/studio …
Apr 28, 2026
790ca70
Fix image rendering
Apr 28, 2026
7aa1b7c
Drop height for bluriness gradient
Apr 28, 2026
d0bfb3d
Fix failing types
Apr 28, 2026
44d3fa9
Merge branch 'trunk' of github.com:Automattic/studio into fix/add-blu…
Apr 28, 2026
aed36ff
Type fix
Apr 28, 2026
95fb664
Merge branch 'enrich-site-list-data' of github.com:Automattic/studio …
Apr 28, 2026
d452c01
Merge remote-tracking branch 'origin/trunk' into fix/add-blueprints-ui
Apr 29, 2026
b07f4ce
Reset changes different from trunk
Apr 29, 2026
3d69a1b
Move blueprints to the same page
Apr 29, 2026
d4b548e
Merge branch 'trunk' of github.com:Automattic/studio into fix/add-blu…
Apr 29, 2026
2af3005
Move add blueprints button to the top
Apr 29, 2026
d68e198
Ensure we resolve images correctly
Apr 29, 2026
689740e
Fix lint
Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apps/cli/commands/site/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,8 +815,13 @@ export const registerCommand = ( yargs: StudioArgv ) => {

// When invoked by the desktop app, the blueprint contents come from a temp file
// but resources should be resolved relative to the original file location.
// For gallery blueprints the path is a URL; use it directly.
if ( argv.originalBlueprintPath ) {
config.blueprint.uri = path.resolve( argv.originalBlueprintPath );
const originalPath = argv.originalBlueprintPath;
config.blueprint.uri =
originalPath.startsWith( 'http://' ) || originalPath.startsWith( 'https://' )
? originalPath
: path.resolve( originalPath );
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://public-api.wordpress.com https://api.wordpress.org https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://unpkg.com https://cdn.jsdelivr.net https://wordpress.github.io https://raw.githubusercontent.com ws://localhost:*; img-src 'self' https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://wordpress.github.io data:;">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://public-api.wordpress.com https://api.wordpress.org https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://unpkg.com https://cdn.jsdelivr.net https://wordpress.github.io https://raw.githubusercontent.com ws://localhost:*; img-src 'self' https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://wordpress.github.io https://raw.githubusercontent.com data:;">
<title>WordPress Studio</title>
</head>
<body class="a8c-body">
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ async function appBoot() {
const basePolicies = [
"default-src 'self'", // Allow resources from these domains
"script-src-attr 'none'",
"img-src 'self' https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://wordpress.github.io data:",
"img-src 'self' https://*.gravatar.com https://*.wp.com https://blueprintlibrary.wordpress.com https://wordpress.github.io https://raw.githubusercontent.com data:",
"style-src 'self' 'unsafe-inline'", // unsafe-inline used by tailwindcss in development, and also in production after the app rename
"script-src 'self' 'wasm-unsafe-eval'", // allow WebAssembly to compile and instantiate
];
Expand Down
122 changes: 122 additions & 0 deletions apps/studio/src/modules/add-site/components/explore-blueprints.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
__experimentalVStack as VStack,
__experimentalHeading as Heading,
__experimentalText as Text,
Spinner,
} from '@wordpress/components';
import { useI18n } from '@wordpress/react-i18n';
import { cx } from 'src/lib/cx';
import { GalleryBlueprint } from 'src/stores/gallery-blueprints-api';

interface ExploreBlueprintsProps {
blueprints: GalleryBlueprint[];
isLoading: boolean;
errorMessage?: string;
onBlueprintSelect: ( blueprint: GalleryBlueprint ) => void;
isSelectingBlueprint: boolean;
selectionError?: string;
}

function GalleryBlueprintCard( {
blueprint,
onClick,
disabled,
}: {
blueprint: GalleryBlueprint;
onClick: () => void;
disabled: boolean;
} ) {
return (
<button
onClick={ onClick }
disabled={ disabled }
className={ cx(
'flex flex-col h-full rounded-lg border overflow-hidden text-left transition-colors',
disabled
? 'opacity-50 cursor-not-allowed border-frame-border'
: 'border-frame-border hover:border-frame-text-secondary'
) }
>
<div className="relative w-full h-24 [@media(min-height:680px)]:h-36 bg-frame-surface overflow-hidden">
{ blueprint.screenshotUrl ? (
<img
src={ blueprint.screenshotUrl }
alt={ blueprint.title }
className="w-full h-full object-cover object-center"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-frame-text-tertiary">
{ blueprint.title }
</div>
) }
</div>
<div className="px-3 pt-3 pb-3">
<Heading level={ 3 } className="text-[13px] text-frame-text mb-1" weight={ 500 }>
{ blueprint.title }
</Heading>
<Text
className="text-[12px] text-frame-text-secondary leading-[18px] text-pretty"
weight={ 400 }
title={ blueprint.description }
>
{ blueprint.description }
</Text>
</div>
</button>
);
}

export function ExploreBlueprints( {
blueprints,
isLoading,
errorMessage,
onBlueprintSelect,
isSelectingBlueprint,
selectionError,
}: ExploreBlueprintsProps ) {
const { __ } = useI18n();

return (
<VStack className="w-full max-w-4xl mx-auto" spacing={ 0 }>
<Heading className="text-center text-[32px] text-frame-text mb-2" weight={ 500 }>
{ __( 'Explore blueprints' ) }
</Heading>
<Text className="text-center text-[15px] font-light text-frame-text-secondary block mb-6">
{ __( 'Community blueprints from the WordPress Blueprints library.' ) }
</Text>

{ selectionError && (
<div className="bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 text-sm rounded-lg px-4 py-3 mb-4 max-w-3xl mx-auto w-full">
{ selectionError }
</div>
) }

{ isLoading ? (
<div className="flex items-center justify-center py-8">
<Spinner />
</div>
) : errorMessage ? (
<div className="flex items-center justify-center text-sm text-frame-text-secondary py-8">
{ __( 'Could not load blueprints.' ) }
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full max-w-3xl mx-auto pb-1">
{ blueprints.map( ( bp ) => (
<GalleryBlueprintCard
key={ bp.slug }
blueprint={ bp }
onClick={ () => onBlueprintSelect( bp ) }
disabled={ isSelectingBlueprint }
/>
) ) }
</div>
) }

{ isSelectingBlueprint && (
<div className="flex items-center justify-center py-4">
<Spinner />
</div>
) }
</VStack>
);
}
113 changes: 113 additions & 0 deletions apps/studio/src/modules/add-site/components/new-site-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { __ } from '@wordpress/i18n';
import { useI18n } from '@wordpress/react-i18n';
import { useCallback } from 'react';
import { cx } from 'src/lib/cx';
import { GalleryBlueprint } from 'src/stores/gallery-blueprints-api';

interface Blueprint {
slug: string;
title: string;
Expand All @@ -29,6 +31,13 @@ interface NewSiteOptionsProps {
selectedBlueprint: string | null;
onBlueprintChange: ( blueprintId: string ) => void;
blueprintFileError?: string;
uploadButton?: React.ReactNode;
galleryBlueprints?: GalleryBlueprint[];
isLoadingGallery?: boolean;
galleryErrorMessage?: string;
onGalleryBlueprintSelect?: ( blueprint: GalleryBlueprint ) => void;
isSelectingGalleryBlueprint?: boolean;
gallerySelectionError?: string;
}

function EmptySiteCard( { isSelected, onClick }: { isSelected: boolean; onClick: () => void } ) {
Expand Down Expand Up @@ -177,6 +186,55 @@ function renameBlueprintsForDisplay( blueprints: Blueprint[] ): Blueprint[] {
} ) );
}

function GalleryBlueprintCard( {
blueprint,
onClick,
disabled,
}: {
blueprint: GalleryBlueprint;
onClick: () => void;
disabled: boolean;
} ) {
return (
<button
onClick={ onClick }
disabled={ disabled }
className={ cx(
'flex flex-col h-full rounded-lg border overflow-hidden text-left transition-colors',
disabled
? 'opacity-50 cursor-not-allowed border-frame-border'
: 'border-frame-border hover:border-frame-text-secondary'
) }
>
<div className="relative w-full h-24 [@media(min-height:680px)]:h-36 bg-frame-surface overflow-hidden">
{ blueprint.screenshotUrl ? (
<img
src={ blueprint.screenshotUrl }
alt={ blueprint.title }
className="w-full h-full object-cover object-center"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-frame-text-tertiary">
{ blueprint.title }
</div>
) }
</div>
<div className="px-3 pt-3 pb-3">
<Heading level={ 3 } className="text-[13px] text-frame-text mb-1" weight={ 500 }>
{ blueprint.title }
</Heading>
<Text
className="text-[12px] text-frame-text-secondary leading-[18px] text-pretty"
weight={ 400 }
title={ blueprint.description }
>
{ blueprint.description }
</Text>
</div>
</button>
);
}

export function NewSiteOptions( {
enableBlueprints,
blueprints,
Expand All @@ -185,6 +243,13 @@ export function NewSiteOptions( {
selectedBlueprint,
onBlueprintChange,
blueprintFileError,
uploadButton,
galleryBlueprints = [],
isLoadingGallery = false,
galleryErrorMessage,
onGalleryBlueprintSelect,
isSelectingGalleryBlueprint = false,
gallerySelectionError,
}: NewSiteOptionsProps ) {
const { __ } = useI18n();

Expand All @@ -208,6 +273,10 @@ export function NewSiteOptions( {
{ __( 'Start with an empty site or choose a template.' ) }
</Text>

{ uploadButton && (
<div className="w-full max-w-2xl mx-auto mb-4 flex justify-end">{ uploadButton }</div>
) }

{ blueprintFileError && (
<div className="bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 text-sm rounded-lg px-4 py-3 mb-4">
{ blueprintFileError }
Expand Down Expand Up @@ -236,6 +305,50 @@ export function NewSiteOptions( {
) )
) }
</div>

{ onGalleryBlueprintSelect && (
<>
<Heading
className="text-[18px] text-frame-text mt-8 mb-4 w-full max-w-2xl mx-auto"
weight={ 500 }
>
{ __( 'Explore more blueprints' ) }
</Heading>

{ gallerySelectionError && (
<div className="bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 text-sm rounded-lg px-4 py-3 mb-4 max-w-2xl mx-auto w-full">
{ gallerySelectionError }
</div>
) }

{ isLoadingGallery ? (
<div className="flex items-center justify-center py-8">
<Spinner />
</div>
) : galleryErrorMessage ? (
<div className="flex items-center justify-center text-sm text-frame-text-secondary py-8">
{ galleryErrorMessage }
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-2xl mx-auto pb-1">
{ galleryBlueprints.map( ( bp ) => (
<GalleryBlueprintCard
key={ bp.slug }
blueprint={ bp }
onClick={ () => onGalleryBlueprintSelect( bp ) }
disabled={ isSelectingGalleryBlueprint }
/>
) ) }
</div>
) }

{ isSelectingGalleryBlueprint && (
<div className="flex items-center justify-center py-4">
<Spinner />
</div>
) }
</>
) }
</VStack>
);
}
Loading
Loading