@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
|
@@ -116,6 +116,7 @@ export const SHARED_CSS_TOKENS = /* css */ `
|
|
|
116
116
|
[data-theme="dark"] .status-warn, .dark .status-warn { background: #854d0e; color: #fef08a; }
|
|
117
117
|
[data-theme="dark"] .status-err, .dark .status-err { background: #991b1b; color: #fecaca; }
|
|
118
118
|
|
|
119
|
+
html { scrollbar-gutter: auto; }
|
|
119
120
|
body {
|
|
120
121
|
font-family: var(--cld-font);
|
|
121
122
|
background: var(--cld-bg);
|
|
@@ -124,18 +125,38 @@ body {
|
|
|
124
125
|
line-height: 1.5;
|
|
125
126
|
font-size: var(--cld-font-xs);
|
|
126
127
|
position: relative;
|
|
128
|
+
overflow-x: hidden;
|
|
127
129
|
}
|
|
128
130
|
.theme-btn {
|
|
129
|
-
position: absolute; top: 4px; right: 4px; z-index: 900;
|
|
130
131
|
width: 22px; height: 22px; border-radius: 50%;
|
|
131
132
|
border: 1px solid transparent; background: transparent;
|
|
132
133
|
color: var(--cld-text3); cursor: pointer;
|
|
133
134
|
display: flex; align-items: center; justify-content: center;
|
|
134
135
|
padding: 0; transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
135
|
-
opacity: 0.5;
|
|
136
|
+
opacity: 0.5; flex-shrink: 0;
|
|
136
137
|
}
|
|
137
138
|
.theme-btn:hover { background: var(--cld-bg3); color: var(--cld-text); border-color: var(--cld-border); opacity: 1; }
|
|
138
139
|
.theme-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
140
|
+
/* Shared icon-button — square pill, same family as theme-btn */
|
|
141
|
+
.icon-btn {
|
|
142
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 5px;
|
|
143
|
+
background: none; border: 1px solid var(--cld-border); border-radius: var(--cld-radius-sm);
|
|
144
|
+
color: var(--cld-text2); cursor: pointer; padding: 4px 8px;
|
|
145
|
+
font-size: 12px; font-weight: 500; font-family: inherit; line-height: 1;
|
|
146
|
+
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
147
|
+
white-space: nowrap; flex-shrink: 0;
|
|
148
|
+
}
|
|
149
|
+
.icon-btn:hover { background: var(--cld-bg3); color: var(--cld-text); border-color: var(--cld-border2); }
|
|
150
|
+
.icon-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
|
|
151
|
+
/* icon-only variant (no text label) */
|
|
152
|
+
.icon-btn.icon-only { padding: 4px; width: 28px; height: 28px; }
|
|
153
|
+
/* header state icon (decorative, not a button) */
|
|
154
|
+
.upload-header-icon { display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
155
|
+
.upload-header-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
156
|
+
.upload-header-icon.icon-success { color: var(--cld-success); }
|
|
157
|
+
.upload-header-icon.icon-error { color: var(--cld-error); }
|
|
158
|
+
.upload-header-icon.icon-warning { color: var(--cld-warning); }
|
|
159
|
+
.upload-header-icon.icon-accent { color: var(--cld-accent); }
|
|
139
160
|
`;
|
|
140
161
|
|
|
141
162
|
// ── CSS: Shared component styles ────────────────────────────────────
|
|
@@ -143,9 +164,11 @@ export const SHARED_CSS_COMPONENTS = /* css */ `
|
|
|
143
164
|
.link { cursor: pointer; }
|
|
144
165
|
.link:hover { color: var(--cld-accent); text-decoration: underline; }
|
|
145
166
|
|
|
146
|
-
/* Modal
|
|
167
|
+
/* Modal — positioned absolutely so it can be placed in the part of the
|
|
168
|
+
* iframe that is currently visible in the host's viewport (since position:fixed
|
|
169
|
+
* inside a tall iframe anchors to iframe-center, not the user's visible area). */
|
|
147
170
|
.modal-overlay {
|
|
148
|
-
position:
|
|
171
|
+
position: absolute; left: 0; right: 0;
|
|
149
172
|
background: rgba(0,0,0,0.45);
|
|
150
173
|
display: flex; align-items: center; justify-content: center;
|
|
151
174
|
z-index: 1000; backdrop-filter: blur(3px); padding: 24px;
|
|
@@ -389,7 +412,8 @@ details.detail-section > summary.detail-section-title::-webkit-details-marker {
|
|
|
389
412
|
}
|
|
390
413
|
.upload-zone:hover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }
|
|
391
414
|
.upload-zone.dragover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }
|
|
392
|
-
.upload-zone-icon {
|
|
415
|
+
.upload-zone-icon { margin-bottom: 8px; color: var(--cld-text3); display: flex; align-items: center; justify-content: center; }
|
|
416
|
+
.upload-zone-icon svg { width: 36px; height: 36px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
393
417
|
.upload-zone-text { font-size: 14px; color: var(--cld-text2); margin-bottom: 4px; }
|
|
394
418
|
.upload-zone-hint { font-size: 12px; color: var(--cld-text3); }
|
|
395
419
|
.upload-zone-btn {
|
|
@@ -435,8 +459,9 @@ details.detail-section > summary.detail-section-title::-webkit-details-marker {
|
|
|
435
459
|
.upload-preview-icon {
|
|
436
460
|
width: 56px; height: 56px; border-radius: var(--cld-radius-sm);
|
|
437
461
|
background: var(--cld-bg3); flex-shrink: 0; display: flex;
|
|
438
|
-
align-items: center; justify-content: center;
|
|
462
|
+
align-items: center; justify-content: center; color: var(--cld-text3);
|
|
439
463
|
}
|
|
464
|
+
.upload-preview-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
440
465
|
.upload-preview-info { flex: 1; min-width: 0; }
|
|
441
466
|
.upload-preview-name { font-size: 13px; font-weight: 600; color: var(--cld-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
442
467
|
.upload-preview-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }
|
|
@@ -585,19 +610,15 @@ details.upload-section > .upload-form { margin: 0; padding: 10px 12px; }
|
|
|
585
610
|
background: var(--cld-accent-bg); border: 1px solid var(--cld-accent);
|
|
586
611
|
border-radius: var(--cld-radius); margin-bottom: 4px; position: relative;
|
|
587
612
|
}
|
|
588
|
-
.upload-staged-icon {
|
|
613
|
+
.upload-staged-icon { flex-shrink: 0; display: flex; align-items: center; color: var(--cld-accent); }
|
|
614
|
+
.upload-staged-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
589
615
|
.upload-staged-info { flex: 1; min-width: 0; }
|
|
590
616
|
.upload-staged-name {
|
|
591
617
|
font-size: 13px; font-weight: 600; color: var(--cld-text);
|
|
592
618
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
593
619
|
}
|
|
594
620
|
.upload-staged-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }
|
|
595
|
-
.upload-staged-clear {
|
|
596
|
-
background: none; border: none; cursor: pointer; font-size: 16px;
|
|
597
|
-
color: var(--cld-text3); padding: 4px 6px; border-radius: var(--cld-radius-sm);
|
|
598
|
-
transition: color 0.15s, background 0.15s; flex-shrink: 0;
|
|
599
|
-
}
|
|
600
|
-
.upload-staged-clear:hover { color: var(--cld-error); background: rgba(206,25,13,0.08); }
|
|
621
|
+
.upload-staged-clear:hover { color: var(--cld-error); border-color: var(--cld-error); background: rgba(206,25,13,0.08); }
|
|
601
622
|
|
|
602
623
|
/* Upload submit button */
|
|
603
624
|
.upload-submit {
|
|
@@ -645,6 +666,24 @@ details.upload-section > .upload-form { margin: 0; padding: 10px 12px; }
|
|
|
645
666
|
}
|
|
646
667
|
`;
|
|
647
668
|
|
|
669
|
+
// ── JS: SVG icon strings (Lucide-style, 24px viewBox, stroke-based) ──
|
|
670
|
+
export const SHARED_JS_ICONS = /* js */ `
|
|
671
|
+
var IC = {
|
|
672
|
+
refresh: '<svg viewBox="0 0 24 24"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/></svg>',
|
|
673
|
+
chevronLeft:'<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>',
|
|
674
|
+
arrowDown: '<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>',
|
|
675
|
+
x: '<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
|
|
676
|
+
zap: '<svg viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>',
|
|
677
|
+
uploadCloud:'<svg viewBox="0 0 24 24"><polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/></svg>',
|
|
678
|
+
alertTriangle:'<svg viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
|
|
679
|
+
folderOpen: '<svg viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><polyline points="22 13 17 13 15 16 9 16 7 13 2 13"/></svg>',
|
|
680
|
+
checkCircle:'<svg viewBox="0 0 24 24"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
|
|
681
|
+
clock: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>',
|
|
682
|
+
file: '<svg viewBox="0 0 24 24"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>',
|
|
683
|
+
image: '<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>',
|
|
684
|
+
};
|
|
685
|
+
`;
|
|
686
|
+
|
|
648
687
|
// ── JS: MCPApp client class ─────────────────────────────────────────
|
|
649
688
|
export const SHARED_JS_MCP_CLIENT = /* js */ `
|
|
650
689
|
var RPC_TIMEOUT_MS = 15000;
|
|
@@ -734,6 +773,29 @@ class MCPApp {
|
|
|
734
773
|
|
|
735
774
|
// ── JS: Helper functions ────────────────────────────────────────────
|
|
736
775
|
export const SHARED_JS_HELPERS = /* js */ `
|
|
776
|
+
function copyText(text) {
|
|
777
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
778
|
+
return navigator.clipboard.writeText(text).catch(function() { return _copyFallback(text); });
|
|
779
|
+
}
|
|
780
|
+
return _copyFallback(text);
|
|
781
|
+
}
|
|
782
|
+
function _copyFallback(text) {
|
|
783
|
+
return new Promise(function(resolve, reject) {
|
|
784
|
+
try {
|
|
785
|
+
var ta = document.createElement("textarea");
|
|
786
|
+
ta.value = text;
|
|
787
|
+
ta.setAttribute("readonly", "");
|
|
788
|
+
ta.style.position = "fixed"; ta.style.top = "0"; ta.style.left = "0";
|
|
789
|
+
ta.style.opacity = "0"; ta.style.pointerEvents = "none";
|
|
790
|
+
document.body.appendChild(ta);
|
|
791
|
+
ta.select(); ta.setSelectionRange(0, text.length);
|
|
792
|
+
var ok = document.execCommand("copy");
|
|
793
|
+
document.body.removeChild(ta);
|
|
794
|
+
if (ok) resolve(); else reject(new Error("Clipboard unavailable"));
|
|
795
|
+
} catch (e) { reject(e); }
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
737
799
|
function fmtBytes(b) {
|
|
738
800
|
if (!b) return "";
|
|
739
801
|
var u = ["B","KB","MB","GB"], i = Math.min(Math.floor(Math.log(b)/Math.log(1024)), 3);
|
|
@@ -954,6 +1016,55 @@ function closeModal() {
|
|
|
954
1016
|
if (ov) ov.remove();
|
|
955
1017
|
}
|
|
956
1018
|
|
|
1019
|
+
/* Position the modal overlay so it's centered on the part of the iframe
|
|
1020
|
+
* the user is currently looking at. Inside an iframe that the host has
|
|
1021
|
+
* sized to scrollHeight, position: fixed anchors to iframe-center, not
|
|
1022
|
+
* the user's visible band — so we compute the visible band ourselves
|
|
1023
|
+
* via IntersectionObserver and place the overlay absolutely there. */
|
|
1024
|
+
function positionModalInVisibleArea(overlay) {
|
|
1025
|
+
if (!overlay) return;
|
|
1026
|
+
var docH = Math.max(document.documentElement.scrollHeight, window.innerHeight);
|
|
1027
|
+
|
|
1028
|
+
// Try IntersectionObserver first (works cross-origin too)
|
|
1029
|
+
if (typeof IntersectionObserver !== "undefined") {
|
|
1030
|
+
var probe = document.createElement("div");
|
|
1031
|
+
probe.style.cssText = "position:absolute;left:0;top:0;width:1px;height:" + docH + "px;pointer-events:none;visibility:hidden;";
|
|
1032
|
+
document.body.appendChild(probe);
|
|
1033
|
+
var io = new IntersectionObserver(function(entries) {
|
|
1034
|
+
try {
|
|
1035
|
+
var entry = entries[0];
|
|
1036
|
+
var rect = entry.intersectionRect;
|
|
1037
|
+
var rootRect = entry.rootBounds || entry.boundingClientRect;
|
|
1038
|
+
// intersectionRect.top is in viewport coords; visible band of the
|
|
1039
|
+
// document is from (probe.top + rect.top) to (probe.top + rect.bottom).
|
|
1040
|
+
var probeTop = entry.boundingClientRect.top; // viewport-relative
|
|
1041
|
+
var visibleTop = rect.top - probeTop; // document-relative
|
|
1042
|
+
var visibleHeight = rect.height;
|
|
1043
|
+
if (visibleHeight > 0) {
|
|
1044
|
+
overlay.style.top = visibleTop + "px";
|
|
1045
|
+
overlay.style.height = visibleHeight + "px";
|
|
1046
|
+
} else {
|
|
1047
|
+
// Fallback: probe not intersecting (shouldn't happen with full-height probe)
|
|
1048
|
+
overlay.style.top = (window.scrollY || 0) + "px";
|
|
1049
|
+
overlay.style.height = window.innerHeight + "px";
|
|
1050
|
+
}
|
|
1051
|
+
} finally {
|
|
1052
|
+
io.disconnect();
|
|
1053
|
+
if (probe.parentNode) probe.parentNode.removeChild(probe);
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
io.observe(probe);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Fallback: same-origin only — read iframe's bounding rect
|
|
1061
|
+
var rect = document.documentElement.getBoundingClientRect();
|
|
1062
|
+
var visibleTop = rect.top < 0 ? -rect.top : 0;
|
|
1063
|
+
var visibleHeight = Math.min(window.innerHeight, rect.bottom) - Math.max(0, rect.top);
|
|
1064
|
+
overlay.style.top = visibleTop + "px";
|
|
1065
|
+
overlay.style.height = Math.max(visibleHeight, 200) + "px";
|
|
1066
|
+
}
|
|
1067
|
+
|
|
957
1068
|
function openModal(headerHtml, bodyHtml) {
|
|
958
1069
|
closeModal();
|
|
959
1070
|
var h = '<div class="modal-overlay"><div class="modal">';
|
|
@@ -963,6 +1074,7 @@ function openModal(headerHtml, bodyHtml) {
|
|
|
963
1074
|
document.body.insertAdjacentHTML("beforeend", h);
|
|
964
1075
|
|
|
965
1076
|
var overlay = document.querySelector(".modal-overlay");
|
|
1077
|
+
positionModalInVisibleArea(overlay);
|
|
966
1078
|
overlay.addEventListener("click", function(e) {
|
|
967
1079
|
if (e.target === overlay || e.target.classList.contains("modal-close")) {
|
|
968
1080
|
closeModal();
|
|
@@ -1966,7 +2078,16 @@ function renderThemeToggle() {
|
|
|
1966
2078
|
applyTheme();
|
|
1967
2079
|
renderThemeToggle();
|
|
1968
2080
|
});
|
|
1969
|
-
document.
|
|
2081
|
+
var slot = document.getElementById("header-actions");
|
|
2082
|
+
if (slot) {
|
|
2083
|
+
slot.appendChild(btn);
|
|
2084
|
+
} else {
|
|
2085
|
+
btn.style.position = "absolute";
|
|
2086
|
+
btn.style.top = "4px";
|
|
2087
|
+
btn.style.right = "4px";
|
|
2088
|
+
btn.style.zIndex = "900";
|
|
2089
|
+
document.body.appendChild(btn);
|
|
2090
|
+
}
|
|
1970
2091
|
}
|
|
1971
2092
|
|
|
1972
2093
|
function setupHostContext(app) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import {
|
|
10
10
|
SHARED_CSS_TOKENS,
|
|
11
11
|
SHARED_CSS_COMPONENTS,
|
|
12
|
+
SHARED_JS_ICONS,
|
|
12
13
|
SHARED_JS_MCP_CLIENT,
|
|
13
14
|
SHARED_JS_HELPERS,
|
|
14
15
|
SHARED_JS_TOOLTIPS,
|
|
@@ -59,16 +60,6 @@ const ASSET_DETAILS_CSS = /* css */ `
|
|
|
59
60
|
padding: 2px 7px; border-radius: 4px; border: 1px solid var(--cld-border);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
.open-link {
|
|
63
|
-
padding: 6px 14px; border-radius: var(--cld-radius-sm);
|
|
64
|
-
font-size: 12px; font-weight: 500; cursor: pointer;
|
|
65
|
-
border: 1px solid var(--cld-accent); background: transparent;
|
|
66
|
-
color: var(--cld-accent); font-family: inherit;
|
|
67
|
-
transition: background 0.15s;
|
|
68
|
-
white-space: nowrap; flex-shrink: 0;
|
|
69
|
-
}
|
|
70
|
-
.open-link:hover { background: var(--cld-accent-bg); }
|
|
71
|
-
|
|
72
63
|
.hero-container {
|
|
73
64
|
position: relative; margin-bottom: var(--cld-sp-md);
|
|
74
65
|
border-radius: var(--cld-radius); overflow: hidden;
|
|
@@ -131,9 +122,9 @@ function renderPage(r) {
|
|
|
131
122
|
if (dur) h += '<span class="pill">' + dur + "</span>";
|
|
132
123
|
if (size) h += '<span class="pill">' + size + "</span>";
|
|
133
124
|
h += "</div></div>";
|
|
134
|
-
h += '<div style="display:flex;gap:
|
|
135
|
-
h += '<button class="
|
|
136
|
-
|
|
125
|
+
h += '<div id="header-actions" style="display:flex;gap:8px;flex-shrink:0;align-items:center">';
|
|
126
|
+
if (url) h += '<button class="icon-btn" id="open-asset">Open</button>';
|
|
127
|
+
h += '<button class="icon-btn icon-only" id="refresh-asset" title="Refresh">' + IC.refresh + '</button>';
|
|
137
128
|
h += "</div>";
|
|
138
129
|
h += "</div>";
|
|
139
130
|
|
|
@@ -175,6 +166,7 @@ function renderPage(r) {
|
|
|
175
166
|
h += "</div>";
|
|
176
167
|
|
|
177
168
|
root.innerHTML = h;
|
|
169
|
+
renderThemeToggle();
|
|
178
170
|
|
|
179
171
|
// Event delegation
|
|
180
172
|
root.addEventListener("click", function handler(e) {
|
|
@@ -220,6 +212,7 @@ function showFetchPrompt() {
|
|
|
220
212
|
h += '<button class="prompt-btn prompt-btn-primary" id="fetch-direct-btn">Fetch Directly</button>';
|
|
221
213
|
h += "</div></div>";
|
|
222
214
|
root.innerHTML = h;
|
|
215
|
+
renderThemeToggle();
|
|
223
216
|
document.getElementById("fetch-direct-btn").addEventListener("click", function() { fetchDirect(); });
|
|
224
217
|
}
|
|
225
218
|
|
|
@@ -300,6 +293,7 @@ ${ASSET_DETAILS_CSS}
|
|
|
300
293
|
<div id="app"><div class="status">Loading asset details…</div></div>
|
|
301
294
|
|
|
302
295
|
<script>
|
|
296
|
+
${SHARED_JS_ICONS}
|
|
303
297
|
${SHARED_JS_MCP_CLIENT}
|
|
304
298
|
${SHARED_JS_HELPERS}
|
|
305
299
|
${SHARED_JS_TOOLTIPS}
|