Skip to content

Commit 75e967e

Browse files
authored
feat: add ai-generated image placeholder feature (#845)
1 parent 1d35d47 commit 75e967e

10 files changed

Lines changed: 543 additions & 168 deletions

File tree

.agent/rules/documentation.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
trigger: always_on
3+
---
4+
5+
# Root.js Project Overview
6+
7+
Root.js is a full-featured web development tool with a built-in CMS, designed for building modern, performant websites.
8+
9+
## Key Features
10+
11+
- **TSX Rendering**: Uses TSX for server-side rendering and templating.
12+
- **Web Components**: Supports standard Web Components for building UI elements.
13+
- **File Routes**: Intuitive file-based routing system.
14+
- **i18n**: Built-in support for internationalization.
15+
- **CMS**: Integrated Content Management System for managing content.
16+
17+
## Project Structure
18+
19+
The project is a monorepo managed with `pnpm` and `turbo`.
20+
21+
### Packages (`packages/`)
22+
23+
- **@blinkk/root** (`packages/root`): The core framework package.
24+
- **@blinkk/root-cms** (`packages/root-cms`): The CMS integration for Root.js.
25+
- **@blinkk/create-root** (`packages/create-root`): CLI tool to scaffold new Root.js projects.
26+
27+
### Documentation (`docs/`)
28+
29+
Contains documentation for the project, likely powering `rootjs.dev`.
30+
31+
### Examples (`examples/`)
32+
33+
Contains example projects demonstrating various features of Root.js.
34+
35+
## Development
36+
37+
- **Package Manager**: `pnpm`
38+
- **Build System**: `turbo`
39+
- **Linting**: `eslint`
40+
41+
## Best Practices for Agents
42+
43+
When working on this project, please adhere to the following guidelines:
44+
45+
### Package Management
46+
47+
- **Always use `pnpm`**: This project uses `pnpm` for package management. Do not use `npm` or `yarn`.
48+
- **Install dependencies**: Run `pnpm install` at the root to install dependencies for all packages.
49+
- **Update types**: Use `pnpm types` within Root CMS projects to regenerate types.
50+
51+
### Build & Test
52+
53+
- **Use Turbo**: Use `turbo run build` or `turbo run test` to run tasks across the monorepo efficiently.
54+
- **Dev Server**: Use `pnpm dev` in specific package or app directories for development.
55+
56+
### UI Development
57+
58+
- Follow existing conventions.
59+
- Avoid manually importing custom element definitions (these are collected by Root.js during the build process).
60+
61+
### Version Control & Contributions
62+
63+
- **Changesets**: If your changes require a release (version bump), you must create a changeset. Run `pnpm changeset` and follow the prompts.
64+
- **Commit Messages**: Follow the conventional commit format (e.g., `feat: add new feature`, `fix: resolve issue`).
65+
- **Linting**: Ensure code passes linting rules by running `pnpm lint`.
66+
67+
### Documentation
68+
69+
- **Comment Style**: All comments MUST end in punctuation. Use block comments to describe interfaces and their fields. Avoid adding superfluous comments. Inline comments MUST also end in punctuation.
70+
- **Comment Requirements**: When creating new classes, hooks, or components, write at least a brief block comment describing what it is for. For very complex functionality, provide an example of how to use it in the comment.
71+
72+
## Contribution
73+
74+
Refer to `CONTRIBUTING.md` for guidelines on contributing to the project.

.changeset/shiny-cheetahs-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@blinkk/root-cms': patch
3+
---
4+
5+
feat: add ai-generated image placeholder feature

AGENTS.md

Lines changed: 1 addition & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1 @@
1-
# Root.js Project Overview
2-
3-
Root.js is a full-featured web development tool with a built-in CMS, designed for building modern, performant websites.
4-
5-
## Key Features
6-
7-
- **TSX Rendering**: Uses TSX for server-side rendering and templating.
8-
- **Web Components**: Supports standard Web Components for building UI elements.
9-
- **File Routes**: Intuitive file-based routing system.
10-
- **i18n**: Built-in support for internationalization.
11-
- **CMS**: Integrated Content Management System for managing content.
12-
13-
## Project Structure
14-
15-
The project is a monorepo managed with `pnpm` and `turbo`.
16-
17-
### Packages (`packages/`)
18-
19-
- **@blinkk/root** (`packages/root`): The core framework package.
20-
- **@blinkk/root-cms** (`packages/root-cms`): The CMS integration for Root.js.
21-
- **@blinkk/create-root** (`packages/create-root`): CLI tool to scaffold new Root.js projects.
22-
- **Other Packages**:
23-
- `eslint-config-root`: Shared ESLint configuration.
24-
- `rds`: Likely related to data services or storage (needs verification).
25-
- `root-core`: Core logic/utilities.
26-
- `root-db`: Database abstraction/layer.
27-
- `root-form`: Form handling utilities.
28-
- `root-password-protect`: Middleware/utility for password protection.
29-
- `root-webui`: UI components or interface for the system.
30-
31-
### Apps (`apps/`)
32-
33-
- **gci**: An application built within the monorepo.
34-
35-
### Documentation (`docs/`)
36-
37-
Contains documentation for the project, likely powering `rootjs.dev`.
38-
39-
### Examples (`examples/`)
40-
41-
Contains example projects demonstrating various features of Root.js.
42-
43-
## Development
44-
45-
- **Package Manager**: `pnpm`
46-
- **Build System**: `turbo`
47-
- **Linting**: `eslint`
48-
49-
## Best Practices for Agents
50-
51-
When working on this project, please adhere to the following guidelines:
52-
53-
### Package Management
54-
55-
- **Always use `pnpm`**: This project uses `pnpm` for package management. Do not use `npm` or `yarn`.
56-
- **Install dependencies**: Run `pnpm install` at the root to install dependencies for all packages.
57-
58-
### Build & Test
59-
60-
- **Use Turbo**: Use `turbo run build` or `turbo run test` to run tasks across the monorepo efficiently.
61-
- **Dev Server**: Use `pnpm dev` in specific package or app directories for development.
62-
63-
### Version Control & Contributions
64-
65-
- **Changesets**: If your changes require a release (version bump), you must create a changeset. Run `pnpm changeset` and follow the prompts.
66-
- **Commit Messages**: Follow the conventional commit format (e.g., `feat: add new feature`, `fix: resolve issue`).
67-
- **Linting**: Ensure code passes linting rules by running `pnpm lint`.
68-
69-
### Documentation
70-
71-
- **Update Docs**: If you change functionality, check if `docs/` needs updating. The documentation is a Root.js app itself.
72-
- **Comment Style**: All comments should end in punctuation. Use block comments to describe interfaces and their fields. Avoid adding superfluous comments.
73-
- **Comment Requirements**: When creating new classes, hooks, or components that have medium-to-complex functionality, ensure you write at least a brief block comment describing what it is for. For complex functionality, provide an example of how to use it in the comment.
74-
75-
## Contribution
76-
77-
Refer to `CONTRIBUTING.md` for guidelines on contributing to the project.
1+
See `.agent/rules/documentation.md` for documentation and rules.

packages/root-cms/core/ai.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,68 @@ export async function summarizeDiff(
9494
return res.text?.trim() || '';
9595
}
9696

97+
/** Allowed aspect ratios for image generation (https://docs.cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1beta1/GenerationConfig#ImageConfig). */
98+
export type AspectRatio =
99+
| '1:1'
100+
| '2:3'
101+
| '3:2'
102+
| '3:4'
103+
| '4:3'
104+
| '4:5'
105+
| '5:4'
106+
| '9:16'
107+
| '16:9'
108+
| '21:9';
109+
110+
export interface GenerateImageOptions {
111+
prompt: string;
112+
aspectRatio: AspectRatio;
113+
}
114+
115+
/**
116+
* Generates an image using Vertex AI.
117+
*/
118+
export async function generateImage(
119+
cmsClient: RootCMSClient,
120+
options: GenerateImageOptions
121+
): Promise<string> {
122+
const cmsPluginOptions = cmsClient.cmsPlugin.getConfig();
123+
const firebaseConfig = cmsPluginOptions.firebaseConfig;
124+
const model = 'vertexai/gemini-2.5-flash-image';
125+
126+
const ai = genkit({
127+
plugins: [
128+
vertexAI({
129+
projectId: firebaseConfig.projectId,
130+
location: firebaseConfig.location || 'us-central1',
131+
}),
132+
],
133+
});
134+
135+
const res = await ai.generate({
136+
model: model,
137+
prompt: [
138+
{
139+
text: options.prompt,
140+
},
141+
],
142+
config: {
143+
imageConfig: {
144+
aspectRatio: options.aspectRatio,
145+
},
146+
},
147+
output: {
148+
format: 'media',
149+
},
150+
});
151+
152+
if (!res.media) {
153+
throw new Error('No image generated');
154+
}
155+
156+
return res.media.url;
157+
}
158+
97159
export class Chat {
98160
chatClient: ChatClient;
99161
cmsClient: RootCMSClient;

packages/root-cms/core/api.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,4 +464,42 @@ export function api(server: Server, options: ApiOptions) {
464464
res.status(500).json({success: false, error: 'UNKNOWN'});
465465
}
466466
});
467+
468+
server.use(
469+
'/cms/api/ai.generate_image',
470+
async (req: Request, res: Response) => {
471+
if (
472+
req.method !== 'POST' ||
473+
!String(req.get('content-type')).startsWith('application/json')
474+
) {
475+
res.status(400).json({success: false, error: 'BAD_REQUEST'});
476+
return;
477+
}
478+
if (!req.user?.email) {
479+
res.status(401).json({success: false, error: 'UNAUTHORIZED'});
480+
return;
481+
}
482+
const reqBody = req.body || {};
483+
const prompt = reqBody.prompt;
484+
const aspectRatio = reqBody.aspectRatio;
485+
486+
if (!prompt || !aspectRatio) {
487+
res.status(400).json({success: false, error: 'MISSING_REQUIRED_FIELD'});
488+
return;
489+
}
490+
491+
try {
492+
const cmsClient = new RootCMSClient(req.rootConfig!);
493+
const {generateImage} = await import('./ai.js');
494+
const imageUrl = await generateImage(cmsClient, {
495+
prompt,
496+
aspectRatio,
497+
});
498+
res.status(200).json({success: true, image: imageUrl});
499+
} catch (err: any) {
500+
console.error(err.stack || err);
501+
res.status(500).json({success: false, error: err.message || 'UNKNOWN'});
502+
}
503+
}
504+
);
467505
}

0 commit comments

Comments
 (0)