Skip to content

Commit f800cfa

Browse files
authored
Merge pull request #22 from tinypluginlabs/copilot/update-playground-overlay-buttons
Read overlay buttons from blueprints.json with custom icon support
2 parents 32d7f42 + 86217b7 commit f800cfa

8 files changed

Lines changed: 495 additions & 17 deletions

File tree

BLUEPRINT_JSON_STRUCTURE.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Blueprint JSON Structure Definition
2+
3+
## Overview
4+
This document provides the structure definition for `/blueprints/blueprints.json` that controls the "Start a new Playground" overlay buttons.
5+
6+
## JSON Structure
7+
8+
The JSON file is a direct array of button configuration objects:
9+
10+
```json
11+
[
12+
{
13+
"id": "string",
14+
"title": "string",
15+
"path": "string",
16+
"icon": "string",
17+
"disabled": false
18+
}
19+
]
20+
```
21+
22+
## Field Definitions
23+
24+
| Field | Type | Required | Description |
25+
|-------|------|----------|-------------|
26+
| `[].id` | String | Yes | Unique identifier for the button (used as React key) |
27+
| `[].title` | String | Yes | Display text shown under the button icon |
28+
| `[].path` | String | Yes | Navigation path (e.g., "/tinyrelated") |
29+
| `[].icon` | String | No | Icon to display (React component name or SVG URL, default: "WordPressIcon") |
30+
| `[].disabled` | Boolean | No | Whether the button is disabled (default: false) |
31+
32+
## Icon Configuration
33+
34+
The `icon` field supports two formats:
35+
36+
### 1. React Component Name
37+
Specify the name of an icon component exported from `@wp-playground/components`:
38+
- `"WordPressIcon"` - WordPress logo (default)
39+
- `"ClockIcon"` - Clock icon
40+
- `"playgroundLogo"` - Playground logo
41+
- `"temporaryStorage"` - Temporary storage icon
42+
43+
### 2. SVG URL
44+
Provide a direct URL to an SVG file:
45+
- `"https://example.com/custom-icon.svg"`
46+
- `"https://cdn.example.com/icons/my-icon.svg"`
47+
48+
If the `icon` field is omitted or an invalid component name is provided, the button will default to using `WordPressIcon`.
49+
50+
## Example Configuration
51+
52+
```json
53+
[
54+
{
55+
"id": "tinyrelated",
56+
"title": "tinyRelated",
57+
"path": "/tinyrelated",
58+
"icon": "WordPressIcon",
59+
"disabled": false
60+
},
61+
{
62+
"id": "tinyrating",
63+
"title": "tinyRating",
64+
"path": "/tinyrating",
65+
"icon": "https://example.com/rating-icon.svg",
66+
"disabled": false
67+
},
68+
{
69+
"id": "tinyevent",
70+
"title": "tinyEvent",
71+
"path": "/tinyevent",
72+
"disabled": false
73+
}
74+
]
75+
```
76+
77+
## File Location
78+
79+
The JSON file should be deployed to:
80+
```
81+
/blueprints/blueprints.json
82+
```
83+
84+
In the source repository, this maps to:
85+
```
86+
packages/playground/website/public/blueprints/blueprints.json
87+
```
88+
89+
**Note:** This directory is in `.gitignore` and files are deployed by CI, not committed to the repository.
90+
91+
## TypeScript Type Definition
92+
93+
The structure is defined in TypeScript at:
94+
```
95+
packages/playground/website/src/lib/types/blueprints-config.ts
96+
```
97+
98+
```typescript
99+
export interface BlueprintButton {
100+
id: string;
101+
title: string;
102+
path: string;
103+
disabled?: boolean;
104+
}
105+
106+
export interface BlueprintsConfig {
107+
buttons: BlueprintButton[];
108+
}
109+
```
110+
111+
## JSON Schema
112+
113+
A JSON Schema file is available for validation at:
114+
```
115+
packages/playground/website/public/blueprints/blueprints.schema.json
116+
```
117+
118+
## Usage in Code
119+
120+
The configuration is loaded in the `SavedPlaygroundsOverlay` component:
121+
122+
```typescript
123+
import { useFetch } from '../../lib/hooks/use-fetch';
124+
import type { BlueprintsConfig } from '../../lib/types/blueprints-config';
125+
126+
const { data: blueprintsConfig } = useFetch<BlueprintsConfig>(
127+
'/blueprints/blueprints.json'
128+
);
129+
```
130+
131+
## Fallback Behavior
132+
133+
If the JSON file cannot be fetched (404, network error, etc.), the application will fall back to hardcoded default buttons to ensure the overlay always functions correctly.
134+
135+
## Validation
136+
137+
Before deploying, validate your JSON against the schema:
138+
139+
```bash
140+
# Using a JSON schema validator
141+
jsonschema -i blueprints.json blueprints.schema.json
142+
```
143+
144+
## Button Behavior
145+
146+
When a button is clicked:
147+
1. The browser navigates to the specified `path`
148+
2. The path is resolved through the blueprint preset system
149+
3. The corresponding blueprint ZIP file is loaded (e.g., `/blueprints/tinyrelated.zip`)
150+
151+
## Icon Resolution
152+
153+
Icons are resolved in the following order:
154+
1. If `icon` is omitted, use `WordPressIcon` (default)
155+
2. If `icon` starts with `http://` or `https://`, load as external SVG image
156+
3. If `icon` matches a component name in `@wp-playground/components`, use that component
157+
4. If none of the above, fall back to `WordPressIcon`
158+
159+
## Limitations
160+
161+
- The `disabled` field only affects button interaction, not visibility
162+
- Button order in the JSON determines display order in the overlay
163+
- External SVG icons must be accessible from the client (CORS considerations apply)
164+
165+
## Related Documentation
166+
167+
- Implementation details: `IMPLEMENTATION_SUMMARY.md`
168+
- Public directory README: `packages/playground/website/public/blueprints/README.md`
169+
- TypeScript types: `packages/playground/website/src/lib/types/blueprints-config.ts`

IMPLEMENTATION_SUMMARY.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Blueprint Buttons Configuration - Implementation Summary
2+
3+
## Overview
4+
This document describes the implementation of dynamic blueprint buttons in the "Start a new Playground" overlay, which now reads from `/blueprints/blueprints.json` instead of using hardcoded values.
5+
6+
## Changes Made
7+
8+
### 1. TypeScript Type Definitions
9+
**File:** `packages/playground/website/src/lib/types/blueprints-config.ts`
10+
11+
Defines the structure for the `blueprints.json` file:
12+
13+
```typescript
14+
export interface BlueprintButton {
15+
id: string; // Unique identifier
16+
title: string; // Display title
17+
path: string; // Navigation path
18+
icon?: string; // Optional: React component name or SVG URL
19+
disabled?: boolean; // Optional: button disabled state
20+
}
21+
22+
export type BlueprintsConfig = BlueprintButton[];
23+
```
24+
25+
### 2. Component Updates
26+
**File:** `packages/playground/website/src/components/saved-playgrounds-overlay/index.tsx`
27+
28+
Updated the `SavedPlaygroundsOverlay` component to:
29+
- Use the `useFetch` hook to load `/blueprints/blueprints.json`
30+
- Fall back to hardcoded defaults if the JSON file cannot be fetched
31+
- Transform the JSON data into button configurations with onClick handlers
32+
- Resolve icons dynamically from React component names or SVG URLs
33+
34+
Key changes:
35+
```typescript
36+
// Helper function to resolve icon from string (component name or URL)
37+
function resolveIcon(iconSpec?: string): React.ReactNode {
38+
if (!iconSpec) {
39+
return <WordPressIcon />;
40+
}
41+
42+
// Check if it's a URL (SVG from external source)
43+
if (iconSpec.startsWith('http://') || iconSpec.startsWith('https://')) {
44+
return <img src={iconSpec} alt="" style={{ width: '100%', height: '100%' }} />;
45+
}
46+
47+
// Try to resolve as a React component from @wp-playground/components
48+
const IconComponent = (PlaygroundIcons as any)[iconSpec];
49+
if (IconComponent && typeof IconComponent === 'function') {
50+
return <IconComponent />;
51+
}
52+
53+
// Fallback to WordPressIcon if component not found
54+
return <WordPressIcon />;
55+
}
56+
57+
// Fetch from JSON (direct array)
58+
const { data: blueprintsConfig } = useFetch<BlueprintsConfig>(
59+
'/blueprints/blueprints.json'
60+
);
61+
62+
// Fallback to defaults
63+
const defaultCreationOptions: BlueprintButton[] = [...];
64+
const buttonsConfig = blueprintsConfig || defaultCreationOptions;
65+
66+
// Transform to button props with dynamic icon resolution
67+
const creationOptions = buttonsConfig.map((button) => ({
68+
id: button.id,
69+
title: button.title,
70+
iconComponent: resolveIcon(button.icon),
71+
onClick: () => { window.location.href = button.path; },
72+
disabled: button.disabled ?? false,
73+
}));
74+
```
75+
76+
### 3. Documentation
77+
**File:** `packages/playground/website/public/blueprints/README.md`
78+
79+
Comprehensive documentation explaining:
80+
- The JSON structure and schema
81+
- Each property's purpose and type
82+
- Example configuration
83+
- Deployment process
84+
- Fallback behavior
85+
86+
## JSON File Structure
87+
88+
The CI process should deploy a `blueprints.json` file to `/blueprints/blueprints.json` as a direct array:
89+
90+
```json
91+
[
92+
{
93+
"id": "tinyrelated",
94+
"title": "tinyRelated",
95+
"path": "/tinyrelated",
96+
"disabled": false
97+
},
98+
{
99+
"id": "tinyrating",
100+
"title": "tinyRating",
101+
"path": "/tinyrating",
102+
"disabled": false
103+
},
104+
{
105+
"id": "tinyevent",
106+
"title": "tinyEvent",
107+
"path": "/tinyevent",
108+
"disabled": false
109+
}
110+
]
111+
```
112+
113+
## Behavior
114+
115+
### Normal Operation
116+
1. When the overlay loads, it fetches `/blueprints/blueprints.json`
117+
2. If successful, buttons are created from the JSON data
118+
3. Icons are resolved dynamically:
119+
- If `icon` is omitted, uses `WordPressIcon` (default)
120+
- If `icon` starts with `http://` or `https://`, loads as external SVG image
121+
- If `icon` matches a component name in `@wp-playground/components`, uses that component
122+
- If none of the above, falls back to `WordPressIcon`
123+
4. Each button navigates to the specified `path` when clicked
124+
125+
### Fallback Mechanism
126+
If the JSON file cannot be fetched (404, network error, etc.):
127+
- The component uses hardcoded default buttons (same as the original implementation)
128+
- This ensures the overlay always displays buttons, even if the JSON file is missing
129+
130+
### Loading State
131+
- The component doesn't show a loading spinner
132+
- Buttons appear immediately using defaults or fetched data
133+
- This provides a seamless user experience
134+
135+
### Icon Support
136+
Buttons can display custom icons in two ways:
137+
1. **React Component Names**: Use exported icon components from `@wp-playground/components` (e.g., "WordPressIcon", "ClockIcon", "playgroundLogo")
138+
2. **SVG URLs**: Provide a direct URL to an SVG file (e.g., "https://example.com/icon.svg")
139+
140+
## Testing
141+
142+
### Type Checking
143+
```bash
144+
npx nx typecheck playground-website
145+
```
146+
✅ Passed
147+
148+
### Linting
149+
```bash
150+
npx nx lint playground-website
151+
```
152+
✅ Passed
153+
154+
### Dev Server
155+
```bash
156+
npm run dev
157+
```
158+
✅ Started successfully at http://127.0.0.1:5400/
159+
160+
## Deployment Notes
161+
162+
1. **JSON File Location**: The `blueprints.json` file should be deployed to the public directory at `/blueprints/blueprints.json`
163+
164+
2. **Git Ignore**: The `/blueprints/` directory is already in `.gitignore`, so the JSON file won't be committed to the repository
165+
166+
3. **CI Process**: The CI process should deploy the JSON file separately from the application build
167+
168+
4. **Validation**: Consider adding JSON schema validation in your CI process to ensure the file structure is correct before deployment
169+
170+
5. **Icon Support**: Icons can be specified as React component names (from `@wp-playground/components`) or as URLs to SVG files. External SVG icons must be accessible from the client (CORS considerations apply).
171+
172+
## Icon Customization
173+
174+
Icons are now fully customizable via the `icon` field in the JSON configuration:
175+
176+
### Available React Component Icons
177+
From `@wp-playground/components`:
178+
- `WordPressIcon` - WordPress logo (default)
179+
- `ClockIcon` - Clock icon
180+
- `playgroundLogo` - Playground logo
181+
- `temporaryStorage` - Temporary storage icon
182+
183+
### Using External SVG Icons
184+
Provide a direct URL to an SVG file:
185+
```json
186+
{
187+
"icon": "https://example.com/custom-icon.svg"
188+
}
189+
```
190+
191+
Note: External SVG icons must be accessible from the client browser. Ensure proper CORS headers are set if the SVG is hosted on a different domain.
192+
193+
## Security Considerations
194+
195+
The implementation:
196+
- Uses standard fetch API (no eval or dynamic code execution)
197+
- Only reads from a known, controlled endpoint (`/blueprints/blueprints.json`)
198+
- Uses TypeScript for type safety
199+
- Falls back to safe defaults if fetch fails
200+
201+
The existing .htaccess rules protecting the `/blueprints/` directory continue to apply.

dist/packages/php-wasm/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"minimisted": "2.0.1",
5858
"octokit": "3.1.2",
5959
"pako": "1.0.10",
60-
"pify": "2.3.0",
60+
"pify": "4.0.1",
6161
"readable-stream": "3.6.2",
6262
"sha.js": "2.4.12",
6363
"simple-get": "4.0.1",

dist/packages/playground/blueprints/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"minimisted": "2.0.1",
4747
"octokit": "3.1.2",
4848
"pako": "1.0.10",
49-
"pify": "2.3.0",
49+
"pify": "4.0.1",
5050
"readable-stream": "3.6.2",
5151
"sha.js": "2.4.12",
5252
"simple-get": "4.0.1",

dist/packages/playground/mcp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"minimisted": "2.0.1",
5454
"octokit": "3.1.2",
5555
"pako": "1.0.10",
56-
"pify": "2.3.0",
56+
"pify": "4.0.1",
5757
"readable-stream": "3.6.2",
5858
"sha.js": "2.4.12",
5959
"simple-get": "4.0.1",

0 commit comments

Comments
 (0)