User:CCW272006487/XCC Editor Test

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>编辑作品 - 未命名作品 - XMUER Coding Community (XCC)</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }

        html, body {
            width: 100%;
            height: 100%;
            overflow: hidden;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        /* 编辑器容器 */
        .editor-container {
            display: flex;
            flex-direction: column;
            width: 100%;
            height: 100vh;
        }

        .editor-header {
            display: flex;
            align-items: center;
            padding: 0 1.25rem;
            height: 48px;
            background: #1e1e1e;
            color: white;
            border-bottom: 1px solid #333;
            flex-shrink: 0;
            position: relative;
            z-index: 50;
        }

        .logo {
            font-weight: 700;
            font-size: 1.125rem;
            margin-right: 1.5rem;
            color: #4d97ff;
        }

        .project-title {
            flex: 1;
            font-size: 0.875rem;
            opacity: 0.8;
        }

        .actions {
            display: flex;
            gap: 0.5rem;
            position: relative;
        }

        .btn {
            padding: 0.4rem 0.875rem;
            border-radius: 6px;
            font-size: 0.8125rem;
            font-weight: 500;
            cursor: pointer;
            border: none;
            transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
        }

        .btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .btn-save {
            background: #4d97ff;
            color: white;
        }

        .btn-save:hover {
            background: #3b82f6;
        }

        .btn-publish {
            background: #ff8c1a;
            color: white;
        }

        .btn-publish:hover {
            background: #f97316;
        }

        .btn-download,
        .btn-safe {
            display: inline-flex;
            align-items: center;
            gap: 0.3rem;
            background: rgba(255,255,255,0.08);
            color: rgba(255,255,255,0.7);
            border: 1px solid rgba(255,255,255,0.1);
            text-decoration: none;
        }

        .btn-download:hover,
        .btn-safe:hover {
            background: rgba(255,255,255,0.14);
            color: #fff;
        }

        .editor-content {
            flex: 1;
            background: #1e1e1e;
            position: relative;
            contain: layout style paint;
        }

        .editor-content iframe {
            width: 100%;
            height: 100%;
            border: none;
        }

        /* ========== 编辑器加载层 ========== */
        .editor-loading {
            position: absolute;
            inset: 0;
            z-index: 10;
            background: #1e1e1e;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: opacity 0.5s ease;
        }

        .editor-loading.fade-out {
            opacity: 0;
            pointer-events: none;
        }

        /* 加载完成后停止spinner动画,释放GPU */
        .editor-loading.fade-out .loading-spinner {
            animation: none;
        }

        .loading-inner {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 1.5rem;
        }

        .loading-spinner {
            width: 36px;
            height: 36px;
            border: 3px solid rgba(255,255,255,0.1);
            border-top-color: #4d97ff;
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        .loading-text {
            color: rgba(255,255,255,0.6);
            font-size: 0.875rem;
            min-height: 1.5em;
            transition: opacity 0.3s ease;
        }

        .loading-text.swap { opacity: 0; }

        .loading-bar-wrap {
            width: 200px;
            height: 3px;
            background: rgba(255,255,255,0.08);
            border-radius: 2px;
            overflow: hidden;
        }

        .loading-bar {
            height: 100%;
            width: 0%;
            background: #4d97ff;
            border-radius: 2px;
            transition: width 0.8s ease;
        }

        /* ========== 溢出菜单 ========== */
        .btn-more {
            display: none;
            padding: 0.35rem 0.5rem;
            background: rgba(255,255,255,0.08);
            color: rgba(255,255,255,0.7);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 6px;
            position: relative;
        }

        .btn-more:hover {
            background: rgba(255,255,255,0.14);
            color: #fff;
        }

        .more-menu {
            position: absolute;
            top: calc(100% + 6px);
            right: 0;
            min-width: 140px;
            background: rgba(30,30,30,0.85);
            backdrop-filter: blur(20px) saturate(1.8);
            -webkit-backdrop-filter: blur(20px) saturate(1.8);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 10px;
            padding: 0.375rem;
            box-shadow: 0 8px 32px rgba(0,0,0,0.4);
            z-index: 200;
            opacity: 0;
            visibility: hidden;
            transform: translateY(-8px) scale(0.95);
            transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1), visibility 0.2s;
        }

        .more-menu.show {
            opacity: 1;
            visibility: visible;
            transform: translateY(0) scale(1);
        }

        .more-menu-item {
            display: block;
            width: 100%;
            padding: 0.5rem 0.75rem;
            background: none;
            border: none;
            color: rgba(255,255,255,0.8);
            font-size: 0.8125rem;
            border-radius: 6px;
            cursor: pointer;
            text-align: left;
            transition: background 0.12s, color 0.12s;
            text-decoration: none;
            box-sizing: border-box;
        }

        .more-menu-item:hover {
            background: rgba(255,255,255,0.1);
            color: #fff;
        }

        .more-menu-publish { color: #ff8c1a; }
        .more-menu-publish:hover {
            color: #ffb366;
            background: rgba(255,140,26,0.1);
        }

        /* ========== 毛玻璃弹窗系统 ========== */
        .xcc-modal-backdrop {
            position: fixed;
            inset: 0;
            z-index: 9999;
            display: flex;
            align-items: center;
            justify-content: center;
            background: rgba(0,0,0,0);
            pointer-events: none;
            transition: background 0.35s ease, backdrop-filter 0.35s ease, -webkit-backdrop-filter 0.35s ease;
            padding: 1.5rem;
            contain: layout style;
        }

        .xcc-modal-backdrop.show {
            background: rgba(0,0,0,0.25);
            backdrop-filter: blur(12px) saturate(1.5);
            -webkit-backdrop-filter: blur(12px) saturate(1.5);
            pointer-events: auto;
        }

        .xcc-modal {
            background: rgba(255,255,255,0.8);
            backdrop-filter: blur(40px) saturate(1.8);
            -webkit-backdrop-filter: blur(40px) saturate(1.8);
            border: 1px solid rgba(255,255,255,0.4);
            border-radius: 16px;
            padding: 2rem 1.75rem 1.5rem;
            max-width: 380px;
            width: 100%;
            text-align: center;
            box-shadow:
                0 8px 32px rgba(0,0,0,0.08),
                0 2px 8px rgba(0,0,0,0.04),
                inset 0 1px 0 rgba(255,255,255,0.6);
            opacity: 0;
            transform: translateY(16px) scale(0.92);
            transition: opacity 0.35s cubic-bezier(0.32, 0.72, 0, 1), transform 0.4s cubic-bezier(0.32, 0.72, 0, 1);
        }

        .xcc-modal-backdrop.show .xcc-modal {
            opacity: 1;
            transform: translateY(0) scale(1);
        }

        .xcc-modal-icon { margin-bottom: 0.75rem; font-size: 2rem; line-height: 1; }
        .xcc-modal-title { font-size: 1rem; font-weight: 600; color: #111; margin: 0 0 0.5rem; }
        .xcc-modal-message { font-size: 0.8125rem; color: #555; margin: 0 0 1.5rem; line-height: 1.6; word-break: break-all; }
        .xcc-modal-actions { display: flex; gap: 0.625rem; justify-content: center; }

        .xcc-modal-btn {
            padding: 0.5rem 1.25rem;
            border-radius: 8px;
            font-size: 0.8125rem;
            font-weight: 500;
            cursor: pointer;
            border: none;
            transition: background 0.2s ease, box-shadow 0.2s ease;
            min-width: 72px;
        }

        .xcc-modal-btn-primary { background: #4d97ff; color: #fff; }
        .xcc-modal-btn-primary:hover { background: #3b82f6; box-shadow: 0 2px 12px rgba(77,151,255,0.3); }
        .xcc-modal-btn-danger { background: #ef4444; color: #fff; }
        .xcc-modal-btn-danger:hover { background: #dc2626; box-shadow: 0 2px 12px rgba(239,68,68,0.3); }
        .xcc-modal-btn-ghost { background: rgba(0,0,0,0.04); color: #555; border: 1px solid rgba(0,0,0,0.08); }
        .xcc-modal-btn-ghost:hover { background: rgba(0,0,0,0.08); }
        .xcc-modal-btn-warning { background: #ff8c1a; color: #fff; }
        .xcc-modal-btn-warning:hover { background: #f97316; box-shadow: 0 2px 12px rgba(255,140,26,0.3); }

        /* ========== Toast 提示 ========== */
        .xcc-toast-container {
            position: fixed;
            top: 1rem;
            left: 50%;
            transform: translateX(-50%);
            z-index: 10000;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 0.5rem;
            pointer-events: none;
        }

        .xcc-toast {
            background: rgba(30,30,30,0.85);
            backdrop-filter: blur(20px) saturate(1.8);
            -webkit-backdrop-filter: blur(20px) saturate(1.8);
            color: #fff;
            padding: 0.625rem 1.25rem;
            border-radius: 10px;
            font-size: 0.8125rem;
            font-weight: 500;
            box-shadow: 0 4px 20px rgba(0,0,0,0.2);
            pointer-events: auto;
            opacity: 0;
            transform: translateY(-12px) scale(0.9);
            transition: opacity 0.3s cubic-bezier(0.32, 0.72, 0, 1), transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);
            max-width: 90vw;
            text-align: center;
        }

        .xcc-toast.show { opacity: 1; transform: translateY(0) scale(1); }
        .xcc-toast-success { border-left: 3px solid #22c55e; }
        .xcc-toast-error { border-left: 3px solid #ef4444; }
        .xcc-toast-info { border-left: 3px solid #4d97ff; }

        /* ========== 发布全屏页面 ========== */
        .publish-page {
            position: fixed;
            inset: 0;
            background: #fff;
            z-index: 1000;
            display: flex;
            flex-direction: column;
            transform: translateY(100%);
            opacity: 0;
            transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.25s ease;
            pointer-events: none;
            contain: layout style paint;
        }

        .publish-page.active {
            transform: translateY(0);
            opacity: 1;
            pointer-events: auto;
        }

        .publish-page-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 1.5rem;
            height: 56px;
            border-bottom: 1px solid #e5e7eb;
            flex-shrink: 0;
        }

        .publish-page-header h2 { font-size: 1rem; font-weight: 600; color: #111; }

        .btn-back {
            display: flex;
            align-items: center;
            gap: 0.375rem;
            background: none;
            border: none;
            font-size: 0.875rem;
            color: #4d97ff;
            cursor: pointer;
            padding: 0.5rem;
            margin-left: -0.5rem;
            border-radius: 6px;
            transition: background 0.15s;
        }

        .btn-back:hover { background: #f0f5ff; }

        .btn-confirm-publish {
            padding: 0.5rem 1.25rem;
            background: #ff8c1a;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 0.875rem;
            font-weight: 500;
            cursor: pointer;
            transition: background 0.15s;
        }

        .btn-confirm-publish:hover { background: #f97316; }
        .btn-confirm-publish:disabled { opacity: 0.5; cursor: not-allowed; }

        .publish-page-body {
            flex: 1;
            overflow-y: auto;
            padding: 2rem;
            display: flex;
            justify-content: center;
        }

        .publish-form { width: 100%; max-width: 560px; }
        .form-group { margin-bottom: 1.25rem; }
        .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
        .form-label { display: block; font-size: 0.8125rem; font-weight: 500; color: #374151; margin-bottom: 0.5rem; }
        .required { color: #ef4444; }

        .form-input,
        .form-textarea {
            width: 100%;
            padding: 0.625rem 0.875rem;
            border: 1px solid #d1d5db;
            border-radius: 8px;
            font-size: 0.875rem;
            background: #fff;
            transition: border-color 0.15s, box-shadow 0.15s;
        }

        .form-input:focus,
        .form-textarea:focus {
            outline: none;
            border-color: #4d97ff;
            box-shadow: 0 0 0 3px rgba(77, 151, 255, 0.15);
        }

        .form-textarea { resize: vertical; min-height: 80px; }
        .form-hint { display: block; font-size: 0.75rem; color: #9ca3af; margin-top: 0.375rem; }
        .form-hint.is-warn { color: #b45309; font-weight: 500; }

        /* 封面工具行 */
        .thumbnail-tools {
            display: flex;
            align-items: center;
            gap: 0.625rem;
            flex-wrap: wrap;
            margin-top: 0.5rem;
        }

        .btn-stage-shot {
            padding: 0.3125rem 0.75rem;
            background: #fff;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            font-size: 0.8125rem;
            color: #374151;
            cursor: pointer;
            flex-shrink: 0;
            transition: border-color 0.15s, color 0.15s, background 0.15s;
        }

        .btn-stage-shot:hover { border-color: #4d97ff; color: #4d97ff; background: #f8faff; }
        .btn-stage-shot:disabled { opacity: 0.6; cursor: not-allowed; }

        .thumbnail-tip { font-size: 0.75rem; line-height: 1.5; color: #b45309; }
        .thumbnail-tip.is-ok { color: #047857; }

        .thumbnail-upload {
            position: relative;
            width: 100%;
            height: 120px;
            border: 2px dashed #d1d5db;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            overflow: hidden;
            transition: border-color 0.15s, background 0.15s;
        }

        .thumbnail-upload:hover { border-color: #4d97ff; background: #f8faff; }

        .thumbnail-upload input[type="file"] {
            position: absolute;
            inset: 0;
            opacity: 0;
            cursor: pointer;
        }

        .thumbnail-placeholder {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 0.375rem;
            color: #9ca3af;
            font-size: 0.8125rem;
        }

        .thumbnail-preview-img { display: none; width: 100%; height: 100%; object-fit: cover; }
        .thumbnail-preview-img[src] { display: block; }

        .publish-meta {
            display: flex;
            gap: 1.5rem;
            font-size: 0.8125rem;
            color: #6b7280;
            padding: 0.75rem 1rem;
            background: #f9fafb;
            border-radius: 8px;
            margin-bottom: 1rem;
        }

        .form-error {
            display: none;
            background: #fef2f2;
            color: #dc2626;
            padding: 0.75rem 1rem;
            border-radius: 8px;
            font-size: 0.8125rem;
            margin-top: 0.5rem;
        }

        .form-error.show { display: block; }

        /* 团队Logo预览 */
        .team-logo-preview {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(15%);
            display: flex;
            align-items: center;
            pointer-events: none;
        }

        .team-logo-preview img { border: 1px solid #e5e7eb; }

        /* ========== 许可证选择 ========== */
        .license-select-btn {
            display: flex !important;
            align-items: center;
            justify-content: space-between;
            cursor: pointer;
            user-select: none;
        }

        .license-select-btn:hover { border-color: #555 !important; }

        .license-modal {
            display: none;
            position: fixed;
            inset: 0;
            z-index: 10001;
            background: rgba(0, 0, 0, 0.6);
            backdrop-filter: blur(4px);
            justify-content: center;
            align-items: center;
            contain: layout style;
        }

        .license-modal.active { display: flex; }

        .license-modal-content {
            width: 520px;
            max-width: 90vw;
            max-height: 80vh;
            background: #1e1e1e;
            border-radius: 12px;
            border: 1px solid #333;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .license-modal-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 1rem 1.25rem;
            border-bottom: 1px solid #333;
        }

        .license-modal-header h3 { margin: 0; font-size: 1rem; font-weight: 600; color: #e0e0e0; }

        .license-modal-close {
            background: none;
            border: none;
            color: #888;
            cursor: pointer;
            padding: 4px;
            border-radius: 4px;
            display: flex;
            align-items: center;
        }

        .license-modal-close:hover { color: #fff; background: rgba(255,255,255,0.1); }

        .license-modal-body { overflow-y: auto; padding: 0.75rem 1.25rem 1.25rem; }
        .license-group { margin-bottom: 1rem; }
        .license-group:last-child { margin-bottom: 0; }

        .license-group-title {
            font-size: 0.7rem;
            font-weight: 600;
            color: #888;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            padding: 0.5rem 0 0.375rem;
        }

        .license-option {
            padding: 0.625rem 0.75rem;
            border-radius: 8px;
            cursor: pointer;
            transition: background 0.12s;
            margin-bottom: 2px;
        }

        .license-option:hover { background: rgba(255, 255, 255, 0.06); }
        .license-option.selected { background: rgba(77, 151, 255, 0.15); }
        .license-option-name { font-size: 0.875rem; font-weight: 500; color: #e0e0e0; }
        .license-option-desc { font-size: 0.75rem; color: #888; margin-top: 2px; }
        .license-option.selected .license-option-name { color: #4d97ff; }

        /* ========== 保险箱面板 ========== */
        .safe-panel {
            position: fixed;
            inset: 0;
            background: #f5f6f8;
            z-index: 1000;
            display: flex;
            flex-direction: column;
            transform: translateY(100%);
            opacity: 0;
            transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.25s ease;
            pointer-events: none;
            contain: layout style paint;
        }

        .safe-panel.active {
            transform: translateY(0);
            opacity: 1;
            pointer-events: auto;
        }

        .safe-panel-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 1.5rem;
            height: 56px;
            background: #fff;
            border-bottom: 1px solid #e5e7eb;
            flex-shrink: 0;
        }

        .safe-panel-header h2 { font-size: 1rem; font-weight: 600; color: #111; }
        .safe-panel-body { flex: 1; overflow-y: auto; padding: 1.5rem; }
        .safe-panel-content { max-width: 640px; margin: 0 auto; }

        .safe-stats {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 2rem;
            padding: 1.25rem 2rem;
            background: #fff;
            border-radius: 14px;
            border: 1px solid #e5e7eb;
            margin-bottom: 1.25rem;
            box-shadow: 0 1px 3px rgba(0,0,0,0.03);
        }

        .safe-stat { display: flex; align-items: baseline; gap: 0.375rem; }
        .safe-stat-value { font-size: 1.5rem; font-weight: 700; color: #111; font-variant-numeric: tabular-nums; }
        .safe-stat-label { font-size: 0.8125rem; color: #9ca3af; }
        .safe-stat-divider { width: 1px; height: 28px; background: #e5e7eb; }

        .safe-loading {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 1rem;
            padding-top: 5rem;
        }

        .safe-empty {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 0.75rem;
            padding-top: 4rem;
        }

        .safe-empty-icon {
            width: 96px;
            height: 96px;
            background: #fff;
            border-radius: 24px;
            border: 1px solid #e5e7eb;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-bottom: 0.5rem;
        }

        .safe-empty p { font-size: 0.9375rem; font-weight: 500; color: #374151; margin: 0; }
        .safe-empty small { font-size: 0.8125rem; color: #9ca3af; }

        .safe-versions { display: flex; flex-direction: column; gap: 0.625rem; }

        .safe-version-card {
            background: #fff;
            border: 1px solid #e5e7eb;
            border-radius: 14px;
            padding: 1.25rem 1.5rem;
            display: flex;
            align-items: center;
            justify-content: space-between;
            transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
            position: relative;
            overflow: hidden;
        }

        .safe-version-card::before {
            content: '';
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            width: 3px;
            background: #4d97ff;
            border-radius: 0 3px 3px 0;
            opacity: 0;
            transition: opacity 0.2s;
        }

        .safe-version-card:hover {
            border-color: #c7d2fe;
            box-shadow: 0 4px 12px rgba(77,151,255,0.08);
            transform: translateY(-1px);
        }

        .safe-version-card:hover::before { opacity: 1; }
        .safe-version-card.is-latest { border-color: #c7d2fe; background: linear-gradient(135deg, #f8faff 0%, #fff 100%); }
        .safe-version-card.is-latest::before { opacity: 1; background: #22c55e; }

        .safe-version-info { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }

        .safe-version-number {
            font-size: 1.0625rem;
            font-weight: 700;
            color: #111;
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .safe-version-badge {
            display: inline-flex;
            align-items: center;
            gap: 3px;
            padding: 0.15rem 0.5rem;
            background: #dcfce7;
            color: #16a34a;
            font-size: 0.6875rem;
            font-weight: 600;
            border-radius: 9999px;
            line-height: 1;
        }

        .safe-version-meta { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; font-size: 0.8125rem; color: #6b7280; }
        .safe-version-meta-item { display: inline-flex; align-items: center; gap: 0.3rem; }
        .safe-version-meta-item svg { flex-shrink: 0; opacity: 0.5; }

        .safe-version-meta-item.file-size {
            font-variant-numeric: tabular-nums;
            font-family: 'SF Mono', 'Cascadia Code', 'Menlo', monospace;
            font-size: 0.75rem;
            background: #f3f4f6;
            padding: 0.125rem 0.5rem;
            border-radius: 4px;
            color: #374151;
        }

        .safe-version-actions { display: flex; gap: 0.5rem; flex-shrink: 0; margin-left: 1.25rem; }

        .btn-restore {
            padding: 0.5rem 1.125rem;
            background: #fff;
            color: #374151;
            border: 1px solid #d1d5db;
            border-radius: 8px;
            font-size: 0.8125rem;
            font-weight: 500;
            cursor: pointer;
            transition: background 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
            display: inline-flex;
            align-items: center;
            gap: 0.375rem;
        }

        .btn-restore:hover { background: #4d97ff; color: #fff; border-color: #4d97ff; box-shadow: 0 2px 8px rgba(77,151,255,0.3); }
        .btn-restore svg { width: 14px; height: 14px; }

        .btn-download-version {
            padding: 0.5rem 1.125rem;
            background: #fff;
            color: #374151;
            border: 1px solid #d1d5db;
            border-radius: 8px;
            font-size: 0.8125rem;
            font-weight: 500;
            cursor: pointer;
            transition: background 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
            display: inline-flex;
            align-items: center;
            gap: 0.375rem;
            text-decoration: none;
        }

        .btn-download-version:hover { background: #22c55e; color: #fff; border-color: #22c55e; box-shadow: 0 2px 8px rgba(34,197,94,0.3); }
        .btn-download-version svg { width: 14px; height: 14px; }

        /* ========== 5秒不可撤销警告 ========== */
        .restore-warning {
            display: none;
            position: fixed;
            inset: 0;
            z-index: 10002;
            background: rgba(0, 0, 0, 0.6);
            backdrop-filter: blur(4px);
            justify-content: center;
            align-items: center;
            contain: layout style;
        }

        .restore-warning.active { display: flex; }

        .restore-warning-content {
            background: #fff;
            border-radius: 16px;
            padding: 2.5rem 2rem 2rem;
            max-width: 400px;
            width: 90vw;
            text-align: center;
            box-shadow: 0 20px 60px rgba(0,0,0,0.2);
            animation: restoreWarnIn 0.3s ease;
        }

        @keyframes restoreWarnIn {
            from { transform: scale(0.9); opacity: 0; }
            to { transform: scale(1); opacity: 1; }
        }

        .restore-warning-icon { margin-bottom: 1rem; }
        .restore-warning-title { font-size: 1.125rem; font-weight: 700; color: #dc2626; margin: 0 0 0.75rem; }
        .restore-warning-msg { font-size: 0.875rem; color: #374151; line-height: 1.7; margin: 0 0 0.5rem; }
        .restore-warning-msg strong { color: #111; }
        .restore-warning-tip { font-size: 0.75rem; color: #9ca3af; margin: 0 0 1.5rem; }
        .restore-warning-actions { display: flex; gap: 0.75rem; justify-content: center; }
        .restore-warning-actions .xcc-modal-btn { min-width: 110px; padding: 0.625rem 1.5rem; }

        .xcc-modal-btn-danger[disabled] { background: #fca5a5; color: #fff; cursor: not-allowed; opacity: 1; }
        .xcc-modal-btn-danger:not([disabled]) { animation: restoreBtnReady 0.3s ease; }

        @keyframes restoreBtnReady {
            0% { transform: scale(0.95); }
            50% { transform: scale(1.03); }
            100% { transform: scale(1); }
        }

        /* ========== 移动端响应式 ========== */
        @media (max-width: 640px) {
            .editor-header { padding: 0 0.75rem; }
            .logo { margin-right: 0.75rem; }
            .project-title { display: none; }
            .actions .btn:not(.btn-more) { display: none; }
            .actions .btn-more { display: flex; }
            .more-menu { background: rgba(30,30,30,0.92); }
        }
    </style>
</head>
<body>
    <div class="editor-container">
    <div class="editor-header">
        <span class="logo">XCC</span>
        <span class="project-title">未命名作品</span>
        <div class="actions">
                        <button class="btn" id="btn-back" onclick="goBack()">返回</button>
            <button class="btn btn-save" id="btn-save" onclick="saveProject()">保存</button>
            <button class="btn btn-publish" id="btn-publish" onclick="openPublishModal()">发布</button>
            <a class="btn btn-download" href="/editor/gt0tqzprlzam0psmxnmo/sb3" title="下载SB3文件">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
                下载
            </a>
            <button class="btn btn-safe" id="btn-safe" onclick="openSafePanel()" title="版本保险箱">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
                保险箱
            </button>
            <button class="btn btn-more" id="btn-more" onclick="toggleMoreMenu(event)">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg>
            </button>
            <div class="more-menu" id="more-menu">
                <button class="more-menu-item" onclick="goBack(); closeMoreMenu()">返回</button>
                <button class="more-menu-item" onclick="saveProject(); closeMoreMenu()">保存</button>
                <button class="more-menu-item more-menu-publish" onclick="openPublishModal(); closeMoreMenu()">发布</button>
                <a class="more-menu-item" href="/editor/gt0tqzprlzam0psmxnmo/sb3" onclick="closeMoreMenu()">下载SB3</a>
                <button class="more-menu-item" onclick="openSafePanel(); closeMoreMenu()">保险箱</button>
            </div>
                    </div>
    </div>
    
    <div class="editor-content">
                <!-- 自定义加载层 -->
        <div class="editor-loading" id="editor-loading">
            <div class="loading-inner">
                <div class="loading-spinner"></div>
                <div class="loading-text" id="loading-text">正在初始化作品</div>
                <div class="loading-bar-wrap">
                    <div class="loading-bar" id="loading-bar"></div>
                </div>
            </div>
        </div>
        <iframe
            id="scratch-editor"
            src="/xce/zh-CN/editor.html?v=1790502944&project_url=https%3A%2F%2Fcoding.xmuer.online%2Feditor%2Fgt0tqzprlzam0psmxnmo%2Fsb3%2F6bc129dfda4100a2"
            allow="autoplay"
        ></iframe>
    </div>
</div>

<!-- 通用毛玻璃弹窗 -->
<div class="xcc-modal-backdrop" id="xcc-modal-backdrop">
    <div class="xcc-modal" id="xcc-modal">
        <div class="xcc-modal-icon" id="xcc-modal-icon"></div>
        <h3 class="xcc-modal-title" id="xcc-modal-title"></h3>
        <p class="xcc-modal-message" id="xcc-modal-message"></p>
        <div class="xcc-modal-actions" id="xcc-modal-actions"></div>
    </div>
</div>

<!-- Toast 提示 -->
<div class="xcc-toast-container" id="xcc-toast-container"></div>

<!-- 发布全屏页面 -->
<div class="publish-page" id="publish-modal">
    <div class="publish-page-header">
        <button class="btn-back" onclick="closePublishModal()">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
            返回编辑
        </button>
        <h2>发布作品</h2>
        <button class="btn-confirm-publish" id="btn-confirm-publish" onclick="publishProject()">确认发布</button>
    </div>
    <div class="publish-page-body">
        <div class="publish-form">
            <div class="form-group">
                <label class="form-label">作品标题 <span class="required">*</span></label>
                <input type="text" id="publish-title" class="form-input" 
                       value="未命名作品"
                       placeholder="给作品起个名字" maxlength="200">
            </div>
            
            <div class="form-row">
                <div class="form-group">
                    <label class="form-label">版本号 <span class="required">*</span></label>
                    <input type="text" id="publish-version" class="form-input" 
                           value="1.0.0"
                           placeholder="1.0.0">
                    <small class="form-hint">x.y.z 格式,须高于当前版本</small>
                </div>
                <div class="form-group">
                    <label class="form-label">作品封面</label>
                    <div class="thumbnail-upload" id="thumbnail-upload">
                        <input type="file" id="publish-thumbnail" accept="image/*">
                        <div class="thumbnail-placeholder" id="thumbnail-placeholder" >
                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
                            <span>点击上传</span>
                        </div>
                        <img id="preview-img" class="thumbnail-preview-img" >
                                            </div>
                    <div class="thumbnail-tools" id="thumbnail-tools" data-has-thumb="0">
                        <button type="button" class="btn-stage-shot" id="btn-stage-shot">从舞台截图</button>
                        <span class="thumbnail-tip" id="thumbnail-tip">
                                                        没封面的作品会被推荐降权,很难被刷到,建议截一张
                                                    </span>
                    </div>
                </div>
            </div>
            
            <div class="form-group">
                <label class="form-label">作品描述</label>
                <textarea id="publish-description" class="form-textarea" rows="3" 
                          placeholder="介绍一下你的作品..."></textarea>
            </div>
            
            <div class="form-group">
                <label class="form-label">更新日志<span class="required" id="changelog-required" style="display:none"> *</span></label>
                <textarea id="publish-changelog" class="form-textarea" rows="3" 
                          placeholder="本次更新了什么...(可选)"></textarea>
                <small class="form-hint" id="changelog-hint">改了许可证必须写清楚;换了团队会自动记一笔</small>
            </div>
            
            <div class="form-group">
                <label class="form-label">所属团队</label>
                <select id="publish-team" class="form-input" data-current="0">
                    <option value="0">不绑定团队</option>
                                    </select>
                <small class="form-hint">只能绑定自己所在的团队;更换团队会自动记进本次更新日志</small>
            </div>

            <div class="form-group">
                <label class="form-label">许可证</label>
                <input type="hidden" id="publish-license" value="all-rights-reserved">
                <button type="button" class="form-input license-select-btn" id="license-select-btn" onclick="openLicenseModal()">
                    <span id="license-selected-name">许可证加载中...</span>
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
                </button>
                <small class="form-hint">选择作品的发布许可证,用户可在作品页查看许可证详情</small>
            </div>
            
            <div class="publish-meta">
                <span>剩余发布次数: <strong>10</strong>/10</span>
                            </div>
            
            <div class="form-error" id="publish-error"></div>
        </div>
    </div>
</div>

<!-- 许可证选择弹窗 -->
<div class="license-modal" id="license-modal" onclick="if(event.target===this)closeLicenseModal()">
    <div class="license-modal-content">
        <div class="license-modal-header">
            <h3>选择许可证</h3>
            <button class="license-modal-close" onclick="closeLicenseModal()">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
            </button>
        </div>
        <div class="license-modal-body">
            <div class="license-group">
                <div class="license-group-title">默认</div>
                <div class="license-option" data-slug="all-rights-reserved" onclick="selectLicense(this)">
                    <div class="license-option-name">All Rights Reserved</div>
                    <div class="license-option-desc">保留所有权利,未经许可不得使用</div>
                </div>
            </div>
            <div class="license-group">
                <div class="license-group-title">Creative Commons</div>
                <div class="license-option" data-slug="cc-by-4.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY 4.0</div>
                    <div class="license-option-desc">署名 — 只要注明原作者即可自由使用</div>
                </div>
                <div class="license-option" data-slug="cc-by-sa-4.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY-SA 4.0</div>
                    <div class="license-option-desc">署名-相同方式共享</div>
                </div>
                <div class="license-option" data-slug="cc-by-nc-4.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY-NC 4.0</div>
                    <div class="license-option-desc">署名-非商业性使用</div>
                </div>
                <div class="license-option" data-slug="cc-by-nc-sa-4.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY-NC-SA 4.0</div>
                    <div class="license-option-desc">署名-非商业性使用-相同方式共享</div>
                </div>
                <div class="license-option" data-slug="cc0-1.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC0 1.0</div>
                    <div class="license-option-desc">公有领域 — 放弃所有权利</div>
                </div>
            </div>
            <div class="license-group">
                <div class="license-group-title">Creative Commons 旧版本</div>
                <div class="license-option" data-slug="cc-by-sa-3.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY-SA 3.0</div>
                    <div class="license-option-desc">署名-相同方式共享,未本地化旧版</div>
                </div>
                <div class="license-option" data-slug="cc-by-sa-2.0" onclick="selectLicense(this)">
                    <div class="license-option-name">CC BY-SA 2.0</div>
                    <div class="license-option-desc">署名-相同方式共享,Scratch 官网 2026 年前的旧许可</div>
                </div>
            </div>
            <div class="license-group">
                <div class="license-group-title">代码许可证</div>
                <div class="license-option" data-slug="mit" onclick="selectLicense(this)">
                    <div class="license-option-name">MIT License</div>
                    <div class="license-option-desc">极度宽松,只要保留声明即可做任何事</div>
                </div>
                <div class="license-option" data-slug="unlicense" onclick="selectLicense(this)">
                    <div class="license-option-name">The Unlicense</div>
                    <div class="license-option-desc">公共领域,完全无限制</div>
                </div>
                <div class="license-option" data-slug="bsd-2-clause" onclick="selectLicense(this)">
                    <div class="license-option-name">BSD 2-Clause</div>
                    <div class="license-option-desc">简化许可证,仅要求保留版权声明</div>
                </div>
                <div class="license-option" data-slug="mpl-2.0" onclick="selectLicense(this)">
                    <div class="license-option-name">MPL 2.0</div>
                    <div class="license-option-desc">弱Copyleft,修改的文件必须开源</div>
                </div>
                <div class="license-option" data-slug="gpl-2.0" onclick="selectLicense(this)">
                    <div class="license-option-name">GPL v2.0</div>
                    <div class="license-option-desc">强Copyleft,衍生作品必须以相同许可证开源</div>
                </div>
                <div class="license-option" data-slug="gpl-3.0" onclick="selectLicense(this)">
                    <div class="license-option-name">GPL v3.0</div>
                    <div class="license-option-desc">强Copyleft,含反Tivo化条款</div>
                </div>
            </div>
            <div class="license-group">
                <div class="license-group-title">CaelLab</div>
                <div class="license-option" data-slug="caellab-bysa-code-v2" onclick="selectLicense(this)">
                    <div class="license-option-name">CaelLab BY-SA Code 2.0</div>
                    <div class="license-option-desc">代码/网站/数字作品的Copyleft许可证第 2 版,新作品建议选这个</div>
                </div>
                <div class="license-option" data-slug="caellab-bysa-code" onclick="selectLicense(this)">
                    <div class="license-option-name">CaelLab BY-SA Code</div>
                    <div class="license-option-desc">适用于代码、网站和数字作品的Copyleft许可证(旧版)</div>
                </div>
                <div class="license-option" data-slug="caellab-bysa-content" onclick="selectLicense(this)">
                    <div class="license-option-name">CaelLab BY-SA Content</div>
                    <div class="license-option-desc">适用于文章、Wiki、文档和创意内容的Copyleft许可证</div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- 保险箱面板 -->
<div class="safe-panel" id="safe-panel">
    <div class="safe-panel-header">
        <button class="btn-back" onclick="closeSafePanel()">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
            返回编辑
        </button>
        <h2>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-3px;margin-right:6px"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
            版本保险箱
        </h2>
        <div style="width:120px"></div>
    </div>
    <div class="safe-panel-body">
        <div class="safe-panel-content">
            <div class="safe-stats" id="safe-stats" style="display:none">
                <div class="safe-stat">
                    <span class="safe-stat-value" id="safe-stat-count">0</span>
                    <span class="safe-stat-label">个版本</span>
                </div>
                <div class="safe-stat-divider"></div>
                <div class="safe-stat">
                    <span class="safe-stat-value" id="safe-stat-size">0</span>
                    <span class="safe-stat-label">总大小</span>
                </div>
            </div>
            <div class="safe-loading" id="safe-loading">
                <div class="loading-spinner"></div>
                <div style="color:#9ca3af;font-size:0.875rem">加载版本列表...</div>
            </div>
            <div class="safe-empty" id="safe-empty" style="display:none">
                <div class="safe-empty-icon">
                    <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#d1d5db" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
                </div>
                <p>还没有发布过任何版本</p>
                <small>发布作品后,所有版本都会出现在这里</small>
            </div>
            <div class="safe-versions" id="safe-versions" style="display:none"></div>
        </div>
    </div>
</div>

<!-- 5秒不可撤销警告弹窗 -->
<div class="restore-warning" id="restore-warning" onclick="if(event.target===this)cancelRestore()">
    <div class="restore-warning-content">
        <div class="restore-warning-icon">
            <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
        </div>
        <h3 class="restore-warning-title">此操作不可撤销</h3>
        <p class="restore-warning-msg">
            即将还原到版本 <strong id="restore-target-version"></strong><br>
            当前编辑器中的<strong>未保存内容将被覆盖</strong>,且无法恢复。
        </p>
        <p class="restore-warning-tip">请确保已保存当前进度</p>
        <div class="restore-warning-actions">
            <button class="xcc-modal-btn xcc-modal-btn-ghost" onclick="cancelRestore()">取消</button>
            <button class="xcc-modal-btn xcc-modal-btn-danger" id="restore-confirm-btn" disabled>
                <span id="restore-btn-text">请等待 5 秒</span>
            </button>
        </div>
    </div>
</div>

<script>
// 自动为所有fetch请求添加 X-Requested-With 头,配合后端 isAjax() 检测
(function() {
    var _fetch = window.fetch;
    window.fetch = function(url, opts) {
        opts = opts || {};
        opts.headers = opts.headers || {};
        if (opts.headers instanceof Headers) {
            opts.headers.set('X-Requested-With', 'XMLHttpRequest');
        } else if (typeof opts.headers === 'object') {
            opts.headers['X-Requested-With'] = 'XMLHttpRequest';
        }
        return _fetch.call(this, url, opts);
    };
})();

var projectId = "gt0tqzprlzam0psmxnmo";
var isAuthor = true;
var editorFrame = document.getElementById('scratch-editor');
var CSRF_TOKEN = 'c303b26af183145f919fcb903e338f73272289a4bba1fde7f31e8d21fbda0a0c';

// ========== 毛玻璃弹窗系统 ==========
var XCC = (function() {
    var backdrop = document.getElementById('xcc-modal-backdrop');
    var iconEl = document.getElementById('xcc-modal-icon');
    var titleEl = document.getElementById('xcc-modal-title');
    var msgEl = document.getElementById('xcc-modal-message');
    var actionsEl = document.getElementById('xcc-modal-actions');
    var toastContainer = document.getElementById('xcc-toast-container');

    function closeModal() {
        backdrop.classList.remove('show');
        setTimeout(function() { actionsEl.innerHTML = ''; }, 350);
    }

    backdrop.addEventListener('click', function(e) {
        if (e.target === backdrop) closeModal();
    });

    return {
        alert: function(title, message, type) {
            type = type || 'info';
            var icons = { info: '\u2139', success: '\u2713', error: '\u2715', warning: '\u26A0' };
            iconEl.textContent = icons[type] || icons.info;
            titleEl.textContent = title;
            msgEl.textContent = message || '';
            actionsEl.innerHTML = '<button class="xcc-modal-btn xcc-modal-btn-primary" onclick="XCC._close()">确定</button>';
            backdrop.classList.add('show');
        },

        confirm: function(opts) {
            var icons = { info: '\u2139', success: '\u2713', error: '\u2715', warning: '\u26A0', danger: '!' };
            iconEl.textContent = icons[opts.type] || '?';
            titleEl.textContent = opts.title || '确认操作';
            msgEl.textContent = opts.message || '';
            var cancelBtn = '<button class="xcc-modal-btn xcc-modal-btn-ghost" onclick="XCC._close()">' + (opts.cancelText || '取消') + '</button>';
            var confirmClass = opts.danger ? 'xcc-modal-btn-danger' : (opts.warning ? 'xcc-modal-btn-warning' : 'xcc-modal-btn-primary');
            var confirmBtn = '<button class="xcc-modal-btn ' + confirmClass + '" id="xcc-confirm-btn">' + (opts.confirmText || '确定') + '</button>';
            actionsEl.innerHTML = cancelBtn + confirmBtn;
            document.getElementById('xcc-confirm-btn').onclick = function() {
                closeModal();
                if (opts.onConfirm) opts.onConfirm();
            };
            backdrop.classList.add('show');
        },

        _close: closeModal,

        toast: function(message, type, duration) {
            type = type || 'info';
            duration = duration || 2500;
            var toast = document.createElement('div');
            toast.className = 'xcc-toast xcc-toast-' + type;
            toast.textContent = message;
            toastContainer.appendChild(toast);
            requestAnimationFrame(function() { toast.classList.add('show'); });
            setTimeout(function() {
                toast.classList.remove('show');
                setTimeout(function() { toast.remove(); }, 350);
            }, duration);
        }
    };
})();

// ========== 溢出菜单 ==========
function toggleMoreMenu(e) {
    e.stopPropagation();
    var menu = document.getElementById('more-menu');
    var isOpen = menu.classList.contains('show');
    closeMoreMenu();
    if (!isOpen) menu.classList.add('show');
}

function closeMoreMenu() {
    document.getElementById('more-menu').classList.remove('show');
}

document.addEventListener('click', function(e) {
    if (!e.target.closest('#btn-more') && !e.target.closest('#more-menu')) {
        closeMoreMenu();
    }
});

// ========== 编辑器加载动画 ==========
(function() {
    var loadingEl = document.getElementById('editor-loading');
    var textEl = document.getElementById('loading-text');
    var barEl = document.getElementById('loading-bar');

    var steps = [
        { text: '正在初始化作品',                progress: 8  },
        { text: '申请作品基础属性',              progress: 15 },
        { text: '拉取作品数据中',                progress: 22 },
        { text: '尝试重新取得令牌',              progress: 30 },
        { text: '正在整理回调信息',              progress: 38 },
        { text: '加载编辑器内核',                progress: 48 },
        { text: '申请扩展信息并组装上下文',       progress: 56 },
        { text: '校验作品完整性',                progress: 64 },
        { text: '构建运行时依赖树',              progress: 72 },
        { text: '挂载积木渲染管线',              progress: 80 },
        { text: '初始化舞台上下文',              progress: 88 },
        { text: '即将就绪',                      progress: 95 },
    ];

    var current = 0;
    var editorReady = false;

    function showStep(index) {
        if (index >= steps.length) return;
        textEl.classList.add('swap');
        setTimeout(function() {
            textEl.textContent = steps[index].text;
            barEl.style.width = steps[index].progress + '%';
            textEl.classList.remove('swap');
        }, 300);
    }

    showStep(0);
    var timer = setInterval(function() {
        current++;
        if (current < steps.length) {
            showStep(current);
        } else {
            clearInterval(timer);
            timer = null;
        }
    }, 1500);

    function dismiss() {
        if (editorReady) return;
        editorReady = true;
        if (timer) { clearInterval(timer); timer = null; }
        barEl.style.width = '100%';
        setTimeout(function() {
            loadingEl.classList.add('fade-out');
            // 淡出完成后彻底移除DOM,释放spinner动画的GPU合成层
            setTimeout(function() { loadingEl.remove(); }, 500);
        }, 400);
    }

    editorFrame.addEventListener('load', function() {
        setTimeout(dismiss, 1500);
    });

    // 保底:最长10秒后无论如何消失
    setTimeout(dismiss, 10000);
})();

// ========== 离开页面提醒 ==========
var hasUnsavedChanges = isAuthor;
window.addEventListener('beforeunload', function(e) {
    if (hasUnsavedChanges) {
        e.preventDefault();
        e.returnValue = '';
    }
});

// ========== 返回上一页(二次确认) ==========
function goBack() {
    if (hasUnsavedChanges) {
        XCC.confirm({
            title: '确认离开',
            message: '作品尚未保存,确定要离开编辑器吗?未保存的更改将会丢失。',
            type: 'warning',
            warning: true,
            confirmText: '确认离开',
            cancelText: '继续编辑',
            onConfirm: function() {
                hasUnsavedChanges = false;
                history.back();
            }
        });
    } else {
        history.back();
    }
}

// ========== 辅助函数 ==========
function base64ToBlob(base64) {
    var binaryStr = atob(base64);
    var len = binaryStr.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
        bytes[i] = binaryStr.charCodeAt(i);
    }
    return new Blob([bytes]);
}

// 无DOM分配的HTML转义(比createElement快约10倍)
var _escapeMap = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
function escapeHtml(str) {
    return str.replace(/[&<>"']/g, function(c) { return _escapeMap[c]; });
}

function formatFileSize(bytes) {
    if (bytes < 1024) return bytes + ' B';
    return (bytes / 1024).toFixed(1) + ' KB';
}

function formatTime(str) {
    var d = new Date(str.replace(' ', 'T') + 'Z');
    if (isNaN(d.getTime())) return str;
    var diff = Date.now() - d;
    if (diff < 60000) return '刚刚';
    if (diff < 3600000) return Math.floor(diff / 60000) + ' 分钟前';
    if (diff < 86400000) return Math.floor(diff / 3600000) + ' 小时前';
    var month = d.getMonth() + 1;
    var day = d.getDate();
    var hours = String(d.getHours()).padStart(2, '0');
    var mins = String(d.getMinutes()).padStart(2, '0');
    return month + '月' + day + '日 ' + hours + ':' + mins;
}

// ========== 从编辑器获取SB3数据(统一复用) ==========
function getEditorData() {
    return new Promise(function(resolve, reject) {
        var timeout = setTimeout(function() {
            window.removeEventListener('message', handler);
            reject(new Error('获取数据超时(60秒)'));
        }, 60000);

        function handler(e) {
            if (e.data && e.data.type === 'save-complete') {
                clearTimeout(timeout);
                window.removeEventListener('message', handler);
                resolve(e.data.data);
            } else if (e.data && e.data.type === 'save-error') {
                clearTimeout(timeout);
                window.removeEventListener('message', handler);
                reject(new Error(e.data.error || '编辑器导出失败'));
            }
        }

        window.addEventListener('message', handler);
        editorFrame.contentWindow.postMessage({type: 'save'}, '*');
    });
}

// ========== 保存项目 ==========
async function saveProject() {
    var btn = document.getElementById('btn-save');
    btn.textContent = '保存中...';
    btn.disabled = true;

    try {
        var sb3Data = await getEditorData();
        var blob = base64ToBlob(sb3Data);
        var formData = new FormData();
        formData.append('sb3_file', blob, 'project.sb3');
        formData.append('_token', CSRF_TOKEN);

        var response = await fetch('/editor/' + projectId + '/save', {
            method: 'POST',
            body: formData
        });

        var text = await response.text();
        var result;
        try { result = JSON.parse(text); } catch (e) {
            throw new Error('服务器返回了非JSON响应,请检查登录状态或刷新页面重试');
        }

        if (result.success) {
            hasUnsavedChanges = false;
            btn.textContent = '已保存';
            XCC.toast('作品已保存', 'success');
            setTimeout(function() { btn.textContent = '保存'; }, 2000);
        } else {
            XCC.alert('保存失败', result.error || result.message || '未知错误', 'error');
            btn.textContent = '保存';
        }
    } catch (err) {
        XCC.alert('保存失败', err.message, 'error');
        btn.textContent = '保存';
    } finally {
        btn.disabled = false;
    }
}

// ========== 封面预览 ==========
document.getElementById('publish-thumbnail').addEventListener('change', function(e) {
    var file = e.target.files[0];
    if (file) {
        var reader = new FileReader();
        reader.onload = function(ev) {
            document.getElementById('preview-img').src = ev.target.result;
            document.getElementById('thumbnail-placeholder').style.display = 'none';
        };
        reader.readAsDataURL(file);
    }
});

// ========== 舞台截图(发布封面) ==========
var stageShotBlob = null;

(function initStageShot() {
    var btn = document.getElementById('btn-stage-shot');
    var tools = document.getElementById('thumbnail-tools');
    var tip = document.getElementById('thumbnail-tip');
    var previewUrl = null;
    var autoDone = false;

    function setTip(text, ok) {
        if (!tip) return;
        tip.textContent = text;
        tip.classList.toggle('is-ok', !!ok);
    }

    function innerWindow() {
        try { return editorFrame.contentWindow; } catch (e) { return null; }
    }

    function requestSnapshot() {
        return new Promise(function(resolve) {
            var win = innerWindow();
            var renderer = win && win.vm && win.vm.renderer;
            if (!renderer || typeof renderer.requestSnapshot !== 'function') {
                resolve(null);
                return;
            }
            var settled = false;
            var timer = setTimeout(function() {
                if (!settled) { settled = true; resolve(null); }
            }, 2500);
            try {
                renderer.requestSnapshot(function(dataURI) {
                    if (settled) return;
                    settled = true;
                    clearTimeout(timer);
                    resolve(dataURI || null);
                });
            } catch (e) {
                clearTimeout(timer);
                if (!settled) { settled = true; resolve(null); }
            }
        });
    }

    function readCanvasDirectly() {
        var doc;
        try { doc = editorFrame.contentDocument; } catch (e) { return null; }
        if (!doc) return null;
        var list = doc.querySelectorAll('canvas');
        for (var i = 0; i < list.length; i++) {
            if (list[i].width >= 200 && list[i].height >= 150) {
                try { return list[i].toDataURL('image/png'); } catch (e) { return null; }
            }
        }
        return null;
    }

    function loadImage(src) {
        return new Promise(function(resolve) {
            var img = new Image();
            img.onload = function() { resolve(img); };
            img.onerror = function() { resolve(null); };
            img.src = src;
        });
    }

    function toCoverBlob(img) {
        return new Promise(function(resolve) {
            if (!img) { resolve(null); return; }
            var W = 480, H = 360;
            var out = document.createElement('canvas');
            out.width = W;
            out.height = H;
            var ctx = out.getContext('2d');
            ctx.fillStyle = '#ffffff';
            ctx.fillRect(0, 0, W, H);
            var scale = Math.max(W / img.width, H / img.height);
            var dw = img.width * scale;
            var dh = img.height * scale;
            ctx.drawImage(img, (W - dw) / 2, (H - dh) / 2, dw, dh);
            out.toBlob(function(blob) { resolve(blob); }, 'image/png');
        });
    }

    function capture() {
        return requestSnapshot()
            .then(function(uri) { return uri || readCanvasDirectly(); })
            .then(loadImage)
            .then(toCoverBlob)
            .then(function(blob) { return (blob && blob.size > 2000) ? blob : null; });
    }

    function applyShot(blob, tipText) {
        stageShotBlob = blob;
        if (previewUrl) URL.revokeObjectURL(previewUrl);
        previewUrl = URL.createObjectURL(blob);
        document.getElementById('preview-img').src = previewUrl;
        document.getElementById('thumbnail-placeholder').style.display = 'none';
        if (btn) btn.textContent = '重新截图';
        setTip(tipText, true);
    }

    function userPickedFile() {
        var input = document.getElementById('publish-thumbnail');
        return !!(input && input.files && input.files.length);
    }

    if (btn) {
        btn.addEventListener('click', function() {
            btn.disabled = true;
            btn.textContent = '截图中...';
            capture().then(function(blob) {
                btn.disabled = false;
                if (!blob) {
                    btn.textContent = '从舞台截图';
                    XCC.toast('没抓到舞台画面,稍等一下再试,或手动上传封面', 'warning');
                    return;
                }
                applyShot(blob, '已截取舞台画面,发布时保存');
            });
        });
    }

    // 作品还没有封面:自动截一张
    if (tools && tools.getAttribute('data-has-thumb') === '0') {
        var attempts = 0;
        var tryAuto = function() {
            if (autoDone || userPickedFile()) return;
            attempts++;
            capture().then(function(blob) {
                if (autoDone || userPickedFile()) return;
                if (blob) {
                    autoDone = true;
                    applyShot(blob, '已自动截取舞台画面,发布时保存为封面');
                    return;
                }
                if (attempts < 4) setTimeout(tryAuto, 2500);
            });
        };
        setTimeout(tryAuto, 4000);
    }
})();

// ========== 发布页:更新日志强制提示 ==========
var hasPublished = false;
var initialLicense = document.getElementById('publish-license').value;
var initialTeamId = parseInt(document.getElementById('publish-team').getAttribute('data-current'), 10) || 0;

function publishChanges() {
    var teamSelect = document.getElementById('publish-team');
    return {
        licenseChanged: hasPublished && document.getElementById('publish-license').value !== initialLicense,
        teamChanged: !!teamSelect && (parseInt(teamSelect.value, 10) || 0) !== initialTeamId
    };
}

function updateChangelogHint() {
    var changed = publishChanges();
    var req = document.getElementById('changelog-required');
    var hint = document.getElementById('changelog-hint');
    if (!req || !hint) return;

    if (changed.licenseChanged) {
        req.style.display = 'inline';
        hint.textContent = '你更换了许可证,必须在这里写清楚改了什么';
        hint.classList.add('is-warn');
    } else {
        req.style.display = 'none';
        hint.textContent = changed.teamChanged
            ? '团队变更会自动记进本次更新日志'
            : '改了许可证必须写清楚;换了团队会自动记一笔';
        hint.classList.remove('is-warn');
    }
}

// ========== 加载团队列表 ==========
(function loadTeams() {
    var teamSelect = document.getElementById('publish-team');
    if (!teamSelect) return;

    var currentTeamId = parseInt(teamSelect.getAttribute('data-current'), 10) || 0;

    var logoPreview = document.createElement('div');
    logoPreview.id = 'team-logo-preview';
    logoPreview.className = 'team-logo-preview';
    logoPreview.style.display = 'none';
    teamSelect.parentNode.style.position = 'relative';
    teamSelect.parentNode.appendChild(logoPreview);

    function syncTeamLogo() {
        var selected = teamSelect.options[teamSelect.selectedIndex];
        var logoUrl = selected ? selected.getAttribute('data-logo') : null;
        if (logoUrl) {
            logoPreview.innerHTML = '<img src="' + escapeHtml(logoUrl) + '" alt="团队Logo" style="width:24px;height:24px;border-radius:4px;object-fit:cover;">';
            logoPreview.style.display = 'flex';
        } else {
            logoPreview.style.display = 'none';
        }
    }

    function findOption(teamId) {
        for (var i = 0; i < teamSelect.options.length; i++) {
            if (teamSelect.options[i].value === String(teamId)) return teamSelect.options[i];
        }
        return null;
    }

    fetch('/api/team/my-teams')
        .then(function(res) { return res.json(); })
        .then(function(result) {
            (result.teams || []).forEach(function(team) {
                var existing = findOption(team.id);
                if (existing) {
                    if (team.logo && !existing.getAttribute('data-logo')) {
                        existing.setAttribute('data-logo', team.logo);
                    }
                    return;
                }
                var option = document.createElement('option');
                option.value = team.id;
                option.textContent = team.name;
                if (team.logo) option.setAttribute('data-logo', team.logo);
                teamSelect.appendChild(option);
            });

            if (currentTeamId > 0) teamSelect.value = String(currentTeamId);
            syncTeamLogo();
            updateChangelogHint();
        })
        .catch(function(err) {
            console.error('加载团队列表失败:', err);
        });

    teamSelect.addEventListener('change', function() {
        syncTeamLogo();
        updateChangelogHint();
    });
})();

// ========== 许可证名称映射 ==========
var licenseNames = {
    'all-rights-reserved': 'All Rights Reserved',
    'cc-by-4.0': 'CC BY 4.0',
    'cc-by-sa-4.0': 'CC BY-SA 4.0',
    'cc-by-nc-4.0': 'CC BY-NC 4.0',
    'cc-by-nc-sa-4.0': 'CC BY-NC-SA 4.0',
    'cc0-1.0': 'CC0 1.0',
    'cc-by-sa-3.0': 'CC BY-SA 3.0',
    'cc-by-sa-2.0': 'CC BY-SA 2.0',
    'mit': 'MIT License',
    'unlicense': 'The Unlicense',
    'bsd-2-clause': 'BSD 2-Clause',
    'mpl-2.0': 'MPL 2.0',
    'gpl-2.0': 'GPL v2.0',
    'gpl-3.0': 'GPL v3.0',
    'caellab-bysa-code-v2': 'CaelLab BY-SA Code 2.0',
    'caellab-bysa-code': 'CaelLab BY-SA Code',
    'caellab-bysa-content': 'CaelLab BY-SA Content'
};

function initLicenseButton() {
    var slug = document.getElementById('publish-license').value || 'all-rights-reserved';
    document.getElementById('license-selected-name').textContent = licenseNames[slug] || slug;
    document.querySelectorAll('#license-modal .license-option').forEach(function(el) {
        el.classList.toggle('selected', el.dataset.slug === slug);
    });
}

// ========== 发布页开关 ==========
function openPublishModal() {
    document.getElementById('publish-modal').classList.add('active');
    initLicenseButton();
    updateChangelogHint();
}

function closePublishModal() {
    document.getElementById('publish-modal').classList.remove('active');
    document.getElementById('publish-error').classList.remove('show');
}

// ========== 许可证弹窗 ==========
function openLicenseModal() {
    initLicenseButton();
    document.getElementById('license-modal').classList.add('active');
}

function closeLicenseModal() {
    document.getElementById('license-modal').classList.remove('active');
}

function selectLicense(el) {
    var slug = el.dataset.slug;
    document.getElementById('publish-license').value = slug;
    document.getElementById('license-selected-name').textContent = licenseNames[slug] || slug;
    closeLicenseModal();
    updateChangelogHint();
}

// ========== 发布项目 ==========
async function publishProject() {
    var btn = document.getElementById('btn-confirm-publish');
    var errorDiv = document.getElementById('publish-error');

    var title = document.getElementById('publish-title').value.trim();
    var version = document.getElementById('publish-version').value.trim();
    var description = document.getElementById('publish-description').value.trim();
    var changelog = document.getElementById('publish-changelog').value.trim();
    var thumbnailFile = document.getElementById('publish-thumbnail').files[0] || stageShotBlob;

    if (!title) {
        errorDiv.textContent = '请输入作品标题';
        errorDiv.classList.add('show');
        return;
    }

    if (!version) {
        errorDiv.textContent = '请输入版本号';
        errorDiv.classList.add('show');
        return;
    }

    if (publishChanges().licenseChanged && !changelog) {
        updateChangelogHint();
        errorDiv.textContent = '你更换了许可证,必须在更新日志里说明改了什么';
        errorDiv.classList.add('show');
        return;
    }

    btn.textContent = '发布中...';
    btn.disabled = true;
    document.getElementById('btn-save').disabled = true;
    errorDiv.classList.remove('show');

    try {
        // 先保存当前编辑器内容
        var sb3Data = await getEditorData();
        var blob = base64ToBlob(sb3Data);
        var saveFormData = new FormData();
        saveFormData.append('sb3_file', blob, 'project.sb3');
        saveFormData.append('_token', CSRF_TOKEN);
        var saveResponse = await fetch('/editor/' + projectId + '/save', {
            method: 'POST',
            body: saveFormData
        });
        var saveResult = await saveResponse.json();
        if (!saveResult.success) {
            throw new Error('保存失败:' + (saveResult.error || '未知错误'));
        }

        var thumbnailUrl = null;
        if (thumbnailFile) {
            var thumbnailFormData = new FormData();
            thumbnailFormData.append('thumbnail', thumbnailFile, thumbnailFile.name || 'stage-shot.png');
            thumbnailFormData.append('_token', CSRF_TOKEN);

            var thumbnailResponse = await fetch('/editor/' + projectId + '/thumbnail', {
                method: 'POST',
                body: thumbnailFormData
            });

            var thumbText = await thumbnailResponse.text();
            var thumbnailResult;
            try { thumbnailResult = JSON.parse(thumbText); } catch (e) {
                throw new Error('封面上传失败:服务器返回了非JSON响应');
            }
            if (thumbnailResult.success) {
                thumbnailUrl = thumbnailResult.thumbnail_url;
            } else {
                throw new Error(thumbnailResult.error || thumbnailResult.message || '封面上传失败');
            }
        }

        var formData = new FormData();
        formData.append('_token', CSRF_TOKEN);
        formData.append('title', title);
        formData.append('version', version);
        formData.append('description', description);
        formData.append('changelog', changelog);
        var teamSelect = document.getElementById('publish-team');
        if (teamSelect) formData.append('team_id', teamSelect.value);
        formData.append('license', document.getElementById('publish-license').value);
        if (thumbnailUrl) formData.append('thumbnail', thumbnailUrl);

        var response = await fetch('/editor/' + projectId + '/publish', {
            method: 'POST',
            body: formData
        });

        var text = await response.text();
        var result;
        try { result = JSON.parse(text); } catch (e) {
            throw new Error('服务器返回了非JSON响应,请检查登录状态或刷新页面重试');
        }

        if (result.success) {
            hasUnsavedChanges = false;
            XCC.toast('发布成功!', 'success');
            setTimeout(function() { window.location.href = '/project/' + projectId; }, 800);
        } else {
            errorDiv.textContent = result.error || result.message || '发布失败';
            if (result.reset_time) {
                errorDiv.textContent += '(' + Math.ceil(result.reset_time / 60) + '分钟后可重试)';
            }
            errorDiv.classList.add('show');
        }
    } catch (err) {
        errorDiv.textContent = '发布失败: ' + err.message;
        errorDiv.classList.add('show');
    } finally {
        btn.textContent = '确认发布';
        btn.disabled = false;
        document.getElementById('btn-save').disabled = false;
    }
}

// ========== 保险箱功能 ==========
var _restoreTimer = null;
var _pendingRestoreVersion = null;

function openSafePanel() {
    var panel = document.getElementById('safe-panel');
    var loading = document.getElementById('safe-loading');
    var empty = document.getElementById('safe-empty');
    var versions = document.getElementById('safe-versions');
    var stats = document.getElementById('safe-stats');

    panel.classList.add('active');
    loading.style.display = 'flex';
    empty.style.display = 'none';
    versions.style.display = 'none';
    stats.style.display = 'none';

    fetch('/editor/' + projectId + '/versions')
        .then(function(res) { return res.json(); })
        .then(function(result) {
            loading.style.display = 'none';
            if (!result.success || !result.versions || result.versions.length === 0) {
                empty.style.display = 'flex';
                return;
            }
            renderVersions(result.versions);
        })
        .catch(function() {
            loading.style.display = 'none';
            XCC.alert('加载失败', '无法获取版本列表,请刷新重试', 'error');
        });
}

function renderVersions(list) {
    var container = document.getElementById('safe-versions');
    container.innerHTML = '';

    var totalSize = list.reduce(function(sum, v) { return sum + v.size; }, 0);
    document.getElementById('safe-stat-count').textContent = list.length;
    document.getElementById('safe-stat-size').textContent = formatFileSize(totalSize);
    document.getElementById('safe-stats').style.display = 'flex';

    // 使用DocumentFragment减少重排次数
    var frag = document.createDocumentFragment();
    list.forEach(function(v, i) {
        var card = document.createElement('div');
        card.className = 'safe-version-card' + (i === 0 ? ' is-latest' : '');
        card.innerHTML =
            '<div class="safe-version-info">' +
                '<div class="safe-version-number">' +
                    'v' + escapeHtml(v.version) +
                    (i === 0 ? '<span class="safe-version-badge">最新</span>' : '') +
                '</div>' +
                '<div class="safe-version-meta">' +
                    '<span class="safe-version-meta-item">' +
                        '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>' +
                        formatTime(v.modified_at) +
                    '</span>' +
                    '<span class="safe-version-meta-item file-size">' +
                        '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>' +
                        formatFileSize(v.size) +
                    '</span>' +
                '</div>' +
            '</div>' +
            '<div class="safe-version-actions">' +
                '<a class="btn-download-version" href="/editor/' + projectId + '/download/' + encodeURIComponent(v.version) + '" title="下载此版本">' +
                    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>' +
                    '下载' +
                '</a>' +
                '<button class="btn-restore" onclick="confirmRestore(\'' + escapeHtml(v.version) + '\')">' +
                    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>' +
                    '还原' +
                '</button>' +
            '</div>';
        frag.appendChild(card);
    });
    container.appendChild(frag);
    container.style.display = 'flex';
}

function closeSafePanel() {
    document.getElementById('safe-panel').classList.remove('active');
}

function confirmRestore(version) {
    _pendingRestoreVersion = version;
    document.getElementById('restore-target-version').textContent = 'v' + version;
    var btn = document.getElementById('restore-confirm-btn');
    var btnText = document.getElementById('restore-btn-text');
    btn.disabled = true;
    btnText.textContent = '请等待 5 秒';
    document.getElementById('restore-warning').classList.add('active');

    var remaining = 5;
    _restoreTimer = setInterval(function() {
        remaining--;
        if (remaining <= 0) {
            clearInterval(_restoreTimer);
            _restoreTimer = null;
            btnText.textContent = '确认还原';
            btn.disabled = false;
        } else {
            btnText.textContent = '请等待 ' + remaining + ' 秒';
        }
    }, 1000);

    btn.onclick = function() {
        if (btn.disabled) return;
        doRestore();
    };
}

function cancelRestore() {
    if (_restoreTimer) { clearInterval(_restoreTimer); _restoreTimer = null; }
    _pendingRestoreVersion = null;
    document.getElementById('restore-warning').classList.remove('active');
}

async function doRestore() {
    var version = _pendingRestoreVersion;
    cancelRestore();
    if (!version) return;

    try {
        var formData = new FormData();
        formData.append('version', version);
        formData.append('_token', CSRF_TOKEN);
        var response = await fetch('/editor/' + projectId + '/restore', {
            method: 'POST',
            body: formData
        });
        var result = await response.json();

        if (result.success) {
            hasUnsavedChanges = false;
            XCC.toast('已还原到版本 v' + version, 'success');
            setTimeout(function() { window.location.reload(); }, 800);
        } else {
            XCC.alert('还原失败', result.error || '未知错误', 'error');
        }
    } catch (err) {
        XCC.alert('还原失败', err.message, 'error');
    }
}

// ========== 统一ESC键处理(合并为单一监听器,减少事件分发开销) ==========
document.addEventListener('keydown', function(e) {
    if (e.key !== 'Escape') return;
    if (document.getElementById('restore-warning').classList.contains('active')) {
        cancelRestore();
    } else if (document.getElementById('license-modal').classList.contains('active')) {
        closeLicenseModal();
    } else if (document.getElementById('safe-panel').classList.contains('active')) {
        closeSafePanel();
    } else if (document.getElementById('publish-modal').classList.contains('active')) {
        closePublishModal();
    } else if (document.getElementById('xcc-modal-backdrop').classList.contains('show')) {
        XCC._close();
    }
});
</script>
<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"fd99a8b0c5ed4d1dbe9dcc25a3a80c86","r":1,"spa":2}' crossorigin="anonymous"></script>
</body>
</html>