@cloudinary/asset-management-mcp 0.9.2 → 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 +2 -2
- package/bin/mcp-server.js +86 -17
- package/bin/mcp-server.js.map +8 -8
- package/esm/landing-page.js +1 -1
- package/esm/lib/config.d.ts +2 -2
- package/esm/lib/config.js +2 -2
- package/esm/mcp-server/apps/app-shared.d.ts +3 -3
- package/esm/mcp-server/apps/app-shared.d.ts.map +1 -1
- package/esm/mcp-server/apps/app-shared.js +56 -3
- package/esm/mcp-server/apps/app-shared.js.map +1 -1
- package/esm/mcp-server/apps/asset-gallery-app.js +24 -8
- package/esm/mcp-server/apps/asset-gallery-app.js.map +1 -1
- package/esm/mcp-server/mcp-server.js +1 -1
- package/esm/mcp-server/server.js +1 -1
- package/package.json +1 -1
- package/src/landing-page.ts +1 -1
- package/src/lib/config.ts +2 -2
- package/src/mcp-server/apps/app-shared.ts +56 -3
- package/src/mcp-server/apps/asset-gallery-app.ts +24 -8
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.ts +1 -1
package/esm/landing-page.js
CHANGED
|
@@ -924,7 +924,7 @@ http_headers = { "api-key" = "YOUR_API_KEY", "api-secret" = "YOUR_API_SECRET", "
|
|
|
924
924
|
<h1>Instructions</h1>
|
|
925
925
|
<p>One-click installation for Claude Desktop users</p>
|
|
926
926
|
<div class="instruction-item">
|
|
927
|
-
<a href="https://github.com/cloudinary/asset-management-mcp/releases/download/v0.9.
|
|
927
|
+
<a href="https://github.com/cloudinary/asset-management-mcp/releases/download/v0.9.3/mcp-server.mcpb" download="mcp-server.mcpb" class="action-button header-action" style="display: inline-flex; margin-bottom: 16px;">
|
|
928
928
|
📥 Download MCP Bundle
|
|
929
929
|
</a>
|
|
930
930
|
</div>
|
package/esm/lib/config.d.ts
CHANGED
|
@@ -66,8 +66,8 @@ export declare function serverURLFromOptions(options: SDKOptions): URL | null;
|
|
|
66
66
|
export declare const SDK_METADATA: {
|
|
67
67
|
readonly language: "typescript";
|
|
68
68
|
readonly openapiDocVersion: "0.5.1";
|
|
69
|
-
readonly sdkVersion: "0.9.
|
|
69
|
+
readonly sdkVersion: "0.9.3";
|
|
70
70
|
readonly genVersion: "2.885.1";
|
|
71
|
-
readonly userAgent: "speakeasy-sdk/mcp-typescript 0.9.
|
|
71
|
+
readonly userAgent: "speakeasy-sdk/mcp-typescript 0.9.3 2.885.1 0.5.1 @cloudinary/asset-management-mcp";
|
|
72
72
|
};
|
|
73
73
|
//# sourceMappingURL=config.d.ts.map
|
package/esm/lib/config.js
CHANGED
|
@@ -55,8 +55,8 @@ export function serverURLFromOptions(options) {
|
|
|
55
55
|
export const SDK_METADATA = {
|
|
56
56
|
language: "typescript",
|
|
57
57
|
openapiDocVersion: "0.5.1",
|
|
58
|
-
sdkVersion: "0.9.
|
|
58
|
+
sdkVersion: "0.9.3",
|
|
59
59
|
genVersion: "2.885.1",
|
|
60
|
-
userAgent: "speakeasy-sdk/mcp-typescript 0.9.
|
|
60
|
+
userAgent: "speakeasy-sdk/mcp-typescript 0.9.3 2.885.1 0.5.1 @cloudinary/asset-management-mcp",
|
|
61
61
|
};
|
|
62
62
|
//# sourceMappingURL=config.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export declare const SHARED_CSS_TOKENS = "\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n:root {\n --cld-primary: #3448c5;\n --cld-primary-light: #4c64d7;\n --cld-bg: #ffffff;\n --cld-bg2: #f9fafb;\n --cld-bg3: #f3f4f7;\n --cld-bg4: #edeef3;\n --cld-text: #0a0c0f;\n --cld-text2: #333b4c;\n --cld-text3: #90a0b3;\n --cld-border: #d1d6e0;\n --cld-border2: #c2c9d6;\n --cld-accent: #3448c5;\n --cld-accent-bg: #f1f2f9;\n --cld-error: #CE190D;\n --cld-warning: #ff620c;\n --cld-success: #22AA00;\n --cld-radius: 8px;\n --cld-radius-sm: 4px;\n --cld-radius-lg: 16px;\n --cld-shadow-sm: 0 2px 4px 0 rgba(0,0,0,0.25);\n --cld-shadow-md: 0 4px 5px 0 rgba(0,0,0,0.2), 0 3px 14px 3px rgba(0,0,0,0.12), 0 8px 10px 1px rgba(0,0,0,0.14);\n --cld-shadow-lg: 0 24px 24px 0 rgba(0,0,0,0.3), 0 0 24px 0 rgba(0,0,0,0.22);\n --cld-sp-xxs: 0.25rem;\n --cld-sp-xs: 0.5rem;\n --cld-sp-sm: 0.75rem;\n --cld-sp-md: 1rem;\n --cld-sp-lg: 1.25rem;\n --cld-sp-xl: 2rem;\n --cld-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --cld-font-xxs: 0.75rem;\n --cld-font-xs: 0.875rem;\n --cld-font-sm: 1rem;\n --cld-chip-tag-bg: #f1f2f9;\n --cld-chip-tag-fg: #3448c5;\n --cld-chip-set-bg: #e6faf6;\n --cld-chip-set-fg: #13a5aa;\n --cld-chip-set-border: #b2e8e9;\n --cld-chip-date-bg: #fff8eb;\n --cld-chip-date-fg: #a16207;\n --cld-chip-date-border: #fde68a;\n --cld-chip-int-bg: #f5f0ff;\n --cld-chip-int-fg: #7c3aed;\n --cld-chip-int-border: #e9d5ff;\n}\n\n[data-theme=\"dark\"], .dark {\n --cld-primary: #0D9AFF;\n --cld-primary-light: #51a3ff;\n --cld-bg: #1f242e;\n --cld-bg2: #14181e;\n --cld-bg3: #090c0f;\n --cld-bg4: #000000;\n --cld-text: #ffffff;\n --cld-text2: #d1d6e0;\n --cld-text3: #90a0b3;\n --cld-border: #3d475c;\n --cld-border2: #535f7a;\n --cld-accent: #0D9AFF;\n --cld-accent-bg: rgba(13,154,255,0.12);\n --cld-error: #ff5959;\n --cld-warning: #ffa359;\n --cld-success: #9affa6;\n --cld-chip-tag-bg: rgba(13,154,255,0.15);\n --cld-chip-tag-fg: #0D9AFF;\n --cld-chip-set-bg: rgba(72,208,216,0.15);\n --cld-chip-set-fg: #7dedff;\n --cld-chip-set-border: rgba(72,208,216,0.3);\n --cld-chip-date-bg: rgba(255,196,121,0.15);\n --cld-chip-date-fg: #ffc479;\n --cld-chip-date-border: rgba(255,196,121,0.3);\n --cld-chip-int-bg: rgba(167,111,255,0.15);\n --cld-chip-int-fg: #a76fff;\n --cld-chip-int-border: rgba(167,111,255,0.3);\n}\n[data-theme=\"dark\"] .status-ok, .dark .status-ok { background: #166534; color: #bbf7d0; }\n[data-theme=\"dark\"] .status-warn, .dark .status-warn { background: #854d0e; color: #fef08a; }\n[data-theme=\"dark\"] .status-err, .dark .status-err { background: #991b1b; color: #fecaca; }\n\nhtml {
|
|
2
|
-
export declare const SHARED_CSS_COMPONENTS = "\n.link { cursor: pointer; }\n.link:hover { color: var(--cld-accent); text-decoration: underline; }\n\n/* Modal */\n.modal-overlay {\n position: fixed; inset: 0;\n background: rgba(0,0,0,0.45);\n display: flex; align-items: center; justify-content: center;\n z-index: 1000; backdrop-filter: blur(3px); padding: 24px;\n}\n.modal {\n background: var(--cld-bg); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius); width: 100%; max-width: 620px;\n max-height: 85vh; display: flex; flex-direction: column;\n box-shadow: var(--cld-shadow-lg); animation: modalIn 0.15s ease-out;\n}\n@keyframes modalIn {\n from { opacity: 0; transform: scale(0.96) translateY(8px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n}\n.modal-header {\n display: flex; align-items: center; gap: 12px;\n padding: 16px 20px; border-bottom: 1px solid var(--cld-border); flex-shrink: 0;\n}\n.modal-header-thumb {\n width: 40px; height: 40px; border-radius: 6px;\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.modal-header-info { flex: 1; min-width: 0; }\n.modal-header-info h2 {\n font-size: 14px; font-weight: 600;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.modal-header-sub { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.modal-close {\n background: var(--cld-bg3); border: 1px solid var(--cld-border);\n width: 28px; height: 28px; border-radius: 6px; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 16px; color: var(--cld-text2); flex-shrink: 0; font-family: inherit;\n}\n.modal-close:hover { background: var(--cld-border); }\n.modal-body { overflow-y: auto; padding: 0; }\n.modal-hero {\n width: 100%; max-height: 220px; object-fit: contain;\n background: var(--cld-bg3); display: block;\n}\n.modal-loading { text-align: center; padding: 48px 20px; color: var(--cld-text2); font-size: 13px; }\n.modal-loading .spinner {\n display: inline-block; width: 24px; height: 24px;\n border: 2.5px solid var(--cld-border); border-top-color: var(--cld-accent);\n border-radius: 50%; animation: spin 0.6s linear infinite; margin-bottom: 10px;\n}\n@keyframes spin { to { transform: rotate(360deg); } }\n\n/* Detail sections */\n.detail-section { padding: 14px 20px; border-bottom: 1px solid var(--cld-bg3); }\n.detail-section:last-child { border-bottom: none; }\n.detail-section-title {\n font-size: 10px; font-weight: 700; text-transform: uppercase;\n letter-spacing: 0.8px; color: var(--cld-text3); margin-bottom: 10px;\n display: flex; align-items: center; gap: 6px;\n}\n.detail-section-title .count {\n background: var(--cld-bg3); padding: 1px 6px; border-radius: 8px;\n font-size: 10px; font-weight: 600; color: var(--cld-text2);\n}\ndetails.detail-section > summary.detail-section-title {\n cursor: pointer; list-style: none; user-select: none;\n}\ndetails.detail-section > summary.detail-section-title::before {\n content: \"\\25B6\"; display: inline-block; width: 14px; font-size: 9px;\n transition: transform 0.15s ease; margin-right: 4px;\n}\ndetails.detail-section[open] > summary.detail-section-title::before {\n transform: rotate(90deg);\n}\ndetails.detail-section > summary.detail-section-title::-webkit-details-marker { display: none; }\n.detail-grid {\n display: grid; grid-template-columns: 1fr 1fr; gap: 1px;\n background: var(--cld-bg3); border-radius: var(--cld-radius-sm); overflow: hidden;\n}\n.detail-cell { background: var(--cld-bg); padding: 8px 12px; }\n.detail-cell-key { font-size: 10px; color: var(--cld-text3); font-weight: 500; margin-bottom: 2px; }\n.detail-cell-val { font-size: 12px; color: var(--cld-text); font-weight: 500; word-break: break-all; }\n.detail-cell-val.link-val { color: var(--cld-accent); cursor: pointer; }\n.detail-cell-val.link-val:hover { text-decoration: underline; }\n.detail-cell.full-width { grid-column: 1 / -1; }\n\n/* Chips */\n.chip-list { display: flex; flex-wrap: wrap; gap: 5px; }\n.chip { font-size: 11px; padding: 3px 10px; border-radius: 12px; font-weight: 500; white-space: nowrap; }\n.chip-tag { background: var(--cld-chip-tag-bg); color: var(--cld-chip-tag-fg); }\n.chip-set { background: var(--cld-chip-set-bg); color: var(--cld-chip-set-fg); border: 1px solid var(--cld-chip-set-border); }\n.chip-date { background: var(--cld-chip-date-bg); color: var(--cld-chip-date-fg); border: 1px solid var(--cld-chip-date-border); }\n.chip-int { background: var(--cld-chip-int-bg); color: var(--cld-chip-int-fg); border: 1px solid var(--cld-chip-int-border); }\n\n/* Meta rows */\n.meta-row {\n display: flex; align-items: baseline; padding: 6px 0;\n border-bottom: 1px solid var(--cld-bg3); gap: 8px; font-size: 12px;\n}\n.meta-row:last-child { border-bottom: none; }\n.meta-key {\n color: var(--cld-text2); min-width: 0; flex-shrink: 0; max-width: 45%;\n font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px;\n}\n.meta-val { color: var(--cld-text); flex: 1; min-width: 0; text-align: right; }\n\n/* Derived assets */\n.derived-card {\n display: flex; align-items: center; gap: 10px; padding: 8px 0;\n border-bottom: 1px solid var(--cld-bg3); font-size: 11px;\n}\n.derived-card:last-child { border-bottom: none; }\n.derived-thumb {\n width: 48px; height: 36px; border-radius: 4px;\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.derived-info { flex: 1; min-width: 0; }\n.derived-tx { color: var(--cld-text); font-family: monospace; font-size: 10px; word-break: break-all; }\n.derived-meta { color: var(--cld-text3); font-size: 10px; margin-top: 2px; }\n.derived-open { color: var(--cld-accent); cursor: pointer; font-weight: 500; white-space: nowrap; font-size: 11px; }\n.derived-open:hover { text-decoration: underline; }\n\n/* Error */\n.modal-error { text-align: center; padding: 32px 20px; color: var(--cld-text2); font-size: 13px; }\n\n/* Status / loading */\n.status { text-align: center; padding: 48px 16px; color: var(--cld-text2); font-size: 14px; }\n.status .icon { font-size: 32px; margin-bottom: 8px; }\n\n/* Fetch prompt */\n.prompt { text-align: center; padding: 48px 24px; color: var(--cld-text2); }\n.prompt-icon { font-size: 36px; margin-bottom: 12px; }\n.prompt-title { font-size: 15px; font-weight: 600; color: var(--cld-text); margin-bottom: 6px; }\n.prompt-desc { font-size: 13px; max-width: 420px; margin: 0 auto 20px; line-height: 1.5; }\n.prompt-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }\n.prompt-btn {\n padding: 8px 20px; border-radius: var(--cld-radius); font-size: 13px; font-weight: 500;\n cursor: pointer; border: 1px solid var(--cld-border); background: var(--cld-bg2);\n color: var(--cld-text); font-family: inherit; transition: background 0.15s, border-color 0.15s;\n}\n.prompt-btn:hover { background: var(--cld-bg3); border-color: var(--cld-border2); }\n.prompt-btn-primary { background: var(--cld-primary); color: #fff; border-color: var(--cld-primary); }\n.prompt-btn-primary:hover { background: var(--cld-primary-light); border-color: var(--cld-primary-light); }\n\n/* Error toast */\n.error-toast {\n position: fixed; bottom: 16px; left: 16px; right: 16px;\n background: var(--cld-error); color: #fff; padding: 12px 16px;\n border-radius: var(--cld-radius); box-shadow: var(--cld-shadow-md);\n font-size: 13px; z-index: 2000; display: flex; align-items: flex-start;\n gap: 10px; animation: toastIn 0.2s ease-out; max-width: 600px; margin: 0 auto;\n}\n.error-toast-icon { font-size: 18px; flex-shrink: 0; line-height: 1; }\n.error-toast-body { flex: 1; min-width: 0; }\n.error-toast-title { font-weight: 600; margin-bottom: 2px; }\n.error-toast-msg { font-size: 12px; opacity: 0.9; word-break: break-word; }\n.error-toast-close {\n background: none; border: none; color: #fff; cursor: pointer;\n font-size: 16px; opacity: 0.8; padding: 0 2px; flex-shrink: 0; font-family: inherit;\n}\n.error-toast-close:hover { opacity: 1; }\n@keyframes toastIn {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Thumb overlays */\n.thumb-overlay {\n position: absolute; inset: 0; display: flex;\n align-items: center; justify-content: center; pointer-events: none;\n}\n.play-icon {\n width: 40px; height: 40px; background: rgba(0,0,0,0.55);\n border-radius: 50%; display: flex; align-items: center; justify-content: center;\n}\n.play-icon::after {\n content: \"\"; display: block; width: 0; height: 0;\n border-style: solid; border-width: 8px 0 8px 14px;\n border-color: transparent transparent transparent #fff; margin-left: 3px;\n}\n.audio-icon {\n width: 40px; height: 40px; background: rgba(0,0,0,0.55);\n border-radius: 50%; display: flex; align-items: center; justify-content: center;\n color: #fff; font-size: 18px;\n}\n.duration-badge {\n position: absolute; bottom: 6px; right: 6px;\n background: rgba(0,0,0,0.7); color: #fff; font-size: 10px;\n font-weight: 600; padding: 2px 6px; border-radius: 4px;\n font-variant-numeric: tabular-nums; backdrop-filter: blur(4px);\n}\n.status-badge {\n display: inline-block; font-size: 11px; font-weight: 600;\n padding: 1px 8px; border-radius: 10px; text-transform: capitalize;\n}\n.status-ok { background: #dcfce7; color: #166534; }\n.status-warn { background: #fef9c3; color: #854d0e; }\n.status-err { background: #fee2e2; color: #991b1b; }\n.file-icon {\n display: flex; flex-direction: column; align-items: center;\n justify-content: center; gap: 4px; color: var(--cld-text3);\n}\n.file-icon svg { width: 36px; height: 36px; }\n.file-icon-label { font-size: 10px; font-weight: 600; text-transform: uppercase; }\n\n/* Native media players */\n.hero-video {\n width: 100%; max-height: 300px; display: block;\n background: #000; border-radius: 0;\n}\n.hero-audio-wrap {\n position: relative; padding: 20px;\n background: var(--cld-bg3); display: flex; flex-direction: column;\n align-items: center; gap: 12px;\n}\n.hero-audio-waveform {\n width: 100%; max-height: 120px; object-fit: contain; display: block;\n opacity: 0.6; border-radius: var(--cld-radius-sm);\n}\n.hero-audio-wrap audio { width: 100%; max-width: 500px; }\n.hero-audio-note {\n font-size: 28px; color: var(--cld-text3); margin-bottom: 4px;\n}\n.media-modal-video {\n width: 100%; display: block; background: #000;\n max-height: 60vh;\n}\n.media-modal-audio-wrap {\n padding: 24px 20px; background: var(--cld-bg3);\n display: flex; flex-direction: column; align-items: center; gap: 12px;\n}\n.media-modal-audio-wrap img {\n width: 100%; max-height: 100px; object-fit: contain; opacity: 0.6;\n border-radius: var(--cld-radius-sm);\n}\n.media-modal-audio-wrap audio { width: 100%; max-width: 480px; }\n.thumb-overlay.playable { pointer-events: auto; cursor: pointer; }\n\n/* Upload app */\n.upload-zone {\n border: 2px dashed var(--cld-border2); border-radius: var(--cld-radius);\n padding: 40px 24px; text-align: center; cursor: pointer;\n transition: border-color 0.2s, background 0.2s;\n background: var(--cld-bg2);\n}\n.upload-zone:hover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }\n.upload-zone.dragover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }\n.upload-zone-icon { margin-bottom: 8px; color: var(--cld-text3); display: flex; align-items: center; justify-content: center; }\n.upload-zone-icon svg { width: 36px; height: 36px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-zone-text { font-size: 14px; color: var(--cld-text2); margin-bottom: 4px; }\n.upload-zone-hint { font-size: 12px; color: var(--cld-text3); }\n.upload-zone-btn {\n display: inline-block; margin-top: 14px; padding: 8px 22px;\n border-radius: var(--cld-radius); font-size: 13px; font-weight: 500;\n cursor: pointer; border: 1px solid var(--cld-accent); background: var(--cld-accent);\n color: #fff; font-family: inherit; transition: background 0.15s;\n}\n.upload-zone-btn:hover { background: var(--cld-primary-light); border-color: var(--cld-primary-light); }\n.upload-or { margin: 16px 0; font-size: 12px; color: var(--cld-text3); display: flex; align-items: center; gap: 10px; }\n.upload-or::before, .upload-or::after { content: \"\"; flex: 1; height: 1px; background: var(--cld-border); }\n.upload-url-row { display: flex; gap: 8px; }\n.upload-url-input {\n flex: 1; padding: 8px 12px; border-radius: var(--cld-radius-sm);\n border: 1px solid var(--cld-border); background: var(--cld-bg);\n color: var(--cld-text); font-size: 13px; font-family: inherit; outline: none;\n}\n.upload-url-input:focus { border-color: var(--cld-accent); }\n.upload-url-input::placeholder { color: var(--cld-text3); }\n.upload-url-btn {\n padding: 8px 16px; border-radius: var(--cld-radius-sm);\n font-size: 13px; font-weight: 500; cursor: pointer;\n border: 1px solid var(--cld-accent); background: transparent;\n color: var(--cld-accent); font-family: inherit; transition: background 0.15s;\n white-space: nowrap;\n}\n.upload-url-btn:hover { background: var(--cld-accent-bg); }\n.upload-params {\n margin-top: 16px; padding: 12px 16px; background: var(--cld-bg3);\n border-radius: var(--cld-radius-sm); font-size: 12px; color: var(--cld-text2);\n}\n.upload-params-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--cld-text3); margin-bottom: 6px; }\n.upload-params .chip { margin-right: 4px; margin-bottom: 4px; }\n.upload-preview {\n display: flex; align-items: center; gap: 14px; padding: 16px;\n background: var(--cld-bg2); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius); margin-bottom: 16px;\n}\n.upload-preview-thumb {\n width: 56px; height: 56px; border-radius: var(--cld-radius-sm);\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.upload-preview-icon {\n width: 56px; height: 56px; border-radius: var(--cld-radius-sm);\n background: var(--cld-bg3); flex-shrink: 0; display: flex;\n align-items: center; justify-content: center; color: var(--cld-text3);\n}\n.upload-preview-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-preview-info { flex: 1; min-width: 0; }\n.upload-preview-name { font-size: 13px; font-weight: 600; color: var(--cld-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.upload-preview-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.upload-progress-wrap { margin-top: 8px; }\n.upload-progress-bar {\n height: 6px; border-radius: 3px; background: var(--cld-bg3); overflow: hidden;\n}\n.upload-progress-fill {\n height: 100%; background: var(--cld-accent); border-radius: 3px;\n transition: width 0.3s ease; width: 0%;\n}\n.upload-progress-text { font-size: 11px; color: var(--cld-text3); margin-top: 4px; text-align: center; }\n.upload-error-msg {\n background: #fef2f2; border: 1px solid #fecaca; border-radius: var(--cld-radius);\n color: #991b1b; padding: 10px 14px; font-size: 12px; margin-top: 8px;\n word-break: break-word; line-height: 1.5;\n}\n.upload-result {\n border: 1px solid var(--cld-border); border-radius: var(--cld-radius);\n overflow: hidden;\n}\n.upload-result-hero {\n position: relative; background: var(--cld-bg3);\n display: flex; align-items: center; justify-content: center; min-height: 120px;\n}\n.upload-result-hero img { width: 100%; max-height: 260px; object-fit: contain; display: block; }\n.upload-result-hero .file-icon { padding: 30px 20px; }\n.upload-result-body { padding: 16px; }\n.upload-result-title {\n font-size: 15px; font-weight: 600; color: var(--cld-text); margin-bottom: 12px;\n display: flex; align-items: center; gap: 8px;\n}\n.upload-result-title .success-icon { color: var(--cld-success); font-size: 18px; }\n.upload-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }\n.upload-actions .prompt-btn { font-size: 12px; padding: 6px 16px; }\n\n/* Upload form fields */\n.upload-form {\n margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n.upload-field { display: flex; flex-direction: column; gap: 3px; }\n.upload-field.full-width { grid-column: 1 / -1; }\n.upload-field label {\n font-size: 10px; font-weight: 700; text-transform: uppercase;\n letter-spacing: 0.6px; color: var(--cld-text3);\n}\n.upload-field input[type=\"text\"],\n.upload-field input[type=\"number\"],\n.upload-field select,\n.upload-field textarea {\n padding: 7px 10px; border-radius: var(--cld-radius-sm);\n border: 1px solid var(--cld-border); background: var(--cld-bg);\n color: var(--cld-text); font-size: 13px; font-family: inherit; outline: none;\n width: 100%;\n}\n.upload-field input[type=\"text\"]:focus,\n.upload-field input[type=\"number\"]:focus,\n.upload-field select:focus,\n.upload-field textarea:focus { border-color: var(--cld-accent); }\n.upload-field input::placeholder,\n.upload-field textarea::placeholder { color: var(--cld-text3); }\n.upload-field textarea { resize: vertical; min-height: 36px; }\n.upload-field select { cursor: pointer; appearance: auto; }\n\n/* Help icon (\"?\") with inline bubble */\n.help-toggle {\n display: inline-flex; align-items: center; justify-content: center;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--cld-border); color: var(--cld-text2);\n font-size: 9px; font-weight: 700; cursor: pointer;\n position: relative; vertical-align: middle; margin-left: 4px;\n user-select: none; line-height: 1; flex-shrink: 0;\n}\n.help-toggle:hover, .help-toggle:focus { background: var(--cld-accent); color: #fff; }\n.help-bubble {\n display: none; position: absolute; left: 50%; bottom: calc(100% + 6px);\n transform: translateX(-50%); width: 220px; padding: 8px 10px;\n background: var(--cld-text); color: var(--cld-bg); font-size: 11px;\n font-weight: 400; line-height: 1.4; border-radius: var(--cld-radius-sm);\n box-shadow: var(--cld-shadow-sm); z-index: 200; text-transform: none;\n letter-spacing: 0; white-space: normal; pointer-events: none;\n}\n.help-bubble::after {\n content: \"\"; position: absolute; top: 100%; left: 50%;\n transform: translateX(-50%); border: 5px solid transparent;\n border-top-color: var(--cld-text);\n}\n.help-toggle:hover .help-bubble,\n.help-toggle:focus .help-bubble { display: block; }\n.detail-grid .help-toggle .help-bubble,\n.detail-section-title .help-toggle .help-bubble {\n left: -4px; transform: none;\n}\n.detail-grid .help-toggle .help-bubble::after,\n.detail-section-title .help-toggle .help-bubble::after {\n left: 10px; transform: none;\n}\n.color-swatch {\n display: inline-flex; flex-direction: column; align-items: center; gap: 2px; text-align: center;\n}\n.color-swatch-box {\n width: 28px; height: 28px; border-radius: 4px; border: 1px solid var(--cld-border);\n}\n.color-swatch-label { font-size: 9px; color: var(--cld-text3); }\n.color-swatch-pct { font-size: 9px; color: var(--cld-text2); }\n.color-group-title {\n font-size: 10px; font-weight: 600; color: var(--cld-text3);\n text-transform: uppercase; margin: 6px 0 4px;\n}\n.color-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }\n\n/* Checkbox field variant */\n.upload-field-check {\n flex-direction: row; align-items: center; gap: 6px;\n}\n.upload-field-check label {\n display: flex; align-items: center; gap: 6px; cursor: pointer;\n text-transform: none; font-weight: 500; font-size: 12px; color: var(--cld-text2);\n}\n.upload-field-check input[type=\"checkbox\"] {\n width: 15px; height: 15px; cursor: pointer; accent-color: var(--cld-accent);\n}\n\n/* Collapsible sections */\ndetails.upload-section {\n margin-top: 14px; border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius-sm); overflow: hidden;\n}\ndetails.upload-section summary {\n padding: 8px 12px; font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: 0.6px; color: var(--cld-text3);\n cursor: pointer; list-style: none; display: flex; align-items: center; gap: 6px;\n background: var(--cld-bg2); user-select: none;\n}\ndetails.upload-section summary::before {\n content: \"\\25B6\"; font-size: 8px; transition: transform 0.15s;\n}\ndetails.upload-section[open] summary::before { transform: rotate(90deg); }\ndetails.upload-section summary::-webkit-details-marker { display: none; }\ndetails.upload-section > .upload-form { margin: 0; padding: 10px 12px; }\n\n/* Staged file preview */\n.upload-staged {\n display: flex; align-items: center; gap: 14px; padding: 14px 16px;\n background: var(--cld-accent-bg); border: 1px solid var(--cld-accent);\n border-radius: var(--cld-radius); margin-bottom: 4px; position: relative;\n}\n.upload-staged-icon { flex-shrink: 0; display: flex; align-items: center; color: var(--cld-accent); }\n.upload-staged-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-staged-info { flex: 1; min-width: 0; }\n.upload-staged-name {\n font-size: 13px; font-weight: 600; color: var(--cld-text);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.upload-staged-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.upload-staged-clear:hover { color: var(--cld-error); border-color: var(--cld-error); background: rgba(206,25,13,0.08); }\n\n/* Upload submit button */\n.upload-submit {\n position: sticky; bottom: 0; z-index: 10;\n background: var(--cld-bg); border-top: 1px solid var(--cld-border);\n padding: 12px 0; margin-top: 16px; text-align: center;\n}\n.upload-submit-btn {\n padding: 10px 32px !important; font-size: 14px !important; font-weight: 600 !important;\n}\n\n/* Combobox (folder picker) */\n.combo-wrap { position: relative; }\n.combo-dropdown {\n display: none; position: absolute; top: 100%; left: 0; right: 0;\n max-height: 180px; overflow-y: auto; z-index: 100;\n background: var(--cld-bg); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius-sm); box-shadow: var(--cld-shadow-sm);\n margin-top: 2px;\n}\n.combo-item {\n padding: 7px 10px; font-size: 13px; color: var(--cld-text);\n cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.combo-item:hover, .combo-item-active { background: var(--cld-accent-bg); color: var(--cld-accent); }\n\n.raw-response-pre {\n margin: 0; padding: 10px 12px; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 11px; line-height: 1.45; white-space: pre-wrap; word-break: break-all;\n background: var(--cld-bg2); border-radius: 6px; color: var(--cld-text2);\n max-height: 500px; overflow-y: auto;\n}\n.json-key { color: #881391; }\n.json-str { color: #0b7285; }\n.json-num { color: #c92a2a; }\n.json-bool { color: #5c940d; }\n.json-null { color: #868e96; }\n@media (prefers-color-scheme: dark) {\n .json-key { color: #da77f2; }\n .json-str { color: #66d9e8; }\n .json-num { color: #ff8787; }\n .json-bool { color: #a9e34b; }\n .json-null { color: #868e96; }\n}\n";
|
|
1
|
+
export declare const SHARED_CSS_TOKENS = "\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n:root {\n --cld-primary: #3448c5;\n --cld-primary-light: #4c64d7;\n --cld-bg: #ffffff;\n --cld-bg2: #f9fafb;\n --cld-bg3: #f3f4f7;\n --cld-bg4: #edeef3;\n --cld-text: #0a0c0f;\n --cld-text2: #333b4c;\n --cld-text3: #90a0b3;\n --cld-border: #d1d6e0;\n --cld-border2: #c2c9d6;\n --cld-accent: #3448c5;\n --cld-accent-bg: #f1f2f9;\n --cld-error: #CE190D;\n --cld-warning: #ff620c;\n --cld-success: #22AA00;\n --cld-radius: 8px;\n --cld-radius-sm: 4px;\n --cld-radius-lg: 16px;\n --cld-shadow-sm: 0 2px 4px 0 rgba(0,0,0,0.25);\n --cld-shadow-md: 0 4px 5px 0 rgba(0,0,0,0.2), 0 3px 14px 3px rgba(0,0,0,0.12), 0 8px 10px 1px rgba(0,0,0,0.14);\n --cld-shadow-lg: 0 24px 24px 0 rgba(0,0,0,0.3), 0 0 24px 0 rgba(0,0,0,0.22);\n --cld-sp-xxs: 0.25rem;\n --cld-sp-xs: 0.5rem;\n --cld-sp-sm: 0.75rem;\n --cld-sp-md: 1rem;\n --cld-sp-lg: 1.25rem;\n --cld-sp-xl: 2rem;\n --cld-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --cld-font-xxs: 0.75rem;\n --cld-font-xs: 0.875rem;\n --cld-font-sm: 1rem;\n --cld-chip-tag-bg: #f1f2f9;\n --cld-chip-tag-fg: #3448c5;\n --cld-chip-set-bg: #e6faf6;\n --cld-chip-set-fg: #13a5aa;\n --cld-chip-set-border: #b2e8e9;\n --cld-chip-date-bg: #fff8eb;\n --cld-chip-date-fg: #a16207;\n --cld-chip-date-border: #fde68a;\n --cld-chip-int-bg: #f5f0ff;\n --cld-chip-int-fg: #7c3aed;\n --cld-chip-int-border: #e9d5ff;\n}\n\n[data-theme=\"dark\"], .dark {\n --cld-primary: #0D9AFF;\n --cld-primary-light: #51a3ff;\n --cld-bg: #1f242e;\n --cld-bg2: #14181e;\n --cld-bg3: #090c0f;\n --cld-bg4: #000000;\n --cld-text: #ffffff;\n --cld-text2: #d1d6e0;\n --cld-text3: #90a0b3;\n --cld-border: #3d475c;\n --cld-border2: #535f7a;\n --cld-accent: #0D9AFF;\n --cld-accent-bg: rgba(13,154,255,0.12);\n --cld-error: #ff5959;\n --cld-warning: #ffa359;\n --cld-success: #9affa6;\n --cld-chip-tag-bg: rgba(13,154,255,0.15);\n --cld-chip-tag-fg: #0D9AFF;\n --cld-chip-set-bg: rgba(72,208,216,0.15);\n --cld-chip-set-fg: #7dedff;\n --cld-chip-set-border: rgba(72,208,216,0.3);\n --cld-chip-date-bg: rgba(255,196,121,0.15);\n --cld-chip-date-fg: #ffc479;\n --cld-chip-date-border: rgba(255,196,121,0.3);\n --cld-chip-int-bg: rgba(167,111,255,0.15);\n --cld-chip-int-fg: #a76fff;\n --cld-chip-int-border: rgba(167,111,255,0.3);\n}\n[data-theme=\"dark\"] .status-ok, .dark .status-ok { background: #166534; color: #bbf7d0; }\n[data-theme=\"dark\"] .status-warn, .dark .status-warn { background: #854d0e; color: #fef08a; }\n[data-theme=\"dark\"] .status-err, .dark .status-err { background: #991b1b; color: #fecaca; }\n\nhtml { scrollbar-gutter: auto; }\nbody {\n font-family: var(--cld-font);\n background: var(--cld-bg);\n color: var(--cld-text);\n padding: var(--cld-sp-md);\n line-height: 1.5;\n font-size: var(--cld-font-xs);\n position: relative;\n overflow-x: hidden;\n}\n.theme-btn {\n width: 22px; height: 22px; border-radius: 50%;\n border: 1px solid transparent; background: transparent;\n color: var(--cld-text3); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n padding: 0; transition: background 0.15s, color 0.15s, border-color 0.15s;\n opacity: 0.5; flex-shrink: 0;\n}\n.theme-btn:hover { background: var(--cld-bg3); color: var(--cld-text); border-color: var(--cld-border); opacity: 1; }\n.theme-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }\n/* Shared icon-button \u2014 square pill, same family as theme-btn */\n.icon-btn {\n display: inline-flex; align-items: center; justify-content: center; gap: 5px;\n background: none; border: 1px solid var(--cld-border); border-radius: var(--cld-radius-sm);\n color: var(--cld-text2); cursor: pointer; padding: 4px 8px;\n font-size: 12px; font-weight: 500; font-family: inherit; line-height: 1;\n transition: background 0.15s, color 0.15s, border-color 0.15s;\n white-space: nowrap; flex-shrink: 0;\n}\n.icon-btn:hover { background: var(--cld-bg3); color: var(--cld-text); border-color: var(--cld-border2); }\n.icon-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }\n/* icon-only variant (no text label) */\n.icon-btn.icon-only { padding: 4px; width: 28px; height: 28px; }\n/* header state icon (decorative, not a button) */\n.upload-header-icon { display: flex; align-items: center; justify-content: center; flex-shrink: 0; }\n.upload-header-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }\n.upload-header-icon.icon-success { color: var(--cld-success); }\n.upload-header-icon.icon-error { color: var(--cld-error); }\n.upload-header-icon.icon-warning { color: var(--cld-warning); }\n.upload-header-icon.icon-accent { color: var(--cld-accent); }\n";
|
|
2
|
+
export declare const SHARED_CSS_COMPONENTS = "\n.link { cursor: pointer; }\n.link:hover { color: var(--cld-accent); text-decoration: underline; }\n\n/* Modal \u2014 positioned absolutely so it can be placed in the part of the\n * iframe that is currently visible in the host's viewport (since position:fixed\n * inside a tall iframe anchors to iframe-center, not the user's visible area). */\n.modal-overlay {\n position: absolute; left: 0; right: 0;\n background: rgba(0,0,0,0.45);\n display: flex; align-items: center; justify-content: center;\n z-index: 1000; backdrop-filter: blur(3px); padding: 24px;\n}\n.modal {\n background: var(--cld-bg); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius); width: 100%; max-width: 620px;\n max-height: 85vh; display: flex; flex-direction: column;\n box-shadow: var(--cld-shadow-lg); animation: modalIn 0.15s ease-out;\n}\n@keyframes modalIn {\n from { opacity: 0; transform: scale(0.96) translateY(8px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n}\n.modal-header {\n display: flex; align-items: center; gap: 12px;\n padding: 16px 20px; border-bottom: 1px solid var(--cld-border); flex-shrink: 0;\n}\n.modal-header-thumb {\n width: 40px; height: 40px; border-radius: 6px;\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.modal-header-info { flex: 1; min-width: 0; }\n.modal-header-info h2 {\n font-size: 14px; font-weight: 600;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.modal-header-sub { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.modal-close {\n background: var(--cld-bg3); border: 1px solid var(--cld-border);\n width: 28px; height: 28px; border-radius: 6px; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 16px; color: var(--cld-text2); flex-shrink: 0; font-family: inherit;\n}\n.modal-close:hover { background: var(--cld-border); }\n.modal-body { overflow-y: auto; padding: 0; }\n.modal-hero {\n width: 100%; max-height: 220px; object-fit: contain;\n background: var(--cld-bg3); display: block;\n}\n.modal-loading { text-align: center; padding: 48px 20px; color: var(--cld-text2); font-size: 13px; }\n.modal-loading .spinner {\n display: inline-block; width: 24px; height: 24px;\n border: 2.5px solid var(--cld-border); border-top-color: var(--cld-accent);\n border-radius: 50%; animation: spin 0.6s linear infinite; margin-bottom: 10px;\n}\n@keyframes spin { to { transform: rotate(360deg); } }\n\n/* Detail sections */\n.detail-section { padding: 14px 20px; border-bottom: 1px solid var(--cld-bg3); }\n.detail-section:last-child { border-bottom: none; }\n.detail-section-title {\n font-size: 10px; font-weight: 700; text-transform: uppercase;\n letter-spacing: 0.8px; color: var(--cld-text3); margin-bottom: 10px;\n display: flex; align-items: center; gap: 6px;\n}\n.detail-section-title .count {\n background: var(--cld-bg3); padding: 1px 6px; border-radius: 8px;\n font-size: 10px; font-weight: 600; color: var(--cld-text2);\n}\ndetails.detail-section > summary.detail-section-title {\n cursor: pointer; list-style: none; user-select: none;\n}\ndetails.detail-section > summary.detail-section-title::before {\n content: \"\\25B6\"; display: inline-block; width: 14px; font-size: 9px;\n transition: transform 0.15s ease; margin-right: 4px;\n}\ndetails.detail-section[open] > summary.detail-section-title::before {\n transform: rotate(90deg);\n}\ndetails.detail-section > summary.detail-section-title::-webkit-details-marker { display: none; }\n.detail-grid {\n display: grid; grid-template-columns: 1fr 1fr; gap: 1px;\n background: var(--cld-bg3); border-radius: var(--cld-radius-sm); overflow: hidden;\n}\n.detail-cell { background: var(--cld-bg); padding: 8px 12px; }\n.detail-cell-key { font-size: 10px; color: var(--cld-text3); font-weight: 500; margin-bottom: 2px; }\n.detail-cell-val { font-size: 12px; color: var(--cld-text); font-weight: 500; word-break: break-all; }\n.detail-cell-val.link-val { color: var(--cld-accent); cursor: pointer; }\n.detail-cell-val.link-val:hover { text-decoration: underline; }\n.detail-cell.full-width { grid-column: 1 / -1; }\n\n/* Chips */\n.chip-list { display: flex; flex-wrap: wrap; gap: 5px; }\n.chip { font-size: 11px; padding: 3px 10px; border-radius: 12px; font-weight: 500; white-space: nowrap; }\n.chip-tag { background: var(--cld-chip-tag-bg); color: var(--cld-chip-tag-fg); }\n.chip-set { background: var(--cld-chip-set-bg); color: var(--cld-chip-set-fg); border: 1px solid var(--cld-chip-set-border); }\n.chip-date { background: var(--cld-chip-date-bg); color: var(--cld-chip-date-fg); border: 1px solid var(--cld-chip-date-border); }\n.chip-int { background: var(--cld-chip-int-bg); color: var(--cld-chip-int-fg); border: 1px solid var(--cld-chip-int-border); }\n\n/* Meta rows */\n.meta-row {\n display: flex; align-items: baseline; padding: 6px 0;\n border-bottom: 1px solid var(--cld-bg3); gap: 8px; font-size: 12px;\n}\n.meta-row:last-child { border-bottom: none; }\n.meta-key {\n color: var(--cld-text2); min-width: 0; flex-shrink: 0; max-width: 45%;\n font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px;\n}\n.meta-val { color: var(--cld-text); flex: 1; min-width: 0; text-align: right; }\n\n/* Derived assets */\n.derived-card {\n display: flex; align-items: center; gap: 10px; padding: 8px 0;\n border-bottom: 1px solid var(--cld-bg3); font-size: 11px;\n}\n.derived-card:last-child { border-bottom: none; }\n.derived-thumb {\n width: 48px; height: 36px; border-radius: 4px;\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.derived-info { flex: 1; min-width: 0; }\n.derived-tx { color: var(--cld-text); font-family: monospace; font-size: 10px; word-break: break-all; }\n.derived-meta { color: var(--cld-text3); font-size: 10px; margin-top: 2px; }\n.derived-open { color: var(--cld-accent); cursor: pointer; font-weight: 500; white-space: nowrap; font-size: 11px; }\n.derived-open:hover { text-decoration: underline; }\n\n/* Error */\n.modal-error { text-align: center; padding: 32px 20px; color: var(--cld-text2); font-size: 13px; }\n\n/* Status / loading */\n.status { text-align: center; padding: 48px 16px; color: var(--cld-text2); font-size: 14px; }\n.status .icon { font-size: 32px; margin-bottom: 8px; }\n\n/* Fetch prompt */\n.prompt { text-align: center; padding: 48px 24px; color: var(--cld-text2); }\n.prompt-icon { font-size: 36px; margin-bottom: 12px; }\n.prompt-title { font-size: 15px; font-weight: 600; color: var(--cld-text); margin-bottom: 6px; }\n.prompt-desc { font-size: 13px; max-width: 420px; margin: 0 auto 20px; line-height: 1.5; }\n.prompt-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }\n.prompt-btn {\n padding: 8px 20px; border-radius: var(--cld-radius); font-size: 13px; font-weight: 500;\n cursor: pointer; border: 1px solid var(--cld-border); background: var(--cld-bg2);\n color: var(--cld-text); font-family: inherit; transition: background 0.15s, border-color 0.15s;\n}\n.prompt-btn:hover { background: var(--cld-bg3); border-color: var(--cld-border2); }\n.prompt-btn-primary { background: var(--cld-primary); color: #fff; border-color: var(--cld-primary); }\n.prompt-btn-primary:hover { background: var(--cld-primary-light); border-color: var(--cld-primary-light); }\n\n/* Error toast */\n.error-toast {\n position: fixed; bottom: 16px; left: 16px; right: 16px;\n background: var(--cld-error); color: #fff; padding: 12px 16px;\n border-radius: var(--cld-radius); box-shadow: var(--cld-shadow-md);\n font-size: 13px; z-index: 2000; display: flex; align-items: flex-start;\n gap: 10px; animation: toastIn 0.2s ease-out; max-width: 600px; margin: 0 auto;\n}\n.error-toast-icon { font-size: 18px; flex-shrink: 0; line-height: 1; }\n.error-toast-body { flex: 1; min-width: 0; }\n.error-toast-title { font-weight: 600; margin-bottom: 2px; }\n.error-toast-msg { font-size: 12px; opacity: 0.9; word-break: break-word; }\n.error-toast-close {\n background: none; border: none; color: #fff; cursor: pointer;\n font-size: 16px; opacity: 0.8; padding: 0 2px; flex-shrink: 0; font-family: inherit;\n}\n.error-toast-close:hover { opacity: 1; }\n@keyframes toastIn {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Thumb overlays */\n.thumb-overlay {\n position: absolute; inset: 0; display: flex;\n align-items: center; justify-content: center; pointer-events: none;\n}\n.play-icon {\n width: 40px; height: 40px; background: rgba(0,0,0,0.55);\n border-radius: 50%; display: flex; align-items: center; justify-content: center;\n}\n.play-icon::after {\n content: \"\"; display: block; width: 0; height: 0;\n border-style: solid; border-width: 8px 0 8px 14px;\n border-color: transparent transparent transparent #fff; margin-left: 3px;\n}\n.audio-icon {\n width: 40px; height: 40px; background: rgba(0,0,0,0.55);\n border-radius: 50%; display: flex; align-items: center; justify-content: center;\n color: #fff; font-size: 18px;\n}\n.duration-badge {\n position: absolute; bottom: 6px; right: 6px;\n background: rgba(0,0,0,0.7); color: #fff; font-size: 10px;\n font-weight: 600; padding: 2px 6px; border-radius: 4px;\n font-variant-numeric: tabular-nums; backdrop-filter: blur(4px);\n}\n.status-badge {\n display: inline-block; font-size: 11px; font-weight: 600;\n padding: 1px 8px; border-radius: 10px; text-transform: capitalize;\n}\n.status-ok { background: #dcfce7; color: #166534; }\n.status-warn { background: #fef9c3; color: #854d0e; }\n.status-err { background: #fee2e2; color: #991b1b; }\n.file-icon {\n display: flex; flex-direction: column; align-items: center;\n justify-content: center; gap: 4px; color: var(--cld-text3);\n}\n.file-icon svg { width: 36px; height: 36px; }\n.file-icon-label { font-size: 10px; font-weight: 600; text-transform: uppercase; }\n\n/* Native media players */\n.hero-video {\n width: 100%; max-height: 300px; display: block;\n background: #000; border-radius: 0;\n}\n.hero-audio-wrap {\n position: relative; padding: 20px;\n background: var(--cld-bg3); display: flex; flex-direction: column;\n align-items: center; gap: 12px;\n}\n.hero-audio-waveform {\n width: 100%; max-height: 120px; object-fit: contain; display: block;\n opacity: 0.6; border-radius: var(--cld-radius-sm);\n}\n.hero-audio-wrap audio { width: 100%; max-width: 500px; }\n.hero-audio-note {\n font-size: 28px; color: var(--cld-text3); margin-bottom: 4px;\n}\n.media-modal-video {\n width: 100%; display: block; background: #000;\n max-height: 60vh;\n}\n.media-modal-audio-wrap {\n padding: 24px 20px; background: var(--cld-bg3);\n display: flex; flex-direction: column; align-items: center; gap: 12px;\n}\n.media-modal-audio-wrap img {\n width: 100%; max-height: 100px; object-fit: contain; opacity: 0.6;\n border-radius: var(--cld-radius-sm);\n}\n.media-modal-audio-wrap audio { width: 100%; max-width: 480px; }\n.thumb-overlay.playable { pointer-events: auto; cursor: pointer; }\n\n/* Upload app */\n.upload-zone {\n border: 2px dashed var(--cld-border2); border-radius: var(--cld-radius);\n padding: 40px 24px; text-align: center; cursor: pointer;\n transition: border-color 0.2s, background 0.2s;\n background: var(--cld-bg2);\n}\n.upload-zone:hover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }\n.upload-zone.dragover { border-color: var(--cld-accent); background: var(--cld-accent-bg); }\n.upload-zone-icon { margin-bottom: 8px; color: var(--cld-text3); display: flex; align-items: center; justify-content: center; }\n.upload-zone-icon svg { width: 36px; height: 36px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-zone-text { font-size: 14px; color: var(--cld-text2); margin-bottom: 4px; }\n.upload-zone-hint { font-size: 12px; color: var(--cld-text3); }\n.upload-zone-btn {\n display: inline-block; margin-top: 14px; padding: 8px 22px;\n border-radius: var(--cld-radius); font-size: 13px; font-weight: 500;\n cursor: pointer; border: 1px solid var(--cld-accent); background: var(--cld-accent);\n color: #fff; font-family: inherit; transition: background 0.15s;\n}\n.upload-zone-btn:hover { background: var(--cld-primary-light); border-color: var(--cld-primary-light); }\n.upload-or { margin: 16px 0; font-size: 12px; color: var(--cld-text3); display: flex; align-items: center; gap: 10px; }\n.upload-or::before, .upload-or::after { content: \"\"; flex: 1; height: 1px; background: var(--cld-border); }\n.upload-url-row { display: flex; gap: 8px; }\n.upload-url-input {\n flex: 1; padding: 8px 12px; border-radius: var(--cld-radius-sm);\n border: 1px solid var(--cld-border); background: var(--cld-bg);\n color: var(--cld-text); font-size: 13px; font-family: inherit; outline: none;\n}\n.upload-url-input:focus { border-color: var(--cld-accent); }\n.upload-url-input::placeholder { color: var(--cld-text3); }\n.upload-url-btn {\n padding: 8px 16px; border-radius: var(--cld-radius-sm);\n font-size: 13px; font-weight: 500; cursor: pointer;\n border: 1px solid var(--cld-accent); background: transparent;\n color: var(--cld-accent); font-family: inherit; transition: background 0.15s;\n white-space: nowrap;\n}\n.upload-url-btn:hover { background: var(--cld-accent-bg); }\n.upload-params {\n margin-top: 16px; padding: 12px 16px; background: var(--cld-bg3);\n border-radius: var(--cld-radius-sm); font-size: 12px; color: var(--cld-text2);\n}\n.upload-params-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--cld-text3); margin-bottom: 6px; }\n.upload-params .chip { margin-right: 4px; margin-bottom: 4px; }\n.upload-preview {\n display: flex; align-items: center; gap: 14px; padding: 16px;\n background: var(--cld-bg2); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius); margin-bottom: 16px;\n}\n.upload-preview-thumb {\n width: 56px; height: 56px; border-radius: var(--cld-radius-sm);\n object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;\n}\n.upload-preview-icon {\n width: 56px; height: 56px; border-radius: var(--cld-radius-sm);\n background: var(--cld-bg3); flex-shrink: 0; display: flex;\n align-items: center; justify-content: center; color: var(--cld-text3);\n}\n.upload-preview-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-preview-info { flex: 1; min-width: 0; }\n.upload-preview-name { font-size: 13px; font-weight: 600; color: var(--cld-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.upload-preview-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.upload-progress-wrap { margin-top: 8px; }\n.upload-progress-bar {\n height: 6px; border-radius: 3px; background: var(--cld-bg3); overflow: hidden;\n}\n.upload-progress-fill {\n height: 100%; background: var(--cld-accent); border-radius: 3px;\n transition: width 0.3s ease; width: 0%;\n}\n.upload-progress-text { font-size: 11px; color: var(--cld-text3); margin-top: 4px; text-align: center; }\n.upload-error-msg {\n background: #fef2f2; border: 1px solid #fecaca; border-radius: var(--cld-radius);\n color: #991b1b; padding: 10px 14px; font-size: 12px; margin-top: 8px;\n word-break: break-word; line-height: 1.5;\n}\n.upload-result {\n border: 1px solid var(--cld-border); border-radius: var(--cld-radius);\n overflow: hidden;\n}\n.upload-result-hero {\n position: relative; background: var(--cld-bg3);\n display: flex; align-items: center; justify-content: center; min-height: 120px;\n}\n.upload-result-hero img { width: 100%; max-height: 260px; object-fit: contain; display: block; }\n.upload-result-hero .file-icon { padding: 30px 20px; }\n.upload-result-body { padding: 16px; }\n.upload-result-title {\n font-size: 15px; font-weight: 600; color: var(--cld-text); margin-bottom: 12px;\n display: flex; align-items: center; gap: 8px;\n}\n.upload-result-title .success-icon { color: var(--cld-success); font-size: 18px; }\n.upload-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }\n.upload-actions .prompt-btn { font-size: 12px; padding: 6px 16px; }\n\n/* Upload form fields */\n.upload-form {\n margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n.upload-field { display: flex; flex-direction: column; gap: 3px; }\n.upload-field.full-width { grid-column: 1 / -1; }\n.upload-field label {\n font-size: 10px; font-weight: 700; text-transform: uppercase;\n letter-spacing: 0.6px; color: var(--cld-text3);\n}\n.upload-field input[type=\"text\"],\n.upload-field input[type=\"number\"],\n.upload-field select,\n.upload-field textarea {\n padding: 7px 10px; border-radius: var(--cld-radius-sm);\n border: 1px solid var(--cld-border); background: var(--cld-bg);\n color: var(--cld-text); font-size: 13px; font-family: inherit; outline: none;\n width: 100%;\n}\n.upload-field input[type=\"text\"]:focus,\n.upload-field input[type=\"number\"]:focus,\n.upload-field select:focus,\n.upload-field textarea:focus { border-color: var(--cld-accent); }\n.upload-field input::placeholder,\n.upload-field textarea::placeholder { color: var(--cld-text3); }\n.upload-field textarea { resize: vertical; min-height: 36px; }\n.upload-field select { cursor: pointer; appearance: auto; }\n\n/* Help icon (\"?\") with inline bubble */\n.help-toggle {\n display: inline-flex; align-items: center; justify-content: center;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--cld-border); color: var(--cld-text2);\n font-size: 9px; font-weight: 700; cursor: pointer;\n position: relative; vertical-align: middle; margin-left: 4px;\n user-select: none; line-height: 1; flex-shrink: 0;\n}\n.help-toggle:hover, .help-toggle:focus { background: var(--cld-accent); color: #fff; }\n.help-bubble {\n display: none; position: absolute; left: 50%; bottom: calc(100% + 6px);\n transform: translateX(-50%); width: 220px; padding: 8px 10px;\n background: var(--cld-text); color: var(--cld-bg); font-size: 11px;\n font-weight: 400; line-height: 1.4; border-radius: var(--cld-radius-sm);\n box-shadow: var(--cld-shadow-sm); z-index: 200; text-transform: none;\n letter-spacing: 0; white-space: normal; pointer-events: none;\n}\n.help-bubble::after {\n content: \"\"; position: absolute; top: 100%; left: 50%;\n transform: translateX(-50%); border: 5px solid transparent;\n border-top-color: var(--cld-text);\n}\n.help-toggle:hover .help-bubble,\n.help-toggle:focus .help-bubble { display: block; }\n.detail-grid .help-toggle .help-bubble,\n.detail-section-title .help-toggle .help-bubble {\n left: -4px; transform: none;\n}\n.detail-grid .help-toggle .help-bubble::after,\n.detail-section-title .help-toggle .help-bubble::after {\n left: 10px; transform: none;\n}\n.color-swatch {\n display: inline-flex; flex-direction: column; align-items: center; gap: 2px; text-align: center;\n}\n.color-swatch-box {\n width: 28px; height: 28px; border-radius: 4px; border: 1px solid var(--cld-border);\n}\n.color-swatch-label { font-size: 9px; color: var(--cld-text3); }\n.color-swatch-pct { font-size: 9px; color: var(--cld-text2); }\n.color-group-title {\n font-size: 10px; font-weight: 600; color: var(--cld-text3);\n text-transform: uppercase; margin: 6px 0 4px;\n}\n.color-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }\n\n/* Checkbox field variant */\n.upload-field-check {\n flex-direction: row; align-items: center; gap: 6px;\n}\n.upload-field-check label {\n display: flex; align-items: center; gap: 6px; cursor: pointer;\n text-transform: none; font-weight: 500; font-size: 12px; color: var(--cld-text2);\n}\n.upload-field-check input[type=\"checkbox\"] {\n width: 15px; height: 15px; cursor: pointer; accent-color: var(--cld-accent);\n}\n\n/* Collapsible sections */\ndetails.upload-section {\n margin-top: 14px; border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius-sm); overflow: hidden;\n}\ndetails.upload-section summary {\n padding: 8px 12px; font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: 0.6px; color: var(--cld-text3);\n cursor: pointer; list-style: none; display: flex; align-items: center; gap: 6px;\n background: var(--cld-bg2); user-select: none;\n}\ndetails.upload-section summary::before {\n content: \"\\25B6\"; font-size: 8px; transition: transform 0.15s;\n}\ndetails.upload-section[open] summary::before { transform: rotate(90deg); }\ndetails.upload-section summary::-webkit-details-marker { display: none; }\ndetails.upload-section > .upload-form { margin: 0; padding: 10px 12px; }\n\n/* Staged file preview */\n.upload-staged {\n display: flex; align-items: center; gap: 14px; padding: 14px 16px;\n background: var(--cld-accent-bg); border: 1px solid var(--cld-accent);\n border-radius: var(--cld-radius); margin-bottom: 4px; position: relative;\n}\n.upload-staged-icon { flex-shrink: 0; display: flex; align-items: center; color: var(--cld-accent); }\n.upload-staged-icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.upload-staged-info { flex: 1; min-width: 0; }\n.upload-staged-name {\n font-size: 13px; font-weight: 600; color: var(--cld-text);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.upload-staged-meta { font-size: 11px; color: var(--cld-text3); margin-top: 2px; }\n.upload-staged-clear:hover { color: var(--cld-error); border-color: var(--cld-error); background: rgba(206,25,13,0.08); }\n\n/* Upload submit button */\n.upload-submit {\n position: sticky; bottom: 0; z-index: 10;\n background: var(--cld-bg); border-top: 1px solid var(--cld-border);\n padding: 12px 0; margin-top: 16px; text-align: center;\n}\n.upload-submit-btn {\n padding: 10px 32px !important; font-size: 14px !important; font-weight: 600 !important;\n}\n\n/* Combobox (folder picker) */\n.combo-wrap { position: relative; }\n.combo-dropdown {\n display: none; position: absolute; top: 100%; left: 0; right: 0;\n max-height: 180px; overflow-y: auto; z-index: 100;\n background: var(--cld-bg); border: 1px solid var(--cld-border);\n border-radius: var(--cld-radius-sm); box-shadow: var(--cld-shadow-sm);\n margin-top: 2px;\n}\n.combo-item {\n padding: 7px 10px; font-size: 13px; color: var(--cld-text);\n cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.combo-item:hover, .combo-item-active { background: var(--cld-accent-bg); color: var(--cld-accent); }\n\n.raw-response-pre {\n margin: 0; padding: 10px 12px; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 11px; line-height: 1.45; white-space: pre-wrap; word-break: break-all;\n background: var(--cld-bg2); border-radius: 6px; color: var(--cld-text2);\n max-height: 500px; overflow-y: auto;\n}\n.json-key { color: #881391; }\n.json-str { color: #0b7285; }\n.json-num { color: #c92a2a; }\n.json-bool { color: #5c940d; }\n.json-null { color: #868e96; }\n@media (prefers-color-scheme: dark) {\n .json-key { color: #da77f2; }\n .json-str { color: #66d9e8; }\n .json-num { color: #ff8787; }\n .json-bool { color: #a9e34b; }\n .json-null { color: #868e96; }\n}\n";
|
|
3
3
|
export declare const SHARED_JS_ICONS = "\nvar IC = {\n 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>',\n chevronLeft:'<svg viewBox=\"0 0 24 24\"><polyline points=\"15 18 9 12 15 6\"/></svg>',\n 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>',\n 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>',\n zap: '<svg viewBox=\"0 0 24 24\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>',\n 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>',\n 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>',\n 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>',\n 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>',\n clock: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/></svg>',\n 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>',\n 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>',\n};\n";
|
|
4
4
|
export declare const SHARED_JS_MCP_CLIENT = "\nvar RPC_TIMEOUT_MS = 15000;\nvar TOOL_CALL_TIMEOUT_MS = 30000;\nvar INIT_TIMEOUT_MS = 10000;\n\nclass MCPApp {\n constructor(info) {\n this.info = info;\n this.ontoolresult = null;\n this.ontoolinput = null;\n this.ontoolcancelled = null;\n this.onhostcontextchanged = null;\n this._id = 1;\n this._pending = new Map();\n this._timers = new Map();\n }\n connect() {\n console.log(LOG_PREFIX, \"connecting\u2026\");\n window.addEventListener(\"message\", (ev) => {\n const m = ev.data;\n if (!m || m.jsonrpc !== \"2.0\") return;\n if (m.method) console.log(LOG_PREFIX, \"recv notification:\", m.method);\n else if (m.id != null) console.log(LOG_PREFIX, \"recv response id=\" + m.id);\n\n if (m.method === \"ui/notifications/tool-result\" && this.ontoolresult)\n this.ontoolresult(m.params);\n else if (m.method === \"ui/notifications/tool-input\" && this.ontoolinput)\n this.ontoolinput(m.params);\n else if (m.method === \"ui/notifications/tool-cancelled\" && this.ontoolcancelled)\n this.ontoolcancelled(m.params);\n else if (m.method === \"ui/notifications/host-context-changed\" && this.onhostcontextchanged)\n this.onhostcontextchanged(m.params);\n else if (m.id != null && this._pending.has(m.id)) {\n clearTimeout(this._timers.get(m.id));\n this._timers.delete(m.id);\n var cb = this._pending.get(m.id);\n this._pending.delete(m.id);\n if (m.error) {\n console.warn(LOG_PREFIX, \"rpc error id=\" + m.id, m.error);\n cb.reject(m.error);\n } else {\n cb.resolve(m.result);\n }\n }\n });\n return this._rpc(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n appInfo: { name: this.info.name, version: this.info.version },\n appCapabilities: {},\n }, INIT_TIMEOUT_MS).then(function() {\n console.log(LOG_PREFIX, \"initialized, sending initialized notification\");\n window.parent.postMessage({ jsonrpc: \"2.0\", method: \"ui/notifications/initialized\" }, \"*\");\n });\n }\n callServerTool(params) {\n console.log(LOG_PREFIX, \"calling tool:\", params.name);\n return this._rpc(\"tools/call\", params, TOOL_CALL_TIMEOUT_MS);\n }\n _rpc(method, params, timeoutMs) {\n var self = this;\n var id = this._id++;\n var ms = timeoutMs || RPC_TIMEOUT_MS;\n console.log(LOG_PREFIX, \"rpc \u2192\", method, \"id=\" + id, \"timeout=\" + ms + \"ms\");\n return new Promise(function(resolve, reject) {\n self._pending.set(id, { resolve: resolve, reject: reject });\n var timer = setTimeout(function() {\n if (self._pending.has(id)) {\n self._pending.delete(id);\n self._timers.delete(id);\n var err = new Error(\"RPC timeout after \" + ms + \"ms: \" + method + \" (id=\" + id + \")\");\n console.error(LOG_PREFIX, err.message);\n reject(err);\n }\n }, ms);\n self._timers.set(id, timer);\n window.parent.postMessage({ jsonrpc: \"2.0\", id: id, method: method, params: params }, \"*\");\n });\n }\n reportSize(height) {\n window.parent.postMessage({\n jsonrpc: \"2.0\", method: \"ui/notifications/size-changed\", params: { height: height },\n }, \"*\");\n }\n}\n";
|
|
5
5
|
export declare const SHARED_JS_HELPERS = "\nfunction copyText(text) {\n if (navigator.clipboard && navigator.clipboard.writeText) {\n return navigator.clipboard.writeText(text).catch(function() { return _copyFallback(text); });\n }\n return _copyFallback(text);\n}\nfunction _copyFallback(text) {\n return new Promise(function(resolve, reject) {\n try {\n var ta = document.createElement(\"textarea\");\n ta.value = text;\n ta.setAttribute(\"readonly\", \"\");\n ta.style.position = \"fixed\"; ta.style.top = \"0\"; ta.style.left = \"0\";\n ta.style.opacity = \"0\"; ta.style.pointerEvents = \"none\";\n document.body.appendChild(ta);\n ta.select(); ta.setSelectionRange(0, text.length);\n var ok = document.execCommand(\"copy\");\n document.body.removeChild(ta);\n if (ok) resolve(); else reject(new Error(\"Clipboard unavailable\"));\n } catch (e) { reject(e); }\n });\n}\n\nfunction fmtBytes(b) {\n if (!b) return \"\";\n var u = [\"B\",\"KB\",\"MB\",\"GB\"], i = Math.min(Math.floor(Math.log(b)/Math.log(1024)), 3);\n var v = b / Math.pow(1024, i);\n return (v < 10 ? v.toFixed(1) : Math.round(v)) + \" \" + u[i];\n}\n\nfunction fmtDate(iso) {\n if (!iso) return \"\";\n return new Date(iso).toLocaleDateString(\"en-US\", { month:\"short\", day:\"numeric\", year:\"numeric\" });\n}\n\nfunction fmtDuration(sec) {\n if (sec == null || sec <= 0) return \"\";\n var s = Math.round(sec);\n var m = Math.floor(s / 60);\n s = s % 60;\n return m + \":\" + (s < 10 ? \"0\" : \"\") + s;\n}\n\nfunction isAudioResource(r) {\n if (!r) return false;\n return r.is_audio === true || (r.resource_type === \"video\" && !r.width && !r.height);\n}\n\nfunction insertTransformation(url, tx, resource) {\n var rt = (resource && resource.resource_type) || \"\";\n var pattern = new RegExp(\"/(\" + (rt || \"image|video|raw\") + \")/([^/]+)/\");\n var match = url.match(pattern);\n if (!match) return \"\";\n var insertAt = match.index + match[0].length;\n return url.slice(0, insertAt) + tx + \"/\" + url.slice(insertAt);\n}\n\nfunction thumbUrl(url, w, h, resource) {\n if (!url) return \"\";\n w = w || 300; h = h || 225;\n var rt = (resource && resource.resource_type) || \"\";\n var fmt = (resource && resource.format || \"\").toLowerCase();\n\n if (rt === \"raw\") return \"\";\n\n if (isAudioResource(resource)) {\n var base = url.replace(/\\.[^/.]+$/, \".png\");\n return insertTransformation(base, \"c_scale,w_\" + w + \",h_\" + h + \"/fl_waveform,b_transparent,co_rgb:3448c5\", resource);\n }\n\n if (rt === \"video\") {\n return insertTransformation(url, \"c_fill,g_auto,w_\" + w + \",h_\" + h + \",so_auto,f_jpg,q_auto\", resource);\n }\n\n if (fmt === \"pdf\") {\n return insertTransformation(url, \"c_fill,w_\" + w + \",h_\" + h + \",pg_1,f_auto,q_auto\", resource);\n }\n\n return insertTransformation(url, \"c_fill,g_auto,w_\" + w + \",h_\" + h + \",f_auto,q_auto\", resource);\n}\n\nfunction mediaUrl(url, resource) {\n if (!url) return \"\";\n var rt = (resource && resource.resource_type) || \"\";\n if (rt !== \"video\") return url;\n return insertTransformation(url, \"q_auto\", resource) || url;\n}\n\nfunction esc(s) {\n var d = document.createElement(\"div\");\n d.textContent = s;\n return d.innerHTML;\n}\n\nfunction prettyKey(k) {\n return k.replace(/[_-]/g, \" \").replace(/\\b\\w/g, function(c) { return c.toUpperCase(); });\n}\n\nvar FILE_TYPE_ICONS = {\n pdf: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#E8384F\" opacity=\"0.15\" stroke=\"#E8384F\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#E8384F\" font-size=\"9\" font-weight=\"700\">PDF</text></svg>',\n zip: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#F5A623\" opacity=\"0.15\" stroke=\"#F5A623\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#F5A623\" font-size=\"9\" font-weight=\"700\">ZIP</text></svg>',\n doc: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#2B7CFF\" opacity=\"0.15\" stroke=\"#2B7CFF\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#2B7CFF\" font-size=\"9\" font-weight=\"700\">DOC</text></svg>',\n xls: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#22AA00\" opacity=\"0.15\" stroke=\"#22AA00\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#22AA00\" font-size=\"9\" font-weight=\"700\">XLS</text></svg>',\n csv: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#22AA00\" opacity=\"0.15\" stroke=\"#22AA00\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#22AA00\" font-size=\"9\" font-weight=\"700\">CSV</text></svg>',\n json: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#7c3aed\" opacity=\"0.15\" stroke=\"#7c3aed\" stroke-width=\"1.5\"/><text x=\"18\" y=\"22\" text-anchor=\"middle\" fill=\"#7c3aed\" font-size=\"8\" font-weight=\"700\">JSON</text></svg>',\n _default: '<svg viewBox=\"0 0 36 36\" fill=\"none\"><rect x=\"6\" y=\"2\" width=\"24\" height=\"32\" rx=\"3\" fill=\"#90a0b3\" opacity=\"0.15\" stroke=\"#90a0b3\" stroke-width=\"1.5\"/><path d=\"M14 14h8M14 18h8M14 22h5\" stroke=\"#90a0b3\" stroke-width=\"1.2\" stroke-linecap=\"round\"/></svg>',\n};\nFILE_TYPE_ICONS.docx = FILE_TYPE_ICONS.doc;\nFILE_TYPE_ICONS.xlsx = FILE_TYPE_ICONS.xls;\nFILE_TYPE_ICONS.rar = FILE_TYPE_ICONS.zip;\nFILE_TYPE_ICONS[\"7z\"] = FILE_TYPE_ICONS.zip;\nFILE_TYPE_ICONS.xml = FILE_TYPE_ICONS.json;\n\nfunction fileTypeIcon(format) {\n var f = (format || \"\").toLowerCase();\n return FILE_TYPE_ICONS[f] || FILE_TYPE_ICONS._default;\n}\n\nvar errorToastTimer = null;\nfunction showError(title, msg) {\n console.error(LOG_PREFIX, title, msg);\n dismissError();\n var h = '<div class=\"error-toast\" id=\"error-toast\">';\n h += '<span class=\"error-toast-icon\">\\u26A0</span>';\n h += '<div class=\"error-toast-body\">';\n h += '<div class=\"error-toast-title\">' + esc(title) + \"</div>\";\n if (msg) h += '<div class=\"error-toast-msg\">' + esc(msg) + \"</div>\";\n h += \"</div>\";\n h += '<button class=\"error-toast-close\" onclick=\"dismissError()\">\\u2715</button>';\n h += \"</div>\";\n document.body.insertAdjacentHTML(\"beforeend\", h);\n errorToastTimer = setTimeout(dismissError, 8000);\n}\n\nfunction dismissError() {\n clearTimeout(errorToastTimer);\n var el = document.getElementById(\"error-toast\");\n if (el) el.remove();\n}\n\nfunction showPersistentError(title, msg) {\n var root = document.getElementById(\"app\");\n var h = '<div class=\"prompt\">';\n h += '<div class=\"prompt-icon\">\\u26A0\\uFE0F</div>';\n h += '<div class=\"prompt-title\">' + esc(title) + '</div>';\n if (msg) h += '<div class=\"prompt-desc\">' + esc(msg) + '</div>';\n h += '</div>';\n root.innerHTML = h;\n requestAnimationFrame(function() { app.reportSize(document.documentElement.scrollHeight); });\n}\n\nfunction renderParamsList(args) {\n if (!args) return '';\n var keys = Object.keys(args);\n if (keys.length === 0) return '';\n var h = '<div style=\"display:flex;flex-wrap:wrap;gap:6px 14px;justify-content:center;margin-top:6px\">';\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n var v = args[k];\n var display;\n if (v === null || v === undefined) display = '<span style=\"color:var(--cld-text3)\">null</span>';\n else if (typeof v === \"boolean\") display = '<span style=\"color:' + (v ? '#16a34a' : 'var(--cld-text3)') + '\">' + v + '</span>';\n else if (typeof v === \"object\") display = '<span style=\"font-family:monospace\">' + esc(JSON.stringify(v)) + '</span>';\n else display = '<span style=\"color:var(--cld-text)\">' + esc(String(v)) + '</span>';\n h += '<span style=\"font-size:11px\"><span style=\"color:var(--cld-text3)\">' + esc(prettyKey(k)) + '</span> ' + display + '</span>';\n }\n h += '</div>';\n return h;\n}\n\nfunction showReadyPrompt(pendingCall, fetchFn) {\n var root = document.getElementById(\"app\");\n var h = '<div style=\"text-align:center;padding:24px 16px;color:var(--cld-text2)\">';\n h += '<div style=\"font-size:13px;color:var(--cld-text2);margin-bottom:4px\">Waiting for result\\u2026</div>';\n h += renderParamsList(pendingCall.args);\n h += '<div style=\"margin-top:12px\"><button class=\"prompt-btn\" id=\"ready-fetch-btn\" style=\"font-size:11px;padding:4px 14px\">Fetch Directly</button></div>';\n h += '</div>';\n root.innerHTML = h;\n document.getElementById(\"ready-fetch-btn\").addEventListener(\"click\", function() { fetchFn(); });\n requestAnimationFrame(function() { app.reportSize(document.documentElement.scrollHeight); });\n}\n\nfunction showCancelledPrompt(pendingCall, fetchFn) {\n var root = document.getElementById(\"app\");\n var name = pendingCall.name;\n var h = '<div class=\"prompt\" style=\"padding:32px 24px\">';\n h += '<div style=\"font-size:14px;font-weight:600;color:var(--cld-text);margin-bottom:4px\">Cancelled</div>';\n if (name) h += '<div style=\"font-size:12px;color:var(--cld-text3);margin-bottom:8px\">' + esc(prettyKey(name)) + '</div>';\n h += renderParamsList(pendingCall.args);\n h += '<div class=\"prompt-actions\" style=\"margin-top:14px\">';\n h += '<button class=\"prompt-btn prompt-btn-primary\" id=\"cancelled-fetch-btn\">Fetch Directly</button>';\n h += '</div>';\n h += '</div>';\n root.innerHTML = h;\n document.getElementById(\"cancelled-fetch-btn\").addEventListener(\"click\", function() { fetchFn(); });\n requestAnimationFrame(function() { app.reportSize(document.documentElement.scrollHeight); });\n}\n\nfunction ingestResult(params) {\n try {\n var payload = params.result || params;\n var isErr = payload.isError === true;\n var content = payload.content || [];\n var text = content.find(function(c) { return c.type === \"text\"; });\n if (!text) return null;\n var raw = text.text;\n if (isErr) {\n console.warn(LOG_PREFIX, \"ingestResult: server error:\", raw);\n return { _error: true, _message: raw };\n }\n if (typeof raw === \"string\" && raw.charAt(0) !== \"{\" && raw.charAt(0) !== \"[\") {\n console.warn(LOG_PREFIX, \"ingestResult: text is not JSON, likely truncated:\", raw.substring(0, 120));\n return { _truncated: true, _message: raw };\n }\n var parsed = JSON.parse(raw);\n if (parsed && parsed.error) {\n var msg = parsed.error.message || JSON.stringify(parsed.error);\n console.warn(LOG_PREFIX, \"ingestResult: API error:\", msg);\n return { _error: true, _message: msg };\n }\n return parsed;\n } catch (e) {\n console.warn(LOG_PREFIX, \"ingestResult parse error:\", e, \"raw length:\", (raw || \"\").length);\n return { _parseError: true, _message: \"JSON parse failed (length \" + (raw || \"\").length + \")\" };\n }\n}\n\nfunction renderModalError(title, detail) {\n return '<div class=\"modal-error\" style=\"padding:40px 20px;text-align:center;\">'\n + '<div style=\"font-size:28px;margin-bottom:8px;\">\\u26A0\\uFE0F</div>'\n + '<div style=\"font-weight:600;font-size:14px;color:var(--cld-text);margin-bottom:6px;\">' + esc(title) + \"</div>\"\n + '<div style=\"font-size:12px;color:var(--cld-text3);max-width:400px;margin:0 auto;\">' + esc(detail) + \"</div>\"\n + \"</div>\";\n}\n";
|
|
6
|
-
export declare const SHARED_JS_MODAL = "\nfunction closeModal() {\n var ov = document.querySelector(\".modal-overlay\");\n if (ov) ov.remove();\n}\n\nfunction openModal(headerHtml, bodyHtml) {\n closeModal();\n var h = '<div class=\"modal-overlay\"><div class=\"modal\">';\n h += headerHtml;\n h += '<div class=\"modal-body\">' + bodyHtml + \"</div>\";\n h += \"</div></div>\";\n document.body.insertAdjacentHTML(\"beforeend\", h);\n\n var overlay = document.querySelector(\".modal-overlay\");\n overlay.addEventListener(\"click\", function(e) {\n if (e.target === overlay || e.target.classList.contains(\"modal-close\")) {\n closeModal();\n return;\n }\n var el = e.target;\n while (el && el !== overlay) {\n if (el.id === \"load-more-derived-btn\") {\n loadMoreDerived(el);\n return;\n }\n if (el.classList && el.classList.contains(\"link-val\") && el.dataset.url) {\n app._rpc(\"ui/open-link\", { url: el.dataset.url });\n return;\n }\n if (el.classList && el.classList.contains(\"derived-open\") && el.dataset.url) {\n app._rpc(\"ui/open-link\", { url: el.dataset.url });\n return;\n }\n el = el.parentElement;\n }\n });\n document.addEventListener(\"keydown\", function onEsc(e) {\n if (e.key === \"Escape\") { closeModal(); document.removeEventListener(\"keydown\", onEsc); }\n });\n}\n\nfunction modalHeader(name, url, sub, resource) {\n var h = '<div class=\"modal-header\">';\n var thumb = thumbUrl(url, 60, 60, resource);\n if (thumb) h += '<img class=\"modal-header-thumb\" src=\"' + esc(thumb) + '\">';\n h += '<div class=\"modal-header-info\">';\n h += '<h2>' + esc(name) + \"</h2>\";\n if (sub) h += '<div class=\"modal-header-sub\">' + esc(sub) + \"</div>\";\n h += \"</div>\";\n h += '<button class=\"modal-close\" title=\"Close\">\\u2715</button>';\n h += \"</div>\";\n return h;\n}\n";
|
|
6
|
+
export declare const SHARED_JS_MODAL = "\nfunction closeModal() {\n var ov = document.querySelector(\".modal-overlay\");\n if (ov) ov.remove();\n}\n\n/* Position the modal overlay so it's centered on the part of the iframe\n * the user is currently looking at. Inside an iframe that the host has\n * sized to scrollHeight, position: fixed anchors to iframe-center, not\n * the user's visible band \u2014 so we compute the visible band ourselves\n * via IntersectionObserver and place the overlay absolutely there. */\nfunction positionModalInVisibleArea(overlay) {\n if (!overlay) return;\n var docH = Math.max(document.documentElement.scrollHeight, window.innerHeight);\n\n // Try IntersectionObserver first (works cross-origin too)\n if (typeof IntersectionObserver !== \"undefined\") {\n var probe = document.createElement(\"div\");\n probe.style.cssText = \"position:absolute;left:0;top:0;width:1px;height:\" + docH + \"px;pointer-events:none;visibility:hidden;\";\n document.body.appendChild(probe);\n var io = new IntersectionObserver(function(entries) {\n try {\n var entry = entries[0];\n var rect = entry.intersectionRect;\n var rootRect = entry.rootBounds || entry.boundingClientRect;\n // intersectionRect.top is in viewport coords; visible band of the\n // document is from (probe.top + rect.top) to (probe.top + rect.bottom).\n var probeTop = entry.boundingClientRect.top; // viewport-relative\n var visibleTop = rect.top - probeTop; // document-relative\n var visibleHeight = rect.height;\n if (visibleHeight > 0) {\n overlay.style.top = visibleTop + \"px\";\n overlay.style.height = visibleHeight + \"px\";\n } else {\n // Fallback: probe not intersecting (shouldn't happen with full-height probe)\n overlay.style.top = (window.scrollY || 0) + \"px\";\n overlay.style.height = window.innerHeight + \"px\";\n }\n } finally {\n io.disconnect();\n if (probe.parentNode) probe.parentNode.removeChild(probe);\n }\n });\n io.observe(probe);\n return;\n }\n\n // Fallback: same-origin only \u2014 read iframe's bounding rect\n var rect = document.documentElement.getBoundingClientRect();\n var visibleTop = rect.top < 0 ? -rect.top : 0;\n var visibleHeight = Math.min(window.innerHeight, rect.bottom) - Math.max(0, rect.top);\n overlay.style.top = visibleTop + \"px\";\n overlay.style.height = Math.max(visibleHeight, 200) + \"px\";\n}\n\nfunction openModal(headerHtml, bodyHtml) {\n closeModal();\n var h = '<div class=\"modal-overlay\"><div class=\"modal\">';\n h += headerHtml;\n h += '<div class=\"modal-body\">' + bodyHtml + \"</div>\";\n h += \"</div></div>\";\n document.body.insertAdjacentHTML(\"beforeend\", h);\n\n var overlay = document.querySelector(\".modal-overlay\");\n positionModalInVisibleArea(overlay);\n overlay.addEventListener(\"click\", function(e) {\n if (e.target === overlay || e.target.classList.contains(\"modal-close\")) {\n closeModal();\n return;\n }\n var el = e.target;\n while (el && el !== overlay) {\n if (el.id === \"load-more-derived-btn\") {\n loadMoreDerived(el);\n return;\n }\n if (el.classList && el.classList.contains(\"link-val\") && el.dataset.url) {\n app._rpc(\"ui/open-link\", { url: el.dataset.url });\n return;\n }\n if (el.classList && el.classList.contains(\"derived-open\") && el.dataset.url) {\n app._rpc(\"ui/open-link\", { url: el.dataset.url });\n return;\n }\n el = el.parentElement;\n }\n });\n document.addEventListener(\"keydown\", function onEsc(e) {\n if (e.key === \"Escape\") { closeModal(); document.removeEventListener(\"keydown\", onEsc); }\n });\n}\n\nfunction modalHeader(name, url, sub, resource) {\n var h = '<div class=\"modal-header\">';\n var thumb = thumbUrl(url, 60, 60, resource);\n if (thumb) h += '<img class=\"modal-header-thumb\" src=\"' + esc(thumb) + '\">';\n h += '<div class=\"modal-header-info\">';\n h += '<h2>' + esc(name) + \"</h2>\";\n if (sub) h += '<div class=\"modal-header-sub\">' + esc(sub) + \"</div>\";\n h += \"</div>\";\n h += '<button class=\"modal-close\" title=\"Close\">\\u2715</button>';\n h += \"</div>\";\n return h;\n}\n";
|
|
7
7
|
export declare const SHARED_JS_DETAIL_RENDERERS = "\nfunction statusBadge(text) {\n if (!text) return \"\";\n var t = text.toLowerCase().trim();\n var cls = \"status-badge\";\n if (t === \"complete\" || t === \"approved\") cls += \" status-ok\";\n else if (t === \"pending\" || t === \"queued\") cls += \" status-warn\";\n else if (t === \"rejected\" || t === \"aborted\" || t === \"failed\" || t === \"error\") cls += \" status-err\";\n return '<span class=\"' + cls + '\">' + esc(text) + '</span>';\n}\n\nvar OPEN_SECTIONS = { tags:1, context:1, timestamps:1, moderation:1, asset_info:1 };\nfunction sectionStart(id) {\n return '<details class=\"detail-section\"' + (OPEN_SECTIONS[id] ? ' open' : '') + '>';\n}\n\nfunction renderAssetGrid(r) {\n var fields = [\n [\"public_id\", \"Public ID\", r.public_id],\n [\"asset_id\", \"Asset ID\", r.asset_id],\n [\"display_name\", \"Display Name\", r.display_name],\n [\"format\", \"Format\", (r.format || \"\").toUpperCase()],\n [\"resource_type\", \"Resource Type\", r.resource_type],\n [\"type\", \"Type\", r.type],\n [\"\", \"Dimensions\", (r.width && r.height) ? r.width + \" \\u00d7 \" + r.height : \"\"],\n [\"duration\", \"Duration\", r.duration ? fmtDuration(r.duration) + \" (\" + r.duration.toFixed(2) + \"s)\" : \"\"],\n [\"bytes\", \"File Size\", r.bytes ? fmtBytes(r.bytes) + \" (\" + r.bytes.toLocaleString() + \" bytes)\" : \"\"],\n [\"created_at\", \"Created\", fmtDate(r.created_at)],\n [\"uploaded_at\", \"Uploaded\", fmtDate(r.uploaded_at)],\n [\"access_mode\", \"Access Mode\", r.access_mode],\n [\"asset_folder\", \"Asset Folder\", r.asset_folder || \"\\u2014\"],\n [\"filename\", \"Filename\", r.filename],\n [\"original_filename\", \"Original Filename\", r.original_filename],\n [\"version\", \"Version\", r.version],\n [\"version_id\", \"Version ID\", r.version_id],\n [\"status\", \"Status\", r.status],\n [\"substatus\", \"Substatus\", r.substatus],\n [\"resource_subtype\", \"Subtype\", r.resource_subtype],\n [\"backup\", \"Backup\", r.backup != null ? String(r.backup) : \"\"],\n [\"backup_bytes\", \"Backup Size\", r.backup_bytes ? fmtBytes(r.backup_bytes) : \"\"],\n [\"pages\", \"Pages\", r.pages],\n [\"pixels\", \"Pixels\", r.pixels ? r.pixels.toLocaleString() : \"\"],\n [\"animated\", \"Animated\", r.animated != null ? String(r.animated) : \"\"],\n [\"placeholder\", \"Placeholder\", r.placeholder != null ? String(r.placeholder) : \"\"],\n [\"etag\", \"ETag\", r.etag],\n [\"illustration_score\", \"Illustration Score\", r.illustration_score != null ? String(r.illustration_score) : \"\"],\n [\"semi_transparent\", \"Semi-Transparent\", r.semi_transparent != null ? String(r.semi_transparent) : \"\"],\n [\"grayscale\", \"Grayscale\", r.grayscale != null ? String(r.grayscale) : \"\"],\n ];\n if (r.is_audio) fields.push([\"\", \"Audio\", \"Yes\"]);\n if (r.audio_codec) fields.push([\"\", \"Audio Codec\", r.audio_codec]);\n if (r.audio_frequency) fields.push([\"\", \"Audio Frequency\", r.audio_frequency + \" Hz\"]);\n if (r.channels) fields.push([\"\", \"Channels\", r.channel_layout ? r.channels + \" (\" + r.channel_layout + \")\" : String(r.channels)]);\n if (r.bit_rate) fields.push([\"\", \"Bit Rate\", Math.round(r.bit_rate / 1000) + \" kbps\"]);\n\n var h = '<div class=\"detail-grid\">';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][2] && fields[i][2] !== 0) continue;\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(fields[i][1]) + tip(fields[i][0]) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(String(fields[i][2])) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div>\";\n\n if (r.url || r.secure_url) {\n h += '<div style=\"margin-top:10px\">';\n if (r.url) {\n h += '<div class=\"meta-row\"><span class=\"meta-key\">URL</span>';\n h += '<span class=\"meta-val link-val detail-cell-val\" data-url=\"' + esc(r.url) + '\">' + esc(r.url) + \"</span></div>\";\n }\n if (r.secure_url) {\n h += '<div class=\"meta-row\"><span class=\"meta-key\">Secure URL</span>';\n h += '<span class=\"meta-val link-val detail-cell-val\" data-url=\"' + esc(r.secure_url) + '\">' + esc(r.secure_url) + \"</span></div>\";\n }\n if (r.playback_url) {\n h += '<div class=\"meta-row\"><span class=\"meta-key\">Playback URL</span>';\n h += '<span class=\"meta-val link-val detail-cell-val\" data-url=\"' + esc(r.playback_url) + '\">' + esc(r.playback_url) + \"</span></div>\";\n }\n h += \"</div>\";\n }\n return h;\n}\n\nfunction renderAudioInfo(r) {\n var a = r.audio || (r.video_metadata && r.video_metadata.audio);\n var codec = (a && a.codec) || r.audio_codec || \"\";\n var bitRate = (a && a.bit_rate) || r.audio_bit_rate || \"\";\n var freq = (a && a.frequency) || r.audio_frequency || \"\";\n var ch = (a && a.channels) || r.channels || \"\";\n var layout = (a && a.channel_layout) || r.channel_layout || \"\";\n if (!codec && !bitRate && !freq && !ch) return \"\";\n var fields = [\n [\"Codec\", codec],\n [\"Bit Rate\", bitRate ? Math.round(Number(bitRate) / 1000) + \" kbps\" : \"\"],\n [\"Frequency\", freq ? Number(freq).toLocaleString() + \" Hz\" : \"\"],\n [\"Channels\", layout ? ch + \" (\" + layout + \")\" : ch],\n ];\n var h = sectionStart(\"audio_info\");\n h += '<summary class=\"detail-section-title\">Audio Info</summary>';\n h += '<div class=\"detail-grid\">';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][1] && fields[i][1] !== 0) continue;\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(fields[i][0]) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(String(fields[i][1])) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction renderVideoInfo(r) {\n var v = r.video || (r.video_metadata && r.video_metadata.video);\n var codec = (v && v.codec) || r.codec || \"\";\n var profile = (v && v.profile) || r.profile || \"\";\n var level = (v && v.level) || r.level || \"\";\n var pixFmt = (v && v.pix_format) || r.pix_format || \"\";\n var vbr = (v && v.bit_rate) || r.video_bit_rate || \"\";\n var dar = (v && v.dar) || r.dar || \"\";\n var tb = (v && v.time_base) || r.time_base || \"\";\n if (!codec && !profile && !pixFmt && !vbr) return \"\";\n var fields = [\n [\"Codec\", codec],\n [\"Profile\", profile],\n [\"Level\", level],\n [\"Pixel Format\", pixFmt],\n [\"Bit Rate\", vbr ? Math.round(Number(vbr) / 1000) + \" kbps\" : \"\"],\n [\"Aspect Ratio\", dar],\n [\"Time Base\", tb],\n [\"Frame Rate\", r.frame_rate ? r.frame_rate + \" fps\" : \"\"],\n [\"Frames\", r.nb_frames],\n [\"Rotation\", (r.rotation != null && r.rotation !== 0) ? r.rotation + \"\\u00b0\" : \"\"],\n ];\n var h = sectionStart(\"video_info\");\n h += '<summary class=\"detail-section-title\">Video Info</summary>';\n h += '<div class=\"detail-grid\">';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][1] && fields[i][1] !== 0) continue;\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(fields[i][0]) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(String(fields[i][1])) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction renderTags(tags) {\n if (!tags || !tags.length) return \"\";\n var h = sectionStart(\"tags\");\n h += '<summary class=\"detail-section-title\">Tags' + tip(\"tags\") + ' <span class=\"count\">' + tags.length + \"</span></summary>\";\n h += '<div class=\"chip-list\">';\n for (var i = 0; i < tags.length; i++) {\n h += '<span class=\"chip chip-tag\">' + esc(tags[i]) + \"</span>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction classifyMetaVal(v) {\n if (Array.isArray(v)) return \"set\";\n if (typeof v === \"number\") return \"int\";\n if (typeof v === \"string\" && /^\\d{4}-\\d{2}-\\d{2}/.test(v)) return \"date\";\n return \"string\";\n}\n\nfunction renderMetadata(meta) {\n if (!meta) return \"\";\n var keys = Object.keys(meta);\n if (!keys.length) return \"\";\n\n var groups = { string: [], int: [], date: [], set: [] };\n for (var i = 0; i < keys.length; i++) {\n var t = classifyMetaVal(meta[keys[i]]);\n groups[t].push({ key: keys[i], val: meta[keys[i]] });\n }\n\n var h = sectionStart(\"metadata\");\n h += '<summary class=\"detail-section-title\">Structured Metadata' + tip(\"metadata\") + ' <span class=\"count\">' + keys.length + \"</span></summary>\";\n\n if (groups.set.length) {\n for (var s = 0; s < groups.set.length; s++) {\n var item = groups.set[s];\n h += '<div class=\"meta-row\" style=\"flex-direction:column;gap:4px\">';\n h += '<span class=\"meta-key\" style=\"max-width:100%\">' + esc(prettyKey(item.key)) + \"</span>\";\n h += '<div class=\"chip-list\">';\n for (var si = 0; si < item.val.length; si++) {\n h += '<span class=\"chip chip-set\">' + esc(String(item.val[si])) + \"</span>\";\n }\n h += \"</div></div>\";\n }\n }\n if (groups.date.length) {\n for (var d = 0; d < groups.date.length; d++) {\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\" title=\"' + esc(groups.date[d].key) + '\">' + esc(prettyKey(groups.date[d].key)) + \"</span>\";\n h += '<span class=\"meta-val\"><span class=\"chip chip-date\">' + esc(groups.date[d].val) + \"</span></span></div>\";\n }\n }\n if (groups.int.length) {\n for (var n = 0; n < groups.int.length; n++) {\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\" title=\"' + esc(groups.int[n].key) + '\">' + esc(prettyKey(groups.int[n].key)) + \"</span>\";\n h += '<span class=\"meta-val\"><span class=\"chip chip-int\">' + esc(String(groups.int[n].val)) + \"</span></span></div>\";\n }\n }\n if (groups.string.length) {\n for (var st = 0; st < groups.string.length; st++) {\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\" title=\"' + esc(groups.string[st].key) + '\">' + esc(prettyKey(groups.string[st].key)) + \"</span>\";\n h += '<span class=\"meta-val\">' + esc(String(groups.string[st].val)) + \"</span></div>\";\n }\n }\n\n h += \"</details>\";\n return h;\n}\n\nfunction renderDerived(derived, nextCursor, assetId) {\n if (!derived || !derived.length) return \"\";\n var h = sectionStart(\"derived\");\n h += '<summary class=\"detail-section-title\">Derived Assets' + tip(\"derived\") + ' <span class=\"count\" id=\"derived-count\">' + derived.length + (nextCursor ? \"+\" : \"\") + \"</span></summary>\";\n h += '<div id=\"derived-list\">';\n for (var i = 0; i < derived.length; i++) {\n var d = derived[i];\n var dUrl = d.secure_url || d.url || \"\";\n h += '<div class=\"derived-card\">';\n if (dUrl) h += '<img class=\"derived-thumb\" src=\"' + esc(dUrl) + '\">';\n h += '<div class=\"derived-info\">';\n h += '<div class=\"derived-tx\">' + esc(d.transformation || \"\") + \"</div>\";\n h += '<div class=\"derived-meta\">' + (d.format || \"\").toUpperCase() + \" · \" + fmtBytes(d.bytes) + \"</div>\";\n h += \"</div>\";\n if (dUrl) h += '<span class=\"derived-open\" data-url=\"' + esc(dUrl) + '\">Open</span>';\n h += \"</div>\";\n }\n h += \"</div>\";\n if (nextCursor && assetId) {\n h += '<div style=\"text-align:center;padding:8px 0;\">';\n h += '<button class=\"prompt-btn\" id=\"load-more-derived-btn\" data-cursor=\"' + esc(nextCursor) + '\" data-asset-id=\"' + esc(assetId) + '\">Load More Derived</button>';\n h += \"</div>\";\n }\n h += \"</details>\";\n return h;\n}\n\nfunction renderContext(ctx) {\n if (!ctx || typeof ctx !== \"object\") return \"\";\n var pairs = ctx.custom || ctx;\n if (typeof pairs !== \"object\") return \"\";\n var keys = Object.keys(pairs);\n if (!keys.length) return \"\";\n var h = sectionStart(\"context\");\n h += '<summary class=\"detail-section-title\">Context' + tip(\"context\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n for (var i = 0; i < keys.length; i++) {\n var v = pairs[keys[i]];\n if (v === null || v === undefined) continue;\n var vs = typeof v === \"object\" ? JSON.stringify(v) : String(v);\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(prettyKey(keys[i])) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(vs) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction summarizeInfoVal(v) {\n if (typeof v !== \"object\" || v === null) return esc(String(v));\n if (v.status) return statusBadge(v.status);\n var parts = [];\n var keys = Object.keys(v);\n for (var i = 0; i < keys.length; i++) {\n var sv = v[keys[i]];\n if (sv && typeof sv === \"object\" && sv.status) {\n parts.push(esc(prettyKey(keys[i])) + \": \" + statusBadge(sv.status));\n } else if (typeof sv === \"string\" || typeof sv === \"number\" || typeof sv === \"boolean\") {\n parts.push(esc(prettyKey(keys[i])) + \": \" + esc(String(sv)));\n } else if (sv && typeof sv === \"object\") {\n parts.push(esc(prettyKey(keys[i])) + \": \" + summarizeInfoVal(sv));\n }\n }\n return parts.length ? parts.join(\", \") : esc(JSON.stringify(v));\n}\n\nfunction renderImageMetadata(meta) {\n if (!meta || typeof meta !== \"object\") return \"\";\n var keys = Object.keys(meta);\n if (!keys.length) return \"\";\n var h = sectionStart(\"image_metadata\");\n h += '<summary class=\"detail-section-title\">Media Metadata' + tip(\"image_metadata\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n for (var i = 0; i < keys.length; i++) {\n var v = meta[keys[i]];\n if (v === null || v === undefined) continue;\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(prettyKey(keys[i])) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(String(v)) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction renderInfo(info) {\n if (!info || typeof info !== \"object\") return \"\";\n var keys = Object.keys(info);\n if (!keys.length) return \"\";\n var rows = \"\";\n for (var i = 0; i < keys.length; i++) {\n var v = info[keys[i]];\n if (v === null || v === undefined) continue;\n var statusStr = summarizeInfoVal(v);\n if (statusStr.length > 300) statusStr = statusStr.substring(0, 297) + \"...\";\n rows += '<div class=\"detail-cell\">';\n rows += '<div class=\"detail-cell-key\">' + esc(prettyKey(keys[i])) + \"</div>\";\n rows += '<div class=\"detail-cell-val\">' + statusStr + \"</div>\";\n rows += \"</div>\";\n }\n if (!rows) return \"\";\n var h = sectionStart(\"info\");\n h += '<summary class=\"detail-section-title\">Info' + tip(\"info\") + '</summary>';\n h += '<div class=\"detail-grid\">' + rows + \"</div></details>\";\n return h;\n}\n\nfunction renderLastUpdated(lu) {\n if (!lu) return \"\";\n var keys = Object.keys(lu);\n if (!keys.length) return \"\";\n var h = sectionStart(\"timestamps\");\n h += '<summary class=\"detail-section-title\">Timestamps' + tip(\"last_updated\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n for (var i = 0; i < keys.length; i++) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(prettyKey(keys[i])) + \"</div>\";\n h += '<div class=\"detail-cell-val\">' + esc(fmtDate(lu[keys[i]])) + \"</div>\";\n h += \"</div>\";\n }\n h += \"</div></details>\";\n return h;\n}\n\nfunction renderHeroPreview(r) {\n var url = r.secure_url || r.url || \"\";\n var rt = r.resource_type || \"\";\n var h = \"\";\n\n if (rt === \"raw\") {\n h += '<div style=\"text-align:center;padding:30px 20px;background:var(--cld-bg3)\">';\n h += '<div class=\"file-icon\">' + fileTypeIcon(r.format);\n h += '<div class=\"file-icon-label\">' + esc((r.format || \"FILE\").toUpperCase()) + \"</div></div></div>\";\n return h;\n }\n\n if (isAudioResource(r)) {\n var waveform = thumbUrl(url, 600, 120, r);\n h += '<div class=\"hero-audio-wrap\">';\n if (waveform) {\n h += '<img class=\"hero-audio-waveform\" src=\"' + esc(waveform) + '\">';\n } else {\n h += '<div class=\"hero-audio-note\">\\u266B</div>';\n }\n h += '<audio controls preload=\"metadata\" src=\"' + esc(mediaUrl(url, r)) + '\"></audio>';\n h += \"</div>\";\n return h;\n }\n\n if (rt === \"video\") {\n var poster = thumbUrl(url, 600, 300, r);\n var src = mediaUrl(url, r);\n h += '<div style=\"position:relative\">';\n h += '<video class=\"hero-video\" controls preload=\"metadata\"';\n if (poster) h += ' poster=\"' + esc(poster) + '\"';\n h += '><source src=\"' + esc(src) + '\"></video>';\n if (r.duration) {\n h += '<div class=\"duration-badge\">' + fmtDuration(r.duration) + \"</div>\";\n }\n h += \"</div>\";\n return h;\n }\n\n var thumb = thumbUrl(url, 600, 220, r);\n if (thumb) {\n h += '<img class=\"modal-hero\" src=\"' + esc(thumb) + '\">';\n }\n return h;\n}\n\nfunction renderMediaModalBody(r) {\n var url = r.secure_url || r.url || \"\";\n var h = \"\";\n\n if (isAudioResource(r)) {\n var waveform = thumbUrl(url, 560, 100, r);\n h += '<div class=\"media-modal-audio-wrap\">';\n if (waveform) h += '<img src=\"' + esc(waveform) + '\">';\n h += '<audio controls autoplay preload=\"metadata\" src=\"' + esc(mediaUrl(url, r)) + '\"></audio>';\n h += \"</div>\";\n return h;\n }\n\n var poster = thumbUrl(url, 600, 340, r);\n h += '<video class=\"media-modal-video\" controls autoplay preload=\"metadata\"';\n if (poster) h += ' poster=\"' + esc(poster) + '\"';\n h += '><source src=\"' + esc(mediaUrl(url, r)) + '\"></video>';\n return h;\n}\n\nfunction renderColors(colors, predominant) {\n var hasColors = colors && colors.length;\n var hasPred = predominant && typeof predominant === \"object\" && Object.keys(predominant).length;\n if (!hasColors && !hasPred) return \"\";\n\n var h = sectionStart(\"colors\");\n h += '<summary class=\"detail-section-title\">Colors' + tip(\"colors\") + '</summary>';\n\n if (hasColors) {\n h += '<div class=\"color-row\">';\n for (var i = 0; i < colors.length; i++) {\n var c = colors[i];\n var hex = c[0] || \"\";\n var pct = c[1] != null ? parseFloat(c[1]).toFixed(1) + \"%\" : \"\";\n h += '<div class=\"color-swatch\">';\n h += '<div class=\"color-swatch-box\" style=\"background:' + esc(hex) + '\"></div>';\n h += '<div class=\"color-swatch-label\">' + esc(hex) + '</div>';\n if (pct) h += '<div class=\"color-swatch-pct\">' + esc(pct) + '</div>';\n h += '</div>';\n }\n h += '</div>';\n }\n\n if (hasPred) {\n var pkeys = Object.keys(predominant);\n for (var p = 0; p < pkeys.length; p++) {\n var group = predominant[pkeys[p]];\n if (!group || !group.length) continue;\n h += '<div class=\"color-group-title\">' + esc(prettyKey(pkeys[p])) + '</div>';\n h += '<div class=\"color-row\">';\n for (var gi = 0; gi < group.length; gi++) {\n var gc = group[gi];\n var ghex = gc[0] || \"\";\n var gpct = gc[1] != null ? parseFloat(gc[1]).toFixed(1) + \"%\" : \"\";\n var isHex = ghex.charAt(0) === \"#\";\n h += '<div class=\"color-swatch\">';\n if (isHex) h += '<div class=\"color-swatch-box\" style=\"background:' + esc(ghex) + '\"></div>';\n h += '<div class=\"color-swatch-label\">' + esc(ghex) + '</div>';\n if (gpct) h += '<div class=\"color-swatch-pct\">' + esc(gpct) + '</div>';\n h += '</div>';\n }\n h += '</div>';\n }\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderModerationSection(moderation, kind, status) {\n var hasArr = moderation && moderation.length;\n if (!hasArr && !kind && !status) return \"\";\n\n var h = sectionStart(\"moderation\");\n h += '<summary class=\"detail-section-title\">Moderation' + tip(\"moderation\") + '</summary>';\n\n if (hasArr) {\n for (var i = 0; i < moderation.length; i++) {\n var m = moderation[i];\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\">' + esc(m.kind || \"unknown\") + '</span>';\n h += '<span class=\"meta-val\">' + (m.status ? statusBadge(m.status) : \"\");\n if (m.updated_at) h += ' \\u00b7 ' + fmtDate(m.updated_at);\n h += '</span></div>';\n }\n } else {\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\">' + esc(kind || \"unknown\") + '</span>';\n h += '<span class=\"meta-val\">' + (status ? statusBadge(status) : \"\") + '</span></div>';\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderAccessControl(acl) {\n if (!acl || !acl.length) return \"\";\n\n var h = sectionStart(\"access_control\");\n h += '<summary class=\"detail-section-title\">Access Control' + tip(\"access_control\") + ' <span class=\"count\">' + acl.length + '</span></summary>';\n\n for (var i = 0; i < acl.length; i++) {\n var rule = acl[i];\n h += '<div class=\"meta-row\" style=\"flex-direction:column;gap:2px\">';\n h += '<span class=\"meta-key\" style=\"max-width:100%\">' + esc(prettyKey(rule.access_type || \"unknown\")) + '</span>';\n var parts = [];\n if (rule.start) parts.push(\"Start: \" + (typeof rule.start === \"number\" ? new Date(rule.start * 1000).toISOString() : String(rule.start)));\n if (rule.end) parts.push(\"End: \" + (typeof rule.end === \"number\" ? new Date(rule.end * 1000).toISOString() : String(rule.end)));\n if (rule.key) parts.push(\"Key: \" + rule.key);\n if (parts.length) h += '<span class=\"meta-val\" style=\"text-align:left;font-size:11px;color:var(--cld-text3)\">' + esc(parts.join(\" \\u00b7 \")) + '</span>';\n h += '</div>';\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderVersions(versions) {\n if (!versions || !versions.length) return \"\";\n\n var h = sectionStart(\"versions\");\n h += '<summary class=\"detail-section-title\">Versions <span class=\"count\">' + versions.length + '</span></summary>';\n\n for (var i = 0; i < versions.length; i++) {\n var v = versions[i];\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\">' + esc(v.version_id || String(v.version || \"#\" + (i + 1))) + '</span>';\n var parts = [];\n if (v.format) parts.push(v.format.toUpperCase());\n if (v.size) parts.push(fmtBytes(v.size));\n if (v.time) parts.push(fmtDate(v.time));\n if (v.restorable != null) parts.push(v.restorable ? \"Restorable\" : \"Not restorable\");\n h += '<span class=\"meta-val\">' + esc(parts.join(\" \\u00b7 \")) + '</span>';\n h += '</div>';\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderEager(eager) {\n if (!eager || !eager.length) return \"\";\n\n var h = sectionStart(\"eager\");\n h += '<summary class=\"detail-section-title\">Eager Transformations <span class=\"count\">' + eager.length + '</span></summary>';\n\n for (var i = 0; i < eager.length; i++) {\n var e = eager[i];\n var eUrl = e.secure_url || e.url || \"\";\n h += '<div class=\"derived-card\">';\n if (eUrl) h += '<img class=\"derived-thumb\" src=\"' + esc(eUrl) + '\">';\n h += '<div class=\"derived-info\">';\n h += '<div class=\"derived-tx\">' + esc(e.transformation || \"\") + '</div>';\n var meta = [];\n if (e.format) meta.push(e.format.toUpperCase());\n if (e.width && e.height) meta.push(e.width + \"\\u00d7\" + e.height);\n if (e.bytes) meta.push(fmtBytes(e.bytes));\n h += '<div class=\"derived-meta\">' + esc(meta.join(\" \\u00b7 \")) + '</div>';\n h += '</div>';\n if (eUrl) h += '<span class=\"derived-open\" data-url=\"' + esc(eUrl) + '\">Open</span>';\n h += '</div>';\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderCoordinates(faces, coordinates) {\n var faceArr = (coordinates && coordinates.faces) || faces || [];\n var customArr = (coordinates && coordinates.custom) || [];\n if (!faceArr.length && !customArr.length) return \"\";\n\n var h = sectionStart(\"coordinates\");\n h += '<summary class=\"detail-section-title\">Coordinates' + tip(\"coordinates\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n\n if (faceArr.length) {\n h += '<div class=\"detail-cell full-width\">';\n h += '<div class=\"detail-cell-key\">Faces' + tip(\"faces\") + '</div>';\n h += '<div class=\"detail-cell-val\">' + faceArr.length + ' region' + (faceArr.length !== 1 ? 's' : '');\n for (var i = 0; i < Math.min(faceArr.length, 5); i++) {\n var f = faceArr[i];\n if (f && f.length >= 4) h += ' [' + f[0] + ',' + f[1] + ',' + f[2] + ',' + f[3] + ']';\n }\n if (faceArr.length > 5) h += ' \\u2026';\n h += '</div></div>';\n }\n\n if (customArr.length) {\n h += '<div class=\"detail-cell full-width\">';\n h += '<div class=\"detail-cell-key\">Custom Regions</div>';\n h += '<div class=\"detail-cell-val\">' + customArr.length + ' region' + (customArr.length !== 1 ? 's' : '');\n for (var j = 0; j < Math.min(customArr.length, 5); j++) {\n var c = customArr[j];\n if (c && c.length >= 4) h += ' [' + c[0] + ',' + c[1] + ',' + c[2] + ',' + c[3] + ']';\n }\n if (customArr.length > 5) h += ' \\u2026';\n h += '</div></div>';\n }\n\n h += '</div></details>';\n return h;\n}\n\nfunction renderDerivatives(derivatives) {\n if (!derivatives || !derivatives.length) return \"\";\n\n var h = sectionStart(\"derivatives\");\n h += '<summary class=\"detail-section-title\">Derivatives' + tip(\"derivatives\") + ' <span class=\"count\">' + derivatives.length + '</span></summary>';\n\n for (var i = 0; i < derivatives.length; i++) {\n var d = derivatives[i];\n var dUrl = d.secure_url || \"\";\n h += '<div class=\"derived-card\">';\n if (dUrl) h += '<img class=\"derived-thumb\" src=\"' + esc(dUrl) + '\">';\n h += '<div class=\"derived-info\">';\n h += '<div class=\"derived-tx\">' + esc(d.transformation || \"\") + '</div>';\n if (d.id) h += '<div class=\"derived-meta\">ID: ' + esc(d.id) + '</div>';\n h += '</div>';\n if (dUrl) h += '<span class=\"derived-open\" data-url=\"' + esc(dUrl) + '\">Open</span>';\n h += '</div>';\n }\n\n h += '</details>';\n return h;\n}\n\nfunction renderQualityAnalysis(qa, score) {\n if (!qa && score == null) return \"\";\n\n var h = sectionStart(\"quality_analysis\");\n h += '<summary class=\"detail-section-title\">Quality Analysis' + tip(\"quality_analysis\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n\n if (score != null) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">Overall Score</div>';\n h += '<div class=\"detail-cell-val\">' + esc(String(score)) + '</div>';\n h += '</div>';\n }\n\n if (qa && typeof qa === \"object\") {\n var keys = Object.keys(qa);\n for (var i = 0; i < keys.length; i++) {\n var v = qa[keys[i]];\n if (v == null) continue;\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(prettyKey(keys[i])) + '</div>';\n h += '<div class=\"detail-cell-val\">' + esc(typeof v === \"number\" ? v.toFixed(2) : String(v)) + '</div>';\n h += '</div>';\n }\n }\n\n h += '</div></details>';\n return h;\n}\n\nfunction renderAccessibilityAnalysis(aa) {\n if (!aa || typeof aa !== \"object\") return \"\";\n\n var h = sectionStart(\"accessibility_analysis\");\n h += '<summary class=\"detail-section-title\">Accessibility Analysis' + tip(\"accessibility_analysis\") + '</summary>';\n h += '<div class=\"detail-grid\">';\n\n if (aa.colorblind_accessibility_score != null) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">Colorblind Score</div>';\n h += '<div class=\"detail-cell-val\">' + esc(String(aa.colorblind_accessibility_score)) + '</div>';\n h += '</div>';\n }\n\n var cba = aa.colorblind_accessibility_analysis;\n if (cba && typeof cba === \"object\") {\n if (cba.distinct_edges != null) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">Distinct Edges</div>';\n h += '<div class=\"detail-cell-val\">' + esc(String(cba.distinct_edges)) + '</div>';\n h += '</div>';\n }\n if (cba.distinct_colors != null) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">Distinct Colors</div>';\n h += '<div class=\"detail-cell-val\">' + esc(String(cba.distinct_colors)) + '</div>';\n h += '</div>';\n }\n if (cba.most_indistinct_pair && cba.most_indistinct_pair.length) {\n h += '<div class=\"detail-cell full-width\">';\n h += '<div class=\"detail-cell-key\">Most Indistinct Pair</div>';\n h += '<div class=\"detail-cell-val\" style=\"display:flex;gap:6px;align-items:center\">';\n for (var i = 0; i < cba.most_indistinct_pair.length; i++) {\n var hex = cba.most_indistinct_pair[i];\n h += '<span style=\"display:inline-flex;align-items:center;gap:4px\">';\n h += '<span style=\"width:16px;height:16px;border-radius:3px;border:1px solid var(--cld-border);background:' + esc(hex) + ';display:inline-block;vertical-align:middle\"></span>';\n h += esc(hex);\n h += '</span>';\n }\n h += '</div></div>';\n }\n }\n\n h += '</div></details>';\n return h;\n}\n\nfunction renderRelatedAssets(related) {\n if (!related || !related.length) return \"\";\n\n var h = sectionStart(\"related_assets\");\n h += '<summary class=\"detail-section-title\">Related Assets' + tip(\"related_assets\") + ' <span class=\"count\">' + related.length + '</span></summary>';\n\n for (var i = 0; i < related.length; i++) {\n var ra = related[i];\n h += '<div class=\"meta-row\">';\n h += '<span class=\"meta-key\">' + esc(ra.asset_id || ra.public_id || \"#\" + (i + 1)) + '</span>';\n var parts = [];\n if (ra.format) parts.push(ra.format.toUpperCase());\n if (ra.resource_type) parts.push(ra.resource_type);\n if (ra.bytes) parts.push(fmtBytes(ra.bytes));\n h += '<span class=\"meta-val\">' + esc(parts.join(\" \\u00b7 \")) + '</span>';\n h += '</div>';\n }\n\n h += '</details>';\n return h;\n}\n\nvar RENDERED_KEYS = {\n asset_id:1, public_id:1, version:1, version_id:1, signature:1,\n width:1, height:1, format:1, resource_type:1, created_at:1,\n tags:1, bytes:1, type:1, etag:1, placeholder:1, url:1,\n secure_url:1, asset_folder:1, display_name:1, access_mode:1,\n pages:1, duration:1, is_audio:1, audio_codec:1, audio_frequency:1,\n channels:1, channel_layout:1, bit_rate:1, backup:1, original_filename:1,\n metadata:1, info:1, derived:1, context:1, image_metadata:1, media_metadata:1,\n colors:1, predominant:1, moderation:1, moderation_kind:1, moderation_status:1,\n faces:1, coordinates:1, eager:1, animated:1, illustration_score:1,\n semi_transparent:1, grayscale:1, status:1, substatus:1, resource_subtype:1,\n backup_bytes:1, pixels:1, uploaded_at:1, filename:1, folder:1,\n api_key:1, derivatives:1, versions:1, access_control:1, related_assets:1,\n quality_analysis:1, quality_score:1, accessibility_analysis:1, phash:1,\n cinemagraph_analysis:1, responsive_breakpoints:1, last_updated:1,\n next_cursor:1, derived_next_cursor:1, usage:1,\n playback_url:1, video_metadata:1,\n frame_rate:1, rotation:1, nb_frames:1,\n audio_codec:1, audio_bit_rate:1, audio_frequency:1, channels:1, channel_layout:1,\n codec:1, profile:1, level:1, pix_format:1, video_bit_rate:1, dar:1, time_base:1\n};\n\nfunction isEmptyObj(v) {\n if (v === null || v === undefined || v === \"\") return true;\n if (typeof v === \"object\" && !Array.isArray(v) && Object.keys(v).length === 0) return true;\n if (Array.isArray(v) && v.length === 0) return true;\n return false;\n}\n\nvar RENDERED_LEAF_KEYS = {\n codec:1, bit_rate:1, frequency:1, channels:1, channel_layout:1,\n pix_format:1, profile:1, level:1, dar:1, time_base:1\n};\n\nfunction flattenObj(obj, prefix, out) {\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n var v = obj[k];\n var label = prefix ? prefix + \" \\u203a \" + prettyKey(k) : prettyKey(k);\n if (isEmptyObj(v)) continue;\n if (prefix && RENDERED_LEAF_KEYS[k] && typeof v !== \"object\") continue;\n if (Array.isArray(v)) {\n var vs = JSON.stringify(v);\n if (vs.length > 500) vs = vs.substring(0, 497) + \"...\";\n out.push([label, vs, k]);\n } else if (typeof v === \"object\") {\n flattenObj(v, label, out);\n } else {\n var s = String(v);\n if (s.length > 500) s = s.substring(0, 497) + \"...\";\n out.push([label, s, k]);\n }\n }\n}\n\nfunction renderExtraFields(r) {\n var cells = [];\n var keys = Object.keys(r);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n if (RENDERED_KEYS[k]) continue;\n var v = r[k];\n if (isEmptyObj(v)) continue;\n if (typeof v === \"object\" && !Array.isArray(v)) {\n flattenObj(v, prettyKey(k), cells);\n } else {\n var vs = typeof v === \"object\" ? JSON.stringify(v) : String(v);\n if (vs.length > 500) vs = vs.substring(0, 497) + \"...\";\n cells.push([prettyKey(k), vs, k]);\n }\n }\n if (!cells.length) return \"\";\n var h = \"\";\n for (var c = 0; c < cells.length; c++) {\n h += '<div class=\"detail-cell\">';\n h += '<div class=\"detail-cell-key\">' + esc(cells[c][0]) + (cells[c][2] && !cells[c][0].indexOf(\"\\u203a\") ? tip(cells[c][2]) : \"\") + \"</div>\";\n h += '<div class=\"detail-cell-val\" style=\"word-break:break-all\">' + esc(cells[c][1]) + \"</div>\";\n h += \"</div>\";\n }\n var out = '<details class=\"upload-section\" style=\"margin-top:12px\">';\n out += \"<summary>More Details</summary>\";\n out += '<div class=\"detail-grid\" style=\"padding:10px 12px\">' + h + \"</div>\";\n out += \"</details>\";\n return out;\n}\n\nfunction syntaxHighlight(json) {\n var s = json.replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\");\n return s.replace(\n /(\"(\\\\u[a-fA-F0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(s*:)?|\b(true|false|null)\b|-?d+(?:.d*)?(?:[eE][+-]?d+)?)/g,\n function(m) {\n var cls = \"json-num\";\n if (/^\"/.test(m)) {\n cls = /:$/.test(m) ? \"json-key\" : \"json-str\";\n } else if (/true|false/.test(m)) {\n cls = \"json-bool\";\n } else if (/null/.test(m)) {\n cls = \"json-null\";\n }\n return '<span class=\"' + cls + '\">' + m + \"</span>\";\n }\n );\n}\n\nfunction renderRawResponse(r) {\n if (!r || typeof r !== \"object\") return \"\";\n var json = JSON.stringify(r, null, 2);\n var out = '<details class=\"upload-section\" style=\"margin-top:12px\">';\n out += \"<summary>Raw Response</summary>\";\n out += '<pre class=\"raw-response-pre\">' + syntaxHighlight(json) + \"</pre>\";\n out += \"</details>\";\n return out;\n}\n\nfunction renderFullDetails(r) {\n var body = renderHeroPreview(r);\n\n body += sectionStart(\"asset_info\");\n body += '<summary class=\"detail-section-title\">Asset Info</summary>';\n body += renderAssetGrid(r);\n body += \"</details>\";\n\n body += renderAudioInfo(r);\n body += renderVideoInfo(r);\n body += renderTags(r.tags);\n body += renderContext(r.context);\n body += renderImageMetadata(r.image_metadata || r.media_metadata);\n body += renderColors(r.colors, r.predominant);\n body += renderModerationSection(r.moderation, r.moderation_kind, r.moderation_status);\n body += renderAccessControl(r.access_control);\n body += renderCoordinates(r.faces, r.coordinates);\n body += renderLastUpdated(r.last_updated);\n body += renderMetadata(r.metadata);\n body += renderInfo(r.info);\n body += renderDerived(r.derived, r.derived_next_cursor, r.asset_id);\n body += renderDerivatives(r.derivatives);\n body += renderRelatedAssets(r.related_assets);\n body += renderVersions(r.versions);\n body += renderEager(r.eager);\n body += renderQualityAnalysis(r.quality_analysis, r.quality_score);\n body += renderAccessibilityAnalysis(r.accessibility_analysis);\n body += renderExtraFields(r);\n body += renderRawResponse(r);\n\n return body;\n}\n\nasync function loadMoreDerived(btn) {\n var cursor = btn.dataset.cursor;\n var aid = btn.dataset.assetId;\n if (!cursor || !aid) return;\n btn.textContent = \"Loading\\u2026\";\n btn.disabled = true;\n try {\n var res = await app.callServerTool({\n name: \"get-asset-details\",\n arguments: { asset_id: aid, derived_next_cursor: cursor },\n });\n var data = ingestResult(res);\n if (data && data.derived && data.derived.length) {\n var container = document.getElementById(\"derived-list\");\n if (container) {\n var frag = \"\";\n for (var i = 0; i < data.derived.length; i++) {\n var d = data.derived[i];\n var dUrl = d.secure_url || d.url || \"\";\n frag += '<div class=\"derived-card\">';\n if (dUrl) frag += '<img class=\"derived-thumb\" src=\"' + esc(dUrl) + '\">';\n frag += '<div class=\"derived-info\">';\n frag += '<div class=\"derived-tx\">' + esc(d.transformation || \"\") + '</div>';\n frag += '<div class=\"derived-meta\">' + (d.format || \"\").toUpperCase() + \" \\u00b7 \" + fmtBytes(d.bytes) + '</div>';\n frag += '</div>';\n if (dUrl) frag += '<span class=\"derived-open\" data-url=\"' + esc(dUrl) + '\">Open</span>';\n frag += '</div>';\n }\n container.insertAdjacentHTML(\"beforeend\", frag);\n requestAnimationFrame(function() {\n app.reportSize(document.documentElement.scrollHeight);\n });\n }\n var countEl = document.getElementById(\"derived-count\");\n if (countEl) {\n var total = container.querySelectorAll(\".derived-card\").length;\n countEl.textContent = total + (data.derived_next_cursor ? \"+\" : \"\");\n }\n if (data.derived_next_cursor) {\n btn.dataset.cursor = data.derived_next_cursor;\n btn.textContent = \"Load More Derived\";\n btn.disabled = false;\n } else {\n btn.parentElement.remove();\n }\n } else {\n btn.parentElement.remove();\n }\n } catch (e) {\n showError(\"Load Failed\", e && e.message ? e.message : String(e));\n btn.textContent = \"Load More Derived\";\n btn.disabled = false;\n }\n}\n";
|
|
8
8
|
export declare const SHARED_JS_HOST_CONTEXT = "\nvar _themeOverride = null;\nvar _hostTheme = null;\n\nfunction applyTheme() {\n var effective;\n if (_themeOverride === \"light\" || _themeOverride === \"dark\") {\n effective = _themeOverride;\n } else {\n effective = _hostTheme || (window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\");\n }\n document.documentElement.setAttribute(\"data-theme\", effective);\n}\n\nvar _themeIcons = {\n light: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg>',\n system: '<svg viewBox=\"0 0 24 24\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M8 21h8M12 17v4\"/></svg>',\n dark: '<svg viewBox=\"0 0 24 24\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg>'\n};\nvar _themeCycle = [\"light\", \"system\", \"dark\"];\nvar _themeLabels = { light: \"Light\", system: \"System\", dark: \"Dark\" };\n\nfunction renderThemeToggle() {\n var existing = document.getElementById(\"theme-btn\");\n if (existing) existing.remove();\n var current = _themeOverride || \"system\";\n var btn = document.createElement(\"button\");\n btn.id = \"theme-btn\";\n btn.className = \"theme-btn\";\n btn.title = _themeLabels[current];\n btn.innerHTML = _themeIcons[current];\n btn.addEventListener(\"click\", function() {\n var cur = _themeOverride || \"system\";\n var idx = (_themeCycle.indexOf(cur) + 1) % _themeCycle.length;\n var next = _themeCycle[idx];\n _themeOverride = next === \"system\" ? null : next;\n try { localStorage.setItem(\"cld-theme\", next); } catch(e) {}\n applyTheme();\n renderThemeToggle();\n });\n var slot = document.getElementById(\"header-actions\");\n if (slot) {\n slot.appendChild(btn);\n } else {\n btn.style.position = \"absolute\";\n btn.style.top = \"4px\";\n btn.style.right = \"4px\";\n btn.style.zIndex = \"900\";\n document.body.appendChild(btn);\n }\n}\n\nfunction setupHostContext(app) {\n try { var saved = localStorage.getItem(\"cld-theme\");\n if (saved === \"light\" || saved === \"dark\") _themeOverride = saved;\n } catch(e) {}\n\n app.onhostcontextchanged = function(ctx) {\n if (ctx.theme) {\n _hostTheme = ctx.theme;\n applyTheme();\n }\n if (ctx.styles && ctx.styles.variables) {\n var vars = ctx.styles.variables;\n for (var k in vars) document.documentElement.style.setProperty(k, vars[k]);\n }\n if (ctx.styles && ctx.styles.css && ctx.styles.css.fonts) {\n var el = document.getElementById(\"host-fonts\");\n if (!el) { el = document.createElement(\"style\"); el.id = \"host-fonts\"; document.head.appendChild(el); }\n el.textContent = ctx.styles.css.fonts;\n }\n if (ctx.safeAreaInsets) {\n var s = ctx.safeAreaInsets;\n document.body.style.padding =\n (s.top||0)+\"px \"+(s.right||0)+\"px \"+(s.bottom||0)+\"px \"+(s.left||0)+\"px\";\n }\n };\n\n window.matchMedia(\"(prefers-color-scheme: dark)\").addEventListener(\"change\", function() {\n if (!_themeOverride) applyTheme();\n });\n\n applyTheme();\n renderThemeToggle();\n}\n\nfunction setupResize(app, minHeight) {\n var _raf = 0;\n function report() {\n cancelAnimationFrame(_raf);\n _raf = requestAnimationFrame(function() {\n app.reportSize(Math.max(document.documentElement.scrollHeight, minHeight));\n });\n }\n var ro = new ResizeObserver(report);\n ro.observe(document.body);\n ro.observe(document.documentElement);\n report();\n}\n";
|
|
9
9
|
export declare const SHARED_JS_TOOLTIPS: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-shared.d.ts","sourceRoot":"","sources":["../../../src/mcp-server/apps/app-shared.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"app-shared.d.ts","sourceRoot":"","sources":["../../../src/mcp-server/apps/app-shared.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,iBAAiB,k7JA2H7B,CAAC;AAGF,eAAO,MAAM,qBAAqB,w4tBAwfjC,CAAC;AAGF,eAAO,MAAM,eAAe,w9DAe3B,CAAC;AAGF,eAAO,MAAM,oBAAoB,kvGAoFhC,CAAC;AAGF,eAAO,MAAM,iBAAiB,g1WA2O7B,CAAC;AAGF,eAAO,MAAM,eAAe,iuIAwG3B,CAAC;AAGF,eAAO,MAAM,0BAA0B,0qrCAs5BtC,CAAC;AAGF,eAAO,MAAM,sBAAsB,wiHAmGlC,CAAC;AAGF,eAAO,MAAM,kBAAkB,QAQ9B,CAAC"}
|
|
@@ -111,7 +111,7 @@ export const SHARED_CSS_TOKENS = /* css */ `
|
|
|
111
111
|
[data-theme="dark"] .status-warn, .dark .status-warn { background: #854d0e; color: #fef08a; }
|
|
112
112
|
[data-theme="dark"] .status-err, .dark .status-err { background: #991b1b; color: #fecaca; }
|
|
113
113
|
|
|
114
|
-
html {
|
|
114
|
+
html { scrollbar-gutter: auto; }
|
|
115
115
|
body {
|
|
116
116
|
font-family: var(--cld-font);
|
|
117
117
|
background: var(--cld-bg);
|
|
@@ -120,6 +120,7 @@ body {
|
|
|
120
120
|
line-height: 1.5;
|
|
121
121
|
font-size: var(--cld-font-xs);
|
|
122
122
|
position: relative;
|
|
123
|
+
overflow-x: hidden;
|
|
123
124
|
}
|
|
124
125
|
.theme-btn {
|
|
125
126
|
width: 22px; height: 22px; border-radius: 50%;
|
|
@@ -157,9 +158,11 @@ export const SHARED_CSS_COMPONENTS = /* css */ `
|
|
|
157
158
|
.link { cursor: pointer; }
|
|
158
159
|
.link:hover { color: var(--cld-accent); text-decoration: underline; }
|
|
159
160
|
|
|
160
|
-
/* Modal
|
|
161
|
+
/* Modal — positioned absolutely so it can be placed in the part of the
|
|
162
|
+
* iframe that is currently visible in the host's viewport (since position:fixed
|
|
163
|
+
* inside a tall iframe anchors to iframe-center, not the user's visible area). */
|
|
161
164
|
.modal-overlay {
|
|
162
|
-
position:
|
|
165
|
+
position: absolute; left: 0; right: 0;
|
|
163
166
|
background: rgba(0,0,0,0.45);
|
|
164
167
|
display: flex; align-items: center; justify-content: center;
|
|
165
168
|
z-index: 1000; backdrop-filter: blur(3px); padding: 24px;
|
|
@@ -1003,6 +1006,55 @@ function closeModal() {
|
|
|
1003
1006
|
if (ov) ov.remove();
|
|
1004
1007
|
}
|
|
1005
1008
|
|
|
1009
|
+
/* Position the modal overlay so it's centered on the part of the iframe
|
|
1010
|
+
* the user is currently looking at. Inside an iframe that the host has
|
|
1011
|
+
* sized to scrollHeight, position: fixed anchors to iframe-center, not
|
|
1012
|
+
* the user's visible band — so we compute the visible band ourselves
|
|
1013
|
+
* via IntersectionObserver and place the overlay absolutely there. */
|
|
1014
|
+
function positionModalInVisibleArea(overlay) {
|
|
1015
|
+
if (!overlay) return;
|
|
1016
|
+
var docH = Math.max(document.documentElement.scrollHeight, window.innerHeight);
|
|
1017
|
+
|
|
1018
|
+
// Try IntersectionObserver first (works cross-origin too)
|
|
1019
|
+
if (typeof IntersectionObserver !== "undefined") {
|
|
1020
|
+
var probe = document.createElement("div");
|
|
1021
|
+
probe.style.cssText = "position:absolute;left:0;top:0;width:1px;height:" + docH + "px;pointer-events:none;visibility:hidden;";
|
|
1022
|
+
document.body.appendChild(probe);
|
|
1023
|
+
var io = new IntersectionObserver(function(entries) {
|
|
1024
|
+
try {
|
|
1025
|
+
var entry = entries[0];
|
|
1026
|
+
var rect = entry.intersectionRect;
|
|
1027
|
+
var rootRect = entry.rootBounds || entry.boundingClientRect;
|
|
1028
|
+
// intersectionRect.top is in viewport coords; visible band of the
|
|
1029
|
+
// document is from (probe.top + rect.top) to (probe.top + rect.bottom).
|
|
1030
|
+
var probeTop = entry.boundingClientRect.top; // viewport-relative
|
|
1031
|
+
var visibleTop = rect.top - probeTop; // document-relative
|
|
1032
|
+
var visibleHeight = rect.height;
|
|
1033
|
+
if (visibleHeight > 0) {
|
|
1034
|
+
overlay.style.top = visibleTop + "px";
|
|
1035
|
+
overlay.style.height = visibleHeight + "px";
|
|
1036
|
+
} else {
|
|
1037
|
+
// Fallback: probe not intersecting (shouldn't happen with full-height probe)
|
|
1038
|
+
overlay.style.top = (window.scrollY || 0) + "px";
|
|
1039
|
+
overlay.style.height = window.innerHeight + "px";
|
|
1040
|
+
}
|
|
1041
|
+
} finally {
|
|
1042
|
+
io.disconnect();
|
|
1043
|
+
if (probe.parentNode) probe.parentNode.removeChild(probe);
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
io.observe(probe);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// Fallback: same-origin only — read iframe's bounding rect
|
|
1051
|
+
var rect = document.documentElement.getBoundingClientRect();
|
|
1052
|
+
var visibleTop = rect.top < 0 ? -rect.top : 0;
|
|
1053
|
+
var visibleHeight = Math.min(window.innerHeight, rect.bottom) - Math.max(0, rect.top);
|
|
1054
|
+
overlay.style.top = visibleTop + "px";
|
|
1055
|
+
overlay.style.height = Math.max(visibleHeight, 200) + "px";
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1006
1058
|
function openModal(headerHtml, bodyHtml) {
|
|
1007
1059
|
closeModal();
|
|
1008
1060
|
var h = '<div class="modal-overlay"><div class="modal">';
|
|
@@ -1012,6 +1064,7 @@ function openModal(headerHtml, bodyHtml) {
|
|
|
1012
1064
|
document.body.insertAdjacentHTML("beforeend", h);
|
|
1013
1065
|
|
|
1014
1066
|
var overlay = document.querySelector(".modal-overlay");
|
|
1067
|
+
positionModalInVisibleArea(overlay);
|
|
1015
1068
|
overlay.addEventListener("click", function(e) {
|
|
1016
1069
|
if (e.target === overlay || e.target.classList.contains("modal-close")) {
|
|
1017
1070
|
closeModal();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-shared.js","sourceRoot":"","sources":["../../../src/mcp-server/apps/app-shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,uEAAuE;AACvE,SAAS,yBAAyB,CAChC,MAA+B;IAE/B,MAAM,KAAK,GAAI,MAAmD;SAC/D,UAAU,IAAI,EAAE,CAAC;IACpB,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,IAAuC,CAAC;QAClD,IAAI,CAAC,EAAE,WAAW;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,EAAE;IAC/C,eAAe,EAAE,KAAK;CACvB,CAA4B,CAAC;AAC9B,MAAM,aAAa,GAAG,YAAY,CAAC,wBAAwB,EAAE;IAC3D,eAAe,EAAE,KAAK;CACvB,CAA4B,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC;IACtC,GAAG,yBAAyB,CAAC,WAAW,CAAC;IACzC,GAAG,yBAAyB,CAAC,aAAa,CAAC;CAC5C,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC
|
|
1
|
+
{"version":3,"file":"app-shared.js","sourceRoot":"","sources":["../../../src/mcp-server/apps/app-shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,uEAAuE;AACvE,SAAS,yBAAyB,CAChC,MAA+B;IAE/B,MAAM,KAAK,GAAI,MAAmD;SAC/D,UAAU,IAAI,EAAE,CAAC;IACpB,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,IAAuC,CAAC;QAClD,IAAI,CAAC,EAAE,WAAW;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,EAAE;IAC/C,eAAe,EAAE,KAAK;CACvB,CAA4B,CAAC;AAC9B,MAAM,aAAa,GAAG,YAAY,CAAC,wBAAwB,EAAE;IAC3D,eAAe,EAAE,KAAK;CACvB,CAA4B,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC;IACtC,GAAG,yBAAyB,CAAC,WAAW,CAAC;IACzC,GAAG,yBAAyB,CAAC,aAAa,CAAC;CAC5C,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2H1C,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwf9C,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;CAevC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoF5C,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2OzC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGvC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,0BAA0B,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAs5BlD,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmG9C,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,oBAAoB,gBAAgB;;;;;;;;CAQ9E,CAAC"}
|
|
@@ -155,17 +155,27 @@ const GALLERY_CSS = /* css */ `
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/* ── Multi-select bar ── */
|
|
158
|
+
/* Wrapper takes layout space ONLY when a selection is active. When idle
|
|
159
|
+
* it's display:none so it contributes 0 to scrollHeight (no phantom
|
|
160
|
+
* scrollbar gutter / no extra iframe height). */
|
|
161
|
+
.select-bar-wrap {
|
|
162
|
+
display: none;
|
|
163
|
+
position: sticky; bottom: 0; left: 0; right: 0;
|
|
164
|
+
justify-content: center; pointer-events: none;
|
|
165
|
+
z-index: 100; height: 64px;
|
|
166
|
+
}
|
|
167
|
+
.select-bar-wrap.visible { display: flex; }
|
|
158
168
|
.select-bar {
|
|
159
|
-
position:
|
|
160
|
-
transform:
|
|
169
|
+
position: absolute; bottom: 8px;
|
|
170
|
+
transform: translateY(80px);
|
|
161
171
|
background: #1a1d24; color: white; border-radius: 40px;
|
|
162
172
|
padding: 0 6px 0 16px; height: 48px;
|
|
163
173
|
display: flex; align-items: center; gap: 4px;
|
|
164
174
|
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
165
175
|
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s;
|
|
166
|
-
opacity: 0; pointer-events: none;
|
|
176
|
+
opacity: 0; pointer-events: none; white-space: nowrap;
|
|
167
177
|
}
|
|
168
|
-
.select-bar.visible { transform:
|
|
178
|
+
.select-bar-wrap.visible .select-bar { transform: translateY(0); opacity: 1; pointer-events: all; }
|
|
169
179
|
.select-count { font-size: 13px; font-weight: 600; margin-right: 8px; }
|
|
170
180
|
.bar-btn {
|
|
171
181
|
height: 36px; padding: 0 14px; border: none; border-radius: 30px;
|
|
@@ -229,12 +239,12 @@ function showToast(msg) {
|
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
function updateSelectBar() {
|
|
232
|
-
var
|
|
242
|
+
var wrap = document.getElementById("select-bar-wrap");
|
|
233
243
|
var countEl = document.getElementById("select-count");
|
|
234
|
-
if (!
|
|
244
|
+
if (!wrap || !countEl) return;
|
|
235
245
|
var n = selected.size;
|
|
236
246
|
countEl.textContent = n + " selected";
|
|
237
|
-
|
|
247
|
+
wrap.classList.toggle("visible", n > 0);
|
|
238
248
|
var btn = document.getElementById("select-all-btn");
|
|
239
249
|
if (btn) {
|
|
240
250
|
var visible = getVisibleIndices();
|
|
@@ -514,7 +524,8 @@ function render() {
|
|
|
514
524
|
h += "</div>";
|
|
515
525
|
}
|
|
516
526
|
|
|
517
|
-
// Multi-select bar
|
|
527
|
+
// Multi-select bar (sticky, in-flow wrapper so iframe sizing stays truthful)
|
|
528
|
+
h += '<div class="select-bar-wrap" id="select-bar-wrap">';
|
|
518
529
|
h += '<div class="select-bar" id="select-bar">';
|
|
519
530
|
h += '<span class="select-count" id="select-count">0 selected</span>';
|
|
520
531
|
h += '<div class="bar-divider"></div>';
|
|
@@ -524,6 +535,7 @@ function render() {
|
|
|
524
535
|
h += '<div class="bar-divider"></div>';
|
|
525
536
|
h += '<button class="bar-btn bar-ghost" id="bar-clear">' + IC.x + '</button>';
|
|
526
537
|
h += '</div>';
|
|
538
|
+
h += '</div>';
|
|
527
539
|
|
|
528
540
|
// Toast
|
|
529
541
|
h += '<div class="gallery-toast" id="gallery-toast"></div>';
|
|
@@ -753,6 +765,9 @@ async function fetchDirect() {
|
|
|
753
765
|
console.log(LOG_PREFIX, "fetchDirect ->", name);
|
|
754
766
|
|
|
755
767
|
document.getElementById("app").innerHTML = '<div class="status">Fetching assets\\u2026</div>';
|
|
768
|
+
requestAnimationFrame(function() {
|
|
769
|
+
app.reportSize(Math.max(document.documentElement.scrollHeight, MIN_HEIGHT));
|
|
770
|
+
});
|
|
756
771
|
try {
|
|
757
772
|
var res = await app.callServerTool({ name: name, arguments: args });
|
|
758
773
|
var data = ingestResult(res);
|
|
@@ -804,6 +819,7 @@ async function loadMore() {
|
|
|
804
819
|
function refreshGallery() {
|
|
805
820
|
allResources = [];
|
|
806
821
|
lastCursor = null;
|
|
822
|
+
selected.clear();
|
|
807
823
|
fetchDirect();
|
|
808
824
|
}
|
|
809
825
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-gallery-app.js","sourceRoot":"","sources":["../../../src/mcp-server/apps/asset-gallery-app.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,UAAU,mBAAmB,CAAC,QAAiB;IACnD,OAAO,cAAc,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,WAAW,GAAG,SAAS,CAAC
|
|
1
|
+
{"version":3,"file":"asset-gallery-app.js","sourceRoot":"","sources":["../../../src/mcp-server/apps/asset-gallery-app.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,UAAU,mBAAmB,CAAC,QAAiB;IACnD,OAAO,cAAc,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,WAAW,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiM7B,CAAC;AAEF,MAAM,UAAU,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAynB3B,CAAC;AAEF,MAAM,kBAAkB,GAAG,UAAU,CAAC;;;;;;;EAOpC,iBAAiB;EACjB,qBAAqB;EACrB,WAAW;;;;;;;;EAQX,eAAe;EACf,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,0BAA0B;EAC1B,sBAAsB;EACtB,UAAU;;;QAGJ,CAAC"}
|
package/esm/mcp-server/server.js
CHANGED
|
@@ -32,7 +32,7 @@ import { tool$usageGetUsage } from "./tools/usageGetUsage.js";
|
|
|
32
32
|
export function createMCPServer(deps) {
|
|
33
33
|
const server = new McpServer({
|
|
34
34
|
name: "CloudinaryAssetMgmt",
|
|
35
|
-
version: "0.9.
|
|
35
|
+
version: "0.9.3",
|
|
36
36
|
});
|
|
37
37
|
const getClient = deps.getSDK || (() => new CloudinaryAssetMgmtCore({
|
|
38
38
|
security: deps.security,
|