{
  "id": "4b674802-fcff-47d3-baf5-8acb21e055aa",
  "name": "MyPreset 规则集",
  "description": "从 SillyTavern 导入的规则",
  "rules": [
    {
      "id": "78b99f83-1fca-4a74-aa23-c19081774315",
      "scriptName": "隐藏-thinking",
      "findRegex": "<thinking>.*?<\\/thinking>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 0,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "565c2e23-be93-44b8-bfba-e48103484dd2",
      "scriptName": "隐藏-MVU变量更新",
      "findRegex": "<UpdateVariable>[\\s\\S]*?</UpdateVariable>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 1,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "a3411920-e1b2-4cea-8513-dbb41972648a",
      "scriptName": "隐藏-COT-正文前",
      "findRegex": "<think-pre-main-body>.*</think-pre-main-body>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 2,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "0e93402e-f9f3-4049-bfc0-4b21b86b9bb4",
      "scriptName": "隐藏-COT-正文中",
      "findRegex": "<COT-MID>.*?<\\/COT-MID>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 3,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "dbf06b5e-f694-475d-8ed8-4fdc62833944",
      "scriptName": "隐藏-表格",
      "findRegex": "<tableEdit>.*?<\\/tableEdit>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 4,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "7808d064-d7d1-4f5d-b7a1-f490e5b7b934",
      "scriptName": "隐藏-表格-确保",
      "findRegex": "<填写表格>.*?<\\/填写表格>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 5,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "26beb992-6895-41fc-96a5-0f87c481d034",
      "scriptName": "对AI隐藏图像提示词",
      "findRegex": "\\[img-gen\\].*?\\[/img-gen\\]",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": false,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 6,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "f9632716-675d-4d66-b022-32ad035a544e",
      "scriptName": "美化-pornhub小剧场",
      "findRegex": "(?<pornhub_video><pornhub_video>.*?<\\/pornhub_video>)",
      "replaceString": "```\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Pornhub Style Video Card</title>\n    <style>\n        :root {\n            --bg-primary: #000000;\n            --bg-card: #111111;\n            --bg-secondary: #1f1f1f;\n            --accent-orange: #ff9000;\n            --accent-orange-dark: #e68200;\n            --text-primary: #ffffff;\n            --text-secondary: #cccccc;\n            --text-muted: #808080;\n            --border-color: #333333;\n            --play-button: rgba(255, 255, 255, 0.9);\n        }\n\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n            background: var(--bg-primary);\n            color: var(--text-primary);\n            min-height: 100vh;\n            padding: 10px 0;\n        }\n\n        .pornhub-container {\n            max-width: 960px;\n            margin: 0 auto;\n            background: var(--bg-card);\n            border-radius: 8px;\n            overflow: hidden;\n            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.8);\n        }\n\n        /* 剧情介绍（原缩略图描述）放在最上方 */\n        .video-preview {\n            position: relative;\n            width: 100%;\n            padding: 30px 20px;\n            background: linear-gradient(to bottom, #222, #111);\n            text-align: center;\n        }\n\n        .preview-text {\n            font-size: clamp(18px, 4.5vw, 28px);\n            font-weight: bold;\n            line-height: 1.4;\n            text-shadow: 0 2px 10px rgba(0,0,0,0.8);\n            color: var(--text-primary);\n        }\n\n        /* 真正的缩略图区域 - 放在剧情介绍后面 */\n        .video-thumbnail {\n            position: relative;\n            width: 100%;\n            padding-bottom: 56.25%; /* 16:9 */\n            background: #000 center/cover no-repeat;\n            cursor: pointer;\n        }\n\n        .thumbnail-overlay {\n            position: absolute;\n            inset: 0;\n            background: linear-gradient(to top, rgba(0,0,0,0.7), transparent 40%);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .play-button {\n            width: 90px;\n            height: 90px;\n            background: var(--play-button);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: 0 8px 25px rgba(0,0,0,0.6);\n            transition: transform 0.2s;\n        }\n\n        .play-button::after {\n            content: '';\n            width: 0;\n            height: 0;\n            border-left: 30px solid var(--accent-orange);\n            border-top: 20px transparent solid;\n            border-bottom: 20px transparent solid;\n            margin-left: 8px;\n        }\n\n        .play-button:hover {\n            transform: scale(1.1);\n        }\n\n        /* 内容区域 */\n        .video-info {\n            padding: 24px 20px;\n        }\n\n        .video-title {\n            font-size: clamp(22px, 5vw, 30px);\n            font-weight: bold;\n            line-height: 1.3;\n            margin-bottom: 20px;\n        }\n\n        .video-description {\n            font-size: 16px;\n            color: var(--text-secondary);\n            line-height: 1.7;\n            margin-bottom: 28px;\n            white-space: pre-wrap;\n        }\n\n        .stats-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n            gap: 16px;\n            padding: 20px 0;\n            border-top: 1px solid var(--border-color);\n            border-bottom: 1px solid var(--border-color);\n            margin-bottom: 28px;\n        }\n\n        .stat-item {\n            font-size: 15px;\n            color: var(--text-secondary);\n        }\n\n        .stat-value {\n            color: var(--text-primary);\n            font-weight: bold;\n            font-size: 16px;\n        }\n\n        .actions {\n            display: flex;\n            gap: 16px;\n            margin-bottom: 32px;\n            flex-wrap: wrap;\n        }\n\n        .subscribe-btn {\n            background: var(--accent-orange);\n            color: white;\n            border: none;\n            padding: 14px 28px;\n            border-radius: 4px;\n            font-size: 16px;\n            font-weight: bold;\n            cursor: pointer;\n            transition: background 0.3s;\n        }\n\n        .subscribe-btn:hover {\n            background: var(--accent-orange-dark);\n        }\n\n        .tags {\n            display: flex;\n            flex-wrap: wrap;\n            gap: 12px;\n            margin-bottom: 40px;\n        }\n\n        .tag {\n            background: var(--bg-secondary);\n            color: var(--text-primary);\n            padding: 10px 18px;\n            border-radius: 30px;\n            font-size: 15px;\n            transition: background 0.2s;\n        }\n\n        .tag:hover {\n            background: #333;\n        }\n\n        .comments-section {\n            border-top: 1px solid var(--border-color);\n            padding-top: 32px;\n        }\n\n        .comments-title {\n            font-size: 22px;\n            font-weight: bold;\n            margin-bottom: 24px;\n        }\n\n        .comment {\n            display: flex;\n            gap: 16px;\n            padding: 20px 0;\n            border-bottom: 1px solid var(--border-color);\n        }\n\n        .comment:last-child {\n            border-bottom: none;\n        }\n\n        .comment-avatar {\n            width: 48px;\n            height: 48px;\n            border-radius: 50%;\n            background: #444;\n            flex-shrink: 0;\n        }\n\n        .comment-content {\n            flex: 1;\n        }\n\n        .comment-header {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            margin-bottom: 8px;\n        }\n\n        .comment-user {\n            font-weight: bold;\n            font-size: 16px;\n        }\n\n        .verified {\n            color: var(--accent-orange);\n            font-size: 14px;\n            font-weight: bold;\n        }\n\n        .comment-text {\n            color: var(--text-secondary);\n            line-height: 1.6;\n            font-size: 16px;\n            margin-bottom: 10px;\n        }\n\n        .comment-likes {\n            color: var(--text-muted);\n            font-size: 15px;\n        }\n\n        /* 手机端优化 */\n        @media (max-width: 768px) {\n            body { padding: 0; }\n            .video-info { padding: 20px 16px; }\n            .video-preview { padding: 24px 16px; }\n            .stats-grid { grid-template-columns: 1fr 1fr; }\n            .actions { justify-content: center; }\n            .play-button { width: 70px; height: 70px; }\n            .play-button::after { border-left-width: 24px; border-top-width: 16px; border-bottom-width: 16px; }\n        }\n    </style>\n</head>\n<body>\n    <div class=\"pornhub-container\" id=\"pornhubCard\">\n        <!-- 剧情介绍（原 thumbnail 内容）放在最上方 -->\n        <div class=\"video-preview\">\n            <div class=\"preview-text\" id=\"thumbnailText\"></div>\n        </div>\n\n        <!-- 缩略图区域（带大播放按钮） -->\n        <div class=\"video-thumbnail\" id=\"thumbnailWrapper\">\n            <div class=\"thumbnail-overlay\">\n                <div class=\"play-button\"></div>\n            </div>\n        </div>\n\n        <div class=\"video-info\">\n            <h1 class=\"video-title\" id=\"title\"></h1>\n            <p class=\"video-description\" id=\"description\"></p>\n\n            <div class=\"stats-grid\" id=\"stats\"></div>\n\n            <div class=\"actions\">\n                <button class=\"subscribe-btn\" id=\"subscribe\"></button>\n            </div>\n\n            <div class=\"tags\" id=\"tags\"></div>\n\n            <div class=\"comments-section\">\n                <h2 class=\"comments-title\">评论</h2>\n                <div id=\"comments\"></div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        // 正则已改为包含根标签，所以替换后是完整 XML\n        const videoData = `$<pornhub_video>`;\n\n        function parsePornhubVideo(xmlString) {\n            if (!xmlString || !xmlString.includes('<pornhub_video>')) return null;\n            \n            const parser = new DOMParser();\n            const doc = parser.parseFromString(xmlString.trim(), 'text/xml');\n\n            if (doc.querySelector('parsererror')) {\n                console.error('XML 解析失败');\n                return null;\n            }\n\n            const getText = (tag) => {\n                const el = doc.querySelector(tag);\n                return el ? el.textContent.trim() : '';\n            };\n\n            return {\n                title: getText('title'),\n                thumbnail: getText('thumbnail'),        // 剧情介绍文字\n                description: getText('description'),\n                views: getText('views'),\n                like_rate: getText('like_rate'),\n                upload_time: getText('upload_time'),\n                uploader: getText('uploader'),\n                subscribe: getText('subscribe'),\n                tags: getText('tags'),\n                comments: Array.from(doc.querySelectorAll('comment')).map(c => ({\n                    user: c.getAttribute('user') || '匿名',\n                    verified: c.getAttribute('verified') === 'true',\n                    text: c.childNodes[0] ? c.childNodes[0].textContent.trim() : '',\n                    likes: getTextFromElement(c, 'likes')\n                }))\n            };\n        }\n\n        function getTextFromElement(parent, tag) {\n            const el = parent.querySelector(tag);\n            return el ? el.textContent.trim() : '';\n        }\n\n        function renderVideoCard(data) {\n            if (!data) return;\n\n            document.getElementById('title').textContent = data.title;\n            document.getElementById('description').textContent = data.description;\n            document.getElementById('thumbnailText').textContent = data.thumbnail;  // 剧情介绍放在最上\n            document.getElementById('subscribe').textContent = data.subscribe;\n\n            // 统计信息\n            const stats = document.getElementById('stats');\n            stats.innerHTML = `\n                <div class=\"stat-item\"><span class=\"stat-value\">${data.views}</span></div>\n                <div class=\"stat-item\"><span class=\"stat-value\">${data.like_rate}</span></div>\n                <div class=\"stat-item\"><span class=\"stat-value\">${data.upload_time}</span></div>\n                <div class=\"stat-item\"><span class=\"stat-value\">${data.uploader}</span></div>\n            `;\n\n            // 标签\n            const tagsContainer = document.getElementById('tags');\n            tagsContainer.innerHTML = '';\n            data.tags.split(/\\s+/).filter(t => t.length > 0).forEach(tag => {\n                const span = document.createElement('span');\n                span.className = 'tag';\n                span.textContent = tag;\n                tagsContainer.appendChild(span);\n            });\n\n            // 评论\n            const commentsContainer = document.getElementById('comments');\n            commentsContainer.innerHTML = '';\n            data.comments.forEach(comment => {\n                const div = document.createElement('div');\n                div.className = 'comment';\n                div.innerHTML = `\n                    <div class=\"comment-avatar\"></div>\n                    <div class=\"comment-content\">\n                        <div class=\"comment-header\">\n                            <span class=\"comment-user\">${escapeHtml(comment.user)}</span>\n                            ${comment.verified ? '<span class=\"verified\">✔ 已认证会员</span>' : ''}\n                        </div>\n                        <div class=\"comment-text\">${escapeHtml(comment.text)}</div>\n                        <div class=\"comment-likes\">${comment.likes}</div>\n                    </div>\n                `;\n                commentsContainer.appendChild(div);\n            });\n        }\n\n        function escapeHtml(text) {\n            const div = document.createElement('div');\n            div.textContent = text;\n            return div.innerHTML;\n        }\n\n        // 执行渲染\n        const data = parsePornhubVideo(videoData);\n        if (data) {\n            renderVideoCard(data);\n        } else {\n            document.getElementById('title').textContent = '加载失败：XML 解析错误';\n        }\n    </script>\n</body>\n</html>\n```",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": false,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 7,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "5f345459-8066-4df0-9641-5fa9113740ab",
      "scriptName": "对AI隐藏-小剧场",
      "findRegex": "<pornhub_video>.*?<\\/pornhub_video>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": false,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 8,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "e301e033-fe97-4b12-b02e-7d8f76a2b324",
      "scriptName": "对AI隐藏-小剧场-确保",
      "findRegex": "<小剧场>.*?<\\/小剧场>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": false,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 9,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "37c62238-b6d5-4445-8fa7-068b68e35109",
      "scriptName": "月经追踪状态栏",
      "findRegex": "<StatusPlaceHolderImpl\\/>",
      "replaceString": "```\n<head>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes\">\n</head>\n<body>\n<%\nif (runType == 'render')\n{\n    function SafeGetValue(value, defaultValue = \"\") {\n        if (value === undefined || value === null) {\n            return defaultValue;\n        }\n        if (Array.isArray(value)) {\n            return value.length !== 0 ? value[0] : defaultValue;\n        }\n        return value;\n    }\n    \n    function SafeGetDescription(value, defaultValue = \"\") {\n        if (Array.isArray(value) && value.length > 1) {\n            return value[1];\n        }\n        return defaultValue;\n    }\n    \n    // 获取周期进度百分比\n    function getCycleProgress(day) {\n        return Math.min((day / 28) * 100, 100);\n    }\n    \n    // 判断是否为危险期（根据分类判断）\n    function isDangerousPeriod(category) {\n        return category.includes('排卵期') || category.includes('危险期');\n    }\n    \n    // 获取周期图标（根据全局calculateMenstrualCycle返回的分类）\n    function getCycleIcon(category) {\n        if (category.includes('月经期')) return '🩸';\n        if (category.includes('排卵期')) return '🌸';\n        if (category.includes('安全期')) return '✅';\n        return '📅';\n    }\n    \n    // 获取周期颜色（根据全局calculateMenstrualCycle返回的分类）\n    function getCycleColor(category) {\n        if (category.includes('月经期')) return '#DC143C';\n        if (category.includes('排卵期')) return '#FF1493';\n        if (category.includes('安全期')) return '#32CD32';\n        return '#666';\n    }\n    \n    // 获取受孕概率等级（基于天数，与计算器函数逻辑一致）\n    function getFertilityLevel(day) {\n        if (day >= 1 && day <= 5) return 0;        // 月经期\n        if (day >= 12 && day <= 16) return 5;      // 排卵期高峰\n        if (day >= 10 && day <= 19) return 4;      // 排卵期\n        if (day >= 6 && day <= 22) return 2;       // 次级可能\n        return 1;                                   // 安全期\n    }\n\nconst data = window.TavernHelper.getVariables({type: 'message', message_id: message_id});\nconst msg_data = data.stat_data;\n\n// 动态获取所有有月经数据的角色\nconst characters = [];\nfor (const key in msg_data) {\n    if (msg_data[key] && msg_data[key].月经 && msg_data[key].月经.周期天数) {\n        // 优先使用姓名字段，如果没有则使用键名\n        const displayName = SafeGetValue(msg_data[key].姓名, key);\n        characters.push({\n            key: key,\n            name: displayName,\n            data: msg_data[key]\n        });\n    }\n}\n%>\n\n<style>\n.menstrual-wrapper {\n    display: flex;\n    justify-content: center;\n    align-items: flex-start;\n    padding: 10px;\n    width: 100%;\n    box-sizing: border-box;\n}\n\n.menstrual-container {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    width: 100%;\n    max-width: 500px;\n    border-radius: 12px;\n    overflow: hidden;\n    box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n    font-size: 13px;\n    background: linear-gradient(135deg, #ffeef8 0%, #fff5f7 100%);\n}\n\n.menstrual-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 12px 16px;\n    background: linear-gradient(135deg, #FF69B4 0%, #FF1493 100%);\n    color: white;\n    cursor: pointer;\n    user-select: none;\n    -webkit-tap-highlight-color: transparent;\n    touch-action: manipulation;\n}\n\n.menstrual-header:hover {\n    background: linear-gradient(135deg, #FF1493 0%, #C71585 100%);\n}\n\n.menstrual-title {\n    font-size: 15px;\n    font-weight: 600;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n}\n\n.collapse-icon {\n    font-size: 14px;\n    transition: transform 0.2s ease;\n    opacity: 0.9;\n}\n\n.collapse-icon.collapsed {\n    transform: rotate(-90deg);\n}\n\n.menstrual-body {\n    max-height: 2000px;\n    overflow: hidden;\n    transition: max-height 0.3s ease, opacity 0.2s ease;\n}\n\n.menstrual-body.collapsed {\n    max-height: 0;\n    opacity: 0;\n}\n\n.tab-nav {\n    display: flex;\n    gap: 4px;\n    padding: 8px 8px 0;\n    background: transparent;\n}\n\n.tab-btn {\n    flex: 1;\n    padding: 10px 14px;\n    border: none;\n    background: rgba(255,255,255,0.6);\n    cursor: pointer;\n    font-size: 14px;\n    font-weight: 500;\n    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n    border-radius: 8px 8px 0 0;\n    color: #666;\n    -webkit-tap-highlight-color: transparent;\n    touch-action: manipulation;\n    backdrop-filter: blur(10px);\n    position: relative;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    min-width: 0;\n}\n\n.tab-btn:hover {\n    background: rgba(255,255,255,0.8);\n    transform: translateY(-2px);\n}\n\n.tab-btn.active {\n    background: white;\n    color: #FF1493;\n    box-shadow: 0 -2px 8px rgba(255,20,147,0.2);\n    font-weight: 600;\n}\n\n.tab-btn.active::after {\n    content: '';\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    height: 2px;\n    background: linear-gradient(90deg, #FF69B4, #FF1493);\n}\n\n.tab-content {\n    display: none;\n    padding: 16px;\n    background: white;\n    border-radius: 0 0 12px 12px;\n}\n\n.tab-content.active {\n    display: block;\n}\n\n.cycle-card {\n    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);\n    padding: 16px;\n    border-radius: 12px;\n    margin-bottom: 16px;\n    box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n    border: 1px solid rgba(255,20,147,0.1);\n}\n\n.cycle-card:hover {\n    box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n    transform: translateY(-2px);\n}\n\n.cycle-main-info {\n    display: flex;\n    align-items: center;\n    gap: 16px;\n    margin-bottom: 16px;\n}\n\n.cycle-icon {\n    font-size: 48px;\n    line-height: 1;\n    filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\n}\n\n.cycle-details {\n    flex: 1;\n}\n\n.cycle-phase {\n    font-size: 18px;\n    font-weight: 700;\n    margin-bottom: 4px;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n}\n\n.cycle-day {\n    font-size: 14px;\n    color: #666;\n    font-weight: 500;\n}\n\n.danger-badge {\n    display: inline-block;\n    padding: 2px 8px;\n    border-radius: 4px;\n    font-size: 11px;\n    font-weight: 600;\n    background: #FF4444;\n    color: white;\n    animation: pulse 2s ease-in-out infinite;\n}\n\n@keyframes pulse {\n    0%, 100% { opacity: 1; }\n    50% { opacity: 0.7; }\n}\n\n.safe-badge {\n    display: inline-block;\n    padding: 2px 8px;\n    border-radius: 4px;\n    font-size: 11px;\n    font-weight: 600;\n    background: #4CAF50;\n    color: white;\n}\n\n.cycle-progress {\n    width: 100%;\n    height: 8px;\n    background: rgba(0,0,0,0.08);\n    border-radius: 4px;\n    overflow: hidden;\n    margin: 12px 0;\n}\n\n.cycle-progress-bar {\n    height: 100%;\n    background: linear-gradient(90deg, #FF69B4 0%, #FF1493 50%, #C71585 100%);\n    border-radius: 4px;\n    transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);\n    box-shadow: 0 0 10px rgba(255,20,147,0.5);\n    position: relative;\n    overflow: hidden;\n}\n\n.cycle-progress-bar::after {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: -100%;\n    width: 100%;\n    height: 100%;\n    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);\n    animation: shimmer 2s infinite;\n}\n\n@keyframes shimmer {\n    0% { left: -100%; }\n    100% { left: 100%; }\n}\n\n.cycle-description {\n    font-size: 13px;\n    color: #555;\n    line-height: 1.6;\n    padding: 12px;\n    background: rgba(255,20,147,0.05);\n    border-radius: 8px;\n    border-left: 4px solid;\n    transition: all 0.3s ease;\n}\n\n.cycle-description:hover {\n    background: rgba(255,20,147,0.08);\n    padding-left: 16px;\n}\n\n.cycle-stats {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 12px;\n    margin-top: 12px;\n}\n\n.stat-item {\n    padding: 10px;\n    background: white;\n    border-radius: 8px;\n    border: 1px solid rgba(0,0,0,0.06);\n    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.stat-item:hover {\n    border-color: rgba(255,20,147,0.3);\n    box-shadow: 0 2px 8px rgba(255,20,147,0.1);\n    transform: translateY(-1px);\n}\n\n.stat-label {\n    font-size: 11px;\n    color: #888;\n    margin-bottom: 4px;\n    font-weight: 500;\n}\n\n.stat-value {\n    font-size: 14px;\n    font-weight: 600;\n    color: #333;\n}\n\n.fertility-indicator {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 12px;\n    background: white;\n    border-radius: 8px;\n    margin-top: 12px;\n    border: 2px solid;\n}\n\n.fertility-label {\n    font-size: 13px;\n    font-weight: 600;\n}\n\n.fertility-dots {\n    display: flex;\n    gap: 6px;\n}\n\n.fertility-dot {\n    width: 10px;\n    height: 10px;\n    border-radius: 50%;\n    background: #ddd;\n    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.fertility-dot.active {\n    background: currentColor;\n    box-shadow: 0 0 8px currentColor;\n    transform: scale(1.2);\n    animation: pulse-dot 2s ease-in-out infinite;\n}\n\n@keyframes pulse-dot {\n    0%, 100% { \n        transform: scale(1.2);\n        opacity: 1; \n    }\n    50% { \n        transform: scale(1.4);\n        opacity: 0.8; \n    }\n}\n\n/* 移动端适配 */\n@media (max-width: 768px) {\n    .menstrual-wrapper {\n        padding: 8px;\n    }\n    \n    .menstrual-container {\n        font-size: 12px;\n    }\n    \n    .menstrual-header {\n        padding: 10px 12px;\n    }\n    \n    .menstrual-title {\n        font-size: 14px;\n    }\n    \n    .tab-content {\n        padding: 12px;\n    }\n    \n    .cycle-card {\n        padding: 12px;\n        margin-bottom: 12px;\n    }\n    \n    .cycle-icon {\n        font-size: 36px;\n    }\n    \n    .cycle-phase {\n        font-size: 16px;\n    }\n    \n    .cycle-day {\n        font-size: 12px;\n    }\n    \n    .cycle-description {\n        font-size: 12px;\n        padding: 10px;\n    }\n    \n    .cycle-stats {\n        gap: 8px;\n    }\n    \n    .stat-item {\n        padding: 8px;\n    }\n}\n\n/* 超小屏幕适配 */\n@media (max-width: 380px) {\n    .menstrual-wrapper {\n        padding: 5px;\n    }\n    \n    .cycle-main-info {\n        flex-direction: column;\n        text-align: center;\n        gap: 12px;\n    }\n    \n    .cycle-stats {\n        grid-template-columns: 1fr;\n    }\n    \n    .tab-btn {\n        font-size: 12px;\n        padding: 8px 10px;\n        min-width: 60px;\n    }\n}\n</style>\n\n<div class=\"menstrual-wrapper\">\n    <div class=\"menstrual-container\">\n        <!-- 标题栏 -->\n        <div class=\"menstrual-header\" onclick=\"toggleCollapse()\">\n            <div class=\"menstrual-title\">\n                <span>🌸</span>\n                <span>月经周期追踪</span>\n                <% if (characters.length > 0) { %>\n                    <span style=\"font-size: 12px; font-weight: 400; opacity: 0.85; margin-left: 4px;\">(<%- characters.length %> 人)</span>\n                <% } %>\n            </div>\n            <div class=\"collapse-icon\" id=\"collapseIcon\">▼</div>\n        </div>\n\n        <!-- 内容区域 -->\n        <div class=\"menstrual-body\" id=\"menstrualBody\">\n            <% if (characters.length === 0) { %>\n                <div class=\"tab-content active\" style=\"text-align: center; padding: 40px 20px;\">\n                    <div style=\"font-size: 64px; margin-bottom: 16px; opacity: 0.5;\">📅</div>\n                    <div style=\"font-size: 16px; color: #999; margin-bottom: 8px; font-weight: 500;\">暂无月经周期数据</div>\n                    <div style=\"font-size: 13px; color: #bbb;\">请在角色数据中添加月经周期信息</div>\n                </div>\n            <% } else { %>\n            <!-- Tab导航 -->\n            <div class=\"tab-nav\">\n                <% characters.forEach((char, index) => { %>\n                <button class=\"tab-btn <%= index === 0 ? 'active' : '' %>\" onclick=\"switchTab(event, 'char_<%- index %>')\">\n                    <%- char.name %>\n                </button>\n                <% }); %>\n            </div>\n\n            <!-- 动态生成角色月经状态 -->\n            <% characters.forEach((char, index) => { \n                const cycleDay = SafeGetValue(char.data.月经.周期天数, 1);\n                const cycleInfo = typeof calculateMenstrualCycle !== 'undefined' \n                    ? calculateMenstrualCycle(Number(cycleDay))\n                    : ['未知', '数据加载中'];\n                const cycleCategory = cycleInfo[0];\n                const cyclePhase = cycleInfo[1];\n                const cycleIcon = getCycleIcon(cycleCategory);\n                const cycleColor = getCycleColor(cycleCategory);\n                const cycleProgress = getCycleProgress(cycleDay);\n                const isDanger = isDangerousPeriod(cycleCategory);\n                // 计算距离下次月经的天数（如果当前在月经期，显示0；否则显示剩余天数）\n                const daysUntilPeriod = cycleDay <= 5 ? 0 : (29 - cycleDay);\n                const fertilityLevel = getFertilityLevel(cycleDay);\n            %>\n            <div id=\"char_<%- index %>\" class=\"tab-content <%= index === 0 ? 'active' : '' %>\">\n                <div class=\"cycle-card\">\n                    <div class=\"cycle-main-info\">\n                        <div class=\"cycle-icon\"><%- cycleIcon %></div>\n                        <div class=\"cycle-details\">\n                            <div class=\"cycle-phase\" style=\"color: <%- cycleColor %>;\">\n                                <%- cycleCategory.replace(/（.*?）/g, '') %>\n                                <% if (isDanger) { %>\n                                    <span class=\"danger-badge\">⚠️ 危险期</span>\n                                <% } else if (cycleDay >= 1 && cycleDay <= 5) { %>\n                                    <!-- 月经期不显示徽章 -->\n                                <% } else { %>\n                                    <span class=\"safe-badge\">✓ 安全期</span>\n                                <% } %>\n                            </div>\n                            <div class=\"cycle-day\">周期第 <strong><%- cycleDay %></strong> 天 / 共 28 天</div>\n                        </div>\n                    </div>\n                    \n                    <div class=\"cycle-progress\">\n                        <div class=\"cycle-progress-bar\" style=\"width: <%- cycleProgress %>%; background: linear-gradient(90deg, <%- cycleColor %> 0%, <%- cycleColor %>dd 100%);\"></div>\n                    </div>\n                    \n                    <div class=\"cycle-description\" style=\"border-left-color: <%- cycleColor %>;\">\n                        <%- cyclePhase %>\n                    </div>\n                    \n                    <div class=\"cycle-stats\">\n                        <div class=\"stat-item\">\n                            <div class=\"stat-label\">距离下次月经</div>\n                            <div class=\"stat-value\" style=\"color: <%- cycleColor %>;\">\n                                <% if (daysUntilPeriod === 0) { %>\n                                    进行中\n                                <% } else { %>\n                                    <%- daysUntilPeriod %> 天\n                                <% } %>\n                            </div>\n                        </div>\n                        <div class=\"stat-item\">\n                            <div class=\"stat-label\">周期进度</div>\n                            <div class=\"stat-value\" style=\"color: <%- cycleColor %>;\"><%- Math.round(cycleProgress) %>%</div>\n                        </div>\n                    </div>\n                    \n                    <div class=\"fertility-indicator\" style=\"border-color: <%- cycleColor %>; color: <%- cycleColor %>;\">\n                        <div class=\"fertility-label\">\n                            <% if (cycleDay >= 1 && cycleDay <= 5) { %>\n                                🩸 月经期间\n                            <% } else if (fertilityLevel >= 4) { %>\n                                💋 受孕概率：高\n                            <% } else if (fertilityLevel >= 2) { %>\n                                🤔 受孕概率：中\n                            <% } else { %>\n                                😌 受孕概率：低\n                            <% } %>\n                        </div>\n                        <div class=\"fertility-dots\">\n                            <% for (let i = 0; i < 5; i++) { %>\n                                <div class=\"fertility-dot <%= i < fertilityLevel ? 'active' : '' %>\"></div>\n                            <% } %>\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <% }); %>\n            <% } %>\n        </div>\n    </div>\n</div>\n\n<script>\n// 检查月经周期计算器是否可用\nif (typeof calculateMenstrualCycle === 'undefined') {\n    console.warn('⚠️ 月经周期计算器函数未找到，请确保已加载 生理周期计算器.js');\n    console.info('💡 函数应返回: [安全期分类, 生理阶段描述]');\n} else {\n    console.log('✅ 月经周期计算器已加载');\n}\n\n// Tab切换功能\nfunction switchTab(evt, tabId) {\n    try {\n        // 隐藏所有tab内容\n        const contents = document.querySelectorAll('.tab-content');\n        contents.forEach(c => c.classList.remove('active'));\n        \n        // 移除所有按钮的active状态\n        const buttons = document.querySelectorAll('.tab-btn');\n        buttons.forEach(b => b.classList.remove('active'));\n        \n        // 显示当前tab\n        const targetTab = document.getElementById(tabId);\n        if (targetTab) {\n            targetTab.classList.add('active');\n            evt.currentTarget.classList.add('active');\n        }\n    } catch (error) {\n        console.error('切换标签页失败:', error);\n    }\n}\n\n// 折叠/展开功能\nfunction toggleCollapse() {\n    try {\n        const menstrualBody = document.getElementById('menstrualBody');\n        const collapseIcon = document.getElementById('collapseIcon');\n        \n        if (menstrualBody && collapseIcon) {\n            menstrualBody.classList.toggle('collapsed');\n            collapseIcon.classList.toggle('collapsed');\n        }\n    } catch (error) {\n        console.error('折叠切换失败:', error);\n    }\n}\n</script>\n\n<% } %>\n</body>\n```\n",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": false,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": true,
      "order": 10,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "26ec780e-149f-489d-a177-de875e559e35",
      "scriptName": "[选项]主题",
      "findRegex": "<options>((?:(?!<options>).)*?)</options>",
      "replaceString": "```html\n<body>\n  <script>\n    $('body').load('https://testingcf.jsdelivr.net/gh/emo-lsp/SillyTavern@refs/heads/main/QingShuHuai/选项1.0.html')\n  </script>\n  </body>\n```",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": true,
      "promptOnly": false,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": 2,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 11,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    },
    {
      "id": "e61a23ac-bc46-4d49-9f3f-a1ec4756c857",
      "scriptName": "[选项]隐藏",
      "findRegex": "<options>(.*?)<\\/options>",
      "replaceString": "",
      "trimStrings": [],
      "placement": [
        2
      ],
      "substituteRegex": 0,
      "markdownOnly": false,
      "promptOnly": true,
      "runOnEdit": true,
      "minDepth": null,
      "maxDepth": null,
      "scope": "preset",
      "characterName": null,
      "presetName": "MyPreset",
      "disabled": false,
      "order": 12,
      "createdAt": 1778078941,
      "updatedAt": 1778078941,
      "description": null
    }
  ],
  "createdAt": 1778078941,
  "updatedAt": 1778078941,
  "version": 1,
  "isSillyTavernFormat": true
}