@wavespeed/mcp 1.2.1 → 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.
Files changed (2) hide show
  1. package/assets/result.html +74 -25
  2. package/package.json +1 -1
@@ -18,9 +18,12 @@
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; }
21
+ .tile { position: relative; background: var(--card); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
22
22
  .tile img, .tile video { display: block; width: 100%; max-height: 520px; object-fit: contain; background: var(--card); }
23
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; }
24
27
  .broken { padding: 28px 12px; text-align: center; color: var(--muted); font-size: 13px; }
25
28
  .tile img { cursor: zoom-in; }
26
29
  .tile audio { display: block; width: 100%; padding: 10px; }
@@ -113,19 +116,21 @@
113
116
  resize();
114
117
  }
115
118
 
116
- function urlOf(o) {
117
- if (typeof o === "string") return o;
118
- if (o && typeof o === "object") return o.url || o.download_url || o.href || "";
119
- return "";
120
- }
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
+ };
121
127
  function kindOf(url) {
122
128
  var path = url.split("?")[0].split("#")[0].toLowerCase();
123
129
  var ext = path.slice(path.lastIndexOf(".") + 1);
124
- if (/^(png|jpe?g|webp|gif|avif|bmp)$/.test(ext)) return "image";
125
- if (/^(mp4|webm|mov|m4v)$/.test(ext)) return "video";
126
- 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;
127
131
  return "file";
128
132
  }
133
+ var KIND_LABEL = { model: "3D model", subtitle: "Subtitles", file: "File" };
129
134
  function el(tag, cls, text) {
130
135
  var n = document.createElement(tag);
131
136
  if (cls) n.className = cls;
@@ -138,30 +143,74 @@
138
143
  return b;
139
144
  }
140
145
 
141
- function tile(url) {
142
- var kind = kindOf(url), t = el("div", "tile"), media;
143
- if (!/^https?:\/\//i.test(url)) return null;
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;
144
187
  if (kind === "image") {
145
188
  media = el("img"); media.src = url; media.alt = "Generated image"; media.loading = "lazy";
146
189
  media.addEventListener("click", function () { openLink(url); });
147
190
  media.addEventListener("load", resize);
148
- // The host may not let the card load this address; say so and keep the Open button working.
149
- media.addEventListener("error", function () {
150
- var m = el("div", "broken", "This chat can't show the image here. Use Open / save.");
151
- t.replaceChild(m, media); resize();
152
- });
191
+ brokenNote(t, media, "image");
153
192
  t.appendChild(media);
193
+ withSpinner(t, media, "load");
154
194
  } else if (kind === "video") {
155
- media = el("video"); media.src = url; media.controls = true; media.playsInline = true; media.preload = "metadata";
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;
156
197
  media.addEventListener("loadedmetadata", resize);
198
+ onlyOnePlays(media);
199
+ brokenNote(t, media, "video");
157
200
  t.appendChild(media);
201
+ withSpinner(t, media, "loadeddata");
158
202
  } else if (kind === "audio") {
159
203
  media = el("audio"); media.src = url; media.controls = true; media.preload = "metadata";
204
+ onlyOnePlays(media);
205
+ brokenNote(t, media, "audio");
160
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));
161
210
  }
162
- var meta = el("div", "meta"), name = url.split("?")[0].split("/").pop() || "file";
163
- meta.appendChild(el("span", "", kind === "file" ? name : kind));
164
- 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"));
165
214
  t.appendChild(meta);
166
215
  return t;
167
216
  }
@@ -191,10 +240,10 @@
191
240
  body.appendChild(el("div", "err", String(d.error)));
192
241
  setState("failed");
193
242
  } else {
194
- var urls = (d.outputs || []).map(urlOf).filter(Boolean);
195
- if (urls.length) {
196
- var grid = el("div", "grid" + (urls.length === 1 ? " one" : ""));
197
- urls.forEach(function (u) { var t = tile(u); if (t) grid.appendChild(t); });
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)); });
198
247
  body.appendChild(grid);
199
248
  setState(d.status === "completed" || d.done ? "done" : (d.status || ""));
200
249
  } else if (d.status && !d.done) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavespeed/mcp",
3
- "version": "1.2.1",
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": {