@wavespeed/mcp 1.2.0 → 1.2.2
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/assets/result.html +77 -21
- package/dist/lib/app.js +3 -2
- package/package.json +1 -1
package/assets/result.html
CHANGED
|
@@ -18,8 +18,13 @@
|
|
|
18
18
|
.prompt { color: var(--muted); margin: 0 0 10px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
19
19
|
.grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
|
|
20
20
|
.grid.one { grid-template-columns: 1fr; }
|
|
21
|
-
.tile { background: var(--card); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
|
|
22
|
-
.tile img, .tile video { display: block; width: 100%; max-height: 520px; object-fit: contain; background:
|
|
21
|
+
.tile { position: relative; background: var(--card); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
|
|
22
|
+
.tile img, .tile video { display: block; width: 100%; max-height: 520px; object-fit: contain; background: var(--card); }
|
|
23
|
+
.tile video { background: #000; }
|
|
24
|
+
.tile-spin { position: absolute; top: 40%; left: 50%; margin: -9px 0 0 -9px; pointer-events: none; }
|
|
25
|
+
.code { margin: 0; padding: 12px; max-height: 360px; overflow: auto; font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; word-break: break-word; }
|
|
26
|
+
.text { padding: 12px; white-space: pre-wrap; word-break: break-word; max-height: 360px; overflow: auto; }
|
|
27
|
+
.broken { padding: 28px 12px; text-align: center; color: var(--muted); font-size: 13px; }
|
|
23
28
|
.tile img { cursor: zoom-in; }
|
|
24
29
|
.tile audio { display: block; width: 100%; padding: 10px; }
|
|
25
30
|
.tile .meta { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 8px 6px 12px; font-size: 12px; color: var(--muted); }
|
|
@@ -111,19 +116,21 @@
|
|
|
111
116
|
resize();
|
|
112
117
|
}
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
// File types by extension, the same lists the WaveSpeed site uses to pick a preview.
|
|
120
|
+
var EXT = {
|
|
121
|
+
image: "jpg jpeg png gif webp avif bmp tiff tif svg ico",
|
|
122
|
+
video: "mp4 mov avi mkv webm flv wmv m4v",
|
|
123
|
+
audio: "mp3 wav m4a ogg aac flac wma aiff au mid midi rmi opus",
|
|
124
|
+
model: "glb gltf",
|
|
125
|
+
subtitle: "srt vtt",
|
|
126
|
+
};
|
|
119
127
|
function kindOf(url) {
|
|
120
128
|
var path = url.split("?")[0].split("#")[0].toLowerCase();
|
|
121
129
|
var ext = path.slice(path.lastIndexOf(".") + 1);
|
|
122
|
-
if (
|
|
123
|
-
if (/^(mp4|webm|mov|m4v)$/.test(ext)) return "video";
|
|
124
|
-
if (/^(mp3|wav|m4a|ogg|flac|aac)$/.test(ext)) return "audio";
|
|
130
|
+
for (var k in EXT) if ((" " + EXT[k] + " ").indexOf(" " + ext + " ") >= 0) return k;
|
|
125
131
|
return "file";
|
|
126
132
|
}
|
|
133
|
+
var KIND_LABEL = { model: "3D model", subtitle: "Subtitles", file: "File" };
|
|
127
134
|
function el(tag, cls, text) {
|
|
128
135
|
var n = document.createElement(tag);
|
|
129
136
|
if (cls) n.className = cls;
|
|
@@ -136,25 +143,74 @@
|
|
|
136
143
|
return b;
|
|
137
144
|
}
|
|
138
145
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
// Like the site: one thing plays at a time.
|
|
147
|
+
function onlyOnePlays(media) {
|
|
148
|
+
media.addEventListener("play", function () {
|
|
149
|
+
document.querySelectorAll("video, audio").forEach(function (m) { if (m !== media) m.pause(); });
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// A spinner stands in until the media has something to draw (the site does the same).
|
|
153
|
+
function withSpinner(t, media, readyEvent) {
|
|
154
|
+
var sp = el("div", "spin tile-spin");
|
|
155
|
+
t.appendChild(sp);
|
|
156
|
+
var done = function () { if (sp.parentNode) sp.parentNode.removeChild(sp); };
|
|
157
|
+
media.addEventListener(readyEvent, done);
|
|
158
|
+
media.addEventListener("error", done);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function brokenNote(t, media, what) {
|
|
162
|
+
// The host may not let the card load this address; say so and keep the Open button working.
|
|
163
|
+
media.addEventListener("error", function () {
|
|
164
|
+
if (media.parentNode !== t) return;
|
|
165
|
+
t.replaceChild(el("div", "broken", "This chat can't show the " + what + " here. Use Open / save."), media); resize();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// One tile per output. Outputs are links to files, plain text, or JSON.
|
|
170
|
+
function tile(o) {
|
|
171
|
+
var t = el("div", "tile");
|
|
172
|
+
if (o !== null && typeof o === "object") {
|
|
173
|
+
var text = JSON.stringify(o, null, 2);
|
|
174
|
+
t.appendChild(el("pre", "code", text));
|
|
175
|
+
var jm = el("div", "meta"); jm.appendChild(el("span", "", "JSON"));
|
|
176
|
+
var cb = el("button", "", "Copy");
|
|
177
|
+
cb.addEventListener("click", function () {
|
|
178
|
+
try { navigator.clipboard.writeText(text).then(function () { cb.textContent = "Copied"; }, function () { cb.textContent = "Copy failed"; }); }
|
|
179
|
+
catch (e) { cb.textContent = "Copy failed"; }
|
|
180
|
+
});
|
|
181
|
+
jm.appendChild(cb); t.appendChild(jm);
|
|
182
|
+
return t;
|
|
183
|
+
}
|
|
184
|
+
var url = typeof o === "string" ? o : String(o);
|
|
185
|
+
if (!/^https?:\/\//i.test(url)) { t.appendChild(el("div", "text", url)); return t; }
|
|
186
|
+
var kind = kindOf(url), media;
|
|
142
187
|
if (kind === "image") {
|
|
143
188
|
media = el("img"); media.src = url; media.alt = "Generated image"; media.loading = "lazy";
|
|
144
189
|
media.addEventListener("click", function () { openLink(url); });
|
|
145
190
|
media.addEventListener("load", resize);
|
|
191
|
+
brokenNote(t, media, "image");
|
|
146
192
|
t.appendChild(media);
|
|
193
|
+
withSpinner(t, media, "load");
|
|
147
194
|
} else if (kind === "video") {
|
|
148
|
-
|
|
195
|
+
// #t=0.001 makes the browser draw the first frame; without it iOS shows a black box until play.
|
|
196
|
+
media = el("video"); media.src = url + (url.indexOf("#") < 0 ? "#t=0.001" : ""); media.controls = true; media.playsInline = true; media.preload = "metadata"; media.loop = true;
|
|
149
197
|
media.addEventListener("loadedmetadata", resize);
|
|
198
|
+
onlyOnePlays(media);
|
|
199
|
+
brokenNote(t, media, "video");
|
|
150
200
|
t.appendChild(media);
|
|
201
|
+
withSpinner(t, media, "loadeddata");
|
|
151
202
|
} else if (kind === "audio") {
|
|
152
203
|
media = el("audio"); media.src = url; media.controls = true; media.preload = "metadata";
|
|
204
|
+
onlyOnePlays(media);
|
|
205
|
+
brokenNote(t, media, "audio");
|
|
153
206
|
t.appendChild(media);
|
|
207
|
+
} else {
|
|
208
|
+
// 3D models, subtitles, documents and other links cannot be drawn here: show the link.
|
|
209
|
+
t.appendChild(el("div", "text", url.split("?")[0].split("/").pop() || url));
|
|
154
210
|
}
|
|
155
|
-
var meta = el("div", "meta")
|
|
156
|
-
meta.appendChild(el("span", "", kind
|
|
157
|
-
meta.appendChild(openButton(url, kind === "file" ? "Open" : "Open / save"));
|
|
211
|
+
var meta = el("div", "meta");
|
|
212
|
+
meta.appendChild(el("span", "", KIND_LABEL[kind] || kind));
|
|
213
|
+
meta.appendChild(openButton(url, kind === "file" || kind === "model" || kind === "subtitle" ? "Open" : "Open / save"));
|
|
158
214
|
t.appendChild(meta);
|
|
159
215
|
return t;
|
|
160
216
|
}
|
|
@@ -184,10 +240,10 @@
|
|
|
184
240
|
body.appendChild(el("div", "err", String(d.error)));
|
|
185
241
|
setState("failed");
|
|
186
242
|
} else {
|
|
187
|
-
var
|
|
188
|
-
if (
|
|
189
|
-
var grid = el("div", "grid" + (
|
|
190
|
-
|
|
243
|
+
var outs = (d.outputs || []).filter(function (o) { return o !== null && o !== undefined && o !== ""; });
|
|
244
|
+
if (outs.length) {
|
|
245
|
+
var grid = el("div", "grid" + (outs.length === 1 ? " one" : ""));
|
|
246
|
+
outs.forEach(function (o) { grid.appendChild(tile(o)); });
|
|
191
247
|
body.appendChild(grid);
|
|
192
248
|
setState(d.status === "completed" || d.done ? "done" : (d.status || ""));
|
|
193
249
|
} else if (d.status && !d.done) {
|
package/dist/lib/app.js
CHANGED
|
@@ -15,8 +15,9 @@ export const UI_TOOLS = new Set([
|
|
|
15
15
|
'edit_video',
|
|
16
16
|
'extend_video',
|
|
17
17
|
]);
|
|
18
|
-
//
|
|
19
|
-
|
|
18
|
+
// "https:" alone should allow every https source, but a host may only honour named hosts when it builds the
|
|
19
|
+
// card's CSP, so the usual media hosts are named too, as wildcards.
|
|
20
|
+
export const VIEW_RESOURCE_DOMAINS = ['https:', 'https://*.cloudfront.net', 'https://*.wavespeed.ai', 'https://*.amazonaws.com'];
|
|
20
21
|
export function loadView() {
|
|
21
22
|
return readFileSync(new URL('../../assets/result.html', import.meta.url), 'utf8');
|
|
22
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wavespeed/mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "WaveSpeed MCP server — run any model on the live WaveSpeed catalog (image, video, audio, 3D) with schema introspection, local-file upload, and price quotes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|