@cloudinary/asset-management-mcp 0.9.0 → 0.9.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/README.md +12 -15
- package/bin/mcp-server.js +269 -379
- package/bin/mcp-server.js.map +17 -17
- package/esm/funcs/searchSearchAssets.d.ts +46 -2
- package/esm/funcs/searchSearchAssets.d.ts.map +1 -1
- package/esm/funcs/searchSearchAssets.js +46 -2
- package/esm/funcs/searchSearchAssets.js.map +1 -1
- 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 +4 -4
- package/esm/lib/config.js +4 -4
- package/esm/mcp-server/apps/app-shared.d.ts +5 -4
- package/esm/mcp-server/apps/app-shared.d.ts.map +1 -1
- package/esm/mcp-server/apps/app-shared.js +79 -12
- 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 +75 -298
- 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/mcp-server/tools/searchSearchAssets.d.ts.map +1 -1
- package/esm/mcp-server/tools/searchSearchAssets.js +46 -2
- package/esm/mcp-server/tools/searchSearchAssets.js.map +1 -1
- package/esm/models/searchparameters.d.ts +4 -1
- package/esm/models/searchparameters.d.ts.map +1 -1
- package/esm/models/searchparameters.js +9 -8
- package/esm/models/searchparameters.js.map +1 -1
- package/esm/tool-names.js +1 -1
- package/esm/tool-names.js.map +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/funcs/searchSearchAssets.ts +46 -2
- package/src/landing-page.ts +9 -3
- package/src/lib/config.ts +4 -4
- package/src/mcp-server/apps/app-shared.ts +80 -12
- package/src/mcp-server/apps/asset-details-app.ts +7 -13
- package/src/mcp-server/apps/asset-gallery-app.ts +75 -297
- 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/mcp-server/tools/searchSearchAssets.ts +46 -2
- package/src/models/searchparameters.ts +25 -9
- package/src/tool-names.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;
|
|
@@ -265,7 +179,6 @@ const GALLERY_CSS = /* css */ `
|
|
|
265
179
|
opacity: 0; pointer-events: none; z-index: 100; white-space: nowrap;
|
|
266
180
|
}
|
|
267
181
|
.select-bar.visible { transform: translateX(-50%) translateY(0); opacity: 1; pointer-events: all; }
|
|
268
|
-
.select-bar-spacer { height: 72px; }
|
|
269
182
|
.select-count { font-size: 13px; font-weight: 600; margin-right: 8px; }
|
|
270
183
|
.bar-btn {
|
|
271
184
|
height: 36px; padding: 0 14px; border: none; border-radius: 30px;
|
|
@@ -306,8 +219,6 @@ var pendingCall = {
|
|
|
306
219
|
args: null,
|
|
307
220
|
};
|
|
308
221
|
var selected = new Set();
|
|
309
|
-
var filterQuery = "";
|
|
310
|
-
var aspectFilter = "";
|
|
311
222
|
var app = new MCPApp({ name: "Cloudinary Asset Gallery", version: "1.0.0" });
|
|
312
223
|
setupHostContext(app);
|
|
313
224
|
|
|
@@ -331,24 +242,6 @@ function showToast(msg) {
|
|
|
331
242
|
_toastTimer = setTimeout(function() { t.classList.remove("show"); }, 2000);
|
|
332
243
|
}
|
|
333
244
|
|
|
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
245
|
function updateSelectBar() {
|
|
353
246
|
var bar = document.getElementById("select-bar");
|
|
354
247
|
var countEl = document.getElementById("select-count");
|
|
@@ -356,8 +249,6 @@ function updateSelectBar() {
|
|
|
356
249
|
var n = selected.size;
|
|
357
250
|
countEl.textContent = n + " selected";
|
|
358
251
|
bar.classList.toggle("visible", n > 0);
|
|
359
|
-
var spacer = document.getElementById("select-bar-spacer");
|
|
360
|
-
if (spacer) spacer.style.display = n > 0 ? "" : "none";
|
|
361
252
|
var btn = document.getElementById("select-all-btn");
|
|
362
253
|
if (btn) {
|
|
363
254
|
var visible = getVisibleIndices();
|
|
@@ -424,11 +315,9 @@ function copyAssetUrl(type, idx) {
|
|
|
424
315
|
var url = r.secure_url || r.url || "";
|
|
425
316
|
var copyUrl = type === "optimized" ? optimizedUrl(url, r) : url;
|
|
426
317
|
if (!copyUrl) return;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
});
|
|
431
|
-
} catch(e) { showError("Copy Failed", String(e)); }
|
|
318
|
+
copyText(copyUrl).then(function() {
|
|
319
|
+
showToast(type === "optimized" ? "\\u2728 Optimized URL copied" : "URL copied");
|
|
320
|
+
}).catch(function(e) { showError("Copy Failed", e && e.message ? e.message : String(e)); });
|
|
432
321
|
}
|
|
433
322
|
|
|
434
323
|
function downloadOne(idx) {
|
|
@@ -450,134 +339,74 @@ function copySelectedUrls(type) {
|
|
|
450
339
|
urls.push(type === "optimized" ? optimizedUrl(url, r) : url);
|
|
451
340
|
});
|
|
452
341
|
if (!urls.length) return;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
});
|
|
457
|
-
} catch(e) { showError("Copy Failed", String(e)); }
|
|
342
|
+
copyText(urls.join("\\n")).then(function() {
|
|
343
|
+
showToast(urls.length + " " + (type === "optimized" ? "optimized " : "") + "URLs copied");
|
|
344
|
+
}).catch(function(e) { showError("Copy Failed", e && e.message ? e.message : String(e)); });
|
|
458
345
|
}
|
|
459
346
|
|
|
460
|
-
function downloadSelected() {
|
|
461
|
-
|
|
347
|
+
async function downloadSelected() {
|
|
348
|
+
if (selected.size === 0) return;
|
|
349
|
+
|
|
350
|
+
var picks = [];
|
|
462
351
|
selected.forEach(function(i) {
|
|
463
352
|
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++; }
|
|
353
|
+
if (r && r.public_id) picks.push(r);
|
|
468
354
|
});
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
355
|
+
if (!picks.length) return;
|
|
356
|
+
|
|
357
|
+
var requestBody = {
|
|
358
|
+
mode: "create",
|
|
359
|
+
target_format: "zip",
|
|
360
|
+
keep_derived: true,
|
|
361
|
+
target_public_id: "mcp-gallery-archive-" + Date.now(),
|
|
362
|
+
fully_qualified_public_ids: picks.map(function(r) {
|
|
363
|
+
return (r.resource_type || "image") + "/" + (r.type || "upload") + "/" + r.public_id;
|
|
364
|
+
}),
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
var btn = document.getElementById("bar-download");
|
|
368
|
+
var origLabel = btn ? btn.innerHTML : "";
|
|
369
|
+
if (btn) { btn.innerHTML = "Creating archive\\u2026"; btn.disabled = true; }
|
|
475
370
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (grid) grid.classList.toggle("filtering", anyFilter);
|
|
485
|
-
|
|
486
|
-
var visibleCount = 0;
|
|
487
|
-
for (var i = 0; i < allResources.length; i++) {
|
|
488
|
-
var r = allResources[i];
|
|
489
|
-
var card = document.getElementById("card-" + i);
|
|
490
|
-
if (!card) continue;
|
|
371
|
+
try {
|
|
372
|
+
var res = await app.callServerTool({
|
|
373
|
+
name: "generate-archive",
|
|
374
|
+
arguments: {
|
|
375
|
+
resource_type: "all",
|
|
376
|
+
RequestBody: requestBody,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
491
379
|
|
|
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>' : '');
|
|
380
|
+
var data = ingestResult(res);
|
|
381
|
+
if (data && (data._error || data._parseError)) {
|
|
382
|
+
showError("Archive Failed", unwrapApiError(data._message));
|
|
383
|
+
return;
|
|
521
384
|
}
|
|
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);
|
|
385
|
+
var archiveUrl = data && (data.secure_url || data.url);
|
|
386
|
+
if (!archiveUrl) {
|
|
387
|
+
showError("Archive Failed", "No delivery URL returned.");
|
|
388
|
+
return;
|
|
540
389
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
390
|
+
try { await copyText(archiveUrl); } catch (e) { /* ignore */ }
|
|
391
|
+
app._rpc("ui/open-link", { url: archiveUrl });
|
|
392
|
+
showToast("Archive saved as raw in Cloudinary \\u2014 opening URL (" + picks.length + " asset" + (picks.length > 1 ? "s" : "") + ")");
|
|
393
|
+
} catch (e) {
|
|
394
|
+
showError("Archive Failed", unwrapApiError(e && e.message ? e.message : String(e)));
|
|
395
|
+
} finally {
|
|
396
|
+
if (btn) { btn.innerHTML = origLabel; btn.disabled = false; }
|
|
544
397
|
}
|
|
545
398
|
}
|
|
546
399
|
|
|
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();
|
|
400
|
+
function unwrapApiError(raw) {
|
|
401
|
+
if (!raw) return "Unknown error.";
|
|
402
|
+
var msg = String(raw);
|
|
403
|
+
try {
|
|
404
|
+
if (msg.charAt(0) === "{") {
|
|
405
|
+
var parsed = JSON.parse(msg);
|
|
406
|
+
msg = (parsed && parsed.error && parsed.error.message) || msg;
|
|
407
|
+
}
|
|
408
|
+
} catch (e) { /* keep raw */ }
|
|
409
|
+
return msg;
|
|
581
410
|
}
|
|
582
411
|
|
|
583
412
|
function render() {
|
|
@@ -597,40 +426,11 @@ function render() {
|
|
|
597
426
|
h += '<h1>Results</h1>';
|
|
598
427
|
h += '<span class="count-badge" id="count-badge">' + allResources.length + (lastCursor ? "+" : "") + ' items</span>';
|
|
599
428
|
h += '</div>';
|
|
600
|
-
h += '<div style="display:flex;align-items:center;gap:8px">';
|
|
601
|
-
h += '<button class="
|
|
602
|
-
h += '<button class="
|
|
603
|
-
h += '</div>';
|
|
429
|
+
h += '<div id="header-actions" style="display:flex;align-items:center;gap:8px">';
|
|
430
|
+
h += '<button class="icon-btn" id="select-all-btn">Select all</button>';
|
|
431
|
+
h += '<button class="icon-btn icon-only" id="refresh-gallery" title="Refresh">' + IC.refresh + '</button>';
|
|
604
432
|
h += '</div>';
|
|
605
|
-
|
|
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
433
|
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
434
|
|
|
635
435
|
// Grid
|
|
636
436
|
h += '<div class="grid" id="gallery-grid">';
|
|
@@ -680,8 +480,8 @@ function render() {
|
|
|
680
480
|
if (url) {
|
|
681
481
|
h += '<div class="card-actions">';
|
|
682
482
|
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"
|
|
483
|
+
if (rt !== "raw") h += '<button class="action-btn act-optimized" data-copy-optimized="' + i + '">' + IC.zap + ' Optimized</button>';
|
|
484
|
+
h += '<button class="action-btn act-download" data-download="' + i + '" title="Download">' + IC.arrowDown + '</button>';
|
|
685
485
|
h += '</div>';
|
|
686
486
|
}
|
|
687
487
|
|
|
@@ -728,24 +528,22 @@ function render() {
|
|
|
728
528
|
h += "</div>";
|
|
729
529
|
}
|
|
730
530
|
|
|
731
|
-
// Spacer so select bar doesn't cover Load More
|
|
732
|
-
h += '<div class="select-bar-spacer" id="select-bar-spacer" style="display:none"></div>';
|
|
733
|
-
|
|
734
531
|
// Multi-select bar
|
|
735
532
|
h += '<div class="select-bar" id="select-bar">';
|
|
736
533
|
h += '<span class="select-count" id="select-count">0 selected</span>';
|
|
737
534
|
h += '<div class="bar-divider"></div>';
|
|
738
|
-
h += '<button class="bar-btn bar-primary" id="bar-copy-optimized" style="display:none"
|
|
535
|
+
h += '<button class="bar-btn bar-primary" id="bar-copy-optimized" style="display:none">' + IC.zap + ' Copy Optimized</button>';
|
|
739
536
|
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"
|
|
537
|
+
h += '<button class="bar-btn bar-secondary" id="bar-download">' + IC.arrowDown + ' Download Selected</button>';
|
|
741
538
|
h += '<div class="bar-divider"></div>';
|
|
742
|
-
h += '<button class="bar-btn bar-ghost" id="bar-clear"
|
|
539
|
+
h += '<button class="bar-btn bar-ghost" id="bar-clear">' + IC.x + '</button>';
|
|
743
540
|
h += '</div>';
|
|
744
541
|
|
|
745
542
|
// Toast
|
|
746
543
|
h += '<div class="gallery-toast" id="gallery-toast"></div>';
|
|
747
544
|
|
|
748
545
|
root.innerHTML = h;
|
|
546
|
+
renderThemeToggle();
|
|
749
547
|
|
|
750
548
|
// Re-apply selection state
|
|
751
549
|
selected.forEach(function(i) {
|
|
@@ -779,37 +577,16 @@ function attachEvents() {
|
|
|
779
577
|
_eventsAttached = true;
|
|
780
578
|
var root = document.getElementById("app");
|
|
781
579
|
|
|
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
580
|
root.addEventListener("click", function(e) {
|
|
797
581
|
var el = e.target;
|
|
798
582
|
while (el && el !== root) {
|
|
799
583
|
if (el.id === "load-more-btn") { loadMore(); return; }
|
|
800
584
|
if (el.id === "refresh-gallery") { refreshGallery(); return; }
|
|
801
585
|
if (el.id === "select-all-btn") { toggleSelectAll(); return; }
|
|
802
|
-
if (el.id === "filter-clear") { clearFilter(); return; }
|
|
803
586
|
if (el.id === "bar-copy-optimized") { copySelectedUrls("optimized"); return; }
|
|
804
587
|
if (el.id === "bar-copy-original") { copySelectedUrls("original"); return; }
|
|
805
588
|
if (el.id === "bar-download") { downloadSelected(); return; }
|
|
806
589
|
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
590
|
if (el.dataset && el.dataset.copyOriginal != null) {
|
|
814
591
|
e.stopPropagation();
|
|
815
592
|
copyAssetUrl("original", parseInt(el.dataset.copyOriginal, 10)); return;
|
|
@@ -970,6 +747,7 @@ function showFetchPrompt() {
|
|
|
970
747
|
h += '<button class="prompt-btn prompt-btn-primary" id="fetch-direct-btn">Fetch Directly</button>';
|
|
971
748
|
h += "</div></div>";
|
|
972
749
|
root.innerHTML = h;
|
|
750
|
+
renderThemeToggle();
|
|
973
751
|
document.getElementById("fetch-direct-btn").addEventListener("click", function() { fetchDirect(); });
|
|
974
752
|
}
|
|
975
753
|
|
|
@@ -1047,7 +825,6 @@ function refreshGallery() {
|
|
|
1047
825
|
document.addEventListener("keydown", function(e) {
|
|
1048
826
|
if (e.key === "Escape") {
|
|
1049
827
|
if (document.querySelector(".modal-overlay")) { closeModal(); return; }
|
|
1050
|
-
if (filterQuery || aspectFilter) { clearFilter(); return; }
|
|
1051
828
|
if (selected.size > 0) { clearSelection(); return; }
|
|
1052
829
|
}
|
|
1053
830
|
});
|
|
@@ -1078,6 +855,7 @@ ${GALLERY_CSS}
|
|
|
1078
855
|
<div class="gallery-toast" id="gallery-toast"></div>
|
|
1079
856
|
|
|
1080
857
|
<script>
|
|
858
|
+
${SHARED_JS_ICONS}
|
|
1081
859
|
${SHARED_JS_MCP_CLIENT}
|
|
1082
860
|
${SHARED_JS_HELPERS}
|
|
1083
861
|
${SHARED_JS_TOOLTIPS}
|