@cloudinary/asset-management-mcp 0.9.1 → 0.9.3
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/README.md +12 -15
- package/bin/mcp-server.js +288 -369
- package/bin/mcp-server.js.map +13 -13
- package/esm/landing-page.d.ts.map +1 -1
- package/esm/landing-page.js +9 -3
- package/esm/landing-page.js.map +1 -1
- package/esm/lib/config.d.ts +3 -3
- package/esm/lib/config.js +3 -3
- package/esm/mcp-server/apps/app-shared.d.ts +6 -5
- package/esm/mcp-server/apps/app-shared.d.ts.map +1 -1
- package/esm/mcp-server/apps/app-shared.js +134 -14
- package/esm/mcp-server/apps/app-shared.js.map +1 -1
- package/esm/mcp-server/apps/asset-details-app.d.ts.map +1 -1
- package/esm/mcp-server/apps/asset-details-app.js +7 -14
- package/esm/mcp-server/apps/asset-details-app.js.map +1 -1
- package/esm/mcp-server/apps/asset-gallery-app.d.ts.map +1 -1
- package/esm/mcp-server/apps/asset-gallery-app.js +99 -306
- package/esm/mcp-server/apps/asset-gallery-app.js.map +1 -1
- package/esm/mcp-server/apps/asset-upload-app.d.ts.map +1 -1
- package/esm/mcp-server/apps/asset-upload-app.js +29 -24
- package/esm/mcp-server/apps/asset-upload-app.js.map +1 -1
- package/esm/mcp-server/apps/config.d.ts.map +1 -1
- package/esm/mcp-server/apps/config.js +1 -2
- package/esm/mcp-server/apps/config.js.map +1 -1
- package/esm/mcp-server/apps/extensions.d.ts.map +1 -1
- package/esm/mcp-server/apps/extensions.js +6 -1
- package/esm/mcp-server/apps/extensions.js.map +1 -1
- package/esm/mcp-server/cli/serve/impl.js +1 -1
- package/esm/mcp-server/cli/serve/impl.js.map +1 -1
- package/esm/mcp-server/mcp-server.js +1 -1
- package/esm/mcp-server/server.js +1 -1
- package/esm/types/bigint.d.ts.map +1 -1
- package/esm/types/bigint.js +4 -3
- package/esm/types/bigint.js.map +1 -1
- package/package.json +1 -1
- package/src/landing-page.ts +9 -3
- package/src/lib/config.ts +3 -3
- package/src/mcp-server/apps/app-shared.ts +135 -14
- package/src/mcp-server/apps/asset-details-app.ts +7 -13
- package/src/mcp-server/apps/asset-gallery-app.ts +99 -305
- package/src/mcp-server/apps/asset-upload-app.ts +29 -23
- package/src/mcp-server/apps/config.ts +1 -2
- package/src/mcp-server/apps/extensions.ts +6 -1
- package/src/mcp-server/cli/serve/impl.ts +1 -1
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.ts +1 -1
- package/src/types/bigint.ts +18 -17
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
SHARED_CSS_TOKENS,
|
|
12
12
|
SHARED_CSS_COMPONENTS,
|
|
13
|
+
SHARED_JS_ICONS,
|
|
13
14
|
SHARED_JS_MCP_CLIENT,
|
|
14
15
|
SHARED_JS_HELPERS,
|
|
15
16
|
SHARED_JS_TOOLTIPS,
|
|
@@ -37,90 +38,10 @@ const GALLERY_CSS = /* css */ `
|
|
|
37
38
|
font-size: var(--cld-font-xxs); color: var(--cld-text3); background: var(--cld-bg3);
|
|
38
39
|
padding: 2px 8px; border-radius: 20px; font-weight: 500;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
transition: color 0.15s, border-color 0.15s;
|
|
45
|
-
}
|
|
46
|
-
.select-all-btn:hover { color: var(--cld-accent); border-color: var(--cld-accent); }
|
|
47
|
-
.refresh-btn {
|
|
48
|
-
background: none; border: 1px solid var(--cld-border); border-radius: var(--cld-radius-sm);
|
|
49
|
-
color: var(--cld-text2); cursor: pointer; font-size: 14px; padding: 2px 7px;
|
|
50
|
-
line-height: 1; transition: background 0.15s, color 0.15s;
|
|
51
|
-
}
|
|
52
|
-
.refresh-btn:hover { background: var(--cld-bg3); color: var(--cld-text); }
|
|
53
|
-
|
|
54
|
-
/* ── Filter bar ── */
|
|
55
|
-
.filter-row {
|
|
56
|
-
margin-bottom: var(--cld-sp-md); display: flex; gap: 8px; align-items: center;
|
|
57
|
-
}
|
|
58
|
-
.filter-text-wrap { position: relative; flex: 1; }
|
|
59
|
-
.filter-input {
|
|
60
|
-
width: 100%; height: 36px; padding: 0 12px 0 34px;
|
|
61
|
-
border: 1px solid var(--cld-border); border-radius: var(--cld-radius);
|
|
62
|
-
background: var(--cld-bg); font-size: 12.5px; color: var(--cld-text);
|
|
63
|
-
outline: none; font-family: inherit;
|
|
64
|
-
transition: border-color 0.18s, box-shadow 0.18s;
|
|
65
|
-
}
|
|
66
|
-
.filter-input::placeholder { color: var(--cld-text3); }
|
|
67
|
-
.filter-input:focus {
|
|
68
|
-
border-color: var(--cld-accent);
|
|
69
|
-
box-shadow: 0 0 0 3px rgba(52,72,197,0.1);
|
|
70
|
-
}
|
|
71
|
-
[data-theme="dark"] .filter-input:focus { box-shadow: 0 0 0 3px rgba(13,154,255,0.15); }
|
|
72
|
-
.filter-icon {
|
|
73
|
-
position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
|
|
74
|
-
color: var(--cld-text3); pointer-events: none; display: flex; align-items: center;
|
|
75
|
-
}
|
|
76
|
-
.filter-clear {
|
|
77
|
-
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
|
78
|
-
background: none; border: none; color: var(--cld-text3); cursor: pointer;
|
|
79
|
-
font-size: 14px; line-height: 1; padding: 2px 4px; border-radius: 4px;
|
|
80
|
-
display: none; font-family: inherit;
|
|
81
|
-
}
|
|
82
|
-
.filter-clear:hover { color: var(--cld-text); background: var(--cld-border); }
|
|
83
|
-
.filter-clear.visible { display: block; }
|
|
84
|
-
|
|
85
|
-
/* Aspect-ratio dropdown */
|
|
86
|
-
.aspect-dropdown { position: relative; flex-shrink: 0; user-select: none; }
|
|
87
|
-
.aspect-btn {
|
|
88
|
-
height: 36px; padding: 0 10px; border: 1px solid var(--cld-border);
|
|
89
|
-
border-radius: var(--cld-radius); background: var(--cld-bg);
|
|
90
|
-
font-size: 12.5px; color: var(--cld-text); cursor: pointer;
|
|
91
|
-
display: flex; align-items: center; gap: 6px; white-space: nowrap;
|
|
92
|
-
transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
|
|
93
|
-
font-family: inherit; outline: none;
|
|
94
|
-
}
|
|
95
|
-
.aspect-btn:hover { border-color: var(--cld-border2); }
|
|
96
|
-
.aspect-btn.active {
|
|
97
|
-
border-color: var(--cld-accent); background: var(--cld-accent-bg);
|
|
98
|
-
color: var(--cld-accent); font-weight: 600;
|
|
99
|
-
}
|
|
100
|
-
.aspect-btn-chevron { color: var(--cld-text3); flex-shrink: 0; transition: transform 0.18s; }
|
|
101
|
-
.aspect-btn.open .aspect-btn-chevron { transform: rotate(180deg); }
|
|
102
|
-
.aspect-menu {
|
|
103
|
-
position: absolute; top: calc(100% + 6px); right: 0;
|
|
104
|
-
background: var(--cld-bg); border: 1px solid var(--cld-border);
|
|
105
|
-
border-radius: 10px; box-shadow: var(--cld-shadow-md);
|
|
106
|
-
padding: 4px; min-width: 160px; z-index: 50; display: none;
|
|
107
|
-
}
|
|
108
|
-
.aspect-menu.open { display: block; }
|
|
109
|
-
.aspect-option {
|
|
110
|
-
display: flex; align-items: center; gap: 10px;
|
|
111
|
-
padding: 7px 10px; border-radius: 6px; font-size: 12.5px;
|
|
112
|
-
color: var(--cld-text); cursor: pointer; transition: background 0.18s;
|
|
113
|
-
}
|
|
114
|
-
.aspect-option:hover { background: var(--cld-bg3); }
|
|
115
|
-
.aspect-option.selected { color: var(--cld-accent); font-weight: 600; }
|
|
116
|
-
.aspect-opt-icon { color: var(--cld-text3); display: flex; align-items: center; flex-shrink: 0; }
|
|
117
|
-
.aspect-option.selected .aspect-opt-icon { color: var(--cld-accent); }
|
|
118
|
-
.aspect-check { margin-left: auto; color: var(--cld-accent); opacity: 0; }
|
|
119
|
-
.aspect-option.selected .aspect-check { opacity: 1; }
|
|
120
|
-
.no-results {
|
|
121
|
-
grid-column: 1 / -1; padding: 60px 20px;
|
|
122
|
-
text-align: center; color: var(--cld-text3); font-size: 13px;
|
|
123
|
-
}
|
|
41
|
+
/* action-btn svg sizing */
|
|
42
|
+
.action-btn svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
|
|
43
|
+
/* select bar svg */
|
|
44
|
+
.bar-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
|
|
124
45
|
|
|
125
46
|
/* ── Grid ── */
|
|
126
47
|
.grid {
|
|
@@ -179,18 +100,11 @@ const GALLERY_CSS = /* css */ `
|
|
|
179
100
|
opacity: 0; transition: opacity 0.18s; z-index: 4; pointer-events: none;
|
|
180
101
|
}
|
|
181
102
|
.card:hover .tags-overlay { opacity: 1; }
|
|
182
|
-
.grid.filtering .tags-overlay { opacity: 1; }
|
|
183
103
|
.tag-overlay {
|
|
184
104
|
font-size: 10px; color: white;
|
|
185
105
|
background: rgba(10, 12, 18, 0.55); padding: 2px 7px; border-radius: 20px;
|
|
186
106
|
backdrop-filter: blur(6px); font-weight: 600; letter-spacing: 0.02em;
|
|
187
107
|
}
|
|
188
|
-
.tag-overlay.tag-match { background: rgba(52, 72, 197, 0.82); }
|
|
189
|
-
.tag-overlay mark {
|
|
190
|
-
background: rgba(255, 213, 79, 0.5); color: white;
|
|
191
|
-
border-radius: 2px; padding: 0 1px;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
108
|
/* Floating action buttons */
|
|
195
109
|
.card-actions {
|
|
196
110
|
position: absolute; bottom: 10px; left: 0; right: 0;
|
|
@@ -254,18 +168,27 @@ const GALLERY_CSS = /* css */ `
|
|
|
254
168
|
}
|
|
255
169
|
|
|
256
170
|
/* ── Multi-select bar ── */
|
|
171
|
+
/* Wrapper takes layout space ONLY when a selection is active. When idle
|
|
172
|
+
* it's display:none so it contributes 0 to scrollHeight (no phantom
|
|
173
|
+
* scrollbar gutter / no extra iframe height). */
|
|
174
|
+
.select-bar-wrap {
|
|
175
|
+
display: none;
|
|
176
|
+
position: sticky; bottom: 0; left: 0; right: 0;
|
|
177
|
+
justify-content: center; pointer-events: none;
|
|
178
|
+
z-index: 100; height: 64px;
|
|
179
|
+
}
|
|
180
|
+
.select-bar-wrap.visible { display: flex; }
|
|
257
181
|
.select-bar {
|
|
258
|
-
position:
|
|
259
|
-
transform:
|
|
182
|
+
position: absolute; bottom: 8px;
|
|
183
|
+
transform: translateY(80px);
|
|
260
184
|
background: #1a1d24; color: white; border-radius: 40px;
|
|
261
185
|
padding: 0 6px 0 16px; height: 48px;
|
|
262
186
|
display: flex; align-items: center; gap: 4px;
|
|
263
187
|
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
264
188
|
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s;
|
|
265
|
-
opacity: 0; pointer-events: none;
|
|
189
|
+
opacity: 0; pointer-events: none; white-space: nowrap;
|
|
266
190
|
}
|
|
267
|
-
.select-bar.visible { transform:
|
|
268
|
-
.select-bar-spacer { height: 72px; }
|
|
191
|
+
.select-bar-wrap.visible .select-bar { transform: translateY(0); opacity: 1; pointer-events: all; }
|
|
269
192
|
.select-count { font-size: 13px; font-weight: 600; margin-right: 8px; }
|
|
270
193
|
.bar-btn {
|
|
271
194
|
height: 36px; padding: 0 14px; border: none; border-radius: 30px;
|
|
@@ -306,8 +229,6 @@ var pendingCall = {
|
|
|
306
229
|
args: null,
|
|
307
230
|
};
|
|
308
231
|
var selected = new Set();
|
|
309
|
-
var filterQuery = "";
|
|
310
|
-
var aspectFilter = "";
|
|
311
232
|
var app = new MCPApp({ name: "Cloudinary Asset Gallery", version: "1.0.0" });
|
|
312
233
|
setupHostContext(app);
|
|
313
234
|
|
|
@@ -331,33 +252,13 @@ function showToast(msg) {
|
|
|
331
252
|
_toastTimer = setTimeout(function() { t.classList.remove("show"); }, 2000);
|
|
332
253
|
}
|
|
333
254
|
|
|
334
|
-
function getAspect(r) {
|
|
335
|
-
if (!r.width || !r.height) return "";
|
|
336
|
-
var ratio = r.width / r.height;
|
|
337
|
-
if (ratio > 1.1) return "landscape";
|
|
338
|
-
if (ratio < 0.9) return "portrait";
|
|
339
|
-
return "square";
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function highlightText(text, query) {
|
|
343
|
-
if (!query) return esc(text);
|
|
344
|
-
var lo = text.toLowerCase();
|
|
345
|
-
var idx = lo.indexOf(query);
|
|
346
|
-
if (idx === -1) return esc(text);
|
|
347
|
-
return esc(text.slice(0, idx))
|
|
348
|
-
+ "<mark>" + esc(text.slice(idx, idx + query.length)) + "</mark>"
|
|
349
|
-
+ esc(text.slice(idx + query.length));
|
|
350
|
-
}
|
|
351
|
-
|
|
352
255
|
function updateSelectBar() {
|
|
353
|
-
var
|
|
256
|
+
var wrap = document.getElementById("select-bar-wrap");
|
|
354
257
|
var countEl = document.getElementById("select-count");
|
|
355
|
-
if (!
|
|
258
|
+
if (!wrap || !countEl) return;
|
|
356
259
|
var n = selected.size;
|
|
357
260
|
countEl.textContent = n + " selected";
|
|
358
|
-
|
|
359
|
-
var spacer = document.getElementById("select-bar-spacer");
|
|
360
|
-
if (spacer) spacer.style.display = n > 0 ? "" : "none";
|
|
261
|
+
wrap.classList.toggle("visible", n > 0);
|
|
361
262
|
var btn = document.getElementById("select-all-btn");
|
|
362
263
|
if (btn) {
|
|
363
264
|
var visible = getVisibleIndices();
|
|
@@ -424,11 +325,9 @@ function copyAssetUrl(type, idx) {
|
|
|
424
325
|
var url = r.secure_url || r.url || "";
|
|
425
326
|
var copyUrl = type === "optimized" ? optimizedUrl(url, r) : url;
|
|
426
327
|
if (!copyUrl) return;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
});
|
|
431
|
-
} catch(e) { showError("Copy Failed", String(e)); }
|
|
328
|
+
copyText(copyUrl).then(function() {
|
|
329
|
+
showToast(type === "optimized" ? "\\u2728 Optimized URL copied" : "URL copied");
|
|
330
|
+
}).catch(function(e) { showError("Copy Failed", e && e.message ? e.message : String(e)); });
|
|
432
331
|
}
|
|
433
332
|
|
|
434
333
|
function downloadOne(idx) {
|
|
@@ -450,134 +349,74 @@ function copySelectedUrls(type) {
|
|
|
450
349
|
urls.push(type === "optimized" ? optimizedUrl(url, r) : url);
|
|
451
350
|
});
|
|
452
351
|
if (!urls.length) return;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
});
|
|
457
|
-
} catch(e) { showError("Copy Failed", String(e)); }
|
|
352
|
+
copyText(urls.join("\\n")).then(function() {
|
|
353
|
+
showToast(urls.length + " " + (type === "optimized" ? "optimized " : "") + "URLs copied");
|
|
354
|
+
}).catch(function(e) { showError("Copy Failed", e && e.message ? e.message : String(e)); });
|
|
458
355
|
}
|
|
459
356
|
|
|
460
|
-
function downloadSelected() {
|
|
461
|
-
|
|
357
|
+
async function downloadSelected() {
|
|
358
|
+
if (selected.size === 0) return;
|
|
359
|
+
|
|
360
|
+
var picks = [];
|
|
462
361
|
selected.forEach(function(i) {
|
|
463
362
|
var r = allResources[i];
|
|
464
|
-
if (
|
|
465
|
-
var url = r.secure_url || r.url || "";
|
|
466
|
-
var dl = downloadUrl(url, r);
|
|
467
|
-
if (dl) { app._rpc("ui/open-link", { url: dl }); count++; }
|
|
363
|
+
if (r && r.public_id) picks.push(r);
|
|
468
364
|
});
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
var
|
|
483
|
-
|
|
484
|
-
if (grid) grid.classList.toggle("filtering", anyFilter);
|
|
365
|
+
if (!picks.length) return;
|
|
366
|
+
|
|
367
|
+
var requestBody = {
|
|
368
|
+
mode: "create",
|
|
369
|
+
target_format: "zip",
|
|
370
|
+
keep_derived: true,
|
|
371
|
+
target_public_id: "mcp-gallery-archive-" + Date.now(),
|
|
372
|
+
fully_qualified_public_ids: picks.map(function(r) {
|
|
373
|
+
return (r.resource_type || "image") + "/" + (r.type || "upload") + "/" + r.public_id;
|
|
374
|
+
}),
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
var btn = document.getElementById("bar-download");
|
|
378
|
+
var origLabel = btn ? btn.innerHTML : "";
|
|
379
|
+
if (btn) { btn.innerHTML = "Creating archive\\u2026"; btn.disabled = true; }
|
|
485
380
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
381
|
+
try {
|
|
382
|
+
var res = await app.callServerTool({
|
|
383
|
+
name: "generate-archive",
|
|
384
|
+
arguments: {
|
|
385
|
+
resource_type: "all",
|
|
386
|
+
RequestBody: requestBody,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
491
389
|
|
|
492
|
-
var
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|| tags.some(function(t) { return t.toLowerCase().indexOf(filterQuery) !== -1; });
|
|
497
|
-
|
|
498
|
-
var aspectMatch = !aspectFilter || getAspect(r) === aspectFilter;
|
|
499
|
-
var match = textMatch && aspectMatch;
|
|
500
|
-
card.style.display = match ? "" : "none";
|
|
501
|
-
|
|
502
|
-
var tagsEl = document.getElementById("tags-overlay-" + i);
|
|
503
|
-
if (tagsEl && tags.length) {
|
|
504
|
-
var maxOv = 3;
|
|
505
|
-
var matchedTags = [];
|
|
506
|
-
var otherTags = [];
|
|
507
|
-
for (var ti = 0; ti < tags.length; ti++) {
|
|
508
|
-
var isMatch = filterQuery && tags[ti].toLowerCase().indexOf(filterQuery) !== -1;
|
|
509
|
-
if (isMatch) matchedTags.push(tags[ti]);
|
|
510
|
-
else otherTags.push(tags[ti]);
|
|
511
|
-
}
|
|
512
|
-
var shown = matchedTags.slice();
|
|
513
|
-
var remaining = maxOv - shown.length;
|
|
514
|
-
if (remaining > 0) shown = shown.concat(otherTags.slice(0, remaining));
|
|
515
|
-
var hidden = tags.length - shown.length;
|
|
516
|
-
var hiddenTags = tags.filter(function(t) { return shown.indexOf(t) === -1; });
|
|
517
|
-
tagsEl.innerHTML = shown.map(function(t) {
|
|
518
|
-
var matched = filterQuery && t.toLowerCase().indexOf(filterQuery) !== -1;
|
|
519
|
-
return '<span class="tag-overlay' + (matched ? ' tag-match' : '') + '">' + highlightText(t, filterQuery) + '</span>';
|
|
520
|
-
}).join("") + (hidden > 0 ? '<span class="tag-overlay" title="' + esc(hiddenTags.join(", ")) + '">+' + hidden + '</span>' : '');
|
|
390
|
+
var data = ingestResult(res);
|
|
391
|
+
if (data && (data._error || data._parseError)) {
|
|
392
|
+
showError("Archive Failed", unwrapApiError(data._message));
|
|
393
|
+
return;
|
|
521
394
|
}
|
|
522
|
-
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
var badge = document.getElementById("count-badge");
|
|
527
|
-
if (badge) {
|
|
528
|
-
badge.textContent = anyFilter
|
|
529
|
-
? visibleCount + " of " + allResources.length
|
|
530
|
-
: allResources.length + (lastCursor ? "+" : "") + " items";
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
var noRes = document.getElementById("no-results");
|
|
534
|
-
if (visibleCount === 0 && anyFilter) {
|
|
535
|
-
if (!noRes && grid) {
|
|
536
|
-
noRes = document.createElement("div");
|
|
537
|
-
noRes.id = "no-results";
|
|
538
|
-
noRes.className = "no-results";
|
|
539
|
-
grid.appendChild(noRes);
|
|
395
|
+
var archiveUrl = data && (data.secure_url || data.url);
|
|
396
|
+
if (!archiveUrl) {
|
|
397
|
+
showError("Archive Failed", "No delivery URL returned.");
|
|
398
|
+
return;
|
|
540
399
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
400
|
+
try { await copyText(archiveUrl); } catch (e) { /* ignore */ }
|
|
401
|
+
app._rpc("ui/open-link", { url: archiveUrl });
|
|
402
|
+
showToast("Archive saved as raw in Cloudinary \\u2014 opening URL (" + picks.length + " asset" + (picks.length > 1 ? "s" : "") + ")");
|
|
403
|
+
} catch (e) {
|
|
404
|
+
showError("Archive Failed", unwrapApiError(e && e.message ? e.message : String(e)));
|
|
405
|
+
} finally {
|
|
406
|
+
if (btn) { btn.innerHTML = origLabel; btn.disabled = false; }
|
|
544
407
|
}
|
|
545
408
|
}
|
|
546
409
|
|
|
547
|
-
function
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function toggleAspectMenu(e) {
|
|
560
|
-
e.stopPropagation();
|
|
561
|
-
var btn = document.getElementById("aspect-btn");
|
|
562
|
-
var menu = document.getElementById("aspect-menu");
|
|
563
|
-
if (!btn || !menu) return;
|
|
564
|
-
var open = menu.classList.toggle("open");
|
|
565
|
-
btn.classList.toggle("open", open);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function selectAspect(val) {
|
|
569
|
-
aspectFilter = val;
|
|
570
|
-
var labels = { "": "All orientations", landscape: "Landscape", portrait: "Portrait", square: "Square" };
|
|
571
|
-
var label = document.getElementById("aspect-btn-label");
|
|
572
|
-
if (label) label.textContent = labels[aspectFilter] || "All orientations";
|
|
573
|
-
document.querySelectorAll(".aspect-option").forEach(function(o) {
|
|
574
|
-
o.classList.toggle("selected", o.getAttribute("data-value") === aspectFilter);
|
|
575
|
-
});
|
|
576
|
-
var menu = document.getElementById("aspect-menu");
|
|
577
|
-
var btn = document.getElementById("aspect-btn");
|
|
578
|
-
if (menu) menu.classList.remove("open");
|
|
579
|
-
if (btn) btn.classList.remove("open");
|
|
580
|
-
handleFilter();
|
|
410
|
+
function unwrapApiError(raw) {
|
|
411
|
+
if (!raw) return "Unknown error.";
|
|
412
|
+
var msg = String(raw);
|
|
413
|
+
try {
|
|
414
|
+
if (msg.charAt(0) === "{") {
|
|
415
|
+
var parsed = JSON.parse(msg);
|
|
416
|
+
msg = (parsed && parsed.error && parsed.error.message) || msg;
|
|
417
|
+
}
|
|
418
|
+
} catch (e) { /* keep raw */ }
|
|
419
|
+
return msg;
|
|
581
420
|
}
|
|
582
421
|
|
|
583
422
|
function render() {
|
|
@@ -597,41 +436,12 @@ function render() {
|
|
|
597
436
|
h += '<h1>Results</h1>';
|
|
598
437
|
h += '<span class="count-badge" id="count-badge">' + allResources.length + (lastCursor ? "+" : "") + ' items</span>';
|
|
599
438
|
h += '</div>';
|
|
600
|
-
h += '<div style="display:flex;align-items:center;gap:8px">';
|
|
601
|
-
h += '<button class="
|
|
602
|
-
h += '<button class="
|
|
439
|
+
h += '<div id="header-actions" style="display:flex;align-items:center;gap:8px">';
|
|
440
|
+
h += '<button class="icon-btn" id="select-all-btn">Select all</button>';
|
|
441
|
+
h += '<button class="icon-btn icon-only" id="refresh-gallery" title="Refresh">' + IC.refresh + '</button>';
|
|
603
442
|
h += '</div>';
|
|
604
443
|
h += '</div>';
|
|
605
444
|
|
|
606
|
-
// Filter bar
|
|
607
|
-
h += '<div class="filter-row">';
|
|
608
|
-
h += '<div class="filter-text-wrap">';
|
|
609
|
-
h += '<span class="filter-icon"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="6.5" cy="6.5" r="4.5"/><path d="M10.5 10.5l3 3"/></svg></span>';
|
|
610
|
-
h += '<input class="filter-input" id="filter-input" type="text" placeholder="Filter by filename or tag\\u2026" autocomplete="off" spellcheck="false">';
|
|
611
|
-
h += '<button class="filter-clear" id="filter-clear">\\u2715</button>';
|
|
612
|
-
h += '</div>';
|
|
613
|
-
h += '<div class="aspect-dropdown" id="aspect-dropdown">';
|
|
614
|
-
h += '<button class="aspect-btn" id="aspect-btn">';
|
|
615
|
-
h += '<span id="aspect-btn-label">All orientations</span>';
|
|
616
|
-
h += '<svg class="aspect-btn-chevron" width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2,4 6,8 10,4"/></svg>';
|
|
617
|
-
h += '</button>';
|
|
618
|
-
h += '<div class="aspect-menu" id="aspect-menu">';
|
|
619
|
-
var aspects = [
|
|
620
|
-
{ val: "", label: "All orientations", icon: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="1" width="12" height="12" rx="1.5"/></svg>' },
|
|
621
|
-
{ val: "landscape", label: "Landscape", icon: '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" stroke="currentColor" stroke-width="1.5"><rect x=".75" y=".75" width="12.5" height="8.5" rx="1.5"/></svg>' },
|
|
622
|
-
{ val: "portrait", label: "Portrait", icon: '<svg width="10" height="14" viewBox="0 0 10 14" fill="none" stroke="currentColor" stroke-width="1.5"><rect x=".75" y=".75" width="8.5" height="12.5" rx="1.5"/></svg>' },
|
|
623
|
-
{ val: "square", label: "Square", icon: '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><rect x=".75" y=".75" width="10.5" height="10.5" rx="1.5"/></svg>' },
|
|
624
|
-
];
|
|
625
|
-
for (var ai = 0; ai < aspects.length; ai++) {
|
|
626
|
-
var ao = aspects[ai];
|
|
627
|
-
h += '<div class="aspect-option' + (ao.val === aspectFilter ? ' selected' : '') + '" data-value="' + ao.val + '">';
|
|
628
|
-
h += '<span class="aspect-opt-icon">' + ao.icon + '</span>';
|
|
629
|
-
h += ao.label;
|
|
630
|
-
h += '<svg class="aspect-check" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2,6 5,9 10,3"/></svg>';
|
|
631
|
-
h += '</div>';
|
|
632
|
-
}
|
|
633
|
-
h += '</div></div></div>';
|
|
634
|
-
|
|
635
445
|
// Grid
|
|
636
446
|
h += '<div class="grid" id="gallery-grid">';
|
|
637
447
|
for (var i = 0; i < allResources.length; i++) {
|
|
@@ -680,8 +490,8 @@ function render() {
|
|
|
680
490
|
if (url) {
|
|
681
491
|
h += '<div class="card-actions">';
|
|
682
492
|
h += '<button class="action-btn act-original" data-copy-original="' + i + '">Copy URL</button>';
|
|
683
|
-
if (rt !== "raw") h += '<button class="action-btn act-optimized" data-copy-optimized="' + i + '"
|
|
684
|
-
h += '<button class="action-btn act-download" data-download="' + i + '" title="Download"
|
|
493
|
+
if (rt !== "raw") h += '<button class="action-btn act-optimized" data-copy-optimized="' + i + '">' + IC.zap + ' Optimized</button>';
|
|
494
|
+
h += '<button class="action-btn act-download" data-download="' + i + '" title="Download">' + IC.arrowDown + '</button>';
|
|
685
495
|
h += '</div>';
|
|
686
496
|
}
|
|
687
497
|
|
|
@@ -728,24 +538,24 @@ function render() {
|
|
|
728
538
|
h += "</div>";
|
|
729
539
|
}
|
|
730
540
|
|
|
731
|
-
//
|
|
732
|
-
h += '<div class="select-bar-
|
|
733
|
-
|
|
734
|
-
// Multi-select bar
|
|
541
|
+
// Multi-select bar (sticky, in-flow wrapper so iframe sizing stays truthful)
|
|
542
|
+
h += '<div class="select-bar-wrap" id="select-bar-wrap">';
|
|
735
543
|
h += '<div class="select-bar" id="select-bar">';
|
|
736
544
|
h += '<span class="select-count" id="select-count">0 selected</span>';
|
|
737
545
|
h += '<div class="bar-divider"></div>';
|
|
738
|
-
h += '<button class="bar-btn bar-primary" id="bar-copy-optimized" style="display:none"
|
|
546
|
+
h += '<button class="bar-btn bar-primary" id="bar-copy-optimized" style="display:none">' + IC.zap + ' Copy Optimized</button>';
|
|
739
547
|
h += '<button class="bar-btn bar-secondary" id="bar-copy-original">Copy Original</button>';
|
|
740
|
-
h += '<button class="bar-btn bar-secondary" id="bar-download"
|
|
548
|
+
h += '<button class="bar-btn bar-secondary" id="bar-download">' + IC.arrowDown + ' Download Selected</button>';
|
|
741
549
|
h += '<div class="bar-divider"></div>';
|
|
742
|
-
h += '<button class="bar-btn bar-ghost" id="bar-clear"
|
|
550
|
+
h += '<button class="bar-btn bar-ghost" id="bar-clear">' + IC.x + '</button>';
|
|
551
|
+
h += '</div>';
|
|
743
552
|
h += '</div>';
|
|
744
553
|
|
|
745
554
|
// Toast
|
|
746
555
|
h += '<div class="gallery-toast" id="gallery-toast"></div>';
|
|
747
556
|
|
|
748
557
|
root.innerHTML = h;
|
|
558
|
+
renderThemeToggle();
|
|
749
559
|
|
|
750
560
|
// Re-apply selection state
|
|
751
561
|
selected.forEach(function(i) {
|
|
@@ -779,37 +589,16 @@ function attachEvents() {
|
|
|
779
589
|
_eventsAttached = true;
|
|
780
590
|
var root = document.getElementById("app");
|
|
781
591
|
|
|
782
|
-
root.addEventListener("input", function(e) {
|
|
783
|
-
if (e.target && e.target.id === "filter-input") handleFilter();
|
|
784
|
-
});
|
|
785
|
-
|
|
786
|
-
document.addEventListener("click", function(e) {
|
|
787
|
-
var dd = document.getElementById("aspect-dropdown");
|
|
788
|
-
if (dd && !dd.contains(e.target)) {
|
|
789
|
-
var menu = document.getElementById("aspect-menu");
|
|
790
|
-
var btn = document.getElementById("aspect-btn");
|
|
791
|
-
if (menu) menu.classList.remove("open");
|
|
792
|
-
if (btn) btn.classList.remove("open");
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
|
|
796
592
|
root.addEventListener("click", function(e) {
|
|
797
593
|
var el = e.target;
|
|
798
594
|
while (el && el !== root) {
|
|
799
595
|
if (el.id === "load-more-btn") { loadMore(); return; }
|
|
800
596
|
if (el.id === "refresh-gallery") { refreshGallery(); return; }
|
|
801
597
|
if (el.id === "select-all-btn") { toggleSelectAll(); return; }
|
|
802
|
-
if (el.id === "filter-clear") { clearFilter(); return; }
|
|
803
598
|
if (el.id === "bar-copy-optimized") { copySelectedUrls("optimized"); return; }
|
|
804
599
|
if (el.id === "bar-copy-original") { copySelectedUrls("original"); return; }
|
|
805
600
|
if (el.id === "bar-download") { downloadSelected(); return; }
|
|
806
601
|
if (el.id === "bar-clear") { clearSelection(); return; }
|
|
807
|
-
if (el.id === "aspect-btn" || el.parentElement && el.parentElement.id === "aspect-btn") {
|
|
808
|
-
toggleAspectMenu(e); return;
|
|
809
|
-
}
|
|
810
|
-
if (el.classList && el.classList.contains("aspect-option")) {
|
|
811
|
-
selectAspect(el.getAttribute("data-value") || ""); return;
|
|
812
|
-
}
|
|
813
602
|
if (el.dataset && el.dataset.copyOriginal != null) {
|
|
814
603
|
e.stopPropagation();
|
|
815
604
|
copyAssetUrl("original", parseInt(el.dataset.copyOriginal, 10)); return;
|
|
@@ -970,6 +759,7 @@ function showFetchPrompt() {
|
|
|
970
759
|
h += '<button class="prompt-btn prompt-btn-primary" id="fetch-direct-btn">Fetch Directly</button>';
|
|
971
760
|
h += "</div></div>";
|
|
972
761
|
root.innerHTML = h;
|
|
762
|
+
renderThemeToggle();
|
|
973
763
|
document.getElementById("fetch-direct-btn").addEventListener("click", function() { fetchDirect(); });
|
|
974
764
|
}
|
|
975
765
|
|
|
@@ -989,6 +779,9 @@ async function fetchDirect() {
|
|
|
989
779
|
console.log(LOG_PREFIX, "fetchDirect ->", name);
|
|
990
780
|
|
|
991
781
|
document.getElementById("app").innerHTML = '<div class="status">Fetching assets\\u2026</div>';
|
|
782
|
+
requestAnimationFrame(function() {
|
|
783
|
+
app.reportSize(Math.max(document.documentElement.scrollHeight, MIN_HEIGHT));
|
|
784
|
+
});
|
|
992
785
|
try {
|
|
993
786
|
var res = await app.callServerTool({ name: name, arguments: args });
|
|
994
787
|
var data = ingestResult(res);
|
|
@@ -1040,6 +833,7 @@ async function loadMore() {
|
|
|
1040
833
|
function refreshGallery() {
|
|
1041
834
|
allResources = [];
|
|
1042
835
|
lastCursor = null;
|
|
836
|
+
selected.clear();
|
|
1043
837
|
fetchDirect();
|
|
1044
838
|
}
|
|
1045
839
|
|
|
@@ -1047,7 +841,6 @@ function refreshGallery() {
|
|
|
1047
841
|
document.addEventListener("keydown", function(e) {
|
|
1048
842
|
if (e.key === "Escape") {
|
|
1049
843
|
if (document.querySelector(".modal-overlay")) { closeModal(); return; }
|
|
1050
|
-
if (filterQuery || aspectFilter) { clearFilter(); return; }
|
|
1051
844
|
if (selected.size > 0) { clearSelection(); return; }
|
|
1052
845
|
}
|
|
1053
846
|
});
|
|
@@ -1078,6 +871,7 @@ ${GALLERY_CSS}
|
|
|
1078
871
|
<div class="gallery-toast" id="gallery-toast"></div>
|
|
1079
872
|
|
|
1080
873
|
<script>
|
|
874
|
+
${SHARED_JS_ICONS}
|
|
1081
875
|
${SHARED_JS_MCP_CLIENT}
|
|
1082
876
|
${SHARED_JS_HELPERS}
|
|
1083
877
|
${SHARED_JS_TOOLTIPS}
|