@cloudinary/asset-management-mcp 0.8.1 → 0.9.0-rc.0
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 +4849 -305
- package/bin/mcp-server.js.map +25 -21
- package/esm/landing-page.js +1 -1
- package/esm/lib/config.d.ts +3 -3
- package/esm/lib/config.js +3 -3
- package/esm/lib/config.js.map +1 -1
- package/esm/lib/encodings.d.ts +1 -0
- package/esm/lib/encodings.d.ts.map +1 -1
- package/esm/lib/encodings.js +26 -5
- package/esm/lib/encodings.js.map +1 -1
- package/esm/lib/security.d.ts +1 -1
- package/esm/lib/security.d.ts.map +1 -1
- package/esm/lib/security.js +29 -17
- package/esm/lib/security.js.map +1 -1
- package/esm/mcp-server/mcp-server.js +1 -1
- package/esm/mcp-server/mcp-server.js.map +1 -1
- package/esm/mcp-server/server.extensions.d.ts.map +1 -1
- package/esm/mcp-server/server.extensions.js +84 -0
- package/esm/mcp-server/server.extensions.js.map +1 -1
- package/esm/mcp-server/server.js +1 -1
- package/esm/mcp-server/server.js.map +1 -1
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.js +4 -0
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.js.map +1 -1
- package/esm/mcp-server/tools/assetsListImages.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListImages.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListImages.js +4 -0
- package/esm/mcp-server/tools/assetsListImages.js.map +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.js +4 -0
- package/esm/mcp-server/tools/assetsListRawFiles.js.map +1 -1
- package/esm/mcp-server/tools/assetsListVideos.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListVideos.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListVideos.js +4 -0
- package/esm/mcp-server/tools/assetsListVideos.js.map +1 -1
- package/esm/mcp-server/tools/searchSearchAssets.d.ts.map +1 -1
- package/esm/mcp-server/tools/searchSearchAssets.js +4 -0
- package/esm/mcp-server/tools/searchSearchAssets.js.map +1 -1
- package/esm/mcp-server/tools/uploadUpload.d.ts.map +1 -1
- package/esm/mcp-server/tools/uploadUpload.js +4 -0
- package/esm/mcp-server/tools/uploadUpload.js.map +1 -1
- package/esm/mcp-server/tools.d.ts +2 -0
- package/esm/mcp-server/tools.d.ts.map +1 -1
- package/esm/mcp-server/tools.js +2 -0
- package/esm/mcp-server/tools.js.map +1 -1
- package/esm/mcp-server/widgets/asset-details-widget.d.ts +3 -0
- package/esm/mcp-server/widgets/asset-details-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-details-widget.js +299 -0
- package/esm/mcp-server/widgets/asset-details-widget.js.map +1 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.d.ts +4 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.js +1063 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.js.map +1 -0
- package/esm/mcp-server/widgets/asset-upload-widget.d.ts +3 -0
- package/esm/mcp-server/widgets/asset-upload-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-upload-widget.js +1093 -0
- package/esm/mcp-server/widgets/asset-upload-widget.js.map +1 -0
- package/esm/mcp-server/widgets/widget-shared.d.ts +9 -0
- package/esm/mcp-server/widgets/widget-shared.d.ts.map +1 -0
- package/esm/mcp-server/widgets/widget-shared.js +2019 -0
- package/esm/mcp-server/widgets/widget-shared.js.map +1 -0
- package/esm/models/fieldsspec.d.ts +1 -1
- package/package.json +1 -1
- package/src/landing-page.ts +1 -1
- package/src/lib/config.ts +3 -3
- package/src/lib/encodings.ts +32 -4
- package/src/lib/security.ts +14 -2
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.extensions.ts +97 -0
- package/src/mcp-server/server.ts +1 -1
- package/src/mcp-server/tools/assetsGetResourceByAssetId.ts +4 -0
- package/src/mcp-server/tools/assetsListImages.ts +4 -0
- package/src/mcp-server/tools/assetsListRawFiles.ts +4 -0
- package/src/mcp-server/tools/assetsListVideos.ts +4 -0
- package/src/mcp-server/tools/searchSearchAssets.ts +4 -0
- package/src/mcp-server/tools/uploadUpload.ts +4 -0
- package/src/mcp-server/tools.ts +4 -0
- package/src/mcp-server/widgets/asset-details-widget.ts +313 -0
- package/src/mcp-server/widgets/asset-gallery-widget.ts +1077 -0
- package/src/mcp-server/widgets/asset-upload-widget.ts +1115 -0
- package/src/mcp-server/widgets/widget-shared.ts +2030 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-upload-widget.js","sourceRoot":"","sources":["../../../src/mcp-server/widgets/asset-upload-widget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAElF,MAAM,CAAC,MAAM,yBAAyB,GAAG,mCAAmC,CAAC;AAE7E,MAAM,UAAU,kBAAkB;IAChC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,YAAY,CAAC,uBAAuB,CAE5D,CAAC;AACF,MAAM,YAAY,GAAG,YAAY,CAAC,4BAA4B,CAG7D,CAAC;AAEF,MAAM,mBAAmB,GAA4B;IACnD,GAAG,CAAC,gBAAgB,CAAC,UAAU,IAAI,EAAE,CAAC;CACvC,CAAC;AACF,MAAM,MAAM,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC;AACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AACzB,mBAAmB,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;AAExD,uEAAuE;AACvE,MAAM,UAAU,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;CAyB5B,CAAC;AAEF,uEAAuE;AACvE,MAAM,SAAS,GAAG,QAAQ,CAAC;;;;;;sBAML,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq+BhC,CAAC;AAEF,uEAAuE;AACvE,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;EAOnC,iBAAiB;EACjB,qBAAqB;EACrB,UAAU;;;;;;;EAOV,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,0BAA0B;EAC1B,sBAAsB;EACtB,SAAS;;;QAGH,CAAC"}
|
|
@@ -0,0 +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\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}\n.theme-btn {\n position: absolute; top: 4px; right: 4px; z-index: 900;\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;\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";
|
|
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 widget */\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 { font-size: 36px; margin-bottom: 8px; color: var(--cld-text3); }\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; font-size: 24px; color: var(--cld-text3);\n}\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 { font-size: 24px; flex-shrink: 0; }\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 {\n background: none; border: none; cursor: pointer; font-size: 16px;\n color: var(--cld-text3); padding: 4px 6px; border-radius: var(--cld-radius-sm);\n transition: color 0.15s, background 0.15s; flex-shrink: 0;\n}\n.upload-staged-clear:hover { 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
|
+
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";
|
|
4
|
+
export declare const SHARED_JS_HELPERS = "\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";
|
|
5
|
+
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_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";
|
|
7
|
+
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 document.body.appendChild(btn);\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";
|
|
8
|
+
export declare const SHARED_JS_TOOLTIPS: string;
|
|
9
|
+
//# sourceMappingURL=widget-shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-shared.d.ts","sourceRoot":"","sources":["../../../src/mcp-server/widgets/widget-shared.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,iBAAiB,6/GAsG7B,CAAC;AAGF,eAAO,MAAM,qBAAqB,80sBAwfjC,CAAC;AAGF,eAAO,MAAM,oBAAoB,kvGAoFhC,CAAC;AAGF,eAAO,MAAM,iBAAiB,+8UAoN7B,CAAC;AAGF,eAAO,MAAM,eAAe,s2DAsD3B,CAAC;AAGF,eAAO,MAAM,0BAA0B,0qrCAs5BtC,CAAC;AAGF,eAAO,MAAM,sBAAsB,wyGA0FlC,CAAC;AAGF,eAAO,MAAM,kBAAkB,QAQ9B,CAAC"}
|