@kvasar/google-stitch 0.1.16 → 0.1.18
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
|
@@ -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
|
};
|
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import { StitchMCPClient } from "../services/stitch-mcp-client.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
2
5
|
|
|
3
6
|
interface ListScreensParams {
|
|
4
7
|
projectId: string;
|
|
5
8
|
}
|
|
6
9
|
|
|
10
|
+
type StitchFile = {
|
|
11
|
+
name?: string;
|
|
12
|
+
downloadUrl?: string;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
fileContentBase64?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type Screen = {
|
|
18
|
+
name?: string;
|
|
19
|
+
id?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
prompt?: string;
|
|
22
|
+
screenshot?: StitchFile;
|
|
23
|
+
htmlCode?: StitchFile;
|
|
24
|
+
figmaExport?: StitchFile;
|
|
25
|
+
deviceType?: string;
|
|
26
|
+
width?: string;
|
|
27
|
+
height?: string;
|
|
28
|
+
groupId?: string;
|
|
29
|
+
groupName?: string;
|
|
30
|
+
generatedBy?: string;
|
|
31
|
+
isCreatedByClient?: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
7
34
|
export const listScreensTool = (client: StitchMCPClient) => ({
|
|
8
35
|
name: "list_screens",
|
|
9
36
|
description: "Lists all screens within a given Stitch project",
|
|
@@ -13,8 +40,95 @@ export const listScreensTool = (client: StitchMCPClient) => ({
|
|
|
13
40
|
throw new Error("projectId is required");
|
|
14
41
|
}
|
|
15
42
|
|
|
16
|
-
|
|
17
|
-
projectId: params.projectId
|
|
18
|
-
});
|
|
19
|
-
|
|
43
|
+
const screens = (await client.request("list_screens", {
|
|
44
|
+
projectId: params.projectId
|
|
45
|
+
})) as unknown as Screen[];
|
|
46
|
+
|
|
47
|
+
const content: Array<{ type: string; [key: string]: any }> = [];
|
|
48
|
+
|
|
49
|
+
if (!screens?.length) {
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: "No screens found in this project."
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const screen of screens) {
|
|
61
|
+
content.push({
|
|
62
|
+
type: "text",
|
|
63
|
+
text: `## ${screen.title || "Untitled screen"}
|
|
64
|
+
Prompt: ${screen.prompt || "-"}
|
|
65
|
+
Device: ${screen.deviceType || "-"}
|
|
66
|
+
Size: ${screen.width || "?"} × ${screen.height || "?"}`
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Prefer embedded image
|
|
70
|
+
if (screen.screenshot?.fileContentBase64) {
|
|
71
|
+
const tempFilePath = path.join(
|
|
72
|
+
os.tmpdir(),
|
|
73
|
+
`stitch-screen-${Date.now()}-${Math.random()
|
|
74
|
+
.toString(36)
|
|
75
|
+
.slice(2)}.png`
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
fs.writeFileSync(
|
|
79
|
+
tempFilePath,
|
|
80
|
+
Buffer.from(
|
|
81
|
+
screen.screenshot.fileContentBase64,
|
|
82
|
+
"base64"
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
content.push({
|
|
87
|
+
type: "image",
|
|
88
|
+
path: tempFilePath,
|
|
89
|
+
caption: screen.title || "Generated screen"
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fallback URL
|
|
96
|
+
if (screen.screenshot?.downloadUrl) {
|
|
97
|
+
content.push({
|
|
98
|
+
type: "image",
|
|
99
|
+
url: screen.screenshot.downloadUrl,
|
|
100
|
+
caption: screen.title || "Generated screen"
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// HTML fallback
|
|
107
|
+
if (screen.htmlCode?.fileContentBase64) {
|
|
108
|
+
const html = Buffer.from(
|
|
109
|
+
screen.htmlCode.fileContentBase64,
|
|
110
|
+
"base64"
|
|
111
|
+
).toString("utf-8");
|
|
112
|
+
|
|
113
|
+
content.push({
|
|
114
|
+
type: "html",
|
|
115
|
+
html
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (screen.htmlCode?.downloadUrl) {
|
|
122
|
+
content.push({
|
|
123
|
+
type: "html",
|
|
124
|
+
html: `<iframe
|
|
125
|
+
src="${screen.htmlCode.downloadUrl}"
|
|
126
|
+
style="width:100%;height:600px;border:none;"
|
|
127
|
+
></iframe>`
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { content };
|
|
133
|
+
}
|
|
20
134
|
});
|