File size: 4,786 Bytes
31e2d98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
 * 构建时脚本:从JSON生成带颜色的HTML
 */

const fs = require('fs');
const path = require('path');

// 文件路径配置
const paths = {
    en: {
        json: path.resolve(__dirname, '../../../data/demo/public/InfoRadar-intro.json'),
        html: path.resolve(__dirname, '../content/home.en.html')
    },
    zh: {
        json: path.resolve(__dirname, '../../../data/demo/public/CN/InfoRadar-介绍.json'),
        html: path.resolve(__dirname, '../content/home.zh.html')
    }
};

// ==========================================
// 颜色计算逻辑(从 SurprisalColorConfig.ts 复制)
// ==========================================

const TOKEN_SURPRISAL_MAX = 18;

/**
 * 计算 surprisal(信息量)
 */
function calculateSurprisal(probability) {
    return -Math.log2(Math.max(probability, Number.EPSILON));
}

/** RGB 部分,通过 CSS 变量复用 */
const INTRO_RGB = '255, 71, 64';

/** alpha 小数位数 */
const ALPHA_PRECISION = 2;

/**
 * 根据 surprisal 计算 alpha(0–0.7),保留指定位数
 */
function getTokenAlpha(surprisal) {
    const normalizedValue = surprisal < 0 ? 0 :
                           surprisal >= TOKEN_SURPRISAL_MAX ? 1 :
                           surprisal / TOKEN_SURPRISAL_MAX;
    const alpha = Math.max(0, Math.min(1, normalizedValue)) * 0.7;
    return alpha.toFixed(ALPHA_PRECISION);
}

// ==========================================
// HTML 生成逻辑
// ==========================================

/**
 * 转义HTML特殊字符
 */
function escapeHtml(text) {
    return text
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}

/**
 * 从JSON生成带颜色的HTML(使用 CSS 变量 --intro-rgb,span 仅写 alpha)
 */
function generateColoredHTML(jsonPath) {
    try {
        const content = fs.readFileSync(jsonPath, 'utf-8');
        const data = JSON.parse(content);

        let html = '';
        for (const token of data.result.bpe_strings) {
            const text = token.raw;
            const prob = token.real_topk[1];
            const surprisal = calculateSurprisal(prob);
            const alpha = getTokenAlpha(surprisal);

            const escapedText = escapeHtml(text);

            if (text.includes('\n')) {
                const parts = text.split(/(\n)/);
                for (const part of parts) {
                    if (part === '\n') {
                        html += '<br>';
                    } else if (part) {
                        html += `<span class="intro-token" style="--a:${alpha}">${escapeHtml(part)}</span>`;
                    }
                }
            } else {
                html += `<span class="intro-token" style="--a:${alpha}">${escapedText}</span>`;
            }
        }

        return html;
    } catch (error) {
        console.error(`Failed to generate HTML from JSON: ${jsonPath}`, error);
        return null;
    }
}

/**
 * 更新HTML文件中的intro-brief内容
 */
function updateHTMLIntro(htmlPath, coloredHTML) {
    try {
        let html = fs.readFileSync(htmlPath, 'utf-8');
        
        // 匹配 <div class="intro-brief" ...> 到 </div> 之间的内容
        const regex = /(<div class="intro-brief"[^>]*>)([\s\S]*?)(<\/div>)/;
        
        if (!regex.test(html)) {
            console.error(`intro-brief not found in ${htmlPath}`);
            return false;
        }
        
        // 替换为带颜色的HTML,容器上定义 CSS 变量供 span 复用
        const replacement = `<div class="intro-brief" style="--intro-rgb: ${INTRO_RGB}">\n    ${coloredHTML}\n</div>`;
        html = html.replace(regex, replacement);
        
        fs.writeFileSync(htmlPath, html, 'utf-8');
        console.log(`✓ Updated ${path.basename(htmlPath)}`);
        return true;
    } catch (error) {
        console.error(`Failed to update HTML file: ${htmlPath}`, error);
        return false;
    }
}

/**
 * 主函数
 */
function main() {
    console.log('Generating colored intro HTML from JSON...\n');
    
    let success = true;
    
    // 生成并更新英文
    const enHTML = generateColoredHTML(paths.en.json);
    if (enHTML) {
        success = updateHTMLIntro(paths.en.html, enHTML) && success;
    } else {
        success = false;
    }
    
    // 生成并更新中文
    const zhHTML = generateColoredHTML(paths.zh.json);
    if (zhHTML) {
        success = updateHTMLIntro(paths.zh.html, zhHTML) && success;
    } else {
        success = false;
    }
    
    if (success) {
        console.log('\n✓ All intro HTML files generated successfully');
    } else {
        console.error('\n✗ Some generation failed');
        process.exit(1);
    }
}

// 执行
main();