\";\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 = '
';\n h += '
\\u26A0\\uFE0F
';\n h += '
' + esc(title) + '
';\n if (msg) h += '
' + esc(msg) + '
';\n h += '
';\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 = '
';\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 = 'null';\n else if (typeof v === \"boolean\") display = '' + v + '';\n else if (typeof v === \"object\") display = '' + esc(JSON.stringify(v)) + '';\n else display = '' + esc(String(v)) + '';\n h += '' + esc(prettyKey(k)) + ' ' + display + '';\n }\n h += '
';\n return h;\n}\n\nfunction showReadyPrompt(pendingCall, fetchFn) {\n var root = document.getElementById(\"app\");\n var h = '
';\n h += '
Waiting for result\\u2026
';\n h += renderParamsList(pendingCall.args);\n h += '';\n h += '
';\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 = '
';\n h += '
Cancelled
';\n if (name) h += '
' + esc(prettyKey(name)) + '
';\n h += renderParamsList(pendingCall.args);\n h += '
';\n h += '';\n h += '
';\n h += '
';\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 '
'\n + '
\\u26A0\\uFE0F
'\n + '
' + esc(title) + \"
\"\n + '
' + esc(detail) + \"
\"\n + \"
\";\n}\n";
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 = '
';\n h += headerHtml;\n h += '
' + bodyHtml + \"
\";\n h += \"
\";\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 = '
';\n var thumb = thumbUrl(url, 60, 60, resource);\n if (thumb) h += '';\n h += '
';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][2] && fields[i][2] !== 0) continue;\n h += '
';\n h += '
' + esc(fields[i][1]) + tip(fields[i][0]) + \"
\";\n h += '
' + esc(String(fields[i][2])) + \"
\";\n h += \"
\";\n }\n h += \"
\";\n\n if (r.url || r.secure_url) {\n h += '
';\n if (r.url) {\n h += '
URL';\n h += '' + esc(r.url) + \"
\";\n }\n if (r.secure_url) {\n h += '
Secure URL';\n h += '' + esc(r.secure_url) + \"
\";\n }\n if (r.playback_url) {\n h += '
Playback URL';\n h += '' + esc(r.playback_url) + \"
\";\n }\n h += \"
\";\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 += 'Audio Info';\n h += '
';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][1] && fields[i][1] !== 0) continue;\n h += '
';\n h += '
' + esc(fields[i][0]) + \"
\";\n h += '
' + esc(String(fields[i][1])) + \"
\";\n h += \"
\";\n }\n h += \"
\";\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 += 'Video Info';\n h += '
';\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i][1] && fields[i][1] !== 0) continue;\n h += '
';\n h += '
' + esc(fields[i][0]) + \"
\";\n h += '
' + esc(String(fields[i][1])) + \"
\";\n h += \"
\";\n }\n h += \"
\";\n return h;\n}\n\nfunction renderTags(tags) {\n if (!tags || !tags.length) return \"\";\n var h = sectionStart(\"tags\");\n h += 'Tags' + tip(\"tags\") + ' ' + tags.length + \"\";\n h += '
';\n for (var i = 0; i < tags.length; i++) {\n h += '' + esc(tags[i]) + \"\";\n }\n h += \"
\";\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 += 'Structured Metadata' + tip(\"metadata\") + ' ' + keys.length + \"\";\n\n if (groups.set.length) {\n for (var s = 0; s < groups.set.length; s++) {\n var item = groups.set[s];\n h += '
';\n h += '' + esc(prettyKey(item.key)) + \"\";\n h += '
';\n for (var si = 0; si < item.val.length; si++) {\n h += '' + esc(String(item.val[si])) + \"\";\n }\n h += \"
\";\n }\n }\n if (groups.date.length) {\n for (var d = 0; d < groups.date.length; d++) {\n h += '
';\n h += '' + esc(prettyKey(groups.date[d].key)) + \"\";\n h += '' + esc(groups.date[d].val) + \"
\";\n }\n }\n if (groups.int.length) {\n for (var n = 0; n < groups.int.length; n++) {\n h += '
';\n h += '' + esc(prettyKey(groups.int[n].key)) + \"\";\n h += '' + esc(String(groups.int[n].val)) + \"
\";\n }\n }\n if (groups.string.length) {\n for (var st = 0; st < groups.string.length; st++) {\n h += '
';\n h += '' + esc(prettyKey(groups.string[st].key)) + \"\";\n h += '' + esc(String(groups.string[st].val)) + \"
\";\n }\n }\n\n h += \"\";\n return h;\n}\n\nfunction renderDerived(derived, nextCursor, assetId) {\n if (!derived || !derived.length) return \"\";\n var h = sectionStart(\"derived\");\n h += 'Derived Assets' + tip(\"derived\") + ' ' + derived.length + (nextCursor ? \"+\" : \"\") + \"\";\n h += '
';\n for (var i = 0; i < derived.length; i++) {\n var d = derived[i];\n var dUrl = d.secure_url || d.url || \"\";\n h += '
\";\n }\n h += \"\";\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 += 'Context' + tip(\"context\") + '';\n h += '
';\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 += '
';\n h += '
' + esc(prettyKey(keys[i])) + \"
\";\n h += '
' + esc(vs) + \"
\";\n h += \"
\";\n }\n h += \"
\";\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 += 'Media Metadata' + tip(\"image_metadata\") + '';\n h += '
';\n for (var i = 0; i < keys.length; i++) {\n var v = meta[keys[i]];\n if (v === null || v === undefined) continue;\n h += '
';\n h += '
' + esc(prettyKey(keys[i])) + \"
\";\n h += '
' + esc(String(v)) + \"
\";\n h += \"
\";\n }\n h += \"
\";\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 += '
';\n rows += '
' + esc(prettyKey(keys[i])) + \"
\";\n rows += '
' + statusStr + \"
\";\n rows += \"
\";\n }\n if (!rows) return \"\";\n var h = sectionStart(\"info\");\n h += 'Info' + tip(\"info\") + '';\n h += '
' + rows + \"
\";\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 += 'Timestamps' + tip(\"last_updated\") + '';\n h += '
';\n for (var i = 0; i < keys.length; i++) {\n h += '
';\n h += '
' + esc(prettyKey(keys[i])) + \"
\";\n h += '
' + esc(fmtDate(lu[keys[i]])) + \"
\";\n h += \"
\";\n }\n h += \"
\";\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 += '
\";\n return h;\n }\n\n if (isAudioResource(r)) {\n var waveform = thumbUrl(url, 600, 120, r);\n h += '
';\n if (waveform) {\n h += '';\n } else {\n h += '
\\u266B
';\n }\n h += '';\n h += \"
\";\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 += '
';\n h += '';\n if (r.duration) {\n h += '
' + fmtDuration(r.duration) + \"
\";\n }\n h += \"
\";\n return h;\n }\n\n var thumb = thumbUrl(url, 600, 220, r);\n if (thumb) {\n h += '';\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 += '
';\n if (waveform) h += '';\n h += '';\n h += \"
\";\n return h;\n }\n\n var poster = thumbUrl(url, 600, 340, r);\n h += '';\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 += 'Colors' + tip(\"colors\") + '';\n\n if (hasColors) {\n h += '
';\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 += '
';\n h += '';\n h += '
' + esc(hex) + '
';\n if (pct) h += '
' + esc(pct) + '
';\n h += '
';\n }\n h += '
';\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 += '
' + esc(prettyKey(pkeys[p])) + '
';\n h += '
';\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 += '
';\n if (isHex) h += '';\n h += '
' + esc(ghex) + '
';\n if (gpct) h += '
' + esc(gpct) + '
';\n h += '
';\n }\n h += '
';\n }\n }\n\n h += '';\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 += 'Moderation' + tip(\"moderation\") + '';\n\n if (hasArr) {\n for (var i = 0; i < moderation.length; i++) {\n var m = moderation[i];\n h += '
';\n h += '' + esc(m.kind || \"unknown\") + '';\n h += '' + (m.status ? statusBadge(m.status) : \"\");\n if (m.updated_at) h += ' \\u00b7 ' + fmtDate(m.updated_at);\n h += '
';\n }\n\n h += '';\n return h;\n}\n\nfunction renderAccessControl(acl) {\n if (!acl || !acl.length) return \"\";\n\n var h = sectionStart(\"access_control\");\n h += 'Access Control' + tip(\"access_control\") + ' ' + acl.length + '';\n\n for (var i = 0; i < acl.length; i++) {\n var rule = acl[i];\n h += '
';\n h += '' + esc(prettyKey(rule.access_type || \"unknown\")) + '';\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 += '' + esc(parts.join(\" \\u00b7 \")) + '';\n h += '
';\n }\n\n h += '';\n return h;\n}\n\nfunction renderVersions(versions) {\n if (!versions || !versions.length) return \"\";\n\n var h = sectionStart(\"versions\");\n h += 'Versions ' + versions.length + '';\n\n for (var i = 0; i < versions.length; i++) {\n var v = versions[i];\n h += '
';\n h += '' + esc(v.version_id || String(v.version || \"#\" + (i + 1))) + '';\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 += '' + esc(parts.join(\" \\u00b7 \")) + '';\n h += '
';\n }\n\n h += '';\n return h;\n}\n\nfunction renderEager(eager) {\n if (!eager || !eager.length) return \"\";\n\n var h = sectionStart(\"eager\");\n h += 'Eager Transformations ' + eager.length + '';\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 += '
';\n if (eUrl) h += '';\n h += '
';\n h += '
' + esc(e.transformation || \"\") + '
';\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 += '
' + esc(meta.join(\" \\u00b7 \")) + '
';\n h += '
';\n if (eUrl) h += 'Open';\n h += '
';\n }\n\n h += '';\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 += 'Coordinates' + tip(\"coordinates\") + '';\n h += '
';\n\n if (faceArr.length) {\n h += '
';\n h += '
Faces' + tip(\"faces\") + '
';\n h += '
' + 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 += '
';\n }\n\n if (customArr.length) {\n h += '
';\n h += '
Custom Regions
';\n h += '
' + 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 += '
';\n }\n\n h += '
';\n return h;\n}\n\nfunction renderDerivatives(derivatives) {\n if (!derivatives || !derivatives.length) return \"\";\n\n var h = sectionStart(\"derivatives\");\n h += 'Derivatives' + tip(\"derivatives\") + ' ' + derivatives.length + '';\n\n for (var i = 0; i < derivatives.length; i++) {\n var d = derivatives[i];\n var dUrl = d.secure_url || \"\";\n h += '
';\n if (dUrl) h += '';\n h += '
';\n h += '
' + esc(d.transformation || \"\") + '
';\n if (d.id) h += '
ID: ' + esc(d.id) + '
';\n h += '
';\n if (dUrl) h += 'Open';\n h += '
';\n }\n\n h += '';\n return h;\n}\n\nfunction renderQualityAnalysis(qa, score) {\n if (!qa && score == null) return \"\";\n\n var h = sectionStart(\"quality_analysis\");\n h += 'Quality Analysis' + tip(\"quality_analysis\") + '';\n h += '
';\n\n if (score != null) {\n h += '
';\n h += '
Overall Score
';\n h += '
' + esc(String(score)) + '
';\n h += '
';\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 += '
';\n return h;\n}\n\nfunction renderAccessibilityAnalysis(aa) {\n if (!aa || typeof aa !== \"object\") return \"\";\n\n var h = sectionStart(\"accessibility_analysis\");\n h += 'Accessibility Analysis' + tip(\"accessibility_analysis\") + '';\n h += '
';\n\n if (aa.colorblind_accessibility_score != null) {\n h += '
';\n }\n\n var cba = aa.colorblind_accessibility_analysis;\n if (cba && typeof cba === \"object\") {\n if (cba.distinct_edges != null) {\n h += '
';\n h += '
Distinct Edges
';\n h += '
' + esc(String(cba.distinct_edges)) + '
';\n h += '
';\n }\n if (cba.distinct_colors != null) {\n h += '
';\n h += '
Distinct Colors
';\n h += '
' + esc(String(cba.distinct_colors)) + '
';\n h += '
';\n }\n if (cba.most_indistinct_pair && cba.most_indistinct_pair.length) {\n h += '
';\n h += '
Most Indistinct Pair
';\n h += '
';\n for (var i = 0; i < cba.most_indistinct_pair.length; i++) {\n var hex = cba.most_indistinct_pair[i];\n h += '';\n h += '';\n h += esc(hex);\n h += '';\n }\n h += '
';\n }\n }\n\n h += '
';\n return h;\n}\n\nfunction renderRelatedAssets(related) {\n if (!related || !related.length) return \"\";\n\n var h = sectionStart(\"related_assets\");\n h += 'Related Assets' + tip(\"related_assets\") + ' ' + related.length + '';\n\n for (var i = 0; i < related.length; i++) {\n var ra = related[i];\n h += '
';\n h += '' + esc(ra.asset_id || ra.public_id || \"#\" + (i + 1)) + '';\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 += '' + esc(parts.join(\" \\u00b7 \")) + '';\n h += '
';\n }\n\n h += '';\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 += '
\";\n }\n var out = '';\n out += \"More Details\";\n out += '
' + h + \"
\";\n out += \"\";\n return out;\n}\n\nfunction syntaxHighlight(json) {\n var s = json.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 '' + m + \"\";\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 = '';\n out += \"Raw Response\";\n out += '
' + syntaxHighlight(json) + \"
\";\n out += \"\";\n return out;\n}\n\nfunction renderFullDetails(r) {\n var body = renderHeroPreview(r);\n\n body += sectionStart(\"asset_info\");\n body += 'Asset Info';\n body += renderAssetGrid(r);\n body += \"\";\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 += '