@kvasar/google-stitch 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -85,7 +85,7 @@ Example prompts:
|
|
|
85
85
|
|
|
86
86
|
### Screen generation
|
|
87
87
|
|
|
88
|
-
For new screens from prompts:
|
|
88
|
+
For creating new screens from prompts:
|
|
89
89
|
|
|
90
90
|
- `generate_screen_from_text`
|
|
91
91
|
|
|
@@ -96,12 +96,19 @@ Example prompts:
|
|
|
96
96
|
- build landing page
|
|
97
97
|
- design mobile onboarding
|
|
98
98
|
|
|
99
|
+
Use this tool when the user wants to create a new UI screen from a text description.
|
|
100
|
+
|
|
99
101
|
Always extract:
|
|
100
102
|
|
|
101
103
|
- projectId
|
|
102
104
|
- prompt
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
|
|
106
|
+
Important:
|
|
107
|
+
- Screen generation usually takes a few minutes to complete
|
|
108
|
+
- Do **not retry automatically** if a connection error or timeout occurs
|
|
109
|
+
- A connection error does **not necessarily mean the generation failed**
|
|
110
|
+
- After waiting a few minutes, use `get_screen` or `list_screens` to verify whether the screen was successfully created
|
|
111
|
+
- Avoid duplicate retries to prevent generating the same screen multiple times
|
|
105
112
|
|
|
106
113
|
## Supported device types:
|
|
107
114
|
|
|
@@ -7,37 +7,31 @@ import fs from "node:fs";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import os from "node:os";
|
|
9
9
|
|
|
10
|
+
type StitchFile = {
|
|
11
|
+
name?: string;
|
|
12
|
+
downloadUrl?: string;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
fileContentBase64?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type SessionOutputComponent = {
|
|
18
|
+
text?: string;
|
|
19
|
+
suggestion?: string;
|
|
20
|
+
|
|
21
|
+
};
|
|
22
|
+
|
|
10
23
|
type StitchResponse = {
|
|
11
24
|
screen?: {
|
|
12
25
|
name?: string;
|
|
13
26
|
title?: string;
|
|
14
27
|
prompt?: string;
|
|
15
|
-
screenshot?:
|
|
16
|
-
|
|
17
|
-
bytes?: string;
|
|
18
|
-
};
|
|
19
|
-
htmlCode?: {
|
|
20
|
-
content?: string;
|
|
21
|
-
url?: string;
|
|
22
|
-
};
|
|
28
|
+
screenshot?: StitchFile;
|
|
29
|
+
htmlCode?: StitchFile;
|
|
23
30
|
deviceType?: DeviceType;
|
|
24
31
|
width?: string;
|
|
25
32
|
height?: string;
|
|
26
33
|
};
|
|
27
|
-
output_components?:
|
|
28
|
-
text?: string;
|
|
29
|
-
suggestion?: string;
|
|
30
|
-
design?: {
|
|
31
|
-
screenshot?: {
|
|
32
|
-
url?: string;
|
|
33
|
-
bytes?: string;
|
|
34
|
-
};
|
|
35
|
-
htmlCode?: {
|
|
36
|
-
content?: string;
|
|
37
|
-
url?: string;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
}>;
|
|
34
|
+
output_components?: SessionOutputComponent[];
|
|
41
35
|
};
|
|
42
36
|
|
|
43
37
|
export function generateScreenFromTextTool(client: StitchMCPClient) {
|
|
@@ -91,39 +85,28 @@ export function generateScreenFromTextTool(client: StitchMCPClient) {
|
|
|
91
85
|
const result = (await client.generateScreen(params)) as StitchResponse;
|
|
92
86
|
|
|
93
87
|
const screen = result.screen;
|
|
94
|
-
const firstComponent = result.output_components?.[0];
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
const modelText = result.output_components
|
|
90
|
+
?.map(component => component.text)
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.join("\n");
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
94
|
+
const suggestionText = result.output_components
|
|
95
|
+
?.map(component => component.suggestion)
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join("\n");
|
|
104
98
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
content.push({ type: "text", text: modelText });
|
|
109
|
-
}
|
|
99
|
+
const content: Array<{ type: string; [key: string]: any }> = [];
|
|
100
|
+
|
|
101
|
+
if (modelText) {
|
|
110
102
|
content.push({
|
|
111
|
-
type: "
|
|
112
|
-
|
|
113
|
-
caption: screen?.title || "Generated by Google Stitch"
|
|
103
|
+
type: "text",
|
|
104
|
+
text: modelText
|
|
114
105
|
});
|
|
115
|
-
if (suggestion) {
|
|
116
|
-
content.push({ type: "text", text: suggestion });
|
|
117
|
-
}
|
|
118
|
-
return { content };
|
|
119
106
|
}
|
|
120
107
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
screen?.screenshot?.bytes ||
|
|
124
|
-
firstComponent?.design?.screenshot?.bytes;
|
|
125
|
-
|
|
126
|
-
if (imageBytes) {
|
|
108
|
+
// Prefer embedded image first
|
|
109
|
+
if (screen?.screenshot?.fileContentBase64) {
|
|
127
110
|
const tempFilePath = path.join(
|
|
128
111
|
os.tmpdir(),
|
|
129
112
|
`stitch-screen-${Date.now()}.png`
|
|
@@ -131,43 +114,83 @@ export function generateScreenFromTextTool(client: StitchMCPClient) {
|
|
|
131
114
|
|
|
132
115
|
fs.writeFileSync(
|
|
133
116
|
tempFilePath,
|
|
134
|
-
Buffer.from(
|
|
117
|
+
Buffer.from(
|
|
118
|
+
screen.screenshot.fileContentBase64,
|
|
119
|
+
"base64"
|
|
120
|
+
)
|
|
135
121
|
);
|
|
136
122
|
|
|
137
|
-
const content: Array<{ type: string; [key: string]: any }> = [];
|
|
138
|
-
if (modelText) {
|
|
139
|
-
content.push({ type: "text", text: modelText });
|
|
140
|
-
}
|
|
141
123
|
content.push({
|
|
142
124
|
type: "image",
|
|
143
125
|
path: tempFilePath,
|
|
144
|
-
caption: screen
|
|
126
|
+
caption: screen.title || "Generated by Google Stitch"
|
|
145
127
|
});
|
|
146
|
-
|
|
147
|
-
|
|
128
|
+
|
|
129
|
+
if (suggestionText) {
|
|
130
|
+
content.push({
|
|
131
|
+
type: "text",
|
|
132
|
+
text: suggestionText
|
|
133
|
+
});
|
|
148
134
|
}
|
|
135
|
+
|
|
149
136
|
return { content };
|
|
150
137
|
}
|
|
151
138
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
139
|
+
// Fallback to remote image URL
|
|
140
|
+
if (screen?.screenshot?.downloadUrl) {
|
|
141
|
+
content.push({
|
|
142
|
+
type: "image",
|
|
143
|
+
url: screen.screenshot.downloadUrl,
|
|
144
|
+
caption: screen.title || "Generated by Google Stitch"
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (suggestionText) {
|
|
148
|
+
content.push({
|
|
149
|
+
type: "text",
|
|
150
|
+
text: suggestionText
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { content };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// HTML fallback
|
|
158
|
+
let html: string | undefined;
|
|
159
|
+
|
|
160
|
+
if (screen?.htmlCode?.fileContentBase64) {
|
|
161
|
+
html = Buffer.from(
|
|
162
|
+
screen.htmlCode.fileContentBase64,
|
|
163
|
+
"base64"
|
|
164
|
+
).toString("utf-8");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!html && screen?.htmlCode?.downloadUrl) {
|
|
168
|
+
html = `<iframe
|
|
169
|
+
src="${screen.htmlCode.downloadUrl}"
|
|
170
|
+
style="width:100%;height:600px;border:none;"
|
|
171
|
+
></iframe>`;
|
|
172
|
+
}
|
|
156
173
|
|
|
157
174
|
if (!html) {
|
|
158
175
|
html = `<div style="padding:16px">
|
|
159
|
-
<h3>${escapeHtml(
|
|
176
|
+
<h3>${escapeHtml(
|
|
177
|
+
screen?.title || "Generated screen"
|
|
178
|
+
)}</h3>
|
|
160
179
|
</div>`;
|
|
161
180
|
}
|
|
162
181
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (
|
|
169
|
-
content.push({
|
|
182
|
+
content.push({
|
|
183
|
+
type: "html",
|
|
184
|
+
html
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (suggestionText) {
|
|
188
|
+
content.push({
|
|
189
|
+
type: "text",
|
|
190
|
+
text: suggestionText
|
|
191
|
+
});
|
|
170
192
|
}
|
|
193
|
+
|
|
171
194
|
return { content };
|
|
172
195
|
}
|
|
173
196
|
};
|