// ==UserScript== // @name 0750珠宝AI 移动端适配 // @namespace https://zbai.art/ // @version 0.7.50 // @description 优化手机浏览器中的珠宝AI创作、资产和作品详情体验 // @author Sulong // @match https://zbai.art/* // @match https://www.zbai.art/* // @match https://jew.haistudio.ai/* // @run-at document-start // @grant none // ==/UserScript== (function () { 'use strict'; // ============================================================================ // 珠宝AI 移动端适配 v0.7.50 public-clean // ============================================================================ // 模块结构: // 1. 常量定义 & 正则表达式 - 配置和匹配规则 // 2. DOM 管理 - 根元素、样式注入、事件监听 // 3. 创作页面处理 - 立即创作按钮识别、点击激活、生成监听 // 4. 历史面板处理 - 历史侧边栏识别、展示、交互 // 5. 支付检测 - 高阶/商业模型识别、支付遮挡检测 // 6. 工具函数 - 文本规范化、元素检测、交互辅助 // ============================================================================ // -------- 第一部分:常量定义 -------- // DOM 元素 ID 和样式类名 const STYLE_ID = 'zbai-mobile-adapter-style'; const ROOT_ID = 'zbai-mobile-root'; const CLASS_READY = 'zbai-mobile-adapted'; const HOME_SHELL_ID = 'zbai-mobile-home-shell'; const WORK_DETAIL_FALLBACK_ID = 'zbai-mobile-work-detail-fallback'; const WORK_PREVIEW_OVERLAY_ID = 'zbai-work-preview-overlay'; const WORK_DETAIL_CACHE_KEY = 'zbai-work-detail-cache-v2'; const HOME_CREDITS_CACHE_KEY = 'zbai-mobile-home-credits-v1'; const PENDING_NAV_KEY = 'zbai-mobile-pending-nav-v1'; const PENDING_RECREATE_KEY = 'zbai-mobile-pending-recreate-url-v1'; const workDetailFetchPromises = new Map(); const MOBILE_QUERY = '(max-width: 768px), (pointer: coarse), (max-device-width: 820px)'; const ASSETS_URL = '/index/mine-homepage'; const HOME_BRAND_LOGO_URL = 'https://assets-us.haistudio.ai/assets/jew/ic_jew_logo.png'; const VERSION = '0.7.50'; const ORIGINAL_USER_AGENT = navigator.userAgent || ''; // 桌面兼容模式配置 const DESKTOP_COMPAT_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'; const DESKTOP_COMPAT_VIEWPORT_WIDTH = 820; const MOBILE_VIEWPORT_CONTENT = 'width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover'; const DESKTOP_COMPAT_VIEWPORT_CONTENT = `width=${DESKTOP_COMPAT_VIEWPORT_WIDTH}, viewport-fit=cover`; // -------- 第二部分:正则表达式匹配规则 -------- // 创作按钮文本识别 const GENERATE_TEXT_RE = /立即创作|开始创作|立即生成|创作|生成/; const PRIMARY_GENERATE_TEXT_RE = /立即创作|开始创作|立即生成/; // 支付和会员相关文本识别 const PAYMENT_ACTION_RE = /开通|升级|我要升级|续费|购买|支付|充值|去开通|去升级|会员升级|升级会员|购买套餐|开通会员/; const DIRECT_PAYMENT_ACTION_RE = /^(立即|马上|去|前往)?(开通|升级|我要升级|续费|购买|支付|充值|去开通|去升级|购买套餐|开通会员|会员升级|升级会员)$/i; const PAYMENT_CONTEXT_RE = /套餐|会员(?!模型)|权益|订阅|入门版|个人版|专业版|旗舰版|商业授权|收银台|订单|支付方式|连续包月|连续包年|个人创作者/; const PAYMENT_CLASS_RE = /vip|member|pay|payment|subscribe|subscription|price|pricing|plan|package|checkout|order/i; // 排除"高阶模型"等非支付文本,防止误判 const NON_PAYMENT_MODEL_TEXT_RE = /高阶模型|商业模型|商业级模型|会员模型|会员专属模型|旗舰版模型/g; const CLASS = { enabled: 'zbai-mobile-enabled', compose: 'zbai-mobile-compose-source', composeOpen: 'zbai-mobile-compose-open', formatChoice: 'zbai-mobile-format-choice', formatLabel: 'zbai-mobile-format-label', formatRow: 'zbai-mobile-format-row', duplicateTitle: 'zbai-mobile-duplicate-title', templateTextMode: 'zbai-mobile-template-text-mode', history: 'zbai-mobile-history-source', historyOpen: 'zbai-mobile-history-open', historyParent: 'zbai-mobile-history-parent', assetHidden: 'zbai-mobile-asset-hidden', uploadArmed: 'zbai-mobile-upload-armed', lock: 'zbai-mobile-lock', }; const EDITOR_ROOT_SELECTOR = '#pictureEditor'; const HOME_TOOL_ACTIONS = [ { key: 'image', label: '图片创作', eyebrow: 'Image', icon: '✦', href: '/create/module/image', desc: '生成首饰海报、商品图和创意画面' }, { key: 'video', label: '视频创作', eyebrow: 'Video', icon: '◆', href: '/create/module/video', desc: '制作运镜展示、旋转视频和短片素材' }, ]; const TEMPLATE_ACTIONS = [ { label: '珠宝海报', href: '/create/ai-effects/jewelry-poster', tag: '海报', icon: '✧', desc: '快速生成东方美学商品海报' }, { label: '道具展示', href: '/create/ai-effects/jewelry-display', tag: '展示', icon: '◇', desc: '为首饰匹配托盘、布景与道具' }, { label: '社交媒体图', href: '/create/ai-effects/social-media', tag: '种草', icon: '◎', desc: '小红书、朋友圈风格内容图' }, { label: '大片模特', href: '/create/ai-effects/jewelry-models-display', tag: '模特', icon: '◐', desc: '真人佩戴与商业大片画面' }, { label: '珠宝纯净图', href: '/create/ai-effects/jewelry-background', tag: '底图', icon: '□', desc: '白底、黑底和纯净背景图' }, { label: '精致摄影', href: '/create/ai-effects/finephotograph', tag: '摄影', icon: '◈', desc: '高级光影、倒影和质感摄影' }, { label: '布料与珠宝', href: '/create/ai-effects/jewelry-cloth', tag: '布料', icon: '≋', desc: '丝绸、天鹅绒等材质布景' }, { label: '首饰图集', href: '/create/ai-effects/jewalbum', tag: '图集', icon: '◫', desc: '首饰图集与灵感参考' }, { label: '珠宝多视角', href: '/create/ai-effects/jewelry-multiview', tag: '视角', icon: '⌁', desc: '多角度展示结构与细节' }, { label: '珠宝详情页', href: '/create/ai-effects/product-details-page', tag: '详情', icon: '☰', desc: '电商详情页视觉图' }, { label: '珠宝视频', href: '/create/ai-effects/jewelry-360-display', tag: '视频', icon: '▶', desc: '旋转展示与火彩展示视频' }, { label: '主体替换', href: '/create/ai-effects/subjectchange', tag: '替换', icon: '⇄', desc: '替换主体并保留画面风格' }, ]; window.__ZBAI_DEBUG__ = isPublicDebugEnabled(); window.__ZBAI_MOBILE_ADAPTER_VERSION__ = VERSION; exposeDiagnosticsIfDebugEnabled(); const media = window.matchMedia(MOBILE_QUERY); let activeSurface = ''; let toastTimer = 0; let authRefreshScheduled = false; let generateActivationInProgress = false; let runGenerateInProgress = false; let lastRootPress = { action: '', time: 0 }; let lastNativeGeneratePress = { target: null, time: 0 }; let lastPreviewCollapsePress = 0; let keyboardViewportInstalled = false; let routeLoadingTimer = 0; let homeRoutesPreloaded = false; installWorkDetailDataCapture(); installDesktopGenerateCompatibility(); // 不默认拦截升级/支付跳转,避免用“挡升级页”假装修复;真实修复走桌面断点和真实生成按钮。 // 如需临时调试可在控制台执行:localStorage.ZBAI_ENABLE_UPGRADE_REDIRECT_BLOCKER='1' if (isUpgradeRedirectBlockerEnabled()) installUpgradeRedirectBlocker(); injectStyles(); waitForBody(boot); function boot() { if (document.getElementById(ROOT_ID)) return; const root = buildRoot(); document.body.append(root); syncMode(); installListeners(root); installKeyboardViewportAdaptation(); scheduleAuthRefreshes(); const observer = new MutationObserver(throttle(() => { syncMode(); }, 180)); observer.observe(document.body, { attributes: true, childList: true, subtree: true }); } function installDesktopGenerateCompatibility() { const state = { attempted: false, applied: false, disabled: false, originalMobileUa: /Mobile|iPhone|iPad|Android|Windows Phone/i.test(navigator.userAgent || ''), routeWatcherInstalled: false, fields: [], viewport: { attempted: false, applied: false, content: '', layoutWidth: 0, reason: '', }, reason: '', }; window.__ZBAI_DESKTOP_GENERATE_COMPAT__ = state; try { state.disabled = localStorage.getItem('ZBAI_DISABLE_DESKTOP_GENERATE_COMPAT') === '1'; } catch (_) { state.disabled = false; } installDesktopGenerateRouteWatcher(state); ensureDesktopGenerateCompatibility('document-start', state); } function installDesktopGenerateRouteWatcher(state) { if (!state || state.routeWatcherInstalled) return; state.routeWatcherInstalled = true; const schedule = (reason, targetUrl = '') => { window.setTimeout(() => ensureDesktopGenerateCompatibility(reason, state, targetUrl), 0); window.setTimeout(() => ensureDesktopGenerateCompatibility(`${reason}:settled`, state, targetUrl), 160); }; try { const originalPushState = window.history.pushState; const originalReplaceState = window.history.replaceState; window.history.pushState = function(...args) { ensureDesktopGenerateCompatibility('history.pushState:before', state, args[2]); const result = originalPushState.apply(this, args); schedule('history.pushState', args[2]); return result; }; window.history.replaceState = function(...args) { ensureDesktopGenerateCompatibility('history.replaceState:before', state, args[2]); const result = originalReplaceState.apply(this, args); schedule('history.replaceState', args[2]); return result; }; } catch (error) { state.routeWatcherError = String(error?.message || error); } window.addEventListener('popstate', () => schedule('popstate'), true); window.addEventListener('hashchange', () => schedule('hashchange'), true); [0, 100, 350, 900, 1800, 3200].forEach((ms) => { window.setTimeout(() => ensureDesktopGenerateCompatibility(`timer-${ms}`, state), ms); }); } function ensureDesktopGenerateCompatibility(reason, state = window.__ZBAI_DESKTOP_GENERATE_COMPAT__, targetUrl = '') { if (!state) return false; const isCreatePage = isCreateRouteTarget(targetUrl || location.href); if (state.disabled) { state.reason = 'disabled-by-localStorage'; return false; } if (!isCreatePage) { state.reason = `waiting-for-create-route:${reason || ''}`; return false; } if (!state.originalMobileUa && !isMobileHardwareForDesktopCompat()) { state.reason = 'not-mobile-device'; return false; } state.attempted = true; installDesktopGenerateViewportCompatibility(state); const defineNavigatorGetter = (key, value) => { try { Object.defineProperty(navigator, key, { configurable: true, get: () => value, }); if (!state.fields.includes(key)) state.fields.push(key); return true; } catch (_) { return false; } }; defineNavigatorGetter('userAgent', DESKTOP_COMPAT_UA); defineNavigatorGetter('appVersion', DESKTOP_COMPAT_UA.replace(/^Mozilla\//, '')); defineNavigatorGetter('platform', 'MacIntel'); defineNavigatorGetter('vendor', 'Google Inc.'); defineDesktopUserAgentData(state); // 覆盖更多移动端检测特征 // 降低触摸点数,让网页认为这是桌面设备 try { Object.defineProperty(navigator, 'maxTouchPoints', { configurable: true, get: () => 0, // 桌面设备通常返回 0 }); if (!state.fields.includes('maxTouchPoints')) state.fields.push('maxTouchPoints'); } catch (_) {} defineDesktopNumberGetter(window, 'innerWidth', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'innerWidth'); defineDesktopNumberGetter(window, 'outerWidth', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'outerWidth'); defineDesktopNumberGetter(window.screen, 'width', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'screen.width'); defineDesktopNumberGetter(window.screen, 'availWidth', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'screen.availWidth'); defineDesktopNumberGetter(document.documentElement, 'clientWidth', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'documentElement.clientWidth'); defineDesktopNumberGetter(document.body, 'clientWidth', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'body.clientWidth'); defineDesktopMatchMedia(state); defineDesktopVisualViewport(state); state.applied = state.fields.length > 0; state.reason = state.applied ? `applied:${reason || ''}` : 'navigator-locked'; window.__ZBAI_DESKTOP_GENERATE_COMPAT_APPLIED__ = state.applied; if (document.documentElement) { document.documentElement.dataset.zbaiDesktopCompat = state.applied ? '1' : '0'; } return state.applied; } function isCreateRouteTarget(urlLike) { try { const url = new URL(String(urlLike || location.href), location.href); return url.pathname.startsWith('/create/') || url.pathname.startsWith('/effects/'); } catch (_) { return location.pathname.startsWith('/create/') || location.pathname.startsWith('/effects/'); } } function defineDesktopUserAgentData(state) { if (state?.fields?.includes('userAgentData')) return true; try { const original = navigator.userAgentData; if (!original) return false; const desktopData = { brands: original.brands || [ { brand: 'Google Chrome', version: '137' }, { brand: 'Chromium', version: '137' }, { brand: 'Not/A)Brand', version: '24' }, ], mobile: false, platform: 'macOS', getHighEntropyValues: async (hints = []) => { const base = typeof original.getHighEntropyValues === 'function' ? await original.getHighEntropyValues.call(original, hints) : {}; return { ...base, mobile: false, platform: 'macOS', platformVersion: '14.7.6', architecture: 'arm', model: '', uaFullVersion: '137.0.0.0', fullVersionList: base.fullVersionList || [ { brand: 'Google Chrome', version: '137.0.0.0' }, { brand: 'Chromium', version: '137.0.0.0' }, { brand: 'Not/A)Brand', version: '24.0.0.0' }, ], }; }, toJSON: () => ({ brands: original.brands || [], mobile: false, platform: 'macOS', }), }; Object.defineProperty(navigator, 'userAgentData', { configurable: true, get: () => desktopData, }); state.fields.push('userAgentData'); return true; } catch (_) { return false; } } function defineDesktopNumberGetter(target, key, minValue, state, fieldName) { if (!target || !key) return false; if (state?.fields?.includes(fieldName)) return true; const owners = [ target, Object.getPrototypeOf(target), target?.constructor?.prototype, ].filter(Boolean); let originalGetter = null; let originalOwner = null; let originalValue = 0; for (const owner of owners) { try { const desc = Object.getOwnPropertyDescriptor(owner, key); if (!desc) continue; originalOwner = owner; if (typeof desc.get === 'function') originalGetter = desc.get; else if ('value' in desc) originalValue = Number(desc.value) || 0; break; } catch (_) { // keep looking } } if (!originalGetter && !originalValue) { try { originalValue = Number(target[key]) || 0; } catch (_) { originalValue = 0; } } const readReal = () => { try { const value = originalGetter ? originalGetter.call(target) : originalValue; return Number.isFinite(Number(value)) ? Number(value) : 0; } catch (_) { return originalValue || 0; } }; const getter = () => Math.max(readReal(), minValue); for (const owner of [target, originalOwner, ...owners].filter(Boolean)) { try { Object.defineProperty(owner, key, { configurable: true, get: getter, }); if (!state.fields.includes(fieldName)) state.fields.push(fieldName); return true; } catch (_) { // try the next owner } } if (state) { state.widthPatchErrors = state.widthPatchErrors || []; state.widthPatchErrors.push(fieldName); } return false; } function defineDesktopMatchMedia(state) { if (state?.fields?.includes('matchMedia')) return true; const original = window.matchMedia?.bind(window); if (typeof original !== 'function') return false; try { window.matchMedia = function(query) { const text = String(query || ''); const patched = text .replace(/max-width\s*:\s*768px/gi, 'max-width: 0px') .replace(/max-device-width\s*:\s*820px/gi, 'max-device-width: 0px'); const result = original(patched); if (/min-width\s*:\s*(769|768|820)px/i.test(text)) { try { Object.defineProperty(result, 'matches', { configurable: true, get: () => true }); } catch (_) {} } if (/max-width\s*:\s*768px|max-device-width\s*:\s*820px/i.test(text)) { try { Object.defineProperty(result, 'matches', { configurable: true, get: () => false }); } catch (_) {} } return result; }; state.fields.push('matchMedia'); return true; } catch (_) { return false; } } function defineDesktopVisualViewport(state) { if (!window.visualViewport || state?.fields?.includes('visualViewport.width')) return false; const ok = defineDesktopNumberGetter(window.visualViewport, 'width', DESKTOP_COMPAT_VIEWPORT_WIDTH, state, 'visualViewport.width'); defineDesktopNumberGetter(window.visualViewport, 'scale', 1, state, 'visualViewport.scale'); return ok; } function isMobileHardwareForDesktopCompat() { const hasTouch = navigator.maxTouchPoints > 0 || 'ontouchstart' in window; const physicalWidth = Math.min(window.screen.width || window.innerWidth, window.screen.height || window.innerHeight); return hasTouch && physicalWidth <= 820; } // ============================================================================ // 升级页面跳转拦截器 // ============================================================================ // 功能:拦截所有跳转到升级/会员页面的操作 // 原因:移动端网页在选择高阶/商业模型后会跳转升级页,即使用户已是会员 // 方案:监听多种跳转方式(location、pushState、replaceState、链接点击)并阻止 // ============================================================================ function isUpgradeRedirectBlockerEnabled() { try { return localStorage.getItem('ZBAI_ENABLE_UPGRADE_REDIRECT_BLOCKER') === '1'; } catch (_) { return false; } } function installUpgradeRedirectBlocker() { const state = { blocked: [], allowed: [], }; window.__ZBAI_UPGRADE_REDIRECT_BLOCKER__ = state; // 升级/会员页面的 URL 特征 const UPGRADE_PAGE_PATTERNS = [ /\/vip\/?$/i, /\/member\/?$/i, /\/upgrade\/?$/i, /\/pricing\/?$/i, /\/subscribe\/?$/i, /\/payment\/?$/i, /[?&]upgrade=1/i, /[?&]action=upgrade/i, /[?&]action=subscribe/i, ]; function isUpgradeUrl(url) { if (!url) return false; try { const urlStr = typeof url === 'string' ? url : url.toString(); return UPGRADE_PAGE_PATTERNS.some(pattern => pattern.test(urlStr)); } catch (_) { return false; } } // 1. 拦截 location 赋值跳转 const originalLocationSetter = Object.getOwnPropertyDescriptor(window.Location.prototype, 'href'); if (originalLocationSetter && originalLocationSetter.set) { Object.defineProperty(window.Location.prototype, 'href', { set: function(value) { if (isCreateRoute() && isUpgradeUrl(value)) { state.blocked.push({ type: 'location.href', url: value, time: Date.now() }); debugLog('upgradeRedirectBlocked:location.href', { url: value }); toast('已阻止跳转到升级页,继续使用当前功能'); return; } originalLocationSetter.set.call(this, value); }, get: originalLocationSetter.get, configurable: true, }); } // 2. 拦截 location.assign / location.replace try { const originalAssign = window.location.assign; const originalReplace = window.location.replace; // 使用 try-catch 保护,因为某些环境下这些属性可能是只读的 try { window.location.assign = function(url) { if (isCreateRoute() && isUpgradeUrl(url)) { state.blocked.push({ type: 'location.assign', url, time: Date.now() }); debugLog('upgradeRedirectBlocked:location.assign', { url }); toast('已阻止跳转到升级页'); return; } return originalAssign.call(window.location, url); }; } catch (e) { debugLog('upgradeRedirectBlocker:location.assign', { error: 'readonly' }); } try { window.location.replace = function(url) { if (isCreateRoute() && isUpgradeUrl(url)) { state.blocked.push({ type: 'location.replace', url, time: Date.now() }); debugLog('upgradeRedirectBlocked:location.replace', { url }); toast('已阻止跳转到升级页'); return; } return originalReplace.call(window.location, url); }; } catch (e) { debugLog('upgradeRedirectBlocker:location.replace', { error: 'readonly' }); } } catch (e) { debugLog('upgradeRedirectBlocker:location methods', { error: e.message }); } // 3. 拦截 history.pushState / history.replaceState const originalPushState = window.history.pushState; const originalReplaceState = window.history.replaceState; window.history.pushState = function(state, title, url) { if (isCreateRoute() && url && isUpgradeUrl(url)) { window.__ZBAI_UPGRADE_REDIRECT_BLOCKER__.blocked.push({ type: 'pushState', url, time: Date.now() }); debugLog('upgradeRedirectBlocked:pushState', { url }); toast('已阻止跳转到升级页'); return; } return originalPushState.call(this, state, title, url); }; window.history.replaceState = function(state, title, url) { if (isCreateRoute() && url && isUpgradeUrl(url)) { window.__ZBAI_UPGRADE_REDIRECT_BLOCKER__.blocked.push({ type: 'replaceState', url, time: Date.now() }); debugLog('upgradeRedirectBlocked:replaceState', { url }); toast('已阻止跳转到升级页'); return; } return originalReplaceState.call(this, state, title, url); }; // 4. 拦截链接点击 document.addEventListener('click', function(e) { if (!isCreateRoute()) return; const link = e.target?.closest('a[href]'); if (!link) return; const href = link.getAttribute('href'); if (isUpgradeUrl(href)) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); state.blocked.push({ type: 'link.click', url: href, time: Date.now() }); debugLog('upgradeRedirectBlocked:link.click', { href, link }); toast('已阻止跳转到升级页'); } }, true); // 使用捕获阶段,优先拦截 debugLog('installUpgradeRedirectBlocker:installed', {}); } function installDesktopGenerateViewportCompatibility(state) { applyDesktopGenerateViewport('document-start', state); [0, 30, 120, 360, 900, 1800].forEach((ms) => { window.setTimeout(() => applyDesktopGenerateViewport(`timer-${ms}`, state), ms); }); try { const observer = new MutationObserver(() => { if (shouldUseDesktopGenerateViewport()) applyDesktopGenerateViewport('head-mutation', state); }); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['content'] }); window.setTimeout(() => observer.disconnect(), 6000); } catch (_) { // MutationObserver is only a best-effort guard against the app rewriting the viewport meta. } } function shouldUseDesktopGenerateViewport() { if (!isCreateRoute()) return false; if (isDesktopViewportCompatDisabled()) return false; const hasTouch = navigator.maxTouchPoints > 0 || 'ontouchstart' in window; const physicalWidth = Math.min(window.screen.width || window.innerWidth, window.screen.height || window.innerHeight); const originalMobileUa = Boolean(window.__ZBAI_DESKTOP_GENERATE_COMPAT__?.attempted); return originalMobileUa || (hasTouch && physicalWidth <= 820); } function isDesktopViewportCompatDisabled() { try { return localStorage.getItem('ZBAI_DISABLE_DESKTOP_VIEWPORT_COMPAT') === '1' || localStorage.getItem('ZBAI_DISABLE_DESKTOP_GENERATE_COMPAT') === '1'; } catch (_) { return false; } } function applyDesktopGenerateViewport(reason, state = window.__ZBAI_DESKTOP_GENERATE_COMPAT__) { const viewportState = state?.viewport || { attempted: false, applied: false, content: '', layoutWidth: 0, reason: '', }; if (state && !state.viewport) state.viewport = viewportState; viewportState.attempted = true; if (!shouldUseDesktopGenerateViewport()) { viewportState.reason = 'not-needed'; viewportState.layoutWidth = window.innerWidth || 0; return false; } const viewport = getOrCreateViewportMeta(); if (!viewport) { viewportState.reason = 'no-viewport-meta-target'; viewportState.layoutWidth = window.innerWidth || 0; return false; } if (document.head && viewport.parentElement !== document.head) { try { document.head.appendChild(viewport); } catch (_) { // Keeping the meta node in documentElement is still better than losing it entirely. } } if (viewport.getAttribute('content') !== DESKTOP_COMPAT_VIEWPORT_CONTENT) { viewport.setAttribute('content', DESKTOP_COMPAT_VIEWPORT_CONTENT); } viewport.dataset.zbaiDesktopGenerateViewport = '1'; viewportState.applied = viewport.getAttribute('content') === DESKTOP_COMPAT_VIEWPORT_CONTENT; viewportState.content = viewport.getAttribute('content') || ''; viewportState.layoutWidth = window.innerWidth || 0; viewportState.reason = reason || (viewportState.applied ? 'applied' : 'failed'); if (document.documentElement) { document.documentElement.dataset.zbaiDesktopViewportCompat = viewportState.applied ? '1' : '0'; } return viewportState.applied; } function getOrCreateViewportMeta() { let viewport = document.querySelector('meta[name="viewport"]'); if (viewport instanceof HTMLMetaElement) return viewport; viewport = document.createElement('meta'); viewport.name = 'viewport'; const target = document.head || document.querySelector('head') || document.documentElement; if (!target?.appendChild) return null; target.appendChild(viewport); return viewport; } function installListeners(root) { root.addEventListener('pointerup', onRootPress); root.addEventListener('touchend', onRootPress, { passive: false }); root.addEventListener('click', onRootPress); listenForModeChange(media); window.addEventListener('resize', throttle(syncMode, 180), { passive: true }); document.addEventListener('pointerup', interceptNativeGenerateClick, true); document.addEventListener('touchend', interceptNativeGenerateClick, { capture: true, passive: false }); document.addEventListener('click', interceptNativeGenerateClick, true); document.addEventListener('pointerup', collapseComposeOnPreviewPress, true); document.addEventListener('touchend', collapseComposeOnPreviewPress, { capture: true, passive: false }); document.addEventListener('click', collapseComposeOnPreviewPress, true); document.addEventListener('click', delayPromoteFloatingMenus, true); document.addEventListener('touchend', delayPromoteFloatingMenus, { capture: true, passive: true }); } function installKeyboardViewportAdaptation() { if (keyboardViewportInstalled) return; keyboardViewportInstalled = true; const scheduleUpdate = () => { updateKeyboardViewportState(); window.setTimeout(updateKeyboardViewportState, 80); window.setTimeout(updateKeyboardViewportState, 260); }; document.addEventListener('focusin', scheduleUpdate, true); document.addEventListener('focusout', () => window.setTimeout(updateKeyboardViewportState, 120), true); window.addEventListener('resize', throttle(updateKeyboardViewportState, 120), { passive: true }); window.visualViewport?.addEventListener?.('resize', throttle(updateKeyboardViewportState, 80), { passive: true }); window.visualViewport?.addEventListener?.('scroll', throttle(updateKeyboardViewportState, 80), { passive: true }); window.setInterval(updateKeyboardViewportState, 360); updateKeyboardViewportState(); } function updateKeyboardViewportState() { const root = document.documentElement; const active = document.activeElement; const activeEditorInput = (isMobile() || isOriginalMobileDevice()) && isCreatePage() && isEditorTextEntry(active); const viewport = window.visualViewport; const viewportHeight = Math.max(260, Math.round(viewport?.height || window.innerHeight || root.clientHeight || 0)); const keyboardBottom = Math.max(0, Math.round((window.innerHeight || viewportHeight) - viewportHeight - (viewport?.offsetTop || 0))); root.style.setProperty('--zbai-keyboard-viewport-height', `${viewportHeight}px`); root.style.setProperty('--zbai-keyboard-bottom', `${keyboardBottom}px`); if (!activeEditorInput) { delete root.dataset.zbaiKeyboardFocus; delete root.dataset.zbaiAndroidKeyboard; return; } root.dataset.zbaiKeyboardFocus = '1'; if (isOriginalAndroidDevice()) root.dataset.zbaiAndroidKeyboard = '1'; else delete root.dataset.zbaiAndroidKeyboard; window.setTimeout(() => { try { active.scrollIntoView?.({ block: 'center', inline: 'nearest', behavior: 'smooth' }); } catch (_) { active.scrollIntoView?.(); } }, 90); } function isEditorTextEntry(node) { if (!node || node.nodeType !== 1 || typeof node.closest !== 'function') return false; if (!node.closest('.picture-editor-content-control, #pictureEditor')) return false; const tagName = String(node.tagName || '').toLowerCase(); if (tagName === 'textarea') return true; if (tagName === 'input') { const type = String(node.type || 'text').toLowerCase(); return !/file|checkbox|radio|button|submit|reset|range|color|hidden/i.test(type); } return node.isContentEditable === true || node.getAttribute('role') === 'textbox'; } function isOriginalAndroidDevice() { return /Android/i.test(ORIGINAL_USER_AGENT) || /Android/i.test(navigator.userAgent || ''); } function isOriginalMobileDevice() { return /Mobile|iPhone|iPad|Android|Windows Phone/i.test(ORIGINAL_USER_AGENT) || /Mobile|iPhone|iPad|Android|Windows Phone/i.test(navigator.userAgent || ''); } function buildRoot() { const root = document.createElement('div'); root.id = ROOT_ID; root.innerHTML = `
移动适配已启用
`; return root; } function onRootPress(event) { const button = event.target.closest('[data-zbai-action]'); if (!button || !isMobile()) return; event.preventDefault(); event.stopPropagation(); const action = button.dataset.zbaiAction; setDebugState('__ZBAI_LAST_ROOT_PRESS__', { action, type: event.type, trusted: event.isTrusted === true, time: Date.now(), }); if (shouldIgnoreDuplicateRootPress(action, event.type)) return; if (action === 'close') closeSurface(); if (action === 'back') goBack(); if (action === 'compose') handleComposeAction(event); if (action === 'upload') openUpload(button); if (action === 'history') toggleHistory(); if (action === 'download') openDownload(); if (action === 'auth') toggleAuth(); } function shouldIgnoreDuplicateRootPress(action, eventType) { const now = Date.now(); if (eventType === 'touchend' || eventType === 'pointerup') { if (lastRootPress.action === action && now - lastRootPress.time < 120) return true; lastRootPress = { action, time: now }; return false; } if (eventType === 'click' && lastRootPress.action === action && now - lastRootPress.time < 650) { return true; } lastRootPress = { action, time: now }; return false; } async function toggleCompose() { const panel = await waitForComposePanel(); if (!panel) { toast('还没找到生成面板,页面加载完再点一次'); return; } if (activeSurface === 'compose') { closeSurface(); return; } openSurface('compose', panel); focusPrompt(panel); } async function handleComposeAction(event) { delete document.documentElement.dataset.zbaiPreviewFocus; setDebugState('__ZBAI_LAST_COMPOSE_ACTION__', { eventType: event?.type || '', trusted: event?.isTrusted === true, activeSurface, shouldGenerate: shouldGenerateImmediately(), time: Date.now(), }); if (activeSurface === 'compose') { await runGenerate({ preferImmediate: isUserGestureEvent(event) }); return; } if (shouldGenerateImmediately()) { await runGenerate({ preferImmediate: isUserGestureEvent(event) }); return; } await toggleCompose(); } function isUserGestureEvent(event) { return Boolean(event?.isTrusted && /^(click|touchend|pointerup)$/i.test(event.type || '')); } function shouldGenerateImmediately() { const panel = findComposePanel(); if (!panel) return false; const generate = findGenerateButton(panel); if (!generate || isDisabledControl(generate)) return false; const prompt = findPromptInput(panel); const hasPromptText = prompt && String(prompt.value || '').trim().length > 0; const hasUpload = Boolean(findUploadControl(panel)); return Boolean(hasPromptText || hasUpload); } function interceptNativeGenerateClick(event) { if (!isMobile() || generateActivationInProgress) return; const rawTarget = event.target instanceof HTMLElement ? event.target : event.target?.parentElement; if (!(rawTarget instanceof HTMLElement) || rawTarget.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) return; if (isGenerateExcludedControl(rawTarget)) return; const clicked = rawTarget.closest?.('button, a, label, [role="button"], [role="menuitem"], .generate-button-container, .control-buttons') || rawTarget; if (isGenerateExcludedControl(clicked)) return; const panel = findComposePanel(); if (!panel || !panel.contains(clicked)) return; const generate = findGenerateButton(panel); if (!generate) return; if (!isGeneratePressPoint(event, generate, clicked)) return; if (!isSameGenerateControl(clicked, generate)) return; if (shouldIgnoreDuplicateNativeGeneratePress(event, generate)) { event.preventDefault?.(); event.stopPropagation?.(); event.stopImmediatePropagation?.(); return; } event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation?.(); ensureDesktopGenerateCompatibility(`native-generate:${event.type || 'event'}`); debugLog('nativeGenerate:intercepted', { eventType: event.type || '', clicked: describeElement(clicked), generate: describeElement(generate), desktopCompat: window.__ZBAI_DESKTOP_GENERATE_COMPAT_APPLIED__ === true, }); runGenerate({ preferImmediate: isUserGestureEvent(event) }); } function shouldIgnoreDuplicateNativeGeneratePress(event, generate) { const now = Date.now(); if (lastNativeGeneratePress.target === generate && now - lastNativeGeneratePress.time < 720) return true; lastNativeGeneratePress = { target: generate, time: now }; return false; } function isSameGenerateControl(clicked, generate) { if (!(clicked instanceof HTMLElement) || !(generate instanceof HTMLElement)) return false; if (isGenerateExcludedControl(clicked)) return false; if (clicked === generate || generate.contains(clicked)) return true; if (clicked.contains(generate) && isNarrowGenerateShell(clicked)) return true; const clickedShell = clicked.closest?.('.generate-button-container, .control-buttons'); const generateShell = generate.closest?.('.generate-button-container, .control-buttons'); return Boolean(clickedShell && generateShell && clickedShell === generateShell); } function isNarrowGenerateShell(node) { if (!(node instanceof HTMLElement)) return false; return node.matches('.generate-button-container, .control-buttons') && !isGenerateExcludedControl(node); } function isGeneratePressPoint(event, generate, clicked) { const point = getEventClientPoint(event); if (!point) return true; const button = generate.closest?.('button, [role="button"]') || generate; if (isPointInsideElement(point, button, 2)) return true; const shell = generate.closest?.('.generate-button-container'); if (shell instanceof HTMLElement && isPointInsideElement(point, shell, 2)) return true; // 没有进入真实按钮或按钮容器的触点,不能用 ControlBottom 大容器兜底。 // 这能防止数量 1/2/4、比例、分辨率等相邻控件被误认为生成。 return clicked instanceof HTMLElement && (clicked === generate || generate.contains(clicked)); } function getEventClientPoint(event) { const touch = event?.changedTouches?.[0] || event?.touches?.[0]; const x = touch?.clientX ?? event?.clientX; const y = touch?.clientY ?? event?.clientY; if (!Number.isFinite(x) || !Number.isFinite(y)) return null; return { x, y }; } function isPointInsideElement(point, node, padding = 0) { if (!(node instanceof HTMLElement)) return false; const rect = node.getBoundingClientRect(); return rect.width > 0 && rect.height > 0 && point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding; } function isGenerateExcludedControl(node) { if (!(node instanceof HTMLElement)) return false; if (node.closest('.generate-button-container')) return false; if (node.closest('.choose-number-content, .choose-number-options, [class*="ChooseNumber"], [class*="choose-number"], [class*="Quantity"], [class*="quantity"], [class*="Count"], [class*="count"]')) return true; const text = normalizedText(node); if (/^(数量|张数|生成数量)$/.test(text)) return true; if (/^[124]$/.test(text)) { const context = findQuantityContext(node); return Boolean(context); } return false; } function findQuantityContext(node) { if (!(node instanceof HTMLElement)) return null; let parent = node; let depth = 0; while (parent && parent !== document.body && depth < 7) { if (parent.matches?.('.generate-button-container')) return null; const text = normalizedText(parent).slice(0, 220); if (/(数量|张数|生成数量)/.test(text)) return parent; parent = parent.parentElement; depth += 1; } return null; } function collapseComposeOnPreviewPress(event) { if (!isMobile() || getPageType() !== 'editor') return; if (activeSurface && activeSurface !== 'compose') return; if (document.documentElement.dataset.zbaiPreviewFocus === '1') return; const panel = findComposePanel(); if (!(panel instanceof HTMLElement)) return; const rawTarget = event.target instanceof HTMLElement ? event.target : event.target?.parentElement; if (!(rawTarget instanceof HTMLElement)) return; if (rawTarget.closest(`#${ROOT_ID}, .${CLASS.compose}, .${CLASS.history}, [role="dialog"], .ant-modal, .ant-drawer`)) return; if (panel.contains(rawTarget)) return; const preview = rawTarget.closest?.('.picture-editor-content-render, [class*="picture-editor-content-render"], [class*="Render"], [class*="Preview"], [class*="Canvas"]'); if (!(preview instanceof HTMLElement)) return; if (!isLikelyEditorPreviewArea(preview)) return; const now = Date.now(); if (now - lastPreviewCollapsePress < 520) { event.preventDefault?.(); event.stopPropagation?.(); return; } lastPreviewCollapsePress = now; event.preventDefault?.(); event.stopPropagation?.(); collapseComposeToPreview(); debugLog('compose:collapsedByPreview', describeElement(preview)); } function collapseComposeToPreview() { closeSurface(); if (getPageType() === 'editor') { document.documentElement.dataset.zbaiPreviewFocus = '1'; } } function isLikelyEditorPreviewArea(node) { if (!(node instanceof HTMLElement)) return false; if (node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) return false; const rect = node.getBoundingClientRect(); if (rect.width < 160 || rect.height < 120) return false; const path = getDomPath(node); return /picture-editor-content-render|render|preview|canvas/i.test(String(node.className || '') + ' ' + path); } // ========== 创作页面核心逻辑 ========== // 执行创作流程: // 1. 找到创作面板和立即创作按钮 // 2. 检测支付遮挡和模型限制 // 3. 激活按钮触发创作 // 4. 监听结果图片 async function runGenerate(options = {}) { delete document.documentElement.dataset.zbaiPreviewFocus; setDebugState('__ZBAI_LAST_RUN_GENERATE__', { options, skipped: runGenerateInProgress === true, time: Date.now(), }); if (runGenerateInProgress) { debugLog('runGenerate:skipDuplicate', {}); return; } runGenerateInProgress = true; try { const panel = findComposePanel() || await waitForComposePanel(); if (!panel) { toast('没找到立即创作按钮,请确认已进入创作页'); return; } // 只在真实创作控制面板内找按钮,避免扫到移动 dock、会员卡片或全局入口。 const generate = findGenerateButton(panel); if (!generate) { debugLog('runGenerate:missing', { panel: describeElement(panel), candidates: describeGenerateCandidates(panel), }); toast('没找到立即创作按钮,请确认已进入创作页'); return; } openSurface('compose', panel); // openSurface 可能改变布局,但不应扩大搜索范围。 const scopedPanel = findComposePanel() || panel; let generate2 = generate.isConnected ? generate : (findGenerateButton(scopedPanel) || generate); const beforeCount = findResultImages().length; if (options.preferImmediate && tryActivateGenerateImmediately(generate2, beforeCount)) { await waitForNewResultAfterGenerate(beforeCount); return; } await prepareGenerateTargetForClick(generate2); let audit = auditGenerateTarget(generate2, { requireClickSafety: true }); debugLog('runGenerate:target', { beforeCount, panel: describeElement(scopedPanel), audit, candidates: describeGenerateCandidates(scopedPanel), }); if (!audit.allowed && shouldTryCloseBlockingPayment(audit)) { const closeResult = await closeBlockingPaymentOverlay(generate2, audit); debugLog('runGenerate:closedPaymentOverlay', closeResult); if (closeResult.closed) { generate2 = findGenerateButton(findComposePanel() || scopedPanel) || generate2; await prepareGenerateTargetForClick(generate2); audit = auditGenerateTarget(generate2, { requireClickSafety: true }); debugLog('runGenerate:targetAfterPaymentClose', { audit, candidates: describeGenerateCandidates(findComposePanel() || scopedPanel), }); } } if (!audit.allowed) { debugLog('runGenerate:blocked', audit); if (audit.directPaymentNode || audit.paymentAncestor || audit.insidePaymentArea) { toast('识别到会员升级区,已阻止误点'); } else if (audit.click?.paymentHit || audit.click?.insidePaymentArea) { toast('识别到升级/支付遮挡,已阻止误点'); } else if (audit.click && !audit.click.safe) { toast('立即创作按钮被遮挡,请关闭弹层后再试'); } else { toast('立即创作按钮当前不可用,请先完成提示词、上传或登录'); } return; } if (!activateGenerateButton(generate2, audit)) return; await waitForNewResultAfterGenerate(beforeCount); } finally { window.setTimeout(() => { runGenerateInProgress = false; }, 600); } } async function openUpload(triggerButton) { delete document.documentElement.dataset.zbaiPreviewFocus; const currentPanel = findComposePanel(); const immediateUpload = currentPanel ? findUploadControl(currentPanel) : null; if (immediateUpload) { openSurface('compose', currentPanel); if (activateUpload(immediateUpload)) { toast('正在打开上传参考图'); return; } armNativeUploadProxy(immediateUpload, triggerButton); toast('上传参考图已准备好,请再点一次上传'); return; } const panel = await waitForComposePanel(); if (!panel) { toast('还没找到上传区,先进入图片创作页'); return; } openSurface('compose', panel); const imageMode = findModeToggle(panel, '图生'); if (imageMode) { activate(imageMode); await delay(360); await waitForImageUploadControl(panel); } const upload = findUploadControl(findComposePanel() || panel); if (!upload) { toast('没找到上传参考图按钮'); return; } try { upload.scrollIntoView({ block: 'center', behavior: 'smooth' }); } catch (_) { upload.scrollIntoView?.(); } armNativeUploadProxy(upload, triggerButton); toast('上传参考图已准备好,请再点一次上传'); } async function toggleHistory() { if (activeSurface !== 'history') showRouteLoading('正在打开历史'); const rail = await waitForHistoryRail(); if (!rail) { hideRouteLoading(); toast('当前创作页没有找到历史缩略图,可点右上角资产查看全部'); return; } if (activeSurface === 'history') { hideRouteLoading(); closeSurface(); return; } rail.classList.add(CLASS.history); debugLog('history:openBefore', describeHistoryRail(rail)); openSurface('history', rail); debugLog('history:openAfter', describeHistoryRail(rail)); rail.scrollIntoView({ block: 'nearest', inline: 'nearest' }); window.setTimeout(hideRouteLoading, 260); } async function openDownload() { closeSurface(); const detailDownload = document.querySelector(`#${WORK_DETAIL_FALLBACK_ID} [data-zbai-work-download]`); if (detailDownload) { activate(detailDownload); return; } const download = findDownloadControl(); if (download) { activate(download); return; } const result = findResultImages()[0] || findPrimaryResult(); if (!result) { toast('先打开或生成一张图片,再点下载'); return; } const hadPreview = hasVisiblePreviewModal(); const beforeUrl = location.href; activate(result); await delay(500); const previewDownload = findDownloadControl(); if (previewDownload) { activate(previewDownload); return; } if (!hadPreview && !hasVisiblePreviewModal() && location.href === beforeUrl) { toast('未能打开预览图'); return; } toast('预览已打开,请点预览里的下载图标'); } function openSurface(surface, node) { closeSurface(); delete document.documentElement.dataset.zbaiPreviewFocus; activeSurface = surface; if (surface === 'history') prepareHistoryRailForMobile(node); node.classList.add(surface === 'compose' ? CLASS.composeOpen : CLASS.historyOpen); if (surface === 'compose') node.scrollTop = 0; document.body.classList.add(CLASS.lock); document.getElementById(ROOT_ID)?.setAttribute('data-zbai-surface', surface); } function closeSurface() { activeSurface = ''; disarmNativeUploadProxy(); document.querySelector(`.${CLASS.composeOpen}`)?.classList.remove(CLASS.composeOpen); document.querySelector(`.${CLASS.historyOpen}`)?.classList.remove(CLASS.historyOpen); document.querySelectorAll(`.${CLASS.historyParent}`).forEach((node) => node.classList.remove(CLASS.historyParent)); document.body?.classList.remove(CLASS.lock); document.getElementById(ROOT_ID)?.removeAttribute('data-zbai-surface'); } function goBack() { closeSurface(); let sameOriginReferrer = false; try { sameOriginReferrer = Boolean(document.referrer) && new URL(document.referrer).origin === window.location.origin; } catch (_) { sameOriginReferrer = false; } if (sameOriginReferrer && window.history.length > 1) { window.history.back(); return; } navigateTo('/'); } function refreshTargets() { if (!document.body) return; ensureDesktopGenerateCompatibility('refreshTargets'); ensureViewport(); updatePageType(); syncLoginModalState(); refreshRoute(); refreshAuthControl(); hideUnwantedAssetTabs(); scheduleRouteLoadingDismiss(); if (getPageType() !== 'editor') delete document.documentElement.dataset.zbaiPreviewFocus; const workDetailRoute = isWorkDetailRoute(); if (workDetailRoute) ensureWorkDetailFallback(); else cleanupWorkDetailFallback(); if (!workDetailRoute) ensureWorkPreviewOverlay(); else cleanupWorkPreviewOverlay(); if (!isMobile()) { closeSurface(); cleanupMobileHomeShell(); cleanupWorkPreviewOverlay(); return; } if (document.documentElement.dataset.zbaiPage === 'home') { ensureMobileHomeShell(); preloadHomeRoutes(); } else { cleanupMobileHomeShell(); } document.querySelector(EDITOR_ROOT_SELECTOR)?.classList.add(CLASS_READY); const compose = findComposePanel(); compose?.classList.add(CLASS.compose); markFormatControls(compose); markTemplateTextMode(compose); markDuplicateTitles(compose); findHistoryRail()?.classList.add(CLASS.history); consumePendingRecreateIfNeeded(compose); promoteFloatingMenus(); if (window.__ZBAI_DEBUG__ === true) debugLog('refreshTargets', collectDiagnostics()); } function refreshRoute() { const root = document.getElementById(ROOT_ID); if (!root) return; root.dataset.zbaiRoute = findComposePanel() || isCreateRoute() ? 'compose' : 'page'; } function refreshAuthControl() { const button = document.querySelector(`#${ROOT_ID} .zbai-mobile-auth`); if (!button) return; const loggedIn = isLoggedIn(); button.textContent = loggedIn ? '退出' : '登录'; button.setAttribute('aria-label', loggedIn ? '退出登录' : '登录'); } function hideUnwantedAssetTabs() { if (getPageType() !== 'assets') { document.querySelectorAll(`.${CLASS.assetHidden}`).forEach((node) => node.classList.remove(CLASS.assetHidden)); return; } [...document.querySelectorAll('button, a, [role="tab"], [role="button"], li, div, span')] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, #${WORK_DETAIL_FALLBACK_ID}, #${WORK_PREVIEW_OVERLAY_ID}`)) .forEach((node) => { const text = normalizedText(node); if (!/^(发过|爱过)$/.test(text)) return; const target = node.closest?.('.ant-tabs-tab, [role="tab"], button, a, li, [role="button"]') || node; if (!(target instanceof HTMLElement)) return; if (normalizedText(target).length > 8 && target !== node) return; target.classList.add(CLASS.assetHidden); }); } function scheduleAuthRefreshes() { if (authRefreshScheduled) return; authRefreshScheduled = true; [300, 900, 1800, 3200, 5200, 8000].forEach((ms) => { window.setTimeout(() => { refreshAuthControl(); const shell = document.getElementById(HOME_SHELL_ID); if (shell) { updateMobileHomeAuth(shell); updateMobileHomeCredits(shell); } if (ms === 8000) authRefreshScheduled = false; }, ms); }); } function syncMode() { const enabled = isMobile(); document.documentElement.dataset.zbaiMobile = enabled ? '1' : '0'; document.body?.classList.toggle(CLASS.enabled, enabled); if (!enabled) closeSurface(); refreshTargets(); } function listenForModeChange(query) { const onChange = () => syncMode(); if (typeof query.addEventListener === 'function') query.addEventListener('change', onChange); else query.addListener?.(onChange); } function delayPromoteFloatingMenus() { window.setTimeout(promoteFloatingMenus, 60); } function promoteFloatingMenus() { if (!isMobile() || !activeSurface) return; const menus = document.querySelectorAll([ '.ant-select-dropdown', '.ant-dropdown', '.arco-select-popup', '.el-select-dropdown', '[role="listbox"]', '[data-radix-popper-content-wrapper]', ].join(',')); [...menus].filter(isFloatingMenu).forEach((node) => { if (node.style.getPropertyValue('z-index') !== '2147483605') { node.style.setProperty('z-index', '2147483605', 'important'); } }); } function isFloatingMenu(node) { if (!node || node.closest(`#${ROOT_ID}`)) return false; const style = getComputedStyle(node); const rect = node.getBoundingClientRect(); const position = style.position === 'absolute' || style.position === 'fixed'; return position && rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden'; } async function toggleAuth() { closeSurface(); if (isLoggedIn()) { await openLogout(); return; } openLogin(); } function openLogin() { document.documentElement.dataset.zbaiLoginOpen = '1'; document.documentElement.dataset.zbaiLoginRequestedAt = String(Date.now()); cleanupMobileHomeShell(); const login = findLoginButton(); if (!login) { delete document.documentElement.dataset.zbaiLoginOpen; delete document.documentElement.dataset.zbaiLoginRequestedAt; if (getPageType() === 'home') ensureMobileHomeShell(); toast('还没找到原站登录入口,刷新首页后再点'); debugLog('openLogin:missing', collectDiagnostics()); return; } debugLog('openLogin', describeElement(login)); activate(login); scheduleLoginStateCleanup(); scheduleAuthRefreshes(); } async function openLogout() { if (getPageType() === 'home') { document.documentElement.dataset.zbaiLogoutPending = '1'; ensureMobileHomeShell(); toast('正在退出登录...'); await delay(80); } let logout = findLogoutButton(); if (logout) { debugLog('openLogout:direct', describeElement(logout)); activate(logout); scheduleLogoutCleanup(); return; } const menu = findMobileMenuButton(); if (!menu) { delete document.documentElement.dataset.zbaiLogoutPending; toast('还没找到原站菜单入口,刷新首页后再点'); return; } debugLog('openLogout:menuButton', describeElement(menu)); activate(menu); await delay(520); const drawer = findOpenMobileDrawer(); logout = findLogoutButton(drawer || document); if (logout) { debugLog('openLogout:drawer', describeElement(logout)); activate(logout); scheduleLogoutCleanup(); return; } toast('账号菜单已打开,请点 Log Out'); scheduleLogoutCleanup(); } function scheduleLoginStateCleanup() { [1800, 2600, 4200, 7000].forEach((ms) => { window.setTimeout(() => { if (hasLoginModalDom()) { document.documentElement.dataset.zbaiLoginOpen = '1'; if (getPageType() === 'home') cleanupMobileHomeShell(); return; } if (!hasVisibleLoginModal()) { delete document.documentElement.dataset.zbaiLoginOpen; delete document.documentElement.dataset.zbaiLoginRequestedAt; if (isMobile() && getPageType() === 'home') ensureMobileHomeShell(); } }, ms); }); } function scheduleNativeMenuCleanup() { const cleanup = () => { if (!document.documentElement.dataset.zbaiNativeMenuOpen) return; const drawer = findOpenMobileDrawer(); if (drawer) { window.setTimeout(cleanup, 800); return; } delete document.documentElement.dataset.zbaiNativeMenuOpen; if (isMobile() && getPageType() === 'home') ensureMobileHomeShell(); }; [1200, 2200, 4500, 8000].forEach((ms) => window.setTimeout(cleanup, ms)); } function scheduleLogoutCleanup() { [700, 1500, 2600, 4200, 7000].forEach((ms) => { window.setTimeout(() => { refreshAuthControl(); const shell = document.getElementById(HOME_SHELL_ID); if (shell) { updateMobileHomeAuth(shell); updateMobileHomeCredits(shell); } if (ms >= 2600 || !isLoggedIn()) { delete document.documentElement.dataset.zbaiLogoutPending; delete document.documentElement.dataset.zbaiNativeMenuOpen; if (isMobile() && getPageType() === 'home') ensureMobileHomeShell(); } }, ms); }); } function syncLoginModalState() { if (hasLoginModalDom()) { document.documentElement.dataset.zbaiLoginOpen = '1'; if (getPageType() === 'home') cleanupMobileHomeShell(); return; } cleanupStaleLoginState(); } function cleanupStaleLoginState() { if (document.documentElement.dataset.zbaiLoginOpen !== '1') return; if (hasVisibleLoginModal()) return; if (hasLoginModalDom()) return; const requestedAt = Number(document.documentElement.dataset.zbaiLoginRequestedAt || '0'); if (requestedAt && Date.now() - requestedAt < 2200) return; delete document.documentElement.dataset.zbaiLoginOpen; delete document.documentElement.dataset.zbaiLoginRequestedAt; } function hasVisibleLoginModal() { const modal = [ ...document.querySelectorAll([ '.LoginModal_LoginModal__O59AS', '[class*="LoginModal"]', '[class*="OverseasLogin_overseasLogin"]', '[class*="overseasLoginModal"]', '.fixed.top-0.left-0.w-screen.h-screen', ].join(',')), ].some((node) => node instanceof HTMLElement && isUsable(node)); if (modal) return true; const phone = document.querySelector('input#first-phone, input[placeholder*="请输入手机号码"]'); if (phone instanceof HTMLElement && isUsable(phone)) return true; return [...document.querySelectorAll('div, section, main')] .some((node) => { if (!(node instanceof HTMLElement) || !isUsable(node)) return false; const text = normalizedText(node); return text.includes('欢迎来到') && text.includes('珠宝AI') && (text.includes('手机号登录') || text.includes('邮箱登录') || text.includes('下一步')); }); } function hasLoginModalDom() { const modalSelectors = [ '.LoginModal_LoginModal__O59AS', '[class*="LoginModal"]', '[class*="OverseasLogin_overseasLogin"]', '[class*="overseasLoginModal"]', '.fixed.top-0.left-0.w-screen.h-screen', ].join(','); const phone = document.querySelector('input#first-phone, input[placeholder*="请输入手机号码"]'); if (phone instanceof HTMLElement) { const style = getComputedStyle(phone); if (style.display !== 'none' && style.visibility !== 'hidden') return true; } return [...document.querySelectorAll(modalSelectors)] .some((node) => { if (!(node instanceof HTMLElement)) return false; const style = getComputedStyle(node); if (style.display === 'none' || style.visibility === 'hidden') return false; const text = normalizedText(node); return text.includes('欢迎来到') && text.includes('珠宝AI') && (text.includes('手机号登录') || text.includes('邮箱登录') || text.includes('下一步')); }); } function isLoggedIn() { return Boolean(findProfileAvatar() || findLoggedInIndicator() || hasHomeLoggedInSignal()); } function hasHomeLoggedInSignal() { if (getPageType() !== 'home') return false; const rootText = normalizedText(document.querySelector('#root')); if (!rootText) return false; return /Log Out|Logout|退出/.test(rootText) || (/\d{3,9}(?=旗舰版|专业版|标准版|基础版|会员)/.test(rootText) && /资产|创作|收藏|从这开始/.test(rootText)); } function findProfileAvatar() { const candidates = document.querySelectorAll( 'img[src*="avatar" i], img[src*="profile" i], img[src*="head" i]' ); return [...candidates].find((image) => { if (!isUsable(image)) return false; const rect = image.getBoundingClientRect(); const src = image.currentSrc || image.src || ''; const style = getComputedStyle(image); const avatarSource = /avatar|profile|head|default_profile_picture/i.test(src); const headerAvatarShape = rect.top < 120 && rect.right > window.innerWidth * 0.66 && rect.width >= 24 && rect.width <= 72 && rect.height >= 24 && rect.height <= 72 && style.cursor === 'pointer'; return avatarSource && headerAvatarShape; }); } function findLoggedInIndicator() { const candidates = document.querySelectorAll([ '[class*="VipCenterButton"]', '[class*="VipLevelButton"]', '[class*="UserInfo"]', '[class*="Avatar"]', '[class*="Profile"]', 'button', 'a', '[role="button"]', 'div', 'span', ].join(',')); return [...candidates] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) .find((node) => { const text = normalizedText(node); const className = String(node.className || ''); const looksLikeVip = /VipCenterButton|VipLevelButton|UserInfo|Avatar|Profile/i.test(className) && /升级|会员|旗舰版|专业版|标准版|基础版|高级版|企业版|个人|账户|我的|VIP/i.test(text || className); const visibleHeaderVip = isUsable(node) && node.getBoundingClientRect().top < 120 && /^(升级|会员中心|旗舰版|专业版|标准版|基础版|高级版|企业版|个人中心|我的|VIP)$/i.test(text); return looksLikeVip || visibleHeaderVip; }) || null; } function findAuthTextControl(pattern, scope = document, ancestorSelector = 'button, a, [role="button"], div') { return [...scope.querySelectorAll( 'button, a, [role="button"], [role="menuitem"], li, div, span' )] .filter((node) => !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) .map((node) => { const text = normalizedText(node); if (text.length > 20 || !pattern.test(text) || !isUsable(node)) return null; return node.closest(ancestorSelector) || findClickableAncestor(node); }) .find(Boolean); } function findLoginButton() { return findAuthTextControl(/^(Log In|Login|Sign In|登录|登录\/注册|注册\/登录)$/i) || findAuthTextControl(/Log In|Login|Sign In|登录/i); } function findLogoutButton(scope = document) { const resolved = findAuthTextControl( /^(退出登录|退出账号|退出|Log Out|Logout|Sign Out)$/i, scope, '[class*="MobileActionBar_LoginButton"], button, a, [role="button"], div, span' ) || findAuthTextControl( /退出登录|退出账号|退出|Log Out|Logout|Sign Out/i, scope, '[class*="MobileActionBar_LoginButton"], button, a, [role="button"], div, span' ); if (resolved) return resolved; // span 标签需要向上找可点击父级 const span = [...(scope || document).querySelectorAll('span')] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) .find((node) => { const text = normalizedText(node); return text.length <= 20 && /^(退出登录|退出账号|退出|Log Out|Logout|Sign Out)$/i.test(text) && isUsable(node); }); return span instanceof HTMLElement ? (span.closest('button, a, [role="button"], div, [class*="MobileActionBar_LoginButton"]') || span) : null; } function findClickableAncestor(node) { return node?.closest?.('button, a, [role="button"], div') || node; } function findComposePanel() { const anchors = [ ...document.querySelectorAll('textarea'), ...document.querySelectorAll('input[type="file"]'), ...[...document.querySelectorAll('button, label, [role="button"], div, span')] .filter((node) => /上传|立即创作|开始创作|立即生成/.test(normalizedText(node))), ]; for (const anchor of anchors) { const panel = walkParents(anchor, isComposePanel); if (panel) return panel; } const editorControl = document.querySelector('.picture-editor-content-control'); if (editorControl instanceof HTMLElement && isUsable(editorControl)) return editorControl; return null; } function isComposePanel(node) { if (!(node instanceof HTMLElement)) return false; const text = normalizedText(node); const hasGeneratorConfig = text.includes('选择模型') || text.includes('选择模板') || text.includes('选个风格') || text.includes('生成比例') || text.includes('数量'); const hasCreate = text.includes('立即创作') || text.includes('开始创作') || text.includes('立即生成'); const hasPrompt = Boolean(findPromptInput(node)) || text.includes('在此输入') || text.includes('快捷提示词') || text.includes('提示词'); const hasUpload = text.includes('上传') || Boolean(node.querySelector?.('input[type="file"]')); const hasSizing = text.includes('生成比例') || text.includes('尺寸') || text.includes('张数') || text.includes('数量'); return hasGeneratorConfig && hasCreate && (hasUpload || hasPrompt || hasSizing); } function markFormatControls(panel) { if (!panel) return; const labels = [...panel.querySelectorAll('div, span, p, label')] .filter((node) => normalizedText(node) === '多格式'); labels.forEach((label) => { label.classList.add(CLASS.formatLabel); const row = walkParents(label, (node) => isFormatRow(node, panel)); if (!row) return; row.classList.add(CLASS.formatRow); markFormatChoices(row); }); } function markTemplateTextMode(panel) { document.querySelectorAll(`.${CLASS.templateTextMode}`).forEach((node) => node.classList.remove(CLASS.templateTextMode)); if (!panel || !isTemplateRoute()) return; [...panel.querySelectorAll('button, a, label, [role="button"], div, span')] .filter((node) => node instanceof HTMLElement && normalizedText(node) === '文生' && isUsable(node)) .map((node) => node.closest('button, a, label, [role="button"]') || node) .filter((node) => node instanceof HTMLElement && !node.querySelector('textarea, input, .ant-upload, .ant-upload-btn')) .forEach((node) => node.classList.add(CLASS.templateTextMode)); } function isFormatRow(node, panel) { if (!node || node === panel) return false; const text = normalizedText(node); return text.includes('多格式') && text.includes('JPEG') && text.includes('PNG') && text.length <= 80; } function markFormatChoices(row) { [...row.querySelectorAll('button, label, div, span, p')] .filter((node) => /^(JPEG|PNG)$/.test(normalizedText(node))) .forEach((node) => { node.classList.add(CLASS.formatChoice); const parent = node.parentElement; if (parent && parent !== row && normalizedText(parent).length <= 20) { parent.classList.add(CLASS.formatChoice); } }); } function markDuplicateTitles(panel) { if (!panel) return; panel.querySelectorAll(`.${CLASS.duplicateTitle}`).forEach((node) => node.classList.remove(CLASS.duplicateTitle)); const titles = [...panel.querySelectorAll('.control-config-title, [class*="title" i], h1, h2, h3')] .filter((node) => node instanceof HTMLElement && isUsable(node)) .filter(isIndependentTitleNode) .filter((node) => { const text = normalizedText(node); if (!text || text.length > 40) return false; return !/选择模型|选择模板|上传参考图|立即创作|开始创作/.test(text); }); titles.forEach((node) => { const text = normalizedText(node); if (/^(请上传图片|请上传首饰图|上传首饰图)$/.test(text) || /^请上传前/.test(text)) { node.classList.add(CLASS.duplicateTitle); } }); for (let i = 0; i < titles.length; i += 1) { for (let j = i + 1; j < titles.length; j += 1) { if (!rectsOverlap(titles[i].getBoundingClientRect(), titles[j].getBoundingClientRect())) continue; const firstText = normalizedText(titles[i]); const secondText = normalizedText(titles[j]); const firstUploadTitle = /^(请上传图片|请上传首饰图|上传首饰图)$/.test(firstText) || /^请上传前/.test(firstText); const secondUploadTitle = /^(请上传图片|请上传首饰图|上传首饰图)$/.test(secondText) || /^请上传前/.test(secondText); if (firstUploadTitle || secondUploadTitle) { (firstUploadTitle ? titles[i] : titles[j]).classList.add(CLASS.duplicateTitle); continue; } if (/Hi|你想创作|创作什么/.test(firstText) && /上传|图片/.test(secondText)) { titles[j].classList.add(CLASS.duplicateTitle); continue; } titles[j].classList.add(CLASS.duplicateTitle); } } } function markOverlappingTitles(panel) { markDuplicateTitles(panel); } function isIndependentTitleNode(node) { if (!(node instanceof HTMLElement)) return false; if (node.matches('button, a, label, textarea, input, [role="button"], [role="textbox"]')) return false; if (node.closest('button, a, label, [role="button"], .ant-upload, .ant-upload-btn')) return false; if (node.querySelector('textarea, input, button, a, label, [role="button"], .ant-upload, .ant-upload-btn')) return false; return true; } function rectsOverlap(a, b) { return a.width > 0 && a.height > 0 && b.width > 0 && b.height > 0 && a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top; } function findPromptInput(scope) { return scope?.querySelector( 'textarea[placeholder*="描述"], textarea[placeholder*="重绘"], textarea' ); } function findUploadControl(scope) { if (!scope) return null; return [...scope.querySelectorAll('input[type="file"]')].find(isImageUploadInput) || findUploadTrigger(scope) || [...scope.querySelectorAll('[class*="upload" i]')].find((node) => isUsable(node) && /上传参考图|上传图片|添加图片/.test(normalizedText(node)) ); } async function waitForComposePanel() { const waits = [0, 180, 420, 800, 1300, 2100]; let fallback = null; for (const wait of waits) { if (wait) await delay(wait); const panel = findComposePanel(); if (!panel) continue; if (isReadyComposePanel(panel)) return panel; if (!fallback) fallback = panel; } return fallback; } function isReadyComposePanel(panel) { return Boolean( findPromptInput(panel) || findGenerateButton(panel) || findModeToggle(panel, '图生') || findUploadTrigger(panel) || [...panel.querySelectorAll('input[type="file"]')].some(isImageUploadInput) ); } async function waitForImageUploadControl(panel) { const waits = [160, 300, 520, 820, 1200, 1800]; for (const wait of waits) { await delay(wait); if (findUploadControl(findComposePanel() || panel)) return true; } return false; } function findModeToggle(scope, text) { if (!scope) return null; return [...scope.querySelectorAll('button, a, label, [role="button"], div, span')] .filter((node) => normalizedText(node) === text && isUsable(node)) .map((node) => node.closest('button, a, label, [role="button"]') || node) .sort((a, b) => elementArea(a) - elementArea(b))[0] || null; } function findUploadTrigger(scope) { const input = [...scope.querySelectorAll('input[type="file"]')].find(isImageUploadInput); if (input) { const uploadParent = findUploadParent(input); if (uploadParent) return uploadParent; } return [...scope.querySelectorAll([ 'button', 'a', 'label', '[role="button"]', '.ant-upload', '.ant-upload-btn', '.upload-custom-button', '[class*="CoverUpload" i]', 'div', 'span', ].join(','))] .filter((node) => { const text = normalizedText(node); return isUsable(node) && /上传参考图|上传图片|添加图片/.test(text); }) .map((node) => node.closest('button, a, label, [role="button"], .ant-upload, .ant-upload-btn') || node) .sort((a, b) => elementArea(a) - elementArea(b))[0] || null; } function findUploadParent(input) { return input.closest?.('label, .ant-upload, .ant-upload-btn, button, [role="button"], [class*="CoverUpload" i]') || walkParents(input.parentElement, (node) => { if (!(node instanceof HTMLElement)) return false; if (node.matches?.('label, .ant-upload, .ant-upload-btn, button, [role="button"], [class*="CoverUpload" i]')) return true; const text = normalizedText(node); return text.includes('上传参考图') && elementArea(node) > 1; }); } function activateUpload(node) { if (!node) return false; let target = node; const fileInput = node instanceof HTMLInputElement && node.type === 'file' ? node : null; if (fileInput) { target = findUploadParent(node) || node; } debugLog('activateUpload', describeElement(target)); try { target.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'auto' }); } catch (_) { // iOS/Safari can reject object-style scroll options in some wrappers. } target.focus?.({ preventScroll: true }); target.click?.(); const nestedFileInput = target instanceof HTMLElement && !fileInput ? [...target.querySelectorAll('input[type="file"]')].find(isImageUploadInput) : null; nestedFileInput?.click?.(); if (fileInput && target !== fileInput) { debugLog('activateUpload:fileInputFallback', describeElement(fileInput)); fileInput.click?.(); } return true; } function armNativeUploadProxy(node, triggerButton) { if (!(node instanceof HTMLElement)) return false; const target = node instanceof HTMLInputElement && node.type === 'file' ? findUploadParent(node) || node : node; if (!(target instanceof HTMLElement)) return false; const button = triggerButton instanceof HTMLElement ? triggerButton : document.querySelector(`#${ROOT_ID} [data-zbai-action="upload"]`); const rect = button?.getBoundingClientRect?.(); if (!rect || rect.width <= 0 || rect.height <= 0) return false; disarmNativeUploadProxy(); target.classList.add(CLASS.uploadArmed); target.dataset.zbaiUploadArmed = '1'; target.style.setProperty('--zbai-upload-left', `${Math.round(rect.left)}px`); target.style.setProperty('--zbai-upload-top', `${Math.round(rect.top)}px`); target.style.setProperty('--zbai-upload-width', `${Math.round(rect.width)}px`); target.style.setProperty('--zbai-upload-height', `${Math.round(rect.height)}px`); target.addEventListener('click', scheduleUploadProxyDisarm, { capture: true, once: true }); debugLog('uploadProxy:armed', { target: describeElement(target), trigger: describeElement(button) }); return true; } function scheduleUploadProxyDisarm() { window.setTimeout(disarmNativeUploadProxy, 1200); } function disarmNativeUploadProxy() { document.querySelectorAll(`.${CLASS.uploadArmed}`).forEach((node) => { node.classList.remove(CLASS.uploadArmed); node.removeAttribute('data-zbai-upload-armed'); node.style.removeProperty('--zbai-upload-left'); node.style.removeProperty('--zbai-upload-top'); node.style.removeProperty('--zbai-upload-width'); node.style.removeProperty('--zbai-upload-height'); node.removeEventListener('click', scheduleUploadProxyDisarm, { capture: true }); }); } function isImageUploadInput(node) { if (!(node instanceof HTMLInputElement) || node.type !== 'file') return false; const accept = node.accept || ''; if (!accept) return true; return /image\/(jpeg|jpg|png|bmp|webp|heic)|\.(jpe?g|png|bmp|webp|heic)/i.test(accept); } function elementArea(node) { const rect = node.getBoundingClientRect(); return Math.max(1, Math.round(rect.width * rect.height)); } // ========== 立即创作按钮识别 ========== // 多阶段候选收集和得分排序 // 优先级:标准容器 > 精确文本匹配 > 广泛搜索 // 支持更多页面布局变体,提高识别准确度 function findGenerateButton(scope = document) { const root = scope === document ? (findComposePanel() || document) : (scope || document); const seen = new Set(); return collectGenerateCandidates(root) .map((node) => getGenerateClickTarget(node)) .filter((node) => { if (!(node instanceof HTMLElement) || seen.has(node)) return false; seen.add(node); return isRealGenerateButton(node); }) .map((node) => ({ node, score: scoreGenerateButton(node) })) .sort((a, b) => b.score - a.score)[0]?.node || null; } function isRealGenerateButton(node) { if (!(node instanceof HTMLElement) || node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) return false; const text = normalizedText(node); if (!GENERATE_TEXT_RE.test(text)) return false; if (hasPaymentActionText(text)) return false; if (!isGenerateClickable(node)) return false; if (!hasComposeConfigContext(node)) return false; if (auditGenerateTarget(node).insidePaymentArea) return false; return isUsable(node); } function hasPaymentOrMembershipAncestor(node) { let parent = node.parentElement; while (parent && parent !== document.body && parent.id !== 'root' && parent.id !== 'haimeta') { if (isGenerateScopeBoundary(parent)) return false; if (isPaymentOrMembershipNode(parent)) return true; parent = parent.parentElement; } return false; } function isGenerateScopeBoundary(node) { if (!(node instanceof HTMLElement)) return false; return node.matches?.([ '.generate-button-container', '.ControlBottom_controlBottom__gooOU', '.control-buttons', '.control-config', '.PictureEditorControl_PictureEditorControl__T6FHN', '.picture-editor-content-control', '.picture-editor-content-box', '.picture-editor-content', '[class*="PictureEditor_pictureEditor"]', ].join(',')); } function isInsidePaymentOrMembershipArea(node) { if (!(node instanceof HTMLElement)) return false; let parent = node.parentElement; while (parent && parent !== document.body && parent.id !== 'root' && parent.id !== 'haimeta') { if (isGenerateScopeBoundary(parent)) return false; if (isPaymentOrMembershipContainer(parent)) return true; parent = parent.parentElement; } return false; } // ========== 支付和会员检测 ========== // 检测支付/会员相关的按钮和容器 // 排除"高阶模型"等非支付文本,防止误将模型名称判为支付 // 多维度检测:直接支付文本 + 价格 + 支付上下文 + 样式类名 function isPaymentOrMembershipNode(node) { const text = normalizedText(node); const className = String(node?.className || ''); if (!text && !className) return false; const compact = normalizePaymentText(text.slice(0, 260)); // 排除"高阶模型"、"商业模型"等模型名称,不将其误认为支付按钮 const isModelName = /高阶模型|商业模型|商业级模型|会员模型|会员专属模型|旗舰版模型/.test(compact); if (isModelName) return false; const hasGenerateAction = GENERATE_TEXT_RE.test(compact); const hasPaymentAction = hasPaymentActionText(compact); if (hasGenerateAction && !hasPaymentAction && isGenerateScopeBoundary(node)) return false; const directPaymentAction = DIRECT_PAYMENT_ACTION_RE.test(compact) || (/^(立即|马上|去|前往)?(开通会员|升级会员|购买套餐|会员升级)$/i.test(compact)); const hasPrice = /¥|¥|\d+\s*元|\/年|\/月|每年|每月/.test(compact); const hasPaymentContext = hasPaymentContextText(compact); const classLooksPaid = PAYMENT_CLASS_RE.test(className); const smallActionButton = node.matches?.('button, a, [role="button"], [role="menuitem"]') && compact.length <= 48 && hasPaymentAction; return directPaymentAction || smallActionButton || (hasPaymentAction && (hasPrice || (hasPaymentContext && looksLikePaymentShell(node)))) || (classLooksPaid && hasPaymentAction); } function getGenerateClickTarget(node) { if (!(node instanceof HTMLElement)) return null; if (node.matches('.generate-button-container, .ControlBottom_controlBottom__gooOU, .control-buttons')) { return node.querySelector('button, [role="button"]') || node; } const button = node.closest('button, [role="button"]'); if (button) return button; return node.closest('.generate-button-container, .ControlBottom_controlBottom__gooOU, .control-buttons') || node; } function isGenerateClickable(node) { if (!(node instanceof HTMLElement)) return false; if (node.matches('button, [role="button"]')) return true; if (node.closest('button, [role="button"]')) return true; if (node.matches('.generate-button-container, .ControlBottom_controlBottom__gooOU')) return true; const style = getComputedStyle(node); return style.cursor === 'pointer' && elementArea(node) >= 900; } function hasComposeConfigContext(node) { if (!(node instanceof HTMLElement)) return false; const controlPanel = node.closest?.([ '.PictureEditorControl_PictureEditorControl__T6FHN', '.picture-editor-content-control', '.control-config', ].join(',')); if (controlPanel instanceof HTMLElement && (isComposePanel(controlPanel) || hasComposeConfigSignal(controlPanel))) { return true; } const context = walkParents(node, (parent) => { if (!(parent instanceof HTMLElement)) return false; return hasComposeConfigSignal(parent) || isComposePanel(parent); }); return Boolean(context); } function hasComposeConfigSignal(node) { if (!(node instanceof HTMLElement)) return false; const text = normalizedText(node); return Boolean(node.querySelector?.('textarea, input[type="file"], .ant-upload, .ant-upload-btn')) || /选择模板|选择模型|选择尺寸|尺寸|张数|上传参考图|上传图片|添加图片|提示词|描述|参考图|多格式/.test(text); } function scoreGenerateButton(node) { const text = normalizedText(node); const rect = node.getBoundingClientRect(); const className = String(node.className || ''); let score = 0; // 提高基础文本匹配的得分权重 if (/^(立即创作|开始创作|立即生成)(\s*\d+)?$/.test(text)) score += 160; // 提高 30 分 else if (/立即创作|开始创作|立即生成/.test(text)) score += 120; // 提高 24 分 else if (/^(创作|生成)(\s*\d+)?$/.test(text)) score += 60; // 提高 12 分 else score += 24; // 容器类型匹配得分 if (node.closest('.generate-button-container')) score += 80; // 提高 20 分 if (node.closest('.ControlBottom_controlBottom__gooOU, .control-buttons')) score += 50; // 提高 14 分 if (node.closest('[class*="ControlBottom"], [class*="control-button"]')) score += 40; if (/generate|ControlBottom|controlBottom|action/i.test(className)) score += 40; // 提高 12 分 // 按钮大小和位置 if (rect.width >= 120 && rect.height >= 40) score += 24; // 提高 8 分 score += Math.min(24, Math.round(elementArea(node) / 1500)); // 提高权重 score += Math.round(rect.top / Math.max(1, window.innerHeight)) * 6; // 提高权重 return score; } function collectGenerateCandidates(root) { // 扩展容器选择器,支持更多页面布局变体 const containers = [...root.querySelectorAll([ '.generate-button-container', '.ControlBottom_controlBottom__gooOU', '.control-buttons', '[class*="ControlBottom"]', '[class*="control-button"]', '[class*="action"]', ].join(','))].filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)); const precise = containers.flatMap((container) => [ ...container.querySelectorAll('button, [role="button"], div, span'), container, ]).filter((node) => { const text = normalizedText(node); if (!GENERATE_TEXT_RE.test(text)) return false; if (hasPaymentActionText(text)) return false; return true; }); if (precise.length) return precise; // 降级方案:在整个root中广泛搜索 return [...root.querySelectorAll('button, [role="button"], div, span')] .filter((node) => { const text = normalizedText(node); if (!PRIMARY_GENERATE_TEXT_RE.test(text)) return false; if (hasPaymentActionText(text)) return false; if (text.length > 40) return false; return !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`); }); } function isPaymentOrMembershipContainer(node) { if (!(node instanceof HTMLElement)) return false; if (isPaymentOrMembershipNode(node)) return true; const text = normalizePaymentText(normalizedText(node).slice(0, 1200)); const className = String(node.className || ''); const hasPaymentAction = hasPaymentActionText(text); if (PRIMARY_GENERATE_TEXT_RE.test(text) && isGenerateScopeBoundary(node) && !hasPaymentAction) return false; const classLooksPaid = PAYMENT_CLASS_RE.test(className); const hasPrice = /¥|¥|\d+\s*元|\/年|\/月|每年|每月/.test(text); const hasPaymentContext = hasPaymentContextText(text); return (classLooksPaid && hasPaymentAction) || (hasPaymentAction && hasPrice) || (hasPaymentAction && hasPaymentContext && looksLikePaymentShell(node)) || (hasPrice && hasPaymentContext && looksLikePaymentShell(node)); } function normalizePaymentText(text) { return String(text || '').replace(NON_PAYMENT_MODEL_TEXT_RE, ''); } function hasPaymentActionText(text) { return PAYMENT_ACTION_RE.test(normalizePaymentText(text)); } function hasPaymentContextText(text) { return PAYMENT_CONTEXT_RE.test(normalizePaymentText(text)); } function looksLikePaymentShell(node) { if (!(node instanceof HTMLElement)) return false; if (isGenerateScopeBoundary(node)) return false; const className = String(node.className || ''); if (PAYMENT_CLASS_RE.test(className)) return true; if (node.matches?.('.ant-modal, .ant-drawer, [class*="Modal"], [class*="modal"], [class*="Drawer"], [class*="drawer"]')) { return true; } const text = normalizePaymentText(normalizedText(node).slice(0, 800)); return /会员中心|选择套餐|购买套餐|开通会员|会员权益|支付方式|确认支付|收银台|订单/.test(text); } async function prepareGenerateTargetForClick(node) { if (!(node instanceof HTMLElement)) return; try { node.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'auto' }); } catch (_) { node.scrollIntoView?.(); } const panel = node.closest?.(`.${CLASS.compose}`) || findComposePanel(); if (panel instanceof HTMLElement) { keepGenerateButtonAboveMobileDock(node, panel); } await delay(60); } function prepareGenerateTargetForImmediateClick(node) { if (!(node instanceof HTMLElement)) return; try { node.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'auto' }); } catch (_) { node.scrollIntoView?.(); } const panel = node.closest?.(`.${CLASS.compose}`) || findComposePanel(); if (panel instanceof HTMLElement) { keepGenerateButtonAboveMobileDock(node, panel); } } function keepGenerateButtonAboveMobileDock(node, panel) { if (!(node instanceof HTMLElement) || !(panel instanceof HTMLElement) || !isMobile()) return; const rect = node.getBoundingClientRect(); const dock = document.querySelector(`#${ROOT_ID} .zbai-mobile-dock`); const dockRect = dock instanceof HTMLElement && isVisibleBox(dock) ? dock.getBoundingClientRect() : null; const safeBottom = dockRect ? Math.max(0, dockRect.top - 12) : window.innerHeight - 92; const safeTop = 72; if (rect.bottom > safeBottom) { panel.scrollTop += Math.ceil(rect.bottom - safeBottom); return; } if (rect.top < safeTop) { panel.scrollTop -= Math.ceil(safeTop - rect.top); } } function inspectClickTarget(node) { if (!(node instanceof HTMLElement)) return null; const rect = node.getBoundingClientRect(); const points = getClickProbePoints(rect); if (!points.length) { return { safe: false, reason: 'no-visible-point', target: describeElement(node), }; } let fallback = null; for (const point of points) { const hit = document.elementFromPoint(point.x, point.y); const hitElement = hit instanceof HTMLElement ? hit : hit?.parentElement; const acceptsTarget = hitElement instanceof HTMLElement && isAcceptedGenerateHit(node, hitElement); const adapterOverlay = hitElement instanceof HTMLElement && Boolean(hitElement.closest(`#${ROOT_ID}`)); const adapterOnlyHit = adapterOverlay && hitElement.closest(`#${ROOT_ID}`)?.id === ROOT_ID; const paymentHit = hitElement instanceof HTMLElement && ( isPaymentOrMembershipNode(hitElement) || hasPaymentOrMembershipAncestor(hitElement) || isInsidePaymentOrMembershipArea(hitElement) ); const safe = (acceptsTarget || adapterOnlyHit) && !paymentHit; const result = { safe, reason: safe ? (adapterOnlyHit && !acceptsTarget ? 'adapter-overlay-ok' : 'ok') : (paymentHit ? 'payment-hit' : (adapterOverlay ? 'adapter-overlay-hit' : 'obscured')), point, acceptsTarget, adapterOverlay, paymentHit, insidePaymentArea: hitElement instanceof HTMLElement && isInsidePaymentOrMembershipArea(hitElement), hit: describeElement(hitElement), }; if (safe) return result; if (!fallback || paymentHit) fallback = result; } return fallback; } function getClickProbePoints(rect) { if (!rect || rect.width <= 0 || rect.height <= 0) return []; const insetX = Math.min(18, Math.max(2, rect.width / 4)); const insetY = Math.min(18, Math.max(2, rect.height / 4)); const raw = [ [rect.left + rect.width / 2, rect.top + rect.height / 2], [rect.left + insetX, rect.top + rect.height / 2], [rect.right - insetX, rect.top + rect.height / 2], [rect.left + rect.width / 2, rect.top + insetY], [rect.left + rect.width / 2, rect.bottom - insetY], ]; return raw .map(([x, y]) => ({ x: Math.round(x), y: Math.round(y) })) .filter((point, index, list) => point.x >= 0 && point.x < window.innerWidth && point.y >= 0 && point.y < window.innerHeight && list.findIndex((item) => item.x === point.x && item.y === point.y) === index ); } function isAcceptedGenerateHit(target, hit) { if (!(target instanceof HTMLElement) || !(hit instanceof HTMLElement)) return false; if (target === hit || target.contains(hit)) return true; const generateShell = target.closest?.('.generate-button-container, .ControlBottom_controlBottom__gooOU, .control-buttons'); if (generateShell instanceof HTMLElement && (generateShell === hit || generateShell.contains(hit) || hit.contains(generateShell))) { return true; } return hit.contains(target) && isGenerateScopeBoundary(hit); } function auditGenerateTarget(node, options = {}) { const element = describeElement(node); const blockingPaymentOverlay = node instanceof HTMLElement ? findPaymentOrMembershipOverlay() : null; const directPaymentNode = node instanceof HTMLElement && isPaymentOrMembershipNode(node); const paymentAncestor = node instanceof HTMLElement && hasPaymentOrMembershipAncestor(node); const insidePaymentArea = node instanceof HTMLElement && ( directPaymentNode || paymentAncestor || isInsidePaymentOrMembershipArea(node) ); const click = node instanceof HTMLElement ? inspectClickTarget(node) : null; const usable = node instanceof HTMLElement && isUsable(node); const realGenerate = node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`) && GENERATE_TEXT_RE.test(normalizedText(node)) && !hasPaymentActionText(normalizedText(node)) && isGenerateClickable(node) && hasComposeConfigContext(node) && !insidePaymentArea && usable; const unsafeClick = options.requireClickSafety === true && (!click || !click.safe); return { element, disabled: isDisabledControl(node), usable, directPaymentNode, paymentAncestor, insidePaymentArea, click, blockingPaymentOverlay: describeElement(blockingPaymentOverlay), allowed: realGenerate && !blockingPaymentOverlay && !isDisabledControl(node) && !unsafeClick, }; } function describeGenerateCandidates(scope = document) { try { return collectGenerateCandidates(scope) .map((node) => getGenerateClickTarget(node)) .filter((node, index, list) => node instanceof HTMLElement && list.indexOf(node) === index) .slice(0, 12) .map((node) => ({ element: describeElement(node), score: scoreGenerateButton(node), audit: { disabled: isDisabledControl(node), paymentActionText: hasPaymentActionText(normalizedText(node)), directPaymentNode: isPaymentOrMembershipNode(node), paymentAncestor: hasPaymentOrMembershipAncestor(node), insidePaymentArea: isInsidePaymentOrMembershipArea(node), click: inspectClickTarget(node), composeContext: hasComposeConfigContext(node), real: isRealGenerateButton(node), }, })); } catch (error) { return [{ error: String(error?.message || error) }]; } } function isDisabledControl(node) { if (!(node instanceof HTMLElement)) return false; return Boolean( node.disabled || node.getAttribute('disabled') !== null || node.getAttribute('aria-disabled') === 'true' || /disabled|not-allowed/i.test(String(node.className || '')) ); } function findResultImages() { return [...document.images] .map((image) => ({ image, rect: image.getBoundingClientRect(), src: image.currentSrc || image.src || '' })) .filter(({ image, rect, src }) => { if (!isUsable(image)) return false; if (rect.width < 120 || rect.height < 120) return false; if (/logo|avatar|profile|placeholder|icon|ic_|input-bg|bg-/i.test(src)) return false; if (image.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, .picture-editor-header`)) return false; return true; }) .sort((a, b) => b.rect.width * b.rect.height - a.rect.width * a.rect.height) .map(({ image }) => image); } async function waitForNewResultAfterGenerate(beforeCount) { const waits = Array.from({ length: 40 }, (_, index) => index < 6 ? 800 : 1000); for (const wait of waits) { await delay(wait); const paymentOverlay = findPaymentOrMembershipOverlay(); if (paymentOverlay) { debugLog('generate:paymentAfterClick', { overlay: describeElement(paymentOverlay), text: normalizedText(paymentOverlay).slice(0, 500), }); toast('原站返回升级/支付弹窗,请检查会员权限'); return false; } const images = findResultImages(); if (images.length > beforeCount) { closeSurface(); revealResult(images[0]); return true; } const text = normalizedText(document.body); if (!/创作中|生成中|processing|creating/i.test(text) && images.length > 0) { closeSurface(); revealResult(images[0]); return true; } } return false; } function findPaymentOrMembershipOverlay() { const candidates = [...document.querySelectorAll([ '.ant-modal', '.ant-drawer', '.ant-popover', '[class*="VipModal"]', '[class*="vipModal"]', '[class*="Vip"]', '[class*="vip"]', '[class*="Modal"]', '[class*="modal"]', '[class*="Drawer"]', '[class*="drawer"]', '[class*="Popover"]', '[class*="popover"]', ].join(','))] .filter((node) => node instanceof HTMLElement && isVisibleBox(node) && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`) && isBlockingPaymentOverlay(node) ); return candidates .sort((a, b) => scorePaymentOverlay(b) - scorePaymentOverlay(a))[0] || null; } function shouldTryCloseBlockingPayment(audit) { if (!audit || audit.directPaymentNode || audit.paymentAncestor || audit.insidePaymentArea) return false; return Boolean(audit.blockingPaymentOverlay || audit.click?.paymentHit || audit.click?.insidePaymentArea); } async function closeBlockingPaymentOverlay(generateNode, audit) { const overlay = findPaymentOverlayAtPoint(audit?.click?.point) || findPaymentOverlayFromAudit(audit) || findPaymentOrMembershipOverlay(); if (!(overlay instanceof HTMLElement)) { return { closed: false, reason: 'no-payment-overlay' }; } if (generateNode instanceof HTMLElement && overlay.contains(generateNode)) { return { closed: false, reason: 'generate-inside-payment-overlay', overlay: describeElement(overlay) }; } const close = findPaymentOverlayCloseControl(overlay); if (!(close instanceof HTMLElement)) { return { closed: false, reason: 'no-close-control', overlay: describeElement(overlay) }; } activate(close); await delay(420); const stillBlocking = findPaymentOrMembershipOverlay(); return { closed: !(stillBlocking instanceof HTMLElement), reason: stillBlocking ? 'close-clicked-still-visible' : 'closed', overlay: describeElement(overlay), close: describeElement(close), remaining: describeElement(stillBlocking), }; } function findPaymentOverlayFromAudit(audit) { const hitPath = audit?.click?.hit?.domPath || ''; if (!hitPath) return null; const overlays = [...document.querySelectorAll([ '[class*="VipModal"]', '[class*="vipModal"]', '[class*="Vip"]', '[class*="vip"]', '[class*="Modal"]', '[class*="modal"]', '.ant-modal', '.ant-drawer', ].join(','))].filter((node) => node instanceof HTMLElement && isVisibleBox(node)); return overlays.find((node) => { const path = describeElement(node)?.domPath || ''; return path && hitPath.includes(path) && isBlockingPaymentOverlay(node); }) || null; } function findPaymentOverlayAtPoint(point) { if (!point || typeof point.x !== 'number' || typeof point.y !== 'number') return null; const hit = document.elementFromPoint(point.x, point.y); const hitElement = hit instanceof HTMLElement ? hit : hit?.parentElement; if (!(hitElement instanceof HTMLElement)) return null; const ancestors = []; for (let node = hitElement; node && node instanceof HTMLElement; node = node.parentElement) { if (node === document.body || node === document.documentElement) break; if (!node.closest?.(`#${ROOT_ID}, #${HOME_SHELL_ID}`) && isBlockingPaymentOverlay(node)) { ancestors.push(node); } } return ancestors .sort((a, b) => scorePaymentOverlay(b) - scorePaymentOverlay(a))[0] || null; } function isBlockingPaymentOverlay(node) { if (!(node instanceof HTMLElement)) return false; if (!isPaymentOrMembershipContainer(node)) return false; const rect = node.getBoundingClientRect(); const className = String(node.className || ''); const text = normalizePaymentText(normalizedText(node).slice(0, 1200)); const style = window.getComputedStyle?.(node); const modalClass = /modal|drawer|popover/i.test(className) || node.matches?.('.ant-modal, .ant-drawer, .ant-popover'); const largeSurface = rect.width >= Math.min(220, window.innerWidth * 0.55) && rect.height >= Math.min(180, window.innerHeight * 0.24); const fixedOrAbsolute = /fixed|absolute|sticky/i.test(style?.position || ''); const coversGenerateArea = rect.bottom > window.innerHeight * 0.45 && rect.right > window.innerWidth * 0.55 && rect.left < window.innerWidth * 0.45; // 顶部“升级”入口也是会员节点,但它不是弹层,不能拿它去找关闭按钮。 if (!largeSurface && !modalClass) return false; if (!modalClass && !fixedOrAbsolute && !coversGenerateArea) return false; return modalClass || (largeSurface && coversGenerateArea && (PAYMENT_CLASS_RE.test(className) || hasPaymentContextText(text) || hasPaymentActionText(text))); } function scorePaymentOverlay(node) { if (!(node instanceof HTMLElement)) return 0; const rect = node.getBoundingClientRect(); const className = String(node.className || ''); const text = normalizePaymentText(normalizedText(node).slice(0, 1200)); let score = 0; if (/vipmodal|modal|drawer|popover/i.test(className)) score += 80; if (node.matches?.('.ant-modal, .ant-drawer, .ant-popover')) score += 70; if (PAYMENT_CLASS_RE.test(className)) score += 25; if (hasPaymentActionText(text)) score += 20; if (hasPaymentContextText(text)) score += 15; score += Math.min(80, Math.round((rect.width * rect.height) / Math.max(1, window.innerWidth * window.innerHeight) * 80)); return score; } function findPaymentOverlayCloseControl(overlay) { if (!(overlay instanceof HTMLElement)) return null; const textClose = [...overlay.querySelectorAll('button, a, [role="button"], div, span')] .filter((node) => node instanceof HTMLElement && isVisibleBox(node)) .map((node) => node.closest('button, a, [role="button"]') || node) .find((node) => { const text = normalizedText(node); const ariaTitle = `${node.getAttribute?.('aria-label') || ''} ${node.getAttribute?.('title') || ''}`; const rect = node.getBoundingClientRect(); if (/^(关闭|取消|Close|×|X)$/i.test(text)) return true; if (text.length <= 12 && /关闭|close/i.test(ariaTitle)) return true; return rect.top < 120 && rect.right > window.innerWidth - 88 && rect.width <= 80 && rect.height <= 80 && !PAYMENT_ACTION_RE.test(text); }); if (textClose instanceof HTMLElement) return textClose; const iconClose = [...overlay.querySelectorAll('[class*="close" i], [class*="Close" i], svg, img')] .filter((node) => node instanceof HTMLElement && isVisibleBox(node)) .map((node) => node.closest('button, a, [role="button"]') || node) .find((node) => { const rect = node.getBoundingClientRect(); const text = normalizedText(node); return rect.top < 160 && rect.right > window.innerWidth - 120 && rect.width <= 96 && rect.height <= 96 && !PAYMENT_ACTION_RE.test(text); }); return iconClose instanceof HTMLElement ? iconClose : null; } function revealResult(node) { try { node?.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'smooth' }); } catch (_) { node?.scrollIntoView?.(); } } function focusPrompt(panel) { const prompt = findPromptInput(panel); if (!prompt) return; prompt.scrollIntoView({ block: 'center' }); prompt.focus({ preventScroll: true }); } function findHistoryRail() { if (getPageType() !== 'editor') return null; const knownRails = [...document.querySelectorAll([ '.picture-editor-content-left-drawer', '.PictureEditorHistoryDrawer_container__fn3Ud', '.TaskSide_record__JuEDU', '[class*="History" i]', '[class*="record" i]', '[class*="Record" i]', '[class*="task" i]', '[class*="Task" i]', '[class*="drawer" i]', '[class*="Drawer" i]', ].join(','))] .filter((node) => node instanceof HTMLElement) .filter((node) => !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, .ant-drawer-body, [class*="MobileDrawer"]`)); for (const rail of knownRails) { if (hasHistoryItems(rail) && looksLikeHistoryRail(rail)) return rail; } const images = [...document.images].filter((image) => { const rect = image.getBoundingClientRect(); return rect.left < 160 && rect.width > 24 && rect.width < 130 && rect.height > 24; }); for (const image of images) { const rail = walkParents(image, (node) => { const rect = node.getBoundingClientRect(); const imageCount = node.querySelectorAll?.('img').length || 0; return imageCount >= 2 && rect.height > 220 && rect.width > 44 && rect.width < 190; }); if (rail && hasHistoryItems(rail)) return rail; } return null; } function hasHistoryItems(rail) { if (!rail) return false; const mediaItems = [...rail.querySelectorAll?.('img, canvas, video') || []] .filter((node) => { if (!(node instanceof HTMLElement)) return false; if (node instanceof HTMLCanvasElement) return node.width > 20 && node.height > 20; if (node instanceof HTMLVideoElement) return Boolean(node.currentSrc || node.src || node.poster); const src = node.currentSrc || node.src || ''; return Boolean(src) && !/logo|avatar|profile|placeholder|icon|ic_|input-bg|bg-/i.test(src); }); if (mediaItems.length > 0) return true; const linkItems = [...rail.querySelectorAll?.('a[href], [data-href], [data-url], [style*="background"]') || []] .filter((node) => { const signature = [ node.getAttribute?.('href'), node.getAttribute?.('data-href'), node.getAttribute?.('data-url'), node.getAttribute?.('style'), ].join(' '); return /work-detail|image|img|oss|cos|cdn|\.jpe?g|\.png|\.webp|\.heic/i.test(signature); }); if (linkItems.length > 0) return true; const nodes = [...rail.querySelectorAll?.('div, button, a, li') || []]; return nodes.some((node) => { if (!(node instanceof HTMLElement)) return false; const style = getComputedStyle(node); const className = String(node.className || ''); const text = normalizedText(node); const hasBg = Boolean(style.backgroundImage && style.backgroundImage !== 'none'); const taskLike = /history|record|task|item|card|work/i.test(className) && (node.onclick || node.getAttribute('role') === 'button' || node.querySelector('img, canvas, video, a[href]')); return hasBg || taskLike || /已完成|生成|创作/.test(text) && elementArea(node) > 1200; }); } async function waitForHistoryRail() { let rail = findHistoryRail(); if (rail) { debugLog('history:found', describeHistoryRail(rail)); return rail; } const trigger = findHistoryTrigger(); if (trigger) { debugLog('history:wakeTrigger', describeElement(trigger)); activate(trigger); await delay(620); rail = findHistoryRail(); if (rail) { debugLog('history:foundAfterWake', describeHistoryRail(rail)); return rail; } } await delay(320); rail = findHistoryRail(); debugLog('history:final', describeHistoryRail(rail)); return rail; } function findHistoryTrigger() { // 扩展模式匹配,支持更多变体文本和标签属性 const pattern = /^(历史|记录|任务|生成记录|创作记录|作品|我的作品|History|Record|Tasks?|Generated|Creations)$/i; return [...document.querySelectorAll('button, a, [role="button"], div, span, i')] .filter((node) => !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, .ant-drawer-body, [class*="MobileDrawer"]`)) .map((node) => findClickableAncestor(node)) .filter((node, index, list) => node instanceof HTMLElement && list.indexOf(node) === index) .find((node) => { if (!isUsable(node)) return false; const text = normalizedText(node); const label = [node.getAttribute('aria-label'), node.getAttribute('title'), String(node.className || '')].join(' '); const dataAttr = node.getAttribute('data-testid') || ''; // 更宽松的匹配策略 return pattern.test(text) || /history|record|task|record|created/i.test(label) || /history|record/i.test(dataAttr); }) || null; } function looksLikeHistoryRail(rail) { if (!(rail instanceof HTMLElement)) return false; const className = String(rail.className || ''); // 增加更多可识别的历史面板类名 if (/picture-editor-content-left-drawer|PictureEditorHistoryDrawer|TaskSide_record|TaskSide|history-rail|record-rail/i.test(className)) return true; const rect = rail.getBoundingClientRect(); const leftLike = rect.left <= Math.max(180, window.innerWidth * 0.48) || rect.width === 0; return leftLike && /History|history|record|Record|task|Task|drawer|Drawer|rail|Rail/i.test(className); } function prepareHistoryRailForMobile(rail) { if (!(rail instanceof HTMLElement)) return; rail.classList.add(CLASS.history); // 改进隐藏父级处理,确保整个链路可见 let parent = rail.parentElement; const hiddenAncestors = []; while (parent && parent !== document.body && parent.id !== 'root' && parent.id !== 'haimeta') { const style = getComputedStyle(parent); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { parent.classList.add(CLASS.historyParent); hiddenAncestors.push(parent); // 不仅标记,还要改变样式使其可见 parent.style.setProperty('display', 'block', 'important'); parent.style.setProperty('visibility', 'visible', 'important'); parent.style.setProperty('opacity', '1', 'important'); } parent = parent.parentElement; } // 确保历史面板本身可见 if (rail.style.display === 'none') rail.style.setProperty('display', 'block', 'important'); if (rail.style.visibility === 'hidden') rail.style.setProperty('visibility', 'visible', 'important'); } function describeHistoryRail(rail) { if (!(rail instanceof HTMLElement)) return null; const rect = rail.getBoundingClientRect(); const parentHidden = getHiddenAncestor(rail); const mediaCount = rail.querySelectorAll?.('img, canvas, video').length || 0; const clickableCount = rail.querySelectorAll?.('button, a, [role="button"], [onclick], [data-href], [data-url]').length || 0; return { element: describeElement(rail), rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height), }, mediaCount, clickableCount, hasItems: hasHistoryItems(rail), hiddenAncestor: parentHidden ? describeElement(parentHidden) : null, scrollHeight: rail.scrollHeight || 0, clientHeight: rail.clientHeight || 0, }; } function getHiddenAncestor(node) { let parent = node?.parentElement; while (parent && parent !== document.body) { const style = getComputedStyle(parent); if (style.display === 'none' || style.visibility === 'hidden') return parent; parent = parent.parentElement; } return null; } function findDownloadControl() { const labeled = [ '[title*="下载"]', '[aria-label*="下载"]', '[download]', '[class*="download" i]', ].flatMap((selector) => [...document.querySelectorAll(selector)]); const byText = [...document.querySelectorAll('button, a, [role="button"], div, span')].filter((node) => normalizedText(node).includes('下载') ); const byRightPreviewIcon = [...document.querySelectorAll('button, a, [role="button"], i, span')] .filter(isLikelyPreviewDownload); return [...labeled, ...byText, ...byRightPreviewIcon] .map((node) => findClickableAncestor(node)) .find((node) => node instanceof HTMLElement && isUsable(node) && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`) && !/上传|上传参考图|上传图片/.test(normalizedText(node))); } function findPrimaryResult() { return [...document.images] .map((image) => ({ image, rect: image.getBoundingClientRect() })) .filter(({ image, rect }) => { const src = image.currentSrc || image.src || ''; return isUsable(image) && rect.width > 150 && rect.height > 150 && !src.includes('logo') && !src.includes('bg-') && !image.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, .picture-editor-header`); }) .sort((a, b) => b.rect.width * b.rect.height - a.rect.width * a.rect.height) .map(({ image }) => image)[0]; } function isLikelyPreviewDownload(node) { const rect = node.getBoundingClientRect(); if (!isUsable(node) || rect.left < window.innerWidth * 0.62 || rect.top > window.innerHeight * 0.66) { return false; } const signature = [ node.className, node.getAttribute('title'), node.getAttribute('aria-label'), node.innerHTML, ].join(' '); return /download|xiazai|icon-download||/i.test(signature) && !normalizedText(node).includes('上传'); } function hasVisiblePreviewModal() { return [...document.querySelectorAll([ '.ant-modal', '[class*="Preview"]', '[class*="preview"]', '[class*="Viewer"]', '[class*="viewer"]', '[class*="Modal"]', '[class*="modal"]', ].join(','))] .some((node) => { if (!(node instanceof HTMLElement) || node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) return false; if (!isUsable(node)) return false; const text = normalizedText(node); if (/欢迎来到|手机号登录|邮箱登录|请输入手机号码/.test(text)) return false; const rect = node.getBoundingClientRect(); const hasLargeImage = [...node.querySelectorAll('img, canvas, video')].some((item) => { const itemRect = item.getBoundingClientRect(); return itemRect.width > 120 && itemRect.height > 120; }); return hasLargeImage && (rect.width > window.innerWidth * 0.45 || rect.height > window.innerHeight * 0.45); }); } function findClickableByText(scope, text) { if (!scope) return null; return [...scope.querySelectorAll('button, a, label, [role="button"]')] .find((node) => normalizedText(node).includes(text) && isUsable(node)); } function walkParents(start, predicate) { let node = start; for (let depth = 0; node && depth < 18; depth += 1, node = node.parentElement) { if (predicate(node)) return node; } return null; } function normalizedText(node) { return (node?.innerText || node?.textContent || '').replace(/\s+/g, ' ').trim(); } function isUsable(node) { if (!node || node.closest?.(`#${ROOT_ID}`)) return false; return isVisibleBox(node); } function isVisibleBox(node) { if (!(node instanceof HTMLElement)) return false; const rect = node.getBoundingClientRect(); const style = getComputedStyle(node); return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none'; } function tryActivateGenerateImmediately(node, beforeCount) { if (!(node instanceof HTMLElement)) return false; prepareGenerateTargetForImmediateClick(node); const audit = auditGenerateTarget(node, { requireClickSafety: true }); setDebugState('__ZBAI_LAST_IMMEDIATE_GENERATE__', { beforeCount, allowed: audit.allowed, audit, time: Date.now(), }); debugLog('runGenerate:immediateTarget', { beforeCount, audit, candidates: describeGenerateCandidates(findComposePanel() || document), }); if (!audit.allowed) return false; return activateGenerateButton(node, audit); } function activateGenerateButton(node, audit) { if (!(node instanceof HTMLElement)) return false; const target = node.closest?.('button, a, label, [role="button"], [role="menuitem"]') || node; const finalAudit = audit?.element?.domPath === describeElement(node)?.domPath ? audit : auditGenerateTarget(node, { requireClickSafety: true }); if (!finalAudit.allowed) { debugLog('activateGenerate:blocked', finalAudit); return false; } debugLog('activateGenerate', { target: describeElement(target), audit: finalAudit, }); try { target.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'auto' }); } catch (_) { target.scrollIntoView?.(); } target.focus?.({ preventScroll: true }); generateActivationInProgress = true; try { target.click?.(); return true; } finally { window.setTimeout(() => { generateActivationInProgress = false; }, 240); } } function activate(node) { if (!node) return false; const target = node.closest?.('button, a, label, [role="button"], [role="menuitem"]') || node; debugLog('activate', describeElement(target)); try { target.scrollIntoView?.({ block: 'center', inline: 'center', behavior: 'auto' }); } catch (_) { // keep going; old mobile browsers can reject scroll options } target.focus?.({ preventScroll: true }); target.click?.(); return true; } function debugLog(label, data) { if (window.__ZBAI_DEBUG__ !== true) return; console.debug('[ZBAI mobile]', label, data); } function isPublicDebugEnabled() { try { return window.__ZBAI_DEBUG__ === true || localStorage.getItem('ZBAI_DEBUG') === '1'; } catch (_) { return window.__ZBAI_DEBUG__ === true; } } function exposeDiagnosticsIfDebugEnabled() { if (window.__ZBAI_DEBUG__ === true) { window.__ZBAI_MOBILE_ADAPTER_DIAGNOSE__ = collectDiagnostics; window.ZBAI_MOBILE_ADAPTER_DIAGNOSE = collectDiagnostics; return; } try { delete window.__ZBAI_MOBILE_ADAPTER_DIAGNOSE__; delete window.ZBAI_MOBILE_ADAPTER_DIAGNOSE; } catch (_) { window.__ZBAI_MOBILE_ADAPTER_DIAGNOSE__ = undefined; window.ZBAI_MOBILE_ADAPTER_DIAGNOSE = undefined; } } function setDebugState(key, value) { if (window.__ZBAI_DEBUG__ !== true) return; window[key] = value; } function collectDiagnostics() { const composePanel = findComposePanel(); const uploadControl = composePanel ? findUploadControl(composePanel) : null; const generateButton = findGenerateButton(composePanel || document); const historyRail = findHistoryRail(); const downloadButton = findDownloadControl(); const loginButton = findLoginButton(); const logoutButton = findLogoutButton(); const profileAvatar = findProfileAvatar(); const mobileMenuButton = findMobileMenuButton(); const paymentOverlay = findPaymentOrMembershipOverlay(); const paymentOverlayClose = paymentOverlay ? findPaymentOverlayCloseControl(paymentOverlay) : null; return { version: VERSION, href: window.location.href, pageType: document.documentElement.dataset.zbaiPage || getPageType(), mobile: isMobile(), viewport: describeViewportState(), activeSurface, desktopGenerateCompat: window.__ZBAI_DESKTOP_GENERATE_COMPAT__ || null, found: { composePanel: Boolean(composePanel), uploadControl: Boolean(uploadControl), generateButton: Boolean(generateButton), historyRail: Boolean(historyRail), downloadButton: Boolean(downloadButton), loginButton: Boolean(loginButton), logoutButton: Boolean(logoutButton), mobileMenuButton: Boolean(mobileMenuButton), profileAvatar: Boolean(profileAvatar), loggedIn: isLoggedIn(), paymentOverlay: Boolean(paymentOverlay), paymentOverlayClose: Boolean(paymentOverlayClose), }, elements: { composePanel: describeElement(composePanel), uploadControl: describeElement(uploadControl), generateButton: describeElement(generateButton), historyRail: describeElement(historyRail), downloadButton: describeElement(downloadButton), loginButton: describeElement(loginButton), logoutButton: describeElement(logoutButton), mobileMenuButton: describeElement(mobileMenuButton), profileAvatar: describeElement(profileAvatar), paymentOverlay: describeElement(paymentOverlay), paymentOverlayClose: describeElement(paymentOverlayClose), }, debug: { lastRootPress: window.__ZBAI_LAST_ROOT_PRESS__ || null, lastComposeAction: window.__ZBAI_LAST_COMPOSE_ACTION__ || null, lastRunGenerate: window.__ZBAI_LAST_RUN_GENERATE__ || null, lastImmediateGenerate: window.__ZBAI_LAST_IMMEDIATE_GENERATE__ || null, generateAudit: auditGenerateTarget(generateButton), generateClickAudit: auditGenerateTarget(generateButton, { requireClickSafety: true }), generateCandidates: describeGenerateCandidates(composePanel || document), historyRail: describeHistoryRail(historyRail), }, }; } function describeViewportState() { const viewport = document.querySelector('meta[name="viewport"]'); return { innerWidth: window.innerWidth, innerHeight: window.innerHeight, screenWidth: window.screen?.width || 0, screenHeight: window.screen?.height || 0, devicePixelRatio: window.devicePixelRatio || 1, content: viewport?.getAttribute('content') || '', desktopCompatDataset: document.documentElement.dataset.zbaiDesktopViewportCompat || '', shouldUseDesktopGenerateViewport: shouldUseDesktopGenerateViewport(), }; } function describeElement(node) { if (!(node instanceof HTMLElement)) return null; const rect = node.getBoundingClientRect(); const style = getComputedStyle(node); return { tag: node.tagName.toLowerCase(), id: node.id || '', className: String(node.className || '').slice(0, 120), text: normalizedText(node).slice(0, 80), innerText: (node.innerText || '').replace(/\s+/g, ' ').trim().slice(0, 160), ariaLabel: node.getAttribute('aria-label') || '', title: node.getAttribute('title') || '', domPath: getDomPath(node), disabled: isDisabledControl(node), pointerEvents: style.pointerEvents, rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height), }, }; } function getDomPath(node) { if (!(node instanceof HTMLElement)) return ''; const parts = []; let current = node; for (let depth = 0; current && current instanceof HTMLElement && depth < 8; depth += 1) { let part = current.tagName.toLowerCase(); if (current.id) part += `#${current.id}`; const className = String(current.className || '') .split(/\s+/) .filter(Boolean) .slice(0, 3) .map((name) => `.${name}`) .join(''); part += className; parts.unshift(part); current = current.parentElement; } return parts.join(' > '); } function ensureMobileHomeShell() { let shell = document.getElementById(HOME_SHELL_ID); if (!shell) { shell = document.createElement('main'); shell.id = HOME_SHELL_ID; shell.innerHTML = `
珠宝AI
AI JEWELRY STUDIO 把灵感变成珠宝大片 海报、商品图、视频与详情页,一站式生成。
开始创作 选择入口
模板功能 点击即用
`; shell.addEventListener('click', handleHomeShellClick); document.body.appendChild(shell); } updateMobileHomeAuth(shell); updateMobileHomeCredits(shell); syncMobileHomeLogo(shell); renderMobileHomeActions(shell); } function cleanupMobileHomeShell() { document.getElementById(HOME_SHELL_ID)?.remove(); } function ensureWorkDetailFallback() { if (!isWorkDetailRoute()) { cleanupWorkDetailFallback(); return; } const media = getWorkDetailMedia(); if (!media.url) { cleanupWorkDetailFallback(); return; } document.title = media.kind === 'video' ? '珠宝AI视频详情' : '珠宝AI作品详情'; let fallback = document.getElementById(WORK_DETAIL_FALLBACK_ID); if (fallback && !fallback.querySelector('.zbai-work-detail-actions')) { cleanupWorkDetailFallback(); fallback = null; } if (!fallback) { fallback = document.createElement('section'); fallback.id = WORK_DETAIL_FALLBACK_ID; fallback.innerHTML = `
作品详情
珠宝AI作品预览
`; fallback.querySelector('[data-zbai-work-back]')?.addEventListener('click', () => { if (window.history.length > 1) window.history.back(); else navigateTo(ASSETS_URL); }); fallback.querySelector('[data-zbai-work-assets]')?.addEventListener('click', () => { navigateTo(ASSETS_URL); }); fallback.querySelector('[data-zbai-work-download]')?.addEventListener('click', async () => { await downloadPreparedWorkDetail(fallback); }); fallback.querySelector('[data-zbai-work-recreate]')?.addEventListener('click', () => { openRecreateFromWork(fallback, fallback.dataset.sourceUrl || getWorkDetailImageUrl()); }); fallback.querySelector('[data-zbai-work-original]')?.addEventListener('click', () => { const url = getWorkDetailFallbackInputUrl(fallback); if (!url) { toast('这个详情页没有拿到上传原图'); return; } if (url) window.open(url, '_blank', 'noopener'); }); fallback.querySelector('[data-zbai-work-original-card]')?.addEventListener('click', () => { const url = getWorkDetailFallbackInputUrl(fallback); if (!url) { toast('这个详情页没有拿到上传原图'); return; } window.open(url, '_blank', 'noopener'); }); document.body.appendChild(fallback); } renderWorkDetailFallback(fallback, media); hydrateWorkDetailFallback(fallback, media); } function renderWorkDetailFallback(fallback, media) { if (!(fallback instanceof HTMLElement) || !media?.url) return; const image = fallback.querySelector('img'); const video = fallback.querySelector('video'); const liveInputUrl = findLiveWorkDetailReferenceUrl(media); const inputUrl = cleanWorkDetailInputCandidate(liveInputUrl || media.inputUrl || '', media.url); fallback.dataset.mediaKind = media.kind; fallback.dataset.inputUrl = inputUrl || ''; fallback.querySelector('.zbai-work-detail-bar strong').textContent = media.kind === 'video' ? '视频详情' : '作品详情'; fallback.querySelector('[data-zbai-work-original]').hidden = !inputUrl; fallback.querySelector('[data-zbai-work-type]').textContent = media.type ? `类型:${media.type}` : (media.kind === 'video' ? '视频作品' : '图像作品'); fallback.querySelector('[data-zbai-work-object]').textContent = media.objectId ? `ID:${media.objectId}` : ''; if (image && media.kind === 'image' && image.src !== media.url) image.src = media.url; if (video && media.kind === 'video' && video.src !== media.url) video.src = media.url; if (image && media.kind !== 'image') image.removeAttribute('src'); if (video && media.kind !== 'video') video.removeAttribute('src'); if (fallback.dataset.sourceUrl !== media.url) { if (fallback.dataset.downloadUrl) URL.revokeObjectURL(fallback.dataset.downloadUrl); fallback.dataset.sourceUrl = media.url; fallback.dataset.downloadKind = media.kind; delete fallback.dataset.downloadUrl; delete fallback.dataset.downloadName; prepareWorkDetailDownload(fallback, media.url); } const promptCard = fallback.querySelector('[data-zbai-work-prompt-card]'); const promptText = fallback.querySelector('[data-zbai-work-prompt]'); const prompt = cleanWorkDetailParam(media.prompt || ''); if (promptCard && promptText) { promptCard.hidden = !prompt; promptText.textContent = prompt; } const sourceCard = fallback.querySelector('[data-zbai-work-source-card]'); const sourceImage = fallback.querySelector('[data-zbai-work-source-img]'); if (sourceCard && sourceImage) { sourceCard.hidden = !inputUrl; if (inputUrl) { sourceImage.dataset.inputUrl = inputUrl; const previewUrl = getDisplayableWorkDetailImageUrl(inputUrl); sourceImage.dataset.displayUrl = previewUrl; sourceImage.dataset.previewIndex = '0'; sourceImage.onerror = () => retryWorkDetailSourceImage(fallback, media); if (sourceImage.src !== previewUrl) sourceImage.src = previewUrl; if (liveInputUrl && liveInputUrl !== media.inputUrl) { storeWorkDetailRecord({ objectId: media.objectId, url: media.url, inputUrl: liveInputUrl, prompt: media.prompt, tags: media.tags, type: media.type, }); } } else { sourceImage.removeAttribute('src'); delete sourceImage.dataset.inputUrl; sourceImage.onerror = null; } } const paramsCard = fallback.querySelector('[data-zbai-work-params-card]'); const tagsNode = fallback.querySelector('[data-zbai-work-tags]'); const tags = normalizeWorkDetailTags([...(media.tags || []), ...findLiveWorkDetailTags()]); if (paramsCard && tagsNode) { paramsCard.hidden = !tags.length; tagsNode.innerHTML = tags.map((tag) => `${escapeHtml(tag)}`).join(''); } } function getWorkDetailFallbackInputUrl(fallback) { if (!(fallback instanceof HTMLElement)) return ''; const sourceImage = fallback.querySelector('[data-zbai-work-source-img]'); return cleanWorkDetailInputCandidate(sourceImage?.dataset?.inputUrl || '', fallback.dataset.mediaUrl || fallback.dataset.sourceUrl || '') || cleanWorkDetailInputCandidate(readImageSource(sourceImage), fallback.dataset.mediaUrl || fallback.dataset.sourceUrl || '') || cleanWorkDetailInputCandidate(fallback.dataset.inputUrl || '', fallback.dataset.mediaUrl || fallback.dataset.sourceUrl || ''); } function retryWorkDetailSourceImage(fallback, media) { if (!(fallback instanceof HTMLElement)) return; const sourceImage = fallback.querySelector('[data-zbai-work-source-img]'); if (!(sourceImage instanceof HTMLImageElement)) return; const mediaUrl = media?.url || fallback.dataset.sourceUrl || ''; const inputUrl = cleanWorkDetailInputCandidate(sourceImage.dataset.inputUrl || fallback.dataset.inputUrl || '', mediaUrl); const candidates = getWorkDetailImagePreviewCandidates(inputUrl); const currentIndex = Math.max(0, Number(sourceImage.dataset.previewIndex || '0')); const next = candidates.find((url, index) => index > currentIndex && url); if (next) { sourceImage.dataset.previewIndex = String(candidates.indexOf(next)); sourceImage.dataset.displayUrl = next; sourceImage.src = next; return; } const retryUrl = cleanWorkDetailInputCandidate(findLiveWorkDetailReferenceUrl(media), mediaUrl) || cleanWorkDetailInputCandidate(findWorkDetailInputUrlInText(document.documentElement?.innerHTML || '', media), mediaUrl); if (!retryUrl || sameWorkDetailUrl(retryUrl, inputUrl)) return; fallback.dataset.inputUrl = retryUrl; sourceImage.dataset.inputUrl = retryUrl; sourceImage.dataset.previewIndex = '0'; const previewUrl = getDisplayableWorkDetailImageUrl(retryUrl); sourceImage.dataset.displayUrl = previewUrl; sourceImage.src = previewUrl; } function hydrateWorkDetailFallback(fallback, baseMedia) { if (!(fallback instanceof HTMLElement) || fallback.dataset.hydrating === '1') return; fallback.dataset.hydrating = '1'; window.setTimeout(async () => { try { const enriched = await enrichWorkDetailMedia(baseMedia); if (enriched && fallback.isConnected && fallback.dataset.sourceUrl === baseMedia.url) { renderWorkDetailFallback(fallback, enriched); } } finally { if (fallback.isConnected) fallback.dataset.hydrating = '0'; } }, 0); } function cleanupWorkDetailFallback() { const fallback = document.getElementById(WORK_DETAIL_FALLBACK_ID); if (fallback?.dataset.downloadUrl) URL.revokeObjectURL(fallback.dataset.downloadUrl); fallback?.remove(); } function installWorkDetailDataCapture() { installFetchWorkDetailCapture(); installXhrWorkDetailCapture(); } function installFetchWorkDetailCapture() { if (window.__ZBAI_WORK_DETAIL_FETCH_CAPTURED__ || typeof window.fetch !== 'function') return; window.__ZBAI_WORK_DETAIL_FETCH_CAPTURED__ = true; const originalFetch = window.fetch; window.fetch = async function(...args) { const response = await originalFetch.apply(this, args); try { const url = String(args[0]?.url || args[0] || ''); const contentType = response.headers?.get?.('content-type') || ''; if (shouldCaptureWorkDetailPayload(url, contentType)) { response.clone().text().then((text) => captureWorkDetailPayload(text, url)).catch(() => {}); } } catch (_) {} return response; }; } function installXhrWorkDetailCapture() { if (window.__ZBAI_WORK_DETAIL_XHR_CAPTURED__ || typeof XMLHttpRequest !== 'function') return; window.__ZBAI_WORK_DETAIL_XHR_CAPTURED__ = true; const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url, ...rest) { this.__zbaiWorkDetailUrl = String(url || ''); return originalOpen.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.send = function(...args) { this.addEventListener?.('load', () => { try { const contentType = this.getResponseHeader?.('content-type') || ''; if (shouldCaptureWorkDetailPayload(this.__zbaiWorkDetailUrl || '', contentType)) { captureWorkDetailPayload(this.responseText || '', this.__zbaiWorkDetailUrl || ''); } } catch (_) {} }); return originalSend.apply(this, args); }; } function shouldCaptureWorkDetailPayload(url, contentType) { return /json/i.test(contentType) || /work|asset|mine|homepage|detail|creation|task|record|history|list|effect|module/i.test(String(url || '')); } function captureWorkDetailPayload(text, sourceUrl = '') { if (!text || text.length > 4_000_000) return; if (!/objectId|workId|prompt|imageUrls|imageUrl|videoUrl|storage\.haistudio|cdn-cgi\/image/i.test(text)) return; let data = null; try { data = JSON.parse(text); } catch (_) { return; } const records = collectWorkDetailRecords(data, sourceUrl); records.forEach(storeWorkDetailRecord); } function collectWorkDetailRecords(value, sourceUrl = '') { const records = []; const seen = new WeakSet(); const visit = (node) => { if (!node || typeof node !== 'object') return; if (seen.has(node)) return; seen.add(node); if (!Array.isArray(node)) { const record = makeWorkDetailRecordFromObject(node, sourceUrl); if (record) records.push(record); } Object.values(node).forEach(visit); }; visit(value); return records; } function makeWorkDetailRecordFromObject(obj, sourceUrl = '') { if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return null; const objectId = cleanWorkDetailParam( obj.objectId || obj.workId || obj.work_id || obj.id || obj.taskId || obj.recordId || obj.resultId || '' ); const mediaUrl = findWorkDetailUrlInObject(obj, 'media'); const inputUrl = normalizeWorkDetailInputUrl(findWorkDetailUrlInObject(obj, 'input', mediaUrl), mediaUrl); const prompt = findWorkDetailPromptInObject(obj); const tags = findWorkDetailTagsInObject(obj); const type = cleanWorkDetailParam(obj.type || obj.bizType || obj.moduleType || obj.scene || ''); if (!objectId && !mediaUrl) return null; if (!mediaUrl && !inputUrl && !prompt && !tags.length) return null; return { objectId, url: mediaUrl, inputUrl, prompt, tags, type, source: sourceUrl, updatedAt: Date.now(), }; } function findWorkDetailUrlInObject(obj, mode, excludeUrl = '') { const preferredKeys = mode === 'input' ? /(?:^|\.)(?:(?:input|source|origin|original|upload|reference|ref)[^.]*?(?:url|image|img)|source_?urls?|input_?images?|reference_?images?|init_?image|origin_?image|original_?image|upload_?image)(?:$|\.|\[)/i : /(?:^|\.)(?:url|image_?url|img_?url|media_?url|cover_?url|result_?url|output_?url|video_?url|file_?url|oss_?url|cdn_?url|thumb_?url|preview_?url)(?:$|\.|\[)/i; const secondaryInputKeys = /(?:^|\.)(?:image_?urls?|images|imgs)(?:$|\.|\[)/i; const avoidedKeys = mode === 'input' ? /avatar|logo|icon|result|output|cover|thumb/i : /avatar|logo|icon|input|source|origin|original|upload|reference|ref/i; const candidates = []; const addCandidate = (value, keyPath, weight = 0) => { const url = normalizeWorkDetailUrl(value); if (!url) return; if (mode === 'input' && !isLikelyWorkDetailInputImageUrl(url)) return; let score = weight; if (preferredKeys.test(keyPath)) score += 80; if (mode === 'input' && secondaryInputKeys.test(keyPath)) score += 35; if (avoidedKeys.test(keyPath)) score -= 80; if (mode === 'input' && sameWorkDetailUrl(url, excludeUrl)) score -= 140; if (/storage\.haistudio|assets.*haistudio|cdn-cgi\/image/i.test(url)) score += 30; if (/\.(png|jpe?g|webp|gif|mp4)(?:\?|$)/i.test(url)) score += 20; candidates.push({ url, score }); }; const walk = (node, keyPath = '', depth = 0) => { if (depth > 3 || node == null) return; if (typeof node === 'string') { addCandidate(node, keyPath); return; } if (Array.isArray(node)) { node.slice(0, 8).forEach((item, index) => walk(item, `${keyPath}[${index}]`, depth + 1)); return; } if (typeof node === 'object') { Object.entries(node).forEach(([key, value]) => { const nextKey = keyPath ? `${keyPath}.${key}` : key; if (typeof value === 'string') addCandidate(value, nextKey); else if (/url|image|img|video|source|origin|upload|reference|result|output|cover|paramMap|params|config|options/i.test(nextKey)) walk(value, nextKey, depth + 1); }); } }; walk(obj); candidates.sort((a, b) => b.score - a.score); return candidates[0]?.score > 0 ? candidates[0].url : ''; } function normalizeWorkDetailUrl(value) { const text = cleanWorkDetailParam(value); if (!text) return ''; const match = text.match(/https?:\/\/[^\s"'<>]+/i); return match ? match[0].replace(/&/g, '&') : ''; } function findWorkDetailInputUrlInText(text, media = {}) { const source = String(text || '') .replace(/\\u002F/gi, '/') .replace(/\\\//g, '/') .replace(/&/g, '&'); if (!source || !workDetailTextMatches(source, media)) return ''; const mediaUrl = media?.url || ''; const urls = [...source.matchAll(/https?:\/\/[^\s"'<>\\]+/gi)] .map((match) => { const raw = match[0].replace(/[),\]}]+$/g, ''); const url = normalizeWorkDetailUrl(raw); return { url, index: match.index || 0 }; }) .filter((item) => item.url) .filter((item, index, list) => list.findIndex((other) => sameWorkDetailUrl(other.url, item.url)) === index); const scored = urls.map(({ url, index }) => { const context = source.slice(Math.max(0, index - 180), Math.min(source.length, index + url.length + 140)); let score = 0; if (!isLikelyWorkDetailInputImageUrl(url)) score -= 220; if (sameWorkDetailUrl(url, mediaUrl)) score -= 300; if (/storage\.haistudio|assets.*haistudio|cdn-cgi\/image/i.test(url)) score += 40; if (/\.(png|jpe?g|webp|gif|heic)(?:[?#]|$)/i.test(url)) score += 22; if (/input|source|origin|original|upload|reference|ref|imageUrls|sourceUrls|inputImages|referenceImages|initImage/i.test(context)) score += 90; if (/avatar|logo|icon|result|output|cover|thumb|generated|mediaUrl|videoUrl/i.test(context)) score -= 60; if (media?.objectId && context.includes(media.objectId)) score += 12; if (mediaUrl && source.includes(mediaUrl) && !sameWorkDetailUrl(url, mediaUrl)) score += 18; return { url, score }; }).sort((a, b) => b.score - a.score); return scored[0]?.score > 35 ? scored[0].url : ''; } function normalizeWorkDetailInputUrl(value, mediaUrl = '') { const inputUrl = normalizeWorkDetailUrl(value); if (!inputUrl || sameWorkDetailUrl(inputUrl, mediaUrl)) return ''; if (!isLikelyWorkDetailInputImageUrl(inputUrl)) return ''; return inputUrl; } function findWorkDetailPromptInObject(obj) { const keys = ['prompt', 'promptText', 'positivePrompt', 'textPrompt', 'content', 'description']; for (const key of keys) { const value = cleanWorkDetailParam(obj[key]); if (value && value.length > 1 && !/^https?:\/\//i.test(value)) return value; } return ''; } function findWorkDetailTagsInObject(obj) { const tags = []; const add = (value) => { const text = cleanWorkDetailParam(value); if (text && text.length <= 80 && !/^https?:\/\//i.test(text)) tags.push(text); }; ['moduleName', 'moduleTitle', 'toolName', 'templateName', 'sceneName', 'typeName', 'taskName'].forEach((key) => add(obj[key])); ['modelName', 'modelTitle', 'model', 'modelType'].forEach((key) => add(obj[key])); ['ratio', 'aspectRatio', 'scale'].forEach((key) => add(obj[key])); if (obj.width && obj.height) add(`${obj.width}*${obj.height}`); if (obj.size && typeof obj.size !== 'object') add(obj.size); const rawParams = obj.params || obj.param || obj.paramMap || obj.extra || obj.config || obj.options; if (rawParams && typeof rawParams === 'object') { ['moduleName', 'toolName', 'templateName', 'modelName', 'model', 'ratio', 'aspectRatio', 'size'].forEach((key) => add(rawParams[key])); add(formatWorkDetailImageSize(rawParams.image_size || rawParams.imageSize)); if (rawParams.width && rawParams.height) add(`${rawParams.width}*${rawParams.height}`); } return normalizeWorkDetailTags(tags); } function formatWorkDetailImageSize(value) { const text = cleanWorkDetailParam(value); const map = { square_hd: '1:1', landscape_4_3: '4:3', portrait_4_3: '3:4', landscape_16_9: '16:9', portrait_16_9: '9:16', }; return map[text] || text.replace(/^portrait_(\d+)_(\d+)$/i, '$1:$2').replace(/^landscape_(\d+)_(\d+)$/i, '$1:$2'); } function normalizeWorkDetailTags(tags) { return [...new Set((tags || []) .flatMap((tag) => String(tag || '').split(/\s{2,}|\n+/)) .map((tag) => normalizeWorkDetailTagLabel(tag.replace(/\s+/g, ' ').trim())) .filter(Boolean) .filter((tag) => !/^null|undefined$/i.test(tag)) )].slice(0, 5); } function normalizeWorkDetailTagLabel(value) { const text = cleanWorkDetailParam(value); if (!text) return ''; const compact = text.toLowerCase().replace(/\s+/g, ''); if (/nanobanana|nano\s*banana/i.test(text) || compact.includes('nanobanana')) return '高阶模型'; if (/gpt\s*image\s*2(?:\.0)?/i.test(text) && /effects|effects用|特效/i.test(text)) return '商业级模型'; if (/商业生图模型|商业模型/i.test(text)) return '商业级模型'; if (/高阶生图模型/i.test(text)) return '高阶模型'; return text; } function storeWorkDetailRecord(record) { const normalized = normalizeWorkDetailRecord(record); if (!normalized) return; const cache = readWorkDetailCache(); const keys = getWorkDetailRecordKeys(normalized); keys.forEach((key) => { const old = cache[key] || {}; cache[key] = mergeWorkDetailMedia(old, normalized); cache[key].updatedAt = Date.now(); }); writeWorkDetailCache(cache); } function normalizeWorkDetailRecord(record) { if (!record || typeof record !== 'object') return null; const mediaUrl = normalizeWorkDetailUrl(record.url || record.mediaUrl || record.imageUrl || ''); const inputUrl = normalizeWorkDetailInputUrl(record.inputUrl || record.sourceUrl || record.originUrl || '', mediaUrl); const normalized = { objectId: cleanWorkDetailParam(record.objectId || record.id || ''), url: mediaUrl, inputUrl, prompt: cleanWorkDetailParam(record.prompt || ''), tags: normalizeWorkDetailTags(record.tags || []), type: cleanWorkDetailParam(record.type || ''), updatedAt: Number(record.updatedAt || Date.now()), }; if (!normalized.objectId && !normalized.url) return null; return normalized; } function getWorkDetailRecordKeys(record) { return [ record.objectId ? `id:${record.objectId}` : '', record.url ? `url:${record.url}` : '', record.url ? `url:${stripImageTransformUrl(record.url)}` : '', ].filter(Boolean); } function readWorkDetailCache() { try { return JSON.parse(sessionStorage.getItem(WORK_DETAIL_CACHE_KEY) || '{}') || {}; } catch (_) { return {}; } } function writeWorkDetailCache(cache) { try { const entries = Object.entries(cache) .sort((a, b) => (b[1]?.updatedAt || 0) - (a[1]?.updatedAt || 0)) .slice(0, 160); sessionStorage.setItem(WORK_DETAIL_CACHE_KEY, JSON.stringify(Object.fromEntries(entries))); } catch (_) {} } function getCachedWorkDetailMedia(media) { const keys = getWorkDetailRecordKeys(media || {}); const cache = readWorkDetailCache(); let result = {}; keys.forEach((key) => { if (cache[key]) result = mergeWorkDetailMedia(result, cache[key]); }); // Some apps keep detail/list payloads in storage. Search them lazily for the current id/url. const storageRecord = findWorkDetailMediaInBrowserStorage(media); if (storageRecord) result = mergeWorkDetailMedia(result, storageRecord); const documentRecord = findWorkDetailMediaInCurrentDocument(media); if (documentRecord) result = mergeWorkDetailMedia(result, documentRecord); return Object.keys(result).length ? result : null; } function findWorkDetailMediaInBrowserStorage(media) { const needles = [media?.objectId, media?.url, media?.url ? stripImageTransformUrl(media.url) : ''].filter(Boolean); if (!needles.length) return null; let result = null; [sessionStorage, localStorage].forEach((storage) => { if (result) return; try { for (let index = 0; index < storage.length; index += 1) { const key = storage.key(index); const value = storage.getItem(key) || ''; if (!needles.some((needle) => value.includes(needle))) continue; try { const records = collectWorkDetailRecords(JSON.parse(value), `storage:${key}`); result = records.map(normalizeWorkDetailRecord) .filter(Boolean) .find((record) => workDetailRecordMatches(record, media)); if (result) break; } catch (_) {} if (!result) { const inputUrl = findWorkDetailInputUrlInText(value, media); if (inputUrl) { result = { objectId: media?.objectId || '', url: media?.url || '', inputUrl }; break; } } } } catch (_) {} }); return result; } function findWorkDetailMediaInCurrentDocument(media) { if (!media?.objectId && !media?.url) return null; const chunks = [ ...[...document.querySelectorAll('script[type="application/json"], script#__NEXT_DATA__, script')] .map((node) => node.textContent || '') .filter((text) => text && text.length < 3_000_000), document.body?.textContent || '', document.documentElement?.innerHTML || '', ].filter(Boolean); for (const text of chunks) { if (!workDetailTextMatches(text, media)) continue; try { const parsed = JSON.parse(text); const records = collectWorkDetailRecords(parsed, 'document') .map(normalizeWorkDetailRecord) .filter(Boolean); const matched = records.find((record) => workDetailRecordMatches(record, media) && record.inputUrl); if (matched) return matched; } catch (_) {} const inputUrl = findWorkDetailInputUrlInText(text, media); if (inputUrl) return { objectId: media.objectId || '', url: media.url || '', inputUrl }; } return null; } function findLiveWorkDetailReferenceUrl(media = {}) { const mediaUrl = media?.url || ''; const selectors = [ '.ai-editor-info-row.image-urls-row img', '.image-urls-row img', '[class*="image-urls-row"] img', 'img[alt*="上传原图"]', 'img[alt*="参考图"]', 'img[alt*="原图"]', ].join(','); const images = [...document.querySelectorAll(selectors)] .filter((node) => node instanceof HTMLImageElement) .filter((node) => !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, #${WORK_DETAIL_FALLBACK_ID}, #${WORK_PREVIEW_OVERLAY_ID}`)) .map((node) => { const src = readImageSource(node); const rect = node.getBoundingClientRect(); const signature = [src, node.alt, node.className, getDomPath(node)].join(' '); let score = 0; if (src && !sameWorkDetailUrl(src, mediaUrl)) score += 80; else score -= 300; if (node.closest('.image-urls-row, [class*="image-urls-row"]')) score += 180; if (/storage\.haistudio|cdn-cgi\/image|jew\/prod/i.test(src)) score += 70; if (/\.(png|jpe?g|webp|gif|heic)(?:[?#]|$)/i.test(src)) score += 24; if (rect.width > 0 && rect.height > 0 && rect.width <= 220 && rect.height <= 220) score += 20; if (/avatar|logo|icon|profile|result|output|cover|thumb/i.test(signature)) score -= 90; return { src, score }; }) .filter((item) => item.src) .sort((a, b) => b.score - a.score); return images[0]?.score > 60 ? images[0].src : ''; } function findLiveWorkDetailTags() { const row = document.querySelector([ '.ai-editor-info-row.other-params-row', '.other-params-row', '[class*="other-params-row"]', ].join(',')); if (!(row instanceof HTMLElement) || row.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, #${WORK_DETAIL_FALLBACK_ID}, #${WORK_PREVIEW_OVERLAY_ID}`)) { return []; } const childTexts = [...row.children].map(normalizedText).filter(Boolean); if (childTexts.length) return childTexts.slice(0, 5); const text = normalizedText(row); return text ? [text] : []; } function workDetailTextMatches(text, media) { const value = String(text || ''); return Boolean( (media?.objectId && value.includes(media.objectId)) || (media?.url && value.includes(media.url)) || (media?.url && value.includes(stripImageTransformUrl(media.url))) ); } function workDetailRecordMatches(record, media) { return Boolean( (media.objectId && record.objectId === media.objectId) || (media.url && sameWorkDetailUrl(record.url, media.url)) ); } async function enrichWorkDetailMedia(media) { let enriched = mergeWorkDetailMedia(media, getCachedWorkDetailMedia(media)); if (hasCompleteWorkDetailInfo(enriched)) return enriched; const fetched = await fetchRealWorkDetailInfo(enriched); if (fetched) enriched = mergeWorkDetailMedia(enriched, fetched); return enriched; } function hasCompleteWorkDetailInfo(media) { return Boolean(media?.inputUrl && media?.prompt && normalizeWorkDetailTags(media?.tags || []).length); } async function fetchRealWorkDetailInfo(media) { const objectId = cleanWorkDetailParam(media?.objectId || ''); const key = cleanWorkDetailParam(media?.type || ''); if (!objectId || !/^(ai_effects|module_tool|matting|elimination|agent)$/i.test(key)) return null; const endpoint = `https://jew-api.haistudio.ai/api/home/getWorksInfoByIdAndKey?id=${encodeURIComponent(objectId)}&key=${encodeURIComponent(key)}`; if (workDetailFetchPromises.has(endpoint)) return workDetailFetchPromises.get(endpoint); const promise = (async () => { const response = await fetch(endpoint, { mode: 'cors', credentials: 'omit' }); if (!response.ok) return null; const text = await response.text(); captureWorkDetailPayload(text, endpoint); const json = JSON.parse(text); const records = collectWorkDetailRecords(json, endpoint) .map(normalizeWorkDetailRecord) .filter(Boolean); return records.find((record) => workDetailRecordMatches(record, media)) || null; })().catch(() => null); workDetailFetchPromises.set(endpoint, promise); return promise; } function mergeWorkDetailMedia(base, extra) { const merged = { ...(base || {}) }; const source = extra || {}; ['objectId', 'url', 'prompt', 'type', 'kind'].forEach((key) => { if (!merged[key] && source[key]) merged[key] = source[key]; }); const sourceInputUrl = normalizeWorkDetailInputUrl(source.inputUrl, merged.url || source.url || ''); if ((!merged.inputUrl || sameWorkDetailUrl(merged.inputUrl, merged.url)) && sourceInputUrl) { merged.inputUrl = sourceInputUrl; } if (merged.inputUrl && sameWorkDetailUrl(merged.inputUrl, merged.url)) merged.inputUrl = ''; merged.tags = normalizeWorkDetailTags([...(merged.tags || []), ...(source.tags || [])]); return merged; } function stripImageTransformUrl(url) { const text = cleanWorkDetailParam(url); const match = text.match(/^(https?:\/\/[^/]+)\/cdn-cgi\/image\/[^/]+\/(.+)$/i); if (!match) return text; let sourcePath = match[2]; try { sourcePath = decodeURIComponent(sourcePath); } catch (_) { // Keep the raw path if it is not URI-encoded. } if (/^https?:\/\//i.test(sourcePath)) return sourcePath; return `${match[1]}/${sourcePath.replace(/^\/+/, '')}`; } function getDisplayableWorkDetailImageUrl(url) { return getWorkDetailImagePreviewCandidates(url)[0] || ''; } function getWorkDetailImagePreviewCandidates(url) { const text = cleanWorkDetailParam(url); if (!text) return []; const stripped = stripImageTransformUrl(text); const candidates = []; if (stripped && stripped !== text && /\.(png|jpe?g|webp|gif|heic)(?:[?#]|$)/i.test(stripped)) { candidates.push(stripped); } candidates.push(text); if (stripped && stripped !== text) candidates.push(stripped); return [...new Set(candidates.filter(Boolean))]; } function sameWorkDetailUrl(left, right) { const a = cleanWorkDetailParam(left).replace(/&/g, '&'); const b = cleanWorkDetailParam(right).replace(/&/g, '&'); if (!a || !b) return false; if (a === b) return true; return stripImageTransformUrl(a) === stripImageTransformUrl(b); } function ensureWorkPreviewOverlay() { if (!isMobile()) { cleanupWorkPreviewOverlay(); return; } const data = getOriginalWorkPreviewData(); if (!data?.mediaUrl) { cleanupWorkPreviewOverlay(); return; } storeWorkDetailRecord({ objectId: data.title, url: data.mediaUrl, inputUrl: data.inputUrl, prompt: data.prompt, tags: data.tags, type: getPageType(), }); let overlay = document.getElementById(WORK_PREVIEW_OVERLAY_ID); if (!overlay) { overlay = document.createElement('section'); overlay.id = WORK_PREVIEW_OVERLAY_ID; overlay.addEventListener('click', handleWorkPreviewOverlayClick); document.body.appendChild(overlay); } const signature = [ data.mediaUrl, data.inputUrl, data.prompt, data.tags.join('|'), data.title, ].join('::'); if (overlay.dataset.signature !== signature) { overlay.dataset.mediaUrl = data.mediaUrl; overlay.dataset.inputUrl = data.inputUrl || ''; overlay.dataset.signature = signature; const inputPreviewUrl = getDisplayableWorkDetailImageUrl(data.inputUrl); overlay.innerHTML = `
作品预览
`; hydrateWorkPreviewReferenceImage(overlay); } overlay.__zbaiSourceOverlay = data.container; document.body.classList.add('zbai-work-preview-adapted'); } function cleanupWorkPreviewOverlay() { document.body?.classList.remove('zbai-work-preview-adapted'); document.getElementById(WORK_PREVIEW_OVERLAY_ID)?.remove(); } function handleWorkPreviewOverlayClick(event) { const action = event.target.closest('[data-zbai-preview-action]')?.getAttribute('data-zbai-preview-action'); if (!action) return; event.preventDefault(); event.stopPropagation(); const overlay = document.getElementById(WORK_PREVIEW_OVERLAY_ID); if (action === 'download') { downloadImageUrl(overlay?.dataset.mediaUrl || ''); return; } if (action === 'recreate') { openRecreateFromWork(overlay?.__zbaiSourceOverlay || overlay || document, overlay?.dataset.mediaUrl || ''); return; } if (action === 'original') { const inputUrl = getWorkPreviewInputUrl(overlay); if (!inputUrl) { toast('没找到上传原图'); return; } window.open(inputUrl, '_blank', 'noopener'); return; } if (action === 'close') { const originalClose = findOriginalWorkPreviewClose(overlay?.__zbaiSourceOverlay || document); cleanupWorkPreviewOverlay(); if (originalClose) activate(originalClose); } } function openRecreateFromWork(scope = document, mediaUrl = '') { const sourceScope = scope instanceof HTMLElement ? scope : document; const native = findRecreateControl(sourceScope) || findRecreateControl(document); if (native) { toast('正在打开二次创作'); activate(native); return true; } try { if (mediaUrl) sessionStorage.setItem(PENDING_RECREATE_KEY, mediaUrl); } catch (_) {} showRouteLoading('正在打开图生图二创'); navigateTo('/create/module/image'); return false; } function findRecreateControl(scope = document) { const pattern = /^(再次创作|二次创作|图生图|继续创作|复用生成|编辑)$/i; const loosePattern = /再次创作|二次创作|图生图|继续创作|复用生成/i; const candidates = [...(scope || document).querySelectorAll?.('button, a, [role="button"], [onclick], div, span') || []] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, #${WORK_DETAIL_FALLBACK_ID}, #${WORK_PREVIEW_OVERLAY_ID}`)) .map((node) => { const text = normalizedText(node); if (!text || text.length > 48) return null; const target = node.closest('button, a, [role="button"], [onclick]') || findClickableAncestor(node); if (!(target instanceof HTMLElement)) return null; const score = (pattern.test(text) ? 100 : 0) + (loosePattern.test(text) ? 40 : 0); return score > 0 ? { target, score } : null; }) .filter(Boolean) .sort((a, b) => b.score - a.score); return candidates[0]?.target || null; } function consumePendingRecreateIfNeeded(compose) { if (!(compose instanceof HTMLElement) || getPageType() !== 'editor') return; let pending = ''; try { pending = sessionStorage.getItem(PENDING_RECREATE_KEY) || ''; if (pending) sessionStorage.removeItem(PENDING_RECREATE_KEY); } catch (_) { pending = ''; } if (!pending) return; const imageMode = findModeToggle(compose, '图生'); if (imageMode) activate(imageMode); toast('已进入图生图,请上传参考图后继续创作'); } function getWorkPreviewInputUrl(overlay) { if (!(overlay instanceof HTMLElement)) return ''; const reference = overlay.querySelector('.zbai-work-preview-reference, img[alt*="上传原图"], img[alt*="参考图"], img[alt*="原图"]'); return cleanWorkDetailInputCandidate(reference?.dataset?.inputUrl || '', overlay.dataset.mediaUrl || '') || cleanWorkDetailInputCandidate(readImageSource(reference), overlay.dataset.mediaUrl || '') || cleanWorkDetailInputCandidate(overlay.dataset.inputUrl || '', overlay.dataset.mediaUrl || ''); } function hydrateWorkPreviewReferenceImage(overlay) { if (!(overlay instanceof HTMLElement)) return; const reference = overlay.querySelector('.zbai-work-preview-reference'); if (!(reference instanceof HTMLImageElement)) return; const inputUrl = cleanWorkDetailInputCandidate(reference.dataset.inputUrl || overlay.dataset.inputUrl || '', overlay.dataset.mediaUrl || ''); if (!inputUrl) return; reference.dataset.inputUrl = inputUrl; reference.dataset.previewIndex = reference.dataset.previewIndex || '0'; reference.onerror = () => retryWorkPreviewReferenceImage(overlay); } function retryWorkPreviewReferenceImage(overlay) { if (!(overlay instanceof HTMLElement)) return; const reference = overlay.querySelector('.zbai-work-preview-reference'); if (!(reference instanceof HTMLImageElement)) return; const inputUrl = cleanWorkDetailInputCandidate(reference.dataset.inputUrl || overlay.dataset.inputUrl || '', overlay.dataset.mediaUrl || ''); if (!inputUrl) return; const candidates = getWorkDetailImagePreviewCandidates(inputUrl); const current = readImageSource(reference); const currentIndex = Math.max(0, Number(reference.dataset.previewIndex || '0')); const next = candidates.find((url, index) => index > currentIndex && url && url !== current); if (!next) return; reference.dataset.previewIndex = String(candidates.indexOf(next)); reference.dataset.displayUrl = next; reference.src = next; } function getOriginalWorkPreviewData() { const container = findOriginalWorkPreviewContainer(); if (!container) return null; const media = findOriginalWorkPreviewMedia(container); const mediaUrl = media ? (media.currentSrc || media.src || media.getAttribute('src') || '') : ''; if (!mediaUrl) return null; return { container, mediaUrl, inputUrl: cleanWorkDetailInputCandidate(findOriginalWorkPreviewReference(container, mediaUrl), mediaUrl), prompt: findOriginalWorkPreviewPrompt(container), tags: findOriginalWorkPreviewTags(container), title: findOriginalWorkPreviewTitle(container), }; } function findOriginalWorkPreviewContainer() { const selectors = [ '.Popup_popupContent__dage3', '[class*="Popup_popupContent"]', '.ActivityStack_activityStack__4nTTx', '[class*="ActivityStack_activityStack"]', '.popu-create-now-content', '#work-detail-info', '[class*="NewWorkInfoDetail"]', ].join(','); const candidates = [...document.querySelectorAll(selectors)] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}, #${WORK_DETAIL_FALLBACK_ID}, #${WORK_PREVIEW_OVERLAY_ID}`)); const scored = candidates.map((node) => { const text = normalizedText(node); const rect = node.getBoundingClientRect(); const hasMedia = Boolean(findOriginalWorkPreviewMedia(node)); let score = 0; if (/提示词|图片创作|视频创作|文件大小|猜你喜欢|编辑/.test(text)) score += 70; if (/work|detail|popup|activity/i.test(String(node.className || '') + node.id)) score += 40; if (hasMedia) score += 80; if (rect.width > 180 && rect.height > 180) score += 20; if (!isVisibleBox(node) && rect.height <= 0) score -= 80; return { node, score }; }).sort((a, b) => b.score - a.score); return scored[0]?.score > 90 ? scored[0].node : null; } function findOriginalWorkPreviewMedia(container) { if (!(container instanceof HTMLElement)) return null; const media = [...container.querySelectorAll('img, video')] .filter((node) => node instanceof HTMLElement && (node.currentSrc || node.src || node.getAttribute('src'))); const scored = media.map((node) => { const rect = node.getBoundingClientRect(); const src = node.currentSrc || node.src || node.getAttribute('src') || ''; const signature = [src, node.alt, node.className, getDomPath(node)].join(' '); const naturalWidth = node.naturalWidth || node.videoWidth || Number(node.getAttribute('width')) || 0; const naturalHeight = node.naturalHeight || node.videoHeight || Number(node.getAttribute('height')) || 0; let score = Math.max(rect.width * rect.height, naturalWidth * naturalHeight * 0.18); if (/avatar|head|user|logo|icon|svg/i.test(signature)) score -= 120000; if (node.closest('.introduction-area, .ai-editor-info, [class*="NewWorkInfoDetail"]')) score -= 40000; if (/storage\\.haistudio|assets.*haistudio|\\.png|\\.jpe?g|\\.webp|\\.mp4/i.test(src)) score += 5000; return { node, score }; }).sort((a, b) => b.score - a.score); return scored[0]?.score > 2000 ? scored[0].node : null; } function findOriginalWorkPreviewReference(container, mediaUrl) { const preferred = [...container.querySelectorAll('.zbai-work-preview-reference, .image-urls-row img, [class*="image-urls-row"] img')] .filter((node) => node instanceof HTMLImageElement && readImageSource(node)); if (preferred.length) { const first = preferred.find((node) => !sameWorkDetailUrl(readImageSource(node), mediaUrl)); return first ? readImageSource(first) : ''; } const refs = [...container.querySelectorAll('.introduction-area img, .ai-editor-info img, [class*="NewWorkInfoDetail"] img')] .filter((node) => node instanceof HTMLImageElement && readImageSource(node)); const scored = refs.map((node) => { const src = readImageSource(node); const rect = node.getBoundingClientRect(); let score = 0; if (src && !sameWorkDetailUrl(src, mediaUrl)) score += 30; else score -= 100; if (rect.width >= 40 && rect.width <= 180 && rect.height >= 40 && rect.height <= 180) score += 40; if (/avatar|head|user|logo|icon/i.test([src, node.alt, node.className].join(' '))) score -= 80; return { node, score }; }).sort((a, b) => b.score - a.score); return scored[0]?.score > 20 ? readImageSource(scored[0].node) : ''; } function readImageSource(node) { if (!(node instanceof HTMLImageElement)) return ''; return cleanWorkDetailParam(node.currentSrc || node.src || node.dataset?.inputUrl || node.getAttribute('src') || ''); } function findOriginalWorkPreviewPrompt(container) { const promptNode = container.querySelector('.prompt, [class*="prompt" i], .ai-editor-info-text'); const direct = normalizedText(promptNode); if (direct && !/^提示词$/.test(direct)) return direct.replace(/^提示词\s*/, '').trim(); const text = normalizedText(container); const match = text.match(/提示词\s*([\s\S]*?)(?:图片创作|视频创作|文件大小|创建|猜你喜欢|编辑|$)/); return (match?.[1] || '').trim(); } function findOriginalWorkPreviewTags(container) { const row = container.querySelector('.other-params-row, [class*="other-params-row"], .ai-editor-info-row'); const childTexts = row ? [...row.children].map(normalizedText).filter(Boolean) : []; if (childTexts.length > 1) return normalizeWorkDetailTags(childTexts).slice(0, 4); const raw = normalizedText(row) || normalizedText(container).match(/(图片创作|视频创作)[^文件创建猜你喜欢编辑]*/)?.[0] || ''; const matches = raw.match(/图片创作|视频创作|文生图|图生图|普通模型|高阶生图模型|商业模型|商业生图模型|\d+\s*:\s*\d+(?:\s*\([^)]*\))?|[124]K/g); return normalizeWorkDetailTags(matches || (raw ? [raw] : [])).slice(0, 4); } function findOriginalWorkPreviewTitle(container) { const text = normalizedText(container); const id = text.match(/\b\d{5,}\b/)?.[0]; return id || '作品详情'; } function findOriginalWorkPreviewClose(scope) { const root = scope instanceof HTMLElement ? scope : document; const candidates = [...root.querySelectorAll('button, [role="button"], [aria-label], [title], svg, i, span, div')] .map((node) => findClickableAncestor(node)) .filter((node, index, list) => node instanceof HTMLElement && list.indexOf(node) === index && isUsable(node)); return candidates.find((node) => { const text = normalizedText(node); const label = [node.getAttribute('aria-label'), node.getAttribute('title'), String(node.className || '')].join(' '); const rect = node.getBoundingClientRect(); return (/^(×|x|关闭)$/i.test(text) || /close|关闭/i.test(label)) && rect.top < window.innerHeight * 0.25; }) || null; } function getWorkDetailImageUrl() { return getWorkDetailMedia().url; } function getWorkDetailMedia() { try { const url = new URL(location.href); const videoUrl = cleanWorkDetailParam(url.searchParams.get('videoUrl') || url.searchParams.get('video') || ''); const imageUrl = cleanWorkDetailParam(url.searchParams.get('url') || url.searchParams.get('imageUrl') || url.searchParams.get('coverUrl') || ''); const rawInputUrl = cleanWorkDetailParam(url.searchParams.get('inputUrl') || url.searchParams.get('sourceUrl') || url.searchParams.get('sourceImageUrl') || url.searchParams.get('originUrl') || url.searchParams.get('originImageUrl') || url.searchParams.get('originalUrl') || url.searchParams.get('uploadUrl') || url.searchParams.get('uploadImageUrl') || url.searchParams.get('imageUrls')); const prompt = cleanWorkDetailParam(url.searchParams.get('prompt') || url.searchParams.get('promptText') || url.searchParams.get('text') || url.searchParams.get('description')); const tags = normalizeWorkDetailTags([ url.searchParams.get('moduleName') || url.searchParams.get('toolName') || '', url.searchParams.get('modelName') || url.searchParams.get('model') || '', url.searchParams.get('ratio') || url.searchParams.get('aspectRatio') || '', url.searchParams.get('size') || '', ]); const sourceUrl = videoUrl || imageUrl; const inputUrl = normalizeWorkDetailInputUrl(rawInputUrl, sourceUrl); const base = { url: sourceUrl, inputUrl, prompt, tags, kind: videoUrl ? 'video' : 'image', objectId: cleanWorkDetailParam(url.searchParams.get('objectId') || url.searchParams.get('id') || ''), type: cleanWorkDetailParam(url.searchParams.get('type') || ''), }; return mergeWorkDetailMedia(base, getCachedWorkDetailMedia(base)); } catch (_) { return { url: '', inputUrl: '', prompt: '', tags: [], kind: 'image', objectId: '', type: '' }; } } function cleanWorkDetailParam(value) { const text = String(value || '').trim(); if (!text || /^(null|undefined|false)$/i.test(text)) return ''; return text; } function cleanWorkDetailInputCandidate(value, mediaUrl = '') { const text = cleanWorkDetailParam(value).replace(/&/g, '&'); if (!text || sameWorkDetailUrl(text, mediaUrl)) return ''; if (!isLikelyWorkDetailInputImageUrl(text)) return ''; return text; } function isLikelyWorkDetailInputImageUrl(value) { const text = cleanWorkDetailParam(value).replace(/&/g, '&'); if (!/^https?:\/\//i.test(text)) return false; const stripped = stripImageTransformUrl(text); if (/^https?:\/\/(?:www\.)?zbai\.art\/[0-9a-f]{8}(?:[0-9a-f-]{8,})?(?:[?#]|$)/i.test(text)) return false; if (/^https?:\/\/(?:www\.)?zbai\.art\/_next\/image\b/i.test(text) && /(?:\?|&)url=https?%3A/i.test(text)) return true; return /storage\.haistudio|assets(?:-[a-z]+)?\.haistudio|cdn-cgi\/image/i.test(text) || /\.(png|jpe?g|webp|gif|heic)(?:[?#]|$)/i.test(text) || /\.(png|jpe?g|webp|gif|heic)(?:[?#]|$)/i.test(stripped); } async function downloadImageUrl(url) { if (!url) { toast('没找到可下载的图片'); return; } try { const response = await fetch(url, { mode: 'cors', credentials: 'omit' }); if (!response.ok) throw new Error(`download ${response.status}`); const blob = await response.blob(); const objectUrl = URL.createObjectURL(blob); triggerDownload(objectUrl, `zbai-${Date.now()}.${guessImageExtension(blob.type, url)}`); notifyImageDownloaded(); window.setTimeout(() => URL.revokeObjectURL(objectUrl), 4000); return; } catch (_) { triggerDownload(url, `zbai-${Date.now()}.jpg`); notifyImageDownloaded(); window.open(url, '_blank', 'noopener'); } } async function prepareWorkDetailDownload(fallback, url) { try { const response = await fetch(url, { mode: 'cors', credentials: 'omit' }); if (!response.ok) throw new Error(`download ${response.status}`); const blob = await response.blob(); if (fallback.dataset.sourceUrl !== url) return; const objectUrl = URL.createObjectURL(blob); if (fallback.dataset.downloadUrl) URL.revokeObjectURL(fallback.dataset.downloadUrl); fallback.dataset.downloadUrl = objectUrl; fallback.dataset.downloadName = `zbai-${Date.now()}.${guessImageExtension(blob.type, url)}`; } catch (_) { // The click handler will fall back to the original URL. } } async function downloadPreparedWorkDetail(fallback) { const preparedUrl = fallback?.dataset.downloadUrl; if (preparedUrl) { triggerDownload(preparedUrl, fallback.dataset.downloadName || `zbai-${Date.now()}.jpg`); notifyImageDownloaded(); return; } await downloadImageUrl(getWorkDetailImageUrl()); } function notifyImageDownloaded() { toast('图片已下载并保存在相册'); } async function copyWorkDetailSourceUrl(fallback) { const url = fallback?.dataset.sourceUrl || getWorkDetailMedia().url; if (!url) { toast('没找到作品链接'); return; } try { await navigator.clipboard?.writeText(url); toast('原文件链接已复制'); return; } catch (_) { const input = document.createElement('textarea'); input.value = url; input.setAttribute('readonly', 'readonly'); input.style.cssText = 'position:fixed;left:-9999px;top:-9999px;'; document.body.appendChild(input); input.select(); try { document.execCommand('copy'); toast('原文件链接已复制'); } catch (error) { toast('复制失败,可点原图打开后复制'); } input.remove(); } } function triggerDownload(url, filename) { const link = document.createElement('a'); link.href = url; link.download = filename; link.rel = 'noopener'; document.body.appendChild(link); link.click(); link.remove(); } function guessImageExtension(mimeType, url) { if (/png/i.test(mimeType)) return 'png'; if (/webp/i.test(mimeType)) return 'webp'; if (/gif/i.test(mimeType)) return 'gif'; const match = String(url || '').split('?')[0].match(/\.([a-z0-9]{3,5})$/i); return match?.[1] || 'jpg'; } function handleHomeShellClick(event) { const actionButton = event.target.closest('[data-home-action]'); if (actionButton) { event.preventDefault(); event.stopPropagation(); runHomeAction(actionButton.getAttribute('data-home-action')); return; } const toolButton = event.target.closest('[data-tool-key]'); if (toolButton) { event.preventDefault(); event.stopPropagation(); openHomeTool(toolButton.getAttribute('data-tool-key')); return; } const navButton = event.target.closest('[data-href]'); if (!navButton) return; event.preventDefault(); event.stopPropagation(); closeHomeMenu(); navigateTo(navButton.getAttribute('data-href')); } function updateMobileHomeAuth(shell) { const loggedIn = isLoggedIn(); const loginButton = shell.querySelector('[data-home-action="login"]'); const logoutButton = shell.querySelector('[data-home-action="logout"]'); if (loginButton) loginButton.hidden = loggedIn; if (logoutButton) logoutButton.hidden = !loggedIn; } function updateMobileHomeCredits(shell) { const pill = shell?.querySelector('[data-home-credits]'); const value = pill?.querySelector('strong'); if (!(pill instanceof HTMLElement) || !(value instanceof HTMLElement)) return; const credits = findAccountCredits() || readCachedAccountCredits(); pill.hidden = !credits; if (!credits) return; value.textContent = credits; cacheAccountCredits(credits); } function findAccountCredits() { const explicit = findExplicitAccountCredits(); if (explicit) return explicit; const rootText = normalizedText(document.querySelector('#root')); const rootMatch = rootText.match(/^(\d{3,9})(?=旗舰版|专业版|标准版|基础版|会员|从这开始|工具|资产|创作|收藏)/); if (rootMatch) return formatAccountCredits(rootMatch[1]); const candidates = [...document.querySelectorAll('header, [class*="header" i], [class*="User" i], [class*="Vip" i], [class*="Credit" i], [class*="Point" i], div, span')] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) .map((node) => normalizedText(node)) .filter((text) => text && text.length <= 80); for (const text of candidates) { const value = parseCreditsFromText(text); if (value) return value; } return ''; } function findExplicitAccountCredits() { const nodes = [...document.querySelectorAll('[class*="credit" i], [class*="point" i], [class*="coin" i], [class*="balance" i], [class*="integral" i], [class*="jifen" i], [data-credit], [data-points]')] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)); for (const node of nodes) { const attr = node.getAttribute('data-credit') || node.getAttribute('data-points') || ''; const fromAttr = parseCreditsFromText(attr); if (fromAttr) return fromAttr; const fromText = parseCreditsFromText(normalizedText(node)); if (fromText) return fromText; } return ''; } function parseCreditsFromText(text) { const compact = String(text || '').replace(/[,,\s]/g, ''); if (!compact) return ''; const labelled = compact.match(/(?:积分|点数|余额|算力|credits?|points?|tokens?)[::]?(\d{1,9})/i) || compact.match(/(\d{1,9})(?:积分|点数|余额|算力|credits?|points?|tokens?)/i); if (labelled) return formatAccountCredits(labelled[1]); const plain = compact.match(/^(\d{3,9})(?=旗舰版|专业版|标准版|基础版|会员|VIP|从这开始|工具|资产)/i); return plain ? formatAccountCredits(plain[1]) : ''; } function formatAccountCredits(value) { const digits = String(value || '').replace(/[^\d]/g, ''); if (!digits) return ''; return Number(digits).toLocaleString('zh-CN'); } function cacheAccountCredits(value) { try { localStorage.setItem(HOME_CREDITS_CACHE_KEY, JSON.stringify({ value, time: Date.now() })); } catch (_) {} } function readCachedAccountCredits() { try { const parsed = JSON.parse(localStorage.getItem(HOME_CREDITS_CACHE_KEY) || 'null'); if (!parsed?.value || Date.now() - Number(parsed.time || 0) > 7 * 24 * 60 * 60 * 1000) return ''; return String(parsed.value); } catch (_) { return ''; } } function syncMobileHomeLogo(shell) { const holder = shell?.querySelector('.zbai-mobile-brand-native'); if (!(holder instanceof HTMLElement) || holder.dataset.ready === '1') return; holder.textContent = ''; const logo = document.createElement('img'); logo.src = HOME_BRAND_LOGO_URL; logo.alt = '珠宝AI'; logo.loading = 'eager'; logo.decoding = 'async'; logo.className = 'zbai-mobile-official-logo'; logo.addEventListener('error', () => { holder.textContent = ''; delete holder.dataset.ready; shell.classList.remove('zbai-mobile-has-native-logo'); }, { once: true }); holder.appendChild(logo); holder.dataset.ready = '1'; holder.dataset.source = 'official'; shell.classList.add('zbai-mobile-has-native-logo'); } function findNativeBrandLogoImages() { const images = [...document.querySelectorAll('img')] .filter((image) => image instanceof HTMLImageElement && !image.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`) && (image.currentSrc || image.src)); const scoreLogo = (image) => { const src = image.currentSrc || image.src || ''; const signature = [ src, image.alt, image.title, image.className, image.id, image.closest?.('[class*="header" i], [class*="logo" i], header')?.className || '', ].join(' '); let score = 0; if (/logo|brand|zbai|zhubao|jew|haimeta|珠宝/i.test(signature)) score += 80; if (/header|top|nav/i.test(signature)) score += 20; const width = image.naturalWidth || Number(image.getAttribute('width')) || 0; const height = image.naturalHeight || Number(image.getAttribute('height')) || 0; if (width && height && width <= 380 && height <= 140) score += 20; if (/avatar|user|banner|poster|cover|card|template|waterfall|history|upload|result/i.test(signature)) score -= 80; return score; }; return images .map((image) => ({ image, score: scoreLogo(image) })) .filter((item) => item.score > 0) .sort((a, b) => b.score - a.score) .map((item) => item.image); } function renderMobileHomeActions(shell) { const toolGrid = shell.querySelector('.zbai-mobile-tool-grid'); const templateGrid = shell.querySelector('.zbai-mobile-template-grid'); if (toolGrid && toolGrid.dataset.ready !== '1') { toolGrid.innerHTML = HOME_TOOL_ACTIONS.map((item) => ` `).join(''); toolGrid.dataset.ready = '1'; } if (templateGrid && templateGrid.dataset.ready !== '1') { templateGrid.innerHTML = TEMPLATE_ACTIONS.map((item) => ` `).join(''); templateGrid.dataset.ready = '1'; } } function runHomeAction(action) { if (action === 'login') { openLogin(); return; } if (action === 'logout') { openLogout(); return; } if (action === 'assets' || action === 'history') { closeHomeMenu(); navigateTo(ASSETS_URL); return; } if (action === 'menu') { toggleHomeMenu(); return; } if (action === 'menu-close') { closeHomeMenu(); return; } if (action === 'menu-templates') { closeHomeMenu(); document.querySelector(`#${HOME_SHELL_ID} .zbai-mobile-template-grid`)?.scrollIntoView?.({ block: 'start', behavior: 'smooth' }); } } function openHomeTool(key) { const action = HOME_TOOL_ACTIONS.find((item) => item.key === key); closeHomeMenu(); if (action) navigateTo(action.href, `正在打开${action.label}`); } function toggleHomeMenu() { const shell = document.getElementById(HOME_SHELL_ID); if (!shell) return; shell.classList.toggle('zbai-mobile-home-menu-open'); } function closeHomeMenu() { document.getElementById(HOME_SHELL_ID)?.classList.remove('zbai-mobile-home-menu-open'); } function findOriginalMenuButton() { return findMobileMenuButton(); } function findMobileMenuButton() { const candidates = [...document.querySelectorAll('button, a, [role="button"], [aria-label], [title], i, span, div')] .filter((node) => node instanceof HTMLElement && !node.closest(`#${ROOT_ID}, #${HOME_SHELL_ID}`)) .map((node) => findClickableAncestor(node)) .filter((node, index, list) => node instanceof HTMLElement && list.indexOf(node) === index && isUsable(node)); const scored = candidates.map((node) => { const rect = node.getBoundingClientRect(); const text = normalizedText(node); const label = [ node.getAttribute('aria-label'), node.getAttribute('title'), String(node.className || ''), ].join(' '); let score = 0; if (/^(☰|三|菜单|Menu)$/i.test(text) || /菜单|menu|hamburger|ic_menu|icon-menu/i.test(label)) score += 90; if (/iconfont/i.test(label) && /menu|caidan|drawer|nav/i.test(label)) score += 40; if (rect.left >= 0 && rect.left < 92 && rect.top >= 0 && rect.top < 96) score += 36; if (rect.width >= 20 && rect.width <= 64 && rect.height >= 20 && rect.height <= 64) score += 18; if (/Log In|Log Out|登录|退出|升级|资产|历史/.test(text)) score -= 80; return { node, score }; }).filter((item) => item.score > 45) .sort((a, b) => b.score - a.score); return scored[0]?.node || null; } function findOpenMobileDrawer() { return [...document.querySelectorAll('.ant-drawer-body, [class*="MobileDrawer"]')] .find((node) => node instanceof HTMLElement && isUsable(node) && /Log Out|Logout|退出|升级|从这开始|图片创作|资产/.test(normalizedText(node))) || null; } function navigateTo(path) { if (!path) return; const target = new URL(path, window.location.origin).href; if (window.location.href === target) return; showRouteLoading(getRouteLoadingMessage(path)); try { sessionStorage.setItem(PENDING_NAV_KEY, JSON.stringify({ target, time: Date.now() })); } catch (_) {} window.location.assign(target); } function getRouteLoadingMessage(path) { const value = String(path || ''); const tool = HOME_TOOL_ACTIONS.find((item) => value.includes(item.href)); if (tool) return `正在打开${tool.label}`; const template = TEMPLATE_ACTIONS.find((item) => value.includes(item.href)); if (template) return `正在打开${template.label}`; if (value.includes(ASSETS_URL)) return '正在打开资产'; if (/create\/module\/image/.test(value)) return '正在打开图片创作'; if (/create\/module\/video/.test(value)) return '正在打开视频创作'; return '正在打开页面'; } function showRouteLoading(message = '正在打开页面') { const loader = document.querySelector(`#${ROOT_ID} .zbai-mobile-route-loading`); if (!(loader instanceof HTMLElement)) return; const text = loader.querySelector('strong'); if (text) text.textContent = message; loader.hidden = false; loader.classList.add('is-visible'); document.documentElement.dataset.zbaiRouteLoading = '1'; window.clearTimeout(routeLoadingTimer); routeLoadingTimer = window.setTimeout(hideRouteLoading, 5200); } function hideRouteLoading() { const loader = document.querySelector(`#${ROOT_ID} .zbai-mobile-route-loading`); if (loader instanceof HTMLElement) { loader.classList.remove('is-visible'); window.setTimeout(() => { loader.hidden = true; }, 220); } delete document.documentElement.dataset.zbaiRouteLoading; try { sessionStorage.removeItem(PENDING_NAV_KEY); } catch (_) {} } function scheduleRouteLoadingDismiss() { let pending = null; try { pending = JSON.parse(sessionStorage.getItem(PENDING_NAV_KEY) || 'null'); } catch (_) { pending = null; } if (pending && Date.now() - Number(pending.time || 0) < 6500) { showRouteLoading(getRouteLoadingMessage(pending.target || location.href)); } const ready = getPageType() === 'home' || document.getElementById(WORK_DETAIL_FALLBACK_ID) || findComposePanel() || document.querySelector('.profiler-homepage-list, #scrollContainer, [class*="PersonalHomepage"]'); if (!ready) return; window.clearTimeout(routeLoadingTimer); routeLoadingTimer = window.setTimeout(hideRouteLoading, 420); } function preloadHomeRoutes() { if (homeRoutesPreloaded) return; homeRoutesPreloaded = true; [...HOME_TOOL_ACTIONS, ...TEMPLATE_ACTIONS, { href: ASSETS_URL }].forEach((item) => { const href = new URL(item.href, window.location.origin).href; if ([...document.querySelectorAll('link[rel="prefetch"]')].some((link) => link.href === href)) return; const link = document.createElement('link'); link.rel = 'prefetch'; link.href = href; link.as = 'document'; document.head?.appendChild(link); }); } function escapeHtml(value) { return String(value).replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', }[char])); } function toast(message) { const box = document.querySelector(`#${ROOT_ID} .zbai-mobile-toast`); if (!box) return; box.textContent = message; box.classList.add('is-visible'); window.clearTimeout(toastTimer); toastTimer = window.setTimeout(() => box.classList.remove('is-visible'), 2600); } function ensureViewport() { if (!isMobile()) return; if (shouldUseDesktopGenerateViewport()) { applyDesktopGenerateViewport('ensureViewport'); return; } const viewport = getOrCreateViewportMeta(); if (!viewport) return; viewport.setAttribute('content', MOBILE_VIEWPORT_CONTENT); } function getPageType() { if (isWorkDetailRoute()) return 'work-detail'; if (location.pathname === ASSETS_URL) return 'assets'; if (document.querySelector(EDITOR_ROOT_SELECTOR)) return 'editor'; if (isCreateRoute()) return 'editor'; return 'home'; } function updatePageType() { document.documentElement.dataset.zbaiPage = getPageType(); } function isCreateRoute() { return location.pathname.startsWith('/create/') || location.pathname.startsWith('/effects/'); } function isTemplateRoute() { return location.pathname.startsWith('/create/ai-effects/') || location.pathname.startsWith('/effects/'); } function isWorkDetailRoute() { return /\/(?:mobile\/)?work-detail/.test(location.pathname); } function isMobile() { const physicalWidth = Math.min(window.screen.width || window.innerWidth, window.screen.height || window.innerHeight); const hasTouch = navigator.maxTouchPoints > 0 || 'ontouchstart' in window; return media.matches || window.innerWidth <= 768 || (hasTouch && physicalWidth <= 820); } function delay(ms) { return new Promise((resolve) => window.setTimeout(resolve, ms)); } function throttle(callback, wait) { let timer = 0; return () => { if (timer) return; timer = window.setTimeout(() => { timer = 0; callback(); }, wait); }; } function waitForBody(callback) { if (document.body) { callback(); return; } let done = false; const run = () => { if (done || !document.body) return; done = true; window.clearInterval(timer); callback(); }; const timer = window.setInterval(run, 50); window.setTimeout(() => window.clearInterval(timer), 8000); document.addEventListener('DOMContentLoaded', run, { once: true }); window.addEventListener('load', run, { once: true }); } function injectStyles() { let style = document.getElementById(STYLE_ID); const shouldAppend = !style; if (!style) style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` @media (max-width: 768px), (pointer: coarse), (max-device-width: 820px) { html[data-zbai-mobile="1"], html[data-zbai-mobile="1"] body#haimeta { width: 100vw !important; min-width: 0 !important; min-height: 100dvh !important; overflow-x: hidden !important; -webkit-text-size-adjust: 100% !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"], html[data-zbai-mobile="1"][data-zbai-page="editor"] body#haimeta { height: 100dvh !important; overflow: hidden !important; overscroll-behavior: none !important; } html[data-zbai-mobile="1"] #haimeta *, html[data-zbai-mobile="1"] #haimeta *::before, html[data-zbai-mobile="1"] #haimeta *::after { box-sizing: border-box !important; } html[data-zbai-mobile="1"] img, html[data-zbai-mobile="1"] video, html[data-zbai-mobile="1"] canvas { opacity: 1 !important; filter: none !important; -webkit-filter: none !important; mix-blend-mode: normal !important; background-blend-mode: normal !important; image-rendering: auto !important; } body.${CLASS.enabled}.${CLASS.lock} { overflow: hidden !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"] #root, html[data-zbai-mobile="1"][data-zbai-page="editor"] .PageContainer_haimetaApp__qfjGd, html[data-zbai-mobile="1"][data-zbai-page="editor"] .PageContainer_indexContent__5aBHJ, html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor { width: 100vw !important; min-width: 0 !important; max-width: 100vw !important; height: 100dvh !important; min-height: 100dvh !important; overflow: hidden !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor { display: flex !important; flex-direction: column !important; transform: none !important; margin: 0 !important; left: 0 !important; right: auto !important; } html[data-zbai-mobile="1"] .picture-editor-header { height: calc(52px + env(safe-area-inset-top, 0px)) !important; min-height: calc(52px + env(safe-area-inset-top, 0px)) !important; padding-top: env(safe-area-inset-top, 0px) !important; position: sticky !important; top: 0 !important; z-index: 80 !important; background: var(--Background-1-Normal, #f4f5fb) !important; } html[data-zbai-mobile="1"] #picture-editor-header { height: 52px !important; min-height: 52px !important; padding: 0 10px !important; display: grid !important; grid-template-columns: minmax(0, 1fr) auto !important; align-items: center !important; gap: 8px !important; } html[data-zbai-mobile="1"] .picture-editor-header .center { display: none !important; } html[data-zbai-mobile="1"] .picture-editor-header .left { width: auto !important; min-width: 0 !important; max-width: 100% !important; position: static !important; transform: none !important; overflow: hidden !important; } html[data-zbai-mobile="1"] .picture-editor-header .left img { max-height: 30px !important; width: auto !important; } html[data-zbai-mobile="1"] .picture-editor-header .right { width: auto !important; min-width: 0 !important; max-width: 46vw !important; position: static !important; display: flex !important; align-items: center !important; justify-content: flex-end !important; gap: 8px !important; overflow: hidden !important; } html[data-zbai-mobile="1"] .picture-editor-header .right .login-btn { white-space: nowrap !important; } html[data-zbai-mobile="1"] .picture-editor-header .right button { min-width: 0 !important; height: 32px !important; padding: 0 10px !important; white-space: nowrap !important; } html[data-zbai-mobile="1"] .picture-editor-content { height: calc(100dvh - 52px - env(safe-area-inset-top, 0px)) !important; width: 100vw !important; min-width: 0 !important; display: flex !important; flex-direction: column !important; overflow: hidden !important; position: relative !important; transform: none !important; margin: 0 !important; padding: 0 !important; left: 0 !important; right: auto !important; } html[data-zbai-mobile="1"] .picture-editor-content-left-drawer, html[data-zbai-mobile="1"] .PictureEditorHistoryDrawer_container__fn3Ud, html[data-zbai-mobile="1"] .TaskSide_record__JuEDU { display: block !important; visibility: hidden !important; position: fixed !important; z-index: 2147483601 !important; inset: 0 auto 0 0 !important; width: min(40vw, 148px) !important; min-width: 92px !important; height: 100dvh !important; margin: 0 !important; padding: 12px 8px calc(92px + env(safe-area-inset-bottom)) !important; border-radius: 0 18px 18px 0 !important; overflow: auto !important; overscroll-behavior: contain !important; pointer-events: none !important; background: #f8f8fb !important; box-shadow: 10px 0 34px rgba(19, 24, 33, .22) !important; transform: translateX(-112%) !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] .picture-editor-content-box { width: 100vw !important; min-width: 0 !important; height: 100% !important; margin: 0 !important; left: auto !important; right: auto !important; transform: none !important; display: flex !important; flex-direction: column !important; overflow: hidden !important; position: relative !important; } html[data-zbai-mobile="1"] .picture-editor-content-render { width: 100vw !important; min-width: 0 !important; flex: 0 0 auto !important; height: 38dvh !important; min-height: 180px !important; max-height: 42dvh !important; padding: 6px 10px !important; margin: 0 !important; left: 0 !important; right: auto !important; transform: none !important; display: flex !important; align-items: center !important; justify-content: center !important; overflow: auto !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] .picture-editor-content-render > * { max-width: 100% !important; max-height: 100% !important; } html[data-zbai-mobile="1"] .picture-editor-content-control { width: 100vw !important; min-width: 0 !important; max-width: 100vw !important; flex: 1 1 auto !important; height: auto !important; min-height: 0 !important; margin: 0 !important; left: 0 !important; right: auto !important; transform: none !important; padding: 12px 12px calc(92px + env(safe-area-inset-bottom, 0px)) !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; border-radius: 18px 18px 0 0 !important; background: var(--Background-2-Normal, #f1f2f8) !important; box-shadow: 0 -10px 30px rgba(20, 24, 38, 0.08) !important; } html[data-zbai-mobile="1"] .PictureEditorControl_PictureEditorControl__T6FHN { width: 100% !important; min-width: 0 !important; height: auto !important; padding: 0 !important; } html[data-zbai-mobile="1"] .control-config { width: 100% !important; height: auto !important; display: flex !important; flex-direction: column !important; gap: 12px !important; } html[data-zbai-mobile="1"] .control-config > div, html[data-zbai-mobile="1"] .option-need-margin { width: 100% !important; margin: 0 !important; } html[data-zbai-mobile="1"] .control-config-title { width: 100% !important; height: auto !important; min-height: 24px !important; margin: 0 0 8px !important; line-height: 24px !important; position: relative !important; z-index: 1 !important; white-space: normal !important; word-break: keep-all !important; transform: none !important; translate: none !important; } html[data-zbai-mobile="1"] #multifunctional-input, html[data-zbai-mobile="1"] .MultifunctionalInput_multifunctionalInput__NHcuq, html[data-zbai-mobile="1"] .Input_input__A9sVA, html[data-zbai-mobile="1"] .input-box, html[data-zbai-mobile="1"] textarea.textarea-buttons, html[data-zbai-mobile="1"] textarea.ant-input { width: 100% !important; min-width: 0 !important; } html[data-zbai-mobile="1"] textarea.textarea-buttons, html[data-zbai-mobile="1"] textarea.ant-input { min-height: 92px !important; max-height: 160px !important; font-size: 16px !important; line-height: 1.45 !important; } html[data-zbai-mobile="1"] .MultifunctionalInput_multifunctionalInput__NHcuq .bg-1, html[data-zbai-mobile="1"] .MultifunctionalInput_multifunctionalInput__NHcuq .bg-2 { display: none !important; } html[data-zbai-mobile="1"] .ChooseModelWithDetails_ChooseModelWithDetails__m_Cbs, html[data-zbai-mobile="1"] .ChooseModelWithDetails_selectBox__HoIuc, html[data-zbai-mobile="1"] .ChooseModelWithDetails_selectItem__7NA37 { width: 100% !important; min-height: 56px !important; height: auto !important; } html[data-zbai-mobile="1"] .ChooseSizeBase_chooseSizeBase__KgHNI, html[data-zbai-mobile="1"] .choose-size-base-shape { width: 100% !important; overflow-x: auto !important; overflow-y: hidden !important; display: flex !important; gap: 8px !important; -webkit-overflow-scrolling: touch !important; scroll-snap-type: x proximity !important; } html[data-zbai-mobile="1"] .choose-size-base-shape > div, html[data-zbai-mobile="1"] .shape-item, html[data-zbai-mobile="1"] .shape-item .item { flex: 0 0 64px !important; width: 64px !important; height: 60px !important; scroll-snap-align: start !important; } html[data-zbai-mobile="1"] .ControlBottom_controlBottom__gooOU, html[data-zbai-mobile="1"] .generate-button-container { width: 100% !important; height: auto !important; margin-top: 10px !important; position: relative !important; bottom: auto !important; z-index: 2 !important; transform: none !important; box-shadow: none !important; background: var(--Background-2-Normal, #f1f2f8) !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] .ChooseNumber_ChooseNumber__J1uo_, html[data-zbai-mobile="1"] .choose-number-content { width: 100% !important; margin-bottom: 14px !important; } html[data-zbai-mobile="1"] .choose-number-content { display: grid !important; grid-template-columns: auto minmax(120px, 1fr) !important; align-items: center !important; gap: 12px !important; } html[data-zbai-mobile="1"] .choose-number-options { justify-self: end !important; width: min(160px, 48vw) !important; } html[data-zbai-mobile="1"] .choose-number-options button, html[data-zbai-mobile="1"] .choose-number-options [role="button"], html[data-zbai-mobile="1"] .choose-number-options div, html[data-zbai-mobile="1"] .ChooseNumber_ChooseNumber__J1uo_ button, html[data-zbai-mobile="1"] .ChooseNumber_ChooseNumber__J1uo_ [role="button"] { min-height: 44px !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] .generate-button-container, html[data-zbai-mobile="1"] .control-buttons { width: 100% !important; min-height: 50px !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] .generate-button-container { margin-top: 18px !important; } html[data-zbai-mobile="1"] .generate-button-container button { width: 100% !important; min-height: 52px !important; border-radius: 16px !important; background: #11120d !important; color: #fff !important; box-shadow: 0 14px 34px rgba(17, 18, 13, 0.18) !important; font-size: 15px !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] .generate-button-container button *, html[data-zbai-mobile="1"] .generate-button-container button span, html[data-zbai-mobile="1"] .generate-button-container button i, html[data-zbai-mobile="1"] .generate-button-container button em, html[data-zbai-mobile="1"] .generate-button-container button strong, html[data-zbai-mobile="1"] .generate-button-container button small, html[data-zbai-mobile="1"] .generate-button-container button div, html[data-zbai-mobile="1"] .generate-button-container button p { color: #fff !important; opacity: 1 !important; text-shadow: none !important; mix-blend-mode: normal !important; } html[data-zbai-mobile="1"] .generate-button-container button svg, html[data-zbai-mobile="1"] .generate-button-container button svg *, html[data-zbai-mobile="1"] .generate-button-container button path { color: #fff !important; fill: currentColor !important; stroke: currentColor !important; opacity: 1 !important; } html[data-zbai-mobile="1"] .generate-button-container button img { opacity: 1 !important; filter: brightness(0) invert(1) !important; -webkit-filter: brightness(0) invert(1) !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] .picture-editor-content { height: calc(var(--zbai-keyboard-viewport-height, 100dvh) - 52px - env(safe-area-inset-top, 0px)) !important; min-height: 0 !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] .picture-editor-content-render { flex: 0 0 78px !important; height: 78px !important; min-height: 66px !important; max-height: 84px !important; padding: 4px 10px !important; opacity: .55 !important; overflow: hidden !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] .picture-editor-content-control { flex: 1 1 auto !important; min-height: 0 !important; max-height: none !important; padding-top: 10px !important; padding-bottom: calc(18px + var(--zbai-keyboard-bottom, 0px)) !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] textarea, html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] textarea.textarea-buttons, html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] textarea.ant-input { min-height: 168px !important; max-height: 42dvh !important; font-size: 16px !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(textarea:focus) .picture-editor-content-render, html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(input:not([type="file"]):focus) .picture-editor-content-render { flex: 0 0 78px !important; height: 78px !important; min-height: 66px !important; max-height: 84px !important; padding: 4px 10px !important; opacity: .55 !important; overflow: hidden !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(textarea:focus) .picture-editor-content-control, html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(input:not([type="file"]):focus) .picture-editor-content-control { flex: 1 1 auto !important; min-height: 0 !important; max-height: none !important; padding-top: 10px !important; padding-bottom: calc(18px + var(--zbai-keyboard-bottom, 0px)) !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(textarea:focus) textarea, html[data-zbai-mobile="1"][data-zbai-page="editor"] #pictureEditor:has(input:not([type="file"]):focus) textarea { min-height: 168px !important; max-height: 42dvh !important; font-size: 16px !important; } html[data-zbai-mobile="1"] .fixed.top-0.left-0.w-screen.h-screen { padding: env(safe-area-inset-top, 0px) 12px env(safe-area-inset-bottom, 0px) !important; align-items: center !important; } html[data-zbai-mobile="1"] .LoginModal_LoginModal__O59AS, html[data-zbai-mobile="1"] [class*="LoginModal"], html[data-zbai-mobile="1"] [class*="OverseasLogin_overseasLogin"], html[data-zbai-mobile="1"] [class*="overseasLoginModal"] { width: calc(100vw - 24px) !important; max-width: 420px !important; min-width: 0 !important; max-height: calc(100dvh - 48px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)) !important; overflow-y: auto !important; border-radius: 18px !important; } html[data-zbai-mobile="1"][data-zbai-login-open="1"] .fixed.top-0.left-0.w-screen.h-screen, html[data-zbai-mobile="1"][data-zbai-login-open="1"] .LoginModal_LoginModal__O59AS, html[data-zbai-mobile="1"][data-zbai-login-open="1"] [class*="LoginModal"], html[data-zbai-mobile="1"][data-zbai-login-open="1"] [class*="OverseasLogin_overseasLogin"], html[data-zbai-mobile="1"][data-zbai-login-open="1"] [class*="overseasLoginModal"] { display: flex !important; visibility: visible !important; opacity: 1 !important; pointer-events: auto !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] #root { display: none !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-login-open="1"] #root { display: block !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-login-open="1"] #${HOME_SHELL_ID} { display: none !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-native-menu-open="1"] #root { display: block !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-native-menu-open="1"] #${HOME_SHELL_ID} { display: none !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-logout-pending="1"] #root { display: block !important; position: fixed !important; inset: 0 !important; width: 100vw !important; min-height: 100dvh !important; opacity: 0 !important; pointer-events: none !important; z-index: 0 !important; } html[data-zbai-mobile="1"][data-zbai-page="home"][data-zbai-logout-pending="1"] #${HOME_SHELL_ID} { display: block !important; opacity: 1 !important; pointer-events: auto !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-auth { display: none !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} { display: block !important; position: fixed !important; inset: 0 !important; z-index: 10025 !important; width: 100vw !important; height: 100dvh !important; min-height: 100dvh !important; padding: calc(12px + env(safe-area-inset-top, 0px)) 10px calc(86px + env(safe-area-inset-bottom, 0px)) !important; background: linear-gradient(180deg, #fbfaf8 0%, #eef2f6 100%) !important; color: #111318 !important; overflow-x: hidden !important; overflow-y: auto !important; scroll-padding-bottom: calc(86px + env(safe-area-inset-bottom, 0px)) !important; overscroll-behavior-y: contain !important; box-sizing: border-box !important; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} * { box-sizing: border-box !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-topbar { display: grid !important; grid-template-columns: 34px minmax(0, 1fr) auto !important; align-items: center !important; gap: 8px !important; min-height: 38px !important; margin-bottom: 16px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-menu-button { width: 32px !important; height: 32px !important; border: 0 !important; border-radius: 16px !important; display: grid !important; place-items: center !important; padding: 0 !important; background: rgba(255, 255, 255, 0.94) !important; color: #17191f !important; box-shadow: 0 8px 22px rgba(21, 24, 33, 0.08) !important; font: 900 18px/32px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand { min-width: 0 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; color: #111318 !important; font: 900 13px/18px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-auth-pills { display: inline-flex !important; align-items: center !important; gap: 5px !important; min-height: 34px !important; padding: 3px !important; border-radius: 18px !important; background: rgba(255, 255, 255, 0.82) !important; box-shadow: 0 10px 28px rgba(19, 24, 33, 0.12) !important; backdrop-filter: blur(14px) !important; -webkit-backdrop-filter: blur(14px) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button { min-width: 46px !important; height: 28px !important; border: 0 !important; border-radius: 14px !important; padding: 0 11px !important; background: #eef0f5 !important; color: #202333 !important; font: 800 12px/28px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; white-space: nowrap !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button[data-home-action="login"], html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button[data-home-action="logout"] { background: #050605 !important; color: #fff !important; box-shadow: 0 8px 18px rgba(5, 6, 5, 0.18) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title { margin: 12px 0 10px !important; display: flex !important; align-items: baseline !important; justify-content: space-between !important; gap: 8px !important; width: 100% !important; min-height: 30px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title strong { color: #141722 !important; font: 900 20px/30px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title small { color: #7a808e !important; font: 700 11px/18px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-grid, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-grid { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 10px !important; width: 100% !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card { position: relative !important; display: block !important; width: 100% !important; height: auto !important; border: 0 !important; border-radius: 12px !important; background: rgba(255, 255, 255, 0.94) !important; color: #141722 !important; box-shadow: 0 12px 30px rgba(38, 45, 65, 0.08) !important; overflow: hidden !important; text-align: left !important; touch-action: manipulation !important; cursor: pointer !important; pointer-events: auto !important; -webkit-tap-highlight-color: transparent !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card { min-height: 98px !important; padding: 14px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card { min-height: 92px !important; padding: 12px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card small, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card small { display: block !important; color: #6f7584 !important; font: 700 11px/16px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card strong { display: block !important; margin-top: 7px !important; color: #171a22 !important; font: 900 18px/23px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card span { display: block !important; margin-top: 5px !important; color: #5b6070 !important; font: 600 11px/16px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-tag { display: inline-flex !important; align-items: center !important; height: 22px !important; padding: 0 8px !important; border-radius: 11px !important; background: #f0f2f6 !important; color: #6d7481 !important; font: 800 10px/22px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card strong { display: block !important; margin-top: 8px !important; color: #171a22 !important; font: 900 15px/20px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card small { margin-top: 4px !important; display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; } /* 首页轻奢珠宝 UI 覆盖:仅作用于移动首页 shell */ html[data-zbai-mobile="1"] #${HOME_SHELL_ID} { position: fixed !important; padding: calc(14px + env(safe-area-inset-top, 0px)) 12px calc(96px + env(safe-area-inset-bottom, 0px)) !important; background: radial-gradient(circle at 82% 2%, rgba(255, 237, 188, .68) 0, rgba(255, 237, 188, 0) 34%), radial-gradient(circle at 2% 19%, rgba(255, 255, 255, .98) 0, rgba(255, 255, 255, 0) 30%), linear-gradient(180deg, #fbf8ef 0%, #f8f1e2 28%, #eef2f6 100%) !important; color: #17130e !important; font-family: "PingFang SC", "Songti SC", "Noto Serif SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-ambient { position: fixed !important; inset: 0 !important; z-index: -1 !important; pointer-events: none !important; background: linear-gradient(115deg, rgba(140, 105, 42, .07), rgba(255, 255, 255, 0) 38%), repeating-linear-gradient(135deg, rgba(142, 111, 45, .035) 0 1px, transparent 1px 12px) !important; opacity: .9 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-ambient::before, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-ambient::after { content: "" !important; position: absolute !important; border-radius: 999px !important; filter: blur(2px) !important; opacity: .7 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-ambient::before { width: 160px !important; height: 160px !important; right: -54px !important; top: 70px !important; background: radial-gradient(circle, rgba(242, 204, 119, .35), rgba(242, 204, 119, 0) 66%) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-ambient::after { width: 210px !important; height: 210px !important; left: -92px !important; bottom: 160px !important; background: radial-gradient(circle, rgba(255, 255, 255, .86), rgba(255, 255, 255, 0) 64%) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-topbar { grid-template-columns: 44px minmax(0, 1fr) auto !important; min-height: 48px !important; gap: 12px !important; margin: 0 0 14px !important; position: sticky !important; top: calc(8px + env(safe-area-inset-top, 0px)) !important; z-index: 3 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-menu-button { width: 42px !important; height: 42px !important; border-radius: 21px !important; background: rgba(255, 255, 255, .82) !important; border: 1px solid rgba(255, 255, 255, .82) !important; color: #1a1712 !important; box-shadow: 0 16px 34px rgba(67, 49, 22, .12), inset 0 1px 0 rgba(255, 255, 255, .92) !important; font: 900 19px/42px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; backdrop-filter: blur(16px) saturate(1.12) !important; -webkit-backdrop-filter: blur(16px) saturate(1.12) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand { display: inline-flex !important; align-items: center !important; gap: 8px !important; color: #17130e !important; font: 900 18px/24px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: .2px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand-mark { width: 19px !important; height: 19px !important; flex: 0 0 19px !important; border-radius: 7px !important; transform: rotate(45deg) !important; background: linear-gradient(135deg, #fff9e9 0%, #d8ab55 48%, #7d551b 100%) !important; box-shadow: 0 7px 18px rgba(169, 121, 42, .28), inset 0 1px 0 rgba(255, 255, 255, .94) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-auth-pills { min-height: 44px !important; padding: 4px !important; border: 1px solid rgba(255, 255, 255, .74) !important; border-radius: 24px !important; background: rgba(255, 255, 255, .62) !important; box-shadow: 0 18px 42px rgba(54, 38, 14, .18), inset 0 1px 0 rgba(255, 255, 255, .9) !important; backdrop-filter: blur(18px) saturate(1.18) !important; -webkit-backdrop-filter: blur(18px) saturate(1.18) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button { min-width: 56px !important; height: 36px !important; border-radius: 18px !important; padding: 0 13px !important; background: rgba(245, 244, 239, .84) !important; color: #302919 !important; font: 900 14px/36px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button[data-home-action="login"], html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-pill-button[data-home-action="logout"] { color: #fff8e8 !important; background: linear-gradient(180deg, #17140f 0%, #040404 100%) !important; box-shadow: 0 12px 24px rgba(0, 0, 0, .24), inset 0 1px 0 rgba(255, 255, 255, .14) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-credit-pill { height: 36px !important; min-width: 74px !important; padding: 4px 10px !important; border-radius: 18px !important; display: inline-grid !important; align-content: center !important; justify-items: center !important; background: linear-gradient(180deg, rgba(255, 250, 231, .96), rgba(233, 210, 154, .82)) !important; color: #4b3513 !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .72), 0 10px 20px rgba(112, 79, 23, .12) !important; white-space: nowrap !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-credit-pill[hidden] { display: none !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-credit-pill small { font: 800 9px/10px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; opacity: .72 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-credit-pill strong { font: 950 13px/15px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: .01em !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-hero { position: relative !important; display: grid !important; grid-template-columns: minmax(0, 1fr) 116px !important; align-items: center !important; gap: 12px !important; min-height: 132px !important; margin: 2px 0 18px !important; padding: 18px 16px !important; border: 1px solid rgba(255, 255, 255, .72) !important; border-radius: 28px !important; overflow: hidden !important; background: linear-gradient(135deg, rgba(255, 255, 255, .86) 0%, rgba(255, 248, 231, .72) 52%, rgba(221, 180, 96, .24) 100%) !important; box-shadow: 0 24px 54px rgba(89, 64, 23, .14), inset 0 1px 0 rgba(255, 255, 255, .96) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-hero::before { content: "" !important; position: absolute !important; inset: -40% -12% auto auto !important; width: 190px !important; height: 190px !important; border-radius: 999px !important; background: radial-gradient(circle, rgba(225, 174, 66, .32), rgba(225, 174, 66, 0) 68%) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-copy { position: relative !important; z-index: 1 !important; min-width: 0 !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-copy span { display: inline-flex !important; align-items: center !important; height: 22px !important; padding: 0 9px !important; border-radius: 999px !important; background: rgba(151, 106, 33, .1) !important; color: #9b6d22 !important; font: 900 10px/22px -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 1.4px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-copy strong { display: block !important; margin-top: 10px !important; max-width: 9em !important; color: #17130e !important; font: 900 25px/31px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: -.6px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-copy small { display: block !important; margin-top: 8px !important; max-width: 15em !important; color: #746a58 !important; font: 700 12px/18px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel { position: relative !important; z-index: 1 !important; width: 108px !important; height: 108px !important; border-radius: 36px !important; background: radial-gradient(circle at 36% 28%, rgba(255, 255, 255, .98), rgba(255, 255, 255, 0) 26%), linear-gradient(145deg, rgba(255, 253, 247, .82), rgba(224, 181, 91, .26)) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .98), 0 20px 38px rgba(145, 103, 34, .18) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel::before { content: "" !important; position: absolute !important; left: 28px !important; top: 22px !important; width: 54px !important; height: 54px !important; transform: rotate(45deg) !important; border-radius: 16px !important; background: linear-gradient(135deg, #ffffff 0%, #f7e5b5 35%, #c69334 100%) !important; box-shadow: 0 18px 32px rgba(151, 101, 24, .22), inset 0 1px 0 rgba(255, 255, 255, .98) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel i { position: absolute !important; width: 8px !important; height: 8px !important; border-radius: 50% !important; background: #fff7dc !important; box-shadow: 0 0 18px rgba(212, 158, 50, .78) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel i:nth-child(1) { right: 15px !important; top: 16px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel i:nth-child(2) { left: 18px !important; bottom: 20px !important; transform: scale(.72) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-jewel i:nth-child(3) { right: 24px !important; bottom: 17px !important; transform: scale(.5) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title { margin: 16px 0 10px !important; min-height: 34px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title strong { color: #17130e !important; font: 900 23px/32px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: -.5px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-section-title small { color: #978c7b !important; font: 800 12px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-grid, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-grid { gap: 12px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card { isolation: isolate !important; border: 1px solid rgba(255, 255, 255, .74) !important; border-radius: 24px !important; background: linear-gradient(180deg, rgba(255, 255, 255, .9) 0%, rgba(255, 255, 255, .72) 100%) !important; box-shadow: 0 18px 42px rgba(53, 42, 24, .1), inset 0 1px 0 rgba(255, 255, 255, .94) !important; backdrop-filter: blur(14px) saturate(1.08) !important; -webkit-backdrop-filter: blur(14px) saturate(1.08) !important; transition: transform .18s ease, box-shadow .18s ease !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card:active, html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card:active { transform: translateY(1px) scale(.99) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-card-shine { position: absolute !important; inset: auto -26px -30px auto !important; z-index: -1 !important; width: 98px !important; height: 98px !important; border-radius: 999px !important; background: radial-gradient(circle, rgba(221, 171, 71, .22), rgba(221, 171, 71, 0) 68%) !important; pointer-events: none !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card { min-height: 122px !important; padding: 15px 15px 13px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card-image { background: radial-gradient(circle at 80% 12%, rgba(255, 244, 207, .98), rgba(255, 244, 207, 0) 42%), linear-gradient(145deg, rgba(255, 255, 255, .92) 0%, rgba(255, 244, 219, .86) 56%, rgba(219, 171, 78, .22) 100%) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card-video { background: radial-gradient(circle at 80% 12%, rgba(235, 241, 255, .96), rgba(235, 241, 255, 0) 42%), linear-gradient(145deg, rgba(255, 255, 255, .9) 0%, rgba(243, 247, 255, .82) 100%) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-icon { position: absolute !important; right: 13px !important; top: 13px !important; display: grid !important; place-items: center !important; width: 34px !important; height: 34px !important; border-radius: 14px !important; color: #9b6d22 !important; background: rgba(255, 255, 255, .72) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .94), 0 10px 22px rgba(123, 88, 24, .12) !important; font: 900 17px/1 "Times New Roman", serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card small { color: #a77a2d !important; font: 900 12px/16px -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: .7px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card strong { margin-top: 9px !important; padding-right: 34px !important; color: #17130e !important; font: 900 21px/27px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: -.5px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card span:not(.zbai-mobile-card-shine):not(.zbai-mobile-tool-icon) { margin-top: 6px !important; color: #766b59 !important; font: 700 12px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card em { display: inline-flex !important; align-items: center !important; gap: 4px !important; margin-top: 11px !important; color: #8a601e !important; font: normal 900 11px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-tool-card em::after { content: "→" !important; font-size: 13px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card { min-height: 108px !important; padding: 13px !important; border-radius: 22px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-head { display: flex !important; align-items: center !important; justify-content: space-between !important; gap: 8px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-tag { height: 24px !important; padding: 0 10px !important; border: 1px solid rgba(180, 136, 53, .08) !important; background: rgba(249, 243, 230, .92) !important; color: #8f7858 !important; font: 900 11px/24px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-icon { display: grid !important; place-items: center !important; width: 24px !important; height: 24px !important; flex: 0 0 24px !important; border-radius: 10px !important; color: #a87725 !important; background: rgba(255, 255, 255, .68) !important; font: 900 13px/1 "Times New Roman", serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card strong { margin-top: 10px !important; color: #18140f !important; font: 900 17px/22px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; letter-spacing: -.2px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-template-card small { margin-top: 5px !important; color: #7a7163 !important; font: 700 12px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } /* 首页顶部布局:topbar 回到文档流,避免滚动时压住 Hero 标题 */ html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-topbar { position: relative !important; top: auto !important; z-index: 5 !important; margin: 0 0 16px !important; padding: 0 2px !important; background: transparent !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand-mark { width: 17px !important; height: 17px !important; flex-basis: 17px !important; border-radius: 6px !important; box-shadow: 0 6px 14px rgba(169, 121, 42, .22), inset 0 1px 0 rgba(255, 255, 255, .94) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-hero { margin-top: 0 !important; padding-top: 20px !important; overflow: hidden !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-hero-copy strong { position: relative !important; z-index: 2 !important; } /* 首页菜单与官方 Logo */ html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand-native { display: none !important; align-items: center !important; justify-content: flex-start !important; gap: 0 !important; min-width: 0 !important; width: 100% !important; max-width: min(176px, 42vw) !important; overflow: hidden !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID}.zbai-mobile-has-native-logo .zbai-mobile-brand-native { display: inline-flex !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID}.zbai-mobile-has-native-logo .zbai-mobile-brand-mark, html[data-zbai-mobile="1"] #${HOME_SHELL_ID}.zbai-mobile-has-native-logo .zbai-mobile-brand-text { display: none !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand-native img { display: block !important; flex: 0 1 auto !important; max-height: clamp(25px, 7vw, 34px) !important; max-width: 100% !important; width: auto !important; height: auto !important; object-fit: contain !important; object-position: left center !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-brand-native .zbai-mobile-official-logo { height: clamp(25px, 7vw, 34px) !important; max-height: clamp(25px, 7vw, 34px) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu-backdrop { position: fixed !important; inset: 0 !important; z-index: 29 !important; opacity: 0 !important; pointer-events: none !important; background: rgba(22, 16, 8, .22) !important; backdrop-filter: blur(2px) !important; -webkit-backdrop-filter: blur(2px) !important; transition: opacity .18s ease !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu { position: fixed !important; z-index: 30 !important; left: max(12px, env(safe-area-inset-left, 0px)) !important; top: calc(74px + env(safe-area-inset-top, 0px)) !important; width: min(300px, calc(100vw - 24px)) !important; padding: 14px !important; border-radius: 28px !important; border: 1px solid rgba(255, 255, 255, .72) !important; background: radial-gradient(circle at 80% 0%, rgba(236, 195, 99, .24), rgba(236, 195, 99, 0) 48%), rgba(255, 252, 244, .9) !important; box-shadow: 0 28px 70px rgba(57, 39, 13, .24), inset 0 1px 0 rgba(255, 255, 255, .96) !important; backdrop-filter: blur(20px) saturate(1.2) !important; -webkit-backdrop-filter: blur(20px) saturate(1.2) !important; transform: translateY(-8px) scale(.98) !important; opacity: 0 !important; pointer-events: none !important; transition: transform .18s ease, opacity .18s ease !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID}.zbai-mobile-home-menu-open .zbai-mobile-home-menu-backdrop { opacity: 1 !important; pointer-events: auto !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID}.zbai-mobile-home-menu-open .zbai-mobile-home-menu { transform: translateY(0) scale(1) !important; opacity: 1 !important; pointer-events: auto !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu-head { display: flex !important; align-items: center !important; justify-content: space-between !important; gap: 12px !important; padding: 3px 2px 12px !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu-head strong { color: #19140e !important; font: 900 18px/24px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu-head button { width: 34px !important; height: 34px !important; border: 0 !important; border-radius: 17px !important; background: rgba(22, 18, 12, .08) !important; color: #211a10 !important; font: 900 24px/34px -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu > button { width: 100% !important; min-height: 62px !important; margin: 8px 0 0 !important; padding: 10px 12px !important; display: grid !important; grid-template-columns: 38px minmax(0, 1fr) !important; grid-template-rows: auto auto !important; column-gap: 10px !important; align-items: center !important; border: 1px solid rgba(255, 255, 255, .72) !important; border-radius: 18px !important; background: rgba(255, 255, 255, .72) !important; color: #17130e !important; text-align: left !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9), 0 10px 24px rgba(53, 42, 24, .08) !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu > button > span { grid-row: 1 / span 2 !important; display: grid !important; place-items: center !important; width: 38px !important; height: 38px !important; border-radius: 14px !important; background: rgba(250, 241, 220, .92) !important; color: #9b6d22 !important; font: 900 16px/1 "Times New Roman", serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu > button > strong { display: block !important; color: #17130e !important; font: 900 15px/20px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${HOME_SHELL_ID} .zbai-mobile-home-menu > button > small { display: block !important; color: #817667 !important; font: 700 11px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] #root, html[data-zbai-mobile="1"][data-zbai-page="home"] .PageContainer_haimetaApp__qfjGd, html[data-zbai-mobile="1"][data-zbai-page="home"] .PageContainer_indexContent__5aBHJ, html[data-zbai-mobile="1"][data-zbai-page="home"] main, html[data-zbai-mobile="1"][data-zbai-page="home"] section { width: 100vw !important; min-width: 0 !important; max-width: 100vw !important; height: auto !important; overflow-x: hidden !important; transform: none !important; margin-left: 0 !important; margin-right: 0 !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] #picture-editor-header.PictureEditorHeader_homepageHeader__j_gnO, html[data-zbai-mobile="1"][data-zbai-page="home"] .picture-editor-header, html[data-zbai-mobile="1"][data-zbai-page="home"] #picture-editor-header, html[data-zbai-mobile="1"][data-zbai-page="home"] header { width: 100vw !important; min-width: 0 !important; height: calc(52px + env(safe-area-inset-top, 0px)) !important; min-height: calc(52px + env(safe-area-inset-top, 0px)) !important; padding: env(safe-area-inset-top, 0px) 10px 0 !important; position: sticky !important; top: 0 !important; z-index: 80 !important; display: flex !important; align-items: center !important; justify-content: space-between !important; background: var(--Background-1-Normal, #f4f5fb) !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] .SidebarNavigation_sidebarNavigation__KTG_b { display: none !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] main.index-main, html[data-zbai-mobile="1"][data-zbai-page="home"] .index-main-content, html[data-zbai-mobile="1"][data-zbai-page="home"] #index-main-content--right { width: 100vw !important; min-width: 0 !important; max-width: 100vw !important; height: auto !important; min-height: calc(100dvh - 52px) !important; margin: 0 !important; padding-left: 0 !important; padding-right: 0 !important; transform: none !important; left: 0 !important; right: auto !important; overflow-x: hidden !important; overflow-y: visible !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="waterfall"], html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="Waterfall"], html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="card-list"], html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="CardList"], html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="template-list"], html[data-zbai-mobile="1"][data-zbai-page="home"] [class*="TemplateList"] { width: 100% !important; max-width: 100% !important; min-width: 0 !important; } html[data-zbai-mobile="1"] .Popup_popupContent__dage3 .content, html[data-zbai-mobile="1"] [class*="Popup_popupContent"] .content, html[data-zbai-mobile="1"] .popu-create-now-content .content { padding-bottom: calc(108px + env(safe-area-inset-bottom, 0px)) !important; box-sizing: border-box !important; } html[data-zbai-mobile="1"] .Popup_popupContent__dage3 .introduction-area, html[data-zbai-mobile="1"] [class*="Popup_popupContent"] .introduction-area, html[data-zbai-mobile="1"] .popu-create-now-content .introduction-area { max-height: calc(100dvh - 108px - env(safe-area-inset-bottom, 0px)) !important; padding-bottom: calc(108px + env(safe-area-inset-bottom, 0px)) !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; box-sizing: border-box !important; } html[data-zbai-mobile="1"][data-zbai-page="assets"] .${CLASS.assetHidden} { display: none !important; } body.${CLASS.enabled} #${ROOT_ID} { display: block !important; position: fixed !important; top: 0 !important; left: 0 !important; width: 0 !important; height: 0 !important; z-index: 2147483638 !important; overflow: visible !important; pointer-events: none !important; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-auth { position: fixed !important; z-index: 2147483642 !important; top: max(12px, env(safe-area-inset-top)) !important; right: max(12px, env(safe-area-inset-right)) !important; min-width: 64px !important; min-height: 38px !important; padding: 0 14px !important; border: 1px solid rgba(255, 255, 255, 0.56) !important; border-radius: 19px !important; color: #fff !important; background: rgba(17, 18, 13, 0.9) !important; box-shadow: 0 8px 22px rgba(20, 24, 38, 0.16) !important; backdrop-filter: blur(12px) !important; -webkit-backdrop-filter: blur(12px) !important; font: 800 13px/38px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; pointer-events: auto !important; touch-action: manipulation !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-close { display: none !important; position: fixed !important; z-index: 2147483645 !important; top: max(10px, env(safe-area-inset-top)) !important; left: max(10px, env(safe-area-inset-left)) !important; min-height: 40px !important; padding: 0 14px !important; border: 1px solid rgba(31, 35, 40, .14) !important; border-radius: 20px !important; color: #17191f !important; background: rgba(255, 255, 255, .96) !important; box-shadow: 0 10px 28px rgba(19, 24, 33, .18) !important; font: 800 14px/40px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; pointer-events: auto !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-back { display: none !important; position: fixed !important; z-index: 2147483642 !important; top: max(10px, env(safe-area-inset-top)) !important; left: max(10px, env(safe-area-inset-left)) !important; min-height: 40px !important; padding: 0 14px !important; border: 1px solid rgba(31, 35, 40, .14) !important; border-radius: 20px !important; color: #17191f !important; background: rgba(255, 255, 255, .96) !important; box-shadow: 0 10px 28px rgba(19, 24, 33, .18) !important; font: 800 14px/40px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"]:not([data-zbai-page="home"]) body.${CLASS.enabled} #${ROOT_ID}:not([data-zbai-surface]) .zbai-mobile-back { display: block !important; } body.${CLASS.enabled} #${ROOT_ID}[data-zbai-surface] .zbai-mobile-close { display: block !important; } body.${CLASS.enabled} #${ROOT_ID}:not([data-zbai-route="compose"]) .zbai-mobile-dock { display: none !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-dock { position: fixed !important; z-index: 2147483643 !important; right: max(12px, env(safe-area-inset-right)) !important; bottom: calc(12px + env(safe-area-inset-bottom)) !important; left: max(12px, env(safe-area-inset-left)) !important; display: grid !important; grid-template-columns: repeat(4, minmax(0, 1fr)) !important; gap: 8px !important; padding: 8px !important; border: 1px solid rgba(255, 255, 255, 0.66) !important; border-radius: 18px !important; background: rgba(255, 255, 255, .9) !important; box-shadow: 0 12px 36px rgba(19, 24, 33, .18) !important; backdrop-filter: blur(14px) !important; -webkit-backdrop-filter: blur(14px) !important; pointer-events: auto !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-dock button { min-width: 0 !important; min-height: 44px !important; border: 0 !important; border-radius: 13px !important; color: #17191f !important; background: #eef0f7 !important; font: 800 14px/20px -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif !important; letter-spacing: 0 !important; touch-action: manipulation !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-dock button:first-child { color: #fff !important; background: #11120d !important; box-shadow: 0 8px 20px rgba(17, 18, 13, .16) !important; } body.${CLASS.enabled} .${CLASS.uploadArmed} { position: fixed !important; z-index: 2147483645 !important; left: var(--zbai-upload-left) !important; top: var(--zbai-upload-top) !important; width: var(--zbai-upload-width) !important; height: var(--zbai-upload-height) !important; min-width: 0 !important; min-height: 0 !important; max-width: none !important; max-height: none !important; margin: 0 !important; padding: 0 !important; border: 0 !important; border-radius: 13px !important; opacity: .01 !important; overflow: hidden !important; transform: none !important; pointer-events: auto !important; touch-action: manipulation !important; } body.${CLASS.enabled} .${CLASS.uploadArmed} * { pointer-events: auto !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-ready { position: fixed !important; z-index: 2147483642 !important; right: max(12px, env(safe-area-inset-right)) !important; bottom: calc(78px + env(safe-area-inset-bottom)) !important; padding: 5px 9px !important; border: 1px solid rgba(31, 35, 40, .12) !important; border-radius: 999px !important; color: #17191f !important; background: rgba(255, 255, 255, .88) !important; box-shadow: 0 8px 22px rgba(19, 24, 33, .16) !important; font-size: 12px !important; font-weight: 700 !important; line-height: 1.35 !important; pointer-events: none !important; } html[data-zbai-mobile="1"][data-zbai-page="home"] body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-ready { left: 10px !important; right: auto !important; bottom: calc(8px + env(safe-area-inset-bottom)) !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-backdrop { position: fixed !important; inset: 0 !important; border: 0 !important; opacity: 0 !important; background: rgba(10, 12, 18, .34) !important; pointer-events: none !important; transition: opacity .18s ease !important; } body.${CLASS.enabled} #${ROOT_ID}[data-zbai-surface] .zbai-mobile-backdrop { opacity: 1 !important; pointer-events: auto !important; } body.${CLASS.enabled} #${ROOT_ID}[data-zbai-surface="compose"] .zbai-mobile-backdrop { pointer-events: none !important; } body.${CLASS.enabled} #${ROOT_ID}[data-zbai-surface="history"] .zbai-mobile-backdrop { inset: 0 0 0 max(92px, min(40vw, 148px)) !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-toast { position: fixed !important; z-index: 2147483646 !important; right: 18px !important; bottom: calc(82px + env(safe-area-inset-bottom)) !important; left: 18px !important; padding: 11px 13px !important; border-radius: 14px !important; color: #fff !important; background: rgba(23, 25, 31, .94) !important; box-shadow: 0 12px 30px rgba(19, 24, 33, .24) !important; font-size: 13px !important; line-height: 1.4 !important; opacity: 0 !important; transform: translateY(8px) !important; transition: opacity .16s ease, transform .16s ease !important; pointer-events: none !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-toast.is-visible { opacity: 1 !important; transform: translateY(0) !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-route-loading { position: fixed !important; z-index: 2147483645 !important; inset: 0 !important; display: grid !important; place-items: center !important; gap: 12px !important; background: rgba(248, 244, 232, .78) !important; backdrop-filter: blur(12px) saturate(1.08) !important; -webkit-backdrop-filter: blur(12px) saturate(1.08) !important; color: #17140f !important; opacity: 0 !important; pointer-events: none !important; transition: opacity .18s ease !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-route-loading[hidden] { display: none !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-route-loading.is-visible { opacity: 1 !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-route-loading span { width: 42px !important; height: 42px !important; border-radius: 999px !important; border: 3px solid rgba(139, 105, 38, .2) !important; border-top-color: rgba(139, 105, 38, .88) !important; animation: zbai-mobile-spin .8s linear infinite !important; } body.${CLASS.enabled} #${ROOT_ID} .zbai-mobile-route-loading strong { padding: 10px 16px !important; border-radius: 999px !important; background: rgba(255, 255, 255, .82) !important; box-shadow: 0 12px 28px rgba(91, 68, 25, .12) !important; font: 900 14px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } @keyframes zbai-mobile-spin { to { transform: rotate(360deg); } } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} { position: fixed !important; z-index: 2147483601 !important; inset: auto 0 0 0 !important; width: auto !important; min-width: 0 !important; max-width: none !important; height: min(88dvh, 860px) !important; max-height: calc(100dvh - 12px) !important; margin: 0 !important; padding-bottom: calc(88px + env(safe-area-inset-bottom)) !important; border-radius: 18px 18px 0 0 !important; overflow: auto !important; overscroll-behavior: contain !important; background: #fff !important; box-shadow: 0 -16px 42px rgba(19, 24, 33, .24) !important; transform: none !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} { height: calc(var(--zbai-keyboard-viewport-height, 100dvh) - 8px) !important; max-height: calc(var(--zbai-keyboard-viewport-height, 100dvh) - 8px) !important; padding-bottom: 18px !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} textarea { min-height: 108px !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-android-keyboard="1"] body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} textarea { min-height: 168px !important; max-height: 42dvh !important; } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} button { min-height: 40px !important; } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} .${CLASS.formatLabel} { flex: 0 0 auto !important; min-width: 48px !important; white-space: nowrap !important; word-break: keep-all !important; writing-mode: horizontal-tb !important; } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} .${CLASS.formatRow} { width: 100% !important; min-width: 0 !important; display: flex !important; flex-wrap: wrap !important; align-items: center !important; gap: 8px !important; pointer-events: auto !important; } body.${CLASS.enabled} .${CLASS.compose}.${CLASS.composeOpen} .${CLASS.formatRow} .${CLASS.formatChoice} { flex: 0 0 auto !important; min-width: 72px !important; min-height: 40px !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; touch-action: manipulation !important; pointer-events: auto !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-preview-focus="1"] .picture-editor-content-render { flex: 1 1 auto !important; height: auto !important; max-height: none !important; min-height: 0 !important; padding-bottom: calc(98px + env(safe-area-inset-bottom, 0px)) !important; } html[data-zbai-mobile="1"][data-zbai-page="editor"][data-zbai-preview-focus="1"] .picture-editor-content-control { display: none !important; } body.${CLASS.enabled} .${CLASS.duplicateTitle} { display: none !important; } body.${CLASS.enabled} .${CLASS.templateTextMode} { display: none !important; } body.${CLASS.enabled} .ant-select-dropdown, body.${CLASS.enabled} .ant-dropdown, body.${CLASS.enabled} .arco-select-popup, body.${CLASS.enabled} .el-select-dropdown, body.${CLASS.enabled} [role="listbox"], body.${CLASS.enabled} [data-radix-popper-content-wrapper] { z-index: 2147483605 !important; } body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} { display: block !important; visibility: visible !important; position: fixed !important; z-index: 2147483601 !important; inset: 0 auto 0 0 !important; width: min(40vw, 148px) !important; min-width: 92px !important; height: 100dvh !important; margin: 0 !important; padding: 12px 8px calc(92px + env(safe-area-inset-bottom)) !important; border-radius: 0 18px 18px 0 !important; overflow: auto !important; overscroll-behavior: contain !important; pointer-events: auto !important; background: #f8f8fb !important; box-shadow: 10px 0 34px rgba(19, 24, 33, .22) !important; transform: none !important; -webkit-overflow-scrolling: touch !important; } body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} [class*="PictureEditorHistoryDrawer"], body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} [class*="TaskSide_record"] { display: block !important; visibility: visible !important; position: relative !important; inset: auto !important; left: auto !important; right: auto !important; top: auto !important; bottom: auto !important; width: 100% !important; min-width: 0 !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; border-radius: 0 !important; overflow: visible !important; pointer-events: auto !important; background: transparent !important; box-shadow: none !important; transform: none !important; } body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} * { visibility: visible !important; pointer-events: auto !important; } body.${CLASS.enabled} .${CLASS.historyParent} { display: block !important; visibility: visible !important; } body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} img { max-width: 100% !important; height: auto !important; pointer-events: auto !important; } body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} a, body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} button, body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} [role="button"], body.${CLASS.enabled} .${CLASS.history}.${CLASS.historyOpen} [onclick] { pointer-events: auto !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID}, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} { position: fixed !important; inset: 0 !important; z-index: 2147483646 !important; display: flex !important; flex-direction: column !important; width: 100vw !important; height: 100dvh !important; min-height: 100vh !important; padding: calc(10px + env(safe-area-inset-top, 0px)) max(10px, env(safe-area-inset-left, 0px)) calc(12px + env(safe-area-inset-bottom, 0px)) max(10px, env(safe-area-inset-right, 0px)) !important; box-sizing: border-box !important; background: radial-gradient(circle at 70% 8%, rgba(220, 176, 83, .22), transparent 34%), linear-gradient(180deg, #15120d 0%, #08090b 100%) !important; color: #fff !important; overflow-x: hidden !important; overflow-y: auto !important; overscroll-behavior-y: contain !important; pointer-events: auto !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-page="work-detail"] body { overflow: hidden !important; background: #08090b !important; } html[data-zbai-page="work-detail"] body > *:not(#${ROOT_ID}):not(#${WORK_DETAIL_FALLBACK_ID}) { visibility: hidden !important; pointer-events: none !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-bar, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-bar { flex: 0 0 48px !important; display: grid !important; grid-template-columns: auto minmax(0, 1fr) auto !important; align-items: center !important; gap: 10px !important; padding: 0 2px !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-bar strong, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-bar strong { overflow: hidden !important; text-align: center !important; text-overflow: ellipsis !important; white-space: nowrap !important; color: #fff7e4 !important; font: 900 16px/22px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} button, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} button { min-width: 66px !important; height: 38px !important; border: 1px solid rgba(255, 255, 255, .16) !important; border-radius: 999px !important; padding: 0 13px !important; background: rgba(255, 255, 255, .92) !important; color: #101217 !important; font-size: 14px !important; font-weight: 800 !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-stage, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-stage { flex: 1 1 auto !important; min-height: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; overflow: auto !important; margin: 8px 0 10px !important; border: 1px solid rgba(255, 255, 255, .1) !important; border-radius: 18px !important; background: rgba(255, 255, 255, .045) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08) !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} img, html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} video, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} img, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} video { display: none !important; max-width: 100% !important; max-height: 100% !important; width: auto !important; height: auto !important; object-fit: contain !important; border-radius: 12px !important; background: transparent !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID}[data-media-kind="image"] img, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID}[data-media-kind="image"] img, html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID}[data-media-kind="video"] video, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID}[data-media-kind="video"] video { display: block !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions { flex: 0 0 auto !important; display: grid !important; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)) !important; gap: 8px !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions button, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions button { width: 100% !important; min-width: 0 !important; background: rgba(255, 247, 228, .94) !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions [data-zbai-work-recreate], html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions [data-zbai-work-recreate] { background: linear-gradient(180deg, #fff1c2 0%, #c7952a 100%) !important; color: #161107 !important; font-weight: 950 !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-info-panel, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-info-panel { flex: 0 0 auto !important; display: flex !important; flex-direction: column !important; gap: 8px !important; max-height: 32dvh !important; margin-top: 8px !important; overflow: auto !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card, html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card, html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card { flex: 0 0 auto !important; padding: 11px !important; border-radius: 16px !important; background: rgba(255, 247, 228, .08) !important; border: 1px solid rgba(255, 247, 228, .12) !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card[hidden], html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card[hidden], html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card[hidden], html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card[hidden], html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card[hidden], html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card[hidden] { display: none !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card small, html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card small, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card small, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-params-card small { display: block !important; margin: 0 0 6px !important; color: rgba(255, 247, 228, .62) !important; font: 800 12px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card p, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-prompt-card p { margin: 0 !important; color: rgba(255, 252, 240, .92) !important; font: 600 13px/20px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; word-break: break-word !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card { padding: 0 !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card button, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card button { width: 100% !important; min-width: 0 !important; min-height: 84px !important; display: grid !important; grid-template-columns: 58px minmax(0, 1fr) auto !important; align-items: center !important; gap: 10px !important; padding: 8px 10px !important; background: transparent !important; color: #fff7e4 !important; text-align: left !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card img, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card img { display: block !important; width: 58px !important; height: 68px !important; max-width: 58px !important; max-height: 68px !important; border-radius: 10px !important; object-fit: contain !important; background: #fff !important; border: 1px solid rgba(255, 247, 228, .22) !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card span, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card span { display: block !important; min-width: 0 !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card small, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card small { display: block !important; color: rgba(255, 247, 228, .62) !important; font: 800 11px/15px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card strong, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card strong { display: block !important; color: #fff7e4 !important; font: 900 13px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card em, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-source-card em { color: #f3d28d !important; font-style: normal !important; font: 900 12px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} [data-zbai-work-tags], html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} [data-zbai-work-tags] { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} [data-zbai-work-tags] span, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} [data-zbai-work-tags] span { max-width: 100% !important; padding: 5px 8px !important; border-radius: 6px !important; background: rgba(255, 247, 228, .12) !important; color: rgba(255, 247, 228, .74) !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; font: 800 11px/15px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-meta, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-meta { flex: 0 0 auto !important; display: flex !important; justify-content: center !important; gap: 10px !important; min-height: 20px !important; padding: 8px 4px 0 !important; color: rgba(255, 247, 228, .68) !important; font: 700 11px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-stage, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-stage { flex: 0 0 auto !important; min-height: min(58dvh, 560px) !important; } html[data-zbai-mobile="1"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-info-panel, html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-info-panel { max-height: none !important; overflow: visible !important; } @media (min-width: 769px) { html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} { padding: 24px !important; } html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-stage { max-width: 1120px !important; width: min(1120px, calc(100vw - 48px)) !important; align-self: center !important; } html[data-zbai-page="work-detail"] #${WORK_DETAIL_FALLBACK_ID} .zbai-work-detail-actions { width: min(560px, calc(100vw - 48px)) !important; align-self: center !important; } } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} { position: fixed !important; inset: 0 !important; z-index: 2147483646 !important; display: grid !important; grid-template-rows: minmax(0, 58dvh) minmax(0, 42dvh) !important; width: 100vw !important; height: 100dvh !important; min-height: 100vh !important; background: #f1f3f8 !important; color: #252936 !important; pointer-events: auto !important; overflow: hidden !important; font-family: "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] body.zbai-work-preview-adapted { overflow: hidden !important; max-width: 100vw !important; } html[data-zbai-mobile="1"] body.zbai-work-preview-adapted > *:not(#${ROOT_ID}):not(#${WORK_PREVIEW_OVERLAY_ID}) { display: none !important; visibility: hidden !important; pointer-events: none !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} * { box-sizing: border-box !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-media-panel { position: relative !important; display: flex !important; align-items: center !important; justify-content: center !important; min-width: 0 !important; min-height: 0 !important; padding: calc(14px + env(safe-area-inset-top, 0px)) 14px 12px !important; overflow: hidden !important; background: #f2f3f7 !important; border-bottom: 1px solid rgba(33, 38, 50, .08) !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-close { position: absolute !important; z-index: 2 !important; top: calc(10px + env(safe-area-inset-top, 0px)) !important; right: 12px !important; width: 38px !important; height: 38px !important; border: 0 !important; border-radius: 19px !important; background: rgba(255, 255, 255, .86) !important; color: #252936 !important; font: 400 26px/36px -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; box-shadow: 0 8px 20px rgba(30, 35, 48, .08) !important; pointer-events: auto !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-image { display: block !important; max-width: calc(100vw - 28px) !important; max-height: calc(58dvh - 44px - env(safe-area-inset-top, 0px)) !important; width: auto !important; height: auto !important; object-fit: contain !important; border-radius: 2px !important; background: #fff !important; box-shadow: 0 12px 30px rgba(31, 36, 49, .08) !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-info-panel { min-width: 0 !important; min-height: 0 !important; display: flex !important; flex-direction: column !important; gap: 12px !important; padding: 14px 14px calc(16px + env(safe-area-inset-bottom, 0px)) !important; overflow: auto !important; background: #f1f3f8 !important; -webkit-overflow-scrolling: touch !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-head { flex: 0 0 auto !important; display: grid !important; grid-template-columns: 42px minmax(0, 1fr) 44px !important; align-items: center !important; gap: 10px !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-avatar { width: 42px !important; height: 42px !important; border-radius: 14px !important; background: radial-gradient(circle at 68% 20%, #fff100 0 26%, transparent 27%), radial-gradient(circle at 33% 72%, #bd64ff 0 30%, transparent 31%), linear-gradient(135deg, #ffef19 0%, #ffcc18 44%, #bf5eff 45%, #8b4dff 100%) !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-head strong { min-width: 0 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; color: #191d29 !important; font: 900 16px/22px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-head button { width: 44px !important; height: 44px !important; border: 0 !important; border-radius: 14px !important; background: #fff !important; color: #151923 !important; font: 900 28px/36px -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; box-shadow: 0 8px 18px rgba(31, 36, 49, .08) !important; pointer-events: auto !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-recreate { flex: 0 0 auto !important; width: 100% !important; min-height: 48px !important; border: 0 !important; border-radius: 16px !important; background: linear-gradient(180deg, #17140f 0%, #050605 100%) !important; color: #fff8e8 !important; font: 950 16px/22px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; box-shadow: 0 14px 30px rgba(0, 0, 0, .18), inset 0 1px 0 rgba(255, 255, 255, .16) !important; pointer-events: auto !important; touch-action: manipulation !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-prompt-card { flex: 0 0 auto !important; padding: 14px !important; border-radius: 16px !important; background: rgba(255, 255, 255, .64) !important; border: 1px solid rgba(31, 36, 49, .06) !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-prompt-card small { display: block !important; margin: 0 0 9px !important; color: #8a91a1 !important; font: 800 13px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-prompt-card p { margin: 0 !important; color: #2b3040 !important; font: 500 15px/23px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; word-break: break-word !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card, html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-params-card { flex: 0 0 auto !important; border-radius: 16px !important; background: rgba(255, 255, 255, .68) !important; border: 1px solid rgba(31, 36, 49, .06) !important; box-shadow: 0 10px 26px rgba(31, 36, 49, .05) !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card[hidden] { display: none !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card button { width: 100% !important; min-height: 94px !important; display: grid !important; grid-template-columns: 64px minmax(0, 1fr) auto !important; align-items: center !important; gap: 11px !important; padding: 10px 12px !important; border: 0 !important; border-radius: 16px !important; background: transparent !important; text-align: left !important; pointer-events: auto !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-reference { display: block !important; width: 64px !important; height: 76px !important; margin: 0 !important; border-radius: 10px !important; object-fit: contain !important; background: #fff !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card span { min-width: 0 !important; display: block !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card small, html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-params-card > small { display: block !important; margin: 0 0 4px !important; color: #8a91a1 !important; font: 800 12px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card strong { display: block !important; color: #242938 !important; font: 900 15px/20px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-source-card em { color: #9d6d1c !important; font: 900 13px/18px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; font-style: normal !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-params-card { padding: 12px !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-tags { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; min-width: 0 !important; overflow: hidden !important; } html[data-zbai-mobile="1"] #${WORK_PREVIEW_OVERLAY_ID} .zbai-work-preview-tags span { max-width: 100% !important; padding: 6px 9px !important; border-radius: 6px !important; background: #e9ecf6 !important; color: #858c9c !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; font: 800 12px/16px "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif !important; } } #${ROOT_ID} { display: none; } `; if (shouldAppend) { const target = document.head || document.documentElement; if (!target) { window.setTimeout(injectStyles, 0); return; } target.appendChild(style); } } })();