This commit is contained in:
wdjwxh 2025-12-11 22:11:08 +08:00
parent 1e8473eb7b
commit 00c45a870f
1 changed files with 508 additions and 10 deletions

518
sync.py
View File

@ -488,6 +488,10 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
return ''.join(result)
# 收集变更块信息用于导航
change_blocks = []
change_block_id = 0
# 生成HTML
html = f'''<!DOCTYPE html>
<html lang="zh-CN">
@ -823,6 +827,166 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
.line-wrapper.blank-placeholder:hover .main-line {{
background-color: rgba(0, 123, 255, 0.02);
}}
/* 变更块高亮样式 */
.line-wrapper.change-block {{
border-left: 3px solid #007bff;
}}
/* 导航浮窗样式 */
.navigation-float {{
position: fixed;
left: 20px;
top: 50%;
transform: translateY(-50%);
width: 280px;
max-height: 70vh;
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
overflow: hidden;
}}
.navigation-header {{
background-color: #007bff;
color: white;
padding: 12px 16px;
font-weight: bold;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
}}
.navigation-toggle {{
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 16px;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s;
}}
.navigation-toggle:hover {{
background-color: rgba(255, 255, 255, 0.2);
}}
.navigation-content {{
max-height: calc(70vh - 50px);
overflow-y: auto;
padding: 8px;
}}
.navigation-item {{
padding: 10px 12px;
margin-bottom: 6px;
background-color: #f8f9fa;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
border-left: 3px solid transparent;
}}
.navigation-item:hover {{
background-color: #e9ecef;
border-left-color: #007bff;
}}
.navigation-item.active {{
background-color: #e3f2fd;
border-left-color: #007bff;
}}
.navigation-item-header {{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}}
.navigation-item-number {{
background-color: #007bff;
color: white;
font-size: 11px;
padding: 2px 6px;
border-radius: 10px;
font-weight: bold;
}}
.navigation-item-type {{
font-size: 11px;
padding: 2px 6px;
border-radius: 3px;
font-weight: bold;
text-transform: uppercase;
}}
.navigation-item-type.added {{
background-color: #28a745;
color: white;
}}
.navigation-item-type.replaced {{
background-color: #ffc107;
color: #212529;
}}
.navigation-item-type.removed {{
background-color: #dc3545;
color: white;
}}
.navigation-item-preview {{
font-size: 12px;
color: #6c757d;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
line-height: 1.3;
word-break: break-word;
}}
.navigation-item-line {{
font-size: 10px;
color: #adb5bd;
margin-top: 2px;
}}
/* 导航浮窗收起状态 */
.navigation-float.collapsed .navigation-content {{
display: none;
}}
.navigation-float.collapsed {{
width: auto;
min-width: 160px;
}}
/* 响应式设计 */
@media (max-width: 768px) {{
.navigation-float {{
left: 10px;
width: 240px;
}}
.navigation-float.collapsed {{
min-width: 120px;
}}
}}
@media (max-width: 480px) {{
.navigation-float {{
position: relative;
left: auto;
top: auto;
transform: none;
width: 100%;
max-height: none;
margin-bottom: 20px;
}}
}}
</style>
</head>
<body>
@ -834,6 +998,16 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
</div>
</div>
<div class="navigation-float" id="navigation-float">
<div class="navigation-header">
<span>变更导航</span>
<button class="navigation-toggle" id="navigation-toggle"></button>
</div>
<div class="navigation-content" id="navigation-content">
<!-- Navigation items will be generated here -->
</div>
</div>
<div class="content-container">
<div class="content-header">中文翻译含英文变更批注</div>
<div class="diff-content">
@ -845,8 +1019,20 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
# 检查是否需要在此行之前插入空白行
if i in blank_lines_to_insert:
additions_list = blank_lines_to_insert[i]
for addition_content in additions_list:
html += f'<div class="line-wrapper blank-placeholder">'
change_block_id += 1
# 添加变更块到导航列表
preview_text = additions_list[0][:50] + "..." if len(additions_list[0]) > 50 else additions_list[0]
change_blocks.append({
'id': change_block_id,
'line': i,
'type': '新增',
'preview': preview_text,
'count': len(additions_list)
})
for idx, addition_content in enumerate(additions_list):
html += f'<div class="line-wrapper blank-placeholder change-block" data-change-id="{change_block_id}">'
html += '<div class="main-line">'
html += '<span class="line-number">&nbsp;</span>' # 不显示行号
html += f'<span class="line-content">(新增英文内容占位)</span>'
@ -870,7 +1056,21 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
# 判断是否为空行
is_empty = not line.strip()
html += f'<div class="line-wrapper {"has-changes" if has_changes else ""} {"empty-line" if is_empty else ""}">'
# 如果有变更(除了新增),添加到导航列表
if has_changes and any(change['type'] in ['replaced', 'removed'] for change in changes):
change_block_id += 1
preview_text = line[:50] + "..." if len(line) > 50 else line
change_type = "替换" if any(change['type'] == 'replaced' for change in changes) else "删除"
change_blocks.append({
'id': change_block_id,
'line': i,
'type': change_type,
'preview': preview_text,
'count': 1
})
html += f'<div class="line-wrapper {"has-changes" if has_changes else ""} {"empty-line" if is_empty else ""} {"change-block" if has_changes else ""}" data-change-id="{change_block_id if has_changes else ""}">'
html += f'<div class="main-line">'
html += f'<span class="line-number">{i}</span>'
html += f'<span class="line-content">{escaped_line if not is_empty else "(空行)"}</span>'
@ -903,23 +1103,321 @@ def create_diff_html(title, en_diff, en_old_lines, en_new_lines, cn_content=None
else:
html += '<div class="no-translation">未找到对应的中文翻译页面</div>'
# 调试日志打印change_blocks信息
print(f"DEBUG: Final change_blocks length = {len(change_blocks)}")
for i, block in enumerate(change_blocks):
print(f"DEBUG: Final block {i}: {block}")
html += '''
</div>
</div>
<script>
// 点击有变更的行时高亮
document.querySelectorAll('.line-wrapper.has-changes').forEach(lineWrapper => {{
lineWrapper.addEventListener('click', () => {{
// 动态生成导航项
function generateNavigationItems() {
const navContent = document.getElementById('navigation-content');
const changeBlocks = document.querySelectorAll('.change-block');
// 清空现有内容
navContent.innerHTML = '';
// 变更块信息已经在HTML中通过data属性标记
const changes = [];
// 收集所有变更块信息
changeBlocks.forEach((block, index) => {
const changeId = block.getAttribute('data-change-id');
if (changeId) {
// 获取变更类型和预览文本
const annotation = block.querySelector('.annotation');
if (annotation) {
const addedItem = annotation.querySelector('.annotation-item.added .annotation-header');
const replacedItem = annotation.querySelector('.annotation-item.replaced .annotation-header');
const removedItem = annotation.querySelector('.annotation-item.removed .annotation-header');
let type = '变更';
let preview = '';
if (addedItem) {
type = '新增';
const content = annotation.querySelector('.annotation-item.added > div:last-child');
preview = content ? content.textContent.substring(0, 50) : '';
} else if (replacedItem) {
type = '替换';
const content = annotation.querySelector('.annotation-item.replaced .old-content');
preview = content ? content.textContent.substring(0, 50) : '';
} else if (removedItem) {
type = '删除';
const content = annotation.querySelector('.annotation-item.removed > div:last-child');
preview = content ? content.textContent.substring(0, 50) : '';
}
if (preview) {
preview += '...';
}
// 获取行号
const lineNumber = block.querySelector('.line-number');
const line = lineNumber ? lineNumber.textContent : '?';
changes.push({
id: changeId,
type: type,
preview: preview || '变更内容',
line: line
});
}
}
});
// 如果没有变更显示未发现变更
if (changes.length === 0) {
navContent.innerHTML = '<div class="navigation-item"><div class="navigation-item-preview">未发现变更</div></div>';
return;
}
// 生成导航项
changes.forEach(change => {
const navItem = document.createElement('div');
navItem.className = 'navigation-item';
navItem.setAttribute('data-change-id', change.id);
navItem.innerHTML = `
<div class="navigation-item-header">
<span class="navigation-item-number">${change.id}</span>
<span class="navigation-item-type ${change.type.toLowerCase()}">${change.type}</span>
</div>
<div class="navigation-item-preview">${change.preview}</div>
<div class="navigation-item-line"> ${change.line}</div>
`;
// 添加点击事件
navItem.addEventListener('click', () => {
const targetBlock = document.querySelector(`[data-change-id="${change.id}"].change-block`);
if (targetBlock) {
targetBlock.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
updateActiveNavItem();
highlightBlock(targetBlock);
}
});
navContent.appendChild(navItem);
});
}
// 导航功能
class DiffNavigation {
constructor() {
this.navFloat = document.getElementById('navigation-float');
this.navToggle = document.getElementById('navigation-toggle');
// 延迟获取导航项确保它们已经生成
this.updateNavigationElements();
this.isCollapsed = false;
// 调试日志
console.log('DiffNavigation Constructor:');
console.log(' navFloat:', this.navFloat);
console.log(' navToggle:', this.navToggle);
console.log(' navItems length:', this.navItems.length);
console.log(' changeBlocks length:', this.changeBlocks.length);
// 打印所有导航项的详细信息
this.navItems.forEach((item, index) => {
console.log(` Navigation item ${index}:`, {
id: item.getAttribute('data-change-id'),
hasType: !!item.querySelector('.navigation-item-type'),
hasPreview: !!item.querySelector('.navigation-item-preview')
});
});
this.init();
}
updateNavigationElements() {
this.navItems = document.querySelectorAll('.navigation-item');
this.changeBlocks = document.querySelectorAll('.change-block');
}
init() {
// 绑定收起/展开按钮
this.navToggle.addEventListener('click', () => {
this.toggleCollapse();
});
// 绑定导航项点击事件
this.navItems.forEach(item => {
item.addEventListener('click', () => {
this.navigateToChange(item);
});
});
// 监听滚动更新当前激活的导航项
window.addEventListener('scroll', () => {
this.updateActiveNavItem();
});
// 初始化当前激活项
this.updateActiveNavItem();
}
toggleCollapse() {
this.isCollapsed = !this.isCollapsed;
if (this.isCollapsed) {
this.navFloat.classList.add('collapsed');
this.navToggle.textContent = '+';
} else {
this.navFloat.classList.remove('collapsed');
this.navToggle.textContent = '';
}
}
navigateToChange(navItem) {
const changeId = navItem.getAttribute('data-change-id');
const targetBlock = document.querySelector(`[data-change-id="${changeId}"].change-block`);
if (targetBlock) {
// 滚动到目标位置
targetBlock.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// 更新激活状态
this.updateActiveNavItem();
// 添加临时高亮效果
this.highlightBlock(targetBlock);
}
}
updateActiveNavItem() {
const scrollPosition = window.scrollY + window.innerHeight / 2;
let activeItem = null;
this.changeBlocks.forEach(block => {
const blockTop = block.offsetTop;
const blockBottom = blockTop + block.offsetHeight;
if (scrollPosition >= blockTop && scrollPosition <= blockBottom) {
const changeId = block.getAttribute('data-change-id');
activeItem = document.querySelector(`[data-change-id="${changeId}"].navigation-item`);
}
});
// 更新激活状态
this.navItems.forEach(item => {
item.classList.remove('active');
});
if (activeItem) {
activeItem.classList.add('active');
// 确保激活项在视口内
this.ensureNavItemVisible(activeItem);
}
}
ensureNavItemVisible(navItem) {
const navContent = document.getElementById('navigation-content');
const itemTop = navItem.offsetTop;
const itemBottom = itemTop + navItem.offsetHeight;
const scrollTop = navContent.scrollTop;
const contentHeight = navContent.clientHeight;
if (itemTop < scrollTop) {
navContent.scrollTop = itemTop - 10;
} else if (itemBottom > scrollTop + contentHeight) {
navContent.scrollTop = itemBottom - contentHeight + 10;
}
}
highlightBlock(block) {
// 移除所有高亮
document.querySelectorAll('.line-wrapper.highlight').forEach(line => {{
document.querySelectorAll('.line-wrapper.highlight').forEach(line => {
line.classList.remove('highlight');
}});
});
// 添加高亮效果
block.classList.add('highlight');
// 2秒后移除高亮
setTimeout(() => {
block.classList.remove('highlight');
}, 2000);
}
// 公共方法跳转到指定变更
goToChange(changeId) {
const navItem = document.querySelector(`[data-change-id="${changeId}"].navigation-item`);
if (navItem) {
this.navigateToChange(navItem);
}
}
// 公共方法获取所有变更列表
getChanges() {
const changes = [];
this.navItems.forEach(item => {
const changeId = item.getAttribute('data-change-id');
const typeElement = item.querySelector('.navigation-item-type');
const previewElement = item.querySelector('.navigation-item-preview');
const lineElement = item.querySelector('.navigation-item-line');
if (changeId && typeElement && previewElement && lineElement) {
changes.push({
id: parseInt(changeId),
type: typeElement.textContent,
preview: previewElement.textContent,
line: lineElement.textContent
});
}
});
return changes;
}
}
// 初始化导航
let diffNavigation;
document.addEventListener('DOMContentLoaded', () => {
// 首先生成导航项
generateNavigationItems();
// 调试日志
console.log('=== Diff Navigation Debug ===');
console.log('Navigation float:', document.getElementById('navigation-float'));
console.log('Navigation content:', document.getElementById('navigation-content'));
console.log('Navigation items:', document.querySelectorAll('.navigation-item'));
console.log('Change blocks:', document.querySelectorAll('.change-block'));
console.log('Has changes lines:', document.querySelectorAll('.line-wrapper.has-changes'));
diffNavigation = new DiffNavigation();
// 添加键盘快捷键支持
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
if (e.key >= '1' && e.key <= '9') {
e.preventDefault();
const changeId = parseInt(e.key);
diffNavigation.goToChange(changeId);
}
}
});
});
// 原有的点击高亮功能
document.querySelectorAll('.line-wrapper.has-changes').forEach(lineWrapper => {
lineWrapper.addEventListener('click', () => {
// 移除所有高亮
document.querySelectorAll('.line-wrapper.highlight').forEach(line => {
line.classList.remove('highlight');
});
// 高亮当前行
lineWrapper.classList.add('highlight');
}});
}});
});
});
</script>
</body>
</html>'''